+snitch.el (pronounced like schnitzel) is a firewall for Emacs.
+
+snitch intercepts calls to create network connections or launch
+subprocesses. Through user-configured default policies, filter
+rules, and user hooks it is able to log and potentially block each
+action. It can be configured with ‘M-x customize-group <RET>
+snitch’.
+
+Subprocesses and network connections are handled independently,
+with their own separate default policies, blacklist and whitelist,
+and logging policies.
+
+The main purpose of snitch is network monitoring. Subprocesses are
+included because it is extremely common for Emacs packages to
+"shell out" to an external program for network access, commonly to
+‘curl’. As a side effect, snitch can also effectively audit and
+prevent undesired access to other programs.
+
+Notifications can be raised on each logged event by ensuring the
+’alert’ package is installed and customizing
+‘snitch-enable-notifications’ to t.
+
+
+=== WHY? ===
+
+Emacs is a general-purpose execution environment, executing with
+the full privileges of whichever user launched it. It can read and
+create files, obviously, but also spawn external programs, open
+network connections, and communicate through pipes. In modern
+times, most users manage large collections of third-party packages
+through intelligent package managers that automatically pull in any
+number of dependencies, updated periodically. Any and all of these
+could be a bit naughty, and the sheer quantity of Lisp code in a
+modern Emacs install makes it un-auditable.
+
+An Emacs firewall, thus, makes sense. Does *snitch* make sense?
+Not really... see the SECURITY section below. But we currently
+have nothing, and snitch is better than nothing.
+
+Also, to answer the question: "I wonder if I can make an Emacs
+firewall?" Yes! ...well, sort of.
+
+
+=== MECHANISM ===
+
+The underlying ’firewall’ mechanism is built on function advice
+surrounding Emacs’s lowest-level core functions for spawning
+connections or subprocesses. When an Emacs package or script makes
+such a request, snitch receives it first, and either passes it
+through or rejects it based on the current rules. Once a
+connection or process is accepted, snitch is no longer involved for
+the duration of that particular communication stream.
+
+For each intercepted call, snitch first builds an event object
+defining everything snitch knows about the call. The metadata
+differs for network connections (host, port, family) and processes
+(executable and argument list), but all events share a common set:
+calling function, calling function’s file path, calling package,
+and request name.
+
+Once an event object is created, it is passed to any hooks defined
+in ‘snitch-on-event-functions’ for early processing. If a hook
+returns nil, the event is dropped immediately. Otherwise, snitch
+then checks the corresponding whitelist (if the default policy is
+deny) or the blacklist (if the default policy is allow) and makes
+its internal decision. Before executing the decision, it calls the
+corresponding hook functions to give the user hooks one more
+opportunity to change the decision. Finally, only if the decision
+was ‘allow’, snitch executes the original request and passes the
+result back to the caller.
+
+As the event flows through the decision tree, it also triggers log
+events. There are several different types defined in
+‘snitch-log-policies’, and users can subscribe to any combination
+of them by customizing ‘snitch-log-policy’. Logs are displayed in
+text format in a dedicated log buffer (by default: ‘*snitch
+firewall log*’), along with text properties that allow extracting
+the event information programatically from a log line with
+‘get-text-property’. The text lines can be "pretty printed" by
+customizing ‘snitch-log-verbose’.
+
+An example log entry is below, split to several lines for display.
+In the actual log, non-verbose logs are a single line.
+
+> [2020-12-03 00:16:50] (whitelisted) -- #s(snitch-network-entry \
+> 1606951010.2966838 helm-M-x-execute-command \
+> /home/trevor/.emacs.d/elpa/helm-20201019.715/helm-command.el \
+> helm 127.0.0.1 127.0.0.1 64222 nil)
+
+With `snitch-log-verbose' enabled, log entries actually do take
+several lines:
+
+> [2020-12-03 01:11:27] (blocked) --
+> (snitch-network-entry "snitch-network-entry-157d34506664"
+>
+> :timestamp 1606954287.770638
+> :src-fn snitch--wrap-make-network-process
+> :src-path "/home/trevor/.emacs.d/snitch/snitch.el"
+> :src-pkg user
+> :proc-name "google.com"
+> :host "google.com"
+> :port 80)
+
+
+=== GETTING SNITCH ===
+
+snitch is not currently published in any package repositories
+(*ELPA). It can be installed by any package manager that supports
+git repositories, or manually.
+
+quelpa:
+
+> (quelpa '(snitch :repo "mrmekon/snitch-el" :fetcher github))
+
+use-package + quelpa + quelpa-use-package:
+
+> (use-package snitch
+> :quelpa (snitch :repo "mrmekon/snitch-el" :fetcher github))
+
+el-get:
+
+> (el-get-bundle mrmekon/snitch-el)
+
+straight.el:
+
+> (straight-use-package
+> '(snitch :type git :host github :repo "mrmekon/snitch-el"))
+
+manual:
+
+> (package-install-file "/path/to/snitch-x.y.z.tar")
+
+
+=== USAGE ===
+
+Enabling snitch is as simple as calling ‘(snitch-init)’.
+Initialization does very little, so this is safe to call in your
+Emacs init without worrying about deferral or negative consequences
+on startup time.
+
+The minimum required initialization is simply:
+
+> (require 'snitch)
+> (snitch-init)
+
+An example initialization using ‘use-package’ might look like so:
+
+> (use-package snitch
+> :config
+> (snitch-init))
+
+snitch then runs in the background, performing its duties according
+to your configuration, and logging in its dedicated buffer.
+
+You may add firewall exception rules manually, as covered in the
+CONFIGURATION section below. Alternatively, you can also build
+filters with a guided UI by switching to the firewall log buffer
+(‘*snitch firewall log*’), highlighting an entry that you wish to
+filter on, and execute ‘M-x snitch-filter-from-log’. This launches
+a popup window that allows you to configure a new filter based on
+one or more fields of the selected log line, and add it to either
+your blacklist or whitelist.
+
+To disable snitch, call ‘(snitch-deinit)’.
+
+
+=== CONFIGURATION ===
+
+Customize snitch with ‘M-x customize-group <RET> snitch’, or
+manually in your Emacs initialization file.
+
+Most users will have five variables that need to be configured
+before use:
+
+ - ‘snitch-network-policy’ -- whether to allow or deny network
+connections by default.
+
+ - ‘snitch-process-policy’ -- whether to allow or deny subprocesses
+by default.
+
+ - ‘snitch-log-policy’ -- which events to log (to see the options,
+run ‘M-x describe-variable <RET> snitch-log-policies’)
+
+ - ‘snitch-network-*list’ -- filter rules containing exceptions to
+the default network policy. See FILTER RULES below. Use
+‘-whitelist’ if the default policy is ‘deny’, or ‘-blacklist’ if
+the default policy is ‘allow’
+
+ - ‘snitch-process-*list’ -- filter rules containing exceptions to
+the default process policy. See FILTER RULES below. Use
+‘-whitelist’ if the default policy is ‘deny’, or ‘-blacklist’ if
+the default policy is ‘allow’
+
+
+Have a look in ‘snitch-filter.el’ for examples of black/whitelist
+filters, and in ‘snitch-test.el’ for contrived examples of pretty
+much everything.
+
+
+==== COMMON CONFIG: DENY ====
+
+A useful configuration is to deny all external communication by
+default, but allow certain packages to communicate. This example
+demonstrates permitting only the ’elfeed’ package to create network
+connections:
+
+> (use-package snitch
+> :config
+> (setq snitch-network-policy 'deny)
+> (setq snitch-process-policy 'deny)
+> (setq snitch-log-policy '(blocked whitelisted allowed))
+> (add-to-list 'snitch-network-whitelist
+> (cons #'snitch-filter-src-pkg '(elfeed)))
+> (snitch-init))
+
+
+==== COMMON CONFIG: ALLOW + AUDIT ====
+
+Another useful configuration is to allow all accesses, but log them
+to keep an audit trail. This might look like so:
+
+> (use-package snitch
+> :config
+> (setq snitch-network-policy 'allow)
+> (setq snitch-process-policy 'allow)
+> (setq snitch-log-policy '(allowed blocked whitelisted blacklisted))
+> (setq snitch-log-verbose t)
+> (snitch-init))
+
+
+==== FILTER RULES ====
+
+Filter rules, as specified in ‘snitch-(process|network)-*list’
+variables, are specified as cons cells where the car is a filtering
+function, and the cdr is a list of arguments to pass to the
+function in addition to the event object:
+
+> (setq snitch-network-whitelist
+> '(
+> (filter-fn1 . (argQ))
+> (filter-fn2 . (argN argP))
+> ))
+
+Each filter function should have a prototype accepting EVENT as the
+snitch event object in consideration, and ARGS as the list of
+arguments from the cdr of the rules entry:
+
+> (defun filter-fn1 (event &rest args))
+
+EVENT is an eieio object defined by ‘snitch-network-entry’ or
+‘snitch-process-entry’, and inheriting from ‘snitch-source’.
+
+A trivial function which matches if a single string in the event
+object matches a known value might look like so:
+
+> (defun filter-fn1 (event name)
+> (string-equal (oref event proc-name) name))
+
+While a more complex filter function might treat ARGS as an
+associative list of key/value pairs:
+
+> (defun filter-fn2 (event &rest alist)
+> (cl-loop for (aslot . avalue) in alist with accept = t
+> do
+> (let ((evalue (eieio-oref event aslot))
+> (val-type (type-of avalue)))
+> (unless (cond
+> ((eq val-type 'string) (string-equal avalue evalue))
+> (t (eq avalue evalue)))
+> (setq accept nil)))
+> when (null accept)
+> return nil
+> finally return accept))
+
+The return value of a filter function determines whether the filter
+should take effect. t means "take effect" and nil means "do not
+take effect". What that means for the event depends on which list
+the filter rule is in. If the rule is in a whitelist, t means
+allow and nil means block. If it is in a blacklist, t means block
+and nil means allow.
+
+
+==== HOOKS ====
+
+Events are passed to user-provided hook functions, if specified.
+These hooks can subscribe to receive events either immediately on
+arrival, upon a final decision, or both. The hooks can change
+snitch’s final decision.
+
+Hook functions take two arguments, the type and the event object:
+
+> (defun snitch-hook (type event))
+
+TYPE is one of `snitch-hook-types', and corresponds with the names
+of the hook lists. This argument is provided so you can define one
+function which can be used in several hooks.
+
+EVENT is an eieio object defined by ‘snitch-network-entry’ or
+‘snitch-process-entry’, and inheriting from ‘snitch-source’.
+
+Hooks should return t to allow snitch to continue processing as it
+would have, or return nil to reverse snitch’s decision. For hooks
+in ‘snitch-on-event-functions’, returning nil cancels all further
+processing of the event and blocks it immediately. For other hook
+lists, returning nil reverses the action implied by the list name:
+returning nil in a ‘snitch-on-allow-functions’ hook causes the
+event to be blocked, returning nil in a ‘snitch-on-block-functions’
+hook causes it to be allowed.
+
+
+=== PERFORMANCE ===
+
+Performance has not been measured, and should not be assumed to be
+particularly good. Nothing is currently optimized.
+
+Memory usage should not be particularly high, as events are
+ephemeral and only contain a small amount of metadata. The largest
+use of memory is the audit log, which does keep copies of all
+events in the log. This can be controlled via
+‘snitch-log-buffer-max-lines’.
+
+Firewall rules are traversed linearly, and short-circuit (if an
+early rule terminates processing, the subsequent rules will not be
+considered). To optimize for performance, the total number of
+rules should be kept to a minimum, and most likely to match rules
+should be added earlier in the lists.
+
+
+=== TIMER TRACING ===
+
+Since snitch’s usefulness is highly dependent on the ability to
+trace back to the original source that triggered an event, Emacs
+timers pose a bit of a challenge. Timers are used to trigger
+network requests asynchronously, but have the side effect of losing
+the stack trace back to the function or package that initiated it.
+
+To deal with this, snitch optionally supports timer tracing. When
+tracing is enabled, by customizing ‘snitch-trace-timers’ to t,
+snitch hooks into Emacs’s timer functions, and records backtraces
+whenever a timer is registered. If a timer later generates a
+snitch-relevant event, snitch concatenates the regular backtrace
+with the cached timer backtrace to get a full call stack for the
+event.
+
+As an example, here are two snitch log entries when opening RSS
+feeds with the elfeed package, which uses timers for web requests:
+
+With ‘snitch-trace-timers’ set to nil (tracing disabled):
+
+> [2020-12-07 21:32:56] (allowed) -- #s(snitch-network-entry \
+> 1607373176.6757963 \
+> timer-event-handler \
+> /usr/share/emacs/27.1/lisp/emacs-lisp/timer.el \
+> site-lisp \
+> www.smbc-comics.com www.smbc-comics.com 443 nil)
+
+Notice how the source is the function ‘timer-event-handler’ in
+‘timer.el’, part of the special ‘site-lisp’ package? *All*
+timer-originated network calls appear to originate from that
+function, since it is the lowest level Emacs timer dispatch
+function. It is impossible to filter on the true source.
+
+Now with ‘snitch-trace-timers’ set to t (tracing enabled):
+
+> [2020-12-07 21:33:06] (allowed) -- #s(snitch-network-entry \
+> 1607373186.6863618 \
+> elfeed-insert-html
+> /home/trevor/.emacs.d/elpa/elfeed-20200910.239/elfeed-show.el \
+> elfeed \
+> www.smbc-comics.com www.smbc-comics.com 443 nil)
+
+For this event, snitch has successfully traced through the timer to
+find the true source, ‘elfeed-insert-html’ in the ‘elfeed’ package!
+
+Timer tracing comes with a cost: snitch has to generate metadata
+for every single timer event. If your Emacs usage involves a very
+large number of timers, or very high-frequency timers, snitch’s
+tracing could lead to delays and inflated memory usage. Consider
+carefully whether this is a feature you need, and leave it disabled
+if you will not use it, or if you experience any performance issues
+while running snitch.
+
+You can run ‘snitch-monitor-unique-timer-fns’ to get a sense of
+which timers are currently active. After running that function,
+there will be a 60 second delay, followed by printing the names of
+all timers that were active during the minute and the number of
+times they fired.
+
+Similarly, if you run with timer tracing enabled for a while, you
+can use ‘snitch--debug-print-timer-state’ to print a summary of how
+many timers snitch has intercepted, and how many saved backtraces
+are currently active in memory.
+
+
+=== SECURITY ===
+
+snitch provides, effectively, zero security.
+
+If you were to ask your Principal Security Engineer friends, they
+might say that an effective security boundary must be
+"tamper-proof" and provide "complete mediation." snitch does
+neither.
+
+Tamper-proof: none at all. Any other Emacs package can simply
+disable snitch, or modify it to pass malicious traffic undetected.
+
+Complete mediation: no attempt has been made to verify that *all*
+network and subprocess accesses must go through the functions that
+snitch hooks. Given the complexity of Emacs, it is extremely
+unlikely that they do.
+
+However, your Principal Security Engineer friends also like to
+blather on about ’defining your security model’, and a fun game to
+play with them is to define your security model such that none of
+the insecurities are in it. As so:
+
+Security model: includes malicious adversaries
+snitch effectiveness: zero.
+
+Security model: includes no malicious adversaries
+snitch effectiveness: great!
+
+snitch is useful for auditing and blocking unwanted features in an
+otherwise well-behaving ecosystem. It is handy for getting a
+record of exactly what your Emacs is doing, and for fine-tuning
+accesses beyond Emacs’s boundaries a little bit better. It will
+not, however, save you from the bad guys.
+
+
+=== KNOWN LIMITATIONS ===
+
+When snitch blocks events, some Emacs functions that seldom throw
+errors in normal use will throw errors because of snitch. It is
+very likely that blocked connections will cause errors to bubble up
+in strange and unexpected ways, as many package authors have not
+handled these exceptional cases.
+
+snitch does not intercept domain name resolution (DNS).
+
+snitch has a strong preference for identifying user-provided
+packages as the "originating source" of events. Events that you
+may consider as originated in built-in/site-lisp code may be
+attributed to a user package instead, if one is higher up in the
+backtrace. For instance, `helm' may often show up as the source if
+installed, since `helm-M-x-execute-command' is often somewhere in
+the stack.
+
+snitch has not been tested with IPv6.
+
+snitch has not been tested with inbound connections. In theory, it
+can prevent the creation of a listening socket. Once a socket is
+open, though, it would not be able to monitor incoming connections
+to the socket.
+
+
+=== TODO ===
+
+ - send notifications in batches?
+ - interactive prompts?
+ - handle service strings as port numbers
+ - ensure the inverted negation rules make sense
+ - add blacklist for timer functions
+ - publish on MELPA?
+ - profit!
+
+
+=== VERSION HISTORY ===
+
+v0.2.0 (2020-12-09)
+
+ - first published version
+
+v0.1.0 (before 2020-12-09)
+
+ - Initial development and testing
+ - Network and process firewall functionality
+ - Audit logging
+ - Whitelist + blacklist filtering
+ - Backtrace processing
+ - Timer backtrace expansion
+ - User event and logging hooks
+ - ert test framework
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+Floor, Boston, MA 02110-1301, USA.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
-```
-snitch.el (pronounced like schnitzel) is a firewall for Emacs.
-
-snitch intercepts calls to create network connections or launch
-subprocesses. Through user-configured default policies, filter
-rules, and user hooks it is able to log and potentially block each
-action. It can be configured with ‘M-x customize-group <RET>
-snitch’.
-
-Subprocesses and network connections are handled independently,
-with their own separate default policies, blacklist and whitelist,
-and logging policies.
-
-The main purpose of snitch is network monitoring. Subprocesses are
-included because it is extremely common for Emacs packages to
-"shell out" to an external program for network access, commonly to
-‘curl’. As a side effect, snitch can also effectively audit and
-prevent undesired access to other programs.
-
-Notifications can be raised on each logged event by ensuring the
-’alert’ package is installed and customizing
-‘snitch-enable-notifications’ to t.
-
-
-=== WHY? ===
-
-Emacs is a general-purpose execution environment, executing with
-the full privileges of whichever user launched it. It can read and
-create files, obviously, but also spawn external programs, open
-network connections, and communicate through pipes. In modern
-times, most users manage large collections of third-party packages
-through intelligent package managers that automatically pull in any
-number of dependencies, updated periodically. Any and all of these
-could be a bit naughty, and the sheer quantity of Lisp code in a
-modern Emacs install makes it un-auditable.
-
-An Emacs firewall, thus, makes sense. Does *snitch* make sense?
-Not really... see the SECURITY section below. But we currently
-have nothing, and snitch is better than nothing.
-
-Also, to answer the question: "I wonder if I can make an Emacs
-firewall?" Yes! ...well, sort of.
-
-
-=== MECHANISM ===
-
-The underlying ’firewall’ mechanism is built on function advice
-surrounding Emacs’s lowest-level core functions for spawning
-connections or subprocesses. When an Emacs package or script makes
-such a request, snitch receives it first, and either passes it
-through or rejects it based on the current rules. Once a
-connection or process is accepted, snitch is no longer involved for
-the duration of that particular communication stream.
-
-For each intercepted call, snitch first builds an event object
-defining everything snitch knows about the call. The metadata
-differs for network connections (host, port, family) and processes
-(executable and argument list), but all events share a common set:
-calling function, calling function’s file path, calling package,
-and request name.
-
-Once an event object is created, it is passed to any hooks defined
-in ‘snitch-on-event-functions’ for early processing. If a hook
-returns nil, the event is dropped immediately. Otherwise, snitch
-then checks the corresponding whitelist (if the default policy is
-deny) or the blacklist (if the default policy is allow) and makes
-its internal decision. Before executing the decision, it calls the
-corresponding hook functions to give the user hooks one more
-opportunity to change the decision. Finally, only if the decision
-was ‘allow’, snitch executes the original request and passes the
-result back to the caller.
-
-As the event flows through the decision tree, it also triggers log
-events. There are several different types defined in
-‘snitch-log-policies’, and users can subscribe to any combination
-of them by customizing ‘snitch-log-policy’. Logs are displayed in
-text format in a dedicated log buffer (by default: ‘*snitch
-firewall log*’), along with text properties that allow extracting
-the event information programatically from a log line with
-‘get-text-property’. The text lines can be "pretty printed" by
-customizing ‘snitch-log-verbose’.
-
-An example log entry is below, split to several lines for display.
-In the actual log, non-verbose logs are a single line.
-
-> [2020-12-03 00:16:50] (whitelisted) -- #s(snitch-network-entry \
-> 1606951010.2966838 helm-M-x-execute-command \
-> /home/trevor/.emacs.d/elpa/helm-20201019.715/helm-command.el \
-> helm 127.0.0.1 127.0.0.1 64222 nil)
-
-With `snitch-log-verbose' enabled, log entries actually do take
-several lines:
-
-> [2020-12-03 01:11:27] (blocked) --
-> (snitch-network-entry "snitch-network-entry-157d34506664"
->
-> :timestamp 1606954287.770638
-> :src-fn snitch--wrap-make-network-process
-> :src-path "/home/trevor/.emacs.d/snitch/snitch.el"
-> :src-pkg user
-> :proc-name "google.com"
-> :host "google.com"
-> :port 80)
-
-
-=== GETTING SNITCH ===
-
-snitch is not currently published in any package repositories
-(*ELPA). It can be installed by any package manager that supports
-git repositories, or manually.
-
-quelpa:
-
-> (quelpa '(snitch :repo "mrmekon/snitch-el" :fetcher github))
-
-use-package + quelpa + quelpa-use-package:
-
-> (use-package snitch
-> :quelpa (snitch :repo "mrmekon/snitch-el" :fetcher github))
-
-el-get:
-
-> (el-get-bundle mrmekon/snitch-el)
-
-straight.el:
-
-> (straight-use-package
-> '(snitch :type git :host github :repo "mrmekon/snitch-el"))
-
-manual:
-
-> (package-install-file "/path/to/snitch-x.y.z.tar")
-
-
-=== USAGE ===
-
-Enabling snitch is as simple as calling ‘(snitch-init)’.
-Initialization does very little, so this is safe to call in your
-Emacs init without worrying about deferral or negative consequences
-on startup time.
-
-The minimum required initialization is simply:
-
-> (require 'snitch)
-> (snitch-init)
-
-An example initialization using ‘use-package’ might look like so:
-
-> (use-package snitch
-> :config
-> (snitch-init))
-
-snitch then runs in the background, performing its duties according
-to your configuration, and logging in its dedicated buffer.
-
-You may add firewall exception rules manually, as covered in the
-CONFIGURATION section below. Alternatively, you can also build
-filters with a guided UI by switching to the firewall log buffer
-(‘*snitch firewall log*’), highlighting an entry that you wish to
-filter on, and execute ‘M-x snitch-filter-from-log’. This launches
-a popup window that allows you to configure a new filter based on
-one or more fields of the selected log line, and add it to either
-your blacklist or whitelist.
-
-To disable snitch, call ‘(snitch-deinit)’.
-
-
-=== CONFIGURATION ===
-
-Customize snitch with ‘M-x customize-group <RET> snitch’, or
-manually in your Emacs initialization file.
-
-Most users will have five variables that need to be configured
-before use:
-
- - ‘snitch-network-policy’ -- whether to allow or deny network
-connections by default.
-
- - ‘snitch-process-policy’ -- whether to allow or deny subprocesses
-by default.
-
- - ‘snitch-log-policy’ -- which events to log (to see the options,
-run ‘M-x describe-variable <RET> snitch-log-policies’)
-
- - ‘snitch-network-*list’ -- filter rules containing exceptions to
-the default network policy. See FILTER RULES below. Use
-‘-whitelist’ if the default policy is ‘deny’, or ‘-blacklist’ if
-the default policy is ‘allow’
-
- - ‘snitch-process-*list’ -- filter rules containing exceptions to
-the default process policy. See FILTER RULES below. Use
-‘-whitelist’ if the default policy is ‘deny’, or ‘-blacklist’ if
-the default policy is ‘allow’
-
-
-Have a look in ‘snitch-filter.el’ for examples of black/whitelist
-filters, and in ‘snitch-test.el’ for contrived examples of pretty
-much everything.
-
-
-==== COMMON CONFIG: DENY ====
-
-A useful configuration is to deny all external communication by
-default, but allow certain packages to communicate. This example
-demonstrates permitting only the ’elfeed’ package to create network
-connections:
-
-> (use-package snitch
-> :config
-> (setq snitch-network-policy 'deny)
-> (setq snitch-process-policy 'deny)
-> (setq snitch-log-policy '(blocked whitelisted allowed))
-> (add-to-list 'snitch-network-whitelist
-> (cons #'snitch-filter/src-pkg '(elfeed)))
-> (snitch-init))
-
-
-==== COMMON CONFIG: ALLOW + AUDIT ====
-
-Another useful configuration is to allow all accesses, but log them
-to keep an audit trail. This might look like so:
-
-> (use-package snitch
-> :config
-> (setq snitch-network-policy 'allow)
-> (setq snitch-process-policy 'allow)
-> (setq snitch-log-policy '(allowed blocked whitelisted blacklisted))
-> (setq snitch-log-verbose t)
-> (snitch-init))
-
-
-==== FILTER RULES ====
-
-Filter rules, as specified in ‘snitch-(process|network)-*list’
-variables, are specified as cons cells where the car is a filtering
-function, and the cdr is a list of arguments to pass to the
-function in addition to the event object:
-
-> (setq snitch-network-whitelist
-> '(
-> (filter-fn1 . (argQ))
-> (filter-fn2 . (argN argP))
-> ))
-
-Each filter function should have a prototype accepting EVENT as the
-snitch event object in consideration, and ARGS as the list of
-arguments from the cdr of the rules entry:
-
-> (defun filter-fn1 (event &rest args))
-
-EVENT is an eieio object defined by ‘snitch-network-entry’ or
-‘snitch-process-entry’, and inheriting from ‘snitch-source’.
-
-A trivial function which matches if a single string in the event
-object matches a known value might look like so:
-
-> (defun filter-fn1 (event name)
-> (string-equal (oref event proc-name) name))
-
-While a more complex filter function might treat ARGS as an
-associative list of key/value pairs:
-
-> (defun filter-fn2 (event &rest alist)
-> (cl-loop for (aslot . avalue) in alist with accept = t
-> do
-> (let ((evalue (eieio-oref event aslot))
-> (val-type (type-of avalue)))
-> (unless (cond
-> ((eq val-type 'string) (string-equal avalue evalue))
-> (t (eq avalue evalue)))
-> (setq accept nil)))
-> when (null accept)
-> return nil
-> finally return accept))
-
-The return value of a filter function determines whether the filter
-should take effect. t means "take effect" and nil means "do not
-take effect". What that means for the event depends on which list
-the filter rule is in. If the rule is in a whitelist, t means
-allow and nil means block. If it is in a blacklist, t means block
-and nil means allow.
-
-
-==== HOOKS ====
-
-Events are passed to user-provided hook functions, if specified.
-These hooks can subscribe to receive events either immediately on
-arrival, upon a final decision, or both. The hooks can change
-snitch’s final decision.
-
-Hook functions take two arguments, the type and the event object:
-
-> (defun snitch-hook (type event))
-
-TYPE is one of `snitch-hook-types', and corresponds with the names
-of the hook lists. This argument is provided so you can define one
-function which can be used in several hooks.
-
-EVENT is an eieio object defined by ‘snitch-network-entry’ or
-‘snitch-process-entry’, and inheriting from ‘snitch-source’.
-
-Hooks should return t to allow snitch to continue processing as it
-would have, or return nil to reverse snitch’s decision. For hooks
-in ‘snitch-on-event-functions’, returning nil cancels all further
-processing of the event and blocks it immediately. For other hook
-lists, returning nil reverses the action implied by the list name:
-returning nil in a ‘snitch-on-allow-functions’ hook causes the
-event to be blocked, returning nil in a ‘snitch-on-block-functions’
-hook causes it to be allowed.
-
-
-=== PERFORMANCE ===
-
-Performance has not been measured, and should not be assumed to be
-particularly good. Nothing is currently optimized.
-
-Memory usage should not be particularly high, as events are
-ephemeral and only contain a small amount of metadata. The largest
-use of memory is the audit log, which does keep copies of all
-events in the log. This can be controlled via
-‘snitch-log-buffer-max-lines’.
-
-Firewall rules are traversed linearly, and short-circuit (if an
-early rule terminates processing, the subsequent rules will not be
-considered). To optimize for performance, the total number of
-rules should be kept to a minimum, and most likely to match rules
-should be added earlier in the lists.
-
-
-=== TIMER TRACING ===
-
-Since snitch’s usefulness is highly dependent on the ability to
-trace back to the original source that triggered an event, Emacs
-timers pose a bit of a challenge. Timers are used to trigger
-network requests asynchronously, but have the side effect of losing
-the stack trace back to the function or package that initiated it.
-
-To deal with this, snitch optionally supports timer tracing. When
-tracing is enabled, by customizing ‘snitch-trace-timers’ to t,
-snitch hooks into Emacs’s timer functions, and records backtraces
-whenever a timer is registered. If a timer later generates a
-snitch-relevant event, snitch concatenates the regular backtrace
-with the cached timer backtrace to get a full call stack for the
-event.
-
-As an example, here are two snitch log entries when opening RSS
-feeds with the elfeed package, which uses timers for web requests:
-
-With ‘snitch-trace-timers’ set to nil (tracing disabled):
-
-> [2020-12-07 21:32:56] (allowed) -- #s(snitch-network-entry \
-> 1607373176.6757963 \
-> timer-event-handler \
-> /usr/share/emacs/27.1/lisp/emacs-lisp/timer.el \
-> site-lisp \
-> www.smbc-comics.com www.smbc-comics.com 443 nil)
-
-Notice how the source is the function ‘timer-event-handler’ in
-‘timer.el’, part of the special ‘site-lisp’ package? *All*
-timer-originated network calls appear to originate from that
-function, since it is the lowest level Emacs timer dispatch
-function. It is impossible to filter on the true source.
-
-Now with ‘snitch-trace-timers’ set to t (tracing enabled):
-
-> [2020-12-07 21:33:06] (allowed) -- #s(snitch-network-entry \
-> 1607373186.6863618 \
-> elfeed-insert-html
-> /home/trevor/.emacs.d/elpa/elfeed-20200910.239/elfeed-show.el \
-> elfeed \
-> www.smbc-comics.com www.smbc-comics.com 443 nil)
-
-For this event, snitch has successfully traced through the timer to
-find the true source, ‘elfeed-insert-html’ in the ‘elfeed’ package!
-
-Timer tracing comes with a cost: snitch has to generate metadata
-for every single timer event. If your Emacs usage involves a very
-large number of timers, or very high-frequency timers, snitch’s
-tracing could lead to delays and inflated memory usage. Consider
-carefully whether this is a feature you need, and leave it disabled
-if you will not use it, or if you experience any performance issues
-while running snitch.
-
-You can run ‘snitch-monitor-unique-timer-fns’ to get a sense of
-which timers are currently active. After running that function,
-there will be a 60 second delay, followed by printing the names of
-all timers that were active during the minute and the number of
-times they fired.
-
-Similarly, if you run with timer tracing enabled for a while, you
-can use ‘snitch--debug-print-timer-state’ to print a summary of how
-many timers snitch has intercepted, and how many saved backtraces
-are currently active in memory.
-
-
-=== SECURITY ===
-
-snitch provides, effectively, zero security.
-
-If you were to ask your Principal Security Engineer friends, they
-might say that an effective security boundary must be
-"tamper-proof" and provide "complete mediation." snitch does
-neither.
-
-Tamper-proof: none at all. Any other Emacs package can simply
-disable snitch, or modify it to pass malicious traffic undetected.
-
-Complete mediation: no attempt has been made to verify that *all*
-network and subprocess accesses must go through the functions that
-snitch hooks. Given the complexity of Emacs, it is extremely
-unlikely that they do.
-
-However, your Principal Security Engineer friends also like to
-blather on about ’defining your security model’, and a fun game to
-play with them is to define your security model such that none of
-the insecurities are in it. As so:
-
-Security model: includes malicious adversaries
-snitch effectiveness: zero.
-
-Security model: includes no malicious adversaries
-snitch effectiveness: great!
-
-snitch is useful for auditing and blocking unwanted features in an
-otherwise well-behaving ecosystem. It is handy for getting a
-record of exactly what your Emacs is doing, and for fine-tuning
-accesses beyond Emacs’s boundaries a little bit better. It will
-not, however, save you from the bad guys.
-
-
-=== KNOWN LIMITATIONS ===
-
-When snitch blocks events, some Emacs functions that seldom throw
-errors in normal use will throw errors because of snitch. It is
-very likely that blocked connections will cause errors to bubble up
-in strange and unexpected ways, as many package authors have not
-handled these exceptional cases.
-
-snitch does not intercept domain name resolution (DNS).
-
-snitch has a strong preference for identifying user-provided
-packages as the "originating source" of events. Events that you
-may consider as originated in built-in/site-lisp code may be
-attributed to a user package instead, if one is higher up in the
-backtrace. For instance, `helm' may often show up as the source if
-installed, since `helm-M-x-execute-command' is often somewhere in
-the stack.
-
-snitch has not been tested with IPv6.
-
-snitch has not been tested with inbound connections. In theory, it
-can prevent the creation of a listening socket. Once a socket is
-open, though, it would not be able to monitor incoming connections
-to the socket.
-
-
-=== TODO ===
-
- - send notifications in batches?
- - interactive prompts?
- - handle service strings as port numbers
- - ensure the inverted negation rules make sense
- - add blacklist for timer functions
- - publish on MELPA?
- - profit!
-
-
-=== VERSION HISTORY ===
-
-v0.2.0 (2020-12-09)
-
- - first published version
-
-v0.1.0 (before 2020-12-09)
-
- - Initial development and testing
- - Network and process firewall functionality
- - Audit logging
- - Whitelist + blacklist filtering
- - Backtrace processing
- - Timer backtrace expansion
- - User event and logging hooks
- - ert test framework
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; see the file COPYING. If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth
-Floor, Boston, MA 02110-1301, USA.
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-```
#!/bin/bash
-echo "\`\`\`" > README.md
cat snitch.el \
| awk 'f&&f++&&f>2;/^;;; Commentary/{f=1};/^;;; Code/{f=0}' \
| sed \$d \
| sed 's/^;;[ ]\?//' \
- >> README.md
-echo "\`\`\`" >> README.md
+ > README