fix: regressions of the 1.1.21–1.1.25 hardening, invalid-JSON emitters, FwUsers GC, small correctness batch (1.1.27) - #230
Merged
Conversation
…x, migrate '..', pathOwned trailing slash), invalid-JSON emitters, FwUsers GC, small correctness batch (1.1.27) Regressions of our own fixes, found by an adversarial self-review: - run_pure.cpp validateCronUser: allow '.' (FreeBSD pw(8) accepts it; 1.1.22 rejected `john.doe` specs). Reject empty; run.cpp maps an empty spec value to the documented "root" default first — the 1.1.22 claim of "caller's default handling" was false and `user: ""` reached /var/cron/tabs/ (a directory). - stack_pure.cpp validateStackIp: validate the CIDR suffix (1.1.25 let 10.0.0.5/999, /abc, / through). - migrate_pure.cpp validateArtifactFile: drop the any-".."-substring rejection — stricter than the server that produces the name, and with '/' excluded a single component cannot traverse. - privops_authz_pure.cpp datasetOwned/pathOwned: trailing-slash prefix guard (same class as the 1.1.25 safePath fix), shared helper. Other: - hub/scheduling_pure.cpp jsonQuote emitted `\u1` (no width) and left the stream in hex mode; compared signed char (UTF-8 bytes "escaped"). daemon/routes.cpp log endpoint escaped only " \ \n \r \t. Both now emit \u00XX for every control byte. - ctx.cpp/ctx.h/clean.cpp: FwUsers::garbageCollect (mirrors FwSlots), called from del() and from `crate clean` — stale pids from SIGKILLed runs kept the shared NAT rule + ip.forwarding alive forever. - util.cpp writeFile(data, fd) no longer closes the caller's fd on error (double close); S_ISREG/S_ISDIR instead of `& S_IF*` (a UNIX socket matched S_IFDIR and aborted `crate create`); routes.cpp gmtime_r; run_services.cpp errors on socat fork() failure. Tests updated in run_pure/migrate_pure/privops_authz_pure/stack. Bumps to 1.1.27; CHANGELOG (incl. a correction to the 1.1.25 wording) + trust-model; TODO updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
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
Step 2 of the agreed plan: regressions introduced by our own 1.1.21–1.1.25 hardening (found by an adversarial self-review of those diffs), plus two invalid-JSON emitters, the
FwUsersGC gap, and a small correctness batch.Regressions of our own fixes
cron/userrejected legitimate dotted usernames —lib/run_pure.cpp(MED). 1.1.22's validator allowed[A-Za-z0-9_-]only, but FreeBSDpw(8)accepts.— a spec withuser: john.doeabortedcrate run..is now allowed (shell-inert;/excluded,./..reserved → no traversal). The 1.1.22 comment "empty is accepted unchanged (caller's default handling)" was false — no such handling existed, souser: ""reached/var/cron/tabs/(a directory).run.cppnow maps empty →rootbefore validating; the validator rejects empty.validateStackIpnever checked the CIDR suffix —lib/stack_pure.cpp.10.0.0.5/999,/abc,/all passed. A/now requires a bare decimal prefix within the family's range.crate migraterefused artifacts its own server produced —lib/migrate_pure.cpp. 1.1.21 rejected any..substring, stricter than the daemon'svalidateArtifactName(allows.freely).app..v2exported fine and then failed to migrate. With/excluded a single component cannot traverse; check dropped (exact./..stay reserved).datasetOwned/pathOwned—lib/privops_authz_pure.cpp(latent). Same class as the 1.1.25safePathfix; both now share one guarded helper.validateStackNameenforces "no.." — it reserves only the exact./...Other
hub/scheduling_pure.cppjsonQuotewrote"\u" << std::hex << cwith no width/fill (0x01 →\u1, stream left in hex mode) and compared a signedchar(every UTF-8 byte "escaped").daemon/routes.cpplog endpoint escaped only" \ \n \r \t(an ESC from a colour code went out raw). Both now emit\u00XXfor all control bytes.FwUsershad no dead-pid GC —lib/ctx.cpp,lib/clean.cpp(MED).FwSlotsalways GC'd;FwUsersdid not, andcrate clean's comment claimed it did while only locking/unlocking. Acrate runkilled without teardown left its pid forever →isEmpty()never true → shared NAT rule +ip.forwardingnever restored. NewFwUsers::garbageCollect(mirrorsFwSlots), called fromdel()and fromcrate clean.Util::Fs::writeFile(data, fd)no longer closes the caller's fd on error (double close).S_ISREG/S_ISDIRinstead ofst_mode & S_IF*(a UNIX socket matchedS_IFDIR→directory_iteratorthrew out offindElfFiles, abortingcrate create).gmtime_rin the snapshot route.socatproxyfork()failure now errors.Tests
Updated/added in
run_pure_test,stack_test,migrate_pure_test,privops_authz_pure_test(incl.hub_scheduling_pure_testre-run). All five pass locally.ctx/clean/util/routeschanges are runtime-only → FreeBSD lite compile gate.Deferred (E13)
The jail-name max-length drift (200 in
privops_purevs 64 in warm/backup/migrate vs 63 in vmwrap) is deliberately not patched here — it needs one shared constant across four modules and belongs to thecheckSafeNamerefactor (step 4).Version
Bumps to 1.1.27;
CHANGELOG.md+docs/trust-model.{md,uk.md}+TODOupdated.🤖 Generated with Claude Code
https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
Generated by Claude Code