feat: warn when a secret-shaped value is found in args instead of env (#1r)
Adds `args_secret_warning(data)` to bcc_core — returns a warning string when any arg positional value looks like a raw credential (token prefix, value following a secret-named flag, or URL with embedded user:pass like postgres://user:pass@host). `--flag=value` inline forms are intentionally skipped (the flag name already labels the value). Adds `secret_warn` QLabel in ServerEditor's stdio page; shown/hidden by `_check_args()` on every field change, and cleared on deselect or stdio→remote type switch. Non-blocking — save path is not touched. Closes #1 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+46
@@ -801,6 +801,10 @@ _TOKEN_PREFIXES = (
|
||||
|
||||
MASK = "••••••••"
|
||||
|
||||
# Matches userinfo credentials embedded in a URL: scheme://user:pass@host
|
||||
# Fires on postgres://user:pass@host but NOT on https://host/path or ssh://user@host.
|
||||
_EMBEDDED_CRED_RE = re.compile(r"://[^:@/\s]+:[^:@/\s]+@")
|
||||
|
||||
|
||||
def is_secret_key(name: str) -> bool:
|
||||
"""Does this env-var / header / flag name look like it holds a secret?"""
|
||||
@@ -841,6 +845,48 @@ def redact_args(args: list[str]) -> list[str]:
|
||||
return out
|
||||
|
||||
|
||||
def args_secret_warning(data: dict) -> str | None:
|
||||
"""
|
||||
Return a warning string when any arg looks like a raw secret that would
|
||||
be better placed in `env`. Returns None when no concern is found.
|
||||
|
||||
Skips --flag=value inline pairs (already partially self-documenting).
|
||||
Fires on:
|
||||
- positional values that start with a well-known token prefix (ghp_, sk-, …)
|
||||
- values that follow a secret-named flag (--token abc, --api-key abc)
|
||||
- URLs with embedded user:pass credentials (postgres://user:pass@host)
|
||||
"""
|
||||
args = [str(a) for a in (data.get("args") or [])]
|
||||
mask_next = False
|
||||
for a in args:
|
||||
if mask_next:
|
||||
mask_next = False
|
||||
if not a.startswith("-"):
|
||||
return (
|
||||
"An arg value following a secret-named flag looks like a credential. "
|
||||
"Where the server supports it, prefer Environment variables — "
|
||||
"args are visible in process listings."
|
||||
)
|
||||
continue
|
||||
# --flag=value inline: skip (the flag name already labels it)
|
||||
if a.startswith("-") and "=" in a:
|
||||
continue
|
||||
# --secretflag (no inline value): flag the next positional arg
|
||||
if a.startswith("-") and is_secret_key(a):
|
||||
mask_next = True
|
||||
continue
|
||||
if a.startswith("-"):
|
||||
continue
|
||||
# Positional value: check for token prefix or embedded URL credentials
|
||||
if _is_secret_value(a) or _EMBEDDED_CRED_RE.search(a):
|
||||
return (
|
||||
"An arg value looks like a credential. "
|
||||
"Where the server supports it, prefer Environment variables — "
|
||||
"args are visible in process listings."
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def split_suspicious_args(args: list[str]) -> tuple[list[str], list[str]]:
|
||||
"""
|
||||
Detect the classic argument-entry mistake: several argv tokens typed on one
|
||||
|
||||
Reference in New Issue
Block a user