java-topology/tools/unsandbox-scan.sh
russell@unturf.com db29a08762 undefect. CWE-407 — 92 sites, 42 ecosystems
B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections.
Squash of 94 local commits onto remote master.
2026-03-26 19:48:18 -04:00

193 lines
10 KiB
Bash

#!/usr/bin/env bash
# CWE-407 scanner for unsandbox — runs INSIDE each language sandbox.
# Finds compiler/stdlib source and checks for O(n) membership in graph traversal.
# Usage: submit this as bash to each unsandbox language container.
set -euo pipefail
LANG="${1:-unknown}"
# ── Find source roots ─────────────────────────────────────────────────────────
find_source_roots() {
local roots=""
# Python
for d in /usr/lib/python* /usr/local/lib/python*; do [ -d "$d" ] && roots="$roots $d"; done
# Ruby stdlib + pre-cached gems (.rb source)
for d in /usr/lib/ruby /usr/local/lib/ruby; do [ -d "$d" ] && roots="$roots $d"; done
for d in /var/lib/gems /usr/lib/ruby/gems /usr/local/lib/ruby/gems; do [ -d "$d" ] && roots="$roots $d"; done
# Perl
for d in /usr/lib/perl5 /usr/share/perl5 /usr/local/lib/perl5; do [ -d "$d" ] && roots="$roots $d"; done
# Haskell/GHC
for d in /usr/lib/ghc* /usr/local/lib/ghc* /opt/ghc*; do [ -d "$d" ] && roots="$roots $d"; done
for d in /root/.cabal/packages /root/.cabal/store; do [ -d "$d" ] && roots="$roots $d"; done
# OCaml
for d in /usr/lib/ocaml /usr/local/lib/ocaml; do [ -d "$d" ] && roots="$roots $d"; done
# Erlang (erlang-src puts .erl files under /usr/lib/erlang/lib/*/src/)
for d in /usr/lib/erlang /usr/local/lib/erlang; do [ -d "$d" ] && roots="$roots $d"; done
# Elixir stdlib + pre-cached hex deps (.ex source)
for d in /usr/lib/elixir /usr/local/lib/elixir /opt/elixir*; do [ -d "$d" ] && roots="$roots $d"; done
for d in /root/.mix/deps /usr/share/elixir-deps; do [ -d "$d" ] && roots="$roots $d"; done
# Node/npm
for d in /usr/lib/node_modules /usr/local/lib/node_modules ~/.nvm/versions/node/*/lib/node_modules; do
[ -d "$d" ] && roots="$roots $d"
done
# Rust std (rust-src component) + pre-cached crate registry (.rs source for all crates)
for d in /usr/lib/rust* /root/.rustup/toolchains/*/lib/rustlib/src; do [ -d "$d" ] && roots="$roots $d"; done
for d in /root/.cargo/registry/src; do [ -d "$d" ] && roots="$roots $d"; done
# Go stdlib + pre-cached module source
for d in /usr/local/go/src /usr/lib/go/src; do [ -d "$d" ] && roots="$roots $d"; done
for d in /root/go/pkg/mod /root/.cache/go/pkg/mod; do [ -d "$d" ] && roots="$roots $d"; done
# Lua
for d in /usr/share/lua* /usr/lib/lua*; do [ -d "$d" ] && roots="$roots $d"; done
# PHP
for d in /usr/share/php* /usr/lib/php*; do [ -d "$d" ] && roots="$roots $d"; done
# R
for d in /usr/lib/R /usr/local/lib/R; do [ -d "$d" ] && roots="$roots $d"; done
# Julia (binary tarball stdlib + pre-cached packages at ~/.julia/packages/)
for d in /opt/julia-*/share/julia /usr/share/julia /usr/local/share/julia; do [ -d "$d" ] && roots="$roots $d"; done
for d in /root/.julia/packages; do [ -d "$d" ] && roots="$roots $d"; done
# Kotlin (SDKMAN + extracted stdlib sources)
for d in /usr/share/kotlin /root/.sdkman/candidates/kotlin/current /opt/kotlin*; do [ -d "$d" ] && roots="$roots $d"; done
# Scala
for d in /usr/share/scala /opt/scala*; do [ -d "$d" ] && roots="$roots $d"; done
# Groovy (apt + extracted sources JAR)
for d in /usr/share/groovy /usr/share/groovy/src /opt/groovy*; do [ -d "$d" ] && roots="$roots $d"; done
# Clojure (linux-install + extracted sources JAR)
for d in /usr/share/clojure /usr/share/clojure/src /usr/lib/clojure /usr/local/lib/clojure; do [ -d "$d" ] && roots="$roots $d"; done
# Common Lisp (SBCL + Quicklisp packages)
for d in /usr/lib/sbcl /root/quicklisp/dists; do [ -d "$d" ] && roots="$roots $d"; done
# Nim stdlib + nimble packages (.nim source)
for d in /opt/Nim/lib /usr/lib/nim; do [ -d "$d" ] && roots="$roots $d"; done
for d in /root/.nimble/pkgs /root/.nimble/pkgs2; do [ -d "$d" ] && roots="$roots $d"; done
# Crystal (install script puts stdlib at /usr/share/crystal/src/ or /usr/lib/crystal/)
for d in /usr/share/crystal /usr/lib/crystal; do [ -d "$d" ] && roots="$roots $d"; done
# D (LDC Phobos stdlib at /usr/include/dlang/)
for d in /usr/include/dlang /usr/share/dmd; do [ -d "$d" ] && roots="$roots $d"; done
# Zig (binary tarball stdlib at /opt/zig-linux-x86_64-VERSION/lib/)
for d in /opt/zig-linux-x86_64-*/lib /usr/lib/zig; do [ -d "$d" ] && roots="$roots $d"; done
# Dart (SDK stdlib at /usr/lib/dart/lib/)
for d in /usr/lib/dart/lib /usr/lib/dart; do [ -d "$d" ] && roots="$roots $d"; done
# V (git clone + vlib)
for d in /opt/vlang/vlib; do [ -d "$d" ] && roots="$roots $d"; done
# Raku
for d in /usr/share/perl6 /usr/lib/raku /root/.raku; do [ -d "$d" ] && roots="$roots $d"; done
# Tcl
for d in /usr/lib/tcl8.6 /usr/share/tcltk /usr/lib/tcltk; do [ -d "$d" ] && roots="$roots $d"; done
# Prolog
for d in /usr/lib/swi-prolog/library /usr/share/swi-prolog; do [ -d "$d" ] && roots="$roots $d"; done
# Forth
for d in /usr/share/gforth; do [ -d "$d" ] && roots="$roots $d"; done
# PowerShell
for d in /opt/microsoft/powershell; do [ -d "$d" ] && roots="$roots $d"; done
# F# extracted sources
for d in /usr/share/fsharp/src; do [ -d "$d" ] && roots="$roots $d"; done
# C# extracted NuGet sources
for d in /usr/share/dotnet-src; do [ -d "$d" ] && roots="$roots $d"; done
# Deno std cache
for d in /root/.deno/deps; do [ -d "$d" ] && roots="$roots $d"; done
# C/C++ headers
for d in /usr/include; do [ -d "$d" ] && roots="$roots $d"; done
echo $roots
}
# ── Graph traversal keywords ───────────────────────────────────────────────────
GRAPH_KW="tarjan|scc|strongly.connected|dfs|topolog|topo.sort|cycle|closure|reachab|postorder|dominator|liveness|digraph"
# ── Membership patterns by extension ──────────────────────────────────────────
check_file() {
local f="$1"
local ext="${f##*.}"
local mem=""
case "$ext" in
py) mem='[^a-z]in (stack|path|visited|seen|queue|nodes|list)\b|\.append\b.*in path' ;;
rb) mem='\.include\?|\.member\?|Array.*include' ;;
pm|pl) mem='grep\s*{.*eq\b|List::Util.*first|foreach.*eq\b|member\([A-Z]' ;;
hs) mem='`elem`|\belem\b' ;;
ml|mli) mem='List\.mem\b|List\.exists\b' ;;
erl) mem='lists:member\b|lists:any\b' ;;
ex|exs) mem='Enum\.member\?|List\.member\?|[^a-z]in\s+(stack|path|list|visited)' ;;
java) mem='\.contains\(|ArrayList.*contains|List.*contains|Deque.*contains' ;;
kt|kts) mem='\.contains\(|[^a-z]in\s+(stack|path|list|visited)' ;;
scala) mem='\.contains\(|List.*contains|Seq.*contains|ArrayBuffer.*contains' ;;
cpp|cc|cxx|c|h|hpp) mem='std::find[(]|[.]count[(]|find.*begin.*end|linear_search' ;;
rs) mem='\.contains\(&|iter\(\)\.any\(|iter\(\)\.position\(' ;;
go) mem='slices\.Contains|for.*range.*==\s*\w' ;;
js|ts|mjs) mem='\.includes\(|\.indexOf\(|\.find\(' ;;
lua) mem='table\.search\|linear.*find' ;;
php) mem='in_array\(|array_search\(' ;;
r|R) mem='%in%.*c(' ;;
# ── Work Item 2: extended language patterns ───────────────────────────────
jl) mem='[^a-z]in\(|findfirst\b|indexin\b|\.in\b' ;;
nim) mem='[^a-z]in\s+\w|\.contains\b|\.find\b' ;;
cr) mem='\.includes\?\b|\.index\b|Array.*includes\?' ;;
d) mem='canFind\b|std\.algorithm\.find\b|\.canFind\b' ;;
zig) mem='for\s*\([^)]+\)\s*\|[^|]+\|[^{]*==\s*\w' ;;
dart) mem='\.contains\(|\.indexOf\(' ;;
fs) mem='List\.contains\b|List\.exists\b|Seq\.contains\b|SEARCH\b' ;;
cs) mem='\.Contains\(|IEnumerable.*Contains|List<.*>.*Contains' ;;
groovy) mem='\.contains\(|\.indexOf\(' ;;
clj) mem='(some\s+#{|member\b|\(\.contains\b' ;;
lisp) mem='(member\s|(find\s|(position\s' ;;
scm) mem='(member\s|(memq\s|(memv\s' ;;
raku|pm6) mem='\(elem\)\b|@.*\.first\b|\$.*~~\s*@' ;;
tcl) mem='lsearch\s|-exact\b' ;;
ps1) mem='-contains\b|\.IndexOf\(' ;;
v) mem='\.contains\(|\.index\(' ;;
cob) mem='SEARCH\s|PERFORM\s.*VARYING\b' ;;
f90|f|for) mem='ANY\s*\(|COUNT\s*\(|FINDLOC\s*(' ;;
*) return ;;
esac
# Check if file has graph traversal context
local graph_lines
graph_lines=$(grep -inE "$GRAPH_KW" "$f" 2>/dev/null | cut -d: -f1 | head -5 || true)
[ -z "$graph_lines" ] && return
while IFS= read -r lineno; do
local s=$(( lineno > 20 ? lineno - 20 : 1 ))
local e=$(( lineno + 20 ))
local hit
hit=$(sed -n "${s},${e}p" "$f" 2>/dev/null | grep -iE "$mem" 2>/dev/null | head -1 | sed 's/^\s*//' || true)
if [ -n "$hit" ]; then
echo "CANDIDATE ${f}:${lineno} ${hit}"
fi
done <<< "$graph_lines"
}
export -f check_file
export GRAPH_KW
# ── Main scan ─────────────────────────────────────────────────────────────────
echo "# CWE-407 scan — lang=${LANG} host=$(hostname) date=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "#"
ROOTS=$(find_source_roots)
if [ -z "$ROOTS" ]; then
echo "# no source roots found"
exit 0
fi
echo "# roots: $ROOTS"
echo "#"
find $ROOTS -type f \( \
-name "*.py" -o -name "*.rb" -o -name "*.pm" -o -name "*.pl" \
-o -name "*.hs" -o -name "*.ml" -o -name "*.mli" \
-o -name "*.erl" -o -name "*.ex" -o -name "*.exs" \
-o -name "*.java" -o -name "*.kt" -o -name "*.kts" -o -name "*.scala" \
-o -name "*.cpp" -o -name "*.cc" -o -name "*.c" -o -name "*.h" -o -name "*.hpp" \
-o -name "*.rs" -o -name "*.go" -o -name "*.js" -o -name "*.ts" -o -name "*.mjs" \
-o -name "*.lua" -o -name "*.php" -o -name "*.r" -o -name "*.R" \
-o -name "*.jl" -o -name "*.nim" -o -name "*.cr" \
-o -name "*.d" -o -name "*.zig" -o -name "*.dart" \
-o -name "*.fs" -o -name "*.cs" -o -name "*.groovy" \
-o -name "*.clj" -o -name "*.lisp" -o -name "*.scm" \
-o -name "*.raku" -o -name "*.pm6" -o -name "*.tcl" \
-o -name "*.ps1" -o -name "*.v" -o -name "*.cob" \
-o -name "*.f90" -o -name "*.f" -o -name "*.for" \
\) -not -path "*/.git/*" -not -path "*/test*" -not -path "*/doc*" \
-not -path "*/bench*" -not -path "*/example*" 2>/dev/null \
| sort | while IFS= read -r f; do
check_file "$f"
done
echo "#"
echo "# scan complete"