Winget Defender False-Positive Triage

SkillMonitoring & ops

Use when a ryl winget-pkgs validation PR is blocked by a Microsoft Defender false positive (`Validation-Defender-Error` / "Installer failed security check"). Covers the transient-vs-reproducible decision, local Defender + VirusTotal reproduction, the WDSI false-positive submission, and the log noise to ignore.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Winget Defender False-Positive Triage skill

What this skill tells your AI

The instructions your AI receives, as published by owenlamont/ryl in .agents/skills/winget-defender-fp/SKILL.md and read by ahel’s review.

ryl ships unsigned Windows binaries (InstallerType: zip + NestedInstallerType: portable). Microsoft Defender's cloud/ML heuristics intermittently flag the downloaded ZIP (mark-of-the-web) as a trojan, failing the winget-pkgs validation. The packaging is correct — this is a false positive, not a ryl bug. Code signing is the durable fix but is deferred (#342); this loop is the interim process.

Recognise it

  • Labels: Validation-Defender-Error is the root cause; Validation-Installation-Error and Validation-Executable-Error are downstream cascades of the same block; Needs-Author-Feedback means the PR is waiting on you (reply, or it can be auto-closed).
  • The moderator's "… Validation ended with" comment names the detection and the artifact (usually the x64 zip), e.g. [FAIL] Installer failed security check. Url: …/ryl-x86_64-pc-windows-msvc.zip … Detection: Trojan:Win32/Sprisky.U!cl.
  • The signature name varies per release (Wacatac.H!ml, Sprisky.U!cl, …); the !ml/!cl suffix means heuristic/cloud, not a content-signature match.
  • Error codes: 0x8A15002D = APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED; 0x80004005 = E_FAIL.
  • Log noise to ignore (always present, never the cause): the validator's WMI wminet_utils.dll TypeInitializationException, and ryl.exe returned exit code: 2 (ryl's by-design no-args usage exit).

Diagnose

The Azure validation pipeline is public/anonymous. From the wingetbot "Validation Pipeline Run" link grab the buildId, then (project GUID 8b78618a-7973-49d8-9174-4360829d979b) read the timeline and the failed task's log (look for the Installation Validation task):

base="https://dev.azure.com/shine-oss/8b78618a-7973-49d8-9174-4360829d979b/_apis/build/builds"
curl -s "$base/<buildId>/timeline?api-version=7.1"
curl -s "$base/<buildId>/logs/<logId>?api-version=7.1"

Reproduce locally (Windows, real-time protection ON — always record the signature build, it is the key datum for the PR reply):

$u = "https://github.com/owenlamont/ryl/releases/download/vX.Y.Z/ryl-x86_64-pc-windows-msvc.zip"
$zip = "$env:TEMP\ryl-check.zip"
Invoke-WebRequest $u -OutFile $zip
# Invoke-WebRequest does NOT set mark-of-the-web, so the heuristic that flags *downloaded*
# zips never fires on an IWR file. Tag it Internet-zone (3) with the real source to mimic a
# browser/winget download — without this the repro tests an unmarked file and proves nothing:
Set-Content -Path $zip -Stream Zone.Identifier -Value "[ZoneTransfer]`r`nZoneId=3`r`nHostUrl=$u"
Start-MpScan -ScanType CustomScan -ScanPath $zip   # file-at-rest scan; weaker than on-access
# THIS run only — Get-MpThreatDetection is cumulative, so a stale hit from a prior version
# reads as a current-version flag unless filtered by date:
Get-MpThreatDetection | Where-Object { $_.InitialDetectionTime -ge (Get-Date).Date } |
  Sort-Object InitialDetectionTime -Descending | Select-Object InitialDetectionTime, Resources
(Get-MpComputerStatus).AntivirusSignatureVersion

A clean Start-MpScan is reassuring but not conclusive: the !ml/!cl verdict is a cloud/ML call on the real-time on-access/download path, which a file-at-rest custom scan may not exercise — VirusTotal's Microsoft engine is the stronger corroboration.

Cross-check VirusTotal — its Microsoft engine is the best proxy for the validator. Look up by hash first (https://www.virustotal.com/gui/file/<sha256-lowercase>); upload if absent (anonymous, no login wall; upload + scan ~30s each). Check all four artifacts: both zips and the unzipped .exes — the bare exe usually scans clean; only the MOTW download trips it. VT's GUI is shadow-DOM heavy: walk shadow roots, read the Microsoft engine row, and rebuild the n/NN ratio from its DOM ancestors (no positives/total attribute); let the "analysing" state clear before reading the final ratio.

Decide

  • Both VT (Microsoft = Undetected) and local Defender clean → the verdict has aged out / is environment-specific. Do not file a WDSI submission (Microsoft would likely reject it as "not detected"). Reply to Needs-Author-Feedback with the evidence — the Defender signature build tested, VT links, "Microsoft: Undetected" — and ask the assigned/active moderator to re-run validation (@wingetbot run is moderator-triggered — an author/maintainer comment does not reliably start a run; the bot also auto-retries ~every 18h). (0.20.0 / microsoft/winget-pkgs#391230.)
  • Still reproducibly flagged (local Defender and/or VT's Microsoft engine) → file a WDSI false-positive submission, then wait for propagation + re-run. (0.19.1 / microsoft/winget-pkgs#390120.)

WDSI submission (only when reproducible)

https://www.microsoft.com/en-us/wdsi/filesubmissionSoftware developer persona → requires a Microsoft account sign-in and a CAPTCHA (human steps; not automatable).

  • Submit the flagged ZIP. Defender blocks copying a quarantined file, so obtain an uploadable copy via a temporary exclusion: Add-MpPreference -ExclusionPath <folder> (admin), re-download into it, submit, then Remove-MpPreference -ExclusionPath <folder>.
  • Fields: detection name, definition version, "Incorrectly detected as malware", company = your own name (ryl is a personal project, not an employer), notes cite the VT link.
  • "Accepted" ≠ propagated. The per-submission details page can transiently error ("Unable to access your submission details") — that is not a failure; verify via the submission history list. After Microsoft clears it, propagation to the validator takes ~1–3 days; then re-run.

Gotchas

  • Cross-repo refs: always microsoft/winget-pkgs#NNN; a bare #NNN links to ryl (the filing-upstream skill covers this and the rest of filing on someone else's repo).
  • Never put a local home path or machine specifics in a public PR comment — use $env:TEMP or placeholders.
  • Delegating to a headless/WSL agent: launch it with cwd = the ryl repo (so it loads this skill) and hand it the full step list (local Defender repro + VT for zips and exes + tie back to the PR). A prompt scoped to "scan the zips on VT" misses the reproduction and PR-triage half.

History

Signals

GitHub stars
72
Forks
3
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
winget-defender-fp
Source
github.com/owenlamont/ryl