---
title: Secure Build and Supply-Chain Policy
tags: [audit, security, supply-chain, build, policy, ci-cd]
created: 2026-05-24
status: active-policy
---

# Secure Build and Supply-Chain Policy

## 1. Policy Statement

Amanat is a financial escrow platform handling cryptocurrency pay-in, payout, dispute-sensitive fund movement, and provider webhook processing. A single compromised dependency in either the frontend (Next.js) or backend (Express/Node.js) application can result in unauthorized fund release, private data exfiltration, or webhook forgery.

The Node.js/npm ecosystem has demonstrated real and recurring supply-chain risk. In the first five months of 2026 alone, four significant incidents directly affected packages used by this platform:

- Express/Multer high-severity vulnerability (February 2026).
- Node.js security releases across active release lines (March 2026).
- Axios npm supply-chain compromise reported by Microsoft (March 2026).
- TanStack npm supply-chain compromise postmortem (May 2026).

This policy establishes mandatory controls for dependency management, vulnerability response, build integrity, and secrets handling across both the frontend and backend codebases. Compliance with this policy is required for all engineers, CI/CD pipelines, and deployment processes.

**Scope:** This policy covers the Amanat frontend (`nickapp-frontend`, Next.js) and backend (`nickapp-backend`, Express/Node.js). It applies to all environments: local development, CI, staging, and production.

**Ownership:** The engineering lead or designated security owner is accountable for enforcing this policy. If a dedicated security owner has not been assigned, the backend lead shall act as default policy owner until one is named. See [[Security Architecture]] for the broader security model.

**References:** [[Backend Stack Security and Refactor Assessment - 2026-05-24]], [[Platform Logical Audit - 2026-05-24]], [[Backend Architecture]], [[Frontend Architecture]], [[Infrastructure]], [[Environment Variables]]

---

## 2. Package Manager and Lockfile Policy

### 2.1 Lockfile commits are mandatory

Every repository must commit its lockfile to version control. The lockfile is the authoritative record of resolved dependency versions.

- Backend: `package-lock.json` must be committed.
- Frontend: `package-lock.json` (or `yarn.lock` / `pnpm-lock.yaml` if the frontend uses an alternative package manager) must be committed.

A PR that modifies `package.json` without a corresponding lockfile update shall be rejected.

### 2.2 Frozen install in CI/CD

All CI/CD pipelines and production build processes must use frozen install mode. This prevents silent resolution changes and ensures the build matches the committed lockfile exactly.

```bash
# Required in CI
npm ci

# Forbidden in CI
npm install
```

For non-npm package managers:
- Yarn: `yarn install --frozen-lockfile`
- pnpm: `pnpm install --frozen-lockfile`

If the frozen install fails due to a lockfile mismatch, the CI job must fail. The developer must regenerate the lockfile locally and commit it.

### 2.3 Lockfile diff review in PRs

Any PR that adds, removes, or updates dependencies must include the lockfile diff in the PR description or as an attached artifact. Reviewers must verify:

- No unexpected transitive dependency additions.
- No version downgrades.
- No substitution of a known package with an identically named package from a different registry or scope.

### 2.4 npm install forbidden in production builds

The `npm install` command (non-frozen mode) must never appear in:

- Dockerfile production stages.
- CI build steps.
- Deployment scripts.

Production Dockerfiles must use multi-stage builds where the install stage runs `npm ci` and the runtime stage copies `node_modules` from the builder. Both current Dockerfiles (`backend/Dockerfile.prod`, `frontend/Dockerfile`) already follow this pattern and must continue to do so.

---

## 3. Dependency Update Cadence

### 3.1 Routine updates

Routine dependency updates (minor and patch versions with no known security implications) shall be performed on a biweekly cadence. The responsible engineer must:

1. Run `npm outdated` on both repositories.
2. Update non-breaking packages.
3. Run the full test suite.
4. Submit a PR with the lockfile diff for review.
5. Obtain approval from at least one other engineer before merging.

### 3.2 Security-critical updates

Security-critical updates (packages with published CVEs or advisories rated Critical or High) must be applied immediately, within the SLAs defined in Section 4. The responsible engineer must:

1. Identify the minimum version that resolves the vulnerability.
2. Update the package and lockfile.
3. Run the full test suite.
4. Submit and merge the PR with expedited review (no more than 24 hours).
5. Deploy to production within the SLA window.

### 3.3 Major version bumps

Major version bumps (semver major) require:

1. A dedicated issue documenting the reason for the upgrade.
2. Review of the package changelog and migration guide.
3. Identification of breaking changes affecting Amanat code.
4. Testing in a dedicated branch or environment before merge.
5. Approval from the engineering lead or security owner.

Major version bumps must not be bundled with other changes. Each major bump is a separate PR.

### 3.4 Approval authority

| Update type | Approver |
|---|---|
| Patch (no security advisory) | Any engineer (one approval) |
| Minor (no security advisory) | Any engineer (one approval) |
| Patch or minor (with security advisory) | Engineering lead or security owner |
| Major (any) | Engineering lead or security owner |
| New dependency addition | Engineering lead or security owner |

---

## 4. Security Advisory Monitoring

### 4.1 Automated vulnerability scanning

Both repositories must be enrolled in automated vulnerability scanning. At minimum, one of the following must be active:

- **GitHub Dependabot** (if hosted on GitHub) or equivalent Gitea-native scanning.
- **npm audit** as a required CI step (exit on Critical or High).
- **Snyk** or equivalent commercial scanner (recommended for production use).

The CI pipeline must run vulnerability scanning on every push and on a daily scheduled basis.

### 4.2 Monitoring sources

Engineers must monitor the following sources for security advisories:

| Source | URL or method | Frequency |
|---|---|---|
| GitHub Security Advisories | `github.com/advisories` or Dependabot alerts | Automated |
| npm audit | `npm audit --audit-level=high` in CI | Every build |
| Snyk vulnerability DB | Snyk dashboard or CLI | Continuous |
| Dependabot / Renovate | Automated PRs for vulnerable deps | Continuous |
| Express security page | `https://expressjs.com/en/security.html` | Manual review monthly |
| Node.js security blog | `https://nodejs.org/en/blog/vulnerability` | Manual review monthly |
| Project-specific package feeds | TanStack, Axios, MUI, wagmi release notes | As released |

### 4.3 Response SLAs

When a vulnerability is identified in a dependency used by either repository, the following response timelines apply:

| Severity | Maximum time to patched version in production | Required action |
|---|---|---|
| Critical | 24 hours | Immediate patch or pin, deploy within SLA |
| High | 72 hours | Patch or pin within 48 hours, deploy within SLA |
| Medium | 1 week | Schedule in next sprint or maintenance window |
| Low | Next routine update cycle (biweekly) | Track, no expedited action required |

Severity ratings follow the advisory source (CVSS score or npm audit severity). When multiple sources disagree, the highest severity wins.

### 4.4 Vulnerability response process

When a vulnerability is discovered:

1. **Assess:** Determine severity, affected versions, and whether the Amanat codebase is actually impacted (some vulnerabilities require specific configuration or usage patterns).
2. **Patch or pin:** Update to the fixed version if available. If no fix exists, pin or remove the dependency. If pinning is not feasible, implement a compensating control (input validation, route restriction, feature flag) and document the exception.
3. **Verify:** Run the full test suite. Perform manual verification if the vulnerability affects payment, auth, or webhook flows.
4. **Deploy:** Follow the expedited deployment path if the SLA requires it. Bypass non-critical CI gates only with engineering lead approval.

All vulnerability responses must be recorded in the Known Exposure Register (Section 5).

---

## 5. Known Exposure Register

This register tracks supply-chain exposures identified during the security assessment dated 2026-05-24. It must be updated whenever a new exposure is discovered or an existing one is resolved.

| # | Package | Current Version | Vulnerability | Severity | Status | Action Required | SLA Deadline | Date Opened |
|---|---|---|---|---|---|---|---|---|
| 1 | `multer` | `^2.0.2` | Express Feb 2026 security release: high-severity Multer issues affecting versions before 2.1.0. Reference: https://expressjs.com/2026/02/27/security-releases.html | High | Open | Update to `>=2.1.0`. Verify resolved lockfile version. Run full test suite. | 2026-05-27 | 2026-05-24 |
| 2 | `axios` | In use (frontend + backend) | npm supply-chain compromise reported by Microsoft, March 2026. Reference: https://www.microsoft.com/en-us/security/blog/2026/04/01/mitigating-the-axios-npm-supply-chain-compromise/ | Critical | Open | Verify installed version is post-compromise clean build. If affected version was installed, rotate all credentials transmitted via Axios interceptors and review access logs for anomalous outbound requests. Update to latest clean version. | 2026-05-25 | 2026-05-24 |
| 3 | `@tanstack/react-query` | In use (frontend) | npm supply-chain compromise, May 2026. Reference: https://tanstack.com/blog/npm-supply-chain-compromise-postmortem | Critical | Open | Verify installed version is post-compromise clean build. If affected version was installed, review browser bundle for injected code, rotate any credentials accessible from the React query context, and audit client-side outbound network requests in production logs. Update to latest clean version. | 2026-05-25 | 2026-05-24 |
| 4 | `express` | In use (backend) | February 2026 security releases affecting Express core. Reference: https://expressjs.com/2026/02/27/security-releases.html | High | Open | Verify installed version is at or above the patched release. Run `npm audit` to confirm no residual advisory. | 2026-05-27 | 2026-05-24 |
| 5 | `node` | `node:22-alpine` (Docker) | Node.js March 2026 security releases across active release lines. Reference: https://nodejs.org/en/blog/vulnerability/march-2026-security-releases | High | Open | Update base Docker image to the latest patched Node.js 22.x release. Rebuild both Docker images. Verify runtime compatibility. | 2026-05-27 | 2026-05-24 |

**Register maintenance rules:**

- Every entry must have an owner, a status (Open, In Progress, Resolved, Accepted Risk), and a target resolution date.
- Resolved entries must not be deleted. Change status to Resolved and record the resolution details.
- Accepted Risk entries require engineering lead approval and a documented justification.

---

## 6. npm Provenance and Signature Policy

### 6.1 Provenance verification

Where npm packages offer provenance attestations (signed build metadata linking a published package to its source repository and build process), engineers must verify provenance before adopting or updating the dependency.

Verification methods:

```bash
# Check npm provenance for a package
npm audit signatures

# Verify provenance attestation (npm 10+)
npm view <package> --json | jq '.attestations'
```

### 6.2 Packages without provenance

Packages that do not offer provenance attestations are not automatically blocked, but they require additional scrutiny:

- Verify the package maintainer identity (GitHub account history, organization membership).
- Check the package download count and publication date for anomalies.
- Review recent version publish history for suspicious patterns (rapid successive publishes, new maintainers added before a release).
- Prefer packages with provenance, two-factor authentication on the maintainer account, and a history of responsible disclosure.

### 6.3 Allowlist and blocklist

The following rules apply:

| Rule | Enforcement |
|---|---|
| Packages from the `@types` scope are permitted without additional review. | Automatic |
| Packages from verified first-party scopes (`@nestjs`, `@mui`, `@tanstack`, `@sentry`, `@next`) are permitted with standard review. | Automatic |
| Any new package not previously used in the project requires engineering lead or security owner approval. | Manual gate |
| Packages known to be compromised or abandoned must be added to the project blocklist. | Immediate upon discovery |

**Blocklist mechanism:** Create a `BLOCKLIST.md` file in each repository root. CI must check that no blocklisted package appears in the dependency tree. This check can be implemented as a CI step:

```bash
# Example CI check
while IFS= read -r pkg; do
  if npm ls "$pkg" 2>/dev/null | grep -q "$pkg"; then
    echo "BLOCKED: $pkg found in dependency tree"
    exit 1
  fi
done < BLOCKLIST.md
```

---

## 7. Secrets Handling

### 7.1 Storage

Secrets must be stored exclusively in:

- Environment variables injected at runtime (`.env` files for local development, Compose `env_file` for Docker, or a secrets manager for multi-host deployments).
- A dedicated secrets manager (HashiCorp Vault, AWS SSM, or GCP Secret Manager) for production multi-host deployments.

Secrets must never be stored in:

- Source code (any file committed to git).
- Docker image layers (environment variables baked into `ENV` directives in production Dockerfiles, except for non-secret `NEXT_PUBLIC_*` values).
- Build artifacts or logs.
- Client-side JavaScript bundles (except intentionally public values prefixed `NEXT_PUBLIC_`).

See [[Environment Variables]] for the complete catalog and classification of all environment variables.

### 7.2 Rotation

| Secret type | Rotation frequency | Method |
|---|---|---|
| `JWT_SECRET` | Annually, or immediately upon suspected compromise | Generate new value (`openssl rand -hex 32`), update in all environments during maintenance window. Invalidates all active sessions. |
| `REFRESH_TOKEN_SECRET` | Annually, or immediately upon suspected compromise | Same process as `JWT_SECRET`. |
| `SHKEEPER_WEBHOOK_SECRET` | Every 6 months | Coordinate with SHKeeper dashboard. Set new secret, verify delivery, remove old. |
| `SHKEEPER_API_KEY` | Every 6 months | Generate new key in SHKeeper dashboard, update backend env. |
| `SMTP_PASS` | Every 6 months | Generate new app password, update backend env. |
| `OPENAI_API_KEY` | Every 6 months | Generate new key in OpenAI dashboard, update backend env. |
| Database passwords | Annually | Update in connection string + database user. |
| `REDIS_PASSWORD` | Annually | Update in compose entrypoint + backend `REDIS_URI`. |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_*` | Every 6 months | Generate new key, rebuild frontend Docker image. |
| `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` | Annually | Generate in WalletConnect dashboard, rebuild frontend. |

### 7.3 Leaked secrets response

If any secret is discovered in a git commit, build log, or publicly accessible location:

1. **Immediate:** Rotate the compromised secret within 1 hour. Do not wait for investigation.
2. **Audit:** Review access logs and audit trails for the compromised secret. Determine whether the secret was used by an unauthorized party.
3. **Assess:** If the secret provided access to financial data, payment endpoints, or admin functions, escalate to the engineering lead and initiate a full incident review.
4. **Clean:** Remove the secret from git history using `git filter-branch` or BFG Repo-Cleaner if the repository is public. Force-push only with engineering lead approval.
5. **Document:** Record the leak and remediation in the incident log.

### 7.4 Frontend public env handling

`NEXT_PUBLIC_*` environment variables are embedded in the browser bundle at build time. The following rules apply:

- Never put secrets (API keys with write access, private keys, webhook secrets) in `NEXT_PUBLIC_*` variables.
- Accept that `NEXT_PUBLIC_*` values (Alchemy keys, WalletConnect ID, Sentry DSN) are visible to any browser user. These services must be configured with domain restrictions and rate limits to mitigate exposure.
- The current Dockerfile hard-codes several `NEXT_PUBLIC_*` values via `ENV` directives. This is acceptable only for non-secret public values. See [[Frontend Architecture]] for the build process and [[Infrastructure]] for Docker configuration.

---

## 8. Production Build Reproducibility

### 8.1 Deterministic builds required

Every production build must be deterministic: the same source code, lockfile, and build environment must produce bit-identical output. This enables verification that a deployed artifact matches its source.

### 8.2 Requirements for reproducible builds

| Requirement | Implementation |
|---|---|
| Frozen dependency resolution | `npm ci` with committed lockfile (see Section 2) |
| Pinned base image | Dockerfile must use a specific Node.js version tag (e.g., `node:22.x.y-alpine`), not `node:22-alpine` or `latest` |
| No floating versions in Dockerfile | Every `RUN npm install` or equivalent uses `npm ci` with the committed lockfile |
| Build-time timestamp normalization | If timestamps matter for output comparison, set `SOURCE_DATE_EPOCH` in the build environment |
| No network calls during build except package install | Build scripts must not fetch remote resources at build time (no `curl`, `wget`, or runtime API calls in Dockerfile build stages) |

### 8.3 Build artifact verification in CI

CI must produce a build artifact (Docker image) with a verifiable identity:

```bash
# Tag the image with the git commit SHA
docker build -t nickapp-backend:${GIT_COMMIT_SHA} .

# Record the image digest
docker inspect --format='{{index .RepoDigests 0}}' nickapp-backend:${GIT_COMMIT_SHA}
```

The production deployment must reference the image by digest, not just by tag. Watchtower must be reconfigured to watch a specific versioned tag (e.g., `nickapp-backend:v2.6.3`) rather than `latest`. See [[Infrastructure]] for the current Watchtower configuration and its known risks.

### 8.4 Current gaps

The current production setup has the following reproducibility gaps that must be resolved:

1. Docker base images use rolling tags (`node:22-alpine`) instead of pinned versions.
2. Watchtower auto-deploys from the `latest` tag with no staging gate.
3. No build digest verification or artifact signing.

---

## 9. Frontend vs Backend Supply-Chain Separation

### 9.1 Frontend npm risk profile

The frontend (Next.js) has a larger dependency tree due to React, MUI, TanStack, wagmi, TipTap, i18next, and their transitive dependencies. The frontend runs in the user's browser, so a compromised dependency can:

- Exfiltrate authentication tokens from `localStorage` or memory.
- Inject malicious code into the rendered page (XSS via dependency).
- Modify API requests via the Axios interceptor chain.
- Access wallet connection state (wagmi/Web3 context).

However, the frontend cannot directly execute server-side operations or access backend secrets. The blast radius of a frontend compromise is limited to the authenticated user's session and data visible to that user.

### 9.2 Backend core risk profile

The backend (Express) has a smaller dependency surface but far higher stakes. A compromised backend dependency can:

- Access database credentials and read/write any document.
- Access SHKeeper API keys and webhook secrets.
- Intercept or forge webhook payloads.
- Bypass authentication or authorization middleware.
- Access OpenAI API keys and SMTP credentials.
- Directly manipulate payment records and escrow state.

The backend must be treated as the higher-risk surface for supply-chain purposes, even though the frontend has more dependencies.

### 9.3 Policy for backend core rewrite

If the backend payment/auth/escrow core is extracted to Go, Kotlin, or Rust as recommended in [[Backend Stack Security and Refactor Assessment - 2026-05-24]], the following supply-chain separation applies:

| Component | Package ecosystem | Supply-chain policy |
|---|---|---|
| Payment/ledger/auth core (Go/Kotlin/Rust) | Go modules / Maven / Cargo | This policy applies analogously: lockfile commits, frozen install, vulnerability scanning, provenance verification |
| Marketplace/chat/notification API (remaining Node.js) | npm | This policy applies in full |
| Frontend (Next.js) | npm | This policy applies in full |

The rewritten core must not share `node_modules` with any remaining Node.js services. Cross-service communication must use authenticated internal APIs, not shared memory or shared filesystem.

### 9.4 Interim policy (both services Node-based)

While both frontend and backend remain on Node.js/npm:

1. The two repositories must maintain separate `package.json` and lockfiles. No shared `node_modules`.
2. A dependency that appears in both repositories (e.g., `axios`) must be independently reviewed and version-pinned in each.
3. Backend dependencies must be reviewed more stringently than frontend dependencies. Any dependency added to the backend must justify why it is necessary and whether a lighter alternative exists.
4. The backend must minimize its dependency count. Before adding a new backend dependency, the engineer must document: (a) what it does, (b) why existing dependencies or the Node.js standard library cannot achieve the same result, (c) the package's maintenance status and security history.

---

## 10. Incident Response for Supply-Chain Compromise

### 10.1 Compromised npm package discovered

**Detection signal:** Automated vulnerability scanner alert, security advisory publication, community report, or anomalous behavior in production.

**Immediate containment (0-1 hour):**

1. Determine which Amanat services (frontend, backend, or both) depend on the compromised package.
2. If the package is in the backend: block the affected routes at the Nginx level if the compromise affects payment, auth, or webhook processing.
3. If the package is in the frontend: assess whether the compromise can exfiltrate user tokens or modify API calls. If so, consider taking the frontend offline or deploying a known-good previous build.
4. Notify the engineering lead and all on-call engineers.

**Assessment (1-4 hours):**

1. Identify the compromised versions and the attack vector (malicious code injection, credential theft, data exfiltration).
2. Determine the clean version range (if available) from the package maintainers or security advisory.
3. Audit production logs and access records for indicators of exploitation specific to the compromise.
4. If the package had access to secrets (API keys, tokens), list all potentially exposed secrets.

**Remediation (4-24 hours):**

1. Update the package to a verified clean version, or remove it if no clean version exists.
2. Rotate all secrets that were accessible to the compromised package (see Section 7.3).
3. Deploy the fix through the standard CI pipeline with expedited review.
4. Verify the fix in staging before production deployment if time permits.

**Communication:**

1. Inform all team members of the compromise, the remediation, and any required user-facing actions.
2. If user data was exfiltrated, notify affected users and comply with applicable data breach regulations.
3. Document the incident in the Known Exposure Register (Section 5) and in the incident log.

**Post-incident review (within 1 week):**

1. Conduct a blameless post-mortem. Identify what detected the compromise (or why detection was delayed).
2. Determine whether additional controls could have prevented or detected the compromise earlier.
3. Update this policy if gaps were identified.
4. Add the compromised package to the blocklist if it remains untrusted.

### 10.2 Leaked API key or secret

**Detection signal:** Secret found in git history, build log, public repository, or unexpected API usage pattern.

**Immediate containment (0-1 hour):**

1. Rotate the leaked secret immediately. Do not wait for assessment. See Section 7.3.
2. If the secret provides database access, consider restricting network access to known IPs as an additional control while the new secret propagates.
3. Notify the engineering lead.

**Assessment (1-4 hours):**

1. Determine the scope of the leak: which environments, which time period, which services.
2. Audit access logs for the compromised secret. Look for access from unrecognized IPs, unusual request patterns, or data exfiltration.
3. Determine whether any financial data, user PII, or payment records were accessed.

**Remediation (4-24 hours):**

1. Remove the secret from git history if the repository is public or shared outside the team.
2. Verify the new secret is active and the old secret is revoked.
3. Scan all code and configuration for other instances of the same secret.

**Communication and post-incident review:** Follow the same process as Section 10.1.

### 10.3 Vulnerable dependency alert (non-compromised)

**Detection signal:** Automated scanner alert, Dependabot PR, npm audit failure.

**Process:**

1. Triage the vulnerability against the SLAs in Section 4.3.
2. Assess whether the Amanat codebase is actually affected (some vulnerabilities require specific configuration or API usage).
3. If affected: patch, test, deploy within the SLA.
4. If not affected: document the assessment in the Known Exposure Register with status Accepted Risk and the justification.
5. No post-incident review required unless the response exceeded the SLA.

---

## 11. CI/CD Enforcement Checklist

Every CI pipeline for both frontend and backend must enforce the following checks. A pipeline that does not pass all checks must not produce a deployable artifact.

| # | Check | Enforcement | Failure action |
|---|---|---|---|
| 1 | **Frozen lockfile install** | Pipeline uses `npm ci` (or `--frozen-lockfile` equivalent). Job fails if lockfile is out of sync with `package.json`. | Block merge |
| 2 | **npm audit / vulnerability scan passes** | `npm audit --audit-level=high` exits 0. No unresolved Critical or High vulnerabilities. | Block merge |
| 3 | **No new Critical/High vulnerabilities** | Delta check: the PR must not introduce new Critical or High vulnerabilities not present in the base branch. | Block merge |
| 4 | **Lockfile diff reviewed if changed** | If `package-lock.json` or `yarn.lock` is modified, require at least one reviewer acknowledgment in the PR. | Block merge |
| 5 | **Build is deterministic** | Build artifacts are tagged with the git commit SHA. Base images use pinned versions, not rolling tags. | Block merge |
| 6 | **No secrets in build output** | Grep the build output and Docker image layers for patterns matching private keys, API key formats (`sk-...`, `AKIA...`), and known secret prefixes. | Block merge and alert |
| 7 | **Dependency count change flagged** | If the total number of resolved dependencies changes by more than 5, flag the PR for additional review. | Require lead approval |
| 8 | **Blocklist check** | Verify no blocklisted package appears in the resolved dependency tree. | Block merge |
| 9 | **Test suite passes** | `npm run test:all` (backend) and equivalent frontend tests exit 0. | Block merge |
| 10 | **Node.js version matches production** | CI build runs on the same Node.js major.minor version as the production Docker image. | Block merge |

### Example CI configuration (Gitea Actions)

```yaml
# .gitea/workflows/security-check.yml
name: Supply-Chain Security Check
on: [push, pull_request]

jobs:
  supply-chain:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Use Node.js 22.x.y (pin to production version)
        uses: actions/setup-node@v4
        with:
          node-version: '22.x.y'
          cache: 'npm'

      - name: Install dependencies (frozen)
        run: npm ci
        # If this fails, lockfile is out of sync. Block merge.

      - name: Audit for vulnerabilities
        run: npm audit --audit-level=high
        # Fails if any High or Critical vulnerability exists.

      - name: Check blocklist
        run: |
          while IFS= read -r pkg; do
            if npm ls "$pkg" 2>/dev/null | grep -q "$pkg"; then
              echo "BLOCKED: $pkg found in dependency tree"
              exit 1
            fi
          done < BLOCKLIST.md

      - name: Scan for secrets in build output
        run: |
          npm run build 2>&1 | tee build.log
          grep -iE '(sk-[a-zA-Z0-9]{20,}|AKIA[A-Z0-9]{16}|-----BEGIN (RSA |EC )?PRIVATE KEY-----)' build.log && exit 1 || true

      - name: Flag dependency count change
        run: |
          CURRENT=$(npm ls --all 2>/dev/null | wc -l)
          echo "Resolved dependency count: $CURRENT"
          # Compare with main branch baseline if available

      - name: Run tests
        run: npm run test:all
```

### Deployment gate

In addition to the CI checks, the deployment process must enforce:

1. **No direct deployment from `latest` tag.** Production must deploy from a versioned tag (e.g., `v2.6.3`) or a SHA-pinned image.
2. **Staging deployment before production.** Every production deployment must first succeed in staging with passing health checks.
3. **Rollback capability.** The previous production image must be retained and redeployable within 15 minutes.

---

## Appendix A: References

| Document | Relationship |
|---|---|
| [[Backend Stack Security and Refactor Assessment - 2026-05-24]] | Source audit identifying supply-chain risks and recommending this policy |
| [[Platform Logical Audit - 2026-05-24]] | Cross-cutting audit with security findings |
| [[Security Architecture]] | Authentication, authorization, transport security, and secrets management |
| [[Backend Architecture]] | Backend module structure, middleware chain, and dependency map |
| [[Frontend Architecture]] | Frontend module structure, state management, and build process |
| [[Infrastructure]] | Docker configuration, Watchtower, and deployment topology |
| [[Environment Variables]] | Complete catalog of all environment variables including secrets |

## Appendix B: External references

| Advisory | URL |
|---|---|
| Express security release (2026-02-27) | https://expressjs.com/2026/02/27/security-releases.html |
| Node.js March 2026 security releases | https://nodejs.org/en/blog/vulnerability/march-2026-security-releases |
| Microsoft: Axios npm supply-chain compromise | https://www.microsoft.com/en-us/security/blog/2026/04/01/mitigating-the-axios-npm-supply-chain-compromise/ |
| TanStack npm supply-chain compromise postmortem | https://tanstack.com/blog/npm-supply-chain-compromise-postmortem |

## Appendix C: Policy revision history

| Date | Version | Author | Description |
|---|---|---|---|
| 2026-05-24 | 1.0 | Security assessment | Initial policy created from supply-chain risk assessment |
