Skip to content

fix: mitigate Windows Defender false positive on APM binary#490

Merged
danielmeppiel merged 2 commits into
microsoft:mainfrom
sergio-sisternes-epam:fix/487-windows-defender-false-positive
Mar 30, 2026
Merged

fix: mitigate Windows Defender false positive on APM binary#490
danielmeppiel merged 2 commits into
microsoft:mainfrom
sergio-sisternes-epam:fix/487-windows-defender-false-positive

Conversation

@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator

@sergio-sisternes-epam sergio-sisternes-epam commented Mar 30, 2026

Description

Mitigates Windows Defender false-positive detection (Trojan:Win32/Bearfoos.B!ml) on the APM binary by addressing the root causes identified in the issue analysis.

Fixes #487

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Changes

build/apm.spec

  • PE version info: Added VSVersionInfo block that embeds company, product, version, and copyright metadata into the Windows PE header. AV heuristic models use this metadata as a positive trust signal.
  • UPX disabled on Windows: Added should_use_upx() wrapper that disables UPX compression on Windows. UPX-packed binaries are a classic AV red flag. Non-Windows platforms continue to use UPX when available.
  • Version auto-read: _read_version_from_pyproject() reads the version from pyproject.toml so the PE header stays in sync automatically.

tests/unit/test_build_spec.py

  • 16 new unit tests covering spec syntax validation, should_use_upx() platform behavior, and _read_version_from_pyproject() parsing (valid, missing, malformed, prerelease).

CHANGELOG.md

  • Added entry under [Unreleased] > Fixed for the mitigation.

Testing

  • Tested locally
  • All existing tests pass (3,124 tests including 16 new)
  • Added tests for new functionality

Tier 2/3 follow-ups (out of scope)

  • Submit new binary to Microsoft for false-positive review (post-release)
  • Authenticode code signing (separate infrastructure issue)
  • CI-integrated VirusTotal scanning (separate issue)

@sergio-sisternes-epam sergio-sisternes-epam force-pushed the fix/487-windows-defender-false-positive branch from c065505 to 76b8a5b Compare March 30, 2026 13:53
@sergio-sisternes-epam sergio-sisternes-epam marked this pull request as ready for review March 30, 2026 13:59
Copilot AI review requested due to automatic review settings March 30, 2026 13:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Mitigates Windows Defender heuristic false positives for the PyInstaller-built Windows APM binary by adding Windows PE version metadata and ensuring UPX is not used on Windows, with accompanying troubleshooting documentation and regression tests around the spec helpers.

Changes:

  • Add should_use_upx() and Windows PE VSVersionInfo embedding (auto-reading version from pyproject.toml) in the PyInstaller spec.
  • Add unit tests that compile and AST-extract the helper functions from build/apm.spec without executing PyInstaller globals.
  • Document Windows AV false-positive troubleshooting steps and add corresponding changelog entries.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
build/apm.spec Disables UPX on Windows and embeds Windows PE version info using a version read from pyproject.toml.
tests/unit/test_build_spec.py Adds hermetic tests that validate spec syntax and behavior of extracted helper functions.
docs/src/content/docs/getting-started/installation.md Adds Windows Defender / antivirus false-positive troubleshooting guidance.
CHANGELOG.md Adds Unreleased entries for the mitigation and docs update.
uv.lock Updates the locked package version to 0.8.6.

Comment thread docs/src/content/docs/getting-started/installation.md Outdated
Comment thread docs/src/content/docs/getting-started/installation.md Outdated
Comment thread tests/unit/test_build_spec.py
Comment thread tests/unit/test_build_spec.py Outdated
Comment thread tests/unit/test_build_spec.py Outdated
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the fix/487-windows-defender-false-positive branch from 76b8a5b to 28ec940 Compare March 30, 2026 14:09
Comment thread build/apm.spec Outdated
Comment thread build/apm.spec Outdated
Comment thread docs/src/content/docs/getting-started/installation.md Outdated
…t#487)

- Add PE version info (VSVersionInfo) to Windows binary via build/apm.spec
- Disable UPX compression on Windows builds (AV heuristic trigger)
- Add AV false-positive troubleshooting section to installation docs
- Update CHANGELOG with fix and documentation entries

Closes microsoft#487

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the fix/487-windows-defender-false-positive branch from 28ec940 to 762625a Compare March 30, 2026 19:37
@danielmeppiel danielmeppiel self-requested a review March 30, 2026 20:25
@danielmeppiel danielmeppiel merged commit d61bc05 into microsoft:main Mar 30, 2026
4 checks passed
@sergio-sisternes-epam sergio-sisternes-epam deleted the fix/487-windows-defender-false-positive branch March 31, 2026 08:30
sergio-sisternes-epam added a commit that referenced this pull request May 19, 2026
)

- Add PE version info (VSVersionInfo) to Windows binary via build/apm.spec
- Disable UPX compression on Windows builds (AV heuristic trigger)
- Add AV false-positive troubleshooting section to installation docs
- Update CHANGELOG with fix and documentation entries

Closes #487

Co-authored-by: Sergio Sisternes <sergio.sisternes@epam.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Daniel Meppiel <51440732+danielmeppiel@users.noreply.github.com>
danielmeppiel added a commit that referenced this pull request May 22, 2026
…1450)

Reverts the optimize=2 -> optimize=1 change from #1307 (shipped in
v0.14.0) that re-embedded ~1.4 MB / +14% of marshalled docstring text
into the PyInstaller PYZ blob. That string surface is the most plausible
winget submission and breaking 'irm aka.ms/apm-windows | iex' on a
growing number of Defender installations (see #1407 and the winget-pkgs
#376283 validation log: 'Detection: Trojan:Script/Wacatac.H!ml,
Defender Security Intelligence 1.449.698.0').

Same family precedent in this repo: #487/#490 already had to disable UPX
heuristic that latched onto a feature surface change in the PE. The
right fix for unsigned PyInstaller binaries is (a) minimise classifier
input surface and (b) Authenticode-sign. This PR does (a); signing via
Azure Trusted Signing is the durable follow-up.

#1307 lowered optimize to keep Click command __doc__ as a help-text
fallback because 'apm view' (and earlier 'apm outdated', #1216) had
shipped without an explicit help= kwarg. That was the wrong fix:
optimize=1 made the docstring-fallback bug invisible by keeping every
docstring in the binary, but the binary is the wrong place to carry
prose. The right fix is to require every Click command to set help=
explicitly, and to fail CI when one doesn't.

Changes:

- build/apm.spec: optimize=1 -> optimize=2 with a comment pointing at
  the new silent-drift guard so future contributors don't repeat #1307.
- src/apm_cli/commands/view.py: lift the help body out of the docstring
  into a module-level _VIEW_HELP string and pass it as help= plus a
  short_help= for the top-level command index. The hidden 'apm info'
  alias in cli.py reads view_cmd.help, so it is fixed automatically.
- tests/unit/test_cli_consistency.py: new
  test_every_registered_command_has_explicit_help walks every command
  and sub-command reachable under the top-level cli group and asserts
  cmd.help (or cmd.short_help) is non-empty. Hidden alias commands are
  skipped because they inherit help from their source. This is the
  permanent regression trap for #1298 / #1407.
- tests/unit/test_build_spec.py: new test_pyinstaller_optimize_is_2
  AST-parses build/apm.spec, locates the Analysis(...) call, and
  asserts optimize=2. Pins the trade-off so a future PR cannot quietly
  lower it again to paper over a missing help= kwarg.

Validation:

- All 7 CI Lint checks green (ruff, format, yaml guard, file length,
  portable_relpath, pylint R0801, auth-signals).
- tests/unit/: 14905 passed, 1 skipped.
- PYTHONOPTIMIZE=2 python -m apm_cli.cli view --help and
  PYTHONOPTIMIZE=2 python -m apm_cli.cli --help both render the new
  explicit help correctly (smoke-test for the -OO code path).

Not in scope:

- Authenticode signing via Azure Trusted Signing. Tracked separately;
  this PR is the smallest change that unblocks the v0.14.x release and
  installs ASAP.
- Re-submitting v0.14.0 to https://www.microsoft.com/wdsi/filesubmission
  for false-positive review. Manual step, post-merge.

Refs: #1407, #1298, #487, #490, winget-pkgs#376283
Reverts the binary-surface effect of: #1307

Co-authored-by: danielmeppiel <danielmeppiel@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

Windows Defender false positive: Trojan:Win32/Bearfoos.B!ml on APM binary

3 participants