security

package
v0.0.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 18, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Index

Constants

View Source
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

View Source
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).

View Source
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

func CapabilitiesForBuiltin(name string) []string

CapabilitiesForBuiltin returns a copy of the capability constants associated with builtin name, or nil if none are known.

func CheckCapabilityAllowed

func CheckCapabilityAllowed(capability string) error

func CheckDBAllowed

func CheckDBAllowed(driver string, dsn string) error

func CheckExecAllowed

func CheckExecAllowed(cmd string) error

func CheckFileReadAllowed

func CheckFileReadAllowed(path string) error

func CheckFileWriteAllowed

func CheckFileWriteAllowed(path string) error

func CheckImportAllowed

func CheckImportAllowed(importPath string, resolvedPath string) error

func CheckNativeModuleAllowed

func CheckNativeModuleAllowed(moduleName string) error

func CheckNetworkAllowed

func CheckNetworkAllowed(target string) error

func CleanAbs

func CleanAbs(path string) string

func ContainsToken

func ContainsToken(list []string, item string) bool

func EnvReadAllowed

func EnvReadAllowed(key string) error

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 EnvWriteAllowed(key string) error

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 HostFromTarget(target string) (string, error)

func MatchHostPattern

func MatchHostPattern(host string, pattern string) bool

func PathMatches

func PathMatches(path string, patterns []string) bool

func RegisterSecretScanner

func RegisterSecretScanner(fn func(src string) ([]string, error))

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

func ResolveModuleBuiltinName(modulePath, member string) string

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL