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
1 change: 1 addition & 0 deletions .audit-allowlist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
33 changes: 32 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,40 @@ jobs:
- name: Validate openapi.yaml
run: swagger-cli validate openapi.yaml

dependency-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Audit dependencies
run: node scripts/audit-deps.js

dependency-review:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4

- name: Review dependency changes
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high

build-test:
runs-on: ubuntu-latest
needs: validate-openapi
needs: [validate-openapi, dependency-audit]
steps:
- uses: actions/checkout@v4

Expand Down
84 changes: 68 additions & 16 deletions docs/CI.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,68 @@
# CI pipeline

GitHub Actions workflow `.github/workflows/ci.yml` runs on every push/PR to `main`:

1. **validate-openapi** — `swagger-cli validate openapi.yaml`
2. **build-test** — `npm ci`, `npm run lint`, `npm run build`, `npm test`, `npm run test:coverage`

Coverage thresholds are enforced in `jest.config.js`:
- **Statements:** 92 % (global), **Branches:** 86 %, **Functions:** 95 %, **Lines:** 92 %
- Impacted module `server.ts` exceeds 95 % coverage on all metrics except functions (the `require.main === module` guard is not exercised in tests).

Upload the `coverage/` artifact from CI when debugging threshold failures locally:

```bash
npm run test:coverage
```
# CI pipeline

GitHub Actions workflow `.github/workflows/ci.yml` runs on every push/PR to `main`:

## Jobs

1. **validate-openapi** — `swagger-cli validate openapi.yaml`
2. **dependency-audit** — `node scripts/audit-deps.js` (wraps `npm audit --json` with allowlist filtering)
3. **dependency-review** — `actions/dependency-review-action@v4` flags newly introduced vulnerable packages on PRs
4. **build-test** (depends on validate-openapi + dependency-audit) — `npm ci`, `npm run lint`, `npm run build`, `npm test`, `npm run test:coverage`

## Coverage thresholds

Coverage thresholds are enforced in `jest.config.js`:
- **Statements:** 92 % (global), **Branches:** 86 %, **Functions:** 95 %, **Lines:** 92 %
- Impacted module `server.ts` exceeds 95 % coverage on all metrics except functions (the `require.main === module` guard is not exercised in tests).

Upload the `coverage/` artifact from CI when debugging threshold failures locally:

```bash
npm run test:coverage
```

## Dependency audit

The `dependency-audit` job runs `scripts/audit-deps.js` which:

1. Calls `npm audit --json` to enumerate known vulnerabilities
2. Reads `.audit-allowlist.json` to exclude advisories with no available fix
3. Fails the build if any high or critical vulnerabilities remain after filtering

### Allowlist

When a vulnerability has no available fix, add it to `.audit-allowlist.json` at the project root:

```json
[
{
"id": "GHSA-xxxx-xxxx-xxxx",
"reason": "No fix available yet — tracked in issue #NNN",
"expires": "2026-10-01"
}
]
```

**Fields:**
- `id` — Advisory identifier (GHSA ID or numeric npm advisory ID)
- `reason` — Brief justification and link to tracking issue
- `expires` — ISO date after which the entry is ignored (time-boxed)

**Policy:**
- Every allowlist entry must have an expiration date (time-boxed)
- Expired entries are automatically ignored by the audit script
- Entries should be removed once a fix is released and the dependency is updated

### Local audit

Run the same check locally before pushing:

```bash
node scripts/audit-deps.js
```

Requires `npm ci` to have been run first so that `node_modules` and `package-lock.json` are present.

## Dependency review

The `dependency-review` job runs only on pull requests. It uses `actions/dependency-review-action@v4` to compare the dependency changes in the PR against the base branch and fails if newly introduced packages have known high-severity vulnerabilities.
24 changes: 24 additions & 0 deletions issue349.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Add add a dependency audit and review job to the CI workflow
Description
.github/workflows/ci.yml validates OpenAPI, lints, builds, tests, and uploads coverage, but never checks the dependency tree for known vulnerabilities or license drift.

Requirements and context
Repository scope: StableRoute-Org/Stableroute-backend only.
Add an npm audit --audit-level=high step that fails the build on high or critical findings.
Add actions/dependency-review-action on pull requests to flag newly introduced vulnerable packages.
Allow a documented, time-boxed allowlist for advisories with no available fix.
Suggested execution
Fork the repo and create a branch
git checkout -b security/ci-add-a-dependency-audit-and
Write code in: .github/workflows/ci.yml
Write comprehensive tests in: src/__tests__/ciWorkflow.test.ts
Add documentation: docs/CI.md
Test and commit
Run npm test, npm run lint
Cover edge cases; include test output
Example commit message
ci(security): add npm audit and dependency review jobs

Guidelines
Minimum 95 percent test coverage for impacted modules
Clear documentation
51 changes: 45 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
],
"license": "MIT",
"dependencies": {
"@types/js-yaml": "^4.0.9",
"cors": "^2.8.5",
"express": "^4.21.0",
"helmet": "^8.3.0",
"iconv-lite": "^0.7.3",
"js-yaml": "^5.2.2",
"pino": "^9.6.0"
},
"devDependencies": {
Expand Down
35 changes: 35 additions & 0 deletions scripts/audit-deps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require("ts-node").register({ transpileOnly: true });
const { checkDependencies } = require("../src/ci/audit");

function main() {
let result;
try {
result = checkDependencies();
} catch (err) {
console.error("Dependency audit error:", err.message);
process.exit(2);
}

if (result.violations.length > 0) {
console.error("Dependency audit FAILED:");
for (const v of result.violations) {
console.error(" [%s] %s: %s (%s)", v.severity.toUpperCase(), v.packageName, v.title, v.id);
if (v.url) console.error(" %s", v.url);
}
}

if (result.allowed.length > 0) {
console.log("Allowlisted advisories (expiration pending):");
for (const v of result.allowed) {
console.log(" [%s] %s: %s (%s)", v.severity.toUpperCase(), v.packageName, v.title, v.id);
}
}

if (result.violations.length === 0) {
console.log("Dependency audit PASSED.");
}

process.exit(result.pass ? 0 : 1);
}

main();
Loading
Loading