Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .agents/configs/prompt-injection-patterns.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# social_engineering — Urgency, authority claims, emotional manipulation
# data_exfiltration — Attempts to leak data via URLs/encoding
# data_exfiltration_dns — DNS-based data exfiltration (CVE-2025-55284)
# credential_exposure — Secrets embedded in URLs/config output (t4954)
# context_manipulation — Token smuggling, history manipulation, false claims
#
# ADDING PATTERNS:
Expand Down Expand Up @@ -614,3 +615,43 @@ context_manipulation:
- severity: HIGH
description: "Dismisses previous content as fake"
pattern: '(?i)\bthe\s+above\s+(was|is)\s+(just\s+)?(a\s+)?(test|joke|fake|distraction)'

# ================================================================
# CREDENTIAL EXPOSURE (t4954)
# ================================================================
# Detects secrets embedded in URL query parameters within command output.
# These patterns catch credential material that leaks from application
# config tables (webhook settings, OAuth configs, integration records)
# where authenticated callback URLs contain secrets as query params.
credential_exposure:
- severity: MEDIUM
description: "URL query param: secret"
pattern: '[?&]secret=[^&\s]{8,}'

- severity: MEDIUM
description: "URL query param: token"
pattern: '[?&]token=[^&\s]{8,}'

- severity: MEDIUM
description: "URL query param: api_key/apikey"
pattern: '[?&](api_key|apikey|api-key)=[^&\s]{8,}'
Comment on lines +636 to +637
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

For consistency with the new rule in .agents/prompts/build.txt (section 8.4, line 295), which mentions ?key=, this pattern should also detect key=. The current pattern only detects api_key, apikey, and api-key. The description should also be updated to reflect this change.

    description: "URL query param: key/api_key/apikey"
    pattern: '[?&](key|api_key|apikey|api-key)=[^&\s]{8,}'
References
  1. This rule emphasizes the need to include all known variations of sensitive patterns (like key=) for comprehensive security detection and sanitization.
  2. This rule promotes consistency by ensuring that if a new sensitive parameter (key=) is identified, all relevant detection and sanitization logic across the codebase is updated.


- severity: MEDIUM
description: "URL query param: password"
pattern: '[?&]password=[^&\s]{8,}'

- severity: MEDIUM
description: "URL query param: access_token"
pattern: '[?&]access_token=[^&\s]{8,}'

- severity: MEDIUM
description: "URL query param: auth/authorization"
pattern: '[?&](auth|authorization)=[^&\s]{8,}'

- severity: MEDIUM
description: "URL query param: client_secret"
pattern: '[?&]client_secret=[^&\s]{8,}'

- severity: MEDIUM
description: "URL query param: webhook_secret"
pattern: '[?&]webhook_secret=[^&\s]{8,}'
22 changes: 22 additions & 0 deletions .agents/prompts/build.txt
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,28 @@ When referencing specific functions or code include the pattern `file_path:line_
# the secret value before presenting it to the user.
- After running any command that references a credential variable, assess whether the output could contain the secret value. If the command failed (non-zero exit) and the secret was passed as an argument (violating 8.2), assume the output is contaminated — do not present it to the user. Flag for immediate credential rotation.
- This is a judgment call, not a regex check. The agent knows which variables contain secrets and can assess whether output looks like it contains credential material (long base64 strings, API key patterns, JSON with auth fields).
#
# 8.4 Application config contains embedded credentials (t4954)
# Threat: application configuration tables (webhook settings, integration
# records, OAuth configs, API endpoint metadata) store authenticated callback
# URLs with secrets as query parameters (e.g., `?secret=<value>`). A general
# `SELECT *` or `SELECT value` on these tables returns the full record
# including embedded credentials — even though the command itself doesn't
# reference any credential variable. Sections 8.2 and 8.3 don't catch this
# because the secret isn't passed as an argument or referenced as a variable.
# Incident: FluentForms webhook config queried via `wp db query`, output
# contained `request_url` with `?secret=<value>`. Required immediate rotation.
- When querying application config (webhook settings, integration records, OAuth configs, API endpoint metadata), NEVER fetch raw record values with `SELECT *` or unfiltered column reads. Query schema/keys first, then extract only non-credential fields via targeted selectors (`jq` field filters, `wp eval` with specific property access, SQL column lists excluding URL/token/secret fields).
- UNSAFE: `wp db query "SELECT value FROM wp_fluentform_form_meta WHERE meta_key='fluentform_webhook_feed'"` — returns full JSON including `request_url` with embedded `?secret=<value>`
- UNSAFE: `SELECT * FROM wp_options WHERE option_name LIKE '%webhook%'` — option values often contain authenticated URLs
- UNSAFE: `wp option get <integration_config>` — raw JSON dump may contain OAuth tokens, API keys, or signed URLs
- SAFE: `wp db query "SELECT meta_key FROM wp_fluentform_form_meta WHERE form_id=1"` — schema/key discovery only, no values
- SAFE: `wp eval 'echo json_encode(array_keys(json_decode(get_option("webhook_config"), true)));'` — key names only
- SAFE: `wp db query "SELECT name, status, form_id FROM wp_fluentform_form_meta WHERE ..."` — specific non-secret columns
- SAFE: pipe raw output through `jq 'del(.request_url, .secret, .token, .api_key)'` to strip credential fields before display
- URLs in config records frequently contain embedded secrets as query parameters (`?secret=`, `?token=`, `?key=`, `?api_key=`, `?password=`). Treat any URL field in application config as potentially containing credentials.
- This applies broadly: WordPress options/meta, Stripe webhook endpoints, Zapier/Make.com integration configs, OAuth redirect URIs with state tokens, any SaaS callback URL stored in a database.
- When investigating webhook or integration issues, describe the config structure (field names, record count, status) without exposing field values. If a specific URL is needed for debugging, ask the user to check it in their admin UI.
- Confirm destructive operations before execution
- NEVER create files in `~/` root - use `~/.aidevops/.agent-workspace/work/[project]/`
- Do not commit files containing secrets (.env, credentials.json, etc.)
Expand Down
47 changes: 46 additions & 1 deletion .agents/scripts/prompt-guard-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ _pg_load_yaml_patterns() {
# data_exfiltration, data_exfiltration_dns, context_manipulation,
# homoglyph, unicode_manipulation, fake_role, comment_injection,
# priority_manipulation, fake_delimiter, split_personality,
# steganographic, fake_conversation
# steganographic, fake_conversation, credential_exposure

# YAML pattern file path (Lasso-compatible format)
PROMPT_GUARD_YAML_PATTERNS="${PROMPT_GUARD_YAML_PATTERNS:-}"
Expand Down Expand Up @@ -327,6 +327,14 @@ LOW|unicode_manipulation|Mixed script with injection|\p{Cyrillic}[\x00-\x7F]*(ns
LOW|steganographic|Acrostic instruction pattern|[A-Z][a-z]+\s*\n[A-Z][a-z]+\s*\n[A-Z][a-z]+\s*\n[A-Z][a-z]+\s*\n[A-Z][a-z]+
LOW|system_prompt_extraction|System prompt extraction variant|([Ww]rite|[Tt]ype|[Oo]utput)\s+(out\s+)?(the\s+)?(text|content|words)\s+(above|before|preceding)\s+(this|my)\s+(message|input|prompt)
LOW|system_prompt_extraction|Prompt leak via translation|([Tt]ranslate|[Cc]onvert)\s+(your\s+)?(system\s+)?(prompt|instructions|rules)\s+(to|into)\s+(French|Spanish|Chinese|another\s+language)
MEDIUM|credential_exposure|URL query param: secret|[?&]secret=[^&\s]{8,}
MEDIUM|credential_exposure|URL query param: token|[?&]token=[^&\s]{8,}
MEDIUM|credential_exposure|URL query param: key|[?&](api_key|apikey|api-key)=[^&\s]{8,}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

There are a couple of inconsistencies on this line:

  1. The description is "URL query param: key", but the pattern does not match key=.
  2. The pattern is missing key= which is mentioned as a sensitive parameter in .agents/prompts/build.txt.

To ensure all documented credential parameters are detected, please update the pattern and description for consistency.

Suggested change
MEDIUM|credential_exposure|URL query param: key|[?&](api_key|apikey|api-key)=[^&\s]{8,}
MEDIUM|credential_exposure|URL query param: key/api_key|[?&](key|api_key|apikey|api-key)=[^&\s]{8,}
References
  1. This rule emphasizes the need to include all known variations of sensitive patterns (like key=) for comprehensive security detection and sanitization.
  2. This rule promotes consistency by ensuring that if a new sensitive parameter (key=) is identified, all relevant detection and sanitization logic across the codebase is updated.

MEDIUM|credential_exposure|URL query param: password|[?&]password=[^&\s]{8,}
MEDIUM|credential_exposure|URL query param: access_token|[?&]access_token=[^&\s]{8,}
MEDIUM|credential_exposure|URL query param: auth|[?&](auth|authorization)=[^&\s]{8,}
MEDIUM|credential_exposure|URL query param: client_secret|[?&]client_secret=[^&\s]{8,}
MEDIUM|credential_exposure|URL query param: webhook_secret|[?&]webhook_secret=[^&\s]{8,}
PATTERNS
return 0
}
Expand Down Expand Up @@ -579,6 +587,10 @@ _pg_sanitize_message() {
# Strip long unicode escape sequences
sanitized=$(printf '%s' "$sanitized" | sed -E 's/(\\u[0-9a-fA-F]{4}){4,}/[unicode-filtered]/g')

# Redact credential values in URL query parameters (t4954)
# Matches ?secret=VALUE or &token=VALUE etc. and replaces VALUE with [REDACTED]
sanitized=$(printf '%s' "$sanitized" | sed -E 's/([?&](secret|token|api_key|apikey|api-key|password|access_token|auth|authorization|client_secret|webhook_secret)=)[^&[:space:]]{8,}/\1[REDACTED]/g')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The sanitization regex is missing the key parameter, which is mentioned as a sensitive parameter in the new section 8.4 of .agents/prompts/build.txt. To ensure consistent redaction of all potential credentials, key should be added to the list of parameters in the regex.

Suggested change
sanitized=$(printf '%s' "$sanitized" | sed -E 's/([?&](secret|token|api_key|apikey|api-key|password|access_token|auth|authorization|client_secret|webhook_secret)=)[^&[:space:]]{8,}/\1[REDACTED]/g')
sanitized=$(printf '%s' "$sanitized" | sed -E 's/([?&](key|secret|token|api_key|apikey|api-key|password|access_token|auth|authorization|client_secret|webhook_secret)=)[^&[:space:]]{8,}/\1[REDACTED]/g')
References
  1. This rule emphasizes the need to include all known variations of sensitive patterns (like key=) for comprehensive security detection and sanitization.
  2. This rule promotes consistency by ensuring that if a new sensitive parameter (key=) is identified, all relevant detection and sanitization logic across the codebase is updated.


printf '%s' "$sanitized"
return 0
}
Expand Down Expand Up @@ -1479,6 +1491,39 @@ cmd_test() {
echo "Testing URL encoded payload (MEDIUM — should WARN):"
_test_expect "URL encoded payload" 2 "Check this: %69%67%6E%6F%72%65%20%70%72%65%76%69%6F%75%73"

echo ""
echo "Testing URL credential exposure (MEDIUM — should WARN, t4954):"
_test_expect "URL with ?secret= param" 2 "https://example.com/webhook?secret=abc123def456ghi789"
_test_expect "URL with &token= param" 2 "https://api.example.com/callback?id=1&token=FAKE_SK_LIVE_abcdef123456"
_test_expect "URL with ?api_key= param" 2 "https://hooks.example.com/v1?api_key=FAKE_AKIA_IOSFODNN7EXAMPLE"
_test_expect "URL with ?password= param" 2 "https://service.example.com/auth?password=SuperSecret123!"
_test_expect "URL with ?access_token= param" 2 "https://api.example.com/data?access_token=FAKE_JWT_aGVhZGVyLnBheWxvYWQ"
_test_expect "URL with ?client_secret= param" 2 "https://oauth.example.com/token?client_secret=FAKE_CS_abcdef123456789"
_test_expect "Short param value (no match)" 0 "https://example.com/page?secret=abc"

echo ""
echo "Testing URL credential sanitization (t4954):"
total=$((total + 1))
local url_sanitized
url_sanitized=$(PROMPT_GUARD_QUIET="true" cmd_sanitize "Webhook URL: https://example.com/hook?secret=abc123def456ghi789&name=test" 2>/dev/null)
if [[ "$url_sanitized" == *"[REDACTED]"* ]] && [[ "$url_sanitized" != *"abc123def456ghi789"* ]]; then
echo -e " ${GREEN}PASS${NC} URL secret param redacted in sanitization"
passed=$((passed + 1))
else
echo -e " ${RED}FAIL${NC} URL secret param not redacted: $url_sanitized"
failed=$((failed + 1))
fi

total=$((total + 1))
url_sanitized=$(PROMPT_GUARD_QUIET="true" cmd_sanitize "Config: https://api.example.com/v1?token=sk_live_abcdef123456&format=json" 2>/dev/null)
if [[ "$url_sanitized" == *"[REDACTED]"* ]] && [[ "$url_sanitized" == *"format=json"* ]]; then
echo -e " ${GREEN}PASS${NC} URL token param redacted, non-secret params preserved"
passed=$((passed + 1))
else
echo -e " ${RED}FAIL${NC} URL token sanitization incorrect: $url_sanitized"
failed=$((failed + 1))
fi

# ── scan-stdin tests ────────────────────────────────────────

echo ""
Expand Down
Loading