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
16 changes: 16 additions & 0 deletions docs/03-operations/production-readiness-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@ For each completed item record commit/release, environment, date, operator respo

No exception can waive unauthorized access, private-evidence exposure, unresolved critical security/data-loss defects, missing core marketplace functionality or an untested recovery path. A noncritical exception records affected requirement, mitigation, expiry, owner and release impact; it cannot silently redefine the product.

### Temporary dependency-audit exceptions

The dependency audit remains configured at the `low` threshold. A temporary exception is not a remediation and is permitted only when the exact GHSA is named, no released compatible fix exists, dependency provenance and runtime/build exposure are assessed, compensating controls are recorded, and an owner, review-by date, and removal trigger are explicit. Every other advisory remains blocking at the configured threshold. Wildcards, registry-error suppression, reduced severity, and ignore-unfixable behavior are not permitted.

Current temporary exception:

- **Advisory:** `GHSA-86w9-cpqp-85rv`; CVE `CVE-2026-85393`; high severity (CVSS 8.7).
- **Package:** `node-forge@1.4.0`; no patched release is currently published. Upstream fix PR [digitalbazaar/forge#1152](https://github.com/digitalbazaar/forge/pull/1152) is open and targets the unreleased 1.4.1 changelog entry.
- **Provenance:** lock graph places the package in `apps/mobile` through `expo@55.0.0` → `@expo/cli@55.0.11` → `node-forge` and `@expo/code-signing-certificates@0.0.6` → `node-forge`; Expo Router adds equivalent paths. It is not a direct Pachi dependency.
- **Runtime/build exposure:** the production dependency listing for `@pachi/mobile` includes this Expo tooling/signing path. The API, marketplace web, admin and worker production dependency graphs do not include `node-forge`. No direct Pachi source import was found. The mobile bundle's reachability of this code has not been established; the dependency is not claimed harmless.
- **Reason:** the registry's patched-version status is unknown, npm's latest release remains 1.4.0, and the current compatible Expo SDK 55 CLI/signing dependency ranges do not remove the vulnerable release.
- **Compensating controls:** audit severity stays `low`; the guard enforces this one exact GHSA and requires this record; every other advisory continues to fail the dependency check. No unaudited fork or unreleased node-forge commit is used.
- **Owner:** Project owner / repository maintainer.
- **Review by:** 2026-10-16.
- **Removal trigger:** remove the exception when a patched node-forge release becomes available through compatible Expo tooling, or when a compatible Expo update removes the vulnerable dependency. Re-run the unfiltered audit and the full CI pipeline after removal.

## Gate summary

| Gate | Required result | Current evidence state |
Expand Down
9 changes: 9 additions & 0 deletions docs/engineering/current-state.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Engineering handoff

## Temporary dependency-audit exception — 2026-10-02

- **Branch and scope:** `chore/node-forge-audit-exception` starts directly from required `main`/`origin/main` `d5b802c0e7eca70ebb45d03befb5ca7d94c4166b`; PR #22 was not modified. This is a repository-wide security-maintenance change only.
- **Diagnosis and provenance:** exact base command `pnpm check:dependencies` reports one HIGH finding, `GHSA-86w9-cpqp-85rv` / `CVE-2026-85393`, `node-forge@1.4.0`; the structured audit contains zero other advisories at any severity. The unfiltered lockfile query (`pnpm --ignore-workspace audit --audit-level low --json`) confirms 10 dependency paths through Expo/Expo Router → `@expo/cli@55.0.11` → `node-forge` or `@expo/code-signing-certificates@0.0.6` → `node-forge`. `pnpm why` and the lockfile confirm no direct Pachi dependency. Production dependency queries for API, web, admin and worker are empty; mobile's production graph includes the Expo CLI/signing chain. No direct source import or `node-forge` marker was found in generated mobile bundles, but that does not establish harmlessness or prove all runtime reachability.
- **Upstream status:** GitHub Advisory Database reports affected versions `<=1.4.0`, patched versions unknown, HIGH/CVSS 8.7. npm's latest node-forge release/tag remains `1.4.0`; upstream `digitalbazaar/forge#1152` is OPEN with an unreleased 1.4.1 fix. Latest compatible Expo SDK 55 is 55.0.31 with CLI 55.0.36; CLI still declares `node-forge ^1.3.3`, and latest `@expo/code-signing-certificates@0.0.7` retains `node-forge ^1.4.0`. No released compatible upgrade/removal was found.
- **Resolution and local validation:** no package was upgraded. `pnpm-workspace.yaml` contains exactly one GHSA ignore and retains audit level `low`; `check:dependencies` invokes a small policy guard first. The guard checks threshold, exact configuration/document match, exception metadata and its maximum 14-day review window. Guard tests pass 3/3. Configured dependency audit, contracts (49 routes/37 schemas), secrets, mobile lint/typecheck, Expo web/Android/iOS export, full workspace lint/typecheck/tests/build, and `git diff --check` pass. Mobile's test command has no test files (0 tests); startup unit tests include the guard (8/8). The unfiltered diagnostic still reports the HIGH finding; this is temporary acceptance, not remediation.
- **Exact-head CI:** push Checks `36990588319` and PR Checks `36990608628` both PASSED on `027f4df06a0fe908f067a1644e1fbbfdcfe32444`. Both executed contracts, clean test migrations, full DB integration, API integration, lint, typecheck, tests, full build, Chromium installation, web Chromium and admin Chromium. No gates were skipped.
- **Remaining security risk:** the vulnerability is still present and temporarily accepted, not fixed. The dependency audit reports one ignored HIGH finding and zero other findings at any severity in the unfiltered result. The mobile dependency is installed in the Expo tooling/signing graph; generated bundle inspection found no `node-forge` marker, but does not establish harmlessness or rule out every runtime path. Review by 2026-10-16 and remove on the stated fixed-release/Expo-removal triggers.

## Public listing discovery implementation — 2026-10-01

- **Branch and scope:** `feat/public-listing-discovery` is based directly on `origin/main` `199efa3d99379f9d93fecd2ef59ec513ed2612ed` and currently ends at `36ec64f`. The slice is limited to anonymous listing search/detail/media, public location mode, public DTOs/contracts, web discovery/detail routes, and focused evidence. Inquiry, Conversation, messaging, viewing, saves, maps, geocoding and alerts remain out of scope.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"test": "pnpm test:startup && turbo run test",
"db:migrate": "pnpm --filter @pachi/database db:migrate",
"check:secrets": "bash scripts/check-secrets.sh",
"check:dependencies": "pnpm audit --audit-level low",
"check:dependencies": "node scripts/check-dependency-audit-policy.mjs && pnpm audit --audit-level low",
"check:contracts": "node scripts/check-contracts.mjs"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ packages:
allowBuilds:
'@nestjs/core': true
esbuild: true
audit:
level: low
ignore:
- GHSA-86w9-cpqp-85rv
# Security fixes for transitive packages pinned by upstream tooling.
overrides:
'path-to-regexp@>=8.0.0 <8.4.0': 8.4.0
Expand Down
46 changes: 46 additions & 0 deletions scripts/check-dependency-audit-policy.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { readFileSync } from 'node:fs';
import { parse } from 'yaml';

const expectedAdvisories = ['GHSA-86w9-cpqp-85rv'];
const reviewBy = '2026-10-16';

export function validateDependencyAuditPolicy(workspace, readiness, today = new Date().toISOString().slice(0, 10)) {
if (workspace.audit?.level !== 'low') throw new Error('Dependency audit threshold must remain low');
const ignored = workspace.audit?.ignore;
if (!Array.isArray(ignored) || JSON.stringify(ignored) !== JSON.stringify(expectedAdvisories)) {
throw new Error('Dependency audit exceptions must exactly match documented GHSA entries');
}

const documented = [...readiness.matchAll(/^- \*\*Advisory:\*\* `([^`]+)`/gm)].map((match) => match[1]);
if (JSON.stringify(documented) !== JSON.stringify(expectedAdvisories)) {
throw new Error('Documented dependency audit exceptions must exactly match workspace configuration');
}
const section = readiness.split('### Temporary dependency-audit exceptions\n')[1]?.split('\n## ')[0] ?? '';
for (const field of [
'CVE-2026-85393',
'node-forge@1.4.0',
'Provenance:',
'Runtime/build exposure:',
'Reason:',
'Compensating controls:',
'Owner:',
`Review by:** ${reviewBy}`,
'Removal trigger:',
]) {
if (!section.includes(field)) throw new Error(`Temporary audit exception metadata is missing ${field}`);
}

const reviewDate = new Date(`${reviewBy}T00:00:00Z`);
const createdDate = new Date('2026-10-02T00:00:00Z');
const maxReviewDate = new Date(createdDate.getTime() + 14 * 24 * 60 * 60 * 1000);
if (reviewDate <= new Date(`${today}T00:00:00Z`) || reviewDate > maxReviewDate) {
throw new Error('Temporary audit exception review date is overdue or exceeds 14 days');
}
}

if (process.argv[1]?.endsWith('check-dependency-audit-policy.mjs')) {
const workspace = parse(readFileSync('pnpm-workspace.yaml', 'utf8'));
const readiness = readFileSync('docs/03-operations/production-readiness-checklist.md', 'utf8');
validateDependencyAuditPolicy(workspace, readiness);
console.log('Dependency audit policy valid: low threshold; one documented, time-bounded GHSA exception');
}
23 changes: 23 additions & 0 deletions scripts/check-dependency-audit-policy.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';
import { parse } from 'yaml';
import { validateDependencyAuditPolicy } from './check-dependency-audit-policy.mjs';

const workspace = parse(readFileSync('pnpm-workspace.yaml', 'utf8'));
const readiness = readFileSync('docs/03-operations/production-readiness-checklist.md', 'utf8');

test('dependency audit policy accepts the exact documented temporary advisory', () => {
assert.doesNotThrow(() => validateDependencyAuditPolicy(workspace, readiness, '2026-10-02'));
});

test('dependency audit policy rejects a changed threshold or extra ignored advisory', () => {
assert.throws(() => validateDependencyAuditPolicy({...workspace,audit:{...workspace.audit,level:'moderate'}},readiness,'2026-10-02'), /threshold must remain low/);
assert.throws(() => validateDependencyAuditPolicy({...workspace,audit:{...workspace.audit,ignore:[...workspace.audit.ignore,'GHSA-unreviewed']}},readiness,'2026-10-02'), /exactly match documented/);
});

test('dependency audit policy rejects undocumented, incomplete, or overdue exceptions', () => {
assert.throws(() => validateDependencyAuditPolicy(workspace,readiness.replace('Review by:** 2026-10-16','Review date missing'),'2026-10-02'), /metadata is missing/);
assert.throws(() => validateDependencyAuditPolicy(workspace,readiness.replace('GHSA-86w9-cpqp-85rv','GHSA-unreviewed'),'2026-10-02'), /exactly match/);
assert.throws(() => validateDependencyAuditPolicy(workspace,readiness,'2026-10-17'), /overdue/);
});
Loading