mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-02 02:42:04 +00:00
security: fix agent-boundary findings from the fourth reviewer
The reviewer that took longest found the most, including one bug that lies
to the user about a security-relevant outcome.
- `agent stop` never sent the capability and never checked the reply. The
daemon refused the shutdown, the CLI printed {"agent":"stopped"} anyway,
and the seed stayed in memory and still served owner-mnemonic. The command
documents itself as wiping held seeds. It now authenticates and reports
what actually happened.
- The owner capability lived at <appDir>/agent.sock.cap -- inside the very
directory a scoped agent must be given to find fingerprints.json and the
socket. A token holder could `unset SEEDPASS_TOKEN` and the CLI would read
the capability on its behalf. It now lives under XDG_RUNTIME_DIR in a 0700
parent, created with wx so a pre-planted file is never adopted. This raises
the bar; it does not make a token a sandbox, and capabilities.ts no longer
claims otherwise.
- Redaction was a three-key denylist over deliberately loose schemas, so
`custom_fields` -- including values Python masks as is_hidden -- went to
any read-scoped token verbatim. It is now an allowlist that recurses into
custom_fields, reporting labels and presence but never values.
- Authorization ran after the vault was decrypted and after the entry
existence check, giving an unauthenticated caller an entry-id enumeration
oracle, no audit record for the misses, and a full PBKDF2 unlock per probe.
Tokens are now checked first, denials are indistinguishable for missing
and forbidden entries, and every denial is audited.
- The audit chain had no anchor: deleting the log, or a suffix of it, left
something that verified perfectly. A signed head now pins the record count
and tail signature. Separately, `loaded` was latched before the read that
could throw, so one transient EACCES made every later append sign from
genesis and broke the chain permanently.
- The exec allowlist -- documented as the only real containment for a `use`
grant -- was bypassable via the clipboard (a session-wide channel) and had
no CLI flag at all, so no shipped token could carry one. Both fixed.
- Unauthenticated connections could buffer without bound, growing to
gigabytes while each chunk rescanned the accumulated string. Line size and
concurrent connections are now capped.
- A token-supplied TOTP timestamp was unbounded, so one use minted codes
valid decades ahead, outliving the token's TTL. Clamped to a small window.
- Prototype keys ("__proto__", "constructor") resolved to inherited
properties, passed constraint checks against undefined values and burned a
use; now an own-property check. The socket was created with the umask mode
and chmod'd afterwards, leaving a connectable window; it is now created
under a restrictive umask. TTL was enforced only when a request arrived, so
an idle agent held the seed indefinitely past expiry; there is now a timer.
220 core + 78 CLI tests green; 36/36 cross-implementation checks green.
Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
ee330c52f7
commit
dafae50ca4
7 files changed
+428
-35
No files matched your search
@@ -77,7 +77,9 @@ the credential itself.
|
||||
|
||||
Append-only, HMAC-chained (`sig = HMAC(key_index, prev_sig || canonical
|
||||
payload)`), keyed by the profile's index key, so only the seed holder can
|
||||
extend or verify it. `agent audit-verify` walks the chain and fails at the
|
||||
extend or verify it. A signed head file records the expected record count
|
||||
and last signature — without it, deleting the log or a suffix of it leaves a
|
||||
shorter file that still verifies perfectly. `agent audit-verify` walks the chain and fails at the
|
||||
first break. Writes are serialized so concurrent requests cannot interleave
|
||||
and desynchronize it. Recorded: unlocks, token lifecycle, index reads,
|
||||
deliveries (with the sink and argv), and every denial with its reason.
|
||||
@@ -85,7 +87,12 @@ Secret values are never recorded.
|
||||
|
||||
## Known limits, stated rather than hidden
|
||||
|
||||
1. **Same-uid attackers win** — see above.
|
||||
1. **Same-uid attackers win** — see above. Concretely: a scoped agent is
|
||||
given the app directory, so the capability is deliberately stored
|
||||
*outside* it (in `XDG_RUNTIME_DIR`), but a token holder that can read that
|
||||
file can still unset its token and act as the owner. Treat a token as a
|
||||
convenience and an audit trail, not as a sandbox. Real containment needs a
|
||||
separate uid or a container.
|
||||
2. **`use` can be turned into `reveal`** by a holder that picks the command,
|
||||
unless an exec allowlist is set.
|
||||
3. **Memory is not scrubbed.** Seeds live in the daemon's heap while
|
||||
|
||||
Reference in new issue
Block a user