NuGet provider
Parses .NET NuGet project files and configuration on disk. Text-only
static analysis, no dotnet restore, no NuGet API access (offline
rules). Behind --resolve-remote, NUGET-008 queries
api.nuget.org for publish-time metadata and NUGET-009 queries the
OSV advisory database.
Producer workflow
# --nuget-path is auto-detected when Directory.Packages.props exists.
pipeline_check --pipeline nuget
pipeline_check --pipeline nuget --nuget-path ./src/
Supported file formats
| File | Parse shape |
|---|---|
*.csproj |
<PackageReference Include="..." Version="..." /> entries |
Directory.Packages.props |
Central package management (<PackageVersion> entries) |
packages.config |
Legacy format (<package id="..." version="..." />) |
NuGet.config |
Package sources and packageSourceMapping sections |
packages.lock.json |
SDK-generated lock file (resolved versions) |
bin/, obj/, and .nuget/ directories are skipped.
What it covers
19 checks · 0 have an autofix patch (--fix).
| Check | Title | Severity | Fix |
|---|---|---|---|
| NUGET-001 | Floating NuGet version range | MEDIUM | |
| NUGET-002 | Wildcard prerelease NuGet version | MEDIUM | |
| NUGET-003 | PackageReference missing explicit version | MEDIUM | |
| NUGET-004 | HTTP-only NuGet package source | HIGH | |
| NUGET-005 | Known-compromised NuGet package version | CRITICAL | |
| NUGET-006 | No NuGet lock file for reproducible restores | MEDIUM | |
| NUGET-007 | Multiple NuGet sources without packageSourceMapping | HIGH | |
| NUGET-008 | NuGet package published within the cooldown window | HIGH | |
| NUGET-009 | NuGet package has a known OSV advisory | CRITICAL | |
| NUGET-010 | NuGet.config stores a feed credential in plaintext | HIGH | |
| NUGET-011 | packageSourceMapping pattern is a global wildcard | HIGH | |
| NUGET-012 | NuGet.config does not enforce signatureValidationMode = require | HIGH | |
| NUGET-013 | dotnet-tools.json entry lacks a version pin | HIGH | |
| NUGET-014 | NuGet.config source URL embeds plaintext credentials | HIGH | |
| NUGET-015 | PackageReference VersionOverride defeats Central Package Management | MEDIUM | |
| NUGET-016 | Private feed without |
HIGH | |
| NUGET-017 | Public gallery active alongside a private feed, not disabled | HIGH | |
| NUGET-018 | Project runs build-time MSBuild logic at restore/build | HIGH | |
| NUGET-019 | signatureValidationMode=require with no trusted signers | HIGH |
NUGET-001: Floating NuGet version range
Fires when a <PackageReference> Version attribute contains a NuGet range interval ([1.0,2.0), (,2.0], etc.) or a bare * wildcard.
Recommended action
Replace NuGet floating version ranges ([1.0,), (,2.0), [1.0,2.0), *) with an exact version pin (<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />). Floating ranges let NuGet resolve any later version that fits the interval, so a compromised patch release reaches the build on the next restore without a project file change. Pair the pinned reference with a committed packages.lock.json (NUGET-006) for reproducible restores.
NUGET-002: Wildcard prerelease NuGet version
Fires when Version ends with -* or equals *-*.
Recommended action
Replace wildcard prerelease specifiers (*-*, 1.0.0-*) with an exact version pin including the prerelease tag (1.0.0-beta.1). The -* suffix tells NuGet to resolve the latest prerelease matching the prefix, so any newly published prerelease (including a malicious one) is pulled on the next restore. Prerelease packages are often less reviewed than stable releases, increasing the attack surface.
NUGET-003: PackageReference missing explicit version
Fires when a <PackageReference> omits the Version attribute and the project is not centrally managed.
Recommended action
Add an explicit Version attribute to every <PackageReference> element (<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />). Without one, NuGet resolves the latest available version at restore time, so a compromised release reaches the build unobserved. If your solution uses Central Package Management (Directory.Packages.props), this rule is skipped because versions are governed centrally.
NUGET-004: HTTP-only NuGet package source
Fires when a <packageSources> entry in NuGet.config uses an http:// URL.
Recommended action
Change every <add key="..." value="http://..." /> package source in NuGet.config to https://. Plaintext-HTTP sources let a network attacker swap downloaded packages in flight (the canonical supply-chain MITM). If your internal feed has a self-signed certificate, install the CA into the build agent's trust store instead of falling back to HTTP.
NUGET-005: Known-compromised NuGet package version
Fires when a PackageReference pins to a version in the curated compromised-package registry.
Recommended action
Rotate every secret reachable to any process that ran dotnet restore against this project while the compromised version was installed. Bump the affected PackageReference to a post-incident clean version announced in the citing advisory, regenerate the lock file, and audit CI build logs for the exfiltration shape the advisory documents. Pair with NUGET-006 (lock file for reproducible restores) so a re-publish at the same version literal is caught by the content hash mismatch.
NUGET-006: No NuGet lock file for reproducible restores
Fires when a csproj project exists but no packages.lock.json was found.
Recommended action
Enable NuGet lock files by setting <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> in the csproj (or Directory.Build.props for solution-wide coverage) and commit the generated packages.lock.json. In CI, restore with dotnet restore --locked-mode so the build fails if the lock file disagrees with the project file. Without a lock file, dotnet restore silently upgrades transitive dependencies to whatever the feed currently serves.
NUGET-007: Multiple NuGet sources without packageSourceMapping
Fires when NuGet.config has more than one package source and no packageSourceMapping section.
Recommended action
Add a <packageSourceMapping> section to NuGet.config that maps each package pattern to its intended source. Without source mapping, NuGet queries every configured source for every package and installs the highest version found across all of them, the exact shape exploited by dependency confusion attacks. Source mapping pins each package namespace to one feed so a malicious publication on a secondary feed is never considered.
NUGET-008: NuGet package published within the cooldown window
Network-dependent: needs --resolve-remote to populate publish timestamps from api.nuget.org. Passes silently when the flag is off.
Recommended action
Pin to a version published before the cooldown window, or wait until the cooldown has elapsed. Most publisher-account compromises are detected within hours-to-days of publication.
NUGET-009: NuGet package has a known OSV advisory
Network-dependent: needs --resolve-remote to query the OSV advisory database. Passes silently when the flag is off.
Recommended action
Upgrade to a patched version or remove the affected package. Consult the advisory URL for remediation guidance.
NUGET-010: NuGet.config stores a feed credential in plaintext
Fires when a NuGet.config carries a <packageSourceCredentials> block whose per-source entry includes an <add key="ClearTextPassword" value="..." /> element. The key match is case-insensitive (NuGet itself treats it that way). The rule does NOT read or echo the literal credential value — findings only name the source the credential is bound to so secrets aren't laundered into reports.
An encrypted <add key="Password" .../> entry is the DPAPI-encrypted form NuGet writes when you run nuget sources update -username ... -password ... on Windows. That key is NOT flagged here — its value is unreadable without the original user's key material. The rule's surface is specifically the ClearTextPassword key, which stores the literal credential in committable plaintext.
Note: a session-scoped NuGet.config written by the build script (never committed) can legitimately use ClearTextPassword to pass a token from an environment variable to dotnet restore. If you scan a tree that contains such a file, suppress on the specific path and rule pair with a rationale; the rule has no way to tell a build-script-generated config apart from a hand-committed one.
Known false-positive modes
- Build-script-generated
NuGet.configfiles written into a workspace at job time legitimately useClearTextPasswordbecause the file isn't committed. The rule can't distinguish those from a checked-in config; suppress with a rationale on the specific path.
Seen in the wild
- NuGet credentials in repo history have driven multiple incidents where a private feed token leaked via a
NuGet.configcommitted to a public mirror or to an open-source release branch; once in git history, the credential is recoverable forever (even after deletion).
Recommended action
Remove the <add key="ClearTextPassword" .../> element from <packageSourceCredentials>. If the feed needs auth for the build, use an environment variable reference (%FEED_PASSWORD% on the value of an encrypted <add key="Password" ...> entry, populated at job time) or NuGet's encrypted-credential workflow (nuget sources update -username ... -password ..., which writes the DPAPI-encrypted Password key on Windows). On Linux / macOS where DPAPI isn't available, inject the secret at build time via the NUGET_CREDENTIALS environment variable or a -StoredPasswordInClearText session-scoped source declared in the build script, never in a checked-in NuGet.config. After removal, rotate the credential — anyone with read access to the repo history has it.
NUGET-011: packageSourceMapping pattern is a global wildcard
Walks NuGet.config <packageSourceMapping> entries and fires when any <package pattern="..."> is a global wildcard. The recognized wildcard shapes:
*— match everything**— equivalent to*in NuGet pattern syntax
Prefix wildcards (Microsoft.*, Corp.*) are the intended use of <package pattern> — they map a package-name namespace to a specific source and don't trip this rule. The signal is specifically the unbounded global wildcard that turns the mapping into a no-op.
Distinct from NUGET-007 (no packageSourceMapping at all): this rule catches the case where mapping exists but is ineffective.
Known false-positive modes
- Some workspaces use a global
*deliberately to route all packages through a single internal mirror that does its own dependency-confusion screening. The rule still fires because the mapping itself doesn't carry the screening guarantee. Suppress per config with a one-line rationale naming the mirror's policy.
Seen in the wild
- Pattern in .NET monorepos that adopt
<packageSourceMapping>as a quick fix during a dependency-confusion incident response: the initial mapping uses*to avoid breaking existing restore paths, the cleanup pass that replaces it with explicit prefixes never lands. The mapping looks present at audit time but provides no real gating.
Recommended action
Replace the * (or other broadly-matching wildcard) pattern with explicit package-name prefixes so each package is routed to the source the team has chosen for it. The point of <packageSourceMapping> is to gate every package against a single trusted source per namespace; a * catch-all defeats the gate and lets any package — including dependency-confusion typo-squats — flow through whichever source happens to win the race.
Example for an internal package convention:
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="Newtonsoft.Json" />
<package pattern="Microsoft.*" />
</packageSource>
<packageSource key="corp-nexus">
<package pattern="Corp.*" />
<package pattern="Internal.*" />
</packageSource>
</packageSourceMapping>
Every package now maps to exactly one source via longest-prefix match. A typo-squat that doesn't match a known prefix is rejected at restore time.
NUGET-012: NuGet.config does not enforce signatureValidationMode = require
Reads each NuGet.config's <config> block for signatureValidationMode. Fires when the key is absent (default is accept) or set to anything other than require (case-insensitive). The rule does NOT verify that <trustedSigners> is populated when require is set; a follow-up rule can audit the signers' completeness.
Distinct from NUGET-010 (cleartext credentials) and NUGET-007 (package-source mapping): those audit credential and routing posture; this rule audits the integrity-verification posture at install time.
Known false-positive modes
- Internal-only NuGet feeds where every package is trusted by the workspace's perimeter posture (a single internal Nexus that the operator controls end-to-end) may legitimately accept unsigned packages. Suppress per config with a one-line rationale; production-facing workspaces should require signatures.
Seen in the wild
- .NET supply-chain compromise pattern: a popular package is published with a slight name variant via a compromised maintainer account. The original package is signed; the variant isn't. Consumers with
signatureValidationMode=acceptinstall both without distinction;requiremode rejects the unsigned variant at restore time.
Recommended action
Set signatureValidationMode to require in NuGet.config and add at least one <trustedSigners> entry naming the authors / repositories whose packages the project will accept:
<config>
<add key="signatureValidationMode" value="require" />
</config>
<trustedSigners>
<author name="microsoft">
<certificate fingerprint="<sha256-of-cert>"
hashAlgorithm="SHA256"
allowUntrustedRoot="false" />
</author>
<repository name="nuget.org" serviceIndex="https://api.nuget.org/v3/index.json">
<certificate fingerprint="<sha256-of-cert>"
hashAlgorithm="SHA256"
allowUntrustedRoot="false" />
</repository>
</trustedSigners>
With require, NuGet rejects any package whose signature doesn't validate against a trusted-signers entry — closing the substitution surface that transport-only verification leaves open. The default (accept) verifies signatures when present but happily accepts unsigned packages, which means a compromised mirror serving unsigned drop-ins isn't rejected at restore time.
NUGET-013: dotnet-tools.json entry lacks a version pin
Reads every .config/dotnet-tools.json (and root-level dotnet-tools.json) under the scan path and walks the tools object. Fires for any entry whose value is either:
- a dict without a
versionkey, or - a dict with
versionset to an empty string
Wildcard / range version specs ("*", "8.0.*") are also flagged because they resolve at restore time to the registry's current content.
Known false-positive modes
- Some templating projects emit a
dotnet-tools.jsonwith no version field so the user picks a tool version at first use. The rule still fires; suppress per file with a one-line rationale, or — better — fill in the version once the project's tool requirements stabilize.
Seen in the wild
- Pattern of .NET tool-manifest compromise: a popular tool ships a poisoned patch release; every consumer running
dotnet tool restorewith a manifest that doesn't pin the version picks up the bad binary automatically. The binary's install hook runs in the developer's shell with their local credentials.
Recommended action
Add an explicit version field to every tool entry in .config/dotnet-tools.json:
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.10",
"commands": ["dotnet-ef"]
}
}
}
Tools listed in the manifest are restored by dotnet tool restore, which executes the tool's binary on first invocation. Without a version pin, the command resolves to whatever nuget.org is currently publishing under the tool's name — including a poisoned patch release that runs in the developer's shell or the CI runner with whatever credentials those environments carry.
Mirrors NUGET-001 (PackageReference floating version) but for the tool-manifest surface: tools execute on every developer's machine, while packages typically execute only when the application that consumes them runs.
NUGET-014: NuGet.config source URL embeds plaintext credentials
Reads each NuGet.config <packageSources> entry and fires when the URL embeds a user:pass@ authority component. Empty-password forms (https://user:@host) and ${env:VAR} placeholders are skipped — the former is operator-flagged 'no credential intended' and the latter resolves at restore time from the environment.
Distinct from NUGET-010 (cleartext password in <packageSourceCredentials>) and NUGET-004 (HTTP scheme): those audit credential and transport posture in their canonical NuGet locations. This rule catches the URL-embedded shape, which is the most common developer mistake when adding a private feed manually.
Known false-positive modes
- Templated NuGet.config files that materialize a placeholder credential form (
https://__USER__:__TOKEN__@host) and substitute the real value at build time trip this rule by shape. Suppress per config when the placeholder marker is stable; the rule's placeholder skip-list only recognizes${env:VAR}and${VAR}.
Seen in the wild
- Pattern across .NET enterprise repositories: a contributor pastes a Nexus feed URL with embedded credentials into NuGet.config during a quick test, intends to replace it before commit, the replacement never happens. The credential persists in git history after the fact even if the next commit cleans the file.
Recommended action
Move the credential out of the URL and into the <packageSourceCredentials> section using the encrypted-password form. The recommended flow:
- Run
dotnet nuget add source <url> --username <user> --password <pass> --store-password-in-clear-text=falseon the runner. NuGet stores the credential using the platform's secure-storage API (DPAPI on Windows, keychain on macOS, libsecret on Linux) and writes an encrypted form into the user-level NuGet.config. - For CI, inject the credential at restore time from the secret manager:
dotnet nuget add source ... --password ${env:NUGET_TOKEN}is expanded only at execution time, the literal credential never lives in the project's NuGet.config. - If the source must live in the project NuGet.config for portability, use only the credential-free URL (
https://nexus.corp/repo) and rely on the consumer's user-level config (where credentials are encrypted) for authentication.
NUGET-015: PackageReference VersionOverride defeats Central Package Management
Re-parses each .csproj and walks <PackageReference> entries for the VersionOverride attribute. Fires when the project participates in Central Package Management (i.e. NuGetProject.is_central_managed is true) AND any VersionOverride is set.
Skips projects that don't participate in CPM — those use Version directly on every PackageReference, and the VersionOverride attribute is a no-op there. The audit anchor is specifically the case where CPM is in force and a project punches a hole through it.
Known false-positive modes
- Some workspaces use
VersionOverrideto selectively test a newer version of a single package in one project before promoting it toDirectory.Packages.props. The rule still fires; suppress per project / per package with a one-line rationale naming the test and the planned promotion milestone.
Seen in the wild
- Pattern in long-lived .NET monorepos that adopt CPM during a posture cleanup but never police
VersionOverrideusage afterward: individual projects accumulate stale overrides for packages whose central version has since moved on, creating a hidden multi-version graph that defeats the single-version-per-package invariant CPM is meant to guarantee.
Recommended action
Remove the VersionOverride attribute and pin the central version instead — update Directory.Packages.props if the override was meant to bump every consumer, or scope the override to a child Directory.Packages.props if only a subtree of the workspace needs the bump. The point of Central Package Management is to keep one version per package across the workspace; per-project VersionOverride punches through that contract and lets individual .csproj files drift away from the central pin silently.
Two stable remediation patterns:
- If the override exists because one project needs a newer version, accept the bump everywhere: update
Directory.Packages.propsto the new version and delete the override. - If only a subtree of the workspace can take the new version, scope it with a nested
Directory.Packages.propsin the subtree's directory; CPM honors the closest parent.
NUGET-016: Private feed without inherits the public gallery
Fires when a NuGet.config declares at least one non-nuget.org package source and its <packageSources> block has no <clear /> element. The rule re-reads the file to detect <clear /> (the loader keeps only <add> entries). A source counts as the public gallery when its URL contains nuget.org; anything else (an internal Nexus / Artifactory / Azure Artifacts feed, a local folder) is treated as a private feed whose names a public package could shadow.
Distinct from NUGET-007 (multiple sources without packageSourceMapping): NUGET-007 only fires when one config enumerates two or more sources, so it structurally misses the common shape this rule catches, a config that lists only the internal feed while nuget.org leaks in through config inheritance. Microsoft's "3 Ways to Mitigate Risk Using Private Package Feeds" names <clear/> as the fix.
Known false-positive modes
- A repo whose only source is an internal mirror that itself proxies and screens nuget.org may accept the inherited gallery deliberately. The rule still fires because the config text alone can't prove the mirror screens for dependency confusion. Suppress per config with a one-line rationale naming the mirror's policy.
Seen in the wild
- Birsan 2021 dependency-confusion research: internal package names resolved against the public registry because the public feed stayed active alongside the private one. The .NET face of the attack is a NuGet.config that adds a private feed without
<clear/>, leaving nuget.org in the resolution set so a public package with the internal name and a higher version is installed.
Recommended action
Add a <clear /> element as the first child of <packageSources> in NuGet.config, then list every source the project is allowed to use explicitly:
<packageSources>
<clear />
<add key="internal" value="https://nuget.corp.local/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
NuGet merges packageSources across the machine, user, and repo configs, so a repo config that lists only the internal feed still resolves nuget.org (added by the machine-level default config). Because NuGet installs the highest version found across every active source, a public package that shadows an internal name can win the race. <clear /> discards the inherited sources so only the ones you list apply. Pair it with <packageSourceMapping> (see NUGET-007) to pin each namespace to one feed.
NUGET-017: Public gallery active alongside a private feed, not disabled
Fires when a NuGet.config (1) lists at least one private (non-nuget.org) source, (2) lists the public gallery as an explicit active <add> source, and (3) does NOT disable that gallery key in <disabledPackageSources>. The rule re-reads the file to collect the truthy <disabledPackageSources> keys (the loader doesn't surface them).
Companion to NUGET-016, scoped to the complementary mitigation: NUGET-016 owns the inheritance case (only the private feed listed, no <clear/>, so nuget.org leaks in from the machine config), while this rule owns the explicit-coexistence case (both feeds listed, the gallery not disabled). A config that uses <clear/> and then re-adds nuget.org passes NUGET-016 but still trips this rule, the gallery is active. A config that absent both mitigations legitimately trips both.
Known false-positive modes
- A repo that deliberately consumes public packages from
nuget.orgalongside its private feed, and pins names withpackageSourceMapping(NUGET-007) so confusion can't occur, may keep the gallery active by design. The rule doesn't read the mapping coverage; suppress per config with a rationale once the namespace pinning is confirmed.
Seen in the wild
- Birsan 2021 dependency-confusion research. The .NET face is a NuGet.config that keeps
nuget.orgactive next to a private feed without disabling it or pinning namespaces, so a public package with an internal name and a higher version wins the highest-version-wins restore.
Recommended action
When a NuGet.config lists both a private feed and nuget.org as active sources, disable the public gallery for restore unless you genuinely consume public packages from it, or pin every namespace to one feed with <packageSourceMapping> (NUGET-007). The targeted fix is a <disabledPackageSources> entry:
<disabledPackageSources>
<add key="nuget.org" value="true" />
</disabledPackageSources>
With the gallery active, NuGet's highest-version-wins resolution lets a public package that shadows an internal name win the restore, the Birsan dependency-confusion vector. packageSourceMapping is the strongest control (each name resolves from exactly one feed); disabling the gallery is the blunt instrument when no public package is needed.
NUGET-018: Project runs build-time MSBuild logic at restore/build
Re-reads each *.csproj and fires on two high-signal shapes of build-time code execution:
- an
<Exec>task nested in a<Target>whoseBeforeTargetsorAfterTargetsnames a build / restore phase (Build,Restore,Compile,Pack,Publish, and the common pre/post hooks), so the command runs automatically; and - an
<Import>whoseProjectreferences a generated package path property ($(Pkg...)), which pulls a package'sbuild/MSBuild logic into the build.
The rule inspects structure, not command content, so a legitimate codegen <Exec> is flagged too (see the known false-positive note). packages.config projects and non-.csproj inputs are skipped.
Known false-positive modes
- Many projects use a build-phase
<Exec>for legitimate codegen (T4, protobuf, a version-stamp script). The rule flags the execution surface, not malice, since the command string alone can't be trusted to stay benign. Review the command; if it's a trusted in-repo script, suppress per project with a one-line rationale.
Seen in the wild
- MSBuild build-time execution is the .NET parallel of the npm lifecycle-script attack class: a package ships
build/<id>.props/.targetsthat MSBuild auto-imports, or a project carries aBeforeTargets="Build"<Exec>, so attacker-controlled commands run during a routine restore / build with the runner's credentials.
Recommended action
Move build-time shell-outs out of the project file, or gate them behind an explicit, reviewed opt-in. Two shapes trip this rule:
-
An
<Exec>task in a<Target>wired to a build / restore phase viaBeforeTargets/AfterTargetsruns an arbitrary command on every build, in the developer shell and the CI runner with whatever credentials those carry. Prefer a checked-in, reviewed build script invoked explicitly over an auto-running<Exec>; if codegen is unavoidable, pin the tool version and review the command. -
A
PackageReferencewithGeneratePathProperty="true"feeding an<Import Project="$(Pkg...)\build\..." />auto-imports a package's MSBuild.props/.targets(the .NET analog of an npmpostinstall). Remove the manual import, or vet the package'sbuild/payload and pin it by version.
The point is that nothing in a package restore or a routine dotnet build should be able to execute attacker-controlled host commands without a human having reviewed exactly what runs.
NUGET-019: signatureValidationMode=require with no trusted signers
The follow-up to NUGET-012. NUGET-012 fires when signatureValidationMode is not require; this rule fires for the opposite, narrower case: the mode IS require but <trustedSigners> is missing or carries no <certificate> under any <author> / <repository> entry. The rule re-reads the file to inspect <config> and <trustedSigners>. When the mode is anything other than require the rule passes and leaves the finding to NUGET-012.
Known false-positive modes
- A config that inherits
<trustedSigners>from a machine-level or parentNuGet.configlooks empty here but validates correctly at restore time. The rule reads a single file, so it can't see inherited signers. Suppress per config with a one-line rationale pointing at the parent config that supplies the signers.
Seen in the wild
- .NET supply-chain hardening guidance: teams enable
signatureValidationMode=requireexpecting it to reject unsigned or untrusted packages, but without a populated<trustedSigners>list the setting has no trust anchor to enforce against, so the protection is silently a no-op.
Recommended action
When signatureValidationMode is require, add at least one <trustedSigners> entry with a certificate so there is something to validate signatures against:
<config>
<add key="signatureValidationMode" value="require" />
</config>
<trustedSigners>
<repository name="nuget.org"
serviceIndex="https://api.nuget.org/v3/index.json">
<certificate fingerprint="<sha256-of-cert>"
hashAlgorithm="SHA256"
allowUntrustedRoot="false" />
</repository>
</trustedSigners>
require only rejects untrusted packages when there is a populated signer list to validate against. With require set but <trustedSigners> empty or absent, NuGet has no anchor to check signatures against, so the integrity guarantee the mode is supposed to provide doesn't actually hold.
Adding a new NuGet check
- Create a new module at
pipeline_check/core/checks/nuget/rules/NNN_<name>.pyexporting a top-levelRULE = Rule(...)and acheck(path, doc) -> Findingfunction. The orchestrator auto-discoversRULEand callscheckwith the parsed YAML document. - Add a mapping for the new ID in
pipeline_check/core/standards/data/owasp_cicd_top_10.py(and any other standard that applies). - Drop unsafe/safe snippets at
tests/fixtures/per_check/nuget/-NNN.{unsafe,safe}.ymland add aCheckCaseentry intests/test_per_check_real_examples.py::CASES. - Regenerate this doc: