From aa3995ae0529fc5f18b254b92249dc21ce6f38e0 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Wed, 24 Jun 2026 19:06:29 +0200 Subject: [PATCH] fix(apply): reject reserved module names in validate_bundle (parity) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interactive create path rejects a module whose name shadows a built-in SDK namespace (`kv`, `secrets`, …) so `import "kv"` can't resolve to a user module, but `pic apply`'s `validate_bundle` skipped the check — a parity gap that let a manifest create what the dashboard forbids. Gate it on `ScriptKind::Module` and share the same `RESERVED_MODULE_NAMES` const (now `pub(crate)`); endpoints remain unrestricted. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/manager-core/src/api.rs | 2 +- crates/manager-core/src/apply_service.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/manager-core/src/api.rs b/crates/manager-core/src/api.rs index fd38f23..275e63a 100644 --- a/crates/manager-core/src/api.rs +++ b/crates/manager-core/src/api.rs @@ -260,7 +260,7 @@ async fn create_script( /// real KV bridge — defense against author confusion, not a security /// boundary (stdlib namespaces and module imports already live in /// disjoint Rhai scopes). -const RESERVED_MODULE_NAMES: &[&str] = &[ +pub(crate) const RESERVED_MODULE_NAMES: &[&str] = &[ "log", "regex", "random", diff --git a/crates/manager-core/src/apply_service.rs b/crates/manager-core/src/apply_service.rs index dc8bdae..38adcf0 100644 --- a/crates/manager-core/src/apply_service.rs +++ b/crates/manager-core/src/apply_service.rs @@ -886,6 +886,15 @@ impl ApplyService { ))); } let res = if s.kind == ScriptKind::Module { + // Parity with the interactive create path (`api.rs`): a module + // may not shadow a built-in SDK namespace, or `import "kv"` + // could resolve to a user module instead of the real bridge. + if crate::api::RESERVED_MODULE_NAMES.contains(&s.name.as_str()) { + return Err(ApplyError::Invalid(format!( + "script `{}`: reserved module name (shadows a built-in SDK namespace)", + s.name + ))); + } self.validator.validate_module(&s.source) } else { self.validator.validate(&s.source)