#!/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"