Security Best Practices
Recommended practices for policy management, version control, CI enforcement, and supply chain security with SCAL-P.
Version control: what to commit
Not everything in .scalp/ belongs in git. Here's the breakdown:
| File | Commit? | Reason |
|---|---|---|
.scalp/policy.json | ✅ Yes | Your security policy — belongs in version control |
.scalp/policy.schema.json | ✅ Yes | JSON Schema for editor autocomplete — commit once |
.scalp/lockfile.json | ✅ Recommended | Integrity snapshot — baseline for cross-session tampering detection |
.scalp/cache/ | ❌ No | Trust score cache (download counts, CVEs) — ephemeral |
.scalp/audit.log | ❌ No | Append-only event log — machine-specific |
.scalp/ci-report.json | ❌ No | CI report output — generated per run |
.scalp/*.sarif | ❌ No | SARIF reports — generated per run |
.scalp/*.key | ❌ No | Signing key — sensitive secrets generated in audit |
Should you commit .scalp/lockfile.json? It depends on your threat model.
Committing it gives you a versioned integrity baseline: if a malicious dependency is introduced in a PR, scalp audit will detect the hash mismatch when the branch is compared against main. This is the strongest tamper-detection SCAL-P offers.
The tradeoff: packages with platform-specific binaries may produce different hashes across OSs (e.g., macOS CI vs Linux CI). In practice, pick one platform as canonical (usually your Linux CI runner) and commit its lockfile. Hash mismatches on other platforms are benign — they don't indicate an attack.
Committing lockfile (maximum security) — recommended
# .gitignore
.scalp/cache/
.scalp/audit.log
.scalp/ci-report.json
.scalp/*.sarif
.scalp/*.keyThe lockfile stays in git. Every CI run (scalp ci) updates it, and every PR audit compares against the committed baseline.
Ignoring lockfile (simplicity)
# .gitignore
.scalp/lockfile.json
.scalp/cache/
.scalp/audit.log
.scalp/ci-report.json
.scalp/*.sarif
.scalp/*.keyTamper detection is limited to what changed since the last scalp install on the same machine. No cross-session or cross-PR baseline.
Policy management
Start permissive, tighten gradually
A policy that's too strict from day one will frustrate your team and get disabled. Follow the migration path:
audit-only+warn— observemin_score+warn— measuredenylist+warn— controlblock— enforce
Review violations regularly
Trust scores change over time — packages lose popularity, new CVEs are discovered, new versions are published. Review .scalp/ci-report.json periodically.
Use allowlist for critical projects
For production services or libraries, use allowlist mode. Only explicitly approved packages can be installed:
{
"trust": { "mode": "allowlist" },
"packages": {
"allow": [
{ "name": "lodash", "versions": "^4.0.0" },
{ "name": "express", "versions": "^4.18.0" }
]
}
}CI/CD security
Pin the action SHA
# Good — immutable
uses: scal-p-labs/scalp-action@0b29e6acb5c358a7c6b6d470c6ab8cb6e86fc217
# Risky — tag could be moved
uses: scal-p-labs/scalp-action@v0.3.0Use fork context for external PRs
- name: SCAL-P (fork)
if: github.event.pull_request.head.repo.fork == true
uses: scal-p-labs/scalp-action@0b29e6acb5c358a7c6b6d470c6ab8cb6e86fc217
with:
pr-context: forkBlock install scripts in CI
# Default — safe for all contexts
pr-context: forkInstall scripts are blocked automatically. Only allow scripts in internal PRs when you know exactly which packages need build steps.
Supply chain hardening
Verify the SCAL-P binary itself
SCAL-P releases are checksummed using SCAL-P itself (dogfooding). Always verify the binary you download:
scalp verify \
--artifact scalp_linux_amd64.tar.gz \
--checksum checksums.txt \
--ciRequire hashes for all packages
Enable require_hash in your policy to ensure every package has a lockfile integrity entry:
{
"trust": {
"require_hash": true,
"min_score": 50
}
}This means any package installed outside the guarded flow (or tampered with after) is an automatic violation.
Use trusted CI for builds
SCAL-P's CI mode (--pr-context fork) blocks install scripts and forces hash verification. This protects against:
- Malicious postinstall scripts in dependencies
- Lockfile tampering in fork PRs
- Dependency confusion attacks
Audit trail
Keep .scalp/audit.log for compliance and forensic analysis:
# Search for all blocking events
grep '"status":"blocked"' .scalp/audit.log
# Search for all hash mismatches
grep '"hash_match":false' .scalp/audit.log
# Export audit events for a specific package
grep '"pkg":"lodash"' .scalp/audit.log