From 86da8b55397981ee9dcf29e59adfd33a344fdcf0 Mon Sep 17 00:00:00 2001 From: cred-driver Date: Sun, 4 Oct 2026 17:48:54 +0000 Subject: [PATCH] onboard-driver: scrub secrets from signin output passthrough - Redact identifier/code (>=4 chars) from muse-signin.py stdout/stderr before passthrough (it echoes OTP input). - Catch TimeoutExpired: its str() includes argv with --email/--otp; print generic timeout instead. - Log email_masked instead of raw email to job-log.jsonl (rc=4 branch). --- bin/onboard-driver.py | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/bin/onboard-driver.py b/bin/onboard-driver.py index 264645f..b65eb60 100755 --- a/bin/onboard-driver.py +++ b/bin/onboard-driver.py @@ -29,6 +29,19 @@ import urllib.request SIGNIN = "/home/super/Projects/NetVM/bin/muse-signin.py" +def _scrub(text, *secrets): + """Redact secret values from captured output before passthrough.""" + for s in secrets: + if s and len(s) >= 4: + text = text.replace(s, "[redacted]") + return text + + +def _mask_email(identifier): + local, _, domain = identifier.partition("@") + return (local[:1] + "***@" + domain) if domain else "***" + + def _load_registry(): path = "/home/super/Projects/NetVM/bin/netvm-registry.py" spec = importlib.util.spec_from_file_location("netvm_registry", path) @@ -93,11 +106,21 @@ def main(): print("ERROR: no code on stdin", file=sys.stderr) return 1 cmd += ["--otp", code] - r = subprocess.run(cmd, capture_output=True, text=True, timeout=220) + try: + r = subprocess.run(cmd, capture_output=True, text=True, + timeout=220) + except subprocess.TimeoutExpired: + # NB: TimeoutExpired str() includes the argv (with secrets) - + # never let it reach stderr uncaught. + print("ERROR: signin step timed out", file=sys.stderr) + return 1 # Propagate the signin script's contract: - # 0 = done, 2 = OTP prompt reached, 3 = NEEDS_HUMAN, 4 = NEEDS_SIGNUP - sys.stdout.write(r.stdout) - sys.stderr.write(r.stderr) + # 0 = done, 2 = OTP prompt reached, 3 = NEEDS_HUMAN, 4 = NEEDS_SIGNUP. + # Scrub: the signin script echoes the identifier/code in progress + # output; redact before passthrough (server scrubs too, defense + # in depth). + sys.stdout.write(_scrub(r.stdout, identifier, code)) + sys.stderr.write(_scrub(r.stderr, identifier, code)) if r.returncode == 4: log_entry = { @@ -105,7 +128,7 @@ def main(): "type": "onboarding_needs_signup", "node": args.node, "service": args.service, - "email": identifier, + "email_masked": _mask_email(identifier), "status": "needs_signup", "action_required": "ask_client_to_sign_up", "signup_url": "https://muse.ai"