Never silence a finding without verifying step 1 and step 2 are not viable.
Triaging and fixing these findings is an implementation task. Prefer delegating it to the implement sub-agent (.claude/agents/implement.md) via your tool's sub-agent mechanism, and have it follow the procedure in this skill. Handle it inline only when delegation would lose important context.
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.