CICDv2 Security --- Findings Index
Related references: manual checklist execution, the threat model (now in system-docs under architecture/security/), and Shai-Hulud variant coverage in the dependency scanner.
Purpose
This document is the reference record of the security findings that apply to the CICDv2 runner posture: the inner-job DinD container, per-tenant network isolation, dispatch authorization, and job-token lifecycle. Each finding records its technical detail, severity, current status, and the action an operator should take or verify.
Severity scale
The severity classification used here is calibrated to release-gate impact, not raw CVSS:
| Severity | Definition | Effect |
|---|---|---|
| Critical | Direct, unmitigated escape of the tenant or host boundary on a default install. Active exploitation is plausible with public information. | Blocks release. Must be fixed, or risk-accepted with a named approver and a documented compensating control, before release. |
| High | Real escape risk on a non-default but supported configuration (e.g. "host without AppArmor loaded"), OR a defense-in-depth failure where the documented two-layer guarantee collapses to one layer. | Does not block release if the conditional posture is documented and the deployment contract gates against the unsupported configuration. |
| Medium | Verification gap: the static + unit-test evidence predicts the desired behaviour and a regression would flip a test red, but a live-host probe has not been executed. Not a security gap until something changes. | Informational. Does not block release. Verify on the first live-host probe window. |
| Low | Administrative / process item (doc review, named-approver capture, etc). | Informational. Does not block release. |
Over-classifying delays a justified ship; under-classifying hides a release gate. Each finding below records the rationale used.
Findings
F1 --- Seccomp profile permits mount(2); /proc-mount block depends entirely on AppArmor
- Severity: High (historical) / Fixed (current behaviour).
- Checklist item: Manual checklist execution, item 6.
- Status: fixed.
- Component:
internal/runnerctl/executor/seccomp/dind.json,internal/runnerctl/executor/apparmor/profile.txt,internal/runnerctl/executor/dind.go(buildContainerConfiglines 589--596). - Brief. The bundled DinD seccomp profile previously allowed
mount(2)(andumount,umount2,pivot_root) via both an explicit DinD-needed allow rule and theSCMP_ACT_ALLOWdefault action. The/proc-mount escape vector was therefore blocked exclusively by the bundled AppArmor profile (deny mount,plus targeteddeny @{PROC}/sys/kernel/**). When AppArmor is not loaded on the host kernel (apparmor.IsAvailable() == false), the executor emits no AppArmorSecurityOptsentry — and the documented defense-in-depth (seccomp + AppArmor) collapsed to defense-in-one (the docker daemon's own default profile). - Resolution. The bundled seccomp profile now denies
mount,umount,umount2, andpivot_rootunderSCMP_ACT_ERRNO(EPERM). The retained DinD-needed allow list is narrowed toclone,setns,unshare— required for nested build tooling but not implicated in the/proc-mount primitive. Nested container runtimes inside the inner container talk to the outer DinD daemon viadocker.sock; the outer daemon (in its own privileged outer container with its own profile) performs all real mount operations. Defense-in-depth is restored: seccomp enforces the block at the kernel-syscall layer regardless of host AppArmor support; AppArmor enforces the block at the LSM layer when loaded. On AppArmor-absent hosts the seccomp layer alone suffices. Pinning tests:TestLoad_DefaultDeniesMountSyscallsandTestProfile_DeniesMountAndProcWrites. - Severity rationale. Classified High (historical), not Critical: on a standard install with AppArmor loaded (the documented deployment contract), the block held even before the fix. The High classification reflected the defense-in-depth violation and the unguarded posture on AppArmor-absent hosts.
- Operator action. No action required. The seccomp layer blocks
mount/umount/umount2/pivot_rootindependently of host AppArmor support, so the runbook entry "this build requires AppArmor" is no longer load-bearing for the/proc-mount primitive.
F2 --- CICDv2 manual checklist live-host probes
- Severity: Medium
- Checklist item: Manual checklist execution, items 1, 2, 3, 6.
- Status: open.
- Component: Live-host kernel-level probes that cannot run inside a review sandbox.
- Brief. The static + unit-test evidence covers each of:
nsenteragainst the inner DinD container shows no host paths (item 1; pinned byTestDinDExecutor_NoHostPathsInBinds).nft list rulesetafterApplier.Refresh()shows the expected per-tenant chains (item 2; pinned byTestRefresh_CrossTenantBlock).- Cross-tenant dataplane packet is dropped by the base chain's
policy dropafter the per-tenant chains fall through (item 3; pinned by the same test). mount("proc", ...)from inside the inner container is blocked by AppArmor (item 6; pinned byTestProfile_DeniesMountAndProcWrites). None of these can be live-probed from a review sandbox; run the probes on a live staging host.
- Severity rationale. Classified Medium, not High: the items are believed-to-work statically and a regression would flip a Go test red. The absence of live-probe confirmation is a verification gap, not a security gap. It would only become a security gap if the underlying static evidence were wrong, which a code review of the cited files rules out at the static tier.
- Operator action. Run these probes on the first staging-host window. Items 1--3 require root + a loaded nftables module; item 6's live probe requires both an AppArmor-present and an AppArmor-absent host (the latter validates the F1 fail-stop landing). Flip the "Summary of verdicts" table in the manual checklist doc to PASS (live-probe) as each item lands.
F3 --- Shai-Hulud variant coverage in the dependency scanner
- Severity: Medium (coverage gap).
- Status: open.
- Component:
internal/scanning/scanner.go,internal/sbom/,internal/scanpolicy/,internal/actions/security-scan, advisory ingestion sources. - Brief. Coverage of the CVE/advisory-driven backends (trivy / OSV / dependency-check) is feed-reactive. The npm lifecycle-script signature class and the bundle-SHA IOC blocklist scanner now close the package-tree zero-day gap for those two first-party signature classes. See the full analysis in Shai-Hulud variant coverage.
- Severity rationale. Classified Medium: the CVE/advisory-driven
backends remain feed-reactive, and the registry-side publish hook that
would reject a known-IOC tarball at upload time is not yet
implemented, so a worm payload published to the internal npm registry
is not intercepted before a consumer's
npm install. - Operator action. Review the recommendations in the Shai-Hulud coverage reference. The bundle-SHA IOC blocklist and the lifecycle-script signature class both ship today; the registry-side publish hook remains the open coverage improvement.
Threat-model follow-up areas
The threat model (now in system-docs under architecture/security/) flags four areas as evolving hardening work rather than open findings. They describe surfaces the CICDv2 backlog continues to extend, not regressions against shipped behaviour, and they do not bear on the release gate:
- Attach-request body schema growth / TTL invariant.
- Per-tenant dispatch-time quota enforcement loop.
- Attach-token one-shot consume race coverage.
- Strict file-mounted secret handoff.
Aggregate
| Severity | Count | Findings |
|---|---|---|
| Critical | 0 | --- |
| High | 0 | F1 (fixed) |
| Medium | 2 | F2, F3 |
| Low | 0 | --- |
Operational gate. Zero open Critical findings. Zero open High
findings: F1 is fixed — the bundled seccomp profile denies
mount/umount/umount2/pivot_root independently of AppArmor, closing
the AppArmor-dependence defense-in-depth gap.
Maintaining this index
This document is additive. Append a new finding below as a
### F<n> section using the same structure (severity / status /
component / brief / rationale / operator action). Severity downgrades
require explicit rationale recorded inline; do not silently rewrite an
existing finding's severity.