B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
91 lines
3.8 KiB
Bash
Executable file
91 lines
3.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# CWE-407 scanner: O(n) linear membership checks in graph traversal code.
|
|
#
|
|
# Pattern: Any call to a linear-scan membership check (.contains, "in list",
|
|
# std::find, etc.) inside a function whose name suggests graph traversal
|
|
# (tarjan, scc, dfs, topo, cycle, closure, reachab*).
|
|
#
|
|
# Usage:
|
|
# scan-cwe407.sh <repo-root> [file-glob]
|
|
#
|
|
# Output: TSV — file:line | function-context | snippet | verdict
|
|
#
|
|
# Supports: .java .py .kt .scala .cpp .h .c .go .rs .rb .js .ts .cs .swift
|
|
set -euo pipefail
|
|
|
|
ROOT="${1:-.}"
|
|
GLOB="${2:-}"
|
|
|
|
# ── Membership check patterns per language ─────────────────────────────────────
|
|
JAVA_MEM='\.contains\s*\(|List\.contains|ArrayList\.contains|LinkedList\.contains|Deque\.contains|ArrayDeque\.contains'
|
|
PY_MEM='[^a-z]in (stack|path|visited|seen|queue|nodes)|path\.append|\.contains\('
|
|
KT_MEM='\.contains\s*\(|in\s+(?:stack|path|visited|list|nodes)\b'
|
|
SCALA_MEM='\.contains\s*\(|\.exists\s*\{|List\.contains|Seq\.contains|ArrayBuffer\.contains'
|
|
CPP_MEM='std::find[(]|[.]count[(]|linear_search|find.*begin.*end'
|
|
GO_MEM='slices\.Contains|contains\s*\(.*\[\]|for.*range.*==\s*\w'
|
|
RS_MEM='\.contains\s*\(&|iter\(\)\.any\s*\(|\.position\s*\('
|
|
|
|
# ── Graph/traversal function name patterns ─────────────────────────────────────
|
|
GRAPH_FN='tarjan|scc|strongly.connected|dfs|bfs|topolog|topo.sort|cycle|closure|reachab|postorder|preorder|dominator|liveness|worklist|scfg|cfg'
|
|
|
|
# ── Scan a single file ─────────────────────────────────────────────────────────
|
|
scan_file() {
|
|
local f="$1"
|
|
local ext="${f##*.}"
|
|
local mem_pat=""
|
|
|
|
case "$ext" in
|
|
java) mem_pat="$JAVA_MEM" ;;
|
|
py) mem_pat="$PY_MEM" ;;
|
|
kt|kts) mem_pat="$KT_MEM" ;;
|
|
scala) mem_pat="$SCALA_MEM" ;;
|
|
cpp|cc|cxx|h|hpp|c) mem_pat="$CPP_MEM" ;;
|
|
go) mem_pat="$GO_MEM" ;;
|
|
rs) mem_pat="$RS_MEM" ;;
|
|
*) return ;;
|
|
esac
|
|
|
|
# Two-pass: find graph-related functions first, then check for membership
|
|
# within ±20 lines of any match.
|
|
local graph_lines
|
|
graph_lines=$(grep -inE "$GRAPH_FN" "$f" 2>/dev/null | cut -d: -f1 || true)
|
|
[ -z "$graph_lines" ] && return
|
|
|
|
while IFS= read -r lineno; do
|
|
local start=$(( lineno - 20 ))
|
|
local end=$(( lineno + 20 ))
|
|
[ $start -lt 1 ] && start=1
|
|
|
|
# Check if any membership pattern appears in the window
|
|
local hit
|
|
hit=$(sed -n "${start},${end}p" "$f" 2>/dev/null \
|
|
| grep -iE "$mem_pat" 2>/dev/null || true)
|
|
if [ -n "$hit" ]; then
|
|
echo -e "CANDIDATE\t${f}:${lineno}\t$(echo "$hit" | head -1 | sed 's/^\s*//')"
|
|
fi
|
|
done <<< "$graph_lines"
|
|
}
|
|
|
|
export -f scan_file
|
|
export JAVA_MEM PY_MEM KT_MEM SCALA_MEM CPP_MEM GO_MEM RS_MEM GRAPH_FN
|
|
|
|
# ── Find files ─────────────────────────────────────────────────────────────────
|
|
echo "# CWE-407 scan: $ROOT"
|
|
echo "# $(date -u)"
|
|
echo "# file:line | snippet"
|
|
echo "#"
|
|
|
|
if [ -n "$GLOB" ]; then
|
|
find "$ROOT" -name "$GLOB" -type f
|
|
else
|
|
find "$ROOT" -type f \( \
|
|
-name "*.java" -o -name "*.py" -o -name "*.kt" -o -name "*.scala" \
|
|
-o -name "*.cpp" -o -name "*.cc" -o -name "*.c" -o -name "*.h" \
|
|
-o -name "*.go" -o -name "*.rs" \
|
|
\) -not -path "*/.git/*" -not -path "*/build/*" -not -path "*/target/*"
|
|
fi | sort | while IFS= read -r f; do
|
|
scan_file "$f"
|
|
done
|
|
|
|
echo "#"
|
|
echo "# Scan complete."
|