Archived
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
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
103 lines
7.4 KiB
Markdown
103 lines
7.4 KiB
Markdown
# 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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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.
|