diff --git a/.agents/configs/prompt-injection-patterns.yaml b/.agents/configs/prompt-injection-patterns.yaml index b66e893d1..1db9e5a67 100644 --- a/.agents/configs/prompt-injection-patterns.yaml +++ b/.agents/configs/prompt-injection-patterns.yaml @@ -633,8 +633,8 @@ credential_exposure: pattern: '[?&]token=[^&\s]{8,}' - severity: MEDIUM - description: "URL query param: api_key/apikey" - pattern: '[?&](api_key|apikey|api-key)=[^&\s]{8,}' + description: "URL query param: key/api_key/apikey" + pattern: '[?&](key|api_key|apikey|api-key)=[^&\s]{8,}' - severity: MEDIUM description: "URL query param: password" diff --git a/.agents/scripts/prompt-guard-helper.sh b/.agents/scripts/prompt-guard-helper.sh index a13642a1e..6134e7fc6 100755 --- a/.agents/scripts/prompt-guard-helper.sh +++ b/.agents/scripts/prompt-guard-helper.sh @@ -329,7 +329,7 @@ LOW|system_prompt_extraction|System prompt extraction variant|([Ww]rite|[Tt]ype| 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,} +MEDIUM|credential_exposure|URL query param: key/api_key|[?&](key|api_key|apikey|api-key)=[^&\s]{8,} 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,} @@ -589,7 +589,7 @@ _pg_sanitize_message() { # 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') + 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') printf '%s' "$sanitized" return 0 @@ -1493,20 +1493,23 @@ cmd_test() { 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 values use PLACEHOLDER_ prefix to avoid secret-scanner false positives (GH#4959). + # Real secrets must never appear in test literals — use env vars or secret managers. + _test_expect "URL with ?secret= param" 2 "https://example.com/webhook?secret=PLACEHOLDER_SECRET_VALUE_123456" + _test_expect "URL with &token= param" 2 "https://api.example.com/callback?id=1&token=PLACEHOLDER_TOKEN_VALUE_123456" + _test_expect "URL with ?api_key= param" 2 "https://hooks.example.com/v1?api_key=PLACEHOLDER_APIKEY_VALUE_123456" + _test_expect "URL with ?password= param" 2 "https://service.example.com/auth?password=PLACEHOLDER_PASSWORD_VALUE_123" + _test_expect "URL with ?access_token= param" 2 "https://api.example.com/data?access_token=PLACEHOLDER_ACCESS_TOKEN_123456" + _test_expect "URL with ?client_secret= param" 2 "https://oauth.example.com/token?client_secret=PLACEHOLDER_CLIENT_SECRET_123" + _test_expect "URL with ?key= param" 2 "https://example.com/api?key=PLACEHOLDER_KEY_VALUE_12345678" _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 + url_sanitized=$(PROMPT_GUARD_QUIET="true" cmd_sanitize "Webhook URL: https://example.com/hook?secret=PLACEHOLDER_SECRET_VALUE_123456&name=test" 2>/dev/null) + if [[ "$url_sanitized" == *"[REDACTED]"* ]] && [[ "$url_sanitized" != *"PLACEHOLDER_SECRET_VALUE_123456"* ]]; then echo -e " ${GREEN}PASS${NC} URL secret param redacted in sanitization" passed=$((passed + 1)) else @@ -1515,7 +1518,7 @@ cmd_test() { 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) + url_sanitized=$(PROMPT_GUARD_QUIET="true" cmd_sanitize "Config: https://api.example.com/v1?token=PLACEHOLDER_TOKEN_VALUE_123456&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))