Skip to content

Prove the engine blocks what the platform generated #506

Prove the engine blocks what the platform generated

Prove the engine blocks what the platform generated #506

Workflow file for this run

name: CI
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
capability-contract:
name: Capability contract
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# The version check compares the manifest against the base branch, so it needs history.
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Manifest is up to date with its source
run: node scripts/emit-capabilities.mjs --check
- name: Vocabulary changes carry a version bump
# A member added or removed without moving CAPABILITY_VERSION leaves every vendoring consumer
# unable to tell it is behind — the same silent drift the manifest exists to remove.
run: node scripts/check-capability-version.mjs --base "origin/${{ github.base_ref || github.event.repository.default_branch }}"
validate:
name: Validate on Node ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version:
- 18.x
- 20.x
- 22.x
- 24.x
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Typecheck
run: npm run typecheck
- name: Test
run: npm test
- name: Build
run: npm run build
# After the build, because it drives the built bin. npm installs a bin as a SYMLINK, so this is the
# only invocation shape that catches an entry point which runs when called directly and does nothing
# when installed — silently, with exit 0.
- name: Packaged bin runs when invoked through a symlink
run: npx vitest run tests/bin-invocation.test.ts
env:
PS_REQUIRE_BIN_CHECK: '1'
# After the build, because it loads `dist/protect.js` — what an application actually loads. Testing
# the source would prove the source blocks the exploit and say nothing about the bundle, and the
# bundler is a real failure surface: an export dropped, a branch shaken out, an edge build diverging.
#
# `PS_REQUIRE_CANARY` turns a skipped run into a failure. A canary that skips reads exactly like one
# that passed, and this is the check that proves a generated rule blocks a real exploit.
- name: Canary — the built engine blocks the exploit and allows the control
run: npx vitest run tests/protect/canary-engine-proof.test.ts
env:
PS_REQUIRE_CANARY: '1'
- name: Verify package contents
run: npm pack --dry-run
production-audit:
name: Production dependency audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Audit published dependency tree
run: npm audit --omit=dev --audit-level=moderate