supply-chain-security

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Supply Chain Security

软件供应链安全

Comprehensive guidance for securing the software supply chain, including dependency management, SBOM generation, vulnerability scanning, and protection against supply chain attacks.
本指南全面介绍软件供应链安全防护方法,包括依赖项管理、SBOM生成、漏洞扫描以及供应链攻击防护等内容。

When to Use This Skill

何时使用本技能

  • Generating Software Bill of Materials (SBOM)
  • Implementing SLSA framework compliance
  • Setting up dependency vulnerability scanning
  • Protecting against dependency confusion attacks
  • Configuring lock files and integrity verification
  • Implementing code signing with Sigstore
  • Verifying software provenance
  • Evaluating project security with OpenSSF Scorecard
  • 生成软件物料清单(SBOM)
  • 实现SLSA框架合规
  • 搭建依赖项漏洞扫描机制
  • 防护依赖混淆攻击
  • 配置锁定文件与完整性验证
  • 使用Sigstore实现代码签名
  • 验证软件来源
  • 通过OpenSSF Scorecard评估项目安全性

Quick Reference

快速参考

Supply Chain Attack Types

供应链攻击类型

Attack TypeDescriptionPrevention
Dependency ConfusionAttacker publishes malicious package with internal package nameNamespace scoping, private registries
TyposquattingMalicious packages with similar names (
lodash
vs
1odash
)
Lockfiles, careful review, tools
Compromised MaintainerLegitimate package hijackedPin versions, verify signatures
Build System AttackCI/CD pipeline compromisedSLSA compliance, hermetic builds
Malicious DependencyNew dependency contains malwareSCA scanning, SBOM review
攻击类型描述防护措施
依赖混淆攻击者发布与内部包名相同的恶意包命名空间限定、私有注册表
包名仿冒恶意包使用相似名称(如
lodash
vs
1odash
锁定文件、仔细审核、工具检测
维护者账户被攻陷合法包的维护者账户被劫持固定版本、验证签名
构建系统攻击CI/CD流水线被攻陷遵循SLSA合规、封闭构建
恶意依赖项新增依赖项包含恶意代码SCA扫描、SBOM审核

SLSA Levels Quick Reference

SLSA等级快速参考

LevelRequirementsProtection
SLSA 1Documentation of build processBasic transparency
SLSA 2Authenticated provenance, hosted buildTampering after build
SLSA 3Hardened build platform, non-falsifiable provenanceTampering during build
SLSA 4Two-person review, hermetic buildsInsider threats
等级要求防护能力
SLSA 1记录构建流程基础透明度
SLSA 2可认证的来源、托管式构建防止构建后篡改
SLSA 3加固的构建平台、不可伪造的来源防止构建过程中篡改
SLSA 4双人审核、封闭构建防范内部威胁

Essential Tools by Ecosystem

各生态系统必备工具

EcosystemVulnerability ScanningLock FileSBOM Generation
npm/Node.js
npm audit
, Snyk
package-lock.json
@cyclonedx/cyclonedx-npm
Python
pip-audit
, Safety
requirements.txt
+ hashes,
poetry.lock
cyclonedx-python
Go
govulncheck
, Snyk
go.sum
cyclonedx-gomod
.NET
dotnet list package --vulnerable
packages.lock.json
CycloneDX
NuGet
Java/MavenOWASP Dependency-Check
pom.xml
with versions
cyclonedx-maven-plugin
Rust
cargo audit
Cargo.lock
cargo-cyclonedx
生态系统漏洞扫描锁定文件SBOM生成
npm/Node.js
npm audit
, Snyk
package-lock.json
@cyclonedx/cyclonedx-npm
Python
pip-audit
, Safety
requirements.txt
+ 哈希,
poetry.lock
cyclonedx-python
Go
govulncheck
, Snyk
go.sum
cyclonedx-gomod
.NET
dotnet list package --vulnerable
packages.lock.json
CycloneDX
NuGet
Java/MavenOWASP Dependency-Check
pom.xml
(带版本)
cyclonedx-maven-plugin
Rust
cargo audit
Cargo.lock
cargo-cyclonedx

SBOM (Software Bill of Materials)

SBOM(软件物料清单)

SBOM Formats

SBOM格式

FormatStandardBest For
CycloneDXOASISSecurity-focused, VEX support
SPDXLinux FoundationLicense compliance, legal
SWIDISO/IEC 19770-2Software asset management
格式标准适用场景
CycloneDXOASIS安全聚焦、支持VEX
SPDXLinux基金会许可证合规、法务场景
SWIDISO/IEC 19770-2软件资产管理

CycloneDX SBOM Generation

CycloneDX SBOM生成

Node.js:
bash
undefined
Node.js:
bash
undefined

Install CycloneDX CLI

Install CycloneDX CLI

npm install -g @cyclonedx/cyclonedx-npm
npm install -g @cyclonedx/cyclonedx-npm

Generate SBOM

Generate SBOM

cyclonedx-npm --output-file sbom.json cyclonedx-npm --output-file sbom.xml --output-format xml

**Python:**

```bash
cyclonedx-npm --output-file sbom.json cyclonedx-npm --output-file sbom.xml --output-format xml

**Python:**

```bash

Install CycloneDX

Install CycloneDX

pip install cyclonedx-bom
pip install cyclonedx-bom

Generate from requirements.txt

Generate from requirements.txt

cyclonedx-py requirements -i requirements.txt -o sbom.json --format json
cyclonedx-py requirements -i requirements.txt -o sbom.json --format json

Generate from Poetry

Generate from Poetry

cyclonedx-py poetry -o sbom.json --format json
cyclonedx-py poetry -o sbom.json --format json

Generate from pip environment

Generate from pip environment

cyclonedx-py environment -o sbom.json

**.NET:**

```bash
cyclonedx-py environment -o sbom.json

**.NET:**

```bash

Install CycloneDX tool

Install CycloneDX tool

dotnet tool install --global CycloneDX
dotnet tool install --global CycloneDX

Generate SBOM

Generate SBOM

dotnet CycloneDX myproject.csproj -o sbom.json -j

**Go:**

```bash
dotnet CycloneDX myproject.csproj -o sbom.json -j

**Go:**

```bash

Install cyclonedx-gomod

Install cyclonedx-gomod

go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest
go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest

Generate SBOM

Generate SBOM

cyclonedx-gomod mod -json -output sbom.json
undefined
cyclonedx-gomod mod -json -output sbom.json
undefined

SBOM in CI/CD

CI/CD中的SBOM集成

yaml
undefined
yaml
undefined

GitHub Actions - Generate and upload SBOM

GitHub Actions - Generate and upload SBOM

name: Generate SBOM on: release: types: [published]
jobs: sbom: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5
  - name: Generate SBOM
    uses: CycloneDX/gh-node-module-generatebom@v1
    with:
      output: sbom.json

  - name: Upload SBOM to release
    uses: actions/upload-release-asset@v1
    with:
      upload_url: ${{ github.event.release.upload_url }}
      asset_path: sbom.json
      asset_name: sbom.json
      asset_content_type: application/json

  - name: Submit to Dependency Track
    run: |
      curl -X POST \
        -H "X-Api-Key: ${{ secrets.DTRACK_API_KEY }}" \
        -H "Content-Type: multipart/form-data" \
        -F "project=${{ github.repository }}" \
        -F "bom=@sbom.json" \
        "${{ secrets.DTRACK_URL }}/api/v1/bom"
undefined
name: Generate SBOM on: release: types: [published]
jobs: sbom: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5
  - name: Generate SBOM
    uses: CycloneDX/gh-node-module-generatebom@v1
    with:
      output: sbom.json

  - name: Upload SBOM to release
    uses: actions/upload-release-asset@v1
    with:
      upload_url: ${{ github.event.release.upload_url }}
      asset_path: sbom.json
      asset_name: sbom.json
      asset_content_type: application/json

  - name: Submit to Dependency Track
    run: |
      curl -X POST \
        -H "X-Api-Key: ${{ secrets.DTRACK_API_KEY }}" \
        -H "Content-Type: multipart/form-data" \
        -F "project=${{ github.repository }}" \
        -F "bom=@sbom.json" \
        "${{ secrets.DTRACK_URL }}/api/v1/bom"
undefined

Vulnerability Scanning

漏洞扫描

npm/Node.js

npm/Node.js

bash
undefined
bash
undefined

Built-in audit

Built-in audit

npm audit npm audit --json > audit-results.json npm audit fix # Auto-fix where possible
npm audit npm audit --json > audit-results.json npm audit fix # Auto-fix where possible

Check for outdated packages

Check for outdated packages

npm outdated
npm outdated

Use better-npm-audit for CI

Use better-npm-audit for CI

npx better-npm-audit audit --level moderate
undefined
npx better-npm-audit audit --level moderate
undefined

Python

Python

bash
undefined
bash
undefined

pip-audit (recommended)

pip-audit (recommended)

pip install pip-audit pip-audit pip-audit --fix # Auto-fix pip-audit -r requirements.txt pip-audit --format json > audit.json
pip install pip-audit pip-audit pip-audit --fix # Auto-fix pip-audit -r requirements.txt pip-audit --format json > audit.json

Safety (alternative)

Safety (alternative)

pip install safety safety check safety check -r requirements.txt
undefined
pip install safety safety check safety check -r requirements.txt
undefined

.NET

.NET

bash
undefined
bash
undefined

Built-in vulnerability check

Built-in vulnerability check

dotnet list package --vulnerable dotnet list package --vulnerable --include-transitive
dotnet list package --vulnerable dotnet list package --vulnerable --include-transitive

Output as JSON for CI

Output as JSON for CI

dotnet list package --vulnerable --format json > vulnerabilities.json
undefined
dotnet list package --vulnerable --format json > vulnerabilities.json
undefined

Go

Go

bash
undefined
bash
undefined

govulncheck (official Go tool)

govulncheck (official Go tool)

go install golang.org/x/vuln/cmd/govulncheck@latest govulncheck ./... govulncheck -json ./... > vuln.json
undefined
go install golang.org/x/vuln/cmd/govulncheck@latest govulncheck ./... govulncheck -json ./... > vuln.json
undefined

Rust

Rust

bash
undefined
bash
undefined

cargo-audit

cargo-audit

cargo install cargo-audit cargo audit cargo audit --json > audit.json cargo audit fix # Auto-fix (with cargo-audit-fix)
undefined
cargo install cargo-audit cargo audit cargo audit --json > audit.json cargo audit fix # Auto-fix (with cargo-audit-fix)
undefined

Lock Files and Integrity

锁定文件与完整性验证

Lock File Best Practices

锁定文件最佳实践

csharp
using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;

/// <summary>
/// Lock file verification utilities for supply chain security.
/// </summary>
public static class LockFileVerification
{
    /// <summary>
    /// Verify npm package-lock.json integrity hashes.
    /// </summary>
    public static Dictionary<string, PackageIntegrityResult> VerifyNpmIntegrity(string packageLockPath)
    {
        var json = File.ReadAllText(packageLockPath);
        var lockData = JsonSerializer.Deserialize<NpmPackageLock>(json)!;

        var results = new Dictionary<string, PackageIntegrityResult>();

        foreach (var (name, info) in lockData.Packages ?? new())
        {
            if (string.IsNullOrEmpty(name)) continue;  // Root package

            if (!string.IsNullOrEmpty(info.Integrity))
            {
                var parts = info.Integrity.Split('-', 2);
                results[name] = new PackageIntegrityResult(
                    HasIntegrity: true,
                    Algorithm: parts[0]);
            }
            else
            {
                results[name] = new PackageIntegrityResult(HasIntegrity: false, Algorithm: null);
            }
        }

        return results;
    }

    /// <summary>
    /// Verify NuGet packages.lock.json integrity.
    /// </summary>
    public static Dictionary<string, PackageIntegrityResult> VerifyNuGetLockFile(string lockFilePath)
    {
        var json = File.ReadAllText(lockFilePath);
        var lockData = JsonSerializer.Deserialize<NuGetPackagesLock>(json)!;

        var results = new Dictionary<string, PackageIntegrityResult>();

        foreach (var (framework, dependencies) in lockData.Dependencies ?? new())
        {
            foreach (var (packageName, info) in dependencies)
            {
                var key = $"{packageName}@{info.Resolved}";
                results[key] = new PackageIntegrityResult(
                    HasIntegrity: !string.IsNullOrEmpty(info.ContentHash),
                    Algorithm: !string.IsNullOrEmpty(info.ContentHash) ? "SHA512" : null);
            }
        }

        return results;
    }
}

public sealed record PackageIntegrityResult(bool HasIntegrity, string? Algorithm);

public sealed record NpmPackageLock(
    [property: JsonPropertyName("packages")] Dictionary<string, NpmPackageInfo>? Packages);

public sealed record NpmPackageInfo(
    [property: JsonPropertyName("integrity")] string? Integrity);

public sealed record NuGetPackagesLock(
    [property: JsonPropertyName("dependencies")] Dictionary<string, Dictionary<string, NuGetDependencyInfo>>? Dependencies);

public sealed record NuGetDependencyInfo(
    [property: JsonPropertyName("resolved")] string? Resolved,
    [property: JsonPropertyName("contentHash")] string? ContentHash);
csharp
using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;

/// <summary>
/// Lock file verification utilities for supply chain security.
/// </summary>
public static class LockFileVerification
{
    /// <summary>
    /// Verify npm package-lock.json integrity hashes.
    /// </summary>
    public static Dictionary<string, PackageIntegrityResult> VerifyNpmIntegrity(string packageLockPath)
    {
        var json = File.ReadAllText(packageLockPath);
        var lockData = JsonSerializer.Deserialize<NpmPackageLock>(json)!;

        var results = new Dictionary<string, PackageIntegrityResult>();

        foreach (var (name, info) in lockData.Packages ?? new())
        {
            if (string.IsNullOrEmpty(name)) continue;  // Root package

            if (!string.IsNullOrEmpty(info.Integrity))
            {
                var parts = info.Integrity.Split('-', 2);
                results[name] = new PackageIntegrityResult(
                    HasIntegrity: true,
                    Algorithm: parts[0]);
            }
            else
            {
                results[name] = new PackageIntegrityResult(HasIntegrity: false, Algorithm: null);
            }
        }

        return results;
    }

    /// <summary>
    /// Verify NuGet packages.lock.json integrity.
    /// </summary>
    public static Dictionary<string, PackageIntegrityResult> VerifyNuGetLockFile(string lockFilePath)
    {
        var json = File.ReadAllText(lockFilePath);
        var lockData = JsonSerializer.Deserialize<NuGetPackagesLock>(json)!;

        var results = new Dictionary<string, PackageIntegrityResult>();

        foreach (var (framework, dependencies) in lockData.Dependencies ?? new())
        {
            foreach (var (packageName, info) in dependencies)
            {
                var key = $"{packageName}@{info.Resolved}";
                results[key] = new PackageIntegrityResult(
                    HasIntegrity: !string.IsNullOrEmpty(info.ContentHash),
                    Algorithm: !string.IsNullOrEmpty(info.ContentHash) ? "SHA512" : null);
            }
        }

        return results;
    }
}

public sealed record PackageIntegrityResult(bool HasIntegrity, string? Algorithm);

public sealed record NpmPackageLock(
    [property: JsonPropertyName("packages")] Dictionary<string, NpmPackageInfo>? Packages);

public sealed record NpmPackageInfo(
    [property: JsonPropertyName("integrity")] string? Integrity);

public sealed record NuGetPackagesLock(
    [property: JsonPropertyName("dependencies")] Dictionary<string, Dictionary<string, NuGetDependencyInfo>>? Dependencies);

public sealed record NuGetDependencyInfo(
    [property: JsonPropertyName("resolved")] string? Resolved,
    [property: JsonPropertyName("contentHash")] string? ContentHash);

pip with Hash Verification

带哈希验证的pip配置

text
undefined
text
undefined

requirements.txt with hashes (most secure)

requirements.txt with hashes (most secure)

requests==2.31.0
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
certifi==2024.2.2
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8
undefined
requests==2.31.0
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
certifi==2024.2.2
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8
undefined

Generate Hashes Automatically

自动生成哈希

bash
undefined
bash
undefined

pip-tools for hash generation

pip-tools for hash generation

pip install pip-tools
pip install pip-tools

Generate requirements with hashes

Generate requirements with hashes

pip-compile --generate-hashes requirements.in -o requirements.txt
pip-compile --generate-hashes requirements.in -o requirements.txt

Poetry with hash export

Poetry with hash export

poetry export --format requirements.txt --with-hashes > requirements.txt
undefined
poetry export --format requirements.txt --with-hashes > requirements.txt
undefined

Dependency Confusion Prevention

依赖混淆防护

Private Registry Configuration

私有注册表配置

npm (.npmrc):
ini
undefined
npm (.npmrc):
ini
undefined

Scope packages to private registry

Scope packages to private registry

@mycompany:registry=https://npm.mycompany.com/ //npm.mycompany.com/:_authToken=${NPM_TOKEN}
@mycompany:registry=https://npm.mycompany.com/ //npm.mycompany.com/:_authToken=${NPM_TOKEN}

Always use exact versions

Always use exact versions

save-exact=true

**Python (pip.conf):**

```ini
[global]
index-url = https://pypi.mycompany.com/simple/
extra-index-url = https://pypi.org/simple/
trusted-host = pypi.mycompany.com

[install]
save-exact=true

**Python (pip.conf):**

```ini
[global]
index-url = https://pypi.mycompany.com/simple/
extra-index-url = https://pypi.org/simple/
trusted-host = pypi.mycompany.com

[install]

Prefer private packages

Prefer private packages

prefer-binary = true

**Preventive Measures:**

```csharp
using System.Net.Http.Json;
using System.Text.Json.Serialization;

/// <summary>
/// Dependency confusion detection and prevention utilities.
/// </summary>
public sealed class DependencyConfusionChecker(HttpClient httpClient)
{
    /// <summary>
    /// Check if internal NuGet package names exist on nuget.org.
    /// </summary>
    public async Task<Dictionary<string, ConfusionCheckResult>> CheckNuGetConfusionAsync(
        IEnumerable<string> internalPackages,
        CancellationToken cancellationToken = default)
    {
        var results = new Dictionary<string, ConfusionCheckResult>();

        foreach (var package in internalPackages)
        {
            try
            {
                var response = await httpClient.GetAsync(
                    $"https://api.nuget.org/v3/registration5-semver1/{package.ToLowerInvariant()}/index.json",
                    cancellationToken);

                if (response.IsSuccessStatusCode)
                {
                    var registration = await response.Content.ReadFromJsonAsync<NuGetRegistration>(
                        cancellationToken: cancellationToken);

                    var latestVersion = registration?.Items?.LastOrDefault()?.Upper;

                    results[package] = new ConfusionCheckResult(
                        ExistsPublicly: true,
                        PublicVersion: latestVersion,
                        Risk: ConfusionRisk.High,
                        Recommendation: "Register placeholder on nuget.org or use package prefix reservation");
                }
                else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    results[package] = new ConfusionCheckResult(
                        ExistsPublicly: false,
                        PublicVersion: null,
                        Risk: ConfusionRisk.Low,
                        Recommendation: "Consider registering placeholder package");
                }
            }
            catch (Exception ex)
            {
                results[package] = new ConfusionCheckResult(
                    ExistsPublicly: false,
                    PublicVersion: null,
                    Risk: ConfusionRisk.Unknown,
                    Recommendation: $"Check failed: {ex.Message}");
            }
        }

        return results;
    }

    /// <summary>
    /// Generate placeholder .csproj for NuGet package reservation.
    /// </summary>
    public static string GeneratePlaceholderProject(
        string packageId,
        string description = "Internal package - not for public use")
    {
        return $"""
            <Project Sdk="Microsoft.NET.Sdk">
              <PropertyGroup>
                <TargetFramework>netstandard2.0</TargetFramework>
                <PackageId>{packageId}</PackageId>
                <Version>0.0.1</Version>
                <Description>{description}</Description>
                <Authors>Security Team</Authors>
                <PackageTags>placeholder;internal;reserved</PackageTags>
                <IncludeSymbols>false</IncludeSymbols>
                <IncludeSource>false</IncludeSource>
              </PropertyGroup>
            </Project>
            """;
    }
}

public sealed record ConfusionCheckResult(
    bool ExistsPublicly,
    string? PublicVersion,
    ConfusionRisk Risk,
    string Recommendation);

public enum ConfusionRisk { Low, Medium, High, Unknown }

public sealed record NuGetRegistration(
    [property: JsonPropertyName("items")] List<NuGetCatalogPage>? Items);

public sealed record NuGetCatalogPage(
    [property: JsonPropertyName("upper")] string? Upper);
prefer-binary = true
undefined

Code Signing with Sigstore

防护措施代码示例

Sigstore Overview

Sigstore provides keyless signing using OIDC identity:
bash
undefined
csharp
using System.Net.Http.Json;
using System.Text.Json.Serialization;

/// <summary>
/// Dependency confusion detection and prevention utilities.
/// </summary>
public sealed class DependencyConfusionChecker(HttpClient httpClient)
{
    /// <summary>
    /// Check if internal NuGet package names exist on nuget.org.
    /// </summary>
    public async Task<Dictionary<string, ConfusionCheckResult>> CheckNuGetConfusionAsync(
        IEnumerable<string> internalPackages,
        CancellationToken cancellationToken = default)
    {
        var results = new Dictionary<string, ConfusionCheckResult>();

        foreach (var package in internalPackages)
        {
            try
            {
                var response = await httpClient.GetAsync(
                    $"https://api.nuget.org/v3/registration5-semver1/{package.ToLowerInvariant()}/index.json",
                    cancellationToken);

                if (response.IsSuccessStatusCode)
                {
                    var registration = await response.Content.ReadFromJsonAsync<NuGetRegistration>(
                        cancellationToken: cancellationToken);

                    var latestVersion = registration?.Items?.LastOrDefault()?.Upper;

                    results[package] = new ConfusionCheckResult(
                        ExistsPublicly: true,
                        PublicVersion: latestVersion,
                        Risk: ConfusionRisk.High,
                        Recommendation: "Register placeholder on nuget.org or use package prefix reservation");
                }
                else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    results[package] = new ConfusionCheckResult(
                        ExistsPublicly: false,
                        PublicVersion: null,
                        Risk: ConfusionRisk.Low,
                        Recommendation: "Consider registering placeholder package");
                }
            }
            catch (Exception ex)
            {
                results[package] = new ConfusionCheckResult(
                    ExistsPublicly: false,
                    PublicVersion: null,
                    Risk: ConfusionRisk.Unknown,
                    Recommendation: $"Check failed: {ex.Message}");
            }
        }

        return results;
    }

    /// <summary>
    /// Generate placeholder .csproj for NuGet package reservation.
    /// </summary>
    public static string GeneratePlaceholderProject(
        string packageId,
        string description = "Internal package - not for public use")
    {
        return $"""
            <Project Sdk="Microsoft.NET.Sdk">
              <PropertyGroup>
                <TargetFramework>netstandard2.0</TargetFramework>
                <PackageId>{packageId}</PackageId>
                <Version>0.0.1</Version>
                <Description>{description}</Description>
                <Authors>Security Team</Authors>
                <PackageTags>placeholder;internal;reserved</PackageTags>
                <IncludeSymbols>false</IncludeSymbols>
                <IncludeSource>false</IncludeSource>
              </PropertyGroup>
            </Project>
            """;
    }
}

public sealed record ConfusionCheckResult(
    bool ExistsPublicly,
    string? PublicVersion,
    ConfusionRisk Risk,
    string Recommendation);

public enum ConfusionRisk { Low, Medium, High, Unknown }

public sealed record NuGetRegistration(
    [property: JsonPropertyName("items")] List<NuGetCatalogPage>? Items);

public sealed record NuGetCatalogPage(
    [property: JsonPropertyName("upper")] string? Upper);

Install cosign

使用Sigstore进行代码签名

macOS

Sigstore概述

brew install cosign
Sigstore提供基于OIDC身份的无密钥签名功能:
bash
undefined

Linux

Install cosign

macOS

curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64" chmod +x cosign-linux-amd64 sudo mv cosign-linux-amd64 /usr/local/bin/cosign
undefined
brew install cosign

Sign Container Images

Linux

bash
undefined
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64" chmod +x cosign-linux-amd64 sudo mv cosign-linux-amd64 /usr/local/bin/cosign
undefined

Sign with keyless (OIDC)

签名容器镜像

cosign sign ghcr.io/myorg/myimage:v1.0.0
bash
undefined

Sign with key

Sign with keyless (OIDC)

cosign generate-key-pair cosign sign --key cosign.key ghcr.io/myorg/myimage:v1.0.0
cosign sign ghcr.io/myorg/myimage:v1.0.0

Verify signature

Sign with key

cosign verify ghcr.io/myorg/myimage:v1.0.0
--certificate-identity=ci@myorg.com
--certificate-oidc-issuer=https://github.com/login/oauth
undefined
cosign generate-key-pair cosign sign --key cosign.key ghcr.io/myorg/myimage:v1.0.0

Sign Python Packages

Verify signature

bash
undefined
cosign verify ghcr.io/myorg/myimage:v1.0.0
--certificate-identity=ci@myorg.com
--certificate-oidc-issuer=https://github.com/login/oauth
undefined

Install sigstore

签名Python包

pip install sigstore
bash
undefined

Sign a package

Install sigstore

python -m sigstore sign dist/mypackage-1.0.0.tar.gz
pip install sigstore

Verify signature

Sign a package

python -m sigstore verify identity
--cert-identity ci@myorg.com
--cert-oidc-issuer https://github.com/login/oauth
dist/mypackage-1.0.0.tar.gz
undefined
python -m sigstore sign dist/mypackage-1.0.0.tar.gz

Sign npm Packages

Verify signature

bash
undefined
python -m sigstore verify identity
--cert-identity ci@myorg.com
--cert-oidc-issuer https://github.com/login/oauth
dist/mypackage-1.0.0.tar.gz
undefined

npm provenance (built-in since npm 9.5.0)

签名npm包

npm publish --provenance
bash
undefined

Verify provenance

npm provenance (built-in since npm 9.5.0)

npm audit signatures
undefined
npm publish --provenance

OpenSSF Scorecard

Verify provenance

Running Scorecard

bash
undefined
npm audit signatures
undefined

Install scorecard

OpenSSF Scorecard

macOS

运行Scorecard

brew install scorecard
bash
undefined

Run on GitHub repo

Install scorecard

macOS

scorecard --repo=github.com/myorg/myproject
brew install scorecard

Run with specific checks

Run on GitHub repo

scorecard --repo=github.com/myorg/myproject
--checks=Vulnerabilities,Dependency-Update-Tool,Pinned-Dependencies
scorecard --repo=github.com/myorg/myproject

Output as JSON

Run with specific checks

scorecard --repo=github.com/myorg/myproject --format=json > scorecard.json
undefined
scorecard --repo=github.com/myorg/myproject
--checks=Vulnerabilities,Dependency-Update-Tool,Pinned-Dependencies

GitHub Action for Scorecard

Output as JSON

yaml
name: Scorecard Analysis
on:
  push:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # Weekly on Monday

permissions:
  security-events: write
  id-token: write
  contents: read
  actions: read

jobs:
  analysis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Run Scorecard
        uses: ossf/scorecard-action@v2
        with:
          results_file: results.sarif
          results_format: sarif
          publish_results: true

      - name: Upload to Security tab
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif
scorecard --repo=github.com/myorg/myproject --format=json > scorecard.json
undefined

Scorecard Checks Explained

GitHub Action集成Scorecard

CheckWhat It MeasuresHow to Improve
VulnerabilitiesKnown vulnerabilities in dependenciesEnable Dependabot, fix vulns
Dependency-Update-ToolAutomated dependency updatesEnable Dependabot/Renovate
Pinned-DependenciesCI uses pinned dependenciesPin action versions, use hashes
Token-PermissionsMinimal CI token permissionsUse least-privilege tokens
Branch-ProtectionMain branch protectionRequire reviews, status checks
Code-ReviewPRs require reviewEnable required reviews
Signed-ReleasesReleases are signedUse Sigstore/GPG signing
Binary-ArtifactsRepo contains binariesRemove binaries, use releases
yaml
name: Scorecard Analysis
on:
  push:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # Weekly on Monday

permissions:
  security-events: write
  id-token: write
  contents: read
  actions: read

jobs:
  analysis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Run Scorecard
        uses: ossf/scorecard-action@v2
        with:
          results_file: results.sarif
          results_format: sarif
          publish_results: true

      - name: Upload to Security tab
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif

Security Checklist

Scorecard检查项说明

Pre-Release Checklist

  • Generate SBOM for release
  • Run vulnerability scan (npm audit, pip-audit, etc.)
  • Verify all dependencies have lock file entries
  • Check for dependency confusion risks
  • Sign release artifacts with Sigstore
  • Run OpenSSF Scorecard
  • Verify provenance generation is enabled
检查项检测内容优化建议
Vulnerabilities依赖项中的已知漏洞启用Dependabot、修复漏洞
Dependency-Update-Tool是否使用自动化依赖更新工具启用Dependabot/Renovate
Pinned-DependenciesCI是否使用固定版本的依赖项固定Action版本、使用哈希
Token-PermissionsCI令牌权限是否最小化使用最小权限令牌
Branch-Protection主分支防护配置启用代码审核、状态检查
Code-ReviewPR是否要求审核启用强制审核
Signed-Releases发布物是否签名使用Sigstore/GPG签名
Binary-Artifacts仓库是否包含二进制文件删除二进制文件、使用发布机制

Repository Security

安全检查清单

发布前检查清单

  • Enable Dependabot or Renovate
  • Configure branch protection rules
  • Pin CI/CD action versions with hashes
  • Use minimal token permissions
  • Enable secret scanning
  • Configure code owners for security files
  • 为发布版本生成SBOM
  • 运行漏洞扫描(npm audit、pip-audit等)
  • 验证所有依赖项均有锁定文件条目
  • 检查依赖混淆风险
  • 使用Sigstore签名发布物
  • 运行OpenSSF Scorecard
  • 验证来源生成已启用

Dependency Management

仓库安全配置

  • Use lock files in all projects
  • Enable integrity hash verification
  • Configure private registry for internal packages
  • Register placeholder packages on public registries
  • Review new dependencies before adding
  • Monitor for typosquatting attempts
  • 启用Dependabot或Renovate
  • 配置分支防护规则
  • 使用哈希固定CI/CD Action版本
  • 使用最小权限令牌
  • 启用密钥扫描
  • 为安全文件配置代码所有者

References

依赖项管理

  • SBOM Generation: See
    references/sbom-generation.md
    for advanced SBOM workflows
  • SLSA Framework: See
    references/slsa-levels.md
    for implementation guidance
  • Attack Prevention: See
    references/dependency-attacks.md
    for detailed attack patterns
  • 所有项目均使用锁定文件
  • 启用完整性哈希验证
  • 为内部包配置私有注册表
  • 在公共注册表注册占位包
  • 新增依赖项前进行审核
  • 监控包名仿冒尝试

Related Skills

参考资料

  • secure-coding
    - Secure development practices
  • devsecops-practices
    - CI/CD security integration
  • container-security
    - Container image signing and scanning

Last Updated: 2025-12-26
  • SBOM生成: 高级SBOM工作流请参考
    references/sbom-generation.md
  • SLSA框架: 实施指南请参考
    references/slsa-levels.md
  • 攻击防护: 详细攻击模式请参考
    references/dependency-attacks.md

相关技能

  • secure-coding
    - 安全开发实践
  • devsecops-practices
    - CI/CD安全集成
  • container-security
    - 容器镜像签名与扫描

最后更新: 2025-12-26