Skip to content

fix(security): use wildcard form for the root-mount deny rules - #410

Merged
dean0x merged 1 commit into
mainfrom
fix/docker-deny-prefix-syntax
Oct 2, 2026
Merged

dean0x merged 1 commit into
mainfrom
fix/docker-deny-prefix-syntax

Conversation

@dean0x

@dean0x dean0x commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Summary

Three deny rules added in #399 end in :*, which Claude Code treats as legacy prefix syntax, so the * in the middle of the pattern is never expanded. Claude Code prints a warning for each rule at every startup, and the rules never matched, so whole-disk root mounts were never actually blocked. This replaces them with the wildcard form so they match.

Changes

  • The affected rules were Bash(docker run*-v /:*), Bash(docker run*--volume /:*) and Bash(docker run*--volume=/:*).
  • src/targets/claude-code/templates/managed-settings.json: replaced 1:1 with Bash(docker run*-v /:/*), Bash(docker run*--volume /:/*) and Bash(docker run*--volume=/:/*). The template stays at 170 entries.
  • Claude Code's suggested docker run*-v /* was rejected: it would block every absolute-path mount (e.g. -v /home/u/proj:/app). A container path is always absolute, so /:/ follows every root mount while ordinary mounts stay allowed.
  • src/targets/claude-code/post-install.ts: the old strings stay in DEVFLOW_HISTORICAL_DENY and the new ones are added, so devflow init retires the old entries from installed settings and security --disable / uninstall recognise both. The D-SECURITY-03 JSDoc records the rationale.
  • CHANGELOG.md: ### Fixed entry under [Unreleased].

Breaking Changes

None.

Testing

  • tests/init-logic.test.ts: the test matcher model did not know the :* suffix rule, so it read the broken rules as working wildcards and masked the bug. It is corrected against the documented examples (Bash(ls:*), Bash(git:* push)); with the corrected model, 8 tests failed on the old template (5 root mounts unblocked).
  • New guard rejects any template permission entry with a * before a trailing :*.
  • Root-mount positive cases: -v /:/host, -v /:/host:ro, and the --volume / --volume= forms.
  • Ordinary-mount negatives: -v $(pwd):/app, -v /home/u/proj:/app, --volume=/srv/data:/data, -v /tmp/:/scratch.
  • Retired-set pin moves 9 to 12. The 3 added could never match, so removing a user's identical copy loses nothing (ADR-024).
  • Locally passing: init-logic 281/281; 12 related files 513 passed / 4 skipped (env-gated); 4 CHANGELOG-reading files 208/208; npm run build exit 0; Snyk code scan 0 issues.

Related Issues

None. Follow-up to #399.

The three whole-disk docker mount rules shipped in 3.0.0 ended in `:*`,
Claude Code's legacy prefix syntax. That makes the rest of the rule a
literal prefix, so the `*` after `docker run` was never expanded: the
rules matched nothing and Claude Code warned about each one at startup.

Replace them 1:1 with `Bash(docker run*-v /:/*)` and the two `--volume`
spellings. A container path is always absolute, so `/:/` follows every
root mount (including `-v /:/host:ro`) while ordinary mounts such as
`-v /home/me/proj:/app` stay allowed. The template stays at 170 entries.

The old strings stay in DEVFLOW_HISTORICAL_DENY so `devflow init`
retires them from installed settings and `security --disable` /
`uninstall` still recognise them (D-SECURITY-03); the retired set grows
from 9 to 12.

Tests: the Bash-rule matcher model now covers the documented legacy `:*`
suffix, a guard rejects any template permission rule mixing `*` with a
trailing `:*`, red probes prove the retired rules never matched, and new
cases pin root mounts denied and project mounts allowed.
@dean0x
dean0x merged commit 577a1bc into main Oct 2, 2026
3 checks passed
@dean0x
dean0x deleted the fix/docker-deny-prefix-syntax branch October 2, 2026 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant