fix(modules): validate group module create per kind (Phase 4b review)

Adversarial-review finding: `create_group_script` validated every source
with `validate()` regardless of kind, so an imperatively-created group
module (`pic scripts deploy --group g --name kv --kind module`) skipped the
two gates every other module-write path enforces (app create/update in
api.rs, declarative apply in apply_service): the stricter `validate_module`
shape check and the `RESERVED_MODULE_NAMES` guard. A malformed-shape group
module was accepted at create (only failing later at import-resolve time)
and a reserved name (`kv`, `log`, …) slipped through.

Branch on kind to mirror the app path. Adds a journey asserting a reserved
group-module name is rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-29 19:32:34 +02:00
parent 52da8a8704
commit 4e25a142bd
2 changed files with 50 additions and 4 deletions

View File

@@ -97,10 +97,25 @@ async fn create_group_script(
)
.await?;
// Phase 4b: group modules + imports are allowed. Imports resolve
// lexically from this group's chain at runtime (§5.5); the recorded
// edges feed the declarative dangling-import plan check.
let validated = s.validator.validate(&input.source)?;
// Phase 4b: group modules + imports are allowed. Validate per kind,
// mirroring the app-script create path (`api.rs`): a module gets the
// stricter shape check + the reserved-name guard; an endpoint the
// parse-only path. Without the kind branch a malformed-shape group
// module would be accepted here and only fail later at import time,
// and a reserved name (`kv`, `log`, …) would slip through. Imports
// resolve lexically from this group's chain at runtime (§5.5); the
// recorded edges feed the declarative dangling-import plan check.
let validated = if input.kind == ScriptKind::Module {
if crate::api::RESERVED_MODULE_NAMES.contains(&input.name.as_str()) {
return Err(GroupScriptsApiError::Invalid(format!(
"{:?} is a reserved module name (shadows a built-in SDK namespace)",
input.name
)));
}
s.validator.validate_module(&input.source)?
} else {
s.validator.validate(&input.source)?
};
s.sandbox_ceiling
.check(&input.sandbox)
.map_err(|e| GroupScriptsApiError::Invalid(e.to_string()))?;