Repository navigation
feat(ENG-13680): add a NuGet credential provider - #458
cloudsmith-iduffy with Copilot wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Plaintext credential exposure, protocol deadline failures, and lost credential configuration remain unresolved.
Review effort: Balanced
Findings: 2
Open (6)
Resolve credentials after handshake to avoid NuGet timeout · New Reject non-HTTPS feeds before sending credentials · New Use fullmatch to reject trailing newlines in workspace values · New Preserve launcher profile and configuration selectors · New Recognize cached custom feeds without requiring credentials · New Make custom-domain discovery asynchronous and cancellation-aware · New
What changed in this PR
Adds a NuGet credential provider that reuses CLI credentials without storing tokens in nuget.config.
Changes:
- Implements plugin authentication and Cloudsmith feed matching.
- Adds launcher installation, status, and removal.
- Adds unit/live tests, CI setup, and documentation.
| File | Description |
|---|---|
| cloudsmith_cli/credential_helpers/nuget/runtime.py | Implements protocol and credential responses. |
| cloudsmith_cli/credential_helpers/nuget/installer.py | Manages launchers and trusted domains. |
| cloudsmith_cli/credential_helpers/nuget/__init__.py | Exports runtime entry point. |
| cloudsmith_cli/credential_helpers/launchers.py | Supports configurable Windows extensions. |
| cloudsmith_cli/cli/tests/test_credential_helper_nuget.py | Tests runtime, command, and installer. |
| cloudsmith_cli/cli/tests/commands/test_credential_helper_nuget_integration.py | Adds live restore and custom-domain checks. |
| cloudsmith_cli/cli/commands/credential_helper/nuget.py | Connects CLI credentials to the protocol. |
| cloudsmith_cli/cli/commands/credential_helper/manage.py | Registers NuGet installation management. |
| cloudsmith_cli/cli/commands/credential_helper/__init__.py | Registers the NuGet command. |
| CHANGELOG.md | Documents the new provider. |
| .github/workflows/test.yml | Configures .NET for integration tests. |
| .env.example | Documents optional NuGet test settings. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@copilot Fix the code for all comments in this review thread. When a review comment includes a suggested change, apply the suggestion exactly. Do not make changes beyond what is described in the linked review thread. |
Addressed all six review threads in e398c8f. |
c33f5a9 to
c74e690
Compare
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The security-sensitive, cross-platform provider has unresolved blockers and only Linux end-to-end coverage.
Review effort: Balanced
Findings: 2
Open (3)
Resolved since last review (6)
Reject non-HTTPS feeds before sending credentials Resolve credentials after handshake to avoid NuGet timeout Make custom-domain discovery asynchronous and cancellation-aware Recognize cached custom feeds without requiring credentials Preserve launcher profile and configuration selectors Use fullmatch to reject trailing newlines in workspace values
1b2f276 to
6a89ccf
Compare
credential-helper nuget)d948a88 to
49dfad5
Compare
c86b2bc to
e0477da
Compare
96771cd to
6649497
Compare
342d3b5 to
31f8df3
Compare
`cloudsmith credential-helper install nuget` writes a `nuget-plugin-cloudsmith` launcher (`.bat` on Windows) on PATH. NuGet 6.13+ finds it and speaks the cross-platform plugin protocol to `cloudsmith credential-helper nuget`. The plugin answers for Cloudsmith hosts and the NuGet domains of the Workspace that is written into the launcher. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
31f8df3 to
091ed1c
Compare


Description
Adds a NuGet credential provider (ENG-13680). After a one-off install,
dotnet restore, MSBuild and Visual Studio authenticate to Cloudsmith feeds with the CLI's own credentials: an API key, a SAML session from the keyring, or OIDC. No token goes intonuget.config.Install.
credential-helper install nugetwrites anuget-plugin-cloudsmithlauncher next tocloudsmith, as a.baton Windows. NuGet 6.13 and later (.NET SDK 9.0.200+) finds anynuget-plugin-*executable on PATH, so no NuGet config changes.NuGet often runs the plugin from an IDE or a build agent that does not have your shell's environment. So the launcher keeps the
CLOUDSMITH_*settings that you gave at install time as a flag or an environment variable: the Workspace, profile, config and credentials files, API host, TLS verification, OIDC and keyring settings. These values win over the runtime environment (ENG-15016). Secrets and the proxy URL are never written, because the launcher is world-readable. On Windows, the installer refuses a value that a batch file cannot hold safely.Runtime.
credential-helper nugetspeaks NuGet's cross-platform plugin protocol. NuGet asks every plugin about every feed:httpsfeed on*.cloudsmith.io,*.cloudsmith.comor a NuGet domain of the Workspace → Basictoken:<credential>.NotFound, so NuGet tries its other credential sources.Error, with a message that says how to authenticate.--domain <host>adds hosts that always match.--no-discoverturns off the domain lookup, so only those hosts and the Cloudsmith hosts match. Stdout carries only protocol messages, also under--debug.Changes outside NuGet.
core/api/init.pywrites its--debugcredential messages to stderr. One of them now says "SAML" instead of "SSO".launcher_arg()checks the standalone binary path withbatch_value. This also applies to the docker, pnpm, cargo and terraform.cmdlaunchers.resolve_credentialskeeps itsCredentialContextonoptsfor the retry.is_cloudsmith_domainpassesconfigure_apithrough, so the domain lookup keeps the CLI's proxy and TLS settings.Type of Change
Additional Notes
Testing. A temporary workflow ran a real
dotnet restore(SDK 10) on Linux, macOS and Windows, from both a pip install and the PyInstaller binary. It covered a standard feed, a custom domain found by the live lookup with--debugon, and--domain --no-discoverwith an unreachable API host. Each job checks that the restore fails with 401 without the plugin and succeeds with it. All 18 jobs passed: run 38967385354.I also ran
dotnet nuget pushanddotnet restorelocally against a private repo with only a SAML session. Both worked with no API key.Known limits.
credential-helper listcan say "not installed" after a default install. This affects every helper, and fix(ENG-15090): find default-installed launchers in credential-helper list #491 fixes it.NuGet's plugin timeouts are 30 seconds in
ProtocolConstants, not the 5 seconds in the Microsoft docs. Credentials resolve before the handshake, so the plugin needs no threads or progress messages.🤖 Generated with Claude Code