Suppressing findings
Some findings will be legitimate: a build script that really does need shell=True, or a test fixture that contains a sample payload. Record each accepted finding once, with a reason, in .malware-detector-ignore.json at the root of your repository:
[
{
"path": "scripts/deploy.py",
"ruleId": "py-subprocess-shell-true",
"reason": "Fixed command, no user input — reviewed 2026-10-01"
},
{
"path": "tests/fixtures/",
"reason": "Intentional malware samples used by our tests"
},
{
"ruleId": "js-network-to-ip",
"path": "src/dev-server.js",
"line": 12,
"reason": "Binds to 127.0.0.1 in development only"
}
]The same file covers every engine (Semgrep, heuristics, GuardDog and malcontent).
Matching rules
Each entry can set any of these fields. A finding is suppressed when it matches every field the entry sets:
| Field | Matches |
|---|---|
path | A substring ("tests/fixtures/") or a glob ("*.generated.js") |
ruleId | An exact rule ID from the rule catalogue |
line | An exact starting line number |
reason | Not used for matching. Write down why, for the next reviewer |
Every entry must set at least path or ruleId. A malformed file stops the scan with exit code 2, so a typo can't silently disable suppressions or hide findings.
Where the file is found
malware-detector walks up from the scan target until it finds .malware-detector-ignore.json, so a single file at the repository root covers every subdirectory. To use a file somewhere else:
malware-detector scan . --ignore-file security/ignore.jsonGood habits
- Be narrow. Prefer
path+ruleIdover a barepath. Suppressing a whole folder hides future findings in it too. - Always write a
reason. Six months from now, "why is this ignored?" should take seconds to answer. - Review it in PRs. Treat changes to the ignore file as security-relevant. Adding a
CODEOWNERSentry for it is a cheap safeguard.