fix(hooks): exported vars judged only by inherited value
ci / nix (pull_request) Successful in 9s
ci / lint (pull_request) Successful in 13s

Raw assignment can hide a short resolved secret or read as a
reference when the exported value is the real literal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-08-21 19:20:31 +01:00
co-authored by Claude Fable 5
parent e06cbe4217
commit d193df0403
+16 -17
View File
@@ -70,23 +70,22 @@ def env_secrets():
val = val[1:-1]
if name in ALLOW_NAMES:
continue
# The inherited environment resolves $-expansions; the raw literal
# is the fallback for values the shell never exported. A literal
# that is nothing but a $-reference matches the referencing style
# itself, so it is never a candidate.
resolved = os.environ.get(name) or val
candidates = {os.environ.get(name, ""), val} - {
v for v in (val,)
if re.fullmatch(r"\$\{?[A-Za-z_][A-Za-z0-9_]*\}?", v)
}
guarded = False
for v in candidates:
if (len(v) < MIN_LEN or v in ALLOWLIST or plain_url(v)
or v.startswith(("/", "~"))):
continue
out.setdefault(name, []).append(v)
guarded = True
if not guarded and 0 < len(resolved) < MIN_LEN:
# An exported variable is judged only by its inherited value — the
# raw assignment may expand to something else entirely. The raw
# literal counts only for never-exported lines, and one that is
# nothing but a $-reference matches the referencing style itself,
# so it is skipped.
if name in os.environ:
resolved = os.environ[name]
elif re.fullmatch(r"\$\{?[A-Za-z_][A-Za-z0-9_]*\}?", val):
continue
else:
resolved = val
if (len(resolved) >= MIN_LEN and resolved not in ALLOWLIST
and not plain_url(resolved)
and not resolved.startswith(("/", "~"))):
out.setdefault(name, []).append(resolved)
elif 0 < len(resolved) < MIN_LEN:
unguardable.append(name)
return out, unguardable