feat(security): #497 — host allowlist for perry.nativeLibrary + perry.compilePackages#951
Merged
Merged
Conversation
This was referenced May 17, 2026
…rry.compilePackages`
The two attack surfaces Perry itself introduced over Node — linking
arbitrary native code (`perry.nativeLibrary`) and compiling untrusted
TS source into the binary (`perry.compilePackages`) — were both
silently honored from any package in the dep graph. This PR makes
both privileged operations require explicit host-app opt-in.
Two new `perry.allow.*` arrays in the host `package.json` (read only
from the host, never from deps):
{
"perry": {
"compilePackages": ["hono"],
"allow": {
"compilePackages": ["hono"],
"nativeLibrary": ["@bloomengine/engine"]
}
}
}
- **`perry.nativeLibrary`** (transitive dep declares it):
`collect_modules.rs` gates the manifest push on
`allow.nativeLibrary` containing the package name. Failure bails
with a diagnostic naming the package and the exact JSON to add.
- **`perry.compilePackages`** (host or workspace declares the list):
every entry must also be in `allow.compilePackages` — a two-key
opt-in. Validation deferred until after env-var overrides apply.
Patterns: exact name, scope wildcard `"@scope/*"`, or universal `"*"`
escape hatch. Default-empty = nothing allowed (matches the
"greenfield projects: nothing allowed" acceptance bullet).
`PERRY_ALLOW_PERRY_FEATURES=1` opts every name into both allowlists
for the current build (emergency one-off knob); `=0` enforces refusal
even when `package.json` opted in (fail-closed CI gate).
Both gates run in the platform-agnostic `compile_command` driver and
`collect_modules.rs` — before any backend (LLVM / WASM / ArkTS /
HarmonyOS / Glance / SwiftUI / JS) is invoked. Every target inherits
the protection from one choke point.
7 unit tests for `allowlist_matches`:
- `empty_allowlist_blocks_everything` — the default-deny invariant.
- `exact_match`, `universal_wildcard`, `scope_wildcard` — primary patterns.
- `non_scoped_wildcards_dont_match` — documents the matcher's
intentionally narrow shape (no freeform `prefix-*`).
- `multiple_patterns_or_together` — OR semantics across the list.
End-to-end smoke against five shapes:
- No `compilePackages` → clean.
- `compilePackages` without `allow` → fails with full diagnostic.
- `compilePackages` + matching `allow` → compiles.
- `compilePackages: ["@foo/bar","@foo/baz"]` + `allow: ["@foo/*"]` → compiles.
- `PERRY_ALLOW_PERRY_FEATURES=1` → compiles regardless of package.json.
Updated 7 release-test fixtures to add `perry.allow.compilePackages`
alongside their existing `compilePackages`:
- hono-basic, ink-link-smoke, nestjs-hello, redis-pubsub, axios-get,
drizzle-sqlite, test-files/issue_652.
NestJS fixture uses the scope-wildcard form (`"@nestjs/*"`) to
demonstrate that mechanism in a real fixture.
- [x] Host `package.json` keys: `perry.allow.nativeLibrary`,
`perry.allow.compilePackages`.
- [x] Transitive dep introducing either fails build with clear
message naming the package + exact JSON to add.
- [x] Wildcards supported (`@scope/*` + universal `*`).
- [x] Default behavior on greenfield projects: nothing allowed.
- [x] Documented (`docs/src/cli/allow-perry-features.md`).
`perry audit` integration (also in the acceptance list) deferred —
the `perry audit` subcommand itself is #495, not yet implemented.
c3d9b07 to
2604f33
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #497.
Summary
The two attack surfaces Perry itself introduced over Node — linking arbitrary native code (
perry.nativeLibrary) and compiling untrusted TS source into the binary (perry.compilePackages) — were silently honored from any package in the dep graph. This PR makes both privileged operations require explicit host-app opt-in via two newperry.allow.*arrays in the hostpackage.json.Zero runtime cost — purely compile-time refusal.
Cross-platform: both gates run in the platform-agnostic
compile_commanddriver andcollect_modules.rsbefore any backend (LLVM / WASM / ArkTS / HarmonyOS / Glance / SwiftUI / JS) is invoked.Refusal diagnostic example
Pattern syntax
"lodash"."@scope/*"."*".Default-empty allowlist = nothing allowed (per the issue's "greenfield projects: nothing allowed" acceptance bullet).
Env-var override
PERRY_ALLOW_PERRY_FEATURES=1opts every name into both allowlists for one build.=0enforces refusal even whenpackage.jsonopted in (fail-closed CI gate).Test coverage
7 unit tests for
allowlist_matches:empty_allowlist_blocks_everything— default-deny invariant.exact_match,universal_wildcard,scope_wildcard— primary patterns.non_scoped_wildcards_dont_match— documents the matcher's intentionally narrow shape (no freeformprefix-*).multiple_patterns_or_together— OR semantics across the list.End-to-end smoke against five shapes (all pass against the release binary):
compilePackages→ clean.compilePackageswithoutallow→ fails with full diagnostic.compilePackages+ matchingallow→ compiles."@foo/*"matches@foo/bar,@foo/baz) → compiles.PERRY_ALLOW_PERRY_FEATURES=1→ compiles regardless ofpackage.json.Test-fixture migration
7 existing release-test fixtures updated to add
perry.allow.compilePackagesalongside their existingcompilePackages:hono-basic,ink-link-smoke,nestjs-hello,redis-pubsub,axios-get,drizzle-sqlite,test-files/issue_652.NestJS fixture uses the scope-wildcard form (
"@nestjs/*") to demonstrate that mechanism in a real fixture and keep the allow-list short.Acceptance
package.jsonkeys:perry.allow.nativeLibrary,perry.allow.compilePackages.@scope/*+ universal*).docs/src/cli/allow-perry-features.md).perry auditintegration (also in the acceptance list) deferred — theperry auditsubcommand itself is #495, not yet implemented.Notes
No
Cargo.tomlversion bump, noCLAUDE.mdversion line touch, noCHANGELOG.mdentry — maintainer folds those in at merge time to avoid patch-version collisions.