Repository navigation
170 lines (167 loc) · 9.13 KB
/
Copy pathshared-compliance-audit.yml
File metadata and controls
170 lines (167 loc) · 9.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# Independent compliance completeness audit. Syft-scans the FINAL pushed image,
# subtracts the baseline SBOM, and reports any shipped component missing from the
# build's unified osrb-deps.csv — the authoritative "did the generators miss
# anything" check that verify_sbom_diff never was (it only compared our own
# outputs). Runs as a sibling job (needs:[<fw>-build]) IN PARALLEL with the test
# jobs so the slow syft pull+scan never blocks testing. SOFT GATE: report-only
# (no --fail-on-findings) until the name-normalization is proven low-noise; the
# job is not a required check.
name: Shared Compliance Audit
on:
workflow_call:
inputs:
framework:
description: 'Framework (vllm, sglang, trtllm, dynamo)'
required: true
type: string
target:
description: 'Docker target (runtime, planner, frontend)'
required: true
type: string
cuda_version:
description: 'Single CUDA version (e.g. 13.0); empty for cpu targets'
required: false
type: string
default: ''
target_tag_plain:
description: 'Plain image tag prefix from the build job (also the compliance artifact prefix)'
required: true
type: string
baseline_sbom_file:
description: 'Baseline SBOM stem from the build job (needs.<fw>-build.outputs.baseline_sbom_file); we append -amd64.cdx.json. Empty = audit full image.'
required: false
type: string
default: ''
jobs:
audit:
name: Compliance audit
# syft catalogs the FULL multi-GB image. Runner history (every PR pool has a
# distinct wall for 15-20GB CUDA images):
# small-v2 : disk too small (dies on all large images)
# prod-default-v2: disk short for ~20GB (frontend/planner OK, big CUDA die)
# deploy-tester-v1: IRSA role lacks ECR -> NoCredentials (Azure ACR + vCluster)
# prod-builder-v3: 482GB disk but the runner pod OOMs syft on large images
# (sized to orchestrate REMOTE BuildKit, not scan locally)
# prod-tester-amd-v2 RUNS these images for tests, so it has the memory the
# builder lacked. Non-GPU tester (syft needs no GPU); registry scan is
# daemon-independent.
runs-on: prod-tester-amd-v2
# Run inside the shipped image itself (same runner combo as shared-test.yml,
# proven on this pool): satisfies the runner's job-container requirement and
# provides python3 for the audit. syft then scans the container's OWN
# filesystem — the image is already materialized as our rootfs, so there's no
# registry re-pull and no ECR / aws CLI / IRSA needed. The plain shipped tag
# (no -test) so the fs we scan is exactly what ships.
container:
image: ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ github.sha }}-${{ inputs.target_tag_plain }}${{ (inputs.cuda_version != '' && !startsWith(inputs.cuda_version, '13.')) && format('-cuda{0}', startsWith(inputs.cuda_version, '12.') && '12' || inputs.cuda_version) || '' }}
env:
# The image's 'dynamo' user is uid 1000 but the ARC runner executes the
# pod as uid 1001; override HOME/USER so actions/checkout and
# getpass.getuser() don't trip on the mismatch (same as shared-test.yml).
HOME: /__w/dynamo/dynamo
USER: dynamo
# Safety net: if a scan still runs away, fail cleanly instead of holding a
# runner for the 360-min default. A healthy scan completes in <20 min.
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install syft
# Best-effort: the audit is a soft gate, so never hard-fail the job on a
# syft install hiccup — the scan step below is also continue-on-error.
continue-on-error: true
shell: bash
run: |
# Install into a runner-writable dir and put it on PATH (no sudo).
# curl||wget — the shipped image normally has curl, but fall back so a
# missing one doesn't silently no-op the audit.
mkdir -p "${RUNNER_TEMP}/bin"
INSTALL_URL=https://raw.githubusercontent.com/anchore/syft/main/install.sh
if command -v curl >/dev/null 2>&1; then
curl -sSfL "${INSTALL_URL}" | sh -s -- -b "${RUNNER_TEMP}/bin"
else
wget -qO- "${INSTALL_URL}" | sh -s -- -b "${RUNNER_TEMP}/bin"
fi
echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH"
- name: Resolve image ref, baseline + artifact name
id: resolve
shell: bash
run: |
set -euo pipefail
CUDA="${{ inputs.cuda_version }}"
# Image tag — keep in sync with shared-copy.yml/shared-build-image.yml:
# cuda13 is implicit (no suffix); other majors keep -cuda<major>.
IMAGE_TAG="${{ github.sha }}-${{ inputs.target_tag_plain }}"
if [ -n "${CUDA}" ] && [ "${CUDA%%.*}" != "13" ]; then
IMAGE_TAG="${IMAGE_TAG}-cuda${CUDA%%.*}"
fi
# image_tag is used only for report naming + the audit --image label;
# the scan reads the container's own filesystem, not a registry ref.
echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"
# Compliance artifact name — compliance-<git-sha>-<container>, matching
# compliance-extract's upload (target_tag_plain == container name). For
# PR (the only caller), git_sha == github.sha.
echo "artifact=compliance-${{ github.sha }}-${{ inputs.target_tag_plain }}" >> "$GITHUB_OUTPUT"
# Baseline SBOM stem comes from the build job's output (it rendered the
# Dockerfile with the deps it has), so the audit runner needs no
# render.py / PyYAML / pip. Append -amd64 to match the amd64 scan.
STEM="${{ inputs.baseline_sbom_file }}"
if [ -n "${STEM}" ] && [ -f "container/compliance/base_sboms/${STEM}-amd64.cdx.json" ]; then
echo "baseline=container/compliance/base_sboms/${STEM}-amd64.cdx.json" >> "$GITHUB_OUTPUT"
else
echo "::notice::no amd64 baseline SBOM for stem '${STEM}'; auditing full image"
echo "baseline=" >> "$GITHUB_OUTPUT"
fi
- name: Download build compliance artifact
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ steps.resolve.outputs.artifact }}
path: /tmp/compliance
- name: Syft scan (container fs) + audit vs osrb-deps.csv
continue-on-error: true # soft gate — never block tests/merge
shell: bash
run: |
set -euo pipefail
# Scan the container's OWN filesystem: this job runs inside the shipped
# image, so the image is already on disk as our rootfs — no registry
# re-pull, no docker daemon, no ECR / aws creds.
#
# Restrict to INSTALLED-package catalogers so syft reports what's
# actually shipped, not deps merely *declared* in source manifests that
# framework images bundle (lmsysorg/sglang ships its repo: .github
# workflows, go.mod, requirements.txt, pre-commit configs). rust-cargo-lock
# is added back because our Rust deps are only visible via Cargo.lock
# (the wheels aren't cargo-auditable) and they're the audit's main signal
# (e.g. vllm's aws-sdk crates). go-module-binary stays (installed), so
# the etcd/nats binaries are still attributed; only source go.mod drops.
# Excludes still skip virtual fs + GHA mounts (cheap; guards /proc etc.).
syft dir:/ \
--select-catalogers 'installed,+rust-cargo-lock-cataloger' \
--exclude './proc/**' --exclude './sys/**' --exclude './dev/**' \
--exclude './__w/**' --exclude './__e/**' --exclude './github/**' \
--exclude './tmp/**' --exclude './run/**' --exclude './home/runner/**' \
-o cyclonedx-json=/tmp/syft.cdx.json
OUR_CSV=$(find /tmp/compliance -path '*linux_amd64*' -name 'osrb-*.csv' | head -1)
[ -z "${OUR_CSV}" ] && OUR_CSV=$(find /tmp/compliance -name 'osrb-*.csv' | head -1)
echo "auditing against ${OUR_CSV}"
BASE_ARG=""
[ -n "${{ steps.resolve.outputs.baseline }}" ] && BASE_ARG="--base-sbom ${{ steps.resolve.outputs.baseline }}"
PYTHONPATH=container python3 -m compliance.audit_image_sbom \
--syft-sbom /tmp/syft.cdx.json \
${BASE_ARG} \
--our-csv "${OUR_CSV}" \
--image "${{ steps.resolve.outputs.image_tag }}" \
--report /tmp/audit-report.txt -v
- name: Upload audit report
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
# compliance-audit-<git-sha>-<container>[-cudaN] (image_tag already carries all three).
name: compliance-audit-${{ steps.resolve.outputs.image_tag }}
path: /tmp/audit-report.txt
retention-days: 7
if-no-files-found: ignore