Implement TH-07 password ingress channels for CLI: - --password-stdin flag (reads one line from stdin) - PDFTRACT_PASSWORD env var - --password VALUE (rejected unless PDFTRACT_INSECURE_CLI_PASSWORD=1) Exit code 64 for insecure password usage with stderr hint. Stderr warning emitted when --password VALUE accepted via opt-in. Priority order: stdin > env var > value (opt-in) > none. Empty password (bare newline) treated as no password. Acceptance criteria: - --password-stdin: PASS - PDFTRACT_PASSWORD: PASS - --password VALUE rejection (exit 64): PASS - Stderr warning on opt-in: PASS - Exit codes: PASS - Python/MCP/Serve: N/A (crates don't exist yet) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
3.6 KiB
3.6 KiB
pdftract-2ka7: Password Ingress Channels
Summary
Implemented secure password ingress channels for PDF password handling in the CLI.
Implementation Status
CLI (pdftract-cli) ✅ PASS
File: crates/pdftract-cli/src/password.rs
Implemented the complete password resolution logic with priority order:
--password-stdinflag (reads one line from stdin)PDFTRACT_PASSWORDenv var--password VALUE(only ifPDFTRACT_INSECURE_CLI_PASSWORD=1)- None
File: crates/pdftract-cli/src/main.rs
Wired the password resolution into the Extract command with proper error handling.
Acceptance Criteria
| Criterion | Status | Notes |
|---|---|---|
--password-stdin flag implemented |
✅ PASS | Reads one newline-terminated line from stdin; empty password = no password |
PDFTRACT_PASSWORD env var |
✅ PASS | Read when present and --password-stdin not supplied |
--password VALUE rejected (exit 64) |
✅ PASS | Requires PDFTRACT_INSECURE_CLI_PASSWORD=1 |
| Stderr warning on opt-in | ✅ PASS | Emits warning when --password VALUE accepted |
Python password= kwarg |
⚠️ N/A | pdftract-py crate doesn't exist yet |
| MCP password body field | ⚠️ N/A | pdftract-mcp crate doesn't exist yet |
| Serve password form field | ⚠️ N/A | pdftract-serve crate doesn't exist yet |
| Exit codes match policy | ✅ PASS | Exit code 64 for usage errors |
| TH-07 test passes | ⚠️ WARN | Separate bead (not implemented yet) |
Test Results
Unit Tests
running 8 tests
test password::tests::test_resolve_password_empty_env_var ... ok
test password::tests::test_resolve_password_opt_in_two_not_accepted ... ok
test password::tests::test_resolve_password_value_rejected_without_opt_in ... ok
test password::tests::test_resolve_password_opt_in_zero_not_accepted ... ok
test password::tests::test_resolve_password_stdin_takes_priority ... ok
test password::tests::test_resolve_password_value_accepted_with_opt_in ... ok
test password::tests::test_resolve_password_env_var ... ok
test password::tests::test_resolve_password_none ... ok
test result: ok. 8 passed; 0 failed
Integration Tests
# Test --password-stdin
$ echo "testpassword" | ./target/release/pdftract extract --password-stdin -
Password provided via secure channel
# ✅ PASS
# Test PDFTRACT_PASSWORD
$ PDFTRACT_PASSWORD="envpass" ./target/release/pdftract extract -
Password provided via secure channel
# ✅ PASS
# Test --password VALUE rejected (no opt-in)
$ ./target/release/pdftract extract --password secret -
Error: --password VALUE is insecure and rejected by default...
Exit code: 64
# ✅ PASS
# Test --password VALUE with opt-in (warning)
$ PDFTRACT_INSECURE_CLI_PASSWORD=1 ./target/release/pdftract extract --password secret -
WARNING: --password VALUE is insecure (visible via 'ps aux')...
Password provided via secure channel
# ✅ PASS
Implementation Notes
Stdin Discipline
- When
--password-stdinis set with-(stdin input), the first line is the password and the rest is the PDF bytes - Newline stripping: trims trailing
\nor\r\nonly; preserves leading/trailing whitespace - Empty password (bare newline) is treated as "no password"
Future Work
The following will be implemented in future beads when the respective crates are created:
pdftract-py/src/lib.rs: PyO3password=kwarg toSecretStringpdftract-mcp/src/handlers/extract.rs: Password parameter in request bodypdftract-serve/src/routes/extract.rs: Multipart form field for password
References
- Plan: line 878 (TH-07 mitigation), line 902-913 (Secrets Handling), line 949 (NEVER log passwords)