From 3fa3db22649be78b3b4f2f425323bc7e67afdefd Mon Sep 17 00:00:00 2001 From: Brad Gessler Date: Mon, 22 Jun 2026 11:02:44 -0700 Subject: [PATCH] test: mechanical security floor (no code-exec / shell-out / unsafe deser / atom creation) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The server runs untrusted input — a hostile client sends arbitrary frames and the CLI runs client-controlled argv. A static test asserts lib/ never contains the primitives that would let that punch through: Code.eval/EEx, System.cmd/:os.cmd/ Port.open, binary_to_term, or String.to_atom/binary_to_atom/list_to_atom (atom-table exhaustion). Passes today; fails the build if one ever creeps in. --- test/security_test.exs | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/security_test.exs diff --git a/test/security_test.exs b/test/security_test.exs new file mode 100644 index 0000000..0babc70 --- /dev/null +++ b/test/security_test.exs @@ -0,0 +1,45 @@ +defmodule Terminalwire.SecurityTest do + use ExUnit.Case, async: true + + # A mechanical security floor. This server runs untrusted input: a hostile client + # can send arbitrary frames, and the CLI it serves runs argv the client controls. + # None of the primitives below — code execution, shell-out, unsafe deserialization, + # or untrusted-string→atom conversion (atom-table exhaustion) — may be even one grep + # away in the library. If you ever need one deliberately, you have to delete it from + # this list on purpose, which is the point: it can't creep in silently. + @forbidden [ + # arbitrary code execution + "Code.eval", + "eval_string", + "eval_quoted", + "EEx.", + # shell / OS command execution + "System.cmd", + ":os.cmd", + "Port.open", + "open_port", + # unsafe deserialization (RCE + atom creation from bytes) + "binary_to_term", + # atom-table exhaustion from untrusted strings (note: the *_to_existing_atom + # variants are bounded and intentionally NOT forbidden) + "String.to_atom", + "binary_to_atom", + "list_to_atom" + ] + + test "the library never reaches for code-exec, shell-out, unsafe deserialization, or atom creation" do + files = Path.wildcard("lib/**/*.ex") + assert files != [], "expected to find library sources under lib/" + + offenders = + for path <- files, + source = File.read!(path), + token <- @forbidden, + String.contains?(source, token), + do: " #{path}: #{token}" + + assert offenders == [], + "Forbidden security-sensitive primitives found in lib/:\n" <> + Enum.join(offenders, "\n") + end +end