Pixir.Permissions (pixir v0.1.7)

Copy Markdown View Source

Permission policy (ADR 0006). Pure decision function — the Executor consults it and, for :ask, calls a front-end-supplied asker.

Modes:

  • :auto (default) — everything is allowed; no prompts. The blessed common path.
  • :ask — only genuinely risky operations prompt. read never asks; bash commands on a conservative safe-list auto-run; write and non-safe bash ask.
  • :read_only — mutating tools are denied; reads and safe commands run.

Workspace confinement is enforced elsewhere (the tools) and is the real floor in every mode — this layer is a convenience gate on top. Shell-token confinement is a conservative pre-exec tripwire, not a shell parser or sandbox: it checks visible path tokens, while intentionally ignoring RHS values of leading POSIX environment assignments before a simple command, so residual runtime expansion such as VAR=/outside cmd $VAR remains out of scope.

Summary

Functions

Classify whether a shell token contains a parent-directory path segment.

Decide whether a tool call may run under mode.

All valid permission modes.

Whether a tool call mutates state (and so is gated outside :auto).

Whether a find token is a mutating predicate such as -delete or -exec.

Return the first shell token that resolves outside workspace.

Whether a shell command is read-only and safe to auto-run: command executable on the safe-list, no shell metacharacters, no parent-directory path references, no mutating git subcommand, and no mutating find predicate.

Strip simple single/double quote wrapping from a shell token.

Types

decision()

@type decision() :: :allow | :deny | {:ask, String.t()}

mode()

@type mode() :: :auto | :ask | :read_only

Functions

classify_parent_directory_token(token)

@spec classify_parent_directory_token(term()) :: {:ok, boolean()}

Classify whether a shell token contains a parent-directory path segment.

decide(atom, tool, args)

@spec decide(mode(), String.t(), map()) :: decision()

Decide whether a tool call may run under mode.

modes()

@spec modes() :: [mode()]

All valid permission modes.

mutating?(arg1, arg2)

@spec mutating?(String.t(), map()) :: boolean()

Whether a tool call mutates state (and so is gated outside :auto).

mutating_find_predicate?(token)

@spec mutating_find_predicate?(term()) :: boolean()

Whether a find token is a mutating predicate such as -delete or -exec.

outside_workspace_shell_token(command, workspace)

@spec outside_workspace_shell_token(term(), String.t()) :: {:ok, String.t() | nil}

Return the first shell token that resolves outside workspace.

This is a conservative workspace-confinement tripwire for shell-shaped commands. It catches explicit parent-directory references, absolute paths outside the workspace, home/env-home references, and relative paths whose deepest existing prefix resolves outside the workspace. Leading POSIX environment assignments before a simple command do not contribute their RHS as a path candidate; literal path arguments, redirection targets, and non-leading NAME=VALUE arguments are still checked. It is not a full shell parser or sandbox; callers should still keep shell execution behind their normal permission and command-boundary gates.

safe_command?(command)

@spec safe_command?(String.t()) :: boolean()

Whether a shell command is read-only and safe to auto-run: command executable on the safe-list, no shell metacharacters, no parent-directory path references, no mutating git subcommand, and no mutating find predicate.

strip_shell_quotes(token)

@spec strip_shell_quotes(String.t()) :: String.t()

Strip simple single/double quote wrapping from a shell token.