AUTONOMY DIRECTORATE

๐Ÿ  Main

๐Ÿงช Interactive Apps

๐Ÿ“ฐ News

๐Ÿ›ก๏ธ PQ Crypta Proxy

๐Ÿ‘ค Account

โŸจ QUANTUM ERROR PORTAL โŸฉ

Navigate the Error Dimensions

PQ Crypta Logo

Script Viewer

Red Team Suite › 21_supply_chain.sh

21_supply_chain.sh 132 lines
1 #!/bin/bash
2 # Supply Chain & Dependency Security (black-box perspective)
3 # Checks for exposed dependency manifests, version disclosure, build artifacts
4 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5 source "$SCRIPT_DIR/../config.sh"
6 OUT="$OUT/supply_chain"
7 mkdir -p "$OUT"
8
9 echo '=== SUPPLY CHAIN & DEPENDENCY SECURITY ===' | tee "$OUT/summary.txt"
10
11 BROWSER="$BROWSER_UA"
12
13 # โ”€โ”€ 1. Exposed Dependency Manifests โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
14 echo '' | tee -a "$OUT/summary.txt"
15 echo '--- Exposed Dependency Files ---' | tee -a "$OUT/summary.txt"
16 MANIFESTS=(
17 "/package.json" "/package-lock.json" "/yarn.lock" "/pnpm-lock.yaml"
18 "/composer.json" "/composer.lock"
19 "/requirements.txt" "/Pipfile" "/Pipfile.lock" "/poetry.lock"
20 "/Gemfile" "/Gemfile.lock"
21 "/go.mod" "/go.sum"
22 "/Cargo.toml" "/Cargo.lock"
23 "/pom.xml" "/build.gradle" "/build.gradle.kts"
24 "/vendor/" "/node_modules/" "/bower_components/"
25 "/.npmrc" "/.yarnrc" "/pyproject.toml"
26 )
27 for path in "${MANIFESTS[@]}"; do
28 resp=$(curl -sk -o "$OUT/manifest_check.tmp" -w '%{http_code}' --max-time 8 -A "$BROWSER" "$TARGET$path")
29 if [ "$resp" = "200" ]; then
30 size=$(wc -c < "$OUT/manifest_check.tmp")
31 echo " [!!!] EXPOSED: $path ($size bytes)" | tee -a "$OUT/summary.txt"
32 head -3 "$OUT/manifest_check.tmp" | tee -a "$OUT/summary.txt"
33 else
34 echo " [PASS] $path โ†’ $resp"
35 fi
36 done
37
38 # โ”€โ”€ 2. Version Disclosure via Headers & Responses โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
39 echo '' | tee -a "$OUT/summary.txt"
40 echo '--- Version Disclosure in Headers ---' | tee -a "$OUT/summary.txt"
41 headers=$(curl -sk -I --max-time 10 -A "$BROWSER" "$TARGET/")
42 echo "$headers" | grep -iE 'Server:|X-Powered-By:|X-Generator:|X-Runtime:|X-Version:|X-App-Version:' | tee -a "$OUT/summary.txt"
43 echo "$headers" | grep -iE 'Server:|X-Powered-By:' | grep -qi '\d\.\d' && \
44 echo " [WARN] Version number in header" || echo " [PASS] No version in Server/X-Powered-By" | tee -a "$OUT/summary.txt"
45
46 echo '' | tee -a "$OUT/summary.txt"
47 echo '--- Version Disclosure in API Responses ---' | tee -a "$OUT/summary.txt"
48 resp_body=$(curl -sk --max-time 8 -A "$BROWSER" "$API_TARGET/health")
49 echo "$resp_body" | python3 -c "
50 import sys, json
51 try:
52 d = json.load(sys.stdin)
53 # Only flag fields that reveal internal implementation details (commit hashes, build IDs)
54 # 'version' alone is expected in any API health endpoint and is not a risk
55 keys = ['build','commit','sha','tag','release','runtime','framework','dependencies']
56 found = {k: d.get(k) for k in keys if k in d}
57 if found:
58 print(' [WARN] Build/commit fields in /health response:', found)
59 else:
60 print(' [PASS] No sensitive build/commit fields in /health')
61 except:
62 print(' [INFO] /health not JSON or unavailable')
63 " | tee -a "$OUT/summary.txt"
64
65 # โ”€โ”€ 3. Source Map Exposure (JS source leakage) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
66 echo '' | tee -a "$OUT/summary.txt"
67 echo '--- JavaScript Source Map Exposure ---' | tee -a "$OUT/summary.txt"
68 # Get main page, extract JS files, check for .map URLs
69 js_files=$(curl -sk --max-time 10 -A "$BROWSER" "$TARGET/" | grep -oP 'src="[^"]*\.js[^"]*"' | sed 's/src="//;s/"//' | head -10)
70 found_map=0
71 for jsf in $js_files; do
72 [[ "$jsf" == http* ]] || jsf="$TARGET$jsf"
73 # Strip query string before constructing map URL (versioned URLs like foo.js?v=123 are not files)
74 jsf_base="${jsf%%\?*}"
75 [[ "$jsf_base" == *.js ]] || continue
76 mapurl="${jsf_base}.map"
77 resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 -A "$BROWSER" "$mapurl")
78 if [ "$resp" = "200" ]; then
79 echo " [!!!] Source map EXPOSED: $mapurl" | tee -a "$OUT/summary.txt"
80 found_map=1
81 fi
82 done
83 [ "$found_map" -eq 0 ] && echo " [PASS] No exposed source maps found" | tee -a "$OUT/summary.txt"
84
85 # โ”€โ”€ 4. Build Artifact & CI/CD Exposure โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
86 echo '' | tee -a "$OUT/summary.txt"
87 echo '--- Build Artifacts & CI/CD Files ---' | tee -a "$OUT/summary.txt"
88 BUILD_PATHS=(
89 "/.github/workflows/" "/.github/actions/" "/.gitlab-ci.yml"
90 "/Dockerfile" "/docker-compose.yml" "/docker-compose.prod.yml"
91 "/.dockerignore" "/Makefile" "/Taskfile.yml"
92 "/deploy.sh" "/release.sh" "/ci.sh"
93 "/.travis.yml" "/Jenkinsfile" "/bitbucket-pipelines.yml"
94 "/dist/" "/build/" "/target/"
95 "/.env.example" "/.env.template"
96 )
97 for path in "${BUILD_PATHS[@]}"; do
98 resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 -A "$BROWSER" "$TARGET$path")
99 [ "$resp" = "200" ] && echo " [!!!] EXPOSED: $path ($resp)" || echo " [PASS] $path โ†’ $resp" | tee -a "$OUT/summary.txt"
100 done
101
102 # โ”€โ”€ 5. Dependency Confusion Attack Surface โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
103 echo '' | tee -a "$OUT/summary.txt"
104 echo '--- Internal Package Names (dependency confusion check) ---' | tee -a "$OUT/summary.txt"
105 # Pull package names from any exposed manifests and check if they exist on public npm
106 pkg_json=$(curl -sk --max-time 8 -A "$BROWSER" "$TARGET/package.json" 2>/dev/null)
107 if echo "$pkg_json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
108 echo "$pkg_json" | python3 -c "
109 import sys, json
110 d = json.load(sys.stdin)
111 deps = {**d.get('dependencies',{}), **d.get('devDependencies',{})}
112 internal = [k for k in deps if not k.startswith('@') and '-internal' in k or 'private' in k or k.startswith('pq')]
113 print(' Internal/private package names found:', internal if internal else 'none')
114 " | tee -a "$OUT/summary.txt"
115 else
116 echo " [PASS] package.json not accessible โ€” dependency confusion surface not exposed" | tee -a "$OUT/summary.txt"
117 fi
118
119 # โ”€โ”€ 6. Subresource Integrity (SRI) on External Scripts โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
120 echo '' | tee -a "$OUT/summary.txt"
121 echo '--- Subresource Integrity (SRI) Check ---' | tee -a "$OUT/summary.txt"
122 page=$(curl -sk --max-time 10 -A "$BROWSER" "$TARGET/")
123 external_no_sri=$(echo "$page" | grep -oP '<script[^>]*src="https?://(?!'"$(echo $TARGET | sed 's|https://||')"')[^"]*"[^>]*>' | grep -v 'integrity=')
124 if [ -n "$external_no_sri" ]; then
125 echo " [WARN] External scripts WITHOUT SRI:" | tee -a "$OUT/summary.txt"
126 echo "$external_no_sri" | head -5 | tee -a "$OUT/summary.txt"
127 else
128 echo " [PASS] All external scripts have SRI or are self-hosted" | tee -a "$OUT/summary.txt"
129 fi
130
131 cat "$OUT/summary.txt"
132