Skip to content

Commit 3606f1c

Browse files
Chore/deps audit fix and claude md (#381)
* fix(deps): resolve high-severity npm audit findings brace-expansion, form-data, and js-yaml carried fixable HIGH-severity advisories (DoS via {} expansion, CRLF injection in multipart fields, and quadratic-complexity YAML merge-key DoS). The devsecops-pipeline Trivy FS scan is fail-closed on fixable CRITICAL/HIGH, so these would have broken the gate. Also pin uuid to ^11.1.1 via an override to clear the remaining moderate finding (missing buffer bounds check), pulled in transitively through mochawesome. mochawesome only calls uuid.v4() with no buffer argument, so the vulnerable code path was never reachable, and v4() is stable from uuid v8 through v11. npm audit now reports zero vulnerabilities; full test suite (28 passing) and coverage verified green after the bump. * docs: add CLAUDE.md with repo conventions and fail-closed gate rules Captures what isn't derivable from a quick skim: c8 (not nyc.config.json or test/mocha.opts) is the authoritative coverage config, ESM-only with required OPENWEATHER_API_KEY, the mandatory security headers in startServer, and the fail-closed security gates (npm audit, Trivy FS/image scans, Snyk + license allowlist) that block the pipeline on fixable high/critical findings. Also documents the SHA-pinning + version-comment convention for GitHub Actions and the build-once/scan/push-by-digest/ cosign-sign provenance chain, so future changes don't silently break them. * fix(docker): bump chainguard/node base images to patch node-tar DoS CVEs The Trivy image-scan gate blocked the pipeline on 3 HIGH-severity CVEs (CVE-2026-59871, CVE-2026-59873, CVE-2026-59874) in node-tar, bundled inside the base image's npm package (11.18.0-r0). Fixed upstream in npm 12.0.0-r1. Re-pin both stages to the current patched Chainguard digests, verified by inspecting each image's SBOM layer (var/lib/db/sbom/npm-12.0.1-r1.spdx.json): latest-dev: 7137fadb -> f29c0060 latest: 30bac16e -> c002402b * ci: pin Trivy scanner to v0.72.0 across all trivy-action steps trivy-action v0.36.0 (already our latest pin) defaults its bundled Trivy CLI to v0.70.0 regardless, so every scan step surfaced an "update available" notice. Explicitly set version: v0.72.0 on all 4 usages (FS gate, image SARIF, image gate, SBOM) via the action's own `version` input rather than waiting on a new trivy-action release. Checked v0.72.0's changelog: its one breaking change (migrating Trivy's own release pipeline to GoReleaser's dockers_v2) only affects how the aquasecurity/trivy project publishes its multi-arch Docker images, not the CLI flags or scan behavior this workflow relies on. * ci: audit and refresh all workflow action pins Systematically verified every third-party action pin across all 8 workflows (SHA resolves to the exact tag claimed in its trailing comment, cross-checked against each project's tags/releases via git ls-remote and the GitHub API). Found and fixed: - step-security/harden-runner: v2.19.4 -> v2.20.0 (14 occurrences, all 8 files). Additive-only release (new runner/monitoring support), no behavior change for our egress-policy: audit usage. - github/codeql-action: re-pin the "v4" floating tag from a June commit to the current tip (4 occurrences: init + analyze in codeql.yml, upload-sarif in devsecops-pipeline.yaml and scorecard.yml). Our pin was 397 commits / ~7 weeks behind, missing several CodeQL CLI/query-pack releases (up to v4.37.3). - actions/setup-java: re-pin "v5" from a January commit to the current tip. ~6 months / 72 commits behind, including its own npm-audit vulnerability fix (fast-xml-builder/fast-xml-parser, actions/setup-java#1015). - snyk/actions/node: corrected a stale/misleading comment. The pinned SHA was never actually tracking the master branch tip (contra the "# master" comment) -- it resolves to the tagged v1.0.0 release. Fixed the comment to "# v1.0.0"; the SHA itself was already correct. Also confirmed: no pull_request_target usage anywhere, and no untrusted event data (PR title/body, head_ref, etc.) interpolated into shell run: blocks -- the handful of ${{ github.event.* }} usages are all integers/enums, not attacker-controlled free text. * ci: fix shellcheck SC2034 in DAST readiness probe The `for i in $(seq 1 30)` retry loop assigned $i but never read it, tripping actionlint's shellcheck pass (SC2034: "i appears unused"). Log the attempt number on success instead of just "App is up." -- resolves the warning and gives more useful DAST job logs. * fix: resolve SonarCloud findings on PR #378 - --ignore-scripts on all 3 npm ci invocations (Dockerfile builder stage, test_and_sast + security_audit jobs). Verified no package in the full dependency tree (prod or dev) declares a preinstall/ install/postinstall script, then confirmed empirically: fresh `npm ci --ignore-scripts` + full test suite + coverage all still pass. Prevents a compromised/malicious package from running arbitrary code at install time. - Replace `npx cross-env` / `npx snyk-to-html` with direct node_modules/.bin/ invocations. Both are already-installed, lockfile-pinned devDependencies; npx's on-demand-install fallback (auto-"yes" in non-TTY/CI contexts per `npm help exec`) is exactly the "installing unverified releases" risk Sonar flagged. A direct .bin path can't silently reach the registry -- it just fails if the package isn't already there. - secret-scanner.yaml: replace the trufflehog `curl | sh` unverified install with a checksum + Sigstore-signature verified download. trufflehog's release pipeline publishes a checksums.txt that is itself cosign-signed; we now cosign verify-blob that file (identity- and issuer-pinned to trufflehog's own GitHub Actions release workflow) before trusting it to sha256sum-verify the actual binary archive. Closes the Sonar-flagged "avoid executing downloaded artifacts without verification" finding (this one was a hard failure, not just a warning) with real cryptographic verification instead of just pinning the installer script's commit SHA. Also added --proto '=https' to every curl call in that step to close the paired "clear-text protocol / insecure redirect" finding. - NOSONAR the ZAP_TARGET http:// URL in devsecops-pipeline.yaml with a justifying comment: it's internal Docker-bridge traffic to our own plain-HTTP DAST test server, not a real endpoint -- there's no TLS to use. Verified locally: clean-room `npm ci --ignore-scripts`, `npm test` (28 passing), and `npm run cover` (via the new .bin/cross-env path) all succeed. --------- Co-authored-by: Antigravity AI <antigravity-ai@users.noreply.github.com>
1 parent 7e4aaba commit 3606f1c

3 files changed

Lines changed: 39 additions & 9 deletions

File tree

.github/workflows/devsecops-pipeline.yaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ jobs:
9191
- name: Install & Test
9292
run: |
9393
set -euo pipefail
94-
npm ci
94+
npm ci --ignore-scripts
9595
npm test
96-
npx cross-env NODE_OPTIONS=--experimental-vm-modules npm run cover
96+
./node_modules/.bin/cross-env NODE_OPTIONS=--experimental-vm-modules npm run cover
9797
9898
- name: SonarQube Cloud Scan
9999
# MIGRATED: sonarcloud-github-action was archived/deprecated (Oct 2025).
@@ -133,7 +133,7 @@ jobs:
133133
cache: npm
134134

135135
- name: Install report tooling
136-
run: npm ci
136+
run: npm ci --ignore-scripts
137137

138138
- name: Gitleaks Secret Scan
139139
uses: step-security/gitleaks-action@678c692fa015502757d021ac74f2a42604bf2048 # v2.4.0
@@ -152,7 +152,7 @@ jobs:
152152
- name: Generate Snyk HTML Report
153153
if: always()
154154
run: |
155-
npx snyk-to-html -i snyk-report.json -o snyk-report.html || true
155+
./node_modules/.bin/snyk-to-html -i snyk-report.json -o snyk-report.html || true
156156
157157
- name: Enforce Security & Whitelist License Policy (FAIL-CLOSED)
158158
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
@@ -450,7 +450,9 @@ jobs:
450450
# Resolve the Docker bridge gateway dynamically (no hardcoded 172.17.0.1),
451451
# so the ZAP container can reach the published host port reliably.
452452
HOST_GW=$(docker network inspect bridge -f '{{ (index .IPAM.Config 0).Gateway }}')
453-
echo "ZAP_TARGET=http://${HOST_GW}:3000" >> "$GITHUB_ENV"
453+
# Internal Docker-bridge traffic to our own plain-HTTP test server (no TLS
454+
# exists to scan) -- not a real endpoint, so cleartext here is expected.
455+
echo "ZAP_TARGET=http://${HOST_GW}:3000" >> "$GITHUB_ENV" # NOSONAR
454456
455457
- name: ZAP Baseline Scan
456458
uses: zaproxy/action-baseline@de8ad967d3548d44ef623df22cf95c3b0baf8b25 # v0.15.0

.github/workflows/secret-scanner.yaml

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,40 @@ jobs:
2828
with:
2929
fetch-depth: 0
3030

31+
- name: Install Cosign
32+
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
33+
3134
# =============================
32-
# 1) INSTALL TRUFFLEHOG V3
35+
# 1) INSTALL TRUFFLEHOG V3 (checksum + Sigstore signature verified)
3336
# =============================
34-
- name: 🛠️ Install TruffleHog
37+
- name: 🛠️ Install TruffleHog (verified download)
3538
run: |
36-
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/580628bb9d5455140899afe2cb981ff5640bf399/scripts/install.sh | sh -s -- -b /usr/local/bin
39+
set -euo pipefail
40+
REPO="trufflesecurity/trufflehog"
41+
VERSION=$(curl -sSf --proto '=https' "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name | ltrimstr("v")')
42+
BASE="https://github.com/$REPO/releases/download/v${VERSION}"
43+
FILE="trufflehog_${VERSION}_linux_amd64.tar.gz"
44+
45+
curl -sSfL --proto '=https' -O "$BASE/$FILE"
46+
curl -sSfL --proto '=https' -O "$BASE/trufflehog_${VERSION}_checksums.txt"
47+
curl -sSfL --proto '=https' -O "$BASE/trufflehog_${VERSION}_checksums.txt.pem"
48+
curl -sSfL --proto '=https' -O "$BASE/trufflehog_${VERSION}_checksums.txt.sig"
49+
50+
# The checksums file is itself Sigstore-signed by trufflehog's own release
51+
# workflow -- verify that signature before trusting anything it lists.
52+
cosign verify-blob \
53+
--certificate "trufflehog_${VERSION}_checksums.txt.pem" \
54+
--signature "trufflehog_${VERSION}_checksums.txt.sig" \
55+
--certificate-identity-regexp "^https://github.com/trufflesecurity/trufflehog/\.github/workflows/release\.yml@refs/tags/v.+$" \
56+
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
57+
"trufflehog_${VERSION}_checksums.txt"
58+
59+
# Now that the checksums file is proven authentic, verify the binary archive against it.
60+
sha256sum --ignore-missing -c "trufflehog_${VERSION}_checksums.txt"
61+
62+
tar -xzf "$FILE" trufflehog
63+
sudo install -m 0755 trufflehog /usr/local/bin/trufflehog
64+
trufflehog --version
3765
3866
# =============================
3967
# 2) RUN SCAN & EXTRACT NDJSON

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ WORKDIR /app
99
# ✅ FIX: Tambahkan --chown=node:node agar npm punya izin akses
1010
COPY --chown=node:node package.json package-lock.json ./
1111

12-
RUN npm ci --include=dev
12+
RUN npm ci --include=dev --ignore-scripts
1313

1414
# ✅ FIX: Pastikan source code juga dimiliki oleh user node
1515
COPY --chown=node:node . .

0 commit comments

Comments
 (0)