Infra: require explicit opt-in for prerelease npm installs#38117
Infra: require explicit opt-in for prerelease npm installs#38117vincentkoc merged 10 commits intomainfrom
Conversation
Greptile SummaryThis PR successfully hardens plugin and hook npm installs by requiring explicit opt-in for prerelease resolutions. The implementation in Key findings:
The core prerelease-blocking logic is sound for the primary use cases (bare spec, Confidence Score: 3/5
Last reviewed commit: e036027 |
| const parsedSpec = parseRegistryNpmSpec(params.spec); | ||
| if (!parsedSpec) { | ||
| return { | ||
| ok: false, | ||
| error: "unsupported npm spec", | ||
| }; | ||
| } |
There was a problem hiding this comment.
When parseRegistryNpmSpec returns null, the error is hardcoded to "unsupported npm spec", which discards the specific validation reason (e.g. "use an exact version or dist-tag (ranges are not allowed)"). Since validateRegistryNpmSpec already provides that detail, callers get a far less actionable message.
| const parsedSpec = parseRegistryNpmSpec(params.spec); | |
| if (!parsedSpec) { | |
| return { | |
| ok: false, | |
| error: "unsupported npm spec", | |
| }; | |
| } | |
| const specValidationError = validateRegistryNpmSpec(params.spec); | |
| if (specValidationError) { | |
| return { | |
| ok: false, | |
| error: specValidationError, | |
| }; | |
| } | |
| const parsedSpec = parseRegistryNpmSpec(params.spec)!; |
(You'll also need to add validateRegistryNpmSpec to the import from ./npm-registry-spec.js.)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/npm-pack-install.ts
Line: 102-108
Comment:
When `parseRegistryNpmSpec` returns `null`, the error is hardcoded to `"unsupported npm spec"`, which discards the specific validation reason (e.g. `"use an exact version or dist-tag (ranges are not allowed)"`). Since `validateRegistryNpmSpec` already provides that detail, callers get a far less actionable message.
```suggestion
const specValidationError = validateRegistryNpmSpec(params.spec);
if (specValidationError) {
return {
ok: false,
error: specValidationError,
};
}
const parsedSpec = parseRegistryNpmSpec(params.spec)!;
```
(You'll also need to add `validateRegistryNpmSpec` to the import from `./npm-registry-spec.js`.)
How can I resolve this? If you propose a fix, please make it concise.| export function isPrereleaseResolutionAllowed(params: { | ||
| spec: ParsedRegistryNpmSpec; | ||
| resolvedVersion?: string; | ||
| }): boolean { | ||
| if (!params.resolvedVersion || !isPrereleaseSemverVersion(params.resolvedVersion)) { | ||
| return true; | ||
| } | ||
| if (params.spec.selectorKind === "none") { | ||
| return false; | ||
| } | ||
| if (params.spec.selectorKind === "exact-version") { | ||
| return params.spec.selectorIsPrerelease; | ||
| } | ||
| return params.spec.selector?.toLowerCase() !== "latest"; | ||
| } |
There was a problem hiding this comment.
The final branch of isPrereleaseResolutionAllowed (line 129) permits prerelease resolutions for any tag whose lowercase form is not "latest". This means a user installing with @stable, @current, or @production — dist-tags that carry an implicit stable-version expectation — will silently receive a prerelease if that tag resolves to one.
The PR description mentions only @beta / @rc as explicit opt-in signals, suggesting only prerelease-named tags should allow prerelease installs. This policy gap is not tested (the test at line 50 covers only @beta) and not documented.
Consider either:
- Adding a known-prerelease-tag allowlist (e.g.
beta,rc,alpha,canary,next,dev,experimental) and only those tags permit prerelease - Checking whether a tag name contains a recognisable prerelease identifier
- At minimum, documenting this behavior in a code comment so maintainers understand the policy and don't accidentally widen it further
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/npm-registry-spec.ts
Line: 116-130
Comment:
The final branch of `isPrereleaseResolutionAllowed` (line 129) permits prerelease resolutions for any tag whose lowercase form is not `"latest"`. This means a user installing with `@stable`, `@current`, or `@production` — dist-tags that carry an implicit stable-version expectation — will silently receive a prerelease if that tag resolves to one.
The PR description mentions only `@beta` / `@rc` as explicit opt-in signals, suggesting only prerelease-named tags should allow prerelease installs. This policy gap is not tested (the test at line 50 covers only `@beta`) and not documented.
Consider either:
1. Adding a known-prerelease-tag allowlist (e.g. `beta`, `rc`, `alpha`, `canary`, `next`, `dev`, `experimental`) and only those tags permit prerelease
2. Checking whether a tag name contains a recognisable prerelease identifier
3. At minimum, documenting this behavior in a code comment so maintainers understand the policy and don't accidentally widen it further
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e036027a09
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!DIST_TAG_RE.test(selector)) { | ||
| return { | ||
| ok: false, | ||
| error: "unsupported npm spec: use an exact version or dist-tag (ranges are not allowed)", |
There was a problem hiding this comment.
Reject x/major semver ranges before treating selector as tag
The new validator only rejects range selectors that fail DIST_TAG_RE, so selectors like 1.x, 1.2, and 1 are still accepted as selectorKind: "tag" even though npm treats <name>@<version range> as a range form (see npm's package-spec docs). This means the change does not actually enforce the documented “ranges are not allowed” contract for common range syntaxes, and users can still install via ranges despite the new guardrail.
Useful? React with 👍 / 👎.
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy (cherry picked from commit f392b81)
…38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy (cherry picked from commit f392b81)
* fix(subagents): recover announce cleanup after kill/complete race (cherry picked from commit 2f86ae7) * feat(hooks): emit compaction lifecycle hooks (openclaw#16788) (cherry picked from commit 71ec421) * fix(agent): harden undici stream timeouts for long openai-completions runs (cherry picked from commit 05fb16d) * fix(agents): allow configured ollama endpoints without dummy api keys (cherry picked from commit 36afd1b) * feat(openai): add gpt-5.4 support for API and Codex OAuth (openclaw#36590) Co-authored-by: Tyler Yust <[email protected]> (cherry picked from commit 5d4b040) * Fix failover for zhipuai 1310 Weekly/Monthly Limit Exhausted (openclaw#33813) Co-authored-by: zhouhe-xydt <[email protected]> Co-authored-by: altaywtf <[email protected]> (cherry picked from commit a65d70f) * fix(failover): classify HTTP 402 as rate_limit when payload indicates usage limit (openclaw#30484) (openclaw#36802) Co-authored-by: Val Alexander <[email protected]> (cherry picked from commit 01b2017) * feat(onboarding): add web search to onboarding flow (openclaw#34009) * add web search to onboarding flow * remove post onboarding step (now redundant) * post-onboarding nudge if no web search set up * address comments * fix test mocking * add enabled: false assertion to the no-key test * --skip-search cli flag * use provider that a user has a key for * add assertions, replace the duplicated switch blocks * test for quickstart fast-path with existing config key * address comments * cover quickstart falls through to key test * bring back key source * normalize secret inputs instead of direct string trimming * preserve enabled: false if it's already set * handle missing API keys in flow * doc updates * hasExistingKey to detect both plaintext strings and SecretRef objects * preserve enabled state only on the "keep current" paths * add test for preserving * better gate flows * guard against invalid provider values in config * Update src/commands/configure.wizard.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * format fix * only mentions env var when it's actually available * search apiKey fields now typed as SecretInput * if no provider check if any search provider key is detectable * handle both kimi keys * remove .filter(Boolean) * do not disable web_search after user enables it * update resolveSearchProvider * fix(onboarding): skip search key prompt in ref mode * fix: add onboarding web search step (openclaw#34009) (thanks @kesku) --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Shadow <[email protected]> (cherry picked from commit 3d7bc59) * fix(ci): restore protocol and schema checks (openclaw#37470) (cherry picked from commit ee6f7b1) * Infra: require explicit opt-in for prerelease npm installs (openclaw#38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy (cherry picked from commit f392b81) * ci: trigger workflow * fix: resolve cherry-pick adaptation failures from upstream web search commit - Add missing hasConfiguredSecretInput() to types.secrets.ts - Add SecretInput import to types.tools.ts for apiKey fields - Replace OpenClawConfig with RemoteClawConfig in onboard-search - Remove non-existent DEFAULT_SECRET_PROVIDER_ALIAS import - Remove invalid 'provider' field from SecretRef literal - Stub resolveSyntheticLocalProviderAuth (models.providers was gutted) - Replace undefined registerRun/emitLifecycleEnd test helpers with existing mod.registerSubagentRun/lifecycleHandler patterns Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * Plugins: clarify registerHttpHandler migration errors (openclaw#36794) * Changelog: note plugin HTTP route migration diagnostics * Tests: cover registerHttpHandler migration diagnostics * Plugins: clarify registerHttpHandler migration errors * Tests: cover registerHttpHandler diagnostic edge cases * Plugins: tighten registerHttpHandler migration hint (cherry picked from commit d4021f4) * Plugins: avoid false integrity drift prompts on unpinned updates (openclaw#37179) * Plugins: skip drift prompts for unpinned updates * Plugins: cover unpinned integrity update behavior (cherry picked from commit 428d176) * docs(protocol): document slash-delimited schema lookup plugin ids (cherry picked from commit f788ba1) * docs(plugins): document context engine slots and registration (cherry picked from commit eb2eeba) * docs(plugins): add context-engine manifest kind example (cherry picked from commit 7cc3376) * docs(config): list the context engine plugin slot (cherry picked from commit 5470337) * fix: Windows: openclaw plugins install fails with spawn EINVAL Fixes openclaw#7631 (cherry picked from commit d000316) * fix(whatsapp): remove implicit [openclaw] self-chat prefix (cherry picked from commit 4d9134f) * WhatsApp: honor outbound mediaMaxMb (openclaw#38097) * WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix (cherry picked from commit 222d635) * fix(mattermost): allow reachable interaction callback URLs (openclaw#37543) Merged via squash. Prepared head SHA: 4d59373 Co-authored-by: mukhtharcm <[email protected]> Co-authored-by: mukhtharcm <[email protected]> Reviewed-by: @mukhtharcm (cherry picked from commit 4a80d48) * Mattermost: harden interaction callback binding (openclaw#38057) (cherry picked from commit a274ef9) * chore: code/dead tests cleanup (openclaw#38286) * Discord: assert bot-self filter queue guard * Tests: remove dead gateway SIGTERM placeholder (cherry picked from commit 38f46e8) * Delete changelog/fragments directory (cherry picked from commit 8f69e07) * fix(feishu): restore explicit media request timeouts (cherry picked from commit b127333) * fix: adapt upstream naming to fork conventions Replace OpenClawConfig with RemoteClawConfig, openclaw/plugin-sdk with remoteclaw/plugin-sdk, and fix import paths for fork type exports. * fix: resolve type errors from upstream cherry-picks - Add missing RemoteClawConfig import in discord test - Use spread with as-any for feishu SDK timeout property - Remove allowUnresolvedSecretRef from mattermost test * ci: re-trigger CI after force push * fix: revert unintended onboarding changes from linter bleed * ci: sync PR head with branch --------- Co-authored-by: Vignesh Natarajan <[email protected]> Co-authored-by: Vincent Koc <[email protected]> Co-authored-by: dorukardahan <[email protected]> Co-authored-by: Tyler Yust <[email protected]> Co-authored-by: zhouhe-xydt <[email protected]> Co-authored-by: zhouhe-xydt <[email protected]> Co-authored-by: altaywtf <[email protected]> Co-authored-by: Xinhua Gu <[email protected]> Co-authored-by: Val Alexander <[email protected]> Co-authored-by: Kesku <[email protected]> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Shadow <[email protected]> Co-authored-by: Altay <[email protected]> Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> Co-authored-by: 0xlin2023 <[email protected]> Co-authored-by: Muhammed Mukhthar CM <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
Summary
openclaw plugins install <pkg>andopenclaw hooks install <pkg>could accept prerelease resolutions when npm returned a beta/rc for a bare spec or@latest.@latestreject prerelease resolutions unless the user explicitly opts in with a prerelease tag or exact prerelease version; tests and docs were updated for plugins and hooks.@beta,@rc, or@1.2.3-beta.4still work, and this does not change global OpenClaw update-channel behavior.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
@latestnow stay stable-only and fail if npm resolves them to a prerelease.^1.2.3and~1.2.3are now rejected for plugin/hook npm installs.Security Impact (required)
No)No)No)No)No)Yes, explain risk + mitigation:Repro + Verification
Environment
Steps
npm packto resolve a bare plugin or hook spec to a prerelease version.@latestand for an explicit prerelease spec.@latestfails with an opt-in message, while explicit prerelease selectors still install.Expected
Actual
Evidence
Human Verification (required)
What you personally verified (not just CI), and how:
@latest, explicit prerelease tag, exact prerelease version parsing, and semver range rejection.Compatibility / Migration
Mostly)No)No)Failure Recovery (if this breaks)
src/infra/npm-registry-spec.tsandsrc/infra/npm-pack-install.ts.Risks and Mitigations
@latest, and explicit prerelease selectors for both plugins and hooks.AI-assisted: yes. Focused local verification only.