|
7 | 7 | from hashlib import sha256 |
8 | 8 | import json |
9 | 9 | from pathlib import Path, PurePosixPath |
| 10 | +import re |
10 | 11 | from types import MappingProxyType |
11 | 12 | from typing import Any, Mapping |
12 | 13 | from urllib.parse import urlparse |
@@ -72,15 +73,26 @@ def _safe_release_directory(value: str) -> bool: |
72 | 73 |
|
73 | 74 |
|
74 | 75 | def _forbidden_nested(value: Any) -> bool: |
75 | | - forbidden = ("credential", "password", "secret", "token", "authorization", "reasoning", "thinking", "chain_of_thought") |
| 76 | + # Keys, rather than ordinary domain prose, are controlled. In particular |
| 77 | + # legitimate limitations may mention future human authorization without |
| 78 | + # becoming a credential-bearing artifact. |
| 79 | + forbidden = frozenset({"credential", "credentials", "password", "secret", "token", "api_key", "apikey", "access_key", "access_token", "private_key", "reasoning", "hidden_reasoning", "thinking", "chain_of_thought"}) |
76 | 80 | if isinstance(value, Mapping): |
77 | 81 | return any( |
78 | | - _forbidden_nested(str(key)) or _forbidden_nested(item) |
| 82 | + str(key).casefold() in forbidden or _forbidden_nested(item) |
79 | 83 | for key, item in value.items() |
80 | 84 | ) |
81 | 85 | if isinstance(value, (tuple, list)): |
82 | 86 | return any(_forbidden_nested(item) for item in value) |
83 | | - return isinstance(value, str) and any(marker in value.casefold() for marker in forbidden) |
| 87 | + if not isinstance(value, str): |
| 88 | + return False |
| 89 | + # Detect structured secret/reasoning disclosures, while allowing normal |
| 90 | + # domain language such as "future authorization is required". |
| 91 | + return bool(re.search( |
| 92 | + r"(?:authorization\s*:\s*bearer\b|(?:secret|token|password|api[_-]?key|access[_-]?token)\s*[:=]|(?:chain_of_thought|hidden_reasoning)\s*:|-----BEGIN(?: [A-Z]+)? PRIVATE KEY-----)", |
| 93 | + value, |
| 94 | + flags=re.IGNORECASE, |
| 95 | + )) |
84 | 96 |
|
85 | 97 |
|
86 | 98 | def _require(condition: bool, message: str) -> None: |
|
0 commit comments