refactor: migrate flag-block groups to ports (PR 2/6 of #2)#4
Open
stevehansen wants to merge 2 commits intomainfrom
Open
refactor: migrate flag-block groups to ports (PR 2/6 of #2)#4stevehansen wants to merge 2 commits intomainfrom
stevehansen wants to merge 2 commits intomainfrom
Conversation
Phase 3 of the ports-and-adapters refactor (issue #2). Migrates 5 command groups (dotnet, docker, npm, pnpm, db) to the direct-port handler shape and extends the structured Blocked JSON envelope contract to those groups. Adds two new safety primitives: - Policy.DenyFlags - exact-match flag denial (--force, -v, --volumes, ...) - Policy.DenyArgsContaining - substring denial for embedded forms like 'migrate:fresh' Collapses the duplicated AllowedScripts HashSet (previously copy-pasted in BunCommands/NpmCommands/PnpmCommands) into a single Safety.NodeScripts source. Six of twelve groups have now rolled over to the structured Blocked envelope under --json. The remaining six (git, file, process, env, proxy, generate) still emit Spectre markup until each migrates in subsequent phases. Tests: 96 total (52 new), 0 failing. Build: clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates SafeCommands to version 0.5.0, introducing new safety primitives DenyFlags and DenyArgsContaining and centralizing the node script allowlist. It migrates the dotnet, docker, npm, pnpm, and db command handlers to a unified structure that supports structured JSON output for blocked operations. Feedback focuses on performance optimizations within the new policy rules to reduce allocations and processing overhead, addresses a lookup performance regression in DbCommands where a HashSet was replaced by an array, and suggests consolidating duplicated logic in the Artisan migration handler.
- Policy.DenyFlagsRule: pre-compute lowercased HashSet in ctor (was per-call alloc) - Policy.DenyArgsContainingRule: pre-lowercase needles in ctor (was per-arg alloc in inner loop) - DbCommands.DestructiveFlags: restore HashSet for O(1) lookup in CheckDestructive's offending-arg search - DbCommands.RunArtisanMigrate: extract ArtisanDestructiveTerms shared between Policy and offending-arg search; cache ArtisanDestructivePolicy - DbCommands.DestructivePolicy: cache as static field instead of per-call expression - DockerCommands.ComposeDownPolicy: cache as static field instead of allocating per call All 96 tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
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
Phase 3 of #2 (PR 2 of 6). Migrates the flag-block groups to the ports-and-adapters seam landed in PR #3 and extends the structured
BlockedJSON envelope contract to five more groups.dotnet,docker,npm,pnpm,db— all now useFunc<Ports, string[], int>handlers and emit structured{ blocked, command, reason, suggestion }JSON envelopes under--json.Policy.DenyFlags(params string[])— exact-match (case-insensitive) flag denial. Powers--force,-v,--volumes,--rmi,--accept-data-loss,--force-reset,--skip-seed,--skip-generate,-f.Policy.DenyArgsContaining(params string[])— substring denial for embedded forms likemigrate:fresh. Reach for it only when exactDenyFlagswon't catch the intended pattern.Safety.NodeScripts.AllowedScriptsreplaces three copies of the same 22-scriptHashSetin Bun/Npm/Pnpm.Per-group rollout status (from #2)
BlockedJSONThe remaining six groups will roll over in Phases 4–5 of #2.
Notable implementation choices
Run.Toolnow acceptsPolicy?mirroringRun.Bun. This is the workhorse for the new groups; Run.Bun stays as a thin facade.DbCommands.CheckDestructivepreserves the project's "drops tables/data" wording rather than usingDenyFlags' generic message — destructive flags carry security context worth keeping. The same pattern is used inline inDockerCommands.RunComposeDownandDbCommands.RunArtisanMigrate/RunDjangoMigratefor cases where the original message conveyed somethingDenyFlagsalone wouldn't.DockerCommands.FilterFlagsretains its silent-filter semantics forbuildandcompose-up(drops unknown flags rather than blocking). This is a known security smell from the issue description but changing the contract is out of scope for this PR.Smoke tests
Test plan
dotnet build SafeCommands.slnxcleandotnet test96/96 pass--jsonfor migrated groupsgitgroup still emits Spectre markup under--json(regression check)🤖 Generated with Claude Code