Never silence a finding without verifying step 1 and step 2 are not viable.
Walk through every finding in this order. Do not skip a rung.
If upgrading would force a major bump that breaks the build or pulls in new peer-dep churn, weigh whether a resolution (step 2) is safer.
Only after steps 1 and 2 are confirmed not viable. Before ignoring, you must be able to answer YES to all of:
If you cannot say YES to all four, do not ignore — escalate to the user with the specific blocker.
-
Collect findings — primary source is the GitHub PR MegaLinter workflow log.
The authoritative Trivy + OSV-Scanner output lives in the MegaLinter run for the PR associated with the current branch. Pull it with gh:
gh pr view --json number,url,headRefName,state
gh run list --branch "$(git rev-parse --abbrev-ref HEAD)" --workflow mega-linter.yml \
--limit 5 --json databaseId,status,conclusion,createdAt,event,headSha
gh run view <runId> --json jobs --jq '.jobs[] | {name, databaseId, conclusion}'
gh run view --job <jobId> --log > megalinter.log
The log is ~1k+ lines. Find each scanner's section:
- OSV-Scanner: grep
OSV_SCANNER / osv-scanner. Block starts at Linted [REPOSITORY] files with [osv-scanner] and prints a table with columns OSV URL | CVSS | ECOSYSTEM | PACKAGE | VERSION | FIXED VERSION | SOURCE.
- Trivy: grep
TRIVY / trivy. Block starts at Linted [REPOSITORY] files with [trivy] (vulns + misconfigs) and Linted [REPOSITORY] files with [trivy-sbom] (SBOM only, informational).
For each finding, capture: CVE/OSV id, package, installed version, fix version (if any), severity / CVSS, ecosystem, and source file (yarn.lock, package.json, …). Then use yarn why <pkg> locally to trace the dependency path before deciding the triage rung.
Clean up after: delete the temporary megalinter.log (do not commit it).
Fallbacks if the PR / workflow log is unavailable:
- Read a local MegaLinter report under
megalinter-reports/ (only present if MegaLinter was run locally).
- Run scanners locally:
osv-scanner scan source --recursive . (this is the command MegaLinter runs)
trivy fs --scanners vuln,misconfig --exit-code 1 . (this is the command MegaLinter runs)
-
For each finding, classify as upgrade, resolution, or ignore using the decision ladder above. Write the choice down before editing.
-
Apply upgrades for everything classified as upgrade:
- Edit
package.json, bump the version range.
yarn install.
- Verify in
yarn.lock that the resolved version is >= fix version.
yarn lint && yarn dev to confirm no regression.
-
Apply resolutions for everything classified as resolution:
- Edit the
"resolutions" block in package.json. Match the existing path-scoped style.
yarn install.
yarn why <vulnerable-pkg> — confirm every installed copy resolves to the patched version. If two paths still resolve to vulnerable versions, add another scoped entry.
yarn lint && yarn dev.
-
Apply ignores for everything classified as ignore (only after steps 3 + 4 cleared what they could):
Trivy — create or update .trivyignore at the repo root. Format: one CVE id per line. Add an expiration date and a justification comment on the line ABOVE the id. Trivy understands # exp:YYYY-MM-DD as an expiry annotation:
# devDependency only (webpack loader); RCE requires attacker-supplied build input we never accept.
# Re-evaluate when <pkg> ships a patched release.
# exp:2026-08-31
CVE-2025-XXXXX
# ReDoS in a code path not reached by this extension (string parser only used for <X>).
# exp:2026-08-31
CVE-2025-YYYYY
Then register the file with the Trivy linter in .mega-linter.yml:
REPOSITORY_TRIVY_ARGUMENTS: "--ignorefile .trivyignore"
OSV-Scanner — create or update osv-scanner.toml at the repo root. Each ignore needs the OSV id, an expiry, and a human-readable reason:
[[IgnoredVulns]]
id = "GHSA-xxxx-yyyy-zzzz"
ignoreUntil = 2026-08-31
reason = "devDependency only (webpack loader); attack vector requires build input we never accept. Watch for a patched release of <pkg>."
[[IgnoredVulns]]
id = "CVE-2025-YYYYY"
ignoreUntil = 2026-08-31
reason = "Vulnerable code path is not reached from extension.ts, worker.ts, or any LWC bundle. Confirmed via yarn why + grep <symbol>."
Then point OSV-Scanner at the config in .mega-linter.yml:
REPOSITORY_OSV_SCANNER_ARGUMENTS: "--config=osv-scanner.toml"
Always pick an ignoreUntil / exp: no more than ~3 months out. Permanent ignores rot; a forced re-evaluation is the point.
-
Update CHANGELOG.md if any user-facing dependency was upgraded with visible behavior change (rare — most security bumps are silent). Pure security maintenance does NOT need a changelog entry. See the implement skill for changelog merge rules.