dc28b63ad8
executus CI / test (push) Successful in 36s
The tool registry core (registry, permission model, Invocation, gated-tool wrapper, ssrf guard, hmac, encryption, argcoerce, helpers, rootrun, session_tools, webhook_rate_limit) had zero mort coupling — it imports only majordomo/llm + x/crypto/hkdf — so it moves verbatim with a package rename (skilltools -> tool). All same-package tests came along and pass; the SSRF, gated-wrapper, encryption and output-pattern invariants are re-anchored here. majordomo re-enters the module graph (now pinned to the latest, incl. the front-loaded-output fix). model/ + llmmeta + structured follow next. Docs: CLAUDE.md now requires README/examples to stay in sync with changes in the same commit; CI skips docs/example-only pushes via paths-ignore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
2.0 KiB
Go
57 lines
2.0 KiB
Go
package tool
|
|
|
|
import "fmt"
|
|
|
|
// CheckAuthoring verifies that the saving user is permitted to author a
|
|
// skill that uses the given tool list. Called from the save path
|
|
// (skills.System.SaveUserSkill); the builtin loader bypasses this check.
|
|
//
|
|
// Why: the AuthoringRequirement gate is the primary admin trust boundary
|
|
// for tools that can read sensitive data (db_select, repo_*) or perform
|
|
// privileged Discord queries. Failing closed at save time prevents the
|
|
// situation where a skill is saved-then-rejected at execute time.
|
|
//
|
|
// What: returns nil if all tools clear; otherwise returns the spec's
|
|
// exact rejection message for the first offending tool.
|
|
//
|
|
// Test: see checks_test.go.
|
|
func CheckAuthoring(reg Registry, tools []string, isAdmin bool) error {
|
|
for _, name := range tools {
|
|
t, ok := reg.Get(name)
|
|
if !ok {
|
|
return fmt.Errorf("unknown tool %q", name)
|
|
}
|
|
if t.Permission().AuthoringRequirement == RequirementAdmin && !isAdmin {
|
|
return fmt.Errorf("The tool `%s` requires admin authoring. Ask an admin to create or publish a skill that uses this tool.", name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckShareSafety verifies that none of the listed tools is unsafe for
|
|
// sharing. Called when a skill's visibility is being set to shared or
|
|
// public.
|
|
//
|
|
// Why: tools that operate on caller-private data (mortbux_get_balance,
|
|
// chatbot_get_memories) leak when invoked by non-owners through a
|
|
// shared/public skill — the executor would compute "the caller is
|
|
// whoever ran the skill", whose data would then surface to the skill
|
|
// authoring user.
|
|
//
|
|
// What: returns nil if all tools clear; otherwise returns the spec's
|
|
// exact rejection message for the first offending tool.
|
|
//
|
|
// Test: see checks_test.go.
|
|
func CheckShareSafety(reg Registry, tools []string) error {
|
|
for _, name := range tools {
|
|
t, ok := reg.Get(name)
|
|
if !ok {
|
|
return fmt.Errorf("unknown tool %q", name)
|
|
}
|
|
if !t.Permission().SafeForShare {
|
|
return fmt.Errorf("The tool `%s` cannot appear in a shared skill because it operates on the caller's own data.", name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|