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