Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions .github/actions/npm-audit-pr-comment/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: npm audit PR comment
description: >
Posts or updates a PR comment with pre-existing npm audit findings.
Informational only β€” never fails. Pair with dependency-review-action for gating.

inputs:
working-directory:
description: Directory containing package.json / package-lock.json
required: true
audit-flags:
description: Extra flags forwarded to npm audit (e.g. --omit=dev)
required: false
default: ''
min-severity:
description: Minimum severity to include in the report (info, low, moderate, high, critical)
required: false
default: high
label:
description: Short label shown in the comment header (e.g. "website", "extension")
required: true

runs:
using: composite
steps:
- name: Collect audit findings and post PR comment
uses: actions/github-script@v7
env:
AUDIT_FLAGS: ${{ inputs.audit-flags }}
MIN_SEVERITY: ${{ inputs.min-severity }}
WORK_DIR: ${{ inputs.working-directory }}
LABEL: ${{ inputs.label }}
with:
script: |
const { execSync } = require('child_process')

let auditOutput
try {
auditOutput = execSync(
`npm audit --json ${process.env.AUDIT_FLAGS}`,
{ cwd: process.env.WORK_DIR, stdio: ['pipe', 'pipe', 'pipe'] }
).toString()
} catch (e) {
auditOutput = e.stdout?.toString() ?? '{}'
}

const SEVERITY_RANK = ['info', 'low', 'moderate', 'high', 'critical']
const minSeverity = (process.env.MIN_SEVERITY || 'high').toLowerCase()
const minRank = Math.max(0, SEVERITY_RANK.indexOf(minSeverity))

let report
try {
const json = JSON.parse(auditOutput)
const advisories = new Map(
Object.values(json.vulnerabilities ?? {})
.flatMap(v => (Array.isArray(v.via) ? v.via : [v.via]))
.filter(v => v && typeof v === 'object' && v.url && SEVERITY_RANK.indexOf(v.severity) >= minRank)
.map(v => [v.url, v])
)

const rows = [...advisories.values()].map(a =>
`| ${a.severity} | [${a.title}](${a.url}) | \`${a.module_name ?? a.name}\` |`
)

report = rows.length === 0
? `βœ… No vulnerabilities found (severity β‰₯ ${minSeverity}).`
: [
`⚠️ **Pre-existing vulnerabilities in \`${process.env.LABEL}\` (severity β‰₯ ${minSeverity})** β€” not introduced by this PR, informational only.`,
'',
`| Severity | Advisory | Package |`,
`|----------|----------|---------|`,
...rows,
'',
`> These were already present on the base branch. Use \`npm audit fix\` or upgrade dependencies to resolve them.`,
].join('\n')
} catch (_) {
report = `⚠️ Could not parse audit output for \`${process.env.LABEL}\`.`
}

const marker = `<!-- npm-audit-comment:${process.env.LABEL} -->`
const body = `${marker}\n### npm audit β€” \`${process.env.LABEL}\`\n\n${report}`

const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
})

const existing = comments.find(c => c.body.includes(marker))
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
})
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
})
}
36 changes: 36 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Dependency Review

# Single repo-wide gate for newly-introduced vulnerabilities.
# dependency-review-action diffs the PR's dependency graph (base -> head), so it
# only flags dependencies CHANGED by this PR β€” per-package scoping comes for free.
# Pre-existing vulnerabilities are reported separately (non-blocking) by the
# npm-audit-pr-comment action in each package's CI workflow.
on:
pull_request:
branches: [main]
paths:
- '**/package.json'
- '**/package-lock.json'

jobs:
dependency-review:
name: Dependency Review (fail on new vulnerabilities)
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@v2
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@v4

- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: moderate
vulnerability-check: true
comment-summary-in-pr: on-failure
13 changes: 10 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
build:
name: Build Documentation
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@v2
Expand All @@ -46,9 +49,13 @@ jobs:
working-directory: website
run: npm ci

- name: Audit for vulnerabilities
working-directory: website
run: npm audit --audit-level=high
- name: Report pre-existing vulnerabilities on PR
if: github.event_name == 'pull_request'
uses: ./.github/actions/npm-audit-pr-comment
with:
working-directory: ${{ github.workspace }}/website
min-severity: high
label: website

- name: Build documentation
working-directory: website
Expand Down
20 changes: 16 additions & 4 deletions .github/workflows/lib-collection-scripts-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,27 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: "fs"
scan-ref: lib
format: "sarif"
output: "trivy-lib-results.sarif"

- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: "trivy-lib-results.sarif"
category: lib

- name: Setup Node.js
uses: actions/setup-node@v4
with:
Expand All @@ -56,10 +72,6 @@ jobs:
working-directory: lib
run: npm run lint

- name: Audit for vulnerabilities
working-directory: lib
run: npm audit --audit-level=high

- name: Run tests
working-directory: lib
run: npm test
Expand Down
17 changes: 13 additions & 4 deletions .github/workflows/vscode-extension-secure-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ jobs:
name: Security Analysis & Vulnerability Scanning
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
pull-requests: write

steps:
- name: Harden Runner
Expand Down Expand Up @@ -62,12 +64,19 @@ jobs:
npm ci --fund=false
npm run build

- name: Install dependencies with audit
- name: Install dependencies
working-directory: ${{ env.EXTENSION_DIR }}
shell: bash
run: |
npm ci --audit --fund=false
npm audit --omit=dev --audit-level=moderate
run: npm ci --fund=false

- name: Report pre-existing vulnerabilities on PR
if: github.event_name == 'pull_request'
uses: ./.github/actions/npm-audit-pr-comment
with:
working-directory: ${{ github.workspace }}
audit-flags: --omit=dev
min-severity: moderate
label: extension

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
Expand Down
Loading