Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
356 changes: 356 additions & 0 deletions doc/specs/#2204 - CVE Detection and Reporting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
---
author: Demitrius Nelon denelon, GitHub Copilot Copilot
created on: 2026-06-17
last updated: 2026-06-17
issue id: 2204
---

# CVE Detection in Validation and Client Reporting

For [#2204](https://github.com/microsoft/winget-cli/issues/2204).

## Abstract

Integrate CVE (Common Vulnerabilities and Exposures) detection into the WinGet ecosystem at two levels: the `winget-pkgs` validation pipeline (flagging packages with known CVEs during submission) and the WinGet client (informing users when installed packages have known vulnerabilities). Group Policy controls enable enterprise management of blocking behavior and reporting.

Check failure on line 14 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`CVEs` is not a recognized word (unrecognized-spelling)

## Inspiration

WinGet manages software installations for millions of Windows users but provides zero signal about known vulnerabilities. This gap is notable because:

- Enterprise security teams cannot use WinGet output to identify vulnerable software on managed devices
- The `winget-pkgs` repository accepts manifest updates without checking whether the version has disclosed CVEs
- Other package managers provide this capability (npm audit, pip-audit, cargo-audit, Dependabot)
- Software supply chain security (SLSA, SBOM) is a growing enterprise requirement

Check failure on line 23 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`SLSA` is not a recognized word (unrecognized-spelling)
- WinGet is becoming critical infrastructure for Windows software management

## Solution Design

### Part 1: Validation Pipeline CVE Detection

During manifest validation in the `winget-pkgs` pipeline:

1. **CVE lookup** β€” The private validation infrastructure queries vulnerability databases using internal package-to-CVE mappings
2. **Known CVE flagging** β€” If the submitted version has known CVEs:
- Add a `Security-CVE` label to the PR
- Post a bot comment listing CVEs with CVSS scores
3. **Severity-based workflow:**
- Critical (CVSS β‰₯ 9.0): **Blocked** β€” submission rejected, no waiver available
- High/Medium/Low (CVSS < 9.0): Informational β€” submission proceeds with the label for visibility

### Part 2: Client CVE Reporting

#### New command: `winget security`

```
winget security scan [--source <source>] [--severity <minimum>]
winget security show <package-id>
```

#### Integration with existing commands

| Command | CVE Behavior |
|---------|-------------|
| `winget list` | CVE column shown via `--details` |
| `winget upgrade` | Security-relevant upgrades highlighted with ⚠️; `--security` flag to upgrade only packages with security fixes |
| `winget install --version` | Non-blocking warning when version has known CVEs (blocking if GPO `CVEBlockInstallThreshold` is set and CVSS meets threshold) |
| `winget show` | CVE details shown in `--details` output |
| `winget configure test` | Reports CVE compliance status per resource |

#### Data Source Architecture

@pl4nty pl4nty Jun 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've found this to be pretty difficult. Manually mapping package IDs to CPEs helped, but a lot of raw NVD entries don't have accurate version ranges. Third-party dbs might have better data

afaik, GHSA's global db doesn't have a PURL format for Windows apps and doesn't ingest CPE data, eg GHSA-589r-wg5p-vwjq. CVE-2024-32002 in Git.Git is another good example, it's GHSA-8h77-4q3w-gfgv in https://github.com/git/git but is not available in the global db


```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ WinGet Client β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ CVE Engine β”‚ β”‚
│ │ - Package→PURL mapper │ │
β”‚ β”‚ - Advisory DB client β”‚ β”‚
β”‚ β”‚ - Local cache (SQLite) β”‚ β”‚
β”‚ β”‚ - Policy evaluator β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β–Ό β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ GHSA β”‚ β”‚ NVD β”‚ β”‚ Enterprise β”‚

Check failure on line 76 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`NVD` is not a recognized word (unrecognized-spelling)

Check failure on line 76 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`GHSA` is not a recognized word (unrecognized-spelling)
β”‚ API β”‚ β”‚ API β”‚ β”‚ Endpoint β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

- **Primary**: GitHub Advisory Database (GHSA) β€” fast updates, good OSS coverage
- **Secondary**: National Vulnerability Database (NVD) β€” broader coverage
- **Mapping**: Package ID β†’ PURL (Package URL) for lookup. Mapping maintained as metadata in source index.
- **Cache**: Local SQLite database with configurable TTL (default: 24 hours)
- **Sync**: Bulk download model (not per-query) to avoid revealing installed software inventory

### Part 3: Package-to-CVE Mapping

The mapping between WinGet package IDs and vulnerability database entries is maintained by Microsoft's private validation infrastructure:

```json
{
"Git.Git": {
"purl": "pkg:github/git/git",
"cpe": "cpe:2.3:a:git-scm:git:*:*:*:*:*:*:*:*"

Check failure on line 95 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`scm` is not a recognized word (unrecognized-spelling)

Check failure on line 95 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`cpe` is not a recognized word (unrecognized-spelling)
},
"Python.Python.3.12": {
"purl": "pkg:pypi/cpython",
"cpe": "cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"
}
}
```

This mapping is:
- Maintained internally by Microsoft β€” community contributions to CVE mappings are **disallowed by policy**
- Updated as part of the private validation infrastructure's periodic rescan process
- Delivered to clients via the source index (same distribution path as other backend-managed metadata)
- Not present in the public `winget-pkgs` manifest files

> [!NOTE]
> Mapping WinGet package IDs to PURLs/CPEs is a known challenge. Many NVD entries have inaccurate version ranges, and GHSA's global database doesn't cover Windows-specific package formats well. Microsoft's internal infrastructure uses multiple data sources and manual curation to improve accuracy over time.

Check failure on line 111 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`CPEs` is not a recognized word (unrecognized-spelling)

### Part 4: CVE Metadata in Merged Manifests

CVE metadata is added to the back-end **merged manifest** by Microsoft's private infrastructure β€” the same mechanism used for package icons and other enrichment metadata. This metadata is NOT authored by community contributors and is NOT present in the public `winget-pkgs` repository manifest files.

**How it works:**

1. Microsoft's validation infrastructure periodically rescans packages against vulnerability databases.

Check failure on line 119 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`rescans` is not a recognized word (unrecognized-spelling)
2. When CVEs are detected (or when existing CVE metadata changes), the merged manifest stored on Azure blob storage is updated with the security metadata.
3. The PreIndexed package source index is regenerated to reflect the updated merged manifest, ensuring the hash in the source still matches the stored blob.

**Merged manifest security metadata format:**

```yaml
Security:
Advisories:
- Id: CVE-2024-32002
Cvss: 9.8
FixedIn: "2.45.1"
Description: "Remote code execution via crafted input"
- Id: CVE-2023-22490
Cvss: 5.5
FixedIn: "2.39.2"
Description: "Path traversal in clone"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be individual links to each advisory?

AdvisoryUrl: https://github.com/git/git/security/advisories
```

**Pipeline hash reconciliation:**

When a periodic rescan detects new CVEs and updates the merged manifest, the publishing pipeline must:
1. Regenerate the merged manifest with updated security metadata
2. Recompute the content hash
3. Update the PreIndexed package source to reference the new hash
4. Publish the updated source index

This requires additional publishing pipeline functionality to support out-of-band manifest updates (updates triggered by rescan rather than by a new PR submission).

### Part 5: Group Policy Controls

| Policy | Type | Default | Description |
|--------|------|---------|-------------|
| `EnableCVEDetection` | Bool | Enabled | Master toggle for all CVE features |
| `CVEBlockInstallThreshold` | Float | 0 (disabled) | Block installs when any CVE has CVSS β‰₯ this value (e.g., 7.0) |
| `CVEBlockUpgradeThreshold` | Float | 0 (disabled) | Block upgrades to versions with CVSS β‰₯ this value |
| `CVEScanFrequency` | Int | 1440 | Cache refresh interval in minutes |
| `CVEReportingEndpoint` | String | Empty | URL to POST scan results for fleet visibility |

@pl4nty pl4nty Jun 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be really powerful, I'd love to write an example server or something


### CLI Arguments

| Argument | Commands | Description |
|----------|----------|-------------|
| `--ignore-security-warnings` | install, upgrade | Proceed despite CVE warnings |
| `--security` | upgrade | Only upgrade packages with available security fixes |
| `--severity` | security scan | Minimum CVSS score to report (e.g., `--severity 7.0`) |

### Settings

```json
{
"security": {
"enableCVEDetection": true,
"minimumReportCvss": 4.0,
"cacheRefreshMinutes": 1440,
"reportingEndpoint": ""
}
}
```

| Setting | CLI Argument | GPO Policy | Interaction |
|---------|-------------|------------|-------------|
| `enableCVEDetection` | N/A | `EnableCVEDetection` | GPO wins |
| `minimumReportCvss` | `--severity` | N/A | Arg overrides setting |
| `cacheRefreshMinutes` | N/A | `CVEScanFrequency` | GPO wins |

### COM API Surface

```idl
interface IPackageSecurityInfo
{
IVectorView<SecurityAdvisory> Advisories { get; };
Double HighestCvss { get; };
Boolean HasKnownVulnerabilities { get; };
}

interface ISecurityAdvisory
{
String Id { get; }; // CVE-YYYY-NNNNN
Double CvssScore { get; };
String Description { get; };
String FixedInVersion { get; };
String AdvisoryUrl { get; };
}
```

### PowerShell Cmdlets

```powershell
# Scan installed packages
Get-WinGetSecurityScan [-Source <String>] [-MinimumCvss <Double>]

# Get security info for a specific package (included in --details output)
Get-WinGetPackage -Id "Git.Git" | Select-Object Id, InstalledVersion, SecurityInfo

# Upgrade only packages with security fixes
Update-WinGetPackage -All -Security
```

### Cross-Repository Impact

- **winget-cli** β€” CVE engine, `winget security` command, GPO policies, settings, COM API additions
- **winget-pkgs** β€” Validation pipeline integration (CVE lookup during submission), `Security-CVE` label, bot comments
- **winget-cli-restsource** β€” REST API extension for serving CVE metadata from merged manifests
- **winget-create** β€” No impact (CVE metadata is backend-managed, not author-managed)
- **Publishing pipeline** β€” New capability: out-of-band manifest updates triggered by periodic rescan (hash reconciliation for updated merged manifests)

### Schema Version

The CVE metadata lives in the merged manifest (backend-managed, not in public repository manifests). No community-facing manifest schema version change is required. The client reads CVE data from the source index where it is delivered alongside other backend-enriched metadata.

## UI/UX Design

### `winget security scan` output:

```
> winget security scan
Scanning installed packages for known vulnerabilities...

Name Id Version CVEs Severity
───────────────────────────────────────────────────────────────────────
Git Git.Git 2.44.0 CVE-2024-32002 Critical
Node.js OpenJS.NodeJS 18.12.0 CVE-2023-44487 High
CVE-2023-45143 Medium
Python Python.Python.3 3.11.2 CVE-2023-27043 Medium

3 packages with known vulnerabilities (1 Critical, 1 High, 1 Medium).
Run 'winget upgrade' to see available fixes.
```

### `winget upgrade` with security:

```
> winget upgrade
The following packages have updates available:

Name Id Installed Available Source Security
───────────────────────────────────────────────────────────────────
Git Git.Git 2.44.0 2.45.1 winget ⚠️ Critical
Node.js OpenJS.NodeJS 18.12.0 18.20.3 winget ⚠️ High
VS Code Microsoft.VS.. 1.90.0 1.91.0 winget

⚠️ 2 packages have security updates. Run 'winget upgrade --all --security' to apply security fixes only.
```

### Blocking behavior (GPO-enabled):

```
> winget install OldPackage --version 1.0.0
This version has known vulnerabilities:
CVE-2024-XXXXX (Critical): Remote code execution via crafted input

Your organization's policy blocks installation of packages with Critical vulnerabilities.
Use 'winget install OldPackage' without --version to install the latest safe version,
or use --ignore-security-warnings to override (if permitted by policy).
```

### Non-interactive / COM API:

The COM API never blocks β€” it returns security information for the caller to evaluate:

```csharp
var package = manager.GetPackage("Git.Git");
var security = package.SecurityInfo;
if (security.HighestSeverity >= SecuritySeverity.High) {
// Handle in caller's UI
}
```

### `--disable-interactivity` behavior:

Warnings are emitted to stderr but do not prompt. Blocking (when GPO-enabled) returns a non-zero exit code without prompting.

## Capabilities

### Accessibility

- Security warnings use text labels (not just emoji/color) β€” screen readers announce severity levels
- `winget security scan --output json` provides structured data for programmatic consumption
- Warning text is in the localization resource file for translation

### Security

- This feature IS the security improvement β€” provides vulnerability awareness previously absent
- CVE cache uses integrity validation (signed responses from GHSA API)
- Reporting endpoint communication uses TLS 1.3+ with certificate validation
- No data sent to Microsoft unless enterprise explicitly configures reporting endpoint
- Bulk-download sync model prevents installed-software fingerprinting via query patterns

### Reliability

- Graceful degradation: if CVE databases are unreachable, operations proceed with a warning (never block on network failure)
- Cache ensures offline functionality with stale data rather than no data
- PURL mapping may have false positives β€” blocking mode requires GPO opt-in
- Source update failure does not prevent package operations

### Compatibility

- No breaking changes β€” all CVE features are additive and off-path when disabled
- Older clients ignore the `Security` manifest field
- GPO policies default to non-blocking behavior
- Existing automation scripts are unaffected unless `CVEBlockInstallSeverity` is configured

### Performance, Power, and Efficiency

- Local SQLite cache eliminates per-operation network calls after initial sync
- Background refresh aligned with `winget source update` (no additional scheduled tasks)
- Incremental sync β€” only fetch new advisories since last update timestamp
- `winget security scan` scans local cache only β€” O(n) where n = installed packages

## Potential Issues

1. **Package-to-CVE mapping accuracy** β€” WinGet package IDs don't directly correspond to PURLs/CPEs. Many NVD entries have inaccurate version ranges, and GHSA's global database doesn't cover Windows-specific package formats well. Microsoft's internal curation improves accuracy over time but gaps will exist.
2. **False positives** β€” A CVE may apply to a specific platform/build but not the Windows version distributed via WinGet (e.g., Linux-only CVE for cross-platform package). CVSS scoring and specificity metadata can filter.
3. **Warning fatigue** β€” Too many warnings for lower-CVSS issues may desensitize users. Default `minimumReportCvss: 4.0` helps; enterprises can raise the threshold.
4. **Data freshness** β€” NVD can lag days behind disclosure. GHSA is faster for GitHub-hosted projects. Microsoft's periodic rescan frequency determines how quickly CVE data propagates to clients.
5. **Privacy** β€” Bulk-download model avoids per-package queries but requires local storage (~5-10 MB for advisory database).
6. **Pipeline hash reconciliation** β€” Out-of-band merged manifest updates (from periodic rescan) require the publishing pipeline to recompute hashes and republish source indexes without a corresponding PR submission. This is new pipeline functionality that must be designed carefully to avoid race conditions with ongoing submissions.
7. **Reporting endpoint trust** β€” Enterprise reporting endpoints must be configured by GPO to prevent data exfiltration of installed software inventory.

## Future Considerations

- **SBOM generation** β€” `winget security sbom` generates Software Bill of Materials

Check failure on line 342 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`sbom` is not a recognized word (unrecognized-spelling)
- **Automated remediation** β€” Scheduled upgrades for security-critical packages with GPO controls
- **Microsoft Defender integration** β€” Feed CVE data into Defender for Endpoint vulnerability management
- **Supply chain attestation** β€” Validate package signatures and provenance
- **REST source CVE serving** β€” REST source implementations could serve CVE data directly, enabling enterprise REST sources to provide their own vulnerability assessments
- **Reporting endpoint examples** β€” Community-contributed example servers for the `CVEReportingEndpoint` feature (fleet-wide vulnerability dashboards)

## Resources

- Original issue: https://github.com/microsoft/winget-cli/issues/2204
- GitHub Advisory Database API: https://docs.github.com/en/rest/security-advisories
- NVD API: https://nvd.nist.gov/developers

Check failure on line 353 in doc/specs/#2204 - CVE Detection and Reporting.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`nvd` is not a recognized word (unrecognized-spelling)
- PURL specification: https://github.com/package-url/purl-spec
- CPE specification: https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/cpe
- Ecosystem comparisons: npm audit, pip-audit, cargo-audit, Dependabot
Loading