Skip to content

Engines ​

malware-detector runs established detection engines and merges their output into one report. Four engines are supported:

EngineUsed byHow it's installedWhat it does
SemgrepscanIncluded (pip dependency, pinned 1.121.0)Runs the malware rule pack on code structure and data flow
Heuristics (built in)scanPart of malware-detectorFlags packed or obfuscated lines that have no useful structure to parse (obf-packed-line)
GuardDogdepsIncluded (pip dependency, 3.x)Supply-chain checks on an npm or PyPI package: malicious behaviours and suspicious metadata
malcontentscan --with-malcontentOptional: brew install malcontentYARA signatures for binaries, archives and heavily obfuscated webshells

Which engines run when ​

CommandEngines
scanSemgrep + heuristics
scan --with-malcontentSemgrep + heuristics + malcontent
scan --staged / --changedSemgrep + heuristics (malcontent is not used, so hooks stay fast)
depsGuardDog

The last line of every report names the engines that ran:

console
Scanned 7 files (Python 3 · JavaScript 2 · PHP 1 · TypeScript 1) with semgrep + heuristics in 1.3s.

Semgrep ​

Semgrep parses JavaScript, TypeScript, Python and PHP and follows data through the code. That lets the rules tell eval(atob(payload)) apart from eval("1 + 1"). Its version is pinned exactly: rule matching can change between Semgrep releases, and every version bump is re-measured against the benchmark.

Files Semgrep can't parse are reported, not skipped. If part of a file can't be parsed, you get an engine-partial-parse finding for those lines. If the whole file fails, for example because of a timeout, you get engine-scan-failed. Malware can hide in code a scanner can't read, so these findings are never dropped silently. Details →

Semgrep only parses files that could match a rule, so a file with nothing suspicious in it won't produce a parse error even if it contains invalid code.

Heuristics ​

Some malware is a single very long line of encoded data and a decoder. Structural rules have little to work with there, so a built-in check flags lines that are both unusually long and contain several obfuscation markers (fromCharCode, atob(, _0x, \x escapes…). It runs alongside Semgrep with no extra install.

GuardDog ​

GuardDog, by Datadog, looks at a package as a whole. It checks for known malicious behaviours and for warning signs in the metadata, such as typosquatted names. Its results are mapped onto the shared severity scale:

GuardDog ruleSeverity
threat-*CRITICAL
metadata rulesHIGH
capability-*MEDIUM

malcontent ​

malcontent, by Chainguard, matches YARA signatures. It finds things code parsers miss, such as compiled binaries, archives and heavily obfuscated PHP. On the benchmark, adding it raises recall from 73% to 91%.

It is a standalone Go binary and is not installed with malware-detector. Install it with Homebrew; go install usually fails because it needs native YARA libraries:

sh
brew install malcontent

String-based signatures can't see code structure, so malcontent findings on source files are capped at MEDIUM. They help catch more malware but can't fail a HIGH gate on their own. Findings on binaries and archives keep their original severity.

Engines are pinned to your install ​

malware-detector runs the Semgrep and GuardDog that were installed with it, even when it is installed with uv tool or pipx. A different Semgrep on your PATH won't change your results.

Not supported ​

  • Opengrep, the open-source fork of Semgrep, uses the same command line and output format but can't be selected from the CLI yet.
  • Known-vulnerability scanning (CVE checks with osv-scanner or grype) isn't available. malware-detector finds malicious code. Use npm audit, pip-audit or Dependabot alongside it for known vulnerabilities.