;; pmacs audit-lint rule set, v1.0 (T M7.9). ;; ;; Each pattern below corresponds to one entry in ;; `src/audit/rules.rs::DEFAULT_RULES` *by index*. Reordering or ;; inserting patterns here without updating the Rust table is a bug; ;; the unit test `rules_table_aligns_with_query_file` catches it. ;; ;; The outermost node of every pattern is captured as `@violation`. ;; The audit engine reports that node's byte range as the finding's ;; snippet and its row/column as the location. ;; ;; Style: queries deliberately match exact identifier text rather ;; than relying on naming heuristics. A package author who routes ;; through a local alias (`local f = io.open` then `f("x", "w")`) ;; bypasses these rules; that is acceptable for v1.0 -- the rule ;; set is a reviewable contract, not an undefeatable barrier. ;; Whole-program data-flow analysis is reserved for the full-moon ;; tier (see TRANSITION-M7.md, T M7.9 section). ;; --------------------------------------------------------------------------- ;; Rule 0: no-private-surface (require) ;; ;; Forbids `require("pmacs._internal.X")` and `require("pmacs.core.X")`. ;; --------------------------------------------------------------------------- ((function_call (identifier) @fn (arguments (string (string_content) @arg))) (#eq? @fn "require") (#match? @arg "^pmacs\\.(_internal|core)(\\.|$)")) @violation ;; --------------------------------------------------------------------------- ;; Rule 1: no-private-surface (identifier) ;; ;; Forbids any identifier prefixed with `_pmacs_internal_` or `_core_`. ;; --------------------------------------------------------------------------- ((identifier) @id (#match? @id "^(_pmacs_internal_|_core_)")) @violation ;; --------------------------------------------------------------------------- ;; Rule 2: no-ffi-call ;; ;; Forbids `ffi.cdef`, `ffi.load`, `ffi.metatype` (LuaJIT FFI surface). ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m)) (#eq? @t "ffi") (#match? @m "^(cdef|load|metatype)$")) @violation ;; --------------------------------------------------------------------------- ;; Rule 3: no-package-loadlib ;; ;; Forbids `package.loadlib(...)` (Lua 5.4 native-library equivalent of ;; the LuaJIT FFI surface). ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m)) (#eq? @t "package") (#eq? @m "loadlib")) @violation ;; --------------------------------------------------------------------------- ;; Rule 4: no-package-cpath-mutation ;; ;; Forbids assignment to `package.cpath` (would extend the C-loader ;; search path, defeating no-FFI). ;; --------------------------------------------------------------------------- ((assignment_statement (variable_list (dot_index_expression (identifier) @t (identifier) @f))) (#eq? @t "package") (#eq? @f "cpath")) @violation ;; --------------------------------------------------------------------------- ;; Rule 5: no-debug-sethook ;; ;; Forbids `debug.sethook(...)`. The pmacs cancellation hook (T M7.8) ;; is installed once at VM init; a package overwriting it disables ;; cooperative cancellation editor-wide. ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m)) (#eq? @t "debug") (#eq? @m "sethook")) @violation ;; --------------------------------------------------------------------------- ;; Rule 6: no-debug-setmetatable ;; ;; Forbids `debug.setmetatable(...)` (bypasses normal metatable rules, ;; can re-skin third-party tables silently). ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m)) (#eq? @t "debug") (#eq? @m "setmetatable")) @violation ;; --------------------------------------------------------------------------- ;; Rule 7: no-rawget-rawset-on-globals ;; ;; Forbids `rawget(_G, ...)` and `rawset(_G, ...)` (escape from any ;; per-package _ENV sandboxing into the shared global table). ;; --------------------------------------------------------------------------- ((function_call (identifier) @fn (arguments . (identifier) @arg1)) (#match? @fn "^(rawget|rawset)$") (#eq? @arg1 "_G")) @violation ;; --------------------------------------------------------------------------- ;; Rule 8: no-setfenv-getfenv ;; ;; Forbids `setfenv` and `getfenv` (Lua 5.1 / LuaJIT environment ;; manipulation; absent from Lua 5.4 by design but still callable on ;; LuaJIT). Static analysis cannot tell whether the target stack ;; frame belongs to another package, so any call is flagged. ;; --------------------------------------------------------------------------- ((function_call (identifier) @fn) (#match? @fn "^(setfenv|getfenv)$")) @violation ;; --------------------------------------------------------------------------- ;; Rule 9: no-fs-mutation-io-open-write ;; ;; Warns on `io.open(, "w"|"a"|"w+"|"a+"|"r+"|"wb"|...)`. The ;; mode string contains 'w' or 'a' or '+' for any non-read-only mode. ;; Read-only `io.open(path, "r")` is allowed; omitting the mode ;; defaults to read and is also allowed. ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m) (arguments . (_) (string (string_content) @mode))) (#eq? @t "io") (#eq? @m "open") (#match? @mode "[wa+]")) @violation ;; --------------------------------------------------------------------------- ;; Rule 10: no-fs-mutation-os ;; ;; Warns on `os.remove(...)` and `os.rename(...)`. ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m)) (#eq? @t "os") (#match? @m "^(remove|rename)$")) @violation ;; --------------------------------------------------------------------------- ;; Rule 11: no-process-spawn-io ;; ;; Warns on `io.popen(...)`. ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m)) (#eq? @t "io") (#eq? @m "popen")) @violation ;; --------------------------------------------------------------------------- ;; Rule 12: no-process-spawn-os ;; ;; Warns on `os.execute(...)`. ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (identifier) @t (identifier) @m)) (#eq? @t "os") (#eq? @m "execute")) @violation ;; --------------------------------------------------------------------------- ;; Rule 13: no-process-spawn-pmacs ;; ;; Warns on `pmacs.process.spawn(...)`. Legitimate users (REPL, ;; magit, LSP launcher) declare process access in their manifest; ;; anything else is a finding. ;; --------------------------------------------------------------------------- ((function_call (dot_index_expression (dot_index_expression (identifier) @t1 (identifier) @t2) (identifier) @m)) (#eq? @t1 "pmacs") (#eq? @t2 "process") (#eq? @m "spawn")) @violation ;; --------------------------------------------------------------------------- ;; Rule 14: reach-around-require ;; ;; Info-level: detects `require("name.submodule")` calls where the ;; require target is dotted (looks like cross-package access). The ;; auditor classifies each finding against the target package's ;; declared `exports`. Absent a known-package registry, this rule ;; fires once per dotted require and a human verifies. With the ;; registry (`pmacs-audit --known-packages packages.toml`) the ;; engine promotes confirmed cross-package private accesses to ;; Error-level findings. ;; ;; The `pmacs.X` namespaces (the host's own surface) are excluded ;; because rules 0/1 already handle private-surface access there. ;; --------------------------------------------------------------------------- ((function_call (identifier) @fn (arguments (string (string_content) @arg))) (#eq? @fn "require") (#match? @arg "\\.") (#not-match? @arg "^pmacs(\\.|$)")) @violation ;; --------------------------------------------------------------------------- ;; Rule 15: reach-around-require-field ;; ;; Info-level: detects field access directly on a required module ;; when the field name is private-looking (`_name`) or a loud ;; in-tree test seam (`__pmacs_*_DO_NOT_USE`). This catches ;; `require("pkg")._private` / `require("pkg").__pmacs_X_DO_NOT_USE`, ;; which the dotted-require rule above cannot see. ;; ;; The host `pmacs.X` namespaces are excluded for the same reason as ;; Rule 14. ;; --------------------------------------------------------------------------- ((dot_index_expression (function_call (identifier) @fn (arguments (string (string_content) @arg))) (identifier) @field) (#eq? @fn "require") (#not-match? @arg "^pmacs(\\.|$)") (#match? @field "^_|^__pmacs_.*_DO_NOT_USE$")) @violation