Files

1 line
3.9 KiB
JSON

{"repo": "agentwasp/agentwasp", "files": {"containers/agent-core/src/utils/redaction.py": {"4f1d4e522b625985cc8f6219b4101c2228a39615": {"t": 1785674426.6277742, "n": 3587, "h": "6fb98db55ff0090a", "c": "\"\"\"Secret Redaction — automatically scrubs API keys and secrets from logs.\n\nApplied to all text before it is written to audit_log or any observability sink.\nCovers known API key formats for all supported providers.\n\"\"\"\nfrom __future__ import annotations\n\nimport re\n\n# Each tuple: (pattern, replacement_label)\n_PATTERNS: list[tuple[re.Pattern, str]] = [\n # OpenAI — sk-proj-... (new format) and sk-... (classic)\n (re.compile(r'\\bsk-proj-[A-Za-z0-9_-]{20,}\\b'), 'sk-proj-***REDACTED***'),\n (re.compile(r'\\bsk-[A-Za-z0-9]{20,}\\b'), 'sk-***REDACTED***'),\n # OAuth / OIDC tokens commonly copied from browser or CLI sessions.\n (re.compile(r'\\beyJ[A-Za-z0-9_-]{20,}\\.[A-Za-z0-9_-]{20,}\\.[A-Za-z0-9_-]{20,}\\b'), 'jwt-***REDACTED***'),\n (re.compile(r'(?i)(?:oauth[-_]?token|refresh[-_]?token|session[-_]?token|id[-_]?token)[\"\\s:=]+[A-Za-z0-9._\\-]{20,}'),\n 'oauth_token=***REDACTED***'),\n # Anthropic — sk-ant-api03-...\n (re.compile(r'\\bsk-ant-[A-Za-z0-9_-]{20,}\\b'), 'sk-ant-***REDACTED***'),\n # Google AI / Gemini — AIza... (real keys are AIza + 33-39 alphanumeric chars)\n (re.compile(r'\\bAIza[A-Za-z0-9_-]{25,}\\b'), 'AIza***REDACTED***'),\n # xAI / Grok\n (re.compile(r'\\bxai-[A-Za-z0-9_-]{20,}\\b'), 'xai-***REDACTED***'),\n # Moonshot / Kimi\n (re.compile(r'\\bsk-[A-Za-z0-9]{40,}\\b'), 'sk-***REDACTED***'),\n # HuggingFace — hf_...\n (re.compile(r'\\bhf_[A-Za-z0-9]{20,}\\b'), 'hf_***REDACTED***'),\n # Generic Bearer tokens in HTTP headers/text\n (re.compile(r'(?i)Bearer\\s+[A-Za-z0-9._\\-]{20,}'), 'Bearer ***REDACTED***'),\n # Authorization header values\n (re.compile(r'(?i)(?:Authorization|api[-_]?key|apikey|api[-_]?token|access[-_]?token)[\"\\s:=]+[A-Za-z0-9._\\-]{20,}'),\n 'Authorization: ***REDACTED***'),\n # Passwords in key=value pairs (common in CLI output / config)\n (re.compile(r'(?i)(?:password|passwd|pwd|secret)[\"\\s:=]+\\S{8,}'), 'password=***REDACTED***'),\n # Generic long random-looking strings that appear after \"key\" keyword\n (re.compile(r'(?i)\\b(?:key|token|secret)[\"\\s:=]+[A-Za-z0-9+/=_\\-]{32,}\\b'), 'key=***REDACTED***'),\n # AWS access key IDs (AKIA...) — standard format is AKIA + 16 uppercase alphanumeric\n (re.compile(r'\\bAKIA[A-Z0-9]{12,}\\b'), 'AKIA***REDACTED***'),\n (re.compile(r'(?i)aws[_\\-]?secret[_\\-]?(?:access[_\\-]?)?key[\"\\s:=]+[A-Za-z0-9+/]{40}\\b'), 'aws_secret=***REDACTED***'),\n # Stripe keys\n (re.compile(r'\\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{24,}\\b'), 'stripe_***REDACTED***'),\n # Slack bot/user tokens\n (re.compile(r'\\bxox[bposa]-[A-Za-z0-9\\-]{10,}\\b'), 'xox***REDACTED***'),\n # SendGrid API keys\n (re.compile(r'\\bSG\\.[A-Za-z0-9_\\-]{22,}\\.[A-Za-z0-9_\\-]{43,}\\b'), 'SG.***REDACTED***'),\n]\n\n# Minimum text length to bother scanning (avoids overhead on very short strings)\n_MIN_LEN = 10\n\n\ndef redact(text: str) -> str:\n \"\"\"Scrub known secret patterns from text. Returns sanitized string.\"\"\"\n if not text or len(text) < _MIN_LEN:\n return text\n for pattern, replacement in _PATTERNS:\n text = pattern.sub(replacement, text)\n return text\n\n\ndef redact_dict(data: dict, keys: list[str] | None = None) -> dict:\n \"\"\"Redact secrets from string values in a dict. Optionally target specific keys.\"\"\"\n result = {}\n for k, v in data.items():\n if isinstance(v, str):\n if keys is None or k in keys:\n result[k] = redact(v)\n else:\n result[k] = v\n elif isinstance(v, dict):\n result[k] = redact_dict(v, keys)\n else:\n result[k] = v\n return result\n"}}}}