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 › 26_cicd_pipeline.sh

26_cicd_pipeline.sh 237 lines
1 #!/bin/bash
2 # 26 - CI/CD Pipeline Active Security
3 # Tests: exposed pipeline configs, build artifact leakage, webhook endpoints,
4 # secret scanning in pipeline paths, registry access, branch protection,
5 # OIDC token theft vectors, dependency substitution surface
6
7 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8 source "$SCRIPT_DIR/../config.sh"
9
10 OUT_FILE="$OUT/26_cicd_pipeline.txt"
11 echo "=== 26. CI/CD Pipeline Security ===" | tee "$OUT_FILE"
12 echo "Target: $TARGET" | tee -a "$OUT_FILE"
13 echo "" | tee -a "$OUT_FILE"
14
15 HOST=$(echo "$TARGET" | sed 's|https\?://||' | cut -d/ -f1)
16
17 do_test() {
18 local label="$1"; shift
19 local code
20 code=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 "$@")
21 printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE"
22 }
23
24 do_body() {
25 local label="$1"; local pattern="$2"; shift 2
26 local body code
27 body=$(curl -sk --max-time 8 "$@")
28 code=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 "$@")
29 printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE"
30 if [ -n "$pattern" ] && echo "$body" | grep -qiE "$pattern"; then
31 echo " [WARN] Sensitive pattern found: $(echo "$body" | grep -ioE "$pattern" | head -3 | tr '\n' ' ')" | tee -a "$OUT_FILE"
32 fi
33 }
34
35 # โ”€โ”€ 1. CI/CD Config File Exposure โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
36 echo "--- CI/CD Config File Exposure ---" | tee -a "$OUT_FILE"
37 CI_PATHS=(
38 "/.github/workflows/deploy.yml"
39 "/.github/workflows/ci.yml"
40 "/.github/workflows/release.yml"
41 "/.github/workflows/main.yml"
42 "/.github/workflows/build.yml"
43 "/.gitlab-ci.yml"
44 "/Jenkinsfile"
45 "/.circleci/config.yml"
46 "/.travis.yml"
47 "/azure-pipelines.yml"
48 "/bitbucket-pipelines.yml"
49 "/.drone.yml"
50 "/Dockerfile"
51 "/docker-compose.yml"
52 "/docker-compose.prod.yml"
53 "/docker-compose.override.yml"
54 "/.dockerignore"
55 "/k8s/"
56 "/kubernetes/"
57 "/helm/"
58 "/charts/"
59 "/deploy/"
60 "/deployment/"
61 "/infrastructure/"
62 "/terraform/"
63 "/ansible/"
64 "/.env.ci"
65 "/.env.build"
66 "/.env.pipeline"
67 "/buildspec.yml"
68 "/cloudbuild.yaml"
69 "/.github/CODEOWNERS"
70 "/.github/dependabot.yml"
71 "/Makefile"
72 "/Taskfile.yml"
73 "/scripts/deploy.sh"
74 "/scripts/build.sh"
75 "/scripts/release.sh"
76 )
77 for CI_PATH in "${CI_PATHS[@]}"; do
78 code=$(curl -skL -o /dev/null -w "%{http_code}" --max-time 5 -A "$BROWSER_UA" "$TARGET$CI_PATH")
79 if [ "$code" = "200" ] || [ "$code" = "301" ] || [ "$code" = "302" ]; then
80 printf ' [%s] %s โ† EXPOSED\n' "$code" "$CI_PATH" | tee -a "$OUT_FILE"
81 fi
82 done
83 echo " (CI config scan complete)" | tee -a "$OUT_FILE"
84 echo "" | tee -a "$OUT_FILE"
85
86 # โ”€โ”€ 2. Build Artifact & Output Exposure โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
87 echo "--- Build Artifact Exposure ---" | tee -a "$OUT_FILE"
88 ARTIFACT_PATHS=(
89 "/dist/"
90 "/build/"
91 "/out/"
92 "/.next/"
93 "/.nuxt/"
94 "/target/"
95 "/release/"
96 "/artifacts/"
97 "/coverage/"
98 "/lcov-report/"
99 "/test-results/"
100 "/test-output/"
101 "/.nyc_output/"
102 "/junit.xml"
103 "/coverage.xml"
104 "/test-report.xml"
105 "/.cache/"
106 "/node_modules/"
107 "/vendor/"
108 "/__pycache__/"
109 "/.gradle/"
110 "/.m2/"
111 )
112 for ART in "${ARTIFACT_PATHS[@]}"; do
113 code=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 5 -A "$BROWSER_UA" "$TARGET$ART")
114 if [ "$code" = "200" ] || [ "$code" = "403" ]; then
115 printf ' [%s] %s\n' "$code" "$ART" | tee -a "$OUT_FILE"
116 fi
117 done
118 echo " (artifact scan complete โ€” 403=blocked-but-exists, 200=exposed)" | tee -a "$OUT_FILE"
119 echo "" | tee -a "$OUT_FILE"
120
121 # โ”€โ”€ 3. Webhook Endpoints โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
122 echo "--- Webhook / Pipeline Trigger Endpoints ---" | tee -a "$OUT_FILE"
123 WEBHOOK_PATHS=(
124 "/webhook"
125 "/webhooks"
126 "/webhook/github"
127 "/webhook/gitlab"
128 "/webhook/bitbucket"
129 "/webhook/deploy"
130 "/hooks/deploy"
131 "/hooks/push"
132 "/trigger"
133 "/trigger/build"
134 "/api/webhook"
135 "/api/webhooks"
136 "/ci/trigger"
137 "/deploy/trigger"
138 "/build/trigger"
139 "/pipeline/trigger"
140 )
141 for WH in "${WEBHOOK_PATHS[@]}"; do
142 do_test "Webhook $WH (GET)" -A "$BROWSER_UA" "$TARGET$WH"
143 done
144 echo "" | tee -a "$OUT_FILE"
145
146 # Forge a fake GitHub webhook delivery
147 echo "--- Webhook Forgery (unsigned GitHub event) ---" | tee -a "$OUT_FILE"
148 FAKE_PAYLOAD="{\"ref\":\"refs/heads/main\",\"repository\":{\"full_name\":\"${PROJECT_NAME}/test\"},\"pusher\":{\"name\":\"attacker\"}}"
149 for WH_PATH in /webhook /webhook/github /api/webhook /hooks/push; do
150 code=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 5 \
151 -X POST -A "$BROWSER_UA" \
152 -H 'Content-Type: application/json' \
153 -H 'X-GitHub-Event: push' \
154 -H 'X-GitHub-Delivery: aaaaaaaa-0000-0000-0000-000000000000' \
155 -d "$FAKE_PAYLOAD" "$TARGET$WH_PATH")
156 printf '[%s] Fake GitHub push event โ†’ %s\n' "$code" "$WH_PATH" | tee -a "$OUT_FILE"
157 done
158 echo "" | tee -a "$OUT_FILE"
159
160 # โ”€โ”€ 4. GitHub/GitLab API โ€” Branch Protection & Secret Exposure โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
161 echo "--- GitHub API โ€” Repo & Branch Protection Check ---" | tee -a "$OUT_FILE"
162 GH_REPO="${GITHUB_REPO}"
163 GH_API="https://api.github.com"
164 do_body "Repo visibility & settings" '"private"\|"visibility"' \
165 -H 'Accept: application/vnd.github+json' \
166 -A "pqc-pentest/1.0" "$GH_API/repos/$GH_REPO"
167 do_body "Branch protection (main)" '"required_status_checks"\|"required_pull_request_reviews"' \
168 -H 'Accept: application/vnd.github+json' \
169 -A "pqc-pentest/1.0" "$GH_API/repos/$GH_REPO/branches/main/protection"
170 do_body "Public repo list (unauthenticated)" '"full_name"' \
171 -H 'Accept: application/vnd.github+json' \
172 -A "pqc-pentest/1.0" "$GH_API/orgs/PQCrypta/repos?type=public&per_page=5"
173 do_body "GitHub Actions secrets (requires auth)" '"secrets"' \
174 -H 'Accept: application/vnd.github+json' \
175 -A "pqc-pentest/1.0" "$GH_API/repos/$GH_REPO/actions/secrets"
176 do_body "GitHub Actions workflows" '"total_count"' \
177 -H 'Accept: application/vnd.github+json' \
178 -A "pqc-pentest/1.0" "$GH_API/repos/$GH_REPO/actions/workflows"
179 echo "" | tee -a "$OUT_FILE"
180
181 # โ”€โ”€ 5. OIDC Token Theft Vectors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
182 echo "--- OIDC / Federation Token Theft Vectors ---" | tee -a "$OUT_FILE"
183 # GitHub Actions OIDC endpoint (only accessible from runner)
184 do_test "GitHub OIDC token endpoint (external probe)" \
185 "https://pipelines.actions.githubusercontent.com/serviceHosts/token"
186 do_test "GCP Workload Identity Federation probe" \
187 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test@pqcrypta.iam.gserviceaccount.com:generateIdToken"
188 # OIDC token endpoint via potential SSRF in app
189 do_test "OIDC via SSRF ?url=GitHub-Actions-OIDC" \
190 -A "$BROWSER_UA" "$TARGET/?url=$(python3 -c "import urllib.parse; print(urllib.parse.quote('https://pipelines.actions.githubusercontent.com/serviceHosts/token'))")"
191 echo "" | tee -a "$OUT_FILE"
192
193 # โ”€โ”€ 6. Dependency Substitution Attack Surface โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
194 echo "--- Dependency Substitution / Confusion Attack Surface ---" | tee -a "$OUT_FILE"
195 # Check if internal package names are published on public registries
196 # (namespace squatting / dependency confusion)
197 # Package names to probe for dependency confusion โ€” derived from PROJECT_NAME
198 INTERNAL_PKG_PATTERNS=("${PROJECT_NAME}" "${PROJECT_NAME}-proxy" "${PROJECT_NAME}-api" "${PROJECT_NAME}-collector" "pqc-proxy")
199 for PKG in "${INTERNAL_PKG_PATTERNS[@]}"; do
200 NPM_CODE=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 "https://registry.npmjs.org/$PKG")
201 printf ' npm registry: %s โ†’ [%s]\n' "$PKG" "$NPM_CODE" | tee -a "$OUT_FILE"
202 done
203 for PKG in "${INTERNAL_PKG_PATTERNS[@]}"; do
204 CARGO_CODE=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 "https://crates.io/api/v1/crates/$PKG")
205 printf ' crates.io: %s โ†’ [%s]\n' "$PKG" "$CARGO_CODE" | tee -a "$OUT_FILE"
206 done
207 echo "" | tee -a "$OUT_FILE"
208
209 # โ”€โ”€ 7. Secret Patterns in Pipeline-Related Endpoints โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
210 echo "--- Secret Pattern Scan in Accessible Endpoints ---" | tee -a "$OUT_FILE"
211 SECRET_PATTERN='(AKIA[A-Z0-9]{16}|ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{82}|AIza[0-9A-Za-z_-]{35}|-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|password\s*[=:]\s*["\047][^"'\'']{8,}|token\s*[=:]\s*["\047][A-Za-z0-9_-]{20,})'
212 PAGES=("$TARGET/" "$TARGET/robots.txt" "$API_TARGET/status")
213 for PAGE in "${PAGES[@]}"; do
214 BODY=$(curl -sk -A "$BROWSER_UA" --max-time 8 "$PAGE")
215 if echo "$BODY" | grep -qiP "$SECRET_PATTERN"; then
216 printf ' [CRITICAL] Secret pattern in: %s\n' "$PAGE" | tee -a "$OUT_FILE"
217 echo "$BODY" | grep -ioP "$SECRET_PATTERN" | head -3 | sed 's/^/ /' | tee -a "$OUT_FILE"
218 else
219 printf ' [OK] No secret patterns in: %s\n' "$PAGE" | tee -a "$OUT_FILE"
220 fi
221 done
222 echo "" | tee -a "$OUT_FILE"
223
224 # โ”€โ”€ 8. Pipeline Artifact Registry โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
225 echo "--- Internal Artifact Registries (port scan) ---" | tee -a "$OUT_FILE"
226 HOST_IP=$(dig +short "$HOST" | grep -oP '^\d+\.\d+\.\d+\.\d+$' | head -1)
227 [ -z "$HOST_IP" ] && HOST_IP="$HOST"
228 for PORT in 8081 8082 8083 4567 4873 5000 5001 5432 3000 3001; do
229 if timeout 2 bash -c "echo >/dev/tcp/$HOST_IP/$PORT" 2>/dev/null; then
230 printf ' [OPEN] port %s on %s\n' "$PORT" "$HOST_IP" | tee -a "$OUT_FILE"
231 fi
232 done
233 echo " (registry port scan complete)" | tee -a "$OUT_FILE"
234 echo "" | tee -a "$OUT_FILE"
235
236 echo "=== 26. CI/CD Pipeline Security COMPLETE ===" | tee -a "$OUT_FILE"
237