fix: close secret-scanner bypasses, harden state durability and the apply lock#20
Merged
Merged
Conversation
e505a72 to
485f1a9
Compare
…pply lock
Resolves 16 verified findings from a multi-agent security + correctness review
(each adversarially verified). The remaining 6 are deferred with rationale
(same-user-racer TOCTOU, a design-scope capture change, and informational items).
Secret scanner (ferry's gate against committing credentials to the shared repo):
- Capture the WHOLE quoted value, not just its first whitespace-free run, so a
quoted multi-word passphrase no longer slips through (was HIGH).
- Scan every line of a multi-line opaque value (ScanValue), not only the first —
the ^-anchored detectors were dead past line 1.
- Narrow the placeholder exemptions: only genuine ${VAR}/$(cmd)/whole-value $VAR
references and <...> templates are exempt, not any $-prefixed or <-containing
literal.
- Expand credKeyword (pass/passphrase/pwd/credentials/*_key) with a path-value
guard so *_file=/path keys don't false-positive.
- Detect Authorization: Bearer/Basic header tokens in generic dotfiles.
- Scan binary bundle members for provider tokens too (secret.HasBinarySecret),
symmetric on export and import.
Durability / crash-safety:
- Route journal/snapshot backup blobs through the fsync'd AtomicWrite so a crash
can't roll a truncated backup over an intact file.
- fsync the dotfile/agents state writers and the statefile migration backup;
treat a zero-length state file as empty on read instead of hard-failing.
Concurrency:
- Publish the apply lock atomically (temp + fsync + hardlink) so a concurrent
acquirer can't reclaim a live owner's half-written lock and run a second apply.
Correctness / hardening:
- Preserve the live file's trailing-newline shape when composing a capture, so a
file lacking a final newline no longer wedges in a permanent conflict.
- sync: propagate an ahead/behind computation failure and roll back instead of
silently skipping remote integration.
- Harden the init-time GitHub push (protocol.ext.allow=never, hooks/fsmonitor/ssh
neutralised) like every other ferry git call.
- Symlink-harden custom `bundle import --out` targets like the default.
New: `ferry restore --undo <snapshot-id>` makes the pre-restore snapshot the
output has always advertised actually reachable.
Also updates the AGENTS.md attribution policy: AI-assisted commits now carry an
Assisted-by trailer (kernel format; human remains author of record).
Docs regenerated; CHANGELOG updated; regression tests added for each scanner fix
and the capture-newline fix.
Assisted-by: Claude:claude-fable-5
485f1a9 to
c3185aa
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves 16 of 22 verified findings from a multi-agent security + correctness review of ferry (6 parallel finders → each finding adversarially verified by an independent skeptic; 3 finder findings were pruned as false positives). The remaining 6 are deferred with documented rationale.
Headline: ferry's path-containment,
~/.sshguard, and archive-extraction code are genuinely hardened — the review found no RCE, no zip-slip, no out-of-$HOMEwrite. The real security surface is the secret scanner, ferry's single automated control keeping plaintext credentials out of the shared, pushed repo. This PR closes a cluster of scanner bypasses plus a set of durability, concurrency, and correctness bugs.Security — secret-scanner bypasses
Each let a real credential reach the committed repo:
password = "correct horse battery") were scanned only up to the first space (was HIGH). The whole quoted value is now scanned.ScanValue, e.g. adefaults exportplist blob): the^-anchored detectors were dead past line 1. Now every line is scanned.$or containing an interior</...was exempted. Only genuine${VAR}/$(cmd)/ whole-value$VARreferences and<...>templates are now exempt.pass,passphrase,pwd,credentials,*_key) are now recognised, with a path-value guard so*_file = /pathkeys don't false-positive.Authorization: Bearer/Basicheader tokens in generic dotfiles are now detected.Security — concurrency & transport
applyin parallel.protocol.ext.allow=never, hooks/fsmonitor/ssh neutralised) like every other ferry git call, so an ambientpushInsteadOfrewrite can't redirect it.Fixed — durability & correctness
ferry syncrolls back on an ahead/behind computation failure instead of silently skipping remote integration.bundle import --outtargets are symlink-hardened like the default.Added
ferry restore --undo <snapshot-id>makes the pre-restore snapshot the restore output has always advertised actually reachable; the post-restore message now prints the exact command.Deferred (6, with rationale)
F03 overlay-shadowed agents-capture revert (needs a routing rework + dedicated tests; the file is backed up and
ferry restore-able), F10 whole-file blocked-capture (design scope; recoverable pre-commit), F15/F16 residual parent-swap TOCTOU (same-user racer outside the threat model; documented; needs anopenat(O_NOFOLLOW)rearchitecture), F17 bounded in-memory member buffer (by design), F22$PATHhelper resolution (reviewed sound — only the root apt rail executes privileged, and it is already resolved through a sanitized fixed dir list).Testing
make build,gofmt -l .,go vet ./..., the fullgo test ./...suite including evals (FERRY_BINset), and-raceon the changed packages are all green. Regression tests were added for every scanner fix and the capture-newline fix; one eval git stub was made robust to-cglobal options to match the new push hardening. Docs regenerated and CHANGELOG updated.Assisted-by: Claude:claude-fable-5