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).
This commit is contained in:
+28
-5
@@ -29,6 +29,19 @@ import urllib.request
|
|||||||
SIGNIN = "/home/super/Projects/NetVM/bin/muse-signin.py"
|
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():
|
def _load_registry():
|
||||||
path = "/home/super/Projects/NetVM/bin/netvm-registry.py"
|
path = "/home/super/Projects/NetVM/bin/netvm-registry.py"
|
||||||
spec = importlib.util.spec_from_file_location("netvm_registry", path)
|
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)
|
print("ERROR: no code on stdin", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
cmd += ["--otp", code]
|
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:
|
# Propagate the signin script's contract:
|
||||||
# 0 = done, 2 = OTP prompt reached, 3 = NEEDS_HUMAN, 4 = NEEDS_SIGNUP
|
# 0 = done, 2 = OTP prompt reached, 3 = NEEDS_HUMAN, 4 = NEEDS_SIGNUP.
|
||||||
sys.stdout.write(r.stdout)
|
# Scrub: the signin script echoes the identifier/code in progress
|
||||||
sys.stderr.write(r.stderr)
|
# 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:
|
if r.returncode == 4:
|
||||||
log_entry = {
|
log_entry = {
|
||||||
@@ -105,7 +128,7 @@ def main():
|
|||||||
"type": "onboarding_needs_signup",
|
"type": "onboarding_needs_signup",
|
||||||
"node": args.node,
|
"node": args.node,
|
||||||
"service": args.service,
|
"service": args.service,
|
||||||
"email": identifier,
|
"email_masked": _mask_email(identifier),
|
||||||
"status": "needs_signup",
|
"status": "needs_signup",
|
||||||
"action_required": "ask_client_to_sign_up",
|
"action_required": "ask_client_to_sign_up",
|
||||||
"signup_url": "https://muse.ai"
|
"signup_url": "https://muse.ai"
|
||||||
|
|||||||
Reference in New Issue
Block a user