Vetrix Docs

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 (buildContainerConfig lines 589--596).
  • Brief. The bundled DinD seccomp profile previously allowed mount(2) (and umount, umount2, pivot_root) via both an explicit DinD-needed allow rule and the SCMP_ACT_ALLOW default action. The /proc-mount escape vector was therefore blocked exclusively by the bundled AppArmor profile (deny mount, plus targeted deny @{PROC}/sys/kernel/**). When AppArmor is not loaded on the host kernel (apparmor.IsAvailable() == false), the executor emits no AppArmor SecurityOpts entry — 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, and pivot_root under SCMP_ACT_ERRNO (EPERM). The retained DinD-needed allow list is narrowed to clone, 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 via docker.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_DefaultDeniesMountSyscalls and TestProfile_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_root independently 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:
    • nsenter against the inner DinD container shows no host paths (item 1; pinned by TestDinDExecutor_NoHostPathsInBinds).
    • nft list ruleset after Applier.Refresh() shows the expected per-tenant chains (item 2; pinned by TestRefresh_CrossTenantBlock).
    • Cross-tenant dataplane packet is dropped by the base chain's policy drop after 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 by TestProfile_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.