;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun snitch-test--save-vars (&optional deinit)
+ "save all snitch globals so they can be restored after a test"
(when deinit
(snitch-test--cleanup))
(list snitch-network-policy
snitch-network-whitelist
snitch-process-policy
snitch-process-blacklist
- snitch-process-whitelist))
+ snitch-process-whitelist
+ snitch-log-policy
+ snitch-log-verbose
+ snitch--log-buffer-max-lines
+ snitch-on-event-functions
+ snitch-on-allow-functions
+ snitch-on-block-functions
+ snitch-on-whitelist-functions
+ snitch-on-blacklist-functions))
(defun snitch-test--restore-vars (vars)
+ "restore saved vars after a test"
(setq snitch-network-policy (nth 0 vars))
(setq snitch-network-blacklist (nth 1 vars))
(setq snitch-network-whitelist (nth 2 vars))
(setq snitch-process-policy (nth 3 vars))
(setq snitch-process-blacklist (nth 4 vars))
- (setq snitch-process-whitelist (nth 5 vars)))
+ (setq snitch-process-whitelist (nth 5 vars))
+ (setq snitch-log-policy (nth 6 vars))
+ (setq snitch-log-verbose (nth 7 vars))
+ (setq snitch--log-buffer-max-lines (nth 8 vars))
+ (setq snitch-on-event-functions (nth 9 vars))
+ (setq snitch-on-allow-functions (nth 10 vars))
+ (setq snitch-on-block-functions (nth 11 vars))
+ (setq snitch-on-whitelist-functions (nth 12 vars))
+ (setq snitch-on-blacklist-functions (nth 13 vars)))
(defun snitch-test--clear-vars (net-policy proc-policy &optional init)
+ "set global vars to known defaults for duration of a test"
(setq snitch-network-policy net-policy)
(setq snitch-network-blacklist '())
(setq snitch-network-whitelist '())
(setq snitch-process-policy proc-policy)
(setq snitch-process-blacklist '())
(setq snitch-process-whitelist '())
+ (setq snitch-log-policy '())
+ (setq snitch-log-verbose nil)
+ (setq snitch--log-buffer-max-lines 5000)
+ (setq snitch-on-event-functions '())
+ (setq snitch-on-allow-functions '())
+ (setq snitch-on-block-functions '())
+ (setq snitch-on-whitelist-functions '())
+ (setq snitch-on-blacklist-functions '())
(when init
(snitch-init)))
+(defun snitch-test--cleanup ()
+ "kill any spawned processes and restart snitch"
+ (cl-loop for proc in (process-list)
+ do (delete-process proc))
+ (snitch-deinit))
+
+(defun snitch-test--server (port)
+ "launch a TCP server to receive connections"
+ (make-network-process :name (format "ert-test-server-%s" port)
+ :server t
+ :host "127.0.0.1"
+ :service port
+ :family 'ipv4))
+
+
(defun snitch-test--net-client (port expect-success)
"Make a network request to a TCP port. Assert t if allowed
through the firewall, nil if blocked. Note that a refused
(let ((res (condition-case nil
;; returns nil if snitch blocks it, t if it makes a
;; connection
- (make-network-process :name "ert-test"
+ (make-network-process :name "ert-test-net"
:host "127.0.0.1"
:service port
:family 'ipv4)
(error nil))))
(should (if expect-success res (null res)))))
-(defun snitch-test--cleanup ()
- (cl-loop for proc in (process-list)
- do (delete-process proc))
- (snitch-deinit))
-
-(defun snitch-test--server (port)
- (make-network-process :name (format "ert-test-server-%s" port)
- :server t
- :host "127.0.0.1"
- :service port
- :family 'ipv4))
-
+(defun snitch-test--process (exe expected-success)
+ "Launch a processes EXE. Assert that the firewall result
+matches EXPECTED-SUCCESS: t if allowed through, nil if blocked."
+ (let ((res (make-process :name "ert-test-proc" :command (list exe))))
+ (should (if expected-success res (null res)))))
+
+(defun snitch-test--clear-logs ()
+ "clear the snitch log buffer"
+ (with-current-buffer (get-buffer-create snitch--log-buffer-name)
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (setq buffer-read-only t)))
+
+(defun snitch-test--get-log-entry (line)
+ "get a single line from the log buffer (non-verbose)"
+ (with-current-buffer (get-buffer-create snitch--log-buffer-name)
+ (let ((line-count (count-lines (point-min) (point-max))))
+ (when (> line-count line)
+ (goto-char (point-min))
+ (forward-line line)
+ (beginning-of-line)
+ (let* ((line (thing-at-point 'line))
+ (match (string-match "(\\([a-zA-Z]*\\)) -- #s(\\([a-zA-Z-]*\\)" line))
+ (event (match-string-no-properties 1 line))
+ (class (match-string-no-properties 2 line))
+ (props (text-properties-at (point))))
+ (list event class props))))))
+
+(defun snitch-test--log-lines ()
+ "get the total number of lines in the snitch log buffer."
+ (with-current-buffer (get-buffer-create snitch--log-buffer-name)
+ (count-lines (point-min) (point-max))))
+
+(defun snitch-test--get-verbose-log-entry ()
+ "Get the first verbose log in the log buffer. Only supports
+first entry in log buffer."
+ (with-current-buffer (get-buffer-create snitch--log-buffer-name)
+ (goto-char (point-min))
+ (forward-line 1)
+ (let* ((start (point-min))
+ (end (search-forward-regexp "^\\["))
+ (line (replace-regexp-in-string "\n" "" (buffer-substring start (- end 1))))
+ (match (string-match "(\\([a-zA-Z]*\\)) --(\\([a-zA-Z-]*\\)" line))
+ (event (match-string-no-properties 1 line))
+ (class (match-string-no-properties 2 line))
+ (props (text-properties-at (point))))
+ (list event class props))))
+
+(defun snitch-test--proc-entry (exe)
+ "create a dummy process event"
+ (snitch-process-entry
+ :src-fn #'identity
+ :src-path "~/.emacs.d/dummy/dummy.el"
+ :src-pkg 'use-package
+ :proc-name "ert-test-net"
+ :executable exe
+ :args '()))
+
+(defun snitch-test--net-entry (host)
+ "create a dummy network event"
+ (snitch-network-entry
+ :src-fn #'identity
+ :src-path "~/.emacs.d/dummy/dummy-net.el"
+ :src-pkg 'use-package
+ :proc-name "ert-test-proc"
+ :host host
+ :port 80
+ :family 'ipv4))
+
+(defun snitch-test--verify-mnemonic (plist)
+ "verify that the fields of the mnemonic map match. That is,
+MNEMONIC-NAME equals NAME when the square brackets are removed,
+and KEY is the character in the square brackets."
+ (let ((key (plist-get plist 'key))
+ (name (plist-get plist 'name))
+ (mnem-name (plist-get plist 'mnemonic-name)))
+ (and (string-match (format "\\[%s\\]" key) mnem-name)
+ (string-equal name
+ (replace-regexp-in-string
+ "\\(\\[\\|\\]\\)" "" mnem-name)))))
+
+(defun snitch-test--deepen-backtrace ()
+ "call snitch--backtrace from a slightly deeper function stack."
+ (let ((lamb (lambda () (snitch--backtrace))))
+ (funcall lamb)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
(should (string-suffix-p "/emacs-lisp/ert.el" (nth 1 (nth 4 backtrace))))
(should (equal (nth 2 (nth 4 backtrace)) 'built-in))))
-(defun deepen-backtrace ()
- (let ((lamb (lambda () (snitch--backtrace))))
- (funcall lamb)))
-
(ert-deftest snitch-test-backtrace-lambdas ()
"Test that backtraces get appropriately deeper when lambdas and
functions are added to the call stack."
(let* ((outer-backtrace (snitch--backtrace))
(middle-backtrace (funcall (lambda () (snitch--backtrace))))
- (inner-backtrace (funcall (lambda () (deepen-backtrace))))
+ (inner-backtrace (funcall (lambda () (snitch-test--deepen-backtrace))))
(outer-frames (length outer-backtrace))
(middle-frames (length middle-backtrace))
(inner-frames (length inner-backtrace)))
;; verify inner backtrace adds a lambda+deepen+funcall
(should (equal (nth 0 (nth 0 inner-backtrace)) #'funcall))
(should (equal (nth 0 (nth 1 inner-backtrace)) #'let))
- (should (equal (nth 0 (nth 2 inner-backtrace)) #'deepen-backtrace))
+ (should (equal (nth 0 (nth 2 inner-backtrace)) #'snitch-test--deepen-backtrace))
(should (equal (nth 0 (nth 3 inner-backtrace)) 'lambda))
(should (equal (nth 0 (nth 4 inner-backtrace)) #'let*))
(should (equal (nth 0 (nth 5 inner-backtrace)) #'ert--run-test-internal))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;
-;; Test cases: firewall
+;; Test cases: network firewall
;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(snitch-test--cleanup)))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; Test cases: process firewall
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest snitch-test-process-default-deny ()
+ "Test that subprocesses are denied when the default policy is
+set to deny."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ ;; set allow policy
+ (snitch-test--clear-vars 'allow 'deny t)
+
+ (snitch-test--process "ls" nil)
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+
+(ert-deftest snitch-test-process-default-allow ()
+ "Test that subprocesses are permitted when the default policy
+is set to allow."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ ;; set allow policy
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ (snitch-test--process "ls" t)
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-process-blacklist ()
+ "Test that subprocesses are blocked when the policy is allow
+but the event matches a blacklist filter."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ ;; set allow policy
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ ;; both should be allowed by default
+ (snitch-test--process "ls" t)
+ (snitch-test--process "curl" t)
+
+ ;; add the second to the blacklist
+ (setq snitch-process-blacklist
+ '(((lambda (evt exe)
+ (string-equal (oref evt executable) exe)) . ("curl"))))
+
+ ;; first allowed, second blacklisted
+ (snitch-test--process "ls" t)
+ (snitch-test--process "curl" nil)
+
+ ;;;; add both to the blacklist
+ (add-to-list 'snitch-process-blacklist
+ (cons (lambda (evt exe) (string-equal (oref evt executable) exe))
+ (list "ls")))
+ ;; all blacklisted
+ (snitch-test--process "ls" nil)
+ (snitch-test--process "curl" nil)
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-process-whitelist ()
+ "Test that subprocesses are allowed when the policy is deny but
+the event matches a whitelist filter."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ ;; set deny policy
+ (snitch-test--clear-vars 'allow 'deny t)
+
+ ;; both should be denied by default
+ (snitch-test--process "ls" nil)
+ (snitch-test--process "curl" nil)
+
+ ;; add the second to the whitelist
+ (setq snitch-process-whitelist
+ '(((lambda (evt exe)
+ (string-equal (oref evt executable) exe)) . ("curl"))))
+
+ ;; first denied, second whitelisted
+ (snitch-test--process "ls" nil)
+ (snitch-test--process "curl" t)
+
+ ;;;; add both to the whitelist
+ (add-to-list 'snitch-process-whitelist
+ (cons (lambda (evt exe) (string-equal (oref evt executable) exe))
+ (list "ls")))
+ ;; all whitelisted
+ (snitch-test--process "ls" t)
+ (snitch-test--process "curl" t)
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; Test cases: hooks
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest snitch-test-hooks-on-event ()
+ "Test that hooks are called upon receiving any event, and
+returning nil from a hook immediately blocks the event."
+ (setq hook1-var 0)
+ (setq hook2-var 0)
+ (let ((orig-vars (snitch-test--save-vars t))
+ (hook1 (lambda (type event) (setq hook1-var (+ hook1-var 1)) t))
+ (hook2 (lambda (type event) (setq hook2-var (+ hook2-var 1)) t))
+ (hook3 (lambda (type event) nil)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ ;; verify hooks run, but don’t change decision
+ (setq snitch-on-event-functions (list hook1 hook2))
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (should (equal hook1-var 1))
+ (should (equal hook2-var 1))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 2))
+ (should (equal hook2-var 2))
+
+ ;; counter decision with final hook
+ (setq snitch-on-event-functions (list hook1 hook2 hook3))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 3))
+ (should (equal hook2-var 3))
+
+ ;; short-circuit with early hook
+ (setq snitch-on-event-functions (list hook3 hook1 hook2))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 3))
+ (should (equal hook2-var 3))
+
+ ;; verify hooks still run when denied
+ (setq snitch-on-event-functions (list hook1 hook2))
+ (setq snitch-process-policy 'deny)
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 4))
+ (should (equal hook2-var 4))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-hooks-on-allow ()
+ "Test that hooks are called when snitch decides to allow an
+event, and that returning nil from the hooks blocks the event."
+ (setq hook1-var 0)
+ (setq hook2-var 0)
+ (let ((orig-vars (snitch-test--save-vars t))
+ (hook1 (lambda (type event) (setq hook1-var (+ hook1-var 1)) t))
+ (hook2 (lambda (type event) (setq hook2-var (+ hook2-var 1)) t))
+ (hook3 (lambda (type event) nil)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ ;; Add to on-event as well, so it increments by 2 when allowed and
+ ;; by 1 when denied.
+ (setq snitch-on-event-functions (list hook1 hook2))
+
+ ;; verify hooks run, but don’t change decision
+ (setq snitch-on-allow-functions (list hook1 hook2))
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (should (equal hook1-var 2))
+ (should (equal hook2-var 2))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 4))
+ (should (equal hook2-var 4))
+
+ ;; counter decision with final hook
+ (setq snitch-on-allow-functions (list hook1 hook2 hook3))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 6))
+ (should (equal hook2-var 6))
+
+ ;; short-circuit with early hook
+ (setq snitch-on-allow-functions (list hook3 hook1 hook2))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 7))
+ (should (equal hook2-var 7))
+
+ ;; verify hooks don’t run when snitch denies
+ (setq snitch-on-allow-functions (list hook1 hook2))
+ (setq snitch-process-policy 'deny)
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 8))
+ (should (equal hook2-var 8))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-hooks-on-block ()
+ "Test that hooks are called when snitch decides to block an
+event, and that returning nil causes snitch to accept the event."
+ (setq hook1-var 0)
+ (setq hook2-var 0)
+ (let ((orig-vars (snitch-test--save-vars t))
+ (hook1 (lambda (type event) (setq hook1-var (+ hook1-var 1)) t))
+ (hook2 (lambda (type event) (setq hook2-var (+ hook2-var 1)) t))
+ (hook3 (lambda (type event) nil)))
+ (snitch-test--clear-vars 'deny 'deny t)
+
+ ;; Add to on-event as well, so it increments by 2 unless a hook
+ ;; blocks it.
+ (setq snitch-on-event-functions (list hook1 hook2))
+
+ ;; verify hooks run, but don’t change decision
+ (setq snitch-on-block-functions (list hook1 hook2))
+ (snitch-test--url-client "http://127.0.0.1" nil)
+ (should (equal hook1-var 2))
+ (should (equal hook2-var 2))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 4))
+ (should (equal hook2-var 4))
+
+ ;; counter decision with final hook
+ (setq snitch-on-block-functions (list hook1 hook2 hook3))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 6))
+ (should (equal hook2-var 6))
+
+ ;; short-circuit with early hook
+ (setq snitch-on-block-functions (list hook3 hook1 hook2))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 7))
+ (should (equal hook2-var 7))
+
+ ;; verify hooks don’t run when snitch allows
+ (setq snitch-on-block-functions (list hook1 hook2))
+ (setq snitch-process-policy 'allow)
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 8))
+ (should (equal hook2-var 8))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-hooks-on-whitelist ()
+ "Test that hooks are called when snitch accepts an event
+because of a whitelist entry, and that returning nil causes
+snitch to block it."
+ (setq hook1-var 0)
+ (setq hook2-var 0)
+ (let ((orig-vars (snitch-test--save-vars t))
+ (hook1 (lambda (type event) (setq hook1-var (+ hook1-var 1)) t))
+ (hook2 (lambda (type event) (setq hook2-var (+ hook2-var 1)) t))
+ (hook3 (lambda (type event) nil)))
+ (snitch-test--clear-vars 'deny 'deny t)
+
+ ;; Add to on-event as well, so it increments by 2 unless a hook
+ ;; blocks it.
+ (setq snitch-on-event-functions (list hook1 hook2))
+
+ ;; only whitelist ls process
+ (setq snitch-process-whitelist
+ '(((lambda (evt exe)
+ (string-equal (oref evt executable) exe)) . ("ls"))))
+
+ ;; verify hooks run, but don’t change decision
+ (setq snitch-on-whitelist-functions (list hook1 hook2))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 2))
+ (should (equal hook2-var 2))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 4))
+ (should (equal hook2-var 4))
+
+ ;; counter decision with final hook
+ (setq snitch-on-whitelist-functions (list hook1 hook2 hook3))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 6))
+ (should (equal hook2-var 6))
+
+ ;; short-circuit with early hook
+ (setq snitch-on-whitelist-functions (list hook3 hook1 hook2))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 7))
+ (should (equal hook2-var 7))
+
+ ;; verify hooks don’t run with a non-whitelisted exe
+ (setq snitch-on-whitelist-functions (list hook1 hook2))
+ (snitch-test--process "curl" nil)
+ (should (equal hook1-var 8))
+ (should (equal hook2-var 8))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-hooks-on-blacklist ()
+ "Test that hooks are called when snitch decides to block an
+event because of the blacklist, and that returning nil causes
+snitch to accept it."
+ (setq hook1-var 0)
+ (setq hook2-var 0)
+ (let ((orig-vars (snitch-test--save-vars t))
+ (hook1 (lambda (type event) (setq hook1-var (+ hook1-var 1)) t))
+ (hook2 (lambda (type event) (setq hook2-var (+ hook2-var 1)) t))
+ (hook3 (lambda (type event) nil)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ ;; Add to on-event as well, so it increments by 2 unless a hook
+ ;; blocks it.
+ (setq snitch-on-event-functions (list hook1 hook2))
+
+ ;; only blacklist ls process
+ (setq snitch-process-blacklist
+ '(((lambda (evt exe)
+ (string-equal (oref evt executable) exe)) . ("ls"))))
+
+ ;; verify hooks run, but don’t change decision
+ (setq snitch-on-blacklist-functions (list hook1 hook2))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 2))
+ (should (equal hook2-var 2))
+ (snitch-test--process "ls" nil)
+ (should (equal hook1-var 4))
+ (should (equal hook2-var 4))
+
+ ;; counter decision with final hook
+ (setq snitch-on-blacklist-functions (list hook1 hook2 hook3))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 6))
+ (should (equal hook2-var 6))
+
+ ;; short-circuit with early hook
+ (setq snitch-on-blacklist-functions (list hook3 hook1 hook2))
+ (snitch-test--process "ls" t)
+ (should (equal hook1-var 7))
+ (should (equal hook2-var 7))
+
+ ;; verify hooks don’t run with a non-blacklisted exe
+ (setq snitch-on-blacklist-functions (list hook1 hook2))
+ (snitch-test--process "curl" t)
+ (should (equal hook1-var 8))
+ (should (equal hook2-var 8))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; Test cases: logging
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest snitch-test-log-policy-matcher ()
+ "Test that the decisions on whether an event should be log
+match the snitch-log-policy."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ (setq snitch-log-policy '(all))
+ (should (snitch--log-policy-match '(all)))
+ (should (snitch--log-policy-match '(whitelisted)))
+ (should (snitch--log-policy-match '(network-whitelisted)))
+ (should (snitch--log-policy-match '(process-whitelisted)))
+ (should (snitch--log-policy-match '(blacklisted)))
+ (should (snitch--log-policy-match '(network-blacklisted)))
+ (should (snitch--log-policy-match '(process-blacklisted)))
+ (should (snitch--log-policy-match '(allowed)))
+ (should (snitch--log-policy-match '(network-allowed)))
+ (should (snitch--log-policy-match '(process-allowed)))
+ (should (snitch--log-policy-match '(blocked)))
+ (should (snitch--log-policy-match '(network-blocked)))
+ (should (snitch--log-policy-match '(process-blocked)))
+
+ (setq snitch-log-policy '(blacklisted))
+ (should (null (snitch--log-policy-match '(all))))
+ (should (null (snitch--log-policy-match '(whitelisted))))
+ (should (null (snitch--log-policy-match '(network-whitelisted))))
+ (should (null (snitch--log-policy-match '(process-whitelisted))))
+ (should (snitch--log-policy-match '(blacklisted)))
+ (should (snitch--log-policy-match '(network-blacklisted)))
+ (should (snitch--log-policy-match '(process-blacklisted)))
+ (should (snitch--log-policy-match '(blacklisted whitelisted)))
+ (should (null (snitch--log-policy-match '(allowed))))
+ (should (null (snitch--log-policy-match '(network-allowed))))
+ (should (null (snitch--log-policy-match '(process-allowed))))
+ (should (null (snitch--log-policy-match '(blocked))))
+ (should (null (snitch--log-policy-match '(network-blocked))))
+ (should (null (snitch--log-policy-match '(process-blocked))))
+
+ (setq snitch-log-policy '(whitelisted))
+ (should (snitch--log-policy-match '(whitelisted)))
+ (should (snitch--log-policy-match '(network-whitelisted)))
+ (should (snitch--log-policy-match '(process-whitelisted)))
+ (should (snitch--log-policy-match '(blacklisted whitelisted)))
+ (should (null (snitch--log-policy-match '(blacklisted))))
+ (should (null (snitch--log-policy-match '(network-blacklisted))))
+ (should (null (snitch--log-policy-match '(process-blacklisted))))
+ (should (null (snitch--log-policy-match '(allowed))))
+ (should (null (snitch--log-policy-match '(blocked))))
+
+ (setq snitch-log-policy '(allowed))
+ (should (snitch--log-policy-match '(network-allowed)))
+
+ (setq snitch-log-policy '(whitelisted allowed))
+ (should (snitch--log-policy-match '(whitelisted)))
+ (should (snitch--log-policy-match '(network-whitelisted)))
+ (should (snitch--log-policy-match '(process-whitelisted)))
+ (should (snitch--log-policy-match '(whitelisted blacklisted)))
+ (should (null (snitch--log-policy-match '(blacklisted))))
+ (should (null (snitch--log-policy-match '(network-blacklisted))))
+ (should (null (snitch--log-policy-match '(process-blacklisted))))
+ (should (snitch--log-policy-match '(allowed)))
+ (should (snitch--log-policy-match '(network-allowed)))
+ (should (snitch--log-policy-match '(process-allowed)))
+ (should (snitch--log-policy-match '(allowed whitelisted)))
+ (should (snitch--log-policy-match '(allowed whitelisted blacklisted)))
+ (should (null (snitch--log-policy-match '(blocked))))
+ (should (null (snitch--log-policy-match '(blocked blacklisted))))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-all ()
+ "Test that the right log events are received when logging all
+events."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'allow 'deny t)
+
+ (setq snitch-log-policy '(all))
+
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" t)
+
+ ;; first line is the arrival
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "event"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ ;; second line is the decision (allow)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 1)))
+ (should (string-equal event "allowed"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 2)))
+
+ (snitch-test--clear-logs)
+ (snitch-test--process "ls" nil)
+ ;; first line is the arrival
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "event"))
+ (should (string-equal class "snitch-process-entry"))
+ (should (string-equal (plist-get props 'snitch-executable) "ls")))
+ ;; second line is the decision (blocked)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 1)))
+ (should (string-equal event "blocked"))
+ (should (string-equal class "snitch-process-entry"))
+ (should (string-equal (plist-get props 'snitch-executable) "ls")))
+ (should (null (snitch-test--get-log-entry 2)))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-allowed ()
+ "Test that the right log events are received when logging only
+allowed events."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ (setq snitch-log-policy '(allowed))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "allowed"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(network-allowed))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "allowed"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(process-allowed))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (should (null (snitch-test--get-log-entry 0)))
+
+ (setq snitch-log-policy '(process-allowed))
+ (snitch-test--clear-logs)
+ (snitch-test--process "ls" t)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "allowed"))
+ (should (string-equal class "snitch-process-entry"))
+ (should (string-equal (plist-get props 'snitch-executable) "ls")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-blocked ()
+ "Test that the right log events are received when logging only
+blocked events."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'deny 'deny t)
+
+ (setq snitch-log-policy '(blocked))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" nil)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "blocked"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(network-blocked))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" nil)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "blocked"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(process-blocked))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" nil)
+ (should (null (snitch-test--get-log-entry 0)))
+
+ (setq snitch-log-policy '(process-blocked))
+ (snitch-test--clear-logs)
+ (snitch-test--process "ls" nil)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "blocked"))
+ (should (string-equal class "snitch-process-entry"))
+ (should (string-equal (plist-get props 'snitch-executable) "ls")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-whitelisted ()
+ "Test that the right log events are received when logging only
+whitelisted events."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'deny 'deny t)
+
+ (setq snitch-network-whitelist
+ '(((lambda (evt host)
+ (string-equal (oref evt host) host)) . ("127.0.0.1"))))
+ (setq snitch-process-whitelist
+ '(((lambda (evt exe)
+ (string-equal (oref evt executable) exe)) . ("ls"))))
+
+ (setq snitch-log-policy '(whitelisted))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "whitelisted"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(network-whitelisted))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "whitelisted"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(process-whitelisted))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" t)
+ (should (null (snitch-test--get-log-entry 0)))
+
+ (setq snitch-log-policy '(process-whitelisted))
+ (snitch-test--clear-logs)
+ (snitch-test--process "ls" t)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "whitelisted"))
+ (should (string-equal class "snitch-process-entry"))
+ (should (string-equal (plist-get props 'snitch-executable) "ls")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-blacklisted ()
+ "Test that the right log events are received when logging only
+blacklisted events."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ (setq snitch-network-blacklist
+ '(((lambda (evt host)
+ (string-equal (oref evt host) host)) . ("127.0.0.1"))))
+ (setq snitch-process-blacklist
+ '(((lambda (evt exe)
+ (string-equal (oref evt executable) exe)) . ("ls"))))
+
+ (setq snitch-log-policy '(blacklisted))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" nil)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "blacklisted"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(network-blacklisted))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" nil)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "blacklisted"))
+ (should (string-equal class "snitch-network-entry"))
+ (should (string-equal (plist-get props 'snitch-host) "127.0.0.1")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ (setq snitch-log-policy '(process-blacklisted))
+ (snitch-test--clear-logs)
+ (snitch-test--url-client "http://127.0.0.1" nil)
+ (should (null (snitch-test--get-log-entry 0)))
+
+ (setq snitch-log-policy '(process-blacklisted))
+ (snitch-test--clear-logs)
+ (snitch-test--process "ls" nil)
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-log-entry 0)))
+ (should (string-equal event "blacklisted"))
+ (should (string-equal class "snitch-process-entry"))
+ (should (string-equal (plist-get props 'snitch-executable) "ls")))
+ (should (null (snitch-test--get-log-entry 1)))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-prune ()
+ "Test that the log buffer can be pruned to a limited side."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ (setq snitch-log-policy '(all))
+ (snitch-test--clear-logs)
+
+ ;; make 40 logs (2 per connection)
+ (dotimes (i 20) (snitch-test--process "ls" t))
+ (should (eq 40 (snitch-test--log-lines)))
+
+ (setq snitch--log-buffer-max-lines 30)
+ (snitch--prune-log-buffer)
+ (should (eq 30 (snitch-test--log-lines)))
+
+ (setq snitch--log-buffer-max-lines 10)
+ (snitch--prune-log-buffer)
+ (should (eq 10 (snitch-test--log-lines)))
+
+ (setq snitch--log-buffer-max-lines 1)
+ (snitch--prune-log-buffer)
+ (should (eq 1 (snitch-test--log-lines)))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-prune-timer ()
+ "Test that the log pruning timer prunes the log correctly."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'allow 'allow t)
+ (setq snitch-log-policy '(all))
+ (snitch-test--clear-logs)
+ ;; make 10 logs (2 per connection)
+ (dotimes (i 5) (snitch-test--process "ls" t))
+ (should (eq 10 (snitch-test--log-lines)))
+
+ (setq snitch--log-buffer-max-lines 5)
+ (snitch--start-log-prune-timer)
+ (timer-set-idle-time snitch--log-prune-timer 0)
+ (timer-activate snitch--log-prune-timer)
+ (sleep-for 0.5)
+ (should (eq 5 (snitch-test--log-lines)))
+ (should (null snitch--log-prune-timer))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+(ert-deftest snitch-test-log-verbose ()
+ "Test that the log buffer receives larger verbose logs when
+snitch-log-verbose is t."
+ (let ((orig-vars (snitch-test--save-vars t)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ (setq snitch-log-policy '(all))
+ (setq snitch-log-verbose t)
+ (snitch-test--clear-logs)
+ (snitch-test--process "ls" t)
+
+ (pcase-let ((`(,event ,class ,props) (snitch-test--get-verbose-log-entry)))
+ (should (string-equal event "event"))
+ (should (string-equal class "snitch-process-entry"))
+ (should (string-equal (plist-get props 'snitch-executable) "ls")))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; Test cases: log filter UI
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest snitch-test-log-filter-mnemonics ()
+ "Test that the name/mnemonic name/key shortcut mappings all
+match for every display line of the log filter UI."
+ (let* ((proc-event (snitch-test--proc-entry "ls"))
+ (net-event (snitch-test--net-entry "127.0.0.1"))
+ (proc-map (snitch--log-filter-map proc-event))
+ (net-map (snitch--log-filter-map net-event)))
+ ;; common fields
+ (should (snitch-test--verify-mnemonic (alist-get 'src-fn net-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'src-fn proc-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'src-path net-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'src-path proc-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'src-pkg net-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'src-pkg proc-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'proc-name net-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'proc-name proc-map)))
+ ;; net fields
+ (should (snitch-test--verify-mnemonic (alist-get 'host net-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'port net-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'family net-map)))
+ ;; proc fields
+ (should (snitch-test--verify-mnemonic (alist-get 'executable proc-map)))
+ (should (snitch-test--verify-mnemonic (alist-get 'args proc-map)))))
+
+(ert-deftest snitch-test-log-filter-popup-hook ()
+ "Test that the user hook is called when the log filter buffer
+is shown or hidden."
+ (setq hook1-var 0)
+ (let ((orig-vars (snitch-test--save-vars t))
+ (hook1 (lambda () (setq hook1-var (+ hook1-var 1)) t)))
+ (snitch-test--clear-vars 'allow 'allow t)
+
+ (setq snitch-log-filter-window-open-hook (list hook1))
+ (setq snitch-log-filter-window-close-hook (list hook1))
+ (snitch--init-log-filter-buffer)
+ (snitch--show-log-filter-window)
+ (should (equal 1 hook1-var))
+ (snitch--hide-log-filter-window snitch--log-filter-buffer)
+ (should (equal 2 hook1-var))
+
+ ;; cleanup
+ (snitch-test--restore-vars orig-vars)
+ (snitch-test--cleanup)))
)
(defun snitch--test-wrap-network-process ()
+ (snitch-init)
(make-network-process :name "netpoop" :host "blommorna.com" :service 443 :family 'ipv4)
(url-retrieve "http://google.com" #'identity)
(setq snitch--log-buffer-max-lines 5)