Documentation
¶
Overview ¶
Package danger classifies shell commands by risk level and provides a configurable approval system for dangerous operations.
Classification is token-based (not regex) — it respects quotes, pipes, redirects, compound commands (&&, ||, ;), and multi-line input. Each command is classified into one of 8 risk classes, and the user can configure which actions (allow/prompt/deny) apply to each class.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Action ¶
type Action string
Action represents what to do when a command of a given risk class is detected.
type Approver ¶
type Approver interface {
// PromptCommand asks the user to approve or deny a shell command.
// cls is the risk class (system_write, network_egress, etc.).
// Returns nil on approve, error on deny or timeout.
PromptCommand(cls RiskClass, cmd, description string) error
// PromptOperation asks the user to approve or deny a native tool operation
// (read_file on /etc, browser to external URL, etc.).
PromptOperation(op ToolOperation) error
}
Approver is the interface for user approval of dangerous operations. Two implementations exist:
- TTYApprover — opens /dev/tty for interactive approval (CLI mode)
- WSApprover — sends approval requests via WebSocket (serve mode)
When nil (no approver configured), calls fall back to non-interactive behavior (NonInteractiveAction). Tools MUST inject an approver to get interactive approval in any mode.
type DangerousConfig ¶
type DangerousConfig struct {
// Classes maps risk classes to their configured action.
// Only overrides for non-default values need to be set.
Classes map[RiskClass]Action `json:"classes,omitempty"`
// Allowlist is a list of command strings that are always allowed,
// regardless of their risk classification. Exact match only.
// Takes priority over Denylist.
Allowlist []string `json:"allowlist,omitempty"`
// Denylist is a list of command strings that are always denied,
// regardless of their risk classification. Prefix match (after trimming).
Denylist []string `json:"denylist,omitempty"`
// DefaultAction is the global default action applied to ALL risk classes
// when set. Per-class overrides in Classes still win.
// "allow" → YOLO mode (everything runs without prompt)
// "deny" → lockdown (everything denied unless explicitly allowed)
// Not set → uses built-in defaults per class
DefaultAction *string `json:"action,omitempty"`
// NonInteractive specifies what to do when running without a TTY.
// "allow" (default) — run everything, "deny" — block all prompted ops.
NonInteractive *string `json:"non_interactive,omitempty"`
// Approver handles interactive approval prompts for dangerous operations.
// When set, all Prompt-class operations use this instead of /dev/tty.
// Tools can inject their own approver (e.g., WebSocket-based for odek serve).
// When nil, CheckOperation falls back to /dev/tty (CLI-compatible default).
Approver Approver `json:"-"`
}
DangerousConfig defines how dangerous operations are handled. Configurable via the standard 4-layer odek config chain.
Default behavior per class (no sandbox):
safe → allow, local_write → allow, system_write → prompt, destructive → deny, network_egress → prompt, code_execution → prompt, install → prompt, blocked → deny
func (*DangerousConfig) ActionFor ¶
func (c *DangerousConfig) ActionFor(cls RiskClass) Action
ActionFor returns the configured action for the given risk class. Per-class overrides in Classes win first, then the global default action (the "action" field), then built-in defaults, then Prompt.
func (*DangerousConfig) ActionForCommand ¶
func (c *DangerousConfig) ActionForCommand(cmd string) Action
ActionForCommand returns the action for a specific command string. Allowlist and denylist are checked first (exact match for allowlist, prefix match for denylist), then falls back to the risk-class-based action.
func (*DangerousConfig) CheckOperation ¶
func (c *DangerousConfig) CheckOperation(op ToolOperation, trustedClasses map[RiskClass]bool) error
CheckOperation checks whether a tool operation is allowed, denied, or needs approval. Returns nil on allow, error on deny, and prompts the user on prompt. Uses the configured Approver when set; falls back to /dev/tty (TTYApprover) when no approver is configured.
func (*DangerousConfig) NonInteractiveAction ¶
func (c *DangerousConfig) NonInteractiveAction() Action
NonInteractiveAction returns the action to use when no TTY is available.
type InjectionPattern ¶
InjectionPattern groups a compiled regex with a human-readable label describing what threat it detects.
type RiskClass ¶
type RiskClass string
RiskClass represents the risk level of a shell command.
func Classify ¶
Classify determines the risk class of a shell command using token-level heuristics. Returns the highest-severity class detected.
Priority (highest to lowest): blocked > destructive > system_write > code_execution > network_egress > install > local_write > safe
func ClassifyPath ¶
ClassifyPath returns a RiskClass for a filesystem path. /tmp/*, working directory → local_write; /etc/*, /root/* → system_write; /boot/*, /dev/*, /sys/* → destructive; home sensitive dirs → system_write.
func ClassifyURL ¶
ClassifyURL returns a RiskClass for a browser URL. Internal IPs → system_write; external → network_egress. Uses proper IP parsing (handles decimal, octal, hex, IPv6 compressed, short forms like 127.1, and all other representations that browsers accept via inet_aton-style parsing) instead of string prefix matching which was trivially bypassable.
type ScanResult ¶
type ScanResult struct {
Label string // human-readable threat label
Pattern string // the regexp pattern that matched (for debugging)
}
ScanResult describes a single detected injection threat.
func ScanInjection ¶
func ScanInjection(content string) []ScanResult
ScanInjection checks content for prompt injection attempts. Returns nil if no threats detected, or a list of found threats. Each threat includes a label describing what was found.
type TTYApprover ¶
type TTYApprover struct {
DangerousConfig *DangerousConfig
TrustedClasses map[RiskClass]bool
TTYPath string // overridden in tests
// contains filtered or unexported fields
}
TTYApprover implements Approver by reading from /dev/tty. This is the default approver used in CLI mode (odek run, odek repl). When /dev/tty is not available (piped stdin, CI), it falls back to the configured NonInteractiveAction.
func NewTTYApprover ¶
func NewTTYApprover(cfg *DangerousConfig) *TTYApprover
NewTTYApprover creates a TTYApprover with the given config.
func (*TTYApprover) PromptCommand ¶
func (a *TTYApprover) PromptCommand(cls RiskClass, cmd, description string) error
func (*TTYApprover) PromptOperation ¶
func (a *TTYApprover) PromptOperation(op ToolOperation) error
func (*TTYApprover) SetTrustAll ¶
func (a *TTYApprover) SetTrustAll(enabled bool)
SetTrustAll enables or disables blanket trust for all risk classes. When enabled, PromptCommand returns nil for every call (used by batch approval).
func (*TTYApprover) SetTrustedClasses ¶
func (a *TTYApprover) SetTrustedClasses(m map[RiskClass]bool)
SetTrustedClasses atomically sets the trusted classes map. Takes ownership of the provided map — caller must not write to it after calling.
type ToolOperation ¶
ToolOperation describes a native tool call for approval checking.