Documentation
¶
Index ¶
- Constants
- Variables
- func AllCapabilities() []string
- func CapabilitiesForBuiltin(name string) []string
- func CheckCapabilityAllowed(capability string) error
- func CheckDBAllowed(driver string, dsn string) error
- func CheckExecAllowed(cmd string) error
- func CheckFileReadAllowed(path string) error
- func CheckFileWriteAllowed(path string) error
- func CheckImportAllowed(importPath string, resolvedPath string) error
- func CheckNativeModuleAllowed(moduleName string) error
- func CheckNetworkAllowed(target string) error
- func CleanAbs(path string) string
- func ContainsToken(list []string, item string) bool
- func EnvReadAllowed(key string) error
- func EnvWriteAllowed(key string) error
- func ExitAllowed() error
- func GetDenialHook() func(category, detail string)
- func HasSecretScanner() bool
- func HostFromTarget(target string) (string, error)
- func MatchHostPattern(host string, pattern string) bool
- func PathMatches(path string, patterns []string) bool
- func RegisterSecretScanner(fn func(src string) ([]string, error))
- func ResolveModuleBuiltinName(modulePath, member string) string
- func ScanForHardcodedSecrets(policy *SecurityPolicy, src string) ([]string, error)
- func SetDenialHook(fn func(category, detail string))
- func WithDenialHookOverride(hook func(category, detail string), fn func() (any, error)) (any, error)
- func WithSecurityPolicyOverride(policy *SecurityPolicy, fn func() (any, error)) (any, error)
- type SecurityPolicy
Constants ¶
const ( CapabilityAsync = "async" CapabilityDB = "db" CapabilityEnvRead = "env_read" CapabilityEnvWrite = "env_write" CapabilityExec = "exec" CapabilityFilesystemRead = "filesystem_read" CapabilityFilesystemWrite = "filesystem_write" CapabilityNetwork = "network" CapabilityPolicy = "policy" CapabilityProcessExit = "process_exit" CapabilityScheduler = "scheduler" CapabilityServer = "server" CapabilitySecrets = "secrets" CapabilitySystem = "system" CapabilityWatch = "watch" )
Variables ¶
var BuiltinCapabilities = map[string][]string{}/* 152 elements not displayed */
BuiltinCapabilities maps a registered SPL builtin name — the flat name under which it is reachable via eval.RegisterBuiltins / eval.RegisterPluginBuiltins, i.e. the name that remains after any module-import prefix is stripped (see StdModulePrefixes below) — to the security Capability* constant(s) it may exercise when called at runtime.
This is the data source for the static "capability/effects" analysis in pkg/tooling (AnalyzeEffects) and the `spltool check --effects` CLI flag: it lets a host answer "what might this script try to do" WITHOUT running it, by matching call expressions in the parsed AST against this table.
The table was built by grepping every call site of security.CheckCapabilityAllowed(security.Capability*) and of the capability-implying wrapper helpers (CheckExecAllowed, CheckNetworkAllowed, CheckDBAllowed, CheckFileReadAllowed, CheckFileWriteAllowed, EnvReadAllowed, EnvWriteAllowed, ExitAllowed) across pkg/builtins/**, pkg/render, pkg/builtins/tools, pkg/builtins/scheduler, pkg/builtins/watcher and plugins/** (pdf, database, integrations, ip, emailvalidator, secretr, rules, tcpguard, server), then attributing each check to the enclosing registered builtin name(s).
It is NOT guaranteed exhaustive — a builtin that reaches a capability check only through several layers of indirection may have been missed, and this map must be extended whenever a new builtin gains a capability check (or a new capability constant is introduced; see the TestAllCapabilitiesAreRegistered consistency check in effects_test.go, which fails the build if a capability constant has zero entries here).
var StdModulePrefixes = map[string]string{
"database": "db_",
"images": "image_",
"xql": "xql_",
"lua": "lua_",
"securetoken": "securetoken_",
"naturaldate": "naturaldate_",
"wuid": "wuid_",
"money": "money_",
"phone": "phone_",
"ip": "ip_",
"shamir": "shamir_",
"yaml": "yaml_",
"config/yaml": "yaml_",
"pdf": "pdf_",
"rules": "rules_",
"secretr": "secretr_",
"tcpguard": "tcpguard_",
"emailvalidator": "email_",
}
StdModulePrefixes mirrors the prefix argument passed to RegisterStdBuiltinModuleWithPrefix in the root package's presets_plugins.go: for a module imported as `import "path" as alias`, a call `alias.method(...)` resolves at runtime to the builtin registered as prefix+method. A module not listed here uses an empty prefix, i.e. the module member name IS already the flat builtin name (e.g. `fs.read_file` resolves to the builtin "read_file").
Keep this in sync with presets_plugins.go's init(); it only needs entries for modules whose members are capability-relevant.
Functions ¶
func AllCapabilities ¶
func AllCapabilities() []string
AllCapabilities returns every Capability* constant defined in this package, sorted, for use by consistency checks and reporting.
func CapabilitiesForBuiltin ¶
CapabilitiesForBuiltin returns a copy of the capability constants associated with builtin name, or nil if none are known.
func CheckCapabilityAllowed ¶
func CheckDBAllowed ¶
func CheckExecAllowed ¶
func CheckFileReadAllowed ¶
func CheckFileWriteAllowed ¶
func CheckImportAllowed ¶
func CheckNetworkAllowed ¶
func ContainsToken ¶
func EnvReadAllowed ¶
EnvReadAllowed reports whether reading the given environment variable is permitted under the active security policy. Under ProtectHost/StrictMode (the untrusted/hardened profiles), keys that look like they hold secrets are denied by default; an embedding host must explicitly allowlist CapabilityEnvRead AND disable StrictMode to read such keys. The default trusted profile (ProtectHost=false, StrictMode=false) keeps prior behavior and allows all reads, for backward compatibility.
func EnvWriteAllowed ¶
func ExitAllowed ¶
func ExitAllowed() error
func GetDenialHook ¶
func GetDenialHook() func(category, detail string)
GetDenialHook returns the currently installed process-wide denial hook, or nil if none is set. Safe to call concurrently with SetDenialHook.
func HasSecretScanner ¶
func HasSecretScanner() bool
HasSecretScanner reports whether a scanner has been registered.
func HostFromTarget ¶
func MatchHostPattern ¶
func PathMatches ¶
func RegisterSecretScanner ¶
RegisterSecretScanner installs the hardcoded-secret detector consulted by ScanForHardcodedSecrets. Intended to be called from an optional plugin's init(); the last registration wins. Passing nil clears the scanner.
func ResolveModuleBuiltinName ¶
ResolveModuleBuiltinName returns the registry key that `alias.member(...)` resolves to, given the import path bound to alias (e.g. ResolveModuleBuiltinName("pdf", "to_docx") == "pdf_to_docx"). For ordinary std modules this is the real flat builtin name (prefix+member, or bare member when the module has no prefix - see StdModulePrefixes). The "native/os" module is a special case: its members have no flat global builtin name of their own, so it gets a synthetic "native/os.<member>" key instead, to avoid a generic member name (e.g. "list") colliding with an unrelated module's or the top level's builtin of the same name.
func ScanForHardcodedSecrets ¶
func ScanForHardcodedSecrets(policy *SecurityPolicy, src string) ([]string, error)
ScanForHardcodedSecrets runs the registered secret scanner over src, but only when scanning is opted into - via policy.BlockHardcodedSecrets, or via SPL_BLOCK_HARDCODED_SECRETS=true as a global operator-level toggle - so merely linking a scanner plugin never changes behavior for callers who haven't asked for it. Returns (nil, nil) when scanning is off or no scanner is registered.
policy is taken as an explicit parameter rather than read from ActiveSecurityPolicy() because callers typically need to run this check before the ambient policy override for a request is installed (e.g. the interpreter package checks source text before handing off to sandbox.RunProgramSandboxed, which is what actually installs the per-request override) - pass the request's already-resolved effective policy explicitly instead.
The env var is consulted independently of policy (rather than only via LoadSecurityPolicyFromEnv's own BlockHardcodedSecrets field) because callers such as the CLI's trusted/untrusted sandbox profiles build their own SecurityPolicy internally without necessarily going through LoadSecurityPolicyFromEnv - so without this, an operator's SPL_BLOCK_HARDCODED_SECRETS=true would silently do nothing for those callers even though it works when a caller passes ExecOptions.Security explicitly.
func SetDenialHook ¶
func SetDenialHook(fn func(category, detail string))
SetDenialHook installs fn as the process-wide denial hook, invoked whenever a Check*Allowed function (or ExitAllowed/EnvWriteAllowed/EnvReadAllowed) denies an operation. category is one of the Capability* constants (or a more specific string such as "exec", "network", "db", "file_read", "file_write", "import", "native_module", "env_read", "env_write" for checks that layer additional rules on top of a capability check); detail is a short human-readable reason.
This is a single process-wide hook (see NewRuntime in runtime.go for the tradeoff this implies when multiple Runtimes exist concurrently): the last call to SetDenialHook wins for every goroutine in the process. Passing nil clears the hook. Safe to call concurrently with itself and with GetDenialHook/notifyDenial.
func WithDenialHookOverride ¶
func WithDenialHookOverride(hook func(category, detail string), fn func() (any, error)) (any, error)
WithDenialHookOverride temporarily installs hook as the active per-call denial hook for the duration of fn(), then restores whatever was active before (mirroring WithSecurityPolicyOverride's mutex-serialized swap-and-restore shape). If hook is nil, fn runs with no change - denials during fn() then fall back to the process-wide hook installed via SetDenialHook, if any.
func WithSecurityPolicyOverride ¶
func WithSecurityPolicyOverride(policy *SecurityPolicy, fn func() (any, error)) (any, error)
WithSecurityPolicyOverride temporarily sets the given policy as the active override, calls fn, then restores the previous policy. The callback uses `any` return types as a placeholder for the interpreter's Object type to avoid circular imports. Callers in the interpreter package should type-assert the returned value back to Object.
The mutex is held for the FULL DURATION of fn(), not just for the swap, so that concurrent in-process callers (e.g. the playground's per-request EvalForPlayground calls) are fully serialized and can never observe or be affected by another request's policy during the overlap window.
Types ¶
type SecurityPolicy ¶
type SecurityPolicy = object.SecurityPolicy
func ActiveSecurityPolicy ¶
func ActiveSecurityPolicy() *SecurityPolicy
func LoadSecurityPolicyFromEnv ¶
func LoadSecurityPolicyFromEnv() *SecurityPolicy