Skip to content

ci(vt-gate): name the engine that flagged, not just the count - #1491

Merged
DeusData merged 1 commit into
mainfrom
ci/vt-gate-names-engines
Aug 7, 2026
Merged

DeusData merged 1 commit into
mainfrom
ci/vt-gate-names-engines

Conversation

@DeusData

@DeusData DeusData commented Aug 7, 2026

Copy link
Copy Markdown
Owner

The problem

The VirusTotal gate reported 1 malicious, 0 suspicious / 59 engines and stopped there — withholding the one fact a detection is actionable on.

A false-positive submission goes to a specific vendor, and triage begins with "known-noisy engine, or a new family?". Neither is answerable from a count.

It cost exactly that in the 2026-08-07 dry run: three artifacts flagged (linux-amd64, linux-amd64-portable, darwin-arm64), one engine each, and the logs could not say by what. The vendor had to be recovered by hand from the web UI afterwards — and the WDSI developer false-positive path is a web form with no public API, so guessing the wrong vendor wastes a filing round trip.

The fix

The analysis response already carries per-engine results; the gate simply discarded them. It now prints, under the BLOCKED line, each engine returning malicious or suspicious, with its detection label and definition date:

BLOCKED: codebase-memory-mcp-linux-amd64 flagged (1 malicious, 0 suspicious / 59 engines)
  detected by: Microsoft = Trojan:Win32/Wacatac.B!ml (engine 1.1.24010.10, defs 20260807)
  https://www.virustotal.com/gui/file-analysis/…

Engines that did not flag are not listed, or the actionable line drowns in sixty clean rows.

Strictness is deliberately unchanged

Zero tolerance still means any detection, any artifact, any version blocks. Two things pin that so "name the engine" can't become a route to tolerating one:

  • an analysis without per-engine results still blocks on the counts alone (asserted)
  • a missing engine list prints an explicit <engine names unavailable in the analysis response> rather than nothing, so the gap is visible instead of reading as a detection-free block

Verification

tests/test_vt_gate_zero_tolerance_contract.sh extended with a stubbed analysis carrying real per-engine results. Asserts the vendor is named, the detection label is reported, non-flagging engines are excluded, and detail-free detections still block.

Revert-checked: against the old gate the new assertions fail with precisely the counts-only output quoted above. Release-gate-chain and exec-bit contracts also pass.

Scope

Does not attempt to address the detections themselves. That investigation did not establish causation — linux-arm64 carries the same mimalloc allocator override (#1477) and scanned clean, and darwin-arm64 flagged without it — so a code "fix" there would be guessing at an ML decision boundary. This PR only makes the next detection diagnosable.

The gate reported "1 malicious, 0 suspicious / 59 engines" and stopped there —
withholding the one fact a detection is actionable on. A false-positive
submission goes to a SPECIFIC vendor, and triage begins with "known-noisy engine
or a new family?". Neither question is answerable from a count.

It cost exactly that in the 2026-08-07 dry run: three artifacts flagged
(linux-amd64, linux-amd64-portable, darwin-arm64), one engine each, and the logs
could not say by what. The vendor had to be recovered by hand from the web UI
afterwards — and the WDSI developer false-positive path is a web form with no
public API, so guessing the wrong vendor wastes a filing round trip.

The analysis response already carries per-engine results; the gate simply threw
them away. It now prints, under the BLOCKED line, each engine that returned
malicious or suspicious with its detection label and definition date:

  BLOCKED: codebase-memory-mcp-linux-amd64 flagged (1 malicious, ...)
    detected by: Microsoft = Trojan:Win32/Wacatac.B!ml (engine ..., defs ...)

Engines that did not flag are not listed, or the actionable line drowns in
sixty clean rows.

Deliberately unchanged: the gate's strictness. Zero tolerance still means any
detection on any artifact on any version blocks, and an analysis WITHOUT
per-engine results still blocks on the counts alone — the contract pins both, so
naming engines cannot become a route to tolerating one. A missing engine list
prints an explicit "<engine names unavailable>" rather than nothing, so the gap
is visible instead of reading as a detection-free block.

Verified by revert: against the old gate the new assertions fail with the
counts-only output quoted above.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData
DeusData enabled auto-merge August 7, 2026 18:29
@DeusData
DeusData merged commit 74f540a into main Aug 7, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant