Repository navigation
test(dns): cover native DNS activate/deactivate admin authorization; add env secrets threat model - #321
Draft
roodboi wants to merge 2 commits into
Draft
test(dns): cover native DNS activate/deactivate admin authorization; add env secrets threat model#321roodboi wants to merge 2 commits into
roodboi wants to merge 2 commits into
Conversation
…mpt and add the env secrets threat model The `hack global dns activate|deactivate` authorize closures move into createNativeDnsAuthorizer (src/lib/native-domain-dns-authorization.ts) with injectable interactivity, confirmation and administrator prompt. A failed sudo -v now throws NativeDnsAdministratorAuthorizationError (closed outcome "authorization-failed" and failure class; message unchanged). Tests wire the authorizer into the real activation/deactivation flows and host effect boundary and prove: sudo -v runs before every privileged and file effect, a failed prompt is authorization-failed (not denied) and changes nothing, and a decline or a non-interactive run never reaches sudo. Adds docs/security/env-secrets-threat-model.md (HACK-1127), linked from docs/env.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efaults
tests/global-dns-authorizer-wiring.test.ts checks, on the TypeScript
syntax tree of src/commands/global.ts, that handleGlobalDnsActivate and
handleGlobalDnsDeactivate each build exactly one authorizer with
createNativeDnsAuthorizer({ action: <own action>, domain }), pass it to
createNativeDnsHostDependencies, and run their flow with those
dependencies. The only other authorizer allowed is the decline-only
`async () => false` on the deactivate verify-only path.
tests/native-domain-dns-authorization.test.ts now also covers the
production defaults: the default interactivity check requires a stdin
TTY before confirming, and the default confirmation declines an
explicitly non-interactive run before sudo. The administrator prompt
stays a fake, so no test reaches real sudo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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
HACK-1127 (unit E4). This PR has two parts.
hack global dns activateandhack global dns deactivateadministrator prompt.Final behavior
DNS authorizer seam (
src/lib/native-domain-dns-authorization.ts, new)authorizeclosures inhandleGlobalDnsActivateandhandleGlobalDnsDeactivateare now one function,createNativeDnsAuthorizer({ action, domain, dependencies? }). Its injectable dependencies areisInteractive,confirmandrequestAdministrator. The production defaults are the same calls as before:process.stdin.isTTY,confirmSafewithnonInteractive: "decline", andrequestAdministratorAuthorization. The confirmation text is the same for both actions.requireNativeAdministratorAuthorizationmoved out ofsrc/commands/global.tsinto the new module, and it now accepts an injectedrequestAdministrator. When the prompt fails, it throwsNativeDnsAdministratorAuthorizationError. The error carriesoutcome: "authorization-failed"and the closedfailureclass, never sudo output. The message text is unchanged.src/commands/global.tsloses 48 lines. User-visible behavior is unchanged.Handler wiring guard (
tests/global-dns-authorizer-wiring.test.ts, new)The handlers need a real macOS host, Homebrew dnsmasq and sudo, so no test runs them. This guard reads the TypeScript syntax tree of
src/commands/global.tsand checks each ofhandleGlobalDnsActivateandhandleGlobalDnsDeactivate:createNativeDnsAuthorizerexactly once, with its ownactionstring literal anddomain, and stores the result in aconst.authorizepassed tocreateNativeDnsHostDependencies.activateNativeDomainDnsordeactivateNativeDomainDns) runs with exactly those dependencies.async () => falseon the deactivate verify-only path.It is a syntax check of one file, not data flow. No production code changed in this rework.
Threat model (
docs/security/env-secrets-threat-model.md, new; linked fromdocs/env.md)The document covers:
project_keyYAML, theencrypted_filestore and its key, thekeychainstore, values from 1Password or another external manager, and decrypted values in flight..hack/.internal/compose.env.override.yml, which is written withBun.write(umask mode, follows symlinks)encrypted_filekey-file hygienesecurity -w <value>argv in the unsupported surfaceThe doc makes no code change.
Validation (local, macOS, Bun 1.4.2)
tests/native-domain-dns-authorization.test.ts: 8 tests. Each one wires the authorizer into the realactivateNativeDomainDnsordeactivateNativeDomainDnsand intocreateNativeDnsHostDependencies, with a temp directory, fake privileged and command runners, and one ordered effect log. No real sudo, HOME, keychain, Docker or CLI spawn is used.confirm, then a singlesudo -v. Everysudo -nstep, receipt write and file effect comes after it. The test checks that those effects actually ran, so the assertion is not vacuous. The confirmation texts are asserted exactly.rejectedexit 1,timed-outexit 124):authorizationwithNativeDnsAdministratorAuthorizationError,outcome: "authorization-failed". The message never says "denied".confirm,sudo -v.confirm. It never reaches sudo.not-foreground,timed-out,interrupted,rejected) throws. None returns false.process.stdin.isTTYset to undefined or false, the authorizer returns false and never confirms. With it set to true, it confirms. The original descriptor is restored afterwards.HACK_NO_INTERACTIVE=1, the realconfirmSafedeclines, and the authorizer returns false before sudo. The variable is restored afterwards.tests/global-dns-authorizer-wiring.test.ts: 2 tests, one per handler.global-dns-authorizer-wiring,native-domain-dns-authorization,native-admin-authorization,native-domain-dns-host,native-domain-dns-activate,native-domain-dns-deactivate,native-hack-local-route-cli,doctor-env-key,cli-reference-docs,tests/integration-ownership.test.ts,tests/cli-spawn-env-isolation.test.ts, and the othernative-domain-dns-*andnative-hack-local-route*suites.bun run --cwd packages/cli typecheckand rootbun run typecheck: exit 0.bun run --cwd packages/cli check: 0 errors. There are 34 existing warnings, none in changed files. Biome is clean on the changed TypeScript files.docs:cli-reference,generate:agent-pluginsand the fingerprint step were not needed.Negative controls
Each mutation was run against the wiring, authorization, activate, deactivate and admin-authorization suites, then reverted. All 16 are caught.
global.tsactivate:const authorize = async () => true;global.tsdeactivate: authorizer built withaction: "activate"global.tsactivate: host dependencies getauthorize: async () => trueglobal.tsdeactivate: flow gets{ ...deps, authorize: async () => true }global.tsactivate: authorizer built with a differentdomainisInteractive: () => trueisInteractive: () => falsenonInteractive: "accept"initialValue: true, nonInteractive: "accept-default"requireNativeAdministratorAuthorizationreturns false instead of throwingErrorinstead of the typed failureRemaining gaps
requestAdministrator(realsudo -v) is not exercised by any test.nonInteractive: "accept-default"withinitialValue: falsebehaves the same as"decline", so no test can tell them apart.hack global dns activateordeactivateon a real host. A realsudo -vprompt, a real dnsmasq restart and a real/etc/resolverwrite are still unproven for this change.62a9d124.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.