Tutorial: Securing an Express API
End-to-end walkthrough — initialize a project, configure SCAL-P policy, run guarded installs, catch violations in CI, and handle a fork PR.
This tutorial walks through a realistic scenario: you're maintaining a public Express API and want to protect it against supply chain attacks with SCAL-P.
You'll initialize the project, configure policy, catch a malicious dependency, set up CI enforcement, and handle a fork PR — all with concrete commands and outputs.
Prerequisites
- Node.js 18+ and npm installed
- SCAL-P installed (
scalp versionworks) - A GitHub repository to push to (for the CI section)
1. Initialize the project
mkdir express-api && cd express-api
npm init -y
npm install expressNow bootstrap SCAL-P's policy and directory structure:
scalp initThis creates .scalp/policy.json with safe defaults — audit-only mode, no blocking.
2. Run your first scalp ci
scalp ciThis creates .scalp/ with policy.json, policy.schema.json, lockfile.json, audit.log, and cache/.
Inspect what was recorded:
scalp auditOutput:
audit ok3. Add a suspicious dependency
A contributor opens a PR adding colors (or any package on the denylist) to the project:
npm install colorsRun scalp ci again with enforcement:
scalp ciThis time the evaluation finds violations and blocks the install:
ci failed: [{"PackageID":"colors@1.4.2","Reason":"denylist","Rule":"denylist"}]The install step never ran — no tarballs were downloaded.
Without SCAL-P, npm install colors would silently succeed. The malicious node_modules/colors
would be deployed to production. SCAL-P caught it before it reached disk.
4. Create a real policy
Now let's create a proper policy for the Express API. If you want a head start, use scalp init --strict — it writes a production-hardened policy with denylist, trust scoring, and block enforcement. Then customize from there.
To manually replace .scalp/policy.json with a custom policy:
{
"$schema": ".scalp/policy.schema.json",
"trust": {
"mode": "audit-only",
"min_score": 50,
"require_hash": true
},
"packages": {
"allow": [
{ "name": "express", "versions": "^4.18.0" },
{ "name": "body-parser", "versions": "^1.20.0" }
],
"deny": ["colors", "faker", "node-ipc"]
},
"transitive": {
"max_depth": 3,
"strategy": "warn"
},
"ci": {
"pr-context": "fork",
"sarif": true,
"sarif-file": ".scalp/sarif-report.sarif"
}
}Commit everything:
git add .scalp/policy.json .scalp/policy.schema.json .scalp/lockfile.json
git commit -m "chore: add SCAL-P policy and initial lockfile"Committing .scalp/lockfile.json gives you a versioned integrity baseline. See Security Best
Practices for the tradeoffs.
5. Review the audit log
SCAL-P logs every operation to .scalp/audit.log. Check what happened during the blocked install:
grep '"status":"blocked"' .scalp/audit.log{
"time": "2026-05-22T10:05:00Z",
"action": "policy_evaluate",
"status": "blocked",
"violations": 2,
"pkg": "colors@1.4.2",
"reason": "denylist + no_hash"
}Search for specific packages:
grep '"pkg":"body-parser"' .scalp/audit.log{
"time": "2026-05-22T10:00:00Z",
"action": "hash_sync",
"status": "ok",
"pkg": "body-parser@1.20.3",
"hash_match": true
}Full compliance trail — no external service required.
6. Set up CI with GitHub Actions
Create .github/workflows/scalp-ci.yml:
name: SCAL-P
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
scalp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: scal-p-labs/scalp-action@0b29e6acb5c358a7c6b6d470c6ab8cb6e86fc217
with:
pr-context: ${{ github.event.pull_request.head.repo.fork == 'true' && 'fork' || 'internal' }}
sarif: true
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: .scalp/sarif-report.sarifPush and open a PR. The action will:
- Run
scalp ciwith the appropriate PR context - Annotate the PR with any violations
- Upload SARIF results to GitHub Security tab
- Fail the check if violations are found
7. Simulate a fork PR attack
Fork PRs are the most common supply chain entry point. An attacker submits a PR from a fork with a modified .scalp/lockfile.json pointing to a tampered package.
Without SCAL-P, the modified lockfile could slip through review. With SCAL-P in fork context:
pr-context: forkThe action:
- Ignores the incoming lockfile — re-computes hashes from scratch
- Blocks install scripts — no
postinstallmalware can execute - Verifies every package — any hash mismatch produces a violation
The PR check fails with annotations pointing to the tampered package — before any code reaches main.
8. Verify the lockfile baseline
After merging a clean PR, run scalp audit to compare the current lockfile against the committed baseline:
scalp audit --ciThis checks every hash in .scalp/lockfile.json against node_modules. If all packages match:
audit okIf any package was tampered with (malicious commit, compromised CI runner, manual edit), the audit returns violations:
Recap
| Step | What you did | SCAL-P command |
|---|---|---|
| 1 | Initialize project + install deps | npm install, scalp init |
| 2 | First guarded run | scalp ci |
| 3 | Catch malicious dependency | scalp ci (blocked) |
| 4 | Configure policy | Edit .scalp/policy.json |
| 5 | Review audit log | grep '"status":"blocked"' .scalp/audit.log |
| 6 | Set up CI | .github/workflows/scalp-ci.yml |
| 7 | Fork PR simulation | pr-context: fork |
| 8 | Verify integrity baseline | scalp audit --ci |