feat(rules): add container privilege and isolation rules - #78
Merged
Conversation
nozaq
force-pushed
the
claude/rule-expansion-proposal-1401z4
branch
from
July 29, 2026 19:49
7e47a58 to
6933938
Compare
no-dangerous-cap-add reports a Linux capability that lets a process act on the host rather than on the container, in "capAdd" or in a "--cap-add" entry. Capabilities the kernel confines to the container's own namespaces are deliberately absent: "SYS_PTRACE" and "NET_ADMIN" sound privileged but reach no further than the container does, and "ALL" is what no-cap-add-all reports. no-host-namespace reports a "runArgs" entry that puts the container in one of the host's namespaces — "--network=host", "--pid=host", "--ipc=host", "--uts=host", "--userns=host", "--cgroupns=host". A network value is read as docker/cli reads it: a field list when some part of it is a bare "word=word" pair, in which case "name" holds the network, and a network name otherwise, so "--network=name = host" asks for a network of that name rather than for the host's. no-apparmor-unconfined reports AppArmor confinement turned off, the counterpart of no-seccomp-unconfined. The rules read a flag through the engine's "runArgs" traversal, so each declares the flags it wants and is handed their occurrences. no-host-namespace derives its paths from its own table, so the two cannot drift apart, and a test pins every key to a flag the table of "docker run" flags holds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NnTsBGF2nTtG8AS1osuHvJ
nozaq
force-pushed
the
claude/rule-expansion-proposal-1401z4
branch
from
August 1, 2026 11:37
2e4cad6 to
b0aac68
Compare
The guard asserted that every hostNamespaces key names a "docker run" flag. It came from an earlier shape of the rule, where the lookup went through a canonicalizing step and the paths did not: a key that was not already canonical left the entry unreachable and the rule calling a nil target. Neither half of that holds now. The paths are built from the keys and the engine matches them against the flag's canonical name, so a match implies the key is there; and the rule checks target for nil, so a zero entry could not panic even if one were reachable. What remains is a failure mode TestNoHostNamespace already covers — mistyping any of the seven keys, or writing one as a shorthand, fails it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NnTsBGF2nTtG8AS1osuHvJ
The rule read node.Arg without checking it, which every runArgs-driven rule but this one had to do anyway for its property path. A "runArgs" that is an object rather than an array is not an argv and is traversed as the ordinary object it is, so a member named "--network" reaches "/runArgs/--network" carrying no flag occurrence. The rule dereferenced nil there, and safeCheck turned that into a "rule panicked" finding. Also make NetworkTarget read a field list Docker rejects, which its own doc already claimed it did not. Docker refuses the flag on an unknown field key, an address that does not parse, or a field written without a key or a value; the value still says which network was asked for, so reading it beats going quiet on a config that already fails to start — the choice IsTrue makes for a boolean flag and parseMountString makes for a spaced mount field. It was half made here: three of those four forms already yielded the name field, and only a field without a value returned nothing, so "--network=name=host,web" asked for the host and was reported by no one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NnTsBGF2nTtG8AS1osuHvJ
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
Adds three rules for container privilege and isolation. They read
runArgsthrough the engine'sdocker runtraversal, which landed in #90 and #92, so each names the flags it wants and is handedtheir occurrences rather than parsing the array itself.
This branch was rebuilt on
mainafter the groundwork it used to carry was split out and merged as#86–#92. What remains is the rules.
Rules
no-dangerous-cap-add— a Linux capability that lets a process act on the host rather than onthe container, in
capAddor in a--cap-addentry. Capabilities the kernel confines to thecontainer's own namespaces are deliberately absent:
SYS_PTRACEandNET_ADMINsound privilegedbut reach no further than the container does, and
ALLis whatno-cap-add-allreports.no-host-namespace— arunArgsentry that puts the container in one of the host'snamespaces:
--network=host,--net=host,--pid=host,--ipc=host,--uts=host,--userns=host,--cgroupns=host.no-apparmor-unconfined— AppArmor confinement turned off, the counterpart ofno-seccomp-unconfined.Reading a network value
no-host-namespaceneeds to know what a--networkvalue names, which no rule needed before, sodockerargsgainsNetworkTarget. It reads the value as docker/cli does: a field list when somepart of it is a bare
word=wordpair, in which case thenamefield holds the network, and anetwork name otherwise. So
--network=name = hostasks for a network of that name, while--network=alias=web, name = hostasks for the host's.Notes
no-host-namespacederives itsPathsfrom its own flag table, so the two cannot drift apart,and a test pins every key to a flag
dockerargs.RunFlagsholds.no-dangerous-cap-addreports, and the effect each is reported with, is pinnedby a list written out in the test rather than read from the rule.
Security category: 8 rules → 11.
https://claude.ai/code/session_01NnTsBGF2nTtG8AS1osuHvJ