#+TITLE: SKILL: Tool Permissions (org-skill-tool-permissions.org) #+AUTHOR: Agent #+FILETAGS: :skill:security:permissions: #+PROPERTY: header-args:lisp :tangle /home/user/.local/share/passepartout/lisp/security-permissions.lisp * Overview: The Authorization Matrix Every cognitive tool (file read, file write, shell execute, etc.) has a permission level: ~:allow~ (executed without asking), ~:ask~ (user is prompted before execution), or ~:deny~ (blocked entirely). Tool Permissions maintains the registry of these levels and provides the ~permission-gate-check~ that the Dispatcher calls before dispatching a tool action. The complexity lives in the Dispatcher (security-dispatcher.org), which consults this table as one of its ten scan vectors. ** Contract 1. (permission-set tool-name level): stores ~level~ for ~tool-name~ in ~*permission-table*~. Tool names are normalized to lowercase. 2. (permission-get tool-name): returns the stored level, or ~:ask~ if no entry exists. 3. Tool name matching is case-insensitive — ~(permission-set :FOO :allow)~ and ~(permission-get :foo)~ return ~:allow~. ** Boundaries - Does NOT enforce permissions — the Dispatcher does that. - Does NOT persist permissions to disk — this is runtime-only. - Does NOT validate that ~level~ is one of ~(:allow :ask :deny)~. * Implementation ** Package Context #+begin_src lisp (in-package :passepartout) #+end_src ** Permission store (tool level) Hash table mapping tool names to their permission level. ;; REPL-VERIFIED: 2026-05-03T13:00:00 #+begin_src lisp (defvar *permission-table* (make-hash-table :test 'equal)) #+end_src ** Set permission Sets the permission level for a specific cognitive tool. ;; REPL-VERIFIED: 2026-05-03T13:00:00 #+begin_src lisp (defun permission-set (tool-name level) "Sets the permission level for a tool." (setf (gethash (string-downcase (string tool-name)) *permission-table*) level)) #+end_src ** Get permission Retrieves the current permission level for a tool. Defaults to ~:ask~ if unset. ;; REPL-VERIFIED: 2026-05-03T13:00:00 #+begin_src lisp (defun permission-get (tool-name) "Retrieves the permission level for a tool. Defaults to :ask." (gethash (string-downcase (string tool-name)) *permission-table* :ask)) #+end_src ** Skill Registration #+begin_src lisp (defskill :passepartout-security-permissions :priority 600 :trigger (lambda (ctx) (declare (ignore ctx)) nil)) #+end_src * Test Suite #+begin_src lisp (eval-when (:compile-toplevel :load-toplevel :execute) (ql:quickload :fiveam :silent t)) (defpackage :passepartout-security-permissions-tests (:use :cl :fiveam :passepartout) (:export #:permissions-suite)) (in-package :passepartout-security-permissions-tests) (def-suite permissions-suite :description "Verification of Tool Permissions") (in-suite permissions-suite) (test test-permission-round-trip "Contract 1: permission-set stores a level; permission-get retrieves it." (permission-set "test-tool" :allow) (is (eq :allow (permission-get "test-tool"))) ;; Clean up (permission-set "test-tool" nil)) (test test-permission-default "Contract 2: unregistered tools default to :ask." (is (eq :ask (permission-get "never-registered-tool-xyz")))) (test test-permission-case-insensitive "Contract 3: tool names are normalized to lowercase." (permission-set :CapitalTool :deny) (is (eq :deny (permission-get :capitaltool))) (permission-set "CapitalTool" nil)) #+end_src