Skip to content

Commit 74f540a

Browse files
authored
Merge pull request #1491 from DeusData/ci/vt-gate-names-engines
ci(vt-gate): name the engine that flagged, not just the count
2 parents a8c6b3f + f3e79f9 commit 74f540a

2 files changed

Lines changed: 61 additions & 2 deletions

File tree

‎scripts/ci/check-virustotal.sh‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ echo "$VT_ANALYSIS" | tr ',' '\n' | while IFS= read -r entry; do
3838
continue
3939
fi
4040

41-
STATS=$(echo "$RESULT" | python3 -c "
41+
# Line 1 is the CSV summary; any further lines name a flagging engine.
42+
# The engine names are what a detection is actionable ON: the FP submission
43+
# has to go to a specific vendor, and "is this the known-noisy one or a new
44+
# family" is the first question asked. Reporting only counts sent the reader
45+
# to the web UI for the one fact the gate already had in hand.
46+
REPORT=$(echo "$RESULT" | python3 -c "
4247
import json, sys
4348
d = json.loads(sys.stdin.read())
4449
attrs = d.get('data', {}).get('attributes', {})
@@ -51,8 +56,16 @@ harmless = stats.get('harmless', 0)
5156
total = sum(stats.values())
5257
completed = malicious + suspicious + undetected + harmless
5358
print(f'{status},{malicious},{suspicious},{completed},{total}')
59+
for engine, r in sorted((attrs.get('results') or {}).items()):
60+
if r.get('category') in ('malicious', 'suspicious'):
61+
label = r.get('result') or r.get('category')
62+
version = r.get('engine_version') or '?'
63+
updated = r.get('engine_update') or '?'
64+
print(f'{engine} = {label} (engine {version}, defs {updated})')
5465
" 2>/dev/null || echo "queued,0,0,0,0")
5566

67+
STATS=$(echo "$REPORT" | head -1)
68+
DETECTIONS=$(echo "$REPORT" | tail -n +2)
5669
STATUS=$(echo "$STATS" | cut -d',' -f1)
5770
MALICIOUS=$(echo "$STATS" | cut -d',' -f2)
5871
SUSPICIOUS=$(echo "$STATS" | cut -d',' -f3)
@@ -66,6 +79,16 @@ print(f'{status},{malicious},{suspicious},{completed},{total}')
6679
fi
6780
if [ "$MALICIOUS" -gt 0 ] || [ "$SUSPICIOUS" -gt 0 ]; then
6881
echo "BLOCKED: $BASENAME flagged ($MALICIOUS malicious, $SUSPICIOUS suspicious / $COMPLETED engines)"
82+
if [ -n "$DETECTIONS" ]; then
83+
echo "$DETECTIONS" | while IFS= read -r detection; do
84+
[ -n "$detection" ] && echo " detected by: $detection"
85+
done
86+
else
87+
# Counts say something flagged but no engine is named: report that
88+
# rather than printing nothing, so the gap is visible instead of
89+
# looking like a detection-free block.
90+
echo " detected by: <engine names unavailable in the analysis response>"
91+
fi
6992
echo " $URL"
7093
echo "FAIL" >> /tmp/vt_gate_fail
7194
else

‎tests/test_vt_gate_zero_tolerance_contract.sh‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ mk_response clean 0 0
5050
mk_response one_malicious 1 0
5151
mk_response one_suspicious 0 1
5252

53+
# Same detection as one_malicious, but carrying the per-engine results a real
54+
# analysis returns. Pins that the gate NAMES the engine that fired.
55+
cat > "$FIX/responses/named_engine.json" <<'EOF'
56+
{"data":{"attributes":{"status":"completed",
57+
"stats":{"malicious":1,"suspicious":0,"undetected":60,"harmless":0},
58+
"results":{
59+
"Microsoft":{"category":"malicious","result":"Trojan:Win32/Wacatac.B!ml",
60+
"engine_version":"1.1.24010.10","engine_update":"20260807"},
61+
"Kaspersky":{"category":"undetected","result":null,
62+
"engine_version":"22.0.1.28","engine_update":"20260807"}
63+
}}}}
64+
EOF
65+
5366
echo "release-bytes" > "$FIX/work/binaries/probe"
5467

5568
run_gate() { # version analysis-id -> echoes exit code
@@ -74,5 +87,28 @@ for version in v0.9.1 v0.9.1-rc.1 v0.9.1-pre1 v1.0.0-alpha.1; do
7487
fail "1 suspicious must block on $version"
7588
done
7689

90+
# A detection must NAME the engine that fired.
91+
#
92+
# The gate reported counts only ("1 malicious / 59 engines"), which is the one
93+
# thing a reader cannot act on: a false-positive submission goes to a SPECIFIC
94+
# vendor (and the WDSI developer path is a web form, so there is no API to guess
95+
# with), and triage starts with "known-noisy engine, or a new family?". In the
96+
# 2026-08-07 dry run three artifacts flagged and the logs could not say by what,
97+
# so the answer had to be fetched by hand from the web UI afterwards.
98+
[ "$(run_gate v0.9.1 named_engine)" != "0" ] || fail "named-engine detection must still block"
99+
grep -q "Microsoft" "$FIX/last.log" || \
100+
fail "gate must name the flagging engine (Microsoft) — got: $(cat "$FIX/last.log")"
101+
grep -q "Trojan:Win32/Wacatac.B!ml" "$FIX/last.log" || \
102+
fail "gate must report the detection label, not just the vendor"
103+
# Engines that did NOT flag must not be listed, or the actionable line drowns.
104+
if grep -q "detected by: Kaspersky" "$FIX/last.log"; then
105+
fail "gate listed a non-flagging engine (Kaspersky) as a detection"
106+
fi
107+
108+
# Naming engines must not weaken the gate: an analysis WITHOUT per-engine
109+
# results still blocks on the counts alone.
110+
[ "$(run_gate v0.9.1 one_malicious)" != "0" ] || \
111+
fail "detection without engine detail must still block"
112+
77113
rm -f /tmp/vt_gate_fail
78-
echo "PASS: VT gate is zero tolerance — every detection blocks on every version"
114+
echo "PASS: VT gate is zero tolerance and names the engine that fired"

0 commit comments

Comments
 (0)