Repository navigation
test: the chart's absences are asserted, not reviewed - #76
Merged
Merged
Conversation
The chart tests enumerated what must be present, and a test that enumerates presence is indifferent to an addition standing next to it. Five mutations were measured passing the whole suite green: resources: [pods, namespaces, nodes, secrets, configmaps] PASS hostPID: true, hostNetwork: true on the controller PASS capabilities.add: [NET_ADMIN, SYS_ADMIN] on the controller PASS a privileged initContainer in the Deployment PASS hostPath: / mounted writable into the controller PASS Each breaks a promise docs/security.md makes in plain words, and none of them had to defeat a test to get in. internal/chartrender/guardrail_test.go names the complete permitted set instead and fails on anything outside it, per rendered pod, in every install profile: the ClusterRole's resources and non-resource URLs, host namespaces (hostPID exactly on the node DaemonSet, hostNetwork and hostIPC nowhere), capabilities added by anything other than the node container, and any host path, Secret or projected volume on the controller. A last test fails if the chart renders a workload kind the pod-spec walk does not know, so a Job added later cannot be silently exempt from all of it. allContainers now includes init containers. An init container carries its own securityContext and can hold privileges the containers beside it do not, so walking only Spec.Containers left the existing privileged/drop-ALL assertions with a hole; the chart renders none today, which is why the hole was invisible. Also fixes two chart comments pointing at charts/runtime-agent/render_test.go, a file that does not exist. All nine mutations (the five above plus a third non-resource URL, hostPID dropped from the node, and a read-only host path on the controller) were re-run and each fails with a message naming the promise it breaks. No agent behaviour changes, no ADR: this adds no decision, it holds decisions already recorded.
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.
What was wrong
chart_test.goasserts that the chart contains what it must — every grant the informers need, the capabilities the profiler asks for, the policy on the receiver's port. Those tests enumerate what is present, and a test that enumerates presence is indifferent to an addition standing next to it.Almost every promise in
docs/security.mdis about absence. Measured, one mutation at a time, against the full./internal/chartrender/suite onmain:resources: [pods, namespaces, nodes, secrets, configmaps]hostPID: true+hostNetwork: trueon the controllercapabilities.add: [NET_ADMIN, SYS_ADMIN]on the controllerinitContainerin the DeploymenthostPath: /mounted writable into the controllersecretswith verbgetis a read-only ClusterRole, soTestTheClusterRoleGrantsNoWriteVerbis satisfied;TestTheClusterRoleGrantsEveryCacheTheAgentOpenschecks the required set is a subset, so a word beside it is invisible.drop: [ALL]next toadd: [SYS_ADMIN]still drops ALL.allContainerswalkedSpec.Containersonly.TestEveryHostMountIsReadOnlywalked DaemonSets only.None of these had to defeat a test. They walked past tests aimed elsewhere.
What this adds
internal/chartrender/guardrail_test.go— the same promises written the other way round: name the complete permitted set, fail on anything outside it, per rendered pod, in every install profile.TestTheClusterRoleNamesNothingBeyondThisList— the resource list is closed, not a floor. Spelled out in the test rather than derived from the chart, because an expectation read from the thing under test asserts nothing.TestTheOnlyNonResourceURLsAreTheOnesNodeTokensNeed— exactly the two OIDC reads when a node is installed, and neither when it is not.TestOnlyTheNodeSharesTheHostsNamespaces—hostPIDasserted in both directions: absent from the controller, present on the node, because losing it there makes the scanner collect nothing and say nothing about why.hostNetworkandhostIPCon no pod anywhere.TestOnlyTheNodeContainerAddsACapability— measured on 1.36.1: a container withdrop: [ALL]andadd: [SYS_ADMIN, NET_ADMIN]getsCapEff: 0000000000201000, bits 21 and 12, exactly the two added. The add wins; the securityContext reads as careful.TestTheControllerReachesNothingOutsideItsOwnPod— no host path (read-only included), no Secret, no projected volume. §7's blast-radius paragraphs are written about the DaemonSet on the understanding that the controller has no such reach.TestNoWorkloadKindEscapesThePodSpecWalk— a guard on the guards. Every assertion above is per pod spec, so it is only as complete asallPodSpecs, which knows two workload kinds. AJobadded later would otherwise be silently exempt from all of them.allContainersnow includes init containers, which is what catches the privileged one above — via the existing test, inchart_test.go.Negative check
All nine mutations re-run against this branch. Each fails, with a message naming the promise it breaks:
Plus a read-only
hostPath: /etcon the controller, caught by the same test as the writable one.Docs
docs/security.md§2 — the host boundary runs between the two roles, and that this is asserted per rendered pod.docs/security.md§9 — the "no secrets, no configmaps" row now says the resource list is closed by a test rather than by review.docs/development.md— a testing-strategy bullet on why absences need their own tests, and the note that widening the chart means widening a list there.charts/runtime-agent/render_test.go, which does not exist. Nowinternal/chartrender/chart_test.go.Not in scope
No ADR: this records no decision, it holds decisions already recorded (0009, 0010, 0036, 0037, 0039). No agent behaviour changes and no chart behaviour changes — the only template edits are comments.
Gates
go build ./...,go test -race -count=1 ./...,go vet -tags e2e ./...,make lint(0 issues),make chart-lint(3 profiles, 0 failed),gofmtclean. No e2e run: nothing outside test files and comments changed.