Skip to content

Commit 273c435

Browse files
Merge pull request #8 from Santandersecurityresearch/feat/openant-pipeline
ci: remove unit limit to scan all reachable units
2 parents 4ca304e + 3ffd63e commit 273c435

2 files changed

Lines changed: 67 additions & 15 deletions

File tree

.github/workflows/openant-scan.yml

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ jobs:
8383
--verify \
8484
--enhance-mode single-shot \
8585
--model sonnet \
86-
--limit 50 \
8786
--no-report \
8887
--workers 8 \
8988
--json \
@@ -94,9 +93,18 @@ jobs:
9493
- name: Convert findings to SARIF
9594
if: always()
9695
run: |
96+
# Prefer results_verified.json (all Stage 1 findings with Stage 2 context)
97+
# so the Security tab shows everything, not just Stage 2-confirmed findings.
98+
VERIFIED="/tmp/openant-results/results_verified.json"
9799
PIPELINE_OUTPUT="/tmp/openant-results/pipeline_output.json"
98-
if [ -f "$PIPELINE_OUTPUT" ]; then
99-
python /tmp/openant/tools/sarif_convert.py "$PIPELINE_OUTPUT" \
100+
SARIF_INPUT="${VERIFIED:-$PIPELINE_OUTPUT}"
101+
if [ -f "$VERIFIED" ]; then
102+
SARIF_INPUT="$VERIFIED"
103+
elif [ -f "$PIPELINE_OUTPUT" ]; then
104+
SARIF_INPUT="$PIPELINE_OUTPUT"
105+
fi
106+
if [ -f "$SARIF_INPUT" ]; then
107+
python /tmp/openant/tools/sarif_convert.py "$SARIF_INPUT" \
100108
-o /tmp/openant-results/results.sarif
101109
else
102110
# Minimal valid empty SARIF so the upload step does not fail.
@@ -122,13 +130,23 @@ jobs:
122130
PIPELINE_OUTPUT="/tmp/openant-results/pipeline_output.json"
123131
124132
VULNERABLE=0; BYPASSABLE=0; INCONCLUSIVE=0; TOTAL=0; COST="N/A"
133+
S1_VULNERABLE=0; S1_INCONCLUSIVE=0; S1_SAFE=0
134+
S2_VERIFIED=0; S2_AGREED=0; S2_DISAGREED=0
125135
if [ -f "$STDOUT" ]; then
126-
VULNERABLE=$(jq -r '.data.metrics.vulnerable // 0' "$STDOUT" 2>/dev/null || echo 0)
127-
BYPASSABLE=$(jq -r '.data.metrics.bypassable // 0' "$STDOUT" 2>/dev/null || echo 0)
128-
INCONCLUSIVE=$(jq -r '.data.metrics.inconclusive // 0' "$STDOUT" 2>/dev/null || echo 0)
129-
TOTAL=$(jq -r '.data.units_count // 0' "$STDOUT" 2>/dev/null || echo 0)
130-
RAW_COST=$(jq -r '.data.usage.total_cost_usd // 0' "$STDOUT" 2>/dev/null || echo 0)
136+
VULNERABLE=$(jq -r '.data.metrics.vulnerable // 0' "$STDOUT" 2>/dev/null || echo 0)
137+
BYPASSABLE=$(jq -r '.data.metrics.bypassable // 0' "$STDOUT" 2>/dev/null || echo 0)
138+
INCONCLUSIVE=$(jq -r '.data.metrics.inconclusive // 0' "$STDOUT" 2>/dev/null || echo 0)
139+
TOTAL=$(jq -r '.data.units_count // 0' "$STDOUT" 2>/dev/null || echo 0)
140+
RAW_COST=$(jq -r '.data.usage.total_cost_usd // 0' "$STDOUT" 2>/dev/null || echo 0)
131141
COST=$(printf '$%.2f' "$RAW_COST" 2>/dev/null || echo "N/A")
142+
# Stage 1 raw counts (from analyze step report)
143+
S1_VULNERABLE=$(jq -r '.data.step_reports[] | select(.step=="analyze") | .summary.verdicts.vulnerable // 0' "$STDOUT" 2>/dev/null || echo 0)
144+
S1_INCONCLUSIVE=$(jq -r '.data.step_reports[] | select(.step=="analyze") | .summary.verdicts.inconclusive // 0' "$STDOUT" 2>/dev/null || echo 0)
145+
S1_SAFE=$(jq -r '.data.step_reports[] | select(.step=="analyze") | .summary.verdicts.safe // 0' "$STDOUT" 2>/dev/null || echo 0)
146+
# Stage 2 counts (from verify step report)
147+
S2_VERIFIED=$(jq -r '.data.step_reports[] | select(.step=="verify") | .summary.findings_verified // 0' "$STDOUT" 2>/dev/null || echo 0)
148+
S2_AGREED=$(jq -r '.data.step_reports[] | select(.step=="verify") | .summary.agreed // 0' "$STDOUT" 2>/dev/null || echo 0)
149+
S2_DISAGREED=$(jq -r '.data.step_reports[] | select(.step=="verify") | .summary.disagreed // 0' "$STDOUT" 2>/dev/null || echo 0)
132150
fi
133151
134152
if [ "$VULNERABLE" -gt 0 ] || [ "$BYPASSABLE" -gt 0 ]; then
@@ -161,13 +179,21 @@ jobs:
161179
162180
${BADGE}
163181
164-
| Metric | Value |
165-
|--------|-------|
166-
| Units scanned | ${TOTAL} |
167-
| Confirmed vulnerable | ${VULNERABLE} |
168-
| Bypassable controls | ${BYPASSABLE} |
169-
| Inconclusive | ${INCONCLUSIVE} |
170-
| LLM cost | ${COST} |
182+
### Pipeline summary
183+
184+
| Stage | Metric | Value |
185+
|-------|--------|-------|
186+
| | Units scanned | ${TOTAL} |
187+
| Stage 1 — Detection | Flagged vulnerable | ${S1_VULNERABLE} |
188+
| Stage 1 — Detection | Inconclusive | ${S1_INCONCLUSIVE} |
189+
| Stage 1 — Detection | Safe | ${S1_SAFE} |
190+
| Stage 2 — Attacker simulation | Sent for verification | ${S2_VERIFIED} |
191+
| Stage 2 — Attacker simulation | Confirmed (agreed) | ${S2_AGREED} |
192+
| Stage 2 — Attacker simulation | Eliminated (disagreed) | ${S2_DISAGREED} |
193+
| Final | Confirmed vulnerable | ${VULNERABLE} |
194+
| Final | Bypassable controls | ${BYPASSABLE} |
195+
| Final | Inconclusive | ${INCONCLUSIVE} |
196+
| | LLM cost | ${COST} |
171197
172198
${FINDING_TABLE}
173199

OPENANT.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# OpenAnt Security Context
2+
3+
## Application Type
4+
web_app
5+
6+
## Purpose
7+
Vulpy is an intentionally vulnerable Python/Flask web application used for security training.
8+
It demonstrates common web vulnerabilities including SQL injection, XSS, CSRF, and insecure session handling.
9+
10+
## Deployment Assumption
11+
For security analysis purposes, assume this application is deployed and accessible over the internet
12+
on a public IP address. The localhost binding in the source (`host='127.0.1.1'`) is a development
13+
artifact — treat the application as a production web server reachable by a remote attacker.
14+
15+
## Trust Boundaries
16+
- All HTTP request parameters (query string, POST body, JSON body, headers) are attacker-controlled
17+
- The SQLite database is initialised and populated
18+
- All routes under `/user`, `/posts`, `/api`, `/mfa`, `/csp`, and `/hello` are accessible without prior authentication unless the route explicitly checks session state
19+
20+
## Known Intentional Vulnerabilities (do not mark as false positives)
21+
- SQL injection via string formatting in `bad/libuser.py` and `bad/libposts.py`
22+
- XSS via unescaped template output
23+
- CSRF — no token validation on state-changing routes
24+
- Session impersonation via predictable session tokens
25+
- Authentication bypass via SQL injection in login
26+
- Insecure deserialisation in session handling

0 commit comments

Comments
 (0)