# -*- coding: utf-8 -*- """Fixture tests for read_credential.py. Run: python3 scripts/test_read_credential.py Every test asserts the EFFECT — the refusal happens, and the message names the reason — not merely that the function ran. Fixtures are temp files; nothing here reads the real credentials file, and no test contains a real secret. """ import os import subprocess import sys import tempfile sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import read_credential as rc # noqa: E402 FAILURES = [] def check(name, cond, detail=""): if cond: print(" OK %s" % name) else: print(" FAIL %s %s" % (name, detail)) FAILURES.append(name) def writefile(body): fd, path = tempfile.mkstemp() with os.fdopen(fd, "w", encoding="utf-8") as fh: fh.write(body) return path # --- E1 — TestR404_CredentialLengthMismatchFailsLoudly ------------------------------------------ # # THE REGRESSION THIS PINS, stated as the thing that actually happened: a session stripped only `"` # from a single-quoted value, sent 15 characters where the password is 13, read the resulting # 200-with-login-page as "the password drifted", and rewrote a live box's password hash. # # RED-PROOF (recorded in REPORT.md): delete the final quote-character assertion in `unwrap` and this # test fails on `quote survives a one-sided strip`. def test_r404_credential_length_mismatch_fails_loudly(): print("E1 TestR404_CredentialLengthMismatchFailsLoudly") # The exact 2026-08-31 shape: single-quoted, and only `"` was stripped by the caller. The reader # must never hand back a value carrying a quote. p = writefile("PASSWORD='abcdefghijklm'\n") check("single-quoted value unwraps to its 13 characters", rc.read(p, "PASSWORD") == "abcdefghijklm") os.unlink(p) # A value that still carries a quote must be REFUSED, not returned. try: rc.unwrap("'abcdefghijklm") check("quote survives a one-sided strip", False, "no refusal was raised") except rc.CredentialError as exc: check("quote survives a one-sided strip", "one side only" in str(exc), str(exc)) try: rc.unwrap("abcdefghijklm'") check("trailing-only quote is refused", False, "no refusal was raised") except rc.CredentialError as exc: check("trailing-only quote is refused", "one side only" in str(exc), str(exc)) # An unquoted value is legitimate and passes through untouched. check("unquoted value passes through", rc.unwrap("abcdefghijklm") == "abcdefghijklm") # Mismatched quote characters are not a pair. try: rc.unwrap("'abcdefghijklm\"") check("mismatched quote pair is refused", False, "no refusal was raised") except rc.CredentialError as exc: check("mismatched quote pair is refused", "one side only" in str(exc), str(exc)) # Empty is refused — an empty password authenticates as nothing and reads as a wrong password. try: rc.unwrap("''") check("empty value is refused", False, "no refusal was raised") except rc.CredentialError as exc: check("empty value is refused", "empty" in str(exc), str(exc)) # The caller's second opinion: --expect-length refuses a value of the wrong size BEFORE use. p = writefile("PASSWORD='abcdefghijklm'\n") out = tempfile.mkstemp()[1] rcode = rc.main([ "PASSWORD", out, "--credentials", p, "--expect-length", "15" ]) check("--expect-length 15 is REFUSED for a 13-character value", rcode == 2, "rc=%s" % rcode) rcode = rc.main([ "PASSWORD", out, "--credentials", p, "--expect-length", "13" ]) check("--expect-length 13 is accepted", rcode == 0, "rc=%s" % rcode) with open(out, encoding="utf-8") as fh: check("the value reached the file", fh.read() == "abcdefghijklm") check("the file is 0600", oct(os.stat(out).st_mode & 0o777) == "0o600") os.unlink(p) os.unlink(out) # A missing key is a refusal, not an empty string. p = writefile("OTHER='x'\n") try: rc.read(p, "PASSWORD") check("missing key is refused", False, "no refusal was raised") except rc.CredentialError as exc: check("missing key is refused", "not present" in str(exc), str(exc)) os.unlink(p) # --- the value must never reach stdout ------------------------------------------------------------ def test_the_value_is_never_printed(): print("TestR404_TheValueIsNeverPrinted") p = writefile("PASSWORD='swordfish1234'\n") out = tempfile.mkstemp()[1] res = subprocess.run( [sys.executable, os.path.join(os.path.dirname(os.path.abspath(__file__)), "read_credential.py"), "PASSWORD", out, "--credentials", p], capture_output=True, text=True) combined = res.stdout + res.stderr check("exit 0", res.returncode == 0, combined) # POSITIVE CONTROL first: the grep can find the secret when it IS there. A "not found" from a # search that cannot find anything is not a measurement. check("positive control — the search finds a planted copy", "swordfish1234" in (combined + "swordfish1234")) check("the secret is NOT in stdout/stderr", "swordfish1234" not in combined, combined) check("the length IS reported", "13 characters" in res.stdout, res.stdout) os.unlink(p) os.unlink(out) if __name__ == "__main__": test_r404_credential_length_mismatch_fails_loudly() test_the_value_is_never_printed() if FAILURES: print("\nFAILED: %d" % len(FAILURES)) sys.exit(1) print("\nread_credential tests OK")