:PROPERTIES: :ID: 98576df2-c496-4e4a-9acb-0bca514a0305 :CREATED: [2026-03-31 Tue 18:28] :EDITED: [2026-04-09 Thu] :END: #+TITLE: SKILL: Global Safety Harness (Universal Literate Note) #+STARTUP: content #+FILETAGS: :security:sandbox:ast:psf: * Overview The *Global Safety Harness* is the primary "Safety Gate" for the Neurosymbolic Lisp Machine. It provides a recursive AST validator that subjects all Elisp/Lisp proposals from System 1 to a strict "Deny-by-Default" sandbox, preventing arbitrary code execution while allowing high-fidelity system manipulation. * Phase A: Demand (PRD) :PROPERTIES: :STATUS: FROZEN :END: ** 1. Purpose Define a high-integrity, recursive security sandbox for Lisp execution. ** 2. User Needs - *Recursive Validation:* Every nested function call and variable access MUST be checked. - *Deny-by-Default:* Only explicitly whitelisted functions and variables are permitted. - *Eval Protection:* Block all forms of `eval`, `load`, or dynamic execution. - *Symbolic Preemption:* This skill acts as a mandatory global System 2 check. ** 3. Success Criteria *** DONE Implement recursive AST walker in Lisp *** DONE Establish strict function whitelist (surgical Org operations) *** DONE Detect and block nested 'eval' attempts *** DONE Verify that malformed or malicious sexps are rejected * Implementation ** Package #+begin_src lisp :tangle ../src/safety-harness.lisp (in-package :org-agent) #+end_src ** Whitelist Definition #+begin_src lisp :tangle ../src/safety-harness.lisp (defparameter *safety-whitelist* '(;; Math & Logic + - * / = < > <= >= 1+ 1- min max and or not null eq eql equal string= string-equal ;; List Manipulation list cons car cdr cadr cddr cdar caar append mapcar remove-if remove-if-not length reverse sort nth nthcdr push pop ;; Plists and Hash Tables getf gethash ;; Control Flow let let* if cond when unless case typecase ;; Strings format concatenate string-downcase string-upcase search ;; Kernel specifics org-agent::kernel-log org-agent::snapshot-object-store org-agent::rollback-object-store org-agent::lookup-object org-agent::list-objects-by-type org-agent::ingest-ast org-agent::find-headline-missing-id org-agent::context-query-store org-agent::context-get-active-projects org-agent::context-get-recent-completed-tasks org-agent::context-list-all-skills org-agent::context-get-system-logs org-agent::context-assemble-global-awareness org-agent::org-object-id org-agent::org-object-type org-agent::org-object-attributes org-agent::org-object-content org-agent::org-object-parent-id org-agent::org-object-children org-agent::org-object-version org-agent::org-object-last-sync org-agent::org-object-hash ;; Essential macros declare ignore ;; Let's also add simple data types t nil quote function)) #+end_src ** Dynamic Symbol Registration We allow other skills to register safe symbols for the harness. #+begin_src lisp (defvar *safety-registry* nil "List of dynamically registered safe symbols.") (defun safety-harness-register (symbols) "Adds symbols to the global safety registry." (setf *safety-registry* (append *safety-registry* (if (listp symbols) symbols (list symbols)))) (kernel-log "SAFETY HARNESS: Registered ~a new safe symbols." (length (if (listp symbols) symbols (list symbols))))) (defun safety-harness-is-safe (symbol) "Checks if a symbol is in the static whitelist or the dynamic registry." (or (member symbol *safety-whitelist* :test #'string-equal) (member symbol *safety-registry* :test #'string-equal))) #+end_src ** Recursive AST Walker #+begin_src lisp (defun safety-harness-ast-walk (form) "Recursively walks the Lisp AST. Returns T if safe, NIL if unsafe." (cond ;; Self-evaluating objects (strings, numbers, keywords) are safe. ((or (stringp form) (numberp form) (keywordp form) (characterp form)) t) ;; Symbols used as variables (in non-function position) ((symbolp form) (safety-harness-is-safe form)) ;; Lists represent function calls or special forms. ((listp form) (let ((head (car form))) (cond ((eq head 'quote) t) ((not (symbolp head)) nil) ((safety-harness-is-safe head) (every #'safety-harness-ast-walk (cdr form))) (t (kernel-log "SAFETY HARNESS: Blocked call to non-whitelisted function ~a" head) nil)))) (t nil))) #+end_src ** Cognitive Tools #+begin_src lisp (org-agent:def-cognitive-tool :security-telemetry "Returns security-related telemetry, including blocked actions and harness status." nil :body (lambda (args) (declare (ignore args)) (format nil "SAFETY HARNESS STATUS: - Static Whitelist: ~a symbols - Dynamic Registry: ~a symbols - Total Blocked Actions: ~a" (length *safety-whitelist*) (length *safety-registry*) "Not implemented"))) #+end_src ** Skill Definition #+begin_src lisp (org-agent:defskill :skill-safety-harness :priority 900 ; High priority, before most skills :trigger (lambda (ctx) ;; Check if any proposed action is an :eval or :shell call (let ((candidate (getf ctx :candidate))) (when candidate (let ((payload (getf candidate :payload))) (member (getf payload :action) '(:eval :shell)))))) :neuro nil ; Purely symbolic/safety skill :symbolic (lambda (action context) ;; The decide-gate already calls safety-harness-validate via global logic, ;; but this skill can provide additional context or logging. (kernel-log "SYSTEM 2 [Safety]: Intercepted critical action for validation.") action)) #+end_src * Phase E: Chaos (Verification) #+begin_src lisp :tangle ../tests/safety-harness-tests.lisp (defpackage :org-agent-safety-tests (:use :cl :fiveam :org-agent) (:export #:safety-suite)) (in-package :org-agent-safety-tests) (def-suite safety-suite :description "Tests for the Global Safety Harness.") (in-suite safety-suite) (test test-basic-math-safe (is (org-agent:safety-harness-validate "(+ 1 2)"))) (test test-blocked-eval (is (not (org-agent:safety-harness-validate "(eval '(+ 1 2))")))) (test test-blocked-shell (is (not (org-agent:safety-harness-validate "(uiop:run-program \"ls\")")))) (test test-nested-unsafe (is (not (org-agent:safety-harness-validate "(let ((x 1)) (delete-file \"test.txt\"))")))) (test test-safe-kernel-api (is (org-agent:safety-harness-validate "(org-agent::lookup-object \"node-1\")"))) #+end_src