Require biometric auth and interactive TTY for wallet export - #6
Conversation
The wallet export command previously printed the seed phrase unconditionally, allowing non-interactive callers (including AI agents) to silently read it. Guard the command with layered human-presence checks: - Refuse to run unless stdin and stdout are real TTYs, blocking piping, redirection, command substitution, and non-interactive execution. - Require OS biometric authentication when available: Touch ID via LocalAuthentication (macOS), fprintd fingerprint verify (Linux), and Windows Hello via UserConsentVerifier (Windows). - Fall back to a random one-time code typed back within a time limit when biometrics are unavailable. Biometric availability is detected per platform; enrolling-less users fall back to the code challenge rather than being locked out.
Review: Require biometric auth and interactive TTY for wallet exportOverall AssessmentApprove with minor suggestions. This is a well-scoped, security-focused change that meaningfully reduces the attack surface for automated seed-phrase exfiltration. The design is fail-closed, introduces no new npm dependencies, and the code is clean and idiomatic for the repo. What changed
Security Review
Suggestions (non-blocking)
Build / TestsI was unable to run VerdictLGTM — please land after addressing the |
Address PR review feedback: - Wrap os.userInfo() in verifyFprintd in try/catch so systems without a passwd entry (containers, CI, minimal systemd units) fall back to the typed challenge instead of crashing. - Extract pure, testable helpers (assertInteractiveTerminal, classifyBiometricOutput, generateChallenge, isChallengeAnswerCorrect) and have the TTY gate throw instead of calling process.exit; the action handler catches and exits, preserving CLI behavior. - Check the biometric failure token before the success token so a failure always wins on a mixed stream. - Document in README that wallet export requires an interactive terminal and biometric authentication, so it cannot be used from scripts or pipes. - Add unit tests covering the security-critical helpers.
|
Approve. This is a well-scoped, security-focused change and the follow-up revision addresses all prior review feedback. What changed
Pure helper functions ( Verification
Security notes
Minor observation (non-blocking)
Verdict: LGTM. Land it. |


Summary
paytaca wallet exportpreviously printed the stored seed phrase unconditionally. Any non-interactive caller — including AI agents and background processes — could run it and silently capture the seed. This change gates the command behind layered human-presence checks so the seed can only be obtained by a live person at an interactive terminal.Threat model
An automated caller typically executes commands without a TTY and can read stdout. Even when it allocates a pseudo-terminal (e.g.
expect, Pythonpty), a terminal-only challenge is scriptable. The only robust confirmation is one the caller's process cannot drive: an OS biometric prompt with a physical presence requirement.Changes
src/commands/wallet.tsonly (plus version bump).Interactive TTY gate (
requireInteractiveTerminal)Refuses unless both
process.stdin.isTTYandprocess.stdout.isTTYare set. Blocks piping (echo ... | paytaca), output redirection (> file), command substitution ($(...)), and all non-interactive agent/plugin execution.OS biometric authentication (
verifyBiometric), when available:LocalAuthentication, driven through the systemosascriptJXA/ObjC bridge. No native addon ornode-gyp.fprintd-verify(fingerprint reader over D-Bus). Availability confirmed viafprintd-listenrollment check so un-enrolled users are not locked out.UserConsentVerifier, invoked through Windows PowerShell.Typed-code fallback (
promptChallenge)When biometrics are unavailable, a random one-time code must be typed back within 120 seconds. No
--yes/--jsonbypass exists.Biometric availability is detected per platform. A denied/cancelled biometric prompt is fail-closed (no fallback); the code challenge is only offered when biometrics are genuinely unavailable.
Testing
npm run build(type-check) passes.npm test— 104/104 tests pass.LocalAuthenticationbridge end-to-end: async Touch ID callback fires and resolves on success.Notes / caveats
fprintd-verifycannot distinguish "reader absent" from "wrong finger"; both are treated as denied once enrollment is confirmed.Version
0.5.2→0.6.0.