Skip to content

docs(meta): add the context glossary and the plumbing-contract ADR #1076

docs(meta): add the context glossary and the plumbing-contract ADR

docs(meta): add the context glossary and the plumbing-contract ADR #1076

Workflow file for this run

name: Security
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Weekly scan every Monday at 02:00 UTC
- cron: "0 2 * * 1"
# Supersede in-flight runs for the same PR; see .claude/CLAUDE.md "Superseding CI runs".
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# ── Monorepo Structure Maintenance ────────────────────────────────────────────
# Checks that per-language tooling is fully wired up before per-module jobs run. Covers Go
# (module matrix completeness, .golangci.yml presence) and Python (workspace tool config,
# member glob coverage, requirements_lock.txt freshness). uv is needed for the lock check;
# setup-uv brings it alongside the 3.14 Python interpreter the script targets.
modules-check:
name: Module completeness check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
# renovate: datasource=python-version depName=python
python-version: "3.14"
- run: python3 meta/scripts/check_modules.py
# ── Static Application Security Testing (SAST) ───────────────────────────────
# Analyses source code for security vulnerabilities — injection flaws, insecure API usage,
# hardcoded secrets — from the source rather than from a resolved dependency set. Two engines run
# here: Semgrep below, and CodeQL after it. Semgrep uses per-language rule packs, passed via
# SEMGREP_RULES; add one per adopted language. The deprecated semgrep/semgrep-action is
# replaced by the upstream-recommended semgrep/semgrep container image running `semgrep ci`, so
# the tool version is SHA-pinned like everything else (the action froze at v0.58.0 — see README's
# "Dependency updates").
# TEND(lang-expand): add p/<language> to SEMGREP_RULES for each adopted language (semgrep.dev/r
# lists available packs, e.g. p/java, p/typescript, p/rust).
semgrep:
name: Semgrep
runs-on: ubuntu-latest
container:
image: semgrep/semgrep:1.174.0@sha256:f1f7b71861c7b28b6e0f661225a2c4f58a484f5d0f182465c6d6b3b22f972ade
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: semgrep ci
env:
SEMGREP_RULES: p/golang p/python p/secrets
# CodeQL, the second SAST engine and the one code scanning's own UI reads. Advanced setup on
# purpose — see .claude/CLAUDE.md "CodeQL runs as advanced setup" for what GitHub's managed
# default setup could not do, and for the repo setting that has to stay off for this to run.
codeql:
name: CodeQL Analysis (${{ matrix.language }})
runs-on: ubuntu-latest
permissions:
contents: read
# Uploads the SARIF. The starter workflow's `actions: read` (private repos) and
# `packages: read` (private CodeQL packs) buy nothing here; this repo is public and
# queries come from the default suite.
security-events: write
strategy:
fail-fast: false
matrix:
# TEND(lang-expand): one entry per adopted language. `none` extracts without building and
# is what interpreted languages want; a compiled language has no such option and needs its
# toolchain installed below, the way Go does.
include:
- language: actions
build-mode: none
- language: go
build-mode: autobuild
- language: python
build-mode: none
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Ahead of init on purpose: the Go autobuilder runs under GOTOOLCHAIN=local, so whatever
# is on PATH when extraction starts is the only toolchain it can use.
# //meta/scripts:test_codeql_toolchain keeps this step, and its position, from being lost.
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
if: matrix.language == 'go'
with:
go-version-file: go.work
# Globbed, not listed: this job has no module matrix to key off, and a hand-listed
# module would go stale the day a second one lands.
cache-dependency-path: |
go.work.sum
**/go.sum
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
languages: ${{ matrix.language }}
# No separate autobuild step: that action documents itself as superseded by this input.
build-mode: ${{ matrix.build-mode }}
- uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
# The string default setup uploaded under; it is what separates one language's
# results from another's within a tool.
category: "/language:${{ matrix.language }}"
# Fan-in for the ruleset; the pattern's reasoning is at devcontainer.yml's `base-image-all`.
# Strict on `skipped` unlike that one, since nothing gates this matrix. `if: always()` is
# load-bearing: without it a failed matrix skips this job, and a skipped check counts as
# passing. //meta/scripts:test_codeql_toolchain keeps both.
codeql-all:
name: CodeQL Analysis (all languages)
runs-on: ubuntu-latest
needs: [codeql]
if: always()
steps:
- name: Verify all codeql jobs passed
run: |
if [ "${{ needs.codeql.result }}" != "success" ]; then
echo "codeql result: ${{ needs.codeql.result }}"
exit 1
fi
echo "All codeql jobs passed"
# ── Dependency CVE Scanning ───────────────────────────────────────────────────
# Per-language scanners that flag known-vulnerable third-party dependencies. The signal model
# differs by language — govulncheck does call-graph reachability (only fires when our code
# reaches the vulnerable symbol); pip-audit is manifest-based (fires on any flagged version in
# the resolved set). Both run once per resolution unit: govulncheck per Go module, pip-audit
# once over the workspace-wide uv resolution (uv enforces single-version-per-package across
# workspace members, so one scan covers every member).
# TEND(lang-expand): add an equivalent per-language scanner as languages are adopted
# (e.g. cargo-audit for Rust, trivy fs --scanners vuln for languages without a dedicated
# reachability tool).
govulncheck:
name: govulncheck (${{ matrix.go_module }})
runs-on: ubuntu-latest
needs: [modules-check]
permissions:
contents: read
strategy:
fail-fast: false
matrix:
go_module:
- tools/network_infrastructure_maintenance
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The setup/install/run trio below is what golang/govulncheck-action wraps. We inline it:
# the action is a thin composite, and its own pinned actions/checkout + actions/setup-go
# trail current releases far enough to emit Node runtime deprecation warnings in every
# consuming workflow. If this ever outgrows install-and-run, check whether that action has
# started keeping its dependencies current and switch back.
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
# go.work pins the toolchain so a workspace-level `go X.Y.Z` directive doesn't trip
# GOTOOLCHAIN=local.
go-version-file: go.work
cache-dependency-path: |
go.work.sum
${{ matrix.go_module }}/go.sum
- run: go install golang.org/x/vuln/cmd/govulncheck@latest
- run: govulncheck -C ${{ matrix.go_module }} ./...
govulncheck-all:
name: govulncheck (all modules)
runs-on: ubuntu-latest
needs: [govulncheck]
if: always()
steps:
- name: Verify all govulncheck jobs passed
run: |
if [ "${{ needs.govulncheck.result }}" != "success" ]; then
echo "govulncheck result: ${{ needs.govulncheck.result }}"
exit 1
fi
echo "All govulncheck jobs passed"
# Python sibling of govulncheck. pip-audit reads requirements.txt format; we feed it a
# fresh `uv export` of uv.lock (the source of truth) into a tempfile rather than the
# committed requirements_lock.txt, so this scan is decoupled from whether the derived
# file is currently in sync — pre-commit and the Renovate auto-commit workflow each
# police that on their own paths.
pip-audit:
name: pip-audit
runs-on: ubuntu-latest
needs: [modules-check]
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
# renovate: datasource=python-version depName=python
python-version: "3.14"
- name: Export uv.lock to requirements.txt
run: |
uv export --format requirements-txt --no-emit-project \
--output-file "${RUNNER_TEMP}/requirements.txt"
- name: pip-audit
env:
# renovate: datasource=pypi depName=pip-audit
PIP_AUDIT_VERSION: "2.10.1"
# `--require-hashes` makes pip-audit trust the file as the resolution (implies
# `--no-deps`) AND fail if any line lacks a hash — a regression check against an
# un-hashed export sneaking back in. `--strict` promotes any remaining warning
# class to a failure.
run: uvx "pip-audit@${PIP_AUDIT_VERSION}" --requirement "${RUNNER_TEMP}/requirements.txt" --require-hashes --strict
# ── Supply Chain and Filesystem Scanning ─────────────────────────────────────
# Scans the entire repository for secrets committed to source, dependency CVEs across all
# ecosystems, and license compliance. Language-agnostic — Trivy covers new languages automatically
# as they are added.
trivy:
name: Trivy
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
format: sarif
output: trivy.sarif
- uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
if: always()
with:
sarif_file: trivy.sarif
category: trivy