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
11 changes: 10 additions & 1 deletion .github/workflows/security-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
Expand Down Expand Up @@ -128,10 +130,15 @@ jobs:
- name: Scan each skill
run: |
set -euo pipefail
skillspector_ref="$(jq -er '.tools.skillspector.version' external-tools.json)"
soak_days="$(sed -nE 's/^export const SOAK_DAYS = ([0-9]+)$/\1/p' scripts/soak/constants.mts)"
test -n "$soak_days"
exclude_newer="$(date -u -d "${soak_days} days ago" '+%Y-%m-%dT%H:%M:%SZ')"
for skill in .claude/skills/*/; do
echo "::group::skillspector ${skill}"
uv tool run --python 3.12 \
--from git+https://github.com/NVIDIA/skillspector@2eb84478 \
--exclude-newer "$exclude_newer" \
--from "git+https://github.com/NVIDIA/skillspector@${skillspector_ref}" \
skillspector scan "${skill}" --no-llm
echo "::endgroup::"
done
Expand All @@ -145,6 +152,8 @@ jobs:
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/soak-autofix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
BRANCH="bot/soak-autofix"
expected_oid=""
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
git fetch origin "refs/heads/$BRANCH:refs/remotes/origin/$BRANCH"
expected_oid="$(git rev-parse "refs/remotes/origin/$BRANCH")"
fi
git checkout -B "$BRANCH"
git add -A
git commit -m "chore(soak): prune expired soak annotations (automated)
Expand All @@ -78,7 +83,12 @@ jobs:
external-tools.mts --fix. Windows that cleared have soaked;
their bypass annotations are dead weight the gates would
otherwise fail on."
git push -f origin "$BRANCH"
if [ -n "$expected_oid" ]; then
git push --force-with-lease="refs/heads/$BRANCH:$expected_oid" \
origin "HEAD:refs/heads/$BRANCH"
else
git push origin "HEAD:refs/heads/$BRANCH"
fi
if [ -z "$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number')" ]; then
gh pr create --head "$BRANCH" \
--title "chore(soak): prune expired soak annotations (automated)" \
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ jobs:
- name: File size limit
run: ./scripts/check_file_size.sh

# Well-known binding provenance pins: every third-party binding in
# well_known_bindings.toml must carry an [bindings.<name>.upstream]
# pin, and the lock-step rule (ported-at == version) must hold, so a
# pin bump can't outrun the wrapper review it demands. Offline/CI-safe
# (no network); the weekly update runs `--check --refresh` to surface
# newly-soaked upstream releases as advisories.
- name: Binding upstream pins (lock-step)
run: node scripts/binding_pins.mjs --check

# GC write-barrier store-site inventory: every raw heap-slot store in
# perry-codegen / perry-runtime / perry-stdlib must be barriered or
# carry a justified GC_STORE_AUDIT(...) marker (or a justified entry
Expand Down
2 changes: 1 addition & 1 deletion changelog.d/6912-security-tooling-soak.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
**Supply-chain soak + pinned security tooling:** port the wheelhouse/nub security stack (nubjs/nub#442). A 7-day release-age soak (`SOAK_DAYS` in `scripts/soak/constants.mts`, `npm run soak` parity gate + `soak:fix` fixer) now governs every dependency surface — root `.npmrc` `min-release-age`, `tools/pnpm-workspace.yaml` `minimumReleaseAge` with dated auto-expiring exclusions, `tools/taze.config.mts` `maturityPeriod` (soaked `npm run deps:update` updater), `.cargo/config.toml` `min-publish-age` (nightly-only; inert on stable), and per-block dependabot `cooldown`. `external-tools.json` pins pnpm 11.15.1 / npm 12.0.1 / sfw 1.14.0 (dated soakBypass) / zizmor 1.28.0 / agentshield 1.4.0 / skillspector `@2eb84478` exact with sha512 SRI; `npm run tools:install` installs them into a local rack and writes Socket Firewall shims for npm/yarn/pnpm/pip/uv/cargo (recursion-sentinel, fail-open, clobber-guarded; the pnpm/npm shims wrap the rack-pinned binaries). CI grows a zizmor workflow running the rack-pinned binary (gate at high with a documented ratchet config in `.github/zizmor.yml`) plus always-run `soak-gate`, `agent-scan` (AgentShield over `.claude/`), and `skills-scan` (NVIDIA SkillSpector over `.claude/skills/`) jobs in security-audit.yml. New `soak` skill documents the workflow.
**Supply-chain soak + pinned security tooling:** port the wheelhouse/nub security stack (nubjs/nub#442). A 7-day release-age soak (`SOAK_DAYS` in `scripts/soak/constants.mts`, `npm run soak` parity gate + `soak:fix` fixer) now governs every dependency surface — root `.npmrc` `min-release-age`, `tools/pnpm-workspace.yaml` `minimumReleaseAge` with dated auto-expiring exclusions, `tools/taze.config.mts` `maturityPeriod` (soaked `npm run deps:update` updater), `.cargo/config.toml` `min-publish-age` (nightly-only; inert on stable), and per-block dependabot `cooldown`. `external-tools.json` pins pnpm 11.15.1 / npm 12.0.1 / sfw 1.14.0 (dated soakBypass) / zizmor 1.28.0 / agentshield 1.4.0 with SHA-512 SRI, and SkillSpector to a full Git SHA with a seven-day uv dependency cutoff; `npm run tools:install` installs them into a local rack and writes Socket Firewall shims for npm/yarn/pnpm/pip/uv/cargo (recursion-sentinel, fail-open, clobber-guarded; the pnpm/npm shims wrap the rack-pinned binaries). CI grows a zizmor workflow running the rack-pinned binary (gate at high with a documented ratchet config in `.github/zizmor.yml`) plus always-run `soak-gate`, `agent-scan` (AgentShield over `.claude/`), and `skills-scan` (NVIDIA SkillSpector over `.claude/skills/`) jobs in security-audit.yml. New `soak` skill documents the workflow.
1 change: 1 addition & 0 deletions changelog.d/7031-binding-upstream-lockstep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**Well-known bindings now carry upstream provenance pins with a lock-step review gate.** Every third-party npm package with a bundled native wrapper in `well_known_bindings.toml` records the exact upstream release it ports (version, tarball sha256, repo, git ref) and the date/release it was last reviewed against. The lock-step rule — `ported-at` must equal `version` — means a pin bump can't outrun the wrapper review it demands; it's enforced by `node scripts/binding_pins.mjs --check` in CI and by the perry binary's own table parser. Node builtins, aliases, and perry-owned packages are exempt. New `scripts/binding_pins.mjs` provisions pins (`--set`/`--backfill`), gates them offline (`--check`), flags newly-soaked upstream releases (`--check --refresh`), and materializes an upstream at its pin for review (`--materialize`). Adapted from the socket-registry fleet's upstream-reference technique. Also adds an `iovalkey` binding (the Valkey fork of ioredis) served by the existing `perry-ext-ioredis` surface.
219 changes: 114 additions & 105 deletions crates/perry-api-manifest/src/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,123 +28,132 @@ use crate::{ApiEntry, ApiKind, ApiSource, ParamSpec, TypeSpec};
/// answer module-resolution questions without depending on
/// `perry-hir`. Order matches the original list to keep diffs minimal.
pub const NATIVE_MODULES: &[&str] = &[
"mysql2",
"mysql2/promise",
"pg",
"uuid",
"bcrypt",
"argon2",
"ioredis",
"axios",
"node-fetch",
"ws",
"zlib",
"crypto",
"dotenv",
"dotenv/config",
"jsonwebtoken",
"nanoid",
"slugify",
"validator",
"ethers",
"mongodb",
"better-sqlite3",
"sqlite",
"tursodb",
"iroh",
// ── Third-party npm packages (native wrappers; see well_known_bindings.toml) ──
"mysql2", // MySQL/MariaDB client
"mysql2/promise", // mysql2's promise-API subpath
"pg", // PostgreSQL client
"uuid", // RFC-4122 UUID generation
"bcrypt", // bcrypt password hashing (replaces the N-API addon)
"argon2", // Argon2 password hashing (replaces the N-API addon)
"ioredis", // Redis/Valkey client
// iovalkey: the Valkey fork of ioredis (valkey-io/iovalkey), served by the
// same perry-ext-ioredis surface — see well_known_bindings.toml.
"iovalkey",
"axios", // HTTP client (routes onto the native fetch/http stack)
"node-fetch", // WHATWG fetch client
"ws", // WebSocket client/server
"zlib", // (Node builtin) gzip/deflate/brotli/zstd compression
"crypto", // (Node builtin) hashing, HMAC, cipher, sign/verify, WebCrypto
"dotenv", // .env file loader
"dotenv/config", // dotenv's auto-load-on-import subpath
"jsonwebtoken", // JWT sign/verify
"nanoid", // compact URL-safe ID generation
"slugify", // string → URL slug
"validator", // string validators/sanitizers
"ethers", // Ethereum library (utils/wallet/ABI)
"mongodb", // MongoDB driver
"better-sqlite3", // synchronous SQLite (replaces the N-API addon)
"sqlite", // node:sqlite builtin surface
"tursodb", // Turso/libSQL client (legacy in-tree; now @perryts/tursodb)
"iroh", // iroh p2p (legacy in-tree; now @perryts/iroh)
// #6562: Bun FFI (C-ABI). The `bun:` prefix is part of the specifier
// (unlike `node:`, which is stripped) — `import { dlopen } from "bun:ffi"`.
"bun:ffi",
"node-cron",
"nodemailer",
"http",
"https",
"http2",
"inspector",
"inspector/promises",
"events",
"domain",
"os",
"buffer",
"assert",
"assert/strict",
"test",
"child_process",
"dns",
"dns/promises",
"dgram",
"net",
"tls",
"stream",
"streams",
"fs",
"module",
"path",
"path/posix",
"path/win32",
"console",
"constants",
"util",
"util/types",
"dns",
"dns/promises",
"url",
"lru-cache",
"commander",
"decimal.js",
"bignumber.js",
"exponential-backoff",
"lodash",
"dayjs",
"date-fns",
"moment",
"sharp",
"cheerio",
"cron",
"fastify",
"async_hooks",
"node-cron", // cron-style scheduler (npm node-cron; aliases `cron`)
"nodemailer", // SMTP email sending
// ── Node.js builtin modules ──
"http", // HTTP client + server
"https", // HTTPS client + server
"http2", // HTTP/2 client + server
"inspector", // V8 inspector protocol
"inspector/promises", // inspector's promise-API subpath
"events", // EventEmitter
"domain", // (legacy) error-domain grouping
"os", // OS info (platform, cpus, hostname, …)
"buffer", // Buffer / Blob
"assert", // assertions
"assert/strict", // assert in strict mode
"test", // node:test runner surface
"child_process", // spawn/exec subprocesses
"dns", // DNS resolution
"dns/promises", // dns promise-API subpath
"dgram", // UDP sockets
"net", // TCP sockets + servers
"tls", // TLS/SSL sockets
"stream", // streams (Readable/Writable/Transform)
"streams", // WHATWG web-streams surface
"fs", // filesystem
"module", // module system introspection (createRequire, …)
"path", // path manipulation (host flavor)
"path/posix", // path, POSIX semantics
"path/win32", // path, Windows semantics
"console", // console.* logging
"constants", // (legacy) OS/fs/crypto constant tables
"util", // promisify, inspect, TextEncoder, …
"util/types", // runtime type predicates (isDate, …)
"dns", // (duplicate of the dns entry above — kept for parity)
"dns/promises", // (duplicate — kept for parity)
"url", // URL / URLSearchParams
// ── More third-party npm packages ──
"lru-cache", // LRU cache
"commander", // CLI argument parser
"decimal.js", // arbitrary-precision decimals
"bignumber.js", // arbitrary-precision big numbers
"exponential-backoff", // retry-with-backoff helper
"lodash", // general utility library
"dayjs", // date/time library
"date-fns", // functional date utilities
"moment", // (legacy) date/time library
"sharp", // image processing (replaces the N-API addon)
"cheerio", // server-side jQuery-style HTML parsing
"cron", // cron scheduler (aliases node-cron)
"fastify", // HTTP server framework
// ── Node.js builtins (cont.) ──
"async_hooks", // async context tracking
// #2875: internal module backing DisposableStack/AsyncDisposableStack
// instance-method dispatch (no JS import surface).
"__disposable__",
"readline",
"repl",
"sea",
"string_decoder",
"querystring",
"cluster",
"tty",
"wasi",
"perf_hooks",
"v8",
"vm",
"process",
"readline", // line-by-line stdin reading
"repl", // REPL surface
"sea", // single-executable-application API
"string_decoder", // incremental byte→string decoding
"querystring", // (legacy) query-string encode/decode
"cluster", // worker-process clustering
"tty", // terminal I/O
"wasi", // WebAssembly System Interface
"perf_hooks", // performance measurement
"v8", // V8-compat introspection surface
"vm", // script compilation/eval sandboxes
"process", // the process object as an importable module
// ── perry-owned builtins (Perry-native; don't resolve under Node/Bun) ──
// Bare `perry` builtin — embedded-asset introspection (#5731):
// `embeddedFiles`, `readEmbedded`, `isStandaloneExecutable`.
"perry",
"perry/tui",
"perry/yoga",
"perry/ui",
"perry/system",
"perry/plugin",
"perry/widget",
"perry/i18n",
"worker_threads",
"perry/thread",
"perry/tui", // terminal-UI framework
"perry/yoga", // Yoga flexbox layout
"perry/ui", // native UI (AppKit/UIKit/Win32/GTK4/…)
"perry/system", // OS integration (keychain, notifications, …)
"perry/plugin", // compile-time plugin surface
"perry/widget", // home-screen widgets (WidgetKit/Glance)
"perry/i18n", // internationalization runtime
"worker_threads", // (Node builtin) OS-thread workers
"perry/thread", // perry-native threading (parallelMap/spawn)
// `perry/gc` — explicit GC control (collect / minor / idleHint).
// Served entirely by perry-runtime; a no-op-style Perry-native
// surface like `perry/thread` (doesn't resolve under Node/Bun).
"perry/gc",
"perry/updater",
"perry/container",
"perry/container-compose",
"perry/compose",
"perry/workloads",
"perry/media",
"perry/audio",
"perry/background",
"redis",
"rate-limiter-flexible",
"fetch",
"perry/updater", // auto-update client (@perry/updater signer side)
"perry/container", // container runtime surface
"perry/container-compose", // docker-compose-style orchestration
"perry/compose", // compose helpers
"perry/workloads", // workload scheduling
"perry/media", // media (video/image) surface
"perry/audio", // audio surface
"perry/background", // background-task surface
// ── More third-party npm packages ──
"redis", // npm `redis` client (aliases ioredis)
"rate-limiter-flexible", // rate limiting
"fetch", // bare-name alias for the node-fetch surface
// `@perryts/pdf` — official PDF creation package (#516).
// Bundled wrapper lives in `crates/perry-ext-pdf`; the producer
// side companion to the existing PdfView widget. d.ts at
Expand All @@ -167,7 +176,7 @@ pub const NATIVE_MODULES: &[&str] = &[
// the API-identical @lydell fork (opencode's static import) resolve to
// the one perry-runtime implementation — no N-API addon involved.
"node-pty",
"@lydell/node-pty",
"@lydell/node-pty", // API-identical node-pty fork (see above)
];

/// Node built-in submodules that Perry routes through the
Expand Down
2 changes: 1 addition & 1 deletion crates/perry-codegen/src/lower_call/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub(super) fn lower_builtin_new(
"Client" | "Pool" => Some(&["pg"]),
"Database" => Some(&["better-sqlite3"]),
"DatabaseSync" | "Session" | "StatementSync" => Some(&["sqlite", "node:sqlite"]),
"Redis" => Some(&["ioredis", "redis"]),
"Redis" => Some(&["ioredis", "redis", "iovalkey"]),
"MongoClient" => Some(&["mongodb"]),
"Decimal" => Some(&["decimal.js"]),
"RateLimiterMemory" => Some(&["rate-limiter-flexible"]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ pub fn native_module_lookup(
// here so call-site lookups find the right runtime fns regardless
// of which alias the user imported from.
let normalized = match module {
"redis" => "ioredis",
// `redis` and `iovalkey` (the Valkey fork of ioredis) share the
// perry-ext-ioredis staticlib and its `js_ioredis_*` dispatch rows;
// normalize both to `ioredis` so call-site lookups resolve.
"redis" | "iovalkey" => "ioredis",
"sys" => "util",
// #6563: @lydell/node-pty is an API-identical fork of node-pty
// (opencode imports the fork, kimi-code the original); both route to
Expand Down
Loading
Loading