Skip to content

Commit d44158a

Browse files
committed
ci(trivy): pick scan profile based on image size
Previous fix hard-coded vuln-only + 20m timeout for every Trivy run, which downgrades scans for small helper images that don't need it. Add scripts/ci-trivy-profile.sh: inspects the loaded image's .Size and emits scanner/timeout outputs: > TRIVY_LARGE_IMAGE_BYTES (default 2 GiB) scanners=vuln timeout=20m otherwise scanners=vuln,secret timeout=10m Threshold is overridable via the TRIVY_LARGE_IMAGE_BYTES repo variable. Wire it into all three scan paths (pr-build-scan, merge-and-scan, rescan-latest). Document in AGENTS.md.
1 parent 781afa1 commit d44158a

3 files changed

Lines changed: 88 additions & 48 deletions

File tree

.github/workflows/docker.yml

Lines changed: 37 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ env:
3636
# (checkout, login-action, metadata-action, etc.) that haven't bumped yet.
3737
# Remove this once Node 24 is the runner default.
3838
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
39+
# Image size (uncompressed, as reported by `docker image inspect .Size`)
40+
# above which Trivy drops the secret scanner and uses an extended timeout.
41+
# Big scientific images full of model weights and reference databases trip
42+
# the secret scanner's per-file size guard; the secret scanner is meant for
43+
# source repos with API keys anyway. Smaller images get the full default
44+
# scan (vuln + secret). Override via repo variable TRIVY_LARGE_IMAGE_BYTES.
45+
TRIVY_LARGE_IMAGE_BYTES: ${{ vars.TRIVY_LARGE_IMAGE_BYTES || '2147483648' }}
3946

4047
jobs:
4148
# ---------------------------------------------------------------------------
@@ -329,20 +336,20 @@ jobs:
329336
- name: Pull merged image for Trivy scan
330337
run: docker pull ${{ steps.refs.outputs.ghcr_image }}:${{ steps.ver.outputs.version }}
331338

339+
- name: Pick Trivy scan profile by image size
340+
id: trivy_cfg
341+
env:
342+
IMAGE_REF: ${{ steps.refs.outputs.ghcr_image }}:${{ steps.ver.outputs.version }}
343+
run: bash scripts/ci-trivy-profile.sh
344+
332345
- name: Trivy scan (table; fails build on HIGH/CRITICAL)
333346
uses: aquasecurity/trivy-action@master
334347
with:
335348
image-ref: ${{ steps.refs.outputs.ghcr_image }}:${{ steps.ver.outputs.version }}
336349
format: table
337350
severity: CRITICAL,HIGH
338-
# Vulnerability scanning only. Secret scanner is too slow on this
339-
# image (~5 GB conda envs, multi-GB model weights and reference data
340-
# files in vcontact2 / DeepVirFinder / tensorflow MLIR headers) and
341-
# is intended for source repos with API keys, not scientific images.
342-
scanners: vuln
343-
# Default Trivy timeout is 5 min; raise so the layer walk on this
344-
# large image completes (last run hit "context deadline exceeded").
345-
timeout: 20m
351+
scanners: ${{ steps.trivy_cfg.outputs.scanners }}
352+
timeout: ${{ steps.trivy_cfg.outputs.timeout }}
346353
exit-code: '1'
347354
ignore-unfixed: true
348355
ignore-policy: docker/${{ matrix.image }}/.trivy-ignore-policy.rego
@@ -356,14 +363,8 @@ jobs:
356363
format: sarif
357364
output: trivy-${{ matrix.image }}.sarif
358365
severity: CRITICAL,HIGH
359-
# Vulnerability scanning only. Secret scanner is too slow on this
360-
# image (~5 GB conda envs, multi-GB model weights and reference data
361-
# files in vcontact2 / DeepVirFinder / tensorflow MLIR headers) and
362-
# is intended for source repos with API keys, not scientific images.
363-
scanners: vuln
364-
# Default Trivy timeout is 5 min; raise so the layer walk on this
365-
# large image completes (last run hit "context deadline exceeded").
366-
timeout: 20m
366+
scanners: ${{ steps.trivy_cfg.outputs.scanners }}
367+
timeout: ${{ steps.trivy_cfg.outputs.timeout }}
367368
limit-severities-for-sarif: true
368369
ignore-unfixed: true
369370
ignore-policy: docker/${{ matrix.image }}/.trivy-ignore-policy.rego
@@ -426,20 +427,20 @@ jobs:
426427
# warm-cache hits when the PR merges.
427428
cache-from: type=gha,scope=${{ matrix.image }}-amd64
428429

430+
- name: Pick Trivy scan profile by image size
431+
id: trivy_cfg
432+
env:
433+
IMAGE_REF: ${{ matrix.image }}:scan
434+
run: bash scripts/ci-trivy-profile.sh
435+
429436
- name: Trivy scan (table; fails build on HIGH/CRITICAL)
430437
uses: aquasecurity/trivy-action@master
431438
with:
432439
image-ref: ${{ matrix.image }}:scan
433440
format: table
434441
severity: CRITICAL,HIGH
435-
# Vulnerability scanning only. Secret scanner is too slow on this
436-
# image (~5 GB conda envs, multi-GB model weights and reference data
437-
# files in vcontact2 / DeepVirFinder / tensorflow MLIR headers) and
438-
# is intended for source repos with API keys, not scientific images.
439-
scanners: vuln
440-
# Default Trivy timeout is 5 min; raise so the layer walk on this
441-
# large image completes (last run hit "context deadline exceeded").
442-
timeout: 20m
442+
scanners: ${{ steps.trivy_cfg.outputs.scanners }}
443+
timeout: ${{ steps.trivy_cfg.outputs.timeout }}
443444
exit-code: '1'
444445
ignore-unfixed: true
445446
ignore-policy: docker/${{ matrix.image }}/.trivy-ignore-policy.rego
@@ -453,14 +454,8 @@ jobs:
453454
format: sarif
454455
output: trivy-${{ matrix.image }}.sarif
455456
severity: CRITICAL,HIGH
456-
# Vulnerability scanning only. Secret scanner is too slow on this
457-
# image (~5 GB conda envs, multi-GB model weights and reference data
458-
# files in vcontact2 / DeepVirFinder / tensorflow MLIR headers) and
459-
# is intended for source repos with API keys, not scientific images.
460-
scanners: vuln
461-
# Default Trivy timeout is 5 min; raise so the layer walk on this
462-
# large image completes (last run hit "context deadline exceeded").
463-
timeout: 20m
457+
scanners: ${{ steps.trivy_cfg.outputs.scanners }}
458+
timeout: ${{ steps.trivy_cfg.outputs.timeout }}
464459
limit-severities-for-sarif: true
465460
ignore-unfixed: true
466461
ignore-policy: docker/${{ matrix.image }}/.trivy-ignore-policy.rego
@@ -511,20 +506,20 @@ jobs:
511506
- name: Pull latest
512507
run: docker pull ${{ steps.refs.outputs.ref }}
513508

509+
- name: Pick Trivy scan profile by image size
510+
id: trivy_cfg
511+
env:
512+
IMAGE_REF: ${{ steps.refs.outputs.ref }}
513+
run: bash scripts/ci-trivy-profile.sh
514+
514515
- name: Trivy scan (table)
515516
uses: aquasecurity/trivy-action@master
516517
with:
517518
image-ref: ${{ steps.refs.outputs.ref }}
518519
format: table
519520
severity: CRITICAL,HIGH
520-
# Vulnerability scanning only. Secret scanner is too slow on this
521-
# image (~5 GB conda envs, multi-GB model weights and reference data
522-
# files in vcontact2 / DeepVirFinder / tensorflow MLIR headers) and
523-
# is intended for source repos with API keys, not scientific images.
524-
scanners: vuln
525-
# Default Trivy timeout is 5 min; raise so the layer walk on this
526-
# large image completes (last run hit "context deadline exceeded").
527-
timeout: 20m
521+
scanners: ${{ steps.trivy_cfg.outputs.scanners }}
522+
timeout: ${{ steps.trivy_cfg.outputs.timeout }}
528523
exit-code: '1'
529524
ignore-unfixed: true
530525
ignore-policy: docker/${{ matrix.image }}/.trivy-ignore-policy.rego
@@ -538,14 +533,8 @@ jobs:
538533
format: sarif
539534
output: trivy-${{ matrix.image }}.sarif
540535
severity: CRITICAL,HIGH
541-
# Vulnerability scanning only. Secret scanner is too slow on this
542-
# image (~5 GB conda envs, multi-GB model weights and reference data
543-
# files in vcontact2 / DeepVirFinder / tensorflow MLIR headers) and
544-
# is intended for source repos with API keys, not scientific images.
545-
scanners: vuln
546-
# Default Trivy timeout is 5 min; raise so the layer walk on this
547-
# large image completes (last run hit "context deadline exceeded").
548-
timeout: 20m
536+
scanners: ${{ steps.trivy_cfg.outputs.scanners }}
537+
timeout: ${{ steps.trivy_cfg.outputs.timeout }}
549538
limit-severities-for-sarif: true
550539
ignore-unfixed: true
551540
ignore-policy: docker/${{ matrix.image }}/.trivy-ignore-policy.rego

AGENTS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ Both `.trivyignore` and `.trivy-ignore-policy.rego` paths are hardcoded in `dock
173173

174174
Trivy runs twice per image: once as a build gate (table format, `exit-code: 1`), once as SARIF upload to `Security → Code scanning` under category `trivy-<image>`. The weekly cron rescans `:latest` of every image without rebuilding, surfacing newly published CVE data.
175175

176+
**Scan profile is auto-chosen by image size.** `scripts/ci-trivy-profile.sh` inspects the loaded image's `.Size` and picks:
177+
178+
- **Large image** (over `TRIVY_LARGE_IMAGE_BYTES`, default `2147483648` = 2 GiB) — `scanners: vuln`, `timeout: 20m`. Drops the secret scanner, which is too slow on multi-GB scientific containers (it walks every text-ish file and chokes on huge reference DBs / model weights / generated headers) and is intended for source repositories with API keys anyway.
179+
- **Small image** (≤ threshold) — `scanners: vuln,secret`, `timeout: 10m`. Full default scan; small helper images aren't downgraded.
180+
181+
Override the threshold per repo via the `TRIVY_LARGE_IMAGE_BYTES` GitHub Actions repository variable.
182+
176183
## WDL validation
177184

178185
The `miniwdl check --strict` pre-commit hook is the **only** WDL gate. There is no CI safety net. If a contributor commits without running pre-commit (`git commit -n`), broken WDLs land on `main`.

scripts/ci-trivy-profile.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env bash
2+
# Pick a Trivy scan profile based on the local image's uncompressed size.
3+
#
4+
# Big scientific images (multi-env conda + model weights + reference DBs) trip
5+
# the secret scanner's per-file size guard and blow past Trivy's default 5-min
6+
# scan deadline (observed: "context deadline exceeded" on a ~5 GB image). The
7+
# secret scanner is designed for source repositories with API keys, not for
8+
# scientific containers full of binary model weights — dropping it on large
9+
# images is safe.
10+
#
11+
# Smaller images get the full default profile (vuln + secret) at the default
12+
# timeout, so generic helper images aren't downgraded just because they share
13+
# this workflow.
14+
#
15+
# Driven by:
16+
# IMAGE_REF : image ref already loaded locally (required)
17+
# TRIVY_LARGE_IMAGE_BYTES : threshold in bytes (default 2 GiB)
18+
#
19+
# Emits to $GITHUB_OUTPUT:
20+
# scanners=vuln | vuln,secret
21+
# timeout=20m | 10m
22+
# size_bytes=<int> (for logs)
23+
set -euo pipefail
24+
25+
: "${IMAGE_REF:?IMAGE_REF must be set}"
26+
threshold="${TRIVY_LARGE_IMAGE_BYTES:-2147483648}"
27+
28+
size="$(docker image inspect -f '{{.Size}}' "$IMAGE_REF")"
29+
30+
if [ "$size" -gt "$threshold" ]; then
31+
scanners="vuln"
32+
timeout="20m"
33+
echo "::notice title=Trivy profile::Image $IMAGE_REF is $size bytes (> $threshold); vuln-only scan, 20m timeout."
34+
else
35+
scanners="vuln,secret"
36+
timeout="10m"
37+
echo "::notice title=Trivy profile::Image $IMAGE_REF is $size bytes (<= $threshold); full scan (vuln+secret), 10m timeout."
38+
fi
39+
40+
{
41+
echo "scanners=$scanners"
42+
echo "timeout=$timeout"
43+
echo "size_bytes=$size"
44+
} >> "$GITHUB_OUTPUT"

0 commit comments

Comments
 (0)