Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified src/prophet_cli/__pycache__/__init__.cpython-312.pyc
Binary file not shown.
Binary file modified src/prophet_cli/__pycache__/config.cpython-312.pyc
Binary file not shown.
9 changes: 9 additions & 0 deletions src/prophet_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,16 @@ def _print_doctor(results: list[dict], *, as_json: bool, quiet: bool) -> int:
errors = [r for r in results if r["level"] == "error"]
if as_json:
import json
# codeql[py/clear-text-logging-sensitive-data] -- the tainted value CodeQL follows is the
# secret's NAME (a key of the `secrets` mapping), never its value or its path. See below.
print(json.dumps({"ok": not errors, "results": results}, indent=2))
elif not quiet:
sym = {"ok": "✓", "warn": "!", "error": "✗"}
for r in results:
# codeql[py/clear-text-logging-sensitive-data] -- the flow is the secret's NAME, not its
# value and not its path. A doctor that reports "a secret failed" without saying WHICH
# is useless, so the name must be emitted. `test_doctor_never_echoes_a_secret_path`
# pins that no path or filename ever reaches this output.
print(f" {sym.get(r['level'],'?')} {r['name']}: {r['message']}", file=sys.stderr)
print(("prophet doctor: OK" if not errors else f"prophet doctor: {len(errors)} error(s) — rollout would NOT work as advertised"),
file=sys.stderr)
Expand Down Expand Up @@ -270,6 +276,9 @@ def run_doctor(argv: list[str]) -> int:
# Unlike `doctor`, this is a GENERATOR: stdout here IS the rc file, and the rc file must
# carry the secret *reference* (a path or a cred name) for the shell to resolve at load.
# It still carries no secret VALUE. Redirect it to a file; do not pipe it into a log.
# codeql[py/clear-text-logging-sensitive-data] -- deliberate: stdout here IS the rc file,
# which must carry the secret REFERENCE (path or cred name) for the shell to resolve at
# load. It carries no secret VALUE. Redirect to a file; do not pipe into a log.
sys.stdout.write(emit(cfg, target=a.emit))
return 0
return _print_doctor(doctor(cfg), as_json=a.json, quiet=a.quiet)
Binary file modified tests/__pycache__/test_config.cpython-312-pytest-8.3.5.pyc
Binary file not shown.
6 changes: 3 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ def test_missing_required_key_is_error():
def test_secret_file_existence_and_0600_enforced_on_posix():
with tempfile.TemporaryDirectory() as td:
good = Path(td) / "k.key"; good.write_text("x"); os.chmod(good, 0o600)
bad = Path(td) / "b.key"; bad.write_text("x"); os.chmod(bad, 0o640) # group-readable: fails 0600, not world-readable
bad = Path(td) / "b.key"; bad.write_text("x"); os.chmod(bad, 0o700) # not 0600 -> refused; no group/world bits at all
c = _cfg(secrets={
"good": {"file": str(good)},
"loose": {"file": str(bad)},
"gone": {"file": str(Path(td) / "nope.key")},
})
lv = _levels(config.doctor(c, platform="posix"))
assert lv["secret:good"] == "ok"
assert lv["secret:loose"] == "error" # 0640 → refused (anything but 0600)
assert lv["secret:loose"] == "error" # 0700 → refused (anything but 0600)
assert lv["secret:gone"] == "error" # missing → refused


Expand Down Expand Up @@ -91,7 +91,7 @@ def test_doctor_never_echoes_a_secret_path():
with tempfile.TemporaryDirectory() as td:
secret = Path(td) / "sovereign-root.key"
secret.write_text("x")
os.chmod(secret, 0o640) # wrong mode, so it is reported at all
os.chmod(secret, 0o700) # wrong mode, so it is reported at all
results = config.doctor(_cfg(secrets={"signing_key": {"file": str(secret)}}), platform="posix")
for r in results:
assert td not in r["message"], f"leaked the directory in {r['name']}"
Expand Down
Loading