protui is an unofficial front-end for the official Proton Pass CLI. It handles SSH keys, so the security properties below are the point of the project rather than a footnote.
Please report privately, not in a public issue.
Use GitHub's private vulnerability reporting — the Report a vulnerability button under the repository's Security tab. It opens a channel visible only to the maintainers.
Useful things to include: what you did, what happened, what you expected, the
protui and pass-cli versions (protui --version, pass-cli --version),
your OS and terminal emulator. A failing test or a reproduction against a
throwaway vault is ideal, but a clear description is enough.
This is a personal project maintained in spare time, so there is no guaranteed response window. Reports are taken seriously and acknowledged as soon as practical.
Anything in this repository:
- Private key material reaching protui, being displayed, logged or written to disk.
- Secrets appearing in process arguments, which any local user can read.
- Untrusted text from Proton Pass reaching the terminal as instructions rather than as data.
- Argument construction that lets a value be interpreted as a flag.
- Anything that causes protui to act on the wrong item, particularly the permanent-delete path.
protui does not talk to Proton's servers, implement any cryptography, or hold
any credential. It shells out to pass-cli for everything.
- Vulnerabilities in
pass-cliitself belong to protonpass/pass-cli. - Vulnerabilities in Proton Pass, the service or its clients, belong to Proton's bug bounty programme.
If a pass-cli weakness is only reachable because of how protui calls it,
that is in scope here — please report it.
protui's guarantees, and where they are enforced:
| Guarantee | Enforced in |
|---|---|
| No private key material ever enters the process | src/passcli/item.rs |
| No secret is ever a command-line argument | src/passcli/item.rs |
| No value can be read as a flag | src/passcli/item.rs |
| No passphrase is inherited from the shell | src/passcli/client.rs |
Only one module may run pass-cli |
src/passcli |
| Text from Proton Pass is sanitised before display | src/keys/sanitize.rs |
No unsafe code |
Cargo.toml (forbid) |
The reasoning behind each, including what was rejected, is in
docs/adr/ — particularly
read public keys with a field-scoped view,
pass passphrases through the environment
and
sanitize text before drawing it.
Every guarantee has tests asserting it, including that no constructed argument
list contains the word private, and that hostile item titles cannot emit
terminal escape sequences.
cargo-deny checks the dependency tree against the RustSec advisory database
in CI on every push and pull request, and clippy runs with every warning treated
as an error.
These are accepted trade-offs rather than oversights, and are documented so you can judge them yourself:
- A passphrase cannot be fully wiped from memory. It is passed to
pass-clithrough the child environment, never argv. protui keeps it in one buffer, allocated at full size before the first keystroke so that typing never leaves a stale copy behind, and zeroes that buffer once the child has started. The standard library builds its own copy of the child's environment while spawning, which protui cannot reach to wipe; it is freed, unzeroed, as soon as the child has started. It is never written to argv, disk, or logs. - A child process environment is not private from root or from the same
user. On Linux it is readable at
/proc/<pid>/environ. The environment is the strongest mechanismpass-clioffers for this, and it is the one upstream documents. - protui trusts
pass-cliand the session it holds. It does not verify the binary beyond resolving it onPATH, so aPATHan attacker controls is apass-clian attacker controls. - The clipboard is not protui's to protect. A copied public key is not
secret, but it stays on the clipboard until something replaces it. Copying
runs the platform's clipboard tool (
pbcopy,wl-copy,xcliporxsel), resolved onPATHlikepass-cliis. - Supported versions: protui is pre-release and unversioned. Fixes land on
main; there are no backports.