Skip to content

feat: support .slnx solution files on --file - #7181

Open
jsutherland-snyk wants to merge 1 commit into
mainfrom
feat/slnx-cli-support
Open

feat: support .slnx solution files on --file#7181
jsutherland-snyk wants to merge 1 commit into
mainfrom
feat/slnx-cli-support

Conversation

@jsutherland-snyk

@jsutherland-snyk jsutherland-snyk commented Aug 27, 2026

Copy link
Copy Markdown

Pull Request Submission Checklist

  • Follows CONTRIBUTING guidelines
  • Commit messages are release-note ready
  • Includes detailed description of changes
  • Contains risk assessment
  • Highlights breaking API changes (none)
  • Links to automated tests covering new functionality
  • Includes manual testing instructions
  • Updates relevant GitBook documentation (PR link: not yet — see below)
  • Includes product update to be announced in the next stable release notes

What does this PR do?

Makes --file=*.slnx work the way --file=*.sln already does.

--file=*.sln is a target selector, not a manifest: the CLI parses the solution, keeps the projects whose folders hold a supported manifest, deletes --file, and rewrites argv as if the user had passed those folders. It sits between one .csproj and --all-projects.

.slnx — the XML solution format the .NET SDK reads from 9.0.200 and creates by default from .NET 10 — was not recognised at all. It fell through to package-manager detection and failed with "could not detect package manager". Customers converting .sln.slnx as part of a .NET 9/10 upgrade hit this and their pipelines break.

Ticket: CMPA-766.

Where should the reviewer start?

src/lib/sln/index.ts. Three things worth a careful look:

  1. No new dependency. Project paths are matched out of the XML with a regex after stripping comments, rather than pulling an XML parser into the CLI bundle. Reviewed adversarially against a 45-input differential vs a real XML parser: the divergences that exist are only reachable by inputs no .NET tool produces (CDATA, processing instructions, namespaced elements, duplicate attributes). Verified identical on real dotnet sln migrate output, including a directory name containing &.
  2. .sln folder resolution is deliberately untouched. The two formats disagree about a project path written with a trailing separator: in .slnx it names a folder, while a .sln ASP.NET Website entry (..\..\WebSites\Site1\) has always resolved to the folder above. An earlier revision of this PR shared one code path and silently changed which folder a .sln scan covers — that would have added projects on monitor and vulns on test for existing users. Each format now has its own rule and sln-website-project/mySolution.sln pins the .sln one.
  3. solutionExtension() replaces path.extname so a file named only .slnx reaches the right parser, and so the unsupported-combination message matches the one cli-extension-os-flows produces for the same rejection.

Two deliberate, disclosed side-effects on .sln, both arguably fixes but both behaviour changes:

  • the extension is now matched case-insensitively, so --file=App.SLN is expanded where it previously fell through to manifest detection;
  • that error message now names the extension in lower case (file=*.SLNXfile=*.slnx).

How should this be manually tested?

cd test/fixtures/nuget-sln            # holds Service.sln and Service.slnx side by side
snyk test --file=Service.slnx         # same projects as --file=Service.sln
snyk sbom --format=cyclonedx1.4+json --file=Service.slnx
snyk test --file=Service.slnx --project-name=x   # rejected, naming .slnx

Automated coverage: test/jest/unit/lib/sln.spec.ts (28 tests — parsing, XML entity decoding including numeric character references, single-quoted attributes, empty solutions, path resolution relative to the solution, and the .sln regression above), plus acceptance tests in test/jest/acceptance/cli-args.spec.ts and test/jest/acceptance/snyk-sbom/nuget-options.spec.ts.

What's the product update that needs to be communicated to CLI users?

--file now accepts .slnx solution files as well as .sln, on snyk test, snyk monitor and snyk sbom. No feature flag or enablement needed.

Docs are not updated by this PR and are the only shipped surface still saying .sln onlyhelp/ is authored in GitBook, so help/cli-commands/{test,monitor,sbom}.md need the --file=<filename>.sln sections retitled and a minimum CLI version added. Worth noting the demand evidence includes an account that has not adopted .slnx because the documented support matrix says we don't support it, so the docs change carries most of the customer value here.

Risk assessment: Medium

Low in what it adds (.slnx was previously a hard failure, so there is no behaviour to regress), medium in where it sits: src/lib/sln/ is on the hot path for every --file=*.sln scan across all ecosystems, not just .NET. The two disclosed .sln side-effects above are the whole of the risk; the trailing-separator regression is fixed and pinned by a test.

Related PRs

The same gap in the two extensions, for reviewers tracing the whole feature:

This PR should land first. The unified resolver hands a solution it cannot fully claim back to the legacy CLI, which needs this change to understand .slnx.

🤖 Generated with Claude Code

`--file=*.sln` selects the projects a solution holds rather than scanning
the solution itself. `.slnx`, the XML solution format that replaces `.sln`
from Visual Studio 17.14 / .NET 9 onwards, was not recognised: it fell
through to package-manager detection and failed with "could not detect
package manager".

Customers converting `.sln` to `.slnx` as part of a .NET 9/10 upgrade hit
this and their pipelines break. CMPA-766.

Project paths are read out of the XML rather than with a new XML
dependency, and comments are stripped first so a commented-out project
isn't scanned.

`.sln` folder resolution is untouched. The two formats disagree about a
project path written with a trailing separator — `.slnx` allows it to name
the project's directory, while a `.sln` ASP.NET Website entry has always
resolved to the directory above — so each format keeps its own rule and
a test pins the `.sln` one.

Two deliberate, disclosed side-effects on `.sln`:
- the extension is now matched case-insensitively, so `--file=App.SLN` is
  expanded where it previously fell through to manifest detection;
- the unsupported-combination message names the extension in lower case,
  matching the message cli-extension-os-flows produces for the same
  rejection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@snyk-io

snyk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@snyk-pr-review-bot

Copy link
Copy Markdown

PR Reviewer Guide 🔍

🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Inconsistent Separator Check 🟡 [minor]

In slnxProjectFolder, the regex test /[\\/]$/.test(projectPath) is performed on the raw projectPath from the XML, but the variable normalised (which uses OS-specific separators) is defined immediately before. While both are checked in the regex, the subsequent slice(0, -1) is performed on normalised. If a path in the XML ends with a separator that matches the regex but isn't the primary OS separator, this logic holds, but it would be safer to check the normalized path to ensure consistent behavior across different environments and input styles.

return /[\\/]$/.test(projectPath)
  ? normalised.slice(0, -1)
📚 Repository Context Analyzed

This review considered 13 relevant code sections from 9 files (average relevance: 0.91)

🤖 Repository instructions applied (from AGENTS.md)

@github-actions

Copy link
Copy Markdown
Contributor
Warnings
⚠️

Since the CLI is unifying on a standard and improved tooling, we're starting to migrate old-style imports and exports to ES6 ones.
A file you've modified is using either module.exports or require(). If you can, please update them to ES6 import syntax and export syntax.
Files found:

  • src/cli/main.ts

Generated by 🚫 dangerJS against 3c878a7

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant