Skip to content

Warn when a vulnerable or EOL SDK version is resolved#53557

Open
JamieMagee wants to merge 1 commit intodotnet:mainfrom
JamieMagee:jamieMagee/sdk-vulnerability-warnings
Open

Warn when a vulnerable or EOL SDK version is resolved#53557
JamieMagee wants to merge 1 commit intodotnet:mainfrom
JamieMagee:jamieMagee/sdk-vulnerability-warnings

Conversation

@JamieMagee
Copy link
Copy Markdown
Member

@JamieMagee JamieMagee commented Mar 23, 2026

Closes #49552

NuGet warns about vulnerable packages (NU1901-NU1904) but nothing warns about the SDK itself. This adds opt-in build warnings when the resolved SDK has known CVEs or is end of life.

Set <CheckSdkVulnerabilities>true</CheckSdkVulnerabilities> in your project or Directory.Build.props. Off by default. Two warning codes: NETSDK1237 for vulnerabilities, NETSDK1238 for EOL. Suppressible with <NoWarn> if you want one but not the other.

During restore, the CLI fetches release metadata in the background (same releases-index.json that dotnet sdk check uses) and caches it to ~/.dotnet/sdk-vulnerability-cache/. The MSBuild task reads from that cache during build. It never hits the network itself. No cache file means no warnings, so air-gapped builds work fine.

Cache refresh runs alongside the workload manifest updater in RestoringCommand, 24h sentinel. Skipped in source-build. DOTNET_SDK_VULNERABILITY_CHECK_DISABLE and DOTNET_SDK_VULNERABILITY_CHECK_INTERVAL_HOURS are there if you need them.

warning NETSDK1237: The current .NET SDK (9.0.100) has known vulnerabilities (CVE-2025-12345, CVE-2025-99999). Update to version 9.0.102: https://dotnet.microsoft.com/download
warning NETSDK1238: The current .NET SDK (7.0.410) is end of life as of 2024-05-14. It will receive no further security updates: https://dotnet.microsoft.com/download

@baronfel would appreciate your input.

Copilot AI review requested due to automatic review settings March 23, 2026 03:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds SDK vulnerability/EOL warnings to improve security visibility when a potentially vulnerable or out-of-support .NET SDK is resolved, using the same release metadata source as dotnet sdk check and a local on-disk cache to avoid introducing startup latency or network calls during MSBuild resolution.

Changes:

  • Add CLI startup notifier that reads cached vulnerability/EOL info and triggers background cache refresh (opt-out + configurable refresh interval via env vars).
  • Add MSBuild SDK resolver warnings sourced only from the local cache (no network calls).
  • Add shared cache-reader + new unit tests and release-metadata test assets to validate the vulnerability/EOL evaluation logic.

Reviewed changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/dotnet.Tests/SdkVulnerabilityTests/GivenSdkVulnerabilityChecker.cs Unit tests for vulnerability/EOL evaluation logic against test release metadata.
test/dotnet.Tests/SdkVulnerabilityTests/GivenSdkVulnerabilityCacheReader.cs Unit tests for reading cached summary JSON (incl. disabled/no-cache cases).
test/dotnet.Tests/SdkVulnerabilityTests/GivenSdkReleaseMetadataCache.cs Unit tests for cache sentinel interval behavior and cache summary reading.
test/TestAssets/TestReleases/VulnerabilityTestRelease/releases-index.json Test releases-index metadata including an active and an EOL channel.
test/TestAssets/TestReleases/VulnerabilityTestRelease/9.0/releases.json Test channel release metadata with CVEs used by unit tests.
test/TestAssets/TestReleases/VulnerabilityTestRelease/7.0/releases.json Test EOL channel metadata with CVEs used by unit tests.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.zh-Hant.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.zh-Hans.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.tr.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.ru.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.pt-BR.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.pl.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.ko.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.ja.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.it.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.fr.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.es.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.de.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/xlf/Strings.cs.xlf New localizable MSBuild resolver warning strings.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Strings.resx Adds MSBuild resolver warning resources for vulnerable/EOL SDKs.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj Links shared cache-reader into MSBuild resolver build.
src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs Emits MSBuild warnings based on cached vulnerability/EOL summary data.
src/Common/SdkVulnerabilityCacheReader.cs Shared cache-summary reader DTO + source-gen JSON context for CLI + resolver.
src/Common/EnvironmentVariableNames.cs Adds env vars to disable the check and configure refresh interval.
src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.tr.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.ru.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.pl.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.ko.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.ja.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.it.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.fr.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.es.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.de.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/xlf/CliStrings.cs.xlf New localizable CLI warning strings for vulnerable/EOL SDKs.
src/Cli/dotnet/dotnet.csproj Links shared cache-reader into CLI build.
src/Cli/dotnet/SdkVulnerability/SdkVulnerabilityNotifier.cs CLI startup hook to read cache, print warnings, and refresh cache in background.
src/Cli/dotnet/SdkVulnerability/SdkVulnerabilityInfo.cs DTOs representing SDK vulnerability/EOL status and CVE entries.
src/Cli/dotnet/SdkVulnerability/SdkVulnerabilityChecker.cs Pure logic that computes vulnerabilities/EOL status from release metadata.
src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs Cache manager: refresh interval/sentinel, background metadata fetch, and summary persistence.
src/Cli/dotnet/Program.cs Invokes the notifier during CLI startup.
src/Cli/dotnet/CliStrings.resx Adds CLI warning resources for vulnerable/EOL SDKs (incl. no-upgrade variants).

Comment thread src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs Outdated
Comment thread src/Cli/dotnet/SdkVulnerability/SdkVulnerabilityChecker.cs
Comment thread src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs Outdated
@marcpopMSFT
Copy link
Copy Markdown
Member

Waiting for @baronfel's comment. We were discussing adding an experience for this once we had a way to update the SDK rather than going to the download page. We were hesitant as we didn't want to have to download releases.json on every CLI action so have not fully designed this feature yet.

@marcpopMSFT
Copy link
Copy Markdown
Member

Summarizing offline conversation:
As noted, we were thinking of heading in this direction once we had a way of helping customers update. Some key feedback on the design.

  • We think it should only be during build commands, not all commands (still not 100% settled on this)
  • As such, it should probably be suppressible through a build property and not just an environment variable. That way you can disable with a checked in change to the diretory.build.props rather than a build script change
  • It has to be disableable or be able to override the message in source build builds as the current message doesn't apply to those installs (it doesn't apply to installs by VS or VSCode either fwiw). CC @mthalman @omajid for their feedback on how this type of message could work for source build versions.
  • It might be worth exploring if we can tell how the SDK was installed so that the message is customized (or silent) in installs it doesn't make sense for.
  • There is some discussion on whether we want it every time but nuget audit does so maybe leave it like that.

Overall, we like the direction of not hitting the network and using the cache instead and it makes sense to warn the user when their out of date. VS and VSCode will have more experiences like this in the future but they're going to have ways of upgrading the user for them which greatly improves the user experience.

@JamieMagee
Copy link
Copy Markdown
Member Author

@marcpopMSFT Thanks for the feedback. I think the right move is to get warnings out of both the CLI startup and the SDK resolver, and put them in a .targets file instead. That one change actually addresses most of your points at once.

Right now warnings fire from Program.ProcessArgs (every command) and from the resolver (during build). If I move them into an MSBuild task with dedicated warning codes (NETSDK2001 for vulnerabilities, NETSDK2002 for EOL), they'll only show up during build, they'll only show up once, and users can suppress them with <NoWarn> or a <SuppressSdkVulnerabilityWarnings> property in Directory.Build.props. Same pattern as SuppressNETCoreSdkPreviewMessage. The resolver can't do property-based suppression because it runs before project evaluation, which is why I want to move out of it entirely.

The cache refresh moves to RestoringCommand, same place as WorkloadManifestUpdater.BackgroundUpdateAdvertisingManifestsAsync. So dotnet --help and dotnet tool list won't touch it.

For source-build, I'll gate the cache refresh with #if !DOT_NET_BUILD_FROM_SOURCE so those SDKs skip network calls. On the .targets side, I can set a _SdkIsSourceBuild property at build time (in GenerateBundledVersions.targets) and either suppress the warning or change the message to say "update via your package manager" instead of linking to dotnet.microsoft.com/download.

For detecting other install sources (VS-managed, install script, etc.), there's no runtime mechanism for that today and adding one means touching MSI, PKG, DEB, RPM, install scripts, and VS bundling. I'd rather defer that to a follow-up. The DOTNET_SDK_VULNERABILITY_CHECK_DISABLE env var stays as the escape hatch, and I'll check it in the .targets Condition too so CI and air-gapped builds can opt out.

@marcpopMSFT
Copy link
Copy Markdown
Member

More offline conversation:
We discussed this further and are ok taking this as an MSBuild flag but opt-in. While that does limit the scope, it limits the risk as well.

Your suggestions about excluding from source build make sense though with this opt-in, becomes less critical.

Another concern we had would be release day. We don't want there to be a timing problem that ends up breaking people's CI and local builds because the releases.json has updated but their SDK hasn't updated. For example, our own repos usually do a deliberate central rollout with new SDK versions which isn't immediate so this would flag during that validation window.

I think we're still ok having it as an opt-in and we do plan on plugging dotnetup into this experience in the future.

@JamieMagee JamieMagee force-pushed the jamieMagee/sdk-vulnerability-warnings branch from fc8aa27 to b0eb48d Compare April 1, 2026 02:57
@JamieMagee JamieMagee requested a review from a team as a code owner April 1, 2026 02:57
@github-actions github-actions Bot added the sdk-diagnostic-docs-needed Indicates that a PR introduces new diagnostic codes, which must be documented over at dotnet/docs label Apr 1, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 1, 2026

📋 SDK Diagnostic Documentation Reminder

This PR introduces 2 new SDK diagnostic codes:

  • NETSDK1238
  • NETSDK1239

Action Required

Please ensure that documentation for these diagnostics is added or updated in the dotnet/docs repository at:

Each diagnostic should have:

  • A clear description of the error/warning
  • Possible causes
  • Recommended solutions
  • Code examples where applicable

Thank you for helping keep our documentation up to date! 🙏

@JamieMagee
Copy link
Copy Markdown
Member Author

@marcpopMSFT Updated the PR.

It's opt-in now. <CheckSdkVulnerabilities>true</CheckSdkVulnerabilities> in a project or Directory.Build.props enables it, off by default. Warnings use NETSDK1237 (CVEs) and NETSDK1238 (EOL), so <NoWarn> works if you want to suppress one but not the other.

Warnings moved out of CLI startup and the SDK resolver entirely. They come from a .targets file that runs an MSBuild task during build. dotnet --help, dotnet tool list, etc. won't trigger anything.

Cache refresh moved to RestoringCommand, same spot as the workload manifest updater. Background, fire-and-forget, skipped in source-build.

On the release-day timing: since it's opt-in the blast radius is already small, but worth noting that CVE warnings only fire when there's a newer release with security fixes in the same channel. So the window only matters if someone opts in, their SDK version is in releases.json, and a newer patch exists they haven't rolled to yet. Repos doing a deliberate rollout could suppress the code in Directory.Build.props during that window.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 6 comments.

Comment thread src/Tasks/Microsoft.NET.Build.Tasks/CheckSdkVulnerabilities.cs Outdated
Comment thread src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs
Comment thread src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs Outdated
Comment thread src/Common/SdkVulnerabilityCacheReader.cs
@JamieMagee JamieMagee force-pushed the jamieMagee/sdk-vulnerability-warnings branch from b0eb48d to 3730cbc Compare April 1, 2026 03:20
@JamieMagee JamieMagee requested a review from Copilot April 1, 2026 03:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

test/dotnet.Tests/SdkVulnerabilityTests/GivenSdkReleaseMetadataCache.cs:140

  • This test sets the sentinel timestamp via File.SetLastAccessTime and DateTime.Now, but the implementation checks GetLastWriteTimeUtc against DateTime.UtcNow. To make the interval logic test meaningful and non-flaky, set LastWriteTimeUtc instead (and use UtcNow) so the 24h vs 1h assertions reflect the actual behavior.
        string sentinelPath = Path.Combine(cacheDir, ".sentinel");
        File.Create(sentinelPath).Close();
        // Set sentinel to 2 hours ago
        File.SetLastAccessTime(sentinelPath, DateTime.Now.AddHours(-2));

        // With default 24h interval, should not be due
        var cache24h = new SdkReleaseMetadataCache(cacheDir, _ => null);
        cache24h.IsDueForUpdate().Should().BeFalse();

        // With 1h interval, should be due
        var cache1h = new SdkReleaseMetadataCache(
            cacheDir,
            name => name == EnvironmentVariableNames.SDK_VULNERABILITY_CHECK_INTERVAL_HOURS ? "1" : null);
        cache1h.IsDueForUpdate().Should().BeTrue();

@JamieMagee JamieMagee force-pushed the jamieMagee/sdk-vulnerability-warnings branch from 3730cbc to fe95e84 Compare April 1, 2026 03:47
@JamieMagee JamieMagee requested a review from Copilot April 1, 2026 03:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 4 comments.

Comment thread src/Tasks/Microsoft.NET.Build.Tasks/CheckSdkVulnerabilities.cs Outdated
Comment thread src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs
Comment thread src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs
Comment thread src/Tasks/Common/Resources/xlf/Strings.de.xlf
@JamieMagee JamieMagee force-pushed the jamieMagee/sdk-vulnerability-warnings branch from fe95e84 to 49b724c Compare April 1, 2026 03:59
@JamieMagee JamieMagee requested a review from Copilot April 1, 2026 04:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.

Comment thread src/Tasks/Microsoft.NET.Build.Tasks/CheckSdkVulnerabilities.cs
@JamieMagee JamieMagee force-pushed the jamieMagee/sdk-vulnerability-warnings branch from 49b724c to 3c8028c Compare April 1, 2026 04:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 4 comments.

Comment thread src/Cli/dotnet/SdkVulnerability/SdkVulnerabilityNotifier.cs Outdated
Comment thread src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs Outdated
Comment thread src/Cli/dotnet/SdkVulnerability/SdkReleaseMetadataCache.cs
Comment thread src/Tasks/Microsoft.NET.Build.Tasks/CheckSdkVulnerabilities.cs Outdated
@JamieMagee JamieMagee force-pushed the jamieMagee/sdk-vulnerability-warnings branch from 3c8028c to ddb5b0a Compare April 1, 2026 04:42
@JamieMagee JamieMagee requested a review from Copilot April 1, 2026 04:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 1 comment.

<CheckSdkVulnerabilities Condition="'$(CheckSdkVulnerabilities)' == ''">false</CheckSdkVulnerabilities>
</PropertyGroup>

<Target Name="_CheckForSdkVulnerabilities" AfterTargets="_CheckForUnsupportedNETCoreVersion"
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

_CheckForSdkVulnerabilities is hooked with AfterTargets="_CheckForUnsupportedNETCoreVersion", but _CheckForUnsupportedNETCoreVersion only runs when TargetFrameworkIdentifier == .NETCoreApp (see Microsoft.NET.TargetFrameworkInference.targets). This means the SDK vulnerability/EOL check will never run for SDK-style projects targeting .NET Framework / non-.NETCoreApp TFMs, even when CheckSdkVulnerabilities=true. Consider attaching to a target that runs for all TFMs (e.g., BeforeTargets="PrepareForBuild"/CoreCompile, or AfterTargets="_CheckForInvalidConfigurationAndPlatform") so the opt-in warnings apply consistently.

Suggested change
<Target Name="_CheckForSdkVulnerabilities" AfterTargets="_CheckForUnsupportedNETCoreVersion"
<Target Name="_CheckForSdkVulnerabilities" AfterTargets="_CheckForInvalidConfigurationAndPlatform"

Copilot uses AI. Check for mistakes.
NETSDK1236 warns if the SDK has known CVEs. NETSDK1237 warns if it's
end-of-life. Both are opt-in: set <CheckSdkVulnerabilities>true</> in
your project or Directory.Build.props. <NoWarn> works too.

Release metadata comes from the same releases-index.json that
dotnet sdk check uses. The CLI fetches it in the background during
restore and writes it to ~/.dotnet/sdk-vulnerability-cache/ with a 24h
refresh sentinel. The MSBuild task reads from cache only, never hits
the network. No cache file means no warnings, so air-gapped and
source-build environments are fine.

DOTNET_SDK_VULNERABILITY_CHECK_DISABLE and
DOTNET_SDK_VULNERABILITY_CHECK_INTERVAL_HOURS control the behavior.

Closes dotnet#49552
@JamieMagee JamieMagee force-pushed the jamieMagee/sdk-vulnerability-warnings branch from 75f065a to 74bea5d Compare April 21, 2026 20:49
@JoeRobich
Copy link
Copy Markdown
Member

Thinking of the offline install scenario, could the SDK installer ship a copy of the vulnerability data and update the local cache if it is newer?

@JamieMagee
Copy link
Copy Markdown
Member Author

@JoeRobich Interesting idea, but I think there's a timing problem. The 11.0.100 installer is built when 11.0.100 is the latest release, so the bundled releases.json would show it as current with zero CVEs. Vulnerabilities get disclosed later in 11.0.101 or 11.0.102, and by then the 11.0.100 installer has already shipped. Installers aren't rebuilt after the fact, so the bundled data would be out of date in exactly the scenarios you'd want it for.

The current design already handles offline. No cache file means no warnings, no errors, just a no-op.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Infrastructure sdk-diagnostic-docs-needed Indicates that a PR introduces new diagnostic codes, which must be documented over at dotnet/docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dotnet should warn if a vulnerable SDK version is resolved

5 participants