plugins/github-copilot-modernization/skills/cve-remediation/SKILL.md
Scan dependency manifests against known CVEs and remediate by upgrading vulnerable dependencies to patched versions, then rebuild and re-scan to confirm. Self-contained scan→fix→verify loop for any project with a dependency manifest. Use when: a cve-remediation task is dispatched; dependency set changed (version bump, new framework); assessment flagged vulnerable or EOL dependencies; or user asked to "fix CVEs", "patch vulnerabilities", or "dependency security". Triggers: "cve", "remediate cve", "fix cves", "patch vulnerable dependencies", "vulnerability scanning", "dependency security", "vulnerable dependencies", "security advisories", "npm audit", "pnpm audit", "maven audit", "gradle audit", "dependency scan", "vulnerability remediation". NOT for: security audit of auth/input/secrets/OWASP code paths (use security-review).
npx skillsauth add microsoft/github-copilot-modernization cve-remediationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill is the implementer-owned scan→fix→verify loop for dependency CVEs. It is dispatched as an execute-phase task to an implementer role (backend), runs per group, and produces patched dependency manifests plus an audit trail.
security role audits and escalates
but does NOT fix. This skill performs the fix (editing dependency manifests), so it is
owned by the implementer who owns those manifests — not by the security role.smoke-test, runtime-validation, and the
coordinator's verdict rules) remain separate and unchanged.CVE scanning is performed by the shared appmod-cve-assessment tool, which all
modernization surfaces (VS Code extension, IntelliJ plugin, Copilot CLI plugin, MCP server)
expose. It queries the GitHub Security Advisories API for a set of package ecosystems and
writes structured findings to a result file. Using the shared tool keeps scanning
consistent across products and emits the standard telemetry.
Primary — appmod-cve-assessment tool (preferred whenever it applies). Consult the
tool's own ecosystem parameter for the ecosystems it currently accepts — treat that
schema as the source of truth rather than assuming a fixed list, since it may grow over
time.
Fallback — LLM-only scan when the tool path does not apply. This covers two cases, both signalled by the tool itself:
appmod-cve-assessment is not registered in the current
runtime (e.g. a standalone Copilot CLI / rearchitecture runtime without the tool wired
up).ecosystem parameter does not accept it / input validation fails). Whichever
ecosystems the tool does not yet cover fall here automatically.In either case the model identifies known CVEs for the listed dependency versions from its own knowledge and writes the same findings schema (see Step 3). This is best-effort — model knowledge has a training cutoff and may miss recent advisories — so prefer the tool whenever it applies.
Map the project's package manager to the ecosystem identifier the tool accepts (for example, Maven and Gradle both resolve to the same JVM identifier). If the tool does not accept any identifier for the project's ecosystem, go straight to the LLM-only fallback.
The coordinator provides an artifact path for this task. Throughout this skill,
{{ARTIFACT_PATH}} refers to that directory. Write all reports and the fix summary there.
Before running any build or dependency commands, verify that the required build tool is available.
Detect the project type by examining the group's project root:
| File(s) found | Project type | Tool ecosystem id (common mapping) |
|---|---|---|
| pom.xml | Maven (Java) | maven |
| build.gradle or build.gradle.kts | Gradle (Java) | maven |
| *.sln | .NET solution | nuget |
| *.csproj | C# project | nuget |
| packages.config | Legacy .NET | nuget |
| package.json | Node.js (npm/pnpm/yarn) | npm |
| any other manifest (e.g. requirements.txt, pyproject.toml, go.mod, Cargo.toml, Gemfile, composer.json) | other | whatever the tool accepts, else LLM-only fallback (Step 2c) |
The ecosystem ids above are the common mappings at the time of writing. The tool's
ecosystemparameter is authoritative — if it accepts an identifier for the project's ecosystem, use the tool; otherwise use the LLM-only fallback.
Resolve the build command — prefer project-local wrappers over global tools:
| Project type | Check order (prefer first match) | Fallback |
|---|---|---|
| Maven | ./mvnw (Unix) or mvnw.cmd (Windows) | mvn on PATH |
| Gradle | ./gradlew (Unix) or gradlew.bat (Windows) | gradle on PATH |
| .NET | dotnet --version | — |
| Node.js | npm / pnpm / yarn (only needed to apply fixes) | — |
If the required tool is not found, stop and report the error. Do not proceed.
The scan also serves as detection: a clean result (an empty findings array) means the group has no known dependency CVEs and the task exits cheaply.
Extract the dependency coordinates for the group, capturing for each one the workspace-relative file path and 1-based line number where it is declared. Prefer resolved versions (which include transitive dependencies — the common source of CVEs) and fall back to manifest-declared versions when a resolver is unavailable.
| Ecosystem | Coordinate format | Where to read |
|---|---|---|
| maven | groupId:artifactId:version | mvn dependency:list / gradle dependencies (resolved), else pom.xml / build.gradle / gradle.properties |
| nuget | PackageName@version | dotnet list package (resolved), else *.csproj / Directory.Packages.props / packages.config |
| npm | package-name@version (e.g. [email protected], @angular/[email protected]) | lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock — resolved, includes transitives), else package.json |
These are the common ecosystems and their coordinate formats. For the authoritative coordinate format expected by each ecosystem the tool accepts, consult the tool's
dependencies/ecosystemparameter descriptions. For an ecosystem the tool does not accept (LLM-only fallback), use that ecosystem's natural coordinate form (e.g.package==versionfor Python) read from its manifest or lockfile.
appmod-cve-assessment tool (primary)Call appmod-cve-assessment with:
cveResultFilePath — an absolute path of {{ARTIFACT_PATH}}/cve-report-N.json
(start at 1, increment each scan to preserve history).ecosystem — the identifier the tool accepts for the project's package ecosystem (see
the tool's ecosystem parameter for the accepted values).dependencies — the coordinate array from Step 2a.dependencyLocations — { coordinate, filePath, lineNumber } for each dependency.The tool fetches CVEs, writes the findings JSON to cveResultFilePath, and returns a
summary message. For very large dependency sets, scan in batches, writing each batch to its
own numbered report. If the tool is unavailable, or it rejects the ecosystem as not
accepted, switch to the LLM-only fallback (Step 2c).
Use this path when the tool cannot do the scan — either because appmod-cve-assessment is
not available in the current runtime, or because the tool does not accept the
project's ecosystem (its ecosystem parameter rejects it). Perform the scan from model
knowledge instead: for each coordinate + version, identify known CVEs and write the same
findings schema (see Step 3) to {{ARTIFACT_PATH}}/cve-report-N.json. Note in the fix
summary that the LLM fallback was used (and why), since it is best-effort and may miss
recent advisories.
Large projects: if scanning is slow, run it in a background (async) terminal and, while it runs, review and fix vulnerabilities already known from earlier runs. Address any newly discovered issues once it completes.
Parse the latest cve-report-N.json and examine each finding:
[] means no CVEs).severity: critical > high > medium > low.evidence.explanation (the Affected dependencies and
Recommended fix lines) — plus the CVE identifier (id) and name.Report schema (one object per CVE):
[
{
"id": "CVE-2022-22965",
"name": "Spring Framework RCE via Data Binding on JDK 9+",
"status": "FOUND",
"category": "CVE",
"severity": "critical",
"storyPoint": 1,
"evidence": {
"files": ["pom.xml:20"],
"explanation": "[CVE-2022-22965](https://github.com/advisories/GHSA-36p3-wjmg-h94x): Spring Framework RCE via Data Binding on JDK 9+\n\nSeverity: CRITICAL\n\nAffected dependencies:\n - org.springframework:[email protected]\n\nRecommended fix:\n - Upgrade org.springframework:spring-core to 5.3.18 or later"
}
}
]
A finding whose
explanationcontains noRecommended fixline has no upstream patch available (unfixable). An empty array ([]) means no known CVEs were found.
For each vulnerable dependency, update to a secure version (the upgrade target from the
Recommended fix line) using the appropriate method.
Fix principles (consistent with the security agent):
<properties>, <dependencyManagement>, or inline <version>).Where CVEs hide — check BOM overrides first. Pay special attention to dependencies that explicitly declare a
<version>tag overriding a managed BOM (e.g. the Spring Boot dependencies BOM). These inline overrides bypass BOM management and are the most common source of missed CVE vulnerabilities — bumping the BOM/parent version alone will not patch them. Cross-check the<version>tags in each sub-module'spom.xmlagainst the vulnerable dependencies and update the override (or remove it to fall back to the managed version) as needed.
build.gradle or gradle.properties.<PackageReference Include="PackageName" Version="X.Y.Z" />.Version attribute to the patched version.Directory.Packages.props, update them there instead.dotnet add package PackageName --version X.Y.Z.package.json under dependencies or devDependencies."^4.17.21"), then regenerate the
lockfile so the resolved transitive versions update too:
npm install [email protected] (or edit package.json then npm install)pnpm add [email protected] (or edit then pnpm install)yarn add [email protected] (or edit then yarn install)package.json. Force a patched version with an
override instead of a direct edit:
"overrides" block ({ "overrides": { "pkg": "X.Y.Z" } })"pnpm": { "overrides": { "pkg": "X.Y.Z" } }"resolutions" block
Then re-run the install command to regenerate the lockfile.Re-run the Step 2 scan and write the output with the next sequential number. Then compare the new report against the previous one and exit the loop when ANY of these hold:
[]). Success.Recommended fix line in
its explanation (no upstream patch available). Success; record these in the summary as
accepted/unfixable.Fixable vs. unfixable — important. A CVE is unfixable only when it has no
Recommended fixline (no patched version exists upstream). A CVE that requires a major-version upgrade still has a patched version, so it is fixable — leaving it means remediation is incomplete (the No progress / deferred path, surfaced as a Step 7 follow-up), not the Only unfixable success. Treat the run as fully successful only when no fixable CVEs remain. This matches the security agent's rule: do not claim success while patchable CVEs are still outstanding.
Otherwise — if the count dropped but fixable CVEs (those with a Recommended fix) still
remain — return to Step 4, fix the newly reported CVEs, and re-scan, incrementing the report
number each time. This loop is the implementer's own confirmation — not a separate gate.
Use the build command resolved in Step 1.
./mvnw clean verify # Maven
./gradlew clean build # Gradle
dotnet build && dotnet test # .NET
npm install && npm test # Node.js (npm) — use pnpm/yarn equivalents as appropriate
Verify the build completes, existing tests pass, and the app starts (if applicable). If the build fails due to breaking API changes from an upgrade, apply the necessary code fixes and re-run the build. Cap this at 3 fix attempts — if the build still fails, stop, keep the dependency changes that scanned clean, and document the build issue in the summary (Step 7) rather than looping. Keep this within the Implementation phase — it is a build recheck, not a new phase.
{{ARTIFACT_PATH}}/cve-fix-summary.md. Note whether the
scan used the appmod-cve-assessment tool or the LLM-only fallback.{{ARTIFACT_PATH}}/final-cve-report.json). All intermediate numbered reports are
preserved for audit history.mvn / mvnw, gradle / gradlew, or dotnet — to apply
fixes and rebuild.npm / pnpm / yarn) — only needed to apply Node.js fixes.GITHUB_TOKEN (or GITHUB_PAT).
The appmod-cve-assessment tool reads it from the environment when present.| Error | Cause | Solution |
|-------|-------|----------|
| appmod-cve-assessment not available | Tool not registered in the current runtime | Use the LLM-only fallback (Step 2c) |
| Tool rejects the ecosystem | The tool does not accept this project's ecosystem | Use the LLM-only fallback (Step 2c) |
| Rate limit / HTTP 403 from the tool | Too many advisory API calls without auth | Set GITHUB_TOKEN / GITHUB_PAT in the environment |
| Empty / malformed coordinate rejected | Wrong coordinate format | maven: groupId:artifactId:version; nuget: PackageName@version; npm: package-name@version |
| Maven/Gradle not found | Build tool not in PATH | Install or ensure the wrapper (mvnw/gradlew) exists |
| dotnet CLI not found | .NET SDK not installed | Install .NET SDK |
| No supported project files found | Unrecognized project type | Ensure the root has pom.xml, build.gradle, *.sln, *.csproj, packages.config, or package.json |
# Maven
mvn dependency:list -DoutputFile=deps.txt -q
grep -E "^ [a-zA-Z]" deps.txt | sed 's/^ //' | awk -F: '{print $1":"$2":"$4}' > coordinates.txt
# Gradle
./gradlew dependencies --configuration compileClasspath > deps.txt
# .NET
dotnet list package > deps.txt
# Node.js (npm) — from package-lock.json (lockfileVersion 2/3)
jq -r '.packages | to_entries[] | select(.key|startswith("node_modules/")) | select(.value.version) | "\(.key|sub(".*node_modules/";""))@\(.value.version)"' package-lock.json | sort -u > coordinates.txt
Feed the resulting coordinates (with their file/line locations) into the
appmod-cve-assessment tool, or — when the tool is unavailable — into the LLM-only
fallback scan.
Set a GitHub token in the environment so the tool authenticates its advisory API calls:
export GITHUB_TOKEN=$(gh auth token)
development
Generate dependency map diagram from project build files
documentation
Generate data architecture and persistence layer documentation with data model diagram
documentation
Generate core business workflow documentation with sequence diagram
data-ai
Generate architecture diagram with component relationship details from project analysis