This repository has been archived on 2026-11-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
platform-ci/runtime/SECURITY-RUNTIME.md
forge-recoveryandClaude Sonnet 5.5 f2fbacf131
KofTwentyTwo/ci-policy: ci / fork-guard (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / go-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / go-nix-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-docker-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-maven-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / manifests-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / nix-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / python-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-10 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-11 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-12 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-13 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-14 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / not-enrolled (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / content-validate-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / standards-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-matrix-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-15 (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / verify (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / secrets (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / gate (pull_request) Waiting for needed jobs
KofTwentyTwo/ci-policy: ci / ci-result (pull_request) Waiting for needed jobs
KofTwentyTwo/ci-policy: native / fork-deny (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-10 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-11 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-12 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-13 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-14 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-15 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-16 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-17 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-18 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-19 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / native-result (pull_request) Canceled after 5s
feat(ci): qualify shared security checks and available native cache
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 02:48:54 +00:00

7.4 KiB

Security runtime and secret-scan / test-report primitives

Scope: runtime/security.Dockerfile, platform_ci/secret_scan.py and platform_ci/test_reports.py with their tests.

Status: unqualified. Nothing here has been built, scanned or published. The root owner builds, scans and publishes the image, and only then pins its digest. Until that happens the image must not be referenced by any workflow or catalog.

Image

security.Dockerfile derives from the published control image git.kof22.com/koftwentytwo/platform-ci-control@sha256:11f8cb328e7fc7aca8e0c39ed979b33cb1db411a59460baae3e0d48281a15e3c (digest-checked at build) and adds one file, /usr/local/bin/gitleaks, rebuilt from source.

Why: the upstream prebuilt 8.21.2 binary was scanned at 28 HIGH/CRITICAL findings (26 Go standard library, 2 golang.org/x/text). Gitleaks' source, rules and fingerprints stay exactly the 8.21.2 commit; only the compiler and golang.org/x/text change. Nothing in the scanner's source or rules is edited.

Rebuild pins (mirroring runtime-qualification/security/rebuild-pins.json, frozen by root; the recipe tests compare the Dockerfile only with the constants in secret_scan.py and do not read that file):

Item Value
Gitleaks 8.21.2, commit 43fae355e6fe4d99d2a7b240a224b85e2903aeb4
Source archive https://codeload.github.com/gitleaks/gitleaks/tar.gz/<commit>, SHA-256 9035d05f3aa6c28dbc06c9785b019b2c2331efdb91c626abe39381417d08ab41
Go toolchain golang:1.27.3-bookworm@sha256:e482b31b5d30c31f9e495f4b8cbdf41429b2d74955dfbcd9c0a904483d738390
golang.org/x/text v0.41.0 (fixes both library findings)

Build stages (only the last ships):

  1. gitleaks-source (control image): downloads the archive with urllib, fails on a hashlib SHA-256 mismatch, extracts with tarfile filter="data", requires exactly one top-level directory.
  2. gitleaks-build (pinned Go image): GOTOOLCHAIN=local, go get golang.org/x/text@v0.41.0, then CGO_ENABLED=0 go build -trimpath -buildvcs=false -ldflags "-s -w -X <module>/cmd.Version=8.21.2" (cmd.Version is the Version variable of package cmd, defined in the frozen archive's cmd/version.go; module path read from go list -m). It checks that the binary's embedded Go is go1.27.3, that its golang.org/x/text dependency is v0.41.0, and that gitleaks version prints 8.21.2.
  3. Runtime: the control image plus that one binary. No platform or application source, and no Go, is shipped.

Downloads and go get (Go module proxy and checksum database) happen at image build time only; nothing is installed or downloaded when a pipeline runs. The constants in secret_scan.py must equal the build args (a test compares them). The build's dependency and compiler facts are therefore: Go 1.27.3 (official image), x/text v0.41.0, source commit above.

Legacy fact only: the old prebuilt gitleaks_8.21.2_linux_x64.tar.gz checksum 5bc41815076e6ed6ef8fbecc9d9b75bcae31f39029ceb55da08086315316e3ba (from tools.env) describes the vulnerable binary that is no longer used. It verifies nothing about the rebuilt binary and is not a build input; the rebuilt binary is verified by the source archive checksum and the pinned Go image, and has no vendor-published binary checksum.

Build and scan proof (root-reported, frozen only; not independently verified here): root built this recipe with the cmd.Version ldflag (the earlier version.Version flag was wrong), the binary prints 8.21.2, and a Trivy scan of the rebuilt image found 0 HIGH/CRITICAL vulnerabilities and 0 secrets, against 28 for the old prebuilt binary. The Gitleaks source commit and rules are unchanged from 8.21.2.

Not yet published: the image is not published and no digest is pinned, so it stays unqualified for workflow use until root publishes it and pins the digest. A real-binary history-scan smoke test is still owed.

Secret scan contract

scan_history(source_directory, source_revision, report_path,
             run_command=run_scan_command, parent_environment=os.environ) -> SecretScanResult
# SecretScanResult(passed, source_revision, failure, findings, exit_code, report_path, gitleaks_version)
  • Preconditions, all failures (never exceptions): revision is exactly 40 lowercase hex; HEAD equals it; the clone is not shallow (git rev-parse --is-shallow-repository must print false); the binary is always GITLEAKS_INSTALL_PATH (/usr/local/bin/gitleaks, not configurable) and must report version 8.21.2 (a binary that cannot start fails the scan); report_path is inside the source checkout and outside .git and .platform-ci.
  • Scan: gitleaks detect --source <dir> --no-banner --redact=100 --report-format json --report-path <report> --exit-code=2 --log-opts="--full-history <sha>". Only that revision's ancestry is scanned. A stale report is deleted first.
  • Failure: any finding (report is authoritative over exit code 0), exit code 2, any other nonzero exit, or a missing, unreadable or non-list report. Gitleaks and Git output is captured and discarded; only counts and generic reasons are logged or returned, so no matched secret or body reaches a log.
  • Environment: only PATH, LANG, LC_ALL, TMPDIR, plus GIT_CONFIG_NOSYSTEM, GIT_CONFIG_GLOBAL=/dev/null, GIT_NO_REPLACE_OBJECTS, GIT_TERMINAL_PROMPT=0. safe.directory is the exact resolved source path: -c on each Git command, and GIT_CONFIG_COUNT/KEY_0/VALUE_0 for the Git processes Gitleaks starts.
  • Exemptions: Gitleaks reads the repository's standard .gitleaksignore itself. There is no central owner/repository map and no baseline option; the approved exact fingerprints are moved into the repositories by the migration worker, and baseline contents are not changed here.
  • Runner: run_scan_command(command, cwd, env) -> CommandOutput(exit_code, stdout), no shell; raises SecretScanError when a program cannot start (converted to a failed result by scan_history).

Test-report contract

check_junit_reports(workspace_directory, report_paths) -> JUnitSummary
# JUnitSummary(reports, tests, skipped, failures, errors, problems); properties executed, passed
  • Reads only the declared files in one pass of the public xml.parsers.expat parser feeding ElementTree.TreeBuilder (the C ElementTree.XMLParser exposes no handlers and is not constructed); no process is started and tests are never rerun.
  • Rejects DOCTYPE, ENTITY and external entity references in UTF-8 and UTF-16 (expat handlers raise, nothing is expanded or fetched), expat errors as malformed XML, roots other than testsuite/testsuites, non-integer counts, and skipped > tests.
  • Fails on failures/errors attributes or on any testcase with a failure or error child.
  • Legacy Maven --allow-empty parity: zero reports, zero tests and all-skipped pass. No new zero or skip gate.
  • Containment: each path must resolve (through symlinks) to a regular file inside the workspace, outside .git and .platform-ci, else TestReportError. A missing declared report is packaging's responsibility, so it is an error here only as "not a regular file", never a pass.

Tests

From candidate/: python -m unittest tests.test_secret_scan tests.test_test_reports (all tests use a fake command runner or temporary files). Lint: ruff check and ruff format --check; typing: pyright. Strict pyright must also pass. The author had no shell, so these changes are unverified until root runs them.