NuGet Packages: The Suppliers You Forgot to Audit
You wouldn’t let a stranger walk into your datacenter and install software on your production servers. Yet every time you execute dotnet add package, you’re doing exactly that: inviting third-party code into your application without verification, without approval, and often without even knowing what transitive dependencies tagged along for the ride.
ISO/IEC 27001 Control A.15.1 (Information security in supplier relationships) doesn’t care whether your “supplier” is a cloud vendor with million-dollar contracts or an open-source maintainer distributing NuGet packages from their basement. Both are suppliers. Both introduce supply chain risk. And both require the same systematic approach to security.
After seeing enterprise teams discover critical vulnerabilities in packages that had been sitting in production for years (packages they couldn’t even remember adding), I’ve learned that dependency management isn’t a developer convenience feature. It’s a fundamental security control that most organizations handle with alarming negligence.
The Fatal Pattern: NuGet as a Free-for-All
Here’s what I see in most .NET codebases when I perform security audits:
// ProjectA.csproj
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Serilog" Version="2.8.0" />
</ItemGroup>
// ProjectB.csproj
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="AutoMapper" Version="9.0.0" />
</ItemGroup>
// ProjectC.csproj
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Serilog" Version="2.10.0" />
</ItemGroup>
Three projects, three different versions of the same package, zero visibility into transitive dependencies, and absolutely no process for:
- Vulnerability scanning: Nobody knows which CVEs affect any of these versions
- Approval workflow: Developers add packages with
Install-Packageduring development - Version consistency: Each project picks its own version creating assembly binding nightmares
- Dependency tracking: What pulled in that obscure
System.Text.Encodings.Webversion? - Security patches: Versions pinned years ago, never updated, accumulating vulnerabilities
And the internal package repository? Configured like this:
<!-- nuget.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="internal" value="http://internal-nuget.company.local/nuget" />
</packageSources>
<!-- No authentication configured -->
<!-- No signature verification -->
<!-- No HTTPS enforcement -->
</configuration>
This violates ISO/IEC 27001 A.15.1.1 (Information security policy for supplier relationships) by failing to establish security requirements for third-party code. It violates A.15.1.3 (Supply chain security) by not monitoring the supply chain for security events. And it completely ignores A.18.2.3 (Technical compliance review) by skipping any technical verification of dependencies.
In practical terms: any developer can add any package from any source, those packages can pull in dozens of transitive dependencies, and nobody discovers the vulnerable packages until a security incident forces an emergency audit.
The Systematic Approach: Dependency Management as Security Control
ISO 27001 requires treating suppliers as part of your security perimeter. For NuGet packages, this means implementing controls at multiple levels:
1. Central Package Management (Directory.Packages.props)
First, enforce version consistency across your entire codebase:
<!-- Directory.Packages.props (at solution root) -->
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Serilog" Version="3.1.1" />
<!-- Version range: auto-update patches, block major versions -->
<PackageVersion Include="Azure.Identity" Version="[1.10.4,2.0)" />
</ItemGroup>
</Project>
Project files now reference packages without versions:
<!-- ProjectA.csproj -->
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Serilog" />
</ItemGroup>
Why this matters for ISO 27001: Control A.15.1.1 requires establishing security requirements. Central Package Management enforces a single source of truth for dependency versions, making vulnerability tracking and patching possible across your entire codebase.
Note the version range syntax [8.0.1,9.0) for security-critical packages. This allows NuGet to automatically pull patch versions (8.0.2, 8.0.3, etc.) that fix security vulnerabilities while preventing breaking changes from major version updates.
2. Automated Vulnerability Scanning in CI/CD
Every build must check for known vulnerabilities:
# .github/workflows/build.yml
name: Build with Security Checks
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Check for vulnerable packages
run: |
dotnet restore
dotnet list package --vulnerable --include-transitive
# Fails if vulnerabilities found
dotnet build /p:TreatWarningsAsErrors=true
This implements A.18.2.3 (Technical compliance review) by automatically verifying that dependencies meet security requirements before code reaches production.
3. GitHub Dependabot Configuration
Enable automated vulnerability alerts and patching:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"
groups:
security-updates:
update-types: ["security"]
reviewers:
- "security-team"
labels:
- "dependencies"
This addresses A.15.1.3 (Supply chain security) by continuously monitoring your supply chain for security events and automatically proposing remediation.
4. Package Source Authentication and Verification
Secure your package sources with authentication and signature verification:
<!-- nuget.config -->
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="Microsoft.*" />
<package pattern="System.*" />
</packageSource>
</packageSourceMapping>
<config>
<add key="signatureValidationMode" value="require" />
</config>
</configuration>
This configuration clears default sources and whitelists approved feeds, maps package patterns to specific sources (preventing substitution attacks), and enables signature verification.
5. Health Checks for Package Repository Availability
Your CI/CD pipeline depends on NuGet feeds being available. If nuget.org goes down during a critical deployment, you need to know. Add a simple health check to your monitoring:
// Check NuGet feed availability
var client = new HttpClient();
var response = await client.GetAsync("https://api.nuget.org/v3/index.json");
if (!response.IsSuccessStatusCode)
logger.LogWarning("NuGet feed unavailable");
This is critical for A.15.1.3 (Supply chain security): you need to know when your dependency supply chain is disrupted.
Regular Dependency Audits
Finally, schedule regular dependency reviews as part of your security process:
# Monthly security audit
dotnet list package --outdated
dotnet list package --vulnerable --include-transitive
dotnet list package --deprecated
Run this monthly and review results with your security team. This provides evidence for A.18.2.3 (Technical compliance review).
The Pragmatic Reality
I’ve seen teams spend months achieving ISO 27001 certification only to completely ignore Control A.15.1 when it comes to NuGet packages. The assumption seems to be that because packages are “just open source” or “from Microsoft,” they don’t count as suppliers.
They absolutely count. Every package is a supplier relationship. Every transitive dependency is a third-party component in your production system. And every vulnerability in those dependencies is your responsibility under ISO 27001.
The good news: modern tooling makes this manageable. Central Package Management eliminates version chaos. Dependabot provides automated monitoring. GitHub Actions enables vulnerability scanning without infrastructure investment. Package signing prevents tampering.
The bad news: none of this happens automatically. You must implement these controls deliberately, enforce them consistently, and audit them regularly.
Key Takeaways
Every NuGet package is a supplier relationship under ISO/IEC 27001 Control A.15.1, requiring security evaluation and monitoring.
Central Package Management is mandatory for any codebase with multiple projects. It’s the foundation for vulnerability tracking and consistent patching.
Fail builds on known vulnerabilities using
dotnet list package --vulnerablein CI/CD pipelines. Security issues should block deployment, not generate ignored warnings.Version ranges enable automatic security patches: use
[8.0.1,9.0)syntax for dependencies where security matters more than version stability.Package signature verification prevents supply chain attacks: configure trusted signers and require signature validation in nuget.config.
Automated scanning finds problems; human review approves solutions: let Dependabot detect issues, but require security team approval for dependency changes.
Monitor your supply chain availability: health checks for package repositories should be part of your operational monitoring.
Supply chain security isn’t a checkbox on a compliance form. It’s a systematic approach to managing the third-party code that forms 70-90% of modern applications. ISO 27001 provides the framework; NuGet tooling provides the implementation. Your job is to connect them before the next CVE announcement forces you to audit thousands of dependencies under emergency conditions.
After 15 years of seeing teams scramble to patch vulnerabilities in packages they didn’t know they had, I can promise you this: the time you invest in dependency management today will save you countless emergency responses tomorrow. And unlike most security theater, these controls actually work.

Comments