mirror of
https://github.com/anchore/syft.git
synced 2026-10-12 06:07:20 +02:00
Compare commits
38
Commits
+3
-3
@@ -26,7 +26,7 @@ tools:
|
||||
# used for linting
|
||||
- name: golangci-lint
|
||||
version:
|
||||
want: v2.3.0
|
||||
want: v2.4.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: golangci/golangci-lint
|
||||
@@ -58,7 +58,7 @@ tools:
|
||||
# used to release all artifacts
|
||||
- name: goreleaser
|
||||
version:
|
||||
want: v2.11.1
|
||||
want: v2.11.2
|
||||
method: github-release
|
||||
with:
|
||||
repo: goreleaser/goreleaser
|
||||
@@ -98,7 +98,7 @@ tools:
|
||||
# used for triggering a release
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.76.1
|
||||
want: v2.78.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
|
||||
@@ -37,7 +37,7 @@ runs:
|
||||
- name: Restore tool cache
|
||||
if: inputs.tools == 'true'
|
||||
id: tool-cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tool
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
|
||||
@@ -63,7 +63,7 @@ runs:
|
||||
|
||||
- name: Restore ORAS cache from github actions
|
||||
if: inputs.download-test-fixture-cache == 'true'
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp/oras-cache
|
||||
key: ${{ inputs.cache-key-prefix }}-oras-cache
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 #v3.29.4
|
||||
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 #v3.29.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@4e828ff8d448a8a6e532957b1811f387a63867e8 #v3.29.4
|
||||
uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 #v3.29.5
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -72,4 +72,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 #v3.29.4
|
||||
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 #v3.29.5
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
environment: release
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
# required for goreleaser signs section with cosign
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: true
|
||||
@@ -125,13 +125,13 @@ jobs:
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 #v3.4.0
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 #v3.5.0
|
||||
with:
|
||||
username: ${{ secrets.ANCHOREOSSWRITE_DH_USERNAME }}
|
||||
password: ${{ secrets.ANCHOREOSSWRITE_DH_PAT }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 #v3.4.0
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 #v3.5.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
# for updating brew formula in anchore/homebrew-syft
|
||||
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
|
||||
|
||||
- uses: anchore/sbom-action@7b36ad622f042cab6f59a75c2ac24ccb256e9b45 #v0.20.4
|
||||
- uses: anchore/sbom-action@da167eac915b4e86f08b264dbdbc867b61be6f0c #v0.20.5
|
||||
continue-on-error: true
|
||||
with:
|
||||
file: go.mod
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'anchore' # only run for main repo (not forks)
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -23,12 +23,12 @@ jobs:
|
||||
contents: read
|
||||
security-events: write # for uploading SARIF results
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run zizmor"
|
||||
uses: zizmorcore/zizmor-action@f52a838cfabf134edcbaa7c8b3677dde20045018 # v0.1.1
|
||||
uses: zizmorcore/zizmor-action@5ca5fc7a4779c5263a3ffa0e1f693009994446d1 # v0.1.2
|
||||
with:
|
||||
config-file: .github/zizmor.yml
|
||||
sarif-upload: true
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
name: "Static analysis"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
# we need more storage than what's on the default runner
|
||||
runs-on: ubuntu-22.04-4core-16gb
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
name: "Integration tests"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
name: "Build snapshot artifacts"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||
- name: Upload snapshot artifacts
|
||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3
|
||||
uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
with:
|
||||
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
||||
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -164,7 +164,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -175,7 +175,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -225,7 +225,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -251,7 +251,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -291,7 +291,7 @@ jobs:
|
||||
- Cli-Linux
|
||||
- Upload-Snapshot-Artifacts
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
+5
-4
@@ -48,12 +48,13 @@ builds:
|
||||
|
||||
archives:
|
||||
- id: linux-archives
|
||||
builds: [linux-build]
|
||||
ids: [linux-build]
|
||||
- id: darwin-archives
|
||||
builds: [darwin-build]
|
||||
ids: [darwin-build]
|
||||
- id: windows-archives
|
||||
format: zip
|
||||
builds: [windows-build]
|
||||
formats:
|
||||
- zip
|
||||
ids: [windows-build]
|
||||
|
||||
nfpms:
|
||||
- license: "Apache 2.0"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/fangs"
|
||||
@@ -218,8 +218,9 @@ func getSource(ctx context.Context, opts *options.Catalog, userInput string, sou
|
||||
cfg := syft.DefaultGetSourceConfig().
|
||||
WithRegistryOptions(opts.Registry.ToOptions()).
|
||||
WithAlias(source.Alias{
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
Supplier: opts.Source.Supplier,
|
||||
}).
|
||||
WithExcludeConfig(source.ExcludeConfig{
|
||||
Paths: opts.Exclusions,
|
||||
|
||||
@@ -259,6 +259,9 @@ func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
|
||||
|
||||
flags.StringVarP(&cfg.Source.BasePath, "base-path", "",
|
||||
"base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory")
|
||||
|
||||
flags.StringVarP(&cfg.Source.Supplier, "source-supplier", "",
|
||||
"the organization that supplied the component, which often may be the manufacturer, distributor, or repackager")
|
||||
}
|
||||
|
||||
func (cfg *Catalog) DescribeFields(descriptions fangs.FieldDescriptionSet) {
|
||||
|
||||
@@ -43,9 +43,9 @@ over to the final syft 1.0 json output this option can be used to ease the trans
|
||||
|
||||
Note: long term support for this option is not guaranteed (it may change or break at any time)`)
|
||||
|
||||
descriptions.Add(&o.Template.Path, `path to the template file to use when rendering the output with the template output format.
|
||||
descriptions.Add(&o.Template.Path, `path to the template file to use when rendering the output with the template output format.
|
||||
Note that all template paths are based on the current syft-json schema`)
|
||||
descriptions.Add(&o.Template.Legacy, `if true, uses the go structs for the syft-json format for templating.
|
||||
descriptions.Add(&o.Template.Legacy, `if true, uses the go structs for the syft-json format for templating.
|
||||
if false, uses the syft-json output for templating (which follows the syft JSON schema exactly).
|
||||
|
||||
Note: long term support for this option is not guaranteed (it may change or break at any time)`)
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
type sourceConfig struct {
|
||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||
Version string `json:"version" yaml:"version" mapstructure:"version"`
|
||||
Supplier string `json:"supplier" yaml:"supplier" mapstructure:"supplier"`
|
||||
Source string `json:"source" yaml:"source" mapstructure:"source"`
|
||||
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
|
||||
File fileSource `json:"file" yaml:"file" mapstructure:"file"`
|
||||
Image imageSource `json:"image" yaml:"image" mapstructure:"image"`
|
||||
|
||||
@@ -87,6 +87,7 @@ func TestPkgCoverageImage(t *testing.T) {
|
||||
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
|
||||
definedPkgs.Remove(string(pkg.TerraformPkg))
|
||||
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // we have coverage for pear instead
|
||||
definedPkgs.Remove(string(pkg.CondaPkg))
|
||||
|
||||
var cases []testCase
|
||||
cases = append(cases, commonTestCases...)
|
||||
@@ -159,6 +160,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
|
||||
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
|
||||
definedPkgs.Remove(string(pkg.Rpkg))
|
||||
definedPkgs.Remove(string(pkg.UnknownPkg))
|
||||
definedPkgs.Remove(string(pkg.CondaPkg))
|
||||
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // this is covered as pear packages
|
||||
|
||||
// for directory scans we should not expect to see any of the following package types
|
||||
|
||||
@@ -78,7 +78,10 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, image := range images {
|
||||
originalSBOM, _ := catalogFixtureImage(t, image, source.SquashedScope)
|
||||
|
||||
// we need a way to inject supplier into this test
|
||||
// supplier is not available as part of the SBOM Config API since the flag
|
||||
// is used in conjunction with the SourceConfig which is injected into generateSBOM during scan
|
||||
originalSBOM.Source.Supplier = "anchore"
|
||||
f := encoders.GetByString(test.name)
|
||||
require.NotNil(t, f)
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
_ "modernc.org/sqlite" // required for rpmdb and other features
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "modernc.org/sqlite" // required for rpmdb and other features
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/format/syftjson"
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
_ "modernc.org/sqlite" // required for rpmdb and other features
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
_ "modernc.org/sqlite" // required for rpmdb and other features
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/format/syftjson"
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ require (
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
|
||||
github.com/anchore/stereoscope v0.1.8
|
||||
github.com/anchore/stereoscope v0.1.9-0.20250826202322-ef061ea78385
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
||||
@@ -35,7 +35,7 @@ require (
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/dave/jennifer v1.7.1
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c
|
||||
github.com/diskfs/go-diskfs v1.7.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/elliotchance/phpserialize v1.4.0
|
||||
@@ -53,7 +53,7 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
github.com/hashicorp/go-getter v1.7.8
|
||||
github.com/hashicorp/go-getter v1.7.9
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
@@ -80,7 +80,7 @@ require (
|
||||
github.com/spdx/tools-golang v0.5.5
|
||||
github.com/spf13/afero v1.14.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/stretchr/testify v1.11.0
|
||||
github.com/vbatts/go-mtree v0.5.4
|
||||
github.com/vifraa/gopom v1.0.0
|
||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
|
||||
@@ -88,10 +88,10 @@ require (
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||
golang.org/x/mod v0.26.0
|
||||
golang.org/x/net v0.42.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
golang.org/x/mod v0.27.0
|
||||
golang.org/x/net v0.43.0
|
||||
modernc.org/sqlite v1.38.2
|
||||
)
|
||||
|
||||
@@ -132,7 +132,7 @@ require (
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/containerd v1.7.27 // indirect
|
||||
github.com/containerd/containerd v1.7.28 // indirect
|
||||
github.com/containerd/containerd/api v1.8.0 // indirect
|
||||
github.com/containerd/continuity v0.4.4 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
@@ -149,7 +149,7 @@ require (
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v28.3.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
@@ -200,7 +200,6 @@ require (
|
||||
github.com/minio/minlz v1.0.0 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
@@ -268,14 +267,14 @@ require (
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.35.0
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/api v0.203.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
@@ -284,6 +283,7 @@ require (
|
||||
google.golang.org/grpc v1.67.3 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.66.3 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
|
||||
@@ -694,8 +694,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
|
||||
github.com/anchore/stereoscope v0.1.8 h1:K1NRFUqGy6z/jp4ADHNXZWbZ+xd6fPr0Kn4gYp1zm1c=
|
||||
github.com/anchore/stereoscope v0.1.8/go.mod h1:VA9zyFcUzN7GIFsXfe8lj3Z6Ocs4CP5QZqbmFc1I7ag=
|
||||
github.com/anchore/stereoscope v0.1.9-0.20250826202322-ef061ea78385 h1:icCqbvAKGZXf29lEi8JmwvHVCBCYkiyZMuSnk+5ajYo=
|
||||
github.com/anchore/stereoscope v0.1.9-0.20250826202322-ef061ea78385/go.mod h1:0UCjLz5MdPNiH9F0h2tSNf3yGF6/MnK8ZCPo0YfDQVc=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
|
||||
@@ -812,8 +812,8 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII=
|
||||
github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0=
|
||||
github.com/containerd/containerd v1.7.28 h1:Nsgm1AtcmEh4AHAJ4gGlNSaKgXiNccU270Dnf81FQ3c=
|
||||
github.com/containerd/containerd v1.7.28/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
|
||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
|
||||
@@ -851,8 +851,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c h1:Vg+RNk+3Kwbe3wUYsgXsk43+7oyOJ5tRDzOrcpV40yQ=
|
||||
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
|
||||
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
|
||||
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
@@ -865,8 +865,8 @@ github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjY
|
||||
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -1133,8 +1133,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-getter v1.7.8 h1:mshVHx1Fto0/MydBekWan5zUipGq7jO0novchgMmSiY=
|
||||
github.com/hashicorp/go-getter v1.7.8/go.mod h1:2c6CboOEb9jG6YvmC9xdD+tyAFsrUaJPedwXDGr0TM4=
|
||||
github.com/hashicorp/go-getter v1.7.9 h1:G9gcjrDixz7glqJ+ll5IWvggSBR+R0B54DSRt4qfdC4=
|
||||
github.com/hashicorp/go-getter v1.7.9/go.mod h1:dyFCmT1AQkDfOIt9NH8pw9XBDqNrIKJT5ylbpi7zPNE=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
@@ -1295,8 +1295,6 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
@@ -1509,8 +1507,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
@@ -1623,6 +1621,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -1643,8 +1643,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1707,8 +1707,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1777,8 +1777,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1936,8 +1936,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -1952,8 +1952,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1974,16 +1974,16 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -2050,8 +2050,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -3,5 +3,5 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.0.36"
|
||||
JSONSchemaVersion = "16.0.39"
|
||||
)
|
||||
|
||||
@@ -31,6 +31,13 @@ var licenseIDs = map[string]string{
|
||||
{{ printf "%q" $k }}: {{ printf "%q" $v }},
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// urlToLicense maps license URLs from the seeAlso field to license IDs
|
||||
var urlToLicense = map[string]string{
|
||||
{{- range $url, $id := .URLToLicense }}
|
||||
{{ printf "%q" $url }}: {{ printf "%q" $id }},
|
||||
{{- end }}
|
||||
}
|
||||
`))
|
||||
|
||||
var versionMatch = regexp.MustCompile(`([0-9]+)\.?([0-9]+)?\.?([0-9]+)?\.?`)
|
||||
@@ -68,17 +75,20 @@ func run() error {
|
||||
}()
|
||||
|
||||
licenseIDs := processSPDXLicense(result)
|
||||
urlToLicense := buildURLToLicenseMap(result)
|
||||
|
||||
err = tmp.Execute(f, struct {
|
||||
Timestamp time.Time
|
||||
URL string
|
||||
Version string
|
||||
LicenseIDs map[string]string
|
||||
Timestamp time.Time
|
||||
URL string
|
||||
Version string
|
||||
LicenseIDs map[string]string
|
||||
URLToLicense map[string]string
|
||||
}{
|
||||
Timestamp: time.Now(),
|
||||
URL: url,
|
||||
Version: result.Version,
|
||||
LicenseIDs: licenseIDs,
|
||||
Timestamp: time.Now(),
|
||||
URL: url,
|
||||
Version: result.Version,
|
||||
LicenseIDs: licenseIDs,
|
||||
URLToLicense: urlToLicense,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -156,3 +166,30 @@ func cleanLicenseID(id string) string {
|
||||
cleanID := strings.ToLower(id)
|
||||
return strings.ReplaceAll(cleanID, "-", "")
|
||||
}
|
||||
|
||||
// buildURLToLicenseMap creates a mapping from license URLs (from seeAlso fields) to license IDs
|
||||
func buildURLToLicenseMap(result LicenseList) map[string]string {
|
||||
urlMap := make(map[string]string)
|
||||
|
||||
for _, l := range result.Licenses {
|
||||
// Skip deprecated licenses
|
||||
if l.Deprecated {
|
||||
// Find replacement license if available
|
||||
replacement := result.findReplacementLicense(l)
|
||||
if replacement != nil {
|
||||
// Map deprecated license URLs to the replacement license
|
||||
for _, url := range l.SeeAlso {
|
||||
urlMap[url] = replacement.ID
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Add URLs from non-deprecated licenses
|
||||
for _, url := range l.SeeAlso {
|
||||
urlMap[url] = l.ID
|
||||
}
|
||||
}
|
||||
|
||||
return urlMap
|
||||
}
|
||||
|
||||
@@ -35,3 +35,20 @@ func cleanLicenseID(id string) string {
|
||||
id = strings.ToLower(id)
|
||||
return strings.ReplaceAll(id, "-", "")
|
||||
}
|
||||
|
||||
// LicenseInfo contains license ID and name
|
||||
type LicenseInfo struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
// LicenseByURL returns the license ID and name for a given URL from the SPDX license list
|
||||
// The URL should match one of the URLs in the seeAlso field of an SPDX license
|
||||
func LicenseByURL(url string) (LicenseInfo, bool) {
|
||||
url = strings.TrimSpace(url)
|
||||
if id, exists := urlToLicense[url]; exists {
|
||||
return LicenseInfo{
|
||||
ID: id,
|
||||
}, true
|
||||
}
|
||||
return LicenseInfo{}, false
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
package spdxlicense
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLicenseByURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantID string
|
||||
wantFound bool
|
||||
}{
|
||||
{
|
||||
name: "MIT license URL (https)",
|
||||
url: "https://opensource.org/license/mit/",
|
||||
wantID: "MIT",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "MIT license URL (http)",
|
||||
url: "http://opensource.org/licenses/MIT",
|
||||
wantID: "MIT",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "Apache 2.0 license URL",
|
||||
url: "https://www.apache.org/licenses/LICENSE-2.0",
|
||||
wantID: "Apache-2.0",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "GPL 3.0 or later URL",
|
||||
url: "https://www.gnu.org/licenses/gpl-3.0-standalone.html",
|
||||
wantID: "GPL-3.0-or-later",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "BSD 3-Clause URL",
|
||||
url: "https://opensource.org/licenses/BSD-3-Clause",
|
||||
wantID: "BSD-3-Clause",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "URL with trailing whitespace",
|
||||
url: " http://opensource.org/licenses/MIT ",
|
||||
wantID: "MIT",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "Unknown URL",
|
||||
url: "https://example.com/unknown-license",
|
||||
wantID: "",
|
||||
wantFound: false,
|
||||
},
|
||||
{
|
||||
name: "Empty URL",
|
||||
url: "",
|
||||
wantID: "",
|
||||
wantFound: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
info, found := LicenseByURL(tt.url)
|
||||
if found != tt.wantFound {
|
||||
t.Errorf("LicenseByURL() found = %v, want %v", found, tt.wantFound)
|
||||
}
|
||||
if found {
|
||||
if info.ID != tt.wantID {
|
||||
t.Errorf("LicenseByURL() ID = %v, want %v", info.ID, tt.wantID)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLicenseByURL_DeprecatedLicenses(t *testing.T) {
|
||||
// Test that deprecated license URLs map to their replacement licenses
|
||||
// For example, GPL-2.0+ should map to GPL-2.0-or-later
|
||||
|
||||
// This test needs actual URLs from deprecated licenses
|
||||
// We can verify by checking if a deprecated license URL maps to a non-deprecated ID
|
||||
url := "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html"
|
||||
info, found := LicenseByURL(url)
|
||||
|
||||
if found {
|
||||
// Check that we got a valid non-deprecated license ID
|
||||
if info.ID == "" {
|
||||
t.Error("Got empty license ID for deprecated license URL")
|
||||
}
|
||||
// The ID should be the replacement (GPL-2.0-only or GPL-2.0-or-later)
|
||||
// depending on the URL
|
||||
t.Logf("Deprecated license URL mapped to: ID=%s", info.ID)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/arch"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary"
|
||||
bitnamiSbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/bitnami"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/conda"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/cpp"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/dart"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/debian"
|
||||
@@ -171,6 +172,7 @@ func DefaultPackageTaskFactories() Factories {
|
||||
newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"),
|
||||
newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"),
|
||||
newSimplePackageTaskFactory(homebrew.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "homebrew"),
|
||||
newSimplePackageTaskFactory(conda.NewCondaMetaCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.PackageTag, "conda"),
|
||||
|
||||
// deprecated catalogers ////////////////////////////////////////
|
||||
// these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible)
|
||||
|
||||
+142
-2
@@ -2,7 +2,7 @@
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
targetNamespace="http://cyclonedx.org/schema/spdx"
|
||||
version="1.0-3.24.0">
|
||||
version="1.0-3.26.0">
|
||||
|
||||
<xs:simpleType name="licenseId">
|
||||
<xs:restriction base="xs:string">
|
||||
@@ -162,6 +162,11 @@
|
||||
<xs:documentation>Any OSI License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="any-OSI-perl-modules">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Any OSI License - Perl Modules</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Apache-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Apache License 1.0</xs:documentation>
|
||||
@@ -307,6 +312,11 @@
|
||||
<xs:documentation>Boehm-Demers-Weiser GC License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Boehm-GC-without-fee">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Boehm-Demers-Weiser GC License (without fee)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Borceux">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Borceux license</xs:documentation>
|
||||
@@ -812,6 +822,16 @@
|
||||
<xs:documentation>Creative Commons Public Domain Dedication and Certification</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CC-PDM-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Creative Commons Public Domain Mark 1.0 Universal</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CC-SA-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Creative Commons Share Alike 1.0 Generic</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CC0-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Creative Commons Zero v1.0 Universal</xs:documentation>
|
||||
@@ -1062,6 +1082,21 @@
|
||||
<xs:documentation>DOC License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DocBook-Schema">
|
||||
<xs:annotation>
|
||||
<xs:documentation>DocBook Schema License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DocBook-Stylesheet">
|
||||
<xs:annotation>
|
||||
<xs:documentation>DocBook Stylesheet License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DocBook-XML">
|
||||
<xs:annotation>
|
||||
<xs:documentation>DocBook XML License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Dotseqn">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Dotseqn License</xs:documentation>
|
||||
@@ -1267,6 +1302,11 @@
|
||||
<xs:documentation>GD License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="generic-xts">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Generic XTS License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="GFDL-1.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>GNU Free Documentation License v1.1</xs:documentation>
|
||||
@@ -1527,6 +1567,11 @@
|
||||
<xs:documentation>hdparm License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HIDAPI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>HIDAPI License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Hippocratic-2.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Hippocratic License 2.1</xs:documentation>
|
||||
@@ -1617,6 +1662,11 @@
|
||||
<xs:documentation>Historical Permission Notice and Disclaimer with MIT disclaimer</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HPND-Netrek">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Historical Permission Notice and Disclaimer - Netrek variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HPND-Pbmplus">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Historical Permission Notice and Disclaimer - Pbmplus variant</xs:documentation>
|
||||
@@ -1712,6 +1762,11 @@
|
||||
<xs:documentation>Inner Net License v2.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="InnoSetup">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Inno Setup License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Intel">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Intel Open Source License</xs:documentation>
|
||||
@@ -2052,6 +2107,11 @@
|
||||
<xs:documentation>Minpack License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MIPS">
|
||||
<xs:annotation>
|
||||
<xs:documentation>MIPS License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MirOS">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The MirOS Licence</xs:documentation>
|
||||
@@ -2072,6 +2132,11 @@
|
||||
<xs:documentation>Enlightenment License (e16)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MIT-Click">
|
||||
<xs:annotation>
|
||||
<xs:documentation>MIT Click License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MIT-CMU">
|
||||
<xs:annotation>
|
||||
<xs:documentation>CMU License</xs:documentation>
|
||||
@@ -2772,6 +2837,11 @@
|
||||
<xs:documentation>Ruby License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Ruby-pty">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Ruby pty extension license</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SAX-PD">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Sax Public Domain Notice</xs:documentation>
|
||||
@@ -2807,6 +2877,11 @@
|
||||
<xs:documentation>Sendmail License 8.23</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Sendmail-Open-Source-1.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Sendmail Open Source License v1.1</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SGI-B-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SGI Free Software License B v1.0</xs:documentation>
|
||||
@@ -2867,6 +2942,11 @@
|
||||
<xs:documentation>Sleepycat License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SMAIL-GPL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SMAIL General Public License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SMLNJ">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Standard ML of New Jersey License</xs:documentation>
|
||||
@@ -3007,6 +3087,11 @@
|
||||
<xs:documentation>Transitive Grace Period Public Licence 1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ThirdEye">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ThirdEye License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="threeparttable">
|
||||
<xs:annotation>
|
||||
<xs:documentation>threeparttable License</xs:documentation>
|
||||
@@ -3037,6 +3122,11 @@
|
||||
<xs:documentation>THOR Public License 1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="TrustedQSL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TrustedQSL License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="TTWL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Text-Tabs+Wrap License</xs:documentation>
|
||||
@@ -3057,6 +3147,11 @@
|
||||
<xs:documentation>Technische Universitaet Berlin License 2.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Ubuntu-font-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Ubuntu Font Licence v1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="UCAR">
|
||||
<xs:annotation>
|
||||
<xs:documentation>UCAR License</xs:documentation>
|
||||
@@ -3172,6 +3267,11 @@
|
||||
<xs:documentation>Do What The F*ck You Want To Public License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="wwl">
|
||||
<xs:annotation>
|
||||
<xs:documentation>WWL License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="wxWindows">
|
||||
<xs:annotation>
|
||||
<xs:documentation>wxWindows Library License</xs:documentation>
|
||||
@@ -3187,6 +3287,11 @@
|
||||
<xs:documentation>X11 License Distribution Modification Variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="X11-swapped">
|
||||
<xs:annotation>
|
||||
<xs:documentation>X11 swapped final paragraphs</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Xdebug-1.03">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Xdebug License v 1.03</xs:documentation>
|
||||
@@ -3358,6 +3463,11 @@
|
||||
<xs:documentation>Bootloader Distribution Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CGAL-linking-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>CGAL Linking Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Classpath-exception-2.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Classpath exception 2.0</xs:documentation>
|
||||
@@ -3383,6 +3493,11 @@
|
||||
<xs:documentation>eCos exception 2.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="erlang-otp-linking-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Erlang/OTP Linking Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Fawkes-Runtime-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Fawkes Runtime Exception</xs:documentation>
|
||||
@@ -3425,7 +3540,7 @@
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Gmsh-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Gmsh exception></xs:documentation>
|
||||
<xs:documentation>Gmsh exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="GNAT-exception">
|
||||
@@ -3448,6 +3563,11 @@
|
||||
<xs:documentation>GNU JavaMail exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="GPL-3.0-389-ds-base-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>GPL-3.0 389 DS Base Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="GPL-3.0-interface-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>GPL-3.0 Interface Exception</xs:documentation>
|
||||
@@ -3478,11 +3598,21 @@
|
||||
<xs:documentation>GStreamer Exception (2008)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="harbour-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>harbour exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="i2p-gpl-java-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>i2p GPL+Java Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Independent-modules-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Independent Module Linking exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="KiCad-libraries-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>KiCad Libraries Exception</xs:documentation>
|
||||
@@ -3528,6 +3658,11 @@
|
||||
<xs:documentation>Macros and Inline Functions Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="mxml-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>mxml Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Nokia-Qt-exception-1.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Nokia Qt LGPL exception 1.1</xs:documentation>
|
||||
@@ -3583,6 +3718,11 @@
|
||||
<xs:documentation>Qwt exception 1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="romic-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Romic Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="RRDtool-FLOSS-exception-2.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>RRDtool FLOSS exception 2.0</xs:documentation>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.36/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.39/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -394,6 +394,146 @@
|
||||
"checksum"
|
||||
]
|
||||
},
|
||||
"CondaLink": {
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"source",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"CondaMetadataEntry": {
|
||||
"properties": {
|
||||
"arch": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"build": {
|
||||
"type": "string"
|
||||
},
|
||||
"build_number": {
|
||||
"type": "integer"
|
||||
},
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"subdir": {
|
||||
"type": "string"
|
||||
},
|
||||
"noarch": {
|
||||
"type": "string"
|
||||
},
|
||||
"license": {
|
||||
"type": "string"
|
||||
},
|
||||
"license_family": {
|
||||
"type": "string"
|
||||
},
|
||||
"md5": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"fn": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"extracted_package_dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"depends": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"paths_data": {
|
||||
"$ref": "#/$defs/CondaPathsData"
|
||||
},
|
||||
"link": {
|
||||
"$ref": "#/$defs/CondaLink"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"version",
|
||||
"build",
|
||||
"build_number"
|
||||
]
|
||||
},
|
||||
"CondaPathData": {
|
||||
"properties": {
|
||||
"_path": {
|
||||
"type": "string"
|
||||
},
|
||||
"path_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha256_in_prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"size_in_bytes": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"_path",
|
||||
"path_type",
|
||||
"sha256",
|
||||
"sha256_in_prefix",
|
||||
"size_in_bytes"
|
||||
]
|
||||
},
|
||||
"CondaPathsData": {
|
||||
"properties": {
|
||||
"paths_version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"paths": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/CondaPathData"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"paths_version",
|
||||
"paths"
|
||||
]
|
||||
},
|
||||
"Coordinates": {
|
||||
"properties": {
|
||||
"path": {
|
||||
@@ -1096,6 +1236,29 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"GoSourceEntry": {
|
||||
"properties": {
|
||||
"h1Digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"os": {
|
||||
"type": "string"
|
||||
},
|
||||
"architecture": {
|
||||
"type": "string"
|
||||
},
|
||||
"buildTags": {
|
||||
"type": "string"
|
||||
},
|
||||
"cgoEnabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cgoEnabled"
|
||||
]
|
||||
},
|
||||
"HaskellHackageStackEntry": {
|
||||
"properties": {
|
||||
"pkgHash": {
|
||||
@@ -1893,6 +2056,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/CocoaPodfileLockEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/CondaMetadataEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DartPubspec"
|
||||
},
|
||||
@@ -1932,6 +2098,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/GoModuleEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/GoSourceEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/HaskellHackageStackEntry"
|
||||
},
|
||||
@@ -3047,6 +3216,9 @@
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"supplier": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -48,7 +48,7 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
||||
packages := s.Artifacts.Packages.Sorted()
|
||||
components := make([]cyclonedx.Component, len(packages))
|
||||
for i, p := range packages {
|
||||
components[i] = helpers.EncodeComponent(p, locationSorter)
|
||||
components[i] = helpers.EncodeComponent(p, s.Source.Supplier, locationSorter)
|
||||
}
|
||||
components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...)
|
||||
|
||||
@@ -220,11 +220,22 @@ func toBomDescriptor(name, version string, srcMetadata source.Description) *cycl
|
||||
},
|
||||
},
|
||||
},
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
Properties: toBomProperties(srcMetadata),
|
||||
Component: toBomDescriptorComponent(srcMetadata),
|
||||
}
|
||||
}
|
||||
|
||||
func toBomSupplier(srcMetadata source.Description) *cyclonedx.OrganizationalEntity {
|
||||
if srcMetadata.Supplier != "" {
|
||||
return &cyclonedx.OrganizationalEntity{
|
||||
Name: srcMetadata.Supplier,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// used to indicate that a relationship listed under the syft artifact package can be represented as a cyclonedx dependency.
|
||||
// NOTE: CycloneDX provides the ability to describe components and their dependency on other components.
|
||||
// The dependency graph is capable of representing both direct and transitive relationships.
|
||||
@@ -321,10 +332,11 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
log.Debugf("unable to get fingerprint of source image metadata=%s: %+v", metadata.ID, err)
|
||||
}
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
Type: cyclonedx.ComponentTypeContainer,
|
||||
Name: name,
|
||||
Version: version,
|
||||
BOMRef: string(bomRef),
|
||||
Type: cyclonedx.ComponentTypeContainer,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
}
|
||||
case source.DirectoryMetadata:
|
||||
if name == "" {
|
||||
@@ -337,9 +349,10 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
// TODO: this is lossy... we can't know if this is a file or a directory
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
}
|
||||
case source.FileMetadata:
|
||||
if name == "" {
|
||||
@@ -352,9 +365,10 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
// TODO: this is lossy... we can't know if this is a file or a directory
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -389,7 +389,69 @@ func Test_toBomDescriptor(t *testing.T) {
|
||||
Name: "syft:image:labels:key1",
|
||||
Value: "value1",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with optional supplier is on the root component and bom metadata",
|
||||
args: args{
|
||||
name: "test-image",
|
||||
version: "1.0.0",
|
||||
srcMetadata: source.Description{
|
||||
Name: "test-image",
|
||||
Version: "1.0.0",
|
||||
Supplier: "optional-supplier",
|
||||
Metadata: source.ImageMetadata{},
|
||||
},
|
||||
},
|
||||
want: &cyclonedx.Metadata{
|
||||
Timestamp: "",
|
||||
Lifecycles: nil,
|
||||
Tools: &cyclonedx.ToolsChoice{
|
||||
Components: &[]cyclonedx.Component{
|
||||
{
|
||||
Type: cyclonedx.ComponentTypeApplication,
|
||||
Author: "anchore",
|
||||
Name: "test-image",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
Authors: nil,
|
||||
Component: &cyclonedx.Component{
|
||||
BOMRef: "",
|
||||
MIMEType: "",
|
||||
Type: "container",
|
||||
Supplier: &cyclonedx.OrganizationalEntity{
|
||||
Name: "optional-supplier",
|
||||
},
|
||||
Author: "",
|
||||
Publisher: "",
|
||||
Group: "",
|
||||
Name: "test-image",
|
||||
Version: "1.0.0",
|
||||
Description: "",
|
||||
Scope: "",
|
||||
Hashes: nil,
|
||||
Licenses: nil,
|
||||
Copyright: "",
|
||||
CPE: "",
|
||||
PackageURL: "",
|
||||
SWID: nil,
|
||||
Modified: nil,
|
||||
Pedigree: nil,
|
||||
ExternalReferences: nil,
|
||||
Properties: nil,
|
||||
Components: nil,
|
||||
Evidence: nil,
|
||||
ReleaseNotes: nil,
|
||||
},
|
||||
Manufacture: nil,
|
||||
Supplier: &cyclonedx.OrganizationalEntity{
|
||||
Name: "optional-supplier",
|
||||
},
|
||||
Licenses: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -257,13 +257,11 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
PackageChecksums: checksums,
|
||||
PackageExternalReferences: nil,
|
||||
PrimaryPackagePurpose: purpose,
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: helpers.NOASSERTION,
|
||||
},
|
||||
PackageCopyrightText: helpers.NOASSERTION,
|
||||
PackageDownloadLocation: helpers.NOASSERTION,
|
||||
PackageLicenseConcluded: helpers.NOASSERTION,
|
||||
PackageLicenseDeclared: helpers.NOASSERTION,
|
||||
PackageSupplier: toSPDXSupplier(s),
|
||||
PackageCopyrightText: helpers.NOASSERTION,
|
||||
PackageDownloadLocation: helpers.NOASSERTION,
|
||||
PackageLicenseConcluded: helpers.NOASSERTION,
|
||||
PackageLicenseDeclared: helpers.NOASSERTION,
|
||||
}
|
||||
|
||||
if purl != nil {
|
||||
@@ -327,6 +325,23 @@ func toSPDXID(identifiable artifact.Identifiable) spdx.ElementID {
|
||||
return spdx.ElementID(helpers.SanitizeElementID(id))
|
||||
}
|
||||
|
||||
func toSPDXSupplier(s source.Description) *spdx.Supplier {
|
||||
supplier := helpers.NOASSERTION
|
||||
if s.Supplier != "" {
|
||||
supplier = s.Supplier
|
||||
}
|
||||
|
||||
supplierType := ""
|
||||
if supplier != helpers.NOASSERTION {
|
||||
supplierType = helpers.SUPPLIERORG
|
||||
}
|
||||
|
||||
return &spdx.Supplier{
|
||||
Supplier: supplier,
|
||||
SupplierType: supplierType,
|
||||
}
|
||||
}
|
||||
|
||||
// packages populates all Package Information from the package Collection (see https://spdx.github.io/spdx-spec/3-package-information/)
|
||||
//
|
||||
//nolint:funlen
|
||||
@@ -391,7 +406,7 @@ func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBO
|
||||
// 7.6: Package Originator: may have single result for either Person or Organization,
|
||||
// or NOASSERTION
|
||||
// Cardinality: optional, one
|
||||
PackageSupplier: toPackageSupplier(p),
|
||||
PackageSupplier: toPackageSupplier(p, sbom.Source.Supplier),
|
||||
|
||||
PackageOriginator: toPackageOriginator(p),
|
||||
|
||||
@@ -556,11 +571,18 @@ func toPackageOriginator(p pkg.Package) *spdx.Originator {
|
||||
}
|
||||
}
|
||||
|
||||
func toPackageSupplier(p pkg.Package) *spdx.Supplier {
|
||||
func toPackageSupplier(p pkg.Package, sbomSupplier string) *spdx.Supplier {
|
||||
kind, supplier := helpers.Supplier(p)
|
||||
if kind == "" || supplier == "" {
|
||||
supplier := helpers.NOASSERTION
|
||||
supplierType := ""
|
||||
if sbomSupplier != "" {
|
||||
supplier = sbomSupplier
|
||||
supplierType = helpers.SUPPLIERORG
|
||||
}
|
||||
return &spdx.Supplier{
|
||||
Supplier: helpers.NOASSERTION,
|
||||
Supplier: supplier,
|
||||
SupplierType: supplierType,
|
||||
}
|
||||
}
|
||||
return &spdx.Supplier{
|
||||
|
||||
@@ -36,8 +36,9 @@ func Test_toFormatModel(t *testing.T) {
|
||||
name: "container",
|
||||
in: sbom.SBOM{
|
||||
Source: source.Description{
|
||||
Name: "alpine",
|
||||
Version: "sha256:d34db33f",
|
||||
Name: "alpine",
|
||||
Version: "sha256:d34db33f",
|
||||
Supplier: "Alpine Linux",
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:latest",
|
||||
ManifestDigest: "sha256:d34db33f",
|
||||
@@ -61,7 +62,8 @@ func Test_toFormatModel(t *testing.T) {
|
||||
PackageName: "pkg-1",
|
||||
PackageVersion: "version-1",
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: "NOASSERTION",
|
||||
Supplier: "Alpine Linux",
|
||||
SupplierType: "Organization",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -78,7 +80,8 @@ func Test_toFormatModel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: "NOASSERTION",
|
||||
Supplier: "Alpine Linux",
|
||||
SupplierType: "Organization",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -145,10 +145,21 @@ func containerSource(p *spdx.Package) source.Description {
|
||||
c := p.PackageChecksums[0]
|
||||
digest = fmt.Sprintf("%s:%s", fromChecksumAlgorithm(c.Algorithm), c.Value)
|
||||
}
|
||||
|
||||
supplier := ""
|
||||
if p.PackageSupplier != nil {
|
||||
// we also don't want NOASSERTION transferred to the syft format
|
||||
// NOASSERTION == ""
|
||||
if p.PackageSupplier.Supplier != helpers.NOASSERTION && p.PackageSupplier.SupplierType == helpers.SUPPLIERORG {
|
||||
supplier = p.PackageSupplier.Supplier
|
||||
}
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: id,
|
||||
Name: p.PackageName,
|
||||
Version: p.PackageVersion,
|
||||
ID: id,
|
||||
Name: p.PackageName,
|
||||
Version: p.PackageVersion,
|
||||
Supplier: supplier,
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: container,
|
||||
ID: id,
|
||||
@@ -179,10 +190,16 @@ func fileSource(p *spdx.Package) source.Description {
|
||||
metadata, version = fileSourceMetadata(p)
|
||||
}
|
||||
|
||||
supplier := ""
|
||||
if p.PackageSupplier.Supplier != helpers.NOASSERTION {
|
||||
supplier = p.PackageSupplier.Supplier
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: string(p.PackageSPDXIdentifier),
|
||||
Name: p.PackageName,
|
||||
Version: version,
|
||||
Supplier: supplier,
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,17 +525,19 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
UserInput: "some-image:some-tag",
|
||||
ManifestDigest: "sha256:ab8b83234bc28f28d8e",
|
||||
},
|
||||
Name: "some-image",
|
||||
Version: "some-tag",
|
||||
Name: "some-image",
|
||||
Version: "some-tag",
|
||||
Supplier: "some-supplier",
|
||||
},
|
||||
packages: packages,
|
||||
relationships: relationships,
|
||||
},
|
||||
{
|
||||
name: ". directory source",
|
||||
name: ". directory source with supplier",
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-Directory-.",
|
||||
Name: ".",
|
||||
ID: "DocumentRoot-Directory-.",
|
||||
Name: ".",
|
||||
Supplier: "some-supplier",
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: ".",
|
||||
},
|
||||
@@ -544,7 +546,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
relationships: relationships,
|
||||
},
|
||||
{
|
||||
name: "directory source",
|
||||
name: "directory source without supplier",
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-Directory-my-app",
|
||||
Name: "my-app",
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func EncodeComponent(p pkg.Package, locationSorter func(a, b file.Location) int) cyclonedx.Component {
|
||||
func EncodeComponent(p pkg.Package, supplier string, locationSorter func(a, b file.Location) int) cyclonedx.Component {
|
||||
props := EncodeProperties(p, "syft:package")
|
||||
|
||||
if p.Metadata != nil {
|
||||
@@ -49,6 +49,7 @@ func EncodeComponent(p pkg.Package, locationSorter func(a, b file.Location) int)
|
||||
Name: p.Name,
|
||||
Group: encodeGroup(p),
|
||||
Version: p.Version,
|
||||
Supplier: encodeSupplier(p, supplier),
|
||||
PackageURL: p.PURL,
|
||||
Licenses: encodeLicenses(p),
|
||||
CPE: encodeSingleCPE(p),
|
||||
@@ -61,6 +62,16 @@ func EncodeComponent(p pkg.Package, locationSorter func(a, b file.Location) int)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we eventually want to update this so that we can read "supplier" from different syft metadata
|
||||
func encodeSupplier(_ pkg.Package, sbomSupplier string) *cyclonedx.OrganizationalEntity {
|
||||
if sbomSupplier != "" {
|
||||
return &cyclonedx.OrganizationalEntity{
|
||||
Name: sbomSupplier,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeriveBomRef(p pkg.Package) string {
|
||||
// try and parse the PURL if possible and append syft id to it, to make
|
||||
// the purl unique in the BOM.
|
||||
|
||||
@@ -152,7 +152,8 @@ func Test_encodeComponentProperties(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := EncodeComponent(test.input, file.LocationSorter(nil))
|
||||
sbomSupplier := ""
|
||||
c := EncodeComponent(test.input, sbomSupplier, file.LocationSorter(nil))
|
||||
if test.expected == nil {
|
||||
if c.Properties != nil {
|
||||
t.Fatalf("expected no properties, got: %+v", *c.Properties)
|
||||
@@ -212,7 +213,8 @@ func Test_encodeCompomentType(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.pkg.ID()
|
||||
p := EncodeComponent(tt.pkg, file.LocationSorter(nil))
|
||||
sbomSupplier := ""
|
||||
p := EncodeComponent(tt.pkg, sbomSupplier, file.LocationSorter(nil))
|
||||
assert.Equal(t, tt.want, p)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -211,6 +211,16 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
}
|
||||
c := meta.Component
|
||||
|
||||
supplier := ""
|
||||
// First check component-level supplier
|
||||
if c.Supplier != nil && c.Supplier.Name != "" {
|
||||
supplier = c.Supplier.Name
|
||||
}
|
||||
// Fall back to metadata-level supplier if component supplier is not set
|
||||
if supplier == "" && meta.Supplier != nil && meta.Supplier.Name != "" {
|
||||
supplier = meta.Supplier.Name
|
||||
}
|
||||
|
||||
switch c.Type {
|
||||
case cyclonedx.ComponentTypeContainer:
|
||||
var labels map[string]string
|
||||
@@ -220,7 +230,8 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: "",
|
||||
ID: "",
|
||||
Supplier: supplier,
|
||||
// TODO: can we decode alias name-version somehow? (it isn't be encoded in the first place yet)
|
||||
|
||||
Metadata: source.ImageMetadata{
|
||||
@@ -236,6 +247,7 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
// TODO: this is lossy... we can't know if this is a file or a directory
|
||||
return source.Description{
|
||||
ID: "",
|
||||
Supplier: supplier,
|
||||
Metadata: source.FileMetadata{Path: c.Name},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License {
|
||||
licenses = append(licenses, pkg.NewLicenseFromURLsWithContext(context.TODO(), l.License.ID, l.License.URL))
|
||||
case l.License != nil && l.License.Name != "":
|
||||
licenses = append(licenses, pkg.NewLicenseFromURLsWithContext(context.TODO(), l.License.Name, l.License.URL))
|
||||
case l.License != nil && l.License.URL != "":
|
||||
// Try to enrich license from URL when ID and Name are empty
|
||||
licenses = append(licenses, pkg.NewLicenseFromURLsWithContext(context.TODO(), "", l.License.URL))
|
||||
case l.Expression != "":
|
||||
licenses = append(licenses, pkg.NewLicenseWithContext(context.TODO(), l.Expression))
|
||||
default:
|
||||
@@ -163,10 +166,18 @@ func processCustomLicense(l pkg.License) cyclonedx.Licenses {
|
||||
func processLicenseURLs(l pkg.License, spdxID string, populate *cyclonedx.Licenses) {
|
||||
for _, url := range l.URLs {
|
||||
if spdxID == "" {
|
||||
// CycloneDX requires either an id or name to be present for a license
|
||||
// If l.Value is empty, use the URL as the name to ensure schema compliance
|
||||
// at this point we've already tried to enrich the license we just don't want the format
|
||||
// conversion to be lossy here
|
||||
name := l.Value
|
||||
if name == "" {
|
||||
name = url
|
||||
}
|
||||
*populate = append(*populate, cyclonedx.LicenseChoice{
|
||||
License: &cyclonedx.License{
|
||||
URL: url,
|
||||
Name: l.Value,
|
||||
Name: name,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -121,6 +121,22 @@ func Test_encodeLicense(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with URL only and no name or SPDX ID",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromURLsWithContext(ctx, "", "http://jaxen.codehaus.org/license.html"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
{
|
||||
License: &cyclonedx.License{
|
||||
Name: "http://jaxen.codehaus.org/license.html",
|
||||
URL: "http://jaxen.codehaus.org/license.html",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with multiple values licenses are deduplicated",
|
||||
input: pkg.Package{
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
const NONE = "NONE"
|
||||
const NOASSERTION = "NOASSERTION"
|
||||
const SUPPLIERORG = "Organization"
|
||||
|
||||
func DownloadLocation(p pkg.Package) string {
|
||||
// 3.7: Package Download Location
|
||||
|
||||
@@ -18,6 +18,7 @@ func Test_OriginatorSupplier(t *testing.T) {
|
||||
pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently)
|
||||
pkg.ConanfileEntry{},
|
||||
pkg.ConaninfoEntry{},
|
||||
pkg.CondaMetaPackage{},
|
||||
pkg.DartPubspecLockEntry{},
|
||||
pkg.DartPubspec{},
|
||||
pkg.DotnetDepsEntry{},
|
||||
@@ -27,6 +28,7 @@ func Test_OriginatorSupplier(t *testing.T) {
|
||||
pkg.ErlangRebarLockEntry{},
|
||||
pkg.GolangBinaryBuildinfoEntry{},
|
||||
pkg.GolangModuleEntry{},
|
||||
pkg.GolangSourceEntry{},
|
||||
pkg.HomebrewFormula{},
|
||||
pkg.HackageStackYamlLockEntry{},
|
||||
pkg.HackageStackYamlEntry{},
|
||||
|
||||
@@ -48,6 +48,8 @@ func SourceInfo(p pkg.Package) string {
|
||||
answer = "acquired package info from installed cocoapods manifest file"
|
||||
case pkg.ConanPkg:
|
||||
answer = "acquired package info from conan manifest"
|
||||
case pkg.CondaPkg:
|
||||
answer = "acquired package info from conda metadata"
|
||||
case pkg.PortagePkg:
|
||||
answer = "acquired package info from portage DB"
|
||||
case pkg.HackagePkg:
|
||||
|
||||
@@ -191,6 +191,14 @@ func Test_SourceInfo(t *testing.T) {
|
||||
"from conan manifest",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.CondaPkg,
|
||||
},
|
||||
expected: []string{
|
||||
"from conda metadata",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.PortagePkg,
|
||||
|
||||
@@ -12,10 +12,16 @@ import (
|
||||
)
|
||||
|
||||
// Source object represents the thing that was cataloged
|
||||
// Note: syft currently makes no claims or runs any logic to determine the Supplier field below
|
||||
|
||||
// Instead, the Supplier can be determined by the user of syft and passed as a config or flag to help fulfill
|
||||
// the NTIA minimum elements. For mor information see the NTIA framing document below
|
||||
// https://www.ntia.gov/files/ntia/publications/framingsbom_20191112.pdf
|
||||
type Source struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Supplier string `json:"supplier,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Metadata interface{} `json:"metadata"`
|
||||
}
|
||||
@@ -25,6 +31,7 @@ type sourceUnpacker struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Supplier string `json:"supplier,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Metadata json.RawMessage `json:"metadata"`
|
||||
Target json.RawMessage `json:"target"` // pre-v9 schema support
|
||||
@@ -40,6 +47,7 @@ func (s *Source) UnmarshalJSON(b []byte) error {
|
||||
|
||||
s.Name = unpacker.Name
|
||||
s.Version = unpacker.Version
|
||||
s.Supplier = unpacker.Supplier
|
||||
s.Type = unpacker.Type
|
||||
s.ID = unpacker.ID
|
||||
|
||||
|
||||
@@ -320,6 +320,7 @@ func toSourceModel(src source.Description) model.Source {
|
||||
ID: src.ID,
|
||||
Name: src.Name,
|
||||
Version: src.Version,
|
||||
Supplier: src.Supplier,
|
||||
Type: sourcemetadata.JSONName(src.Metadata),
|
||||
Metadata: src.Metadata,
|
||||
}
|
||||
|
||||
@@ -56,19 +56,21 @@ func Test_toSourceModel(t *testing.T) {
|
||||
{
|
||||
name: "directory",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
},
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "directory",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
@@ -78,9 +80,10 @@ func Test_toSourceModel(t *testing.T) {
|
||||
{
|
||||
name: "file",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
@@ -88,10 +91,11 @@ func Test_toSourceModel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "file",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Type: "file",
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
|
||||
@@ -315,6 +315,7 @@ func toSyftSourceData(s model.Source) *source.Description {
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Version: s.Version,
|
||||
Supplier: s.Supplier,
|
||||
Metadata: s.Metadata,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ func AllTypes() []any {
|
||||
pkg.ConanV2LockEntry{},
|
||||
pkg.ConanfileEntry{},
|
||||
pkg.ConaninfoEntry{},
|
||||
pkg.CondaMetaPackage{},
|
||||
pkg.DartPubspec{},
|
||||
pkg.DartPubspecLockEntry{},
|
||||
pkg.DotnetDepsEntry{},
|
||||
@@ -29,6 +30,7 @@ func AllTypes() []any {
|
||||
pkg.GitHubActionsUseStatement{},
|
||||
pkg.GolangBinaryBuildinfoEntry{},
|
||||
pkg.GolangModuleEntry{},
|
||||
pkg.GolangSourceEntry{},
|
||||
pkg.HackageStackYamlEntry{},
|
||||
pkg.HackageStackYamlLockEntry{},
|
||||
pkg.HomebrewFormula{},
|
||||
|
||||
@@ -82,6 +82,7 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.GitHubActionsUseStatement{}, "github-actions-use-statement"),
|
||||
jsonNames(pkg.GolangBinaryBuildinfoEntry{}, "go-module-buildinfo-entry", "GolangBinMetadata", "GolangMetadata"),
|
||||
jsonNames(pkg.GolangModuleEntry{}, "go-module-entry", "GolangModMetadata"),
|
||||
jsonNames(pkg.GolangSourceEntry{}, "go-source-entry"),
|
||||
jsonNames(pkg.HackageStackYamlLockEntry{}, "haskell-hackage-stack-lock-entry", "HackageMetadataType"),
|
||||
jsonNamesWithoutLookup(pkg.HackageStackYamlEntry{}, "haskell-hackage-stack-entry", "HackageMetadataType"), // the legacy value is split into two types, where the other is preferred
|
||||
jsonNames(pkg.JavaArchive{}, "java-archive", "JavaMetadata"),
|
||||
@@ -119,6 +120,7 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.LuaRocksPackage{}, "luarocks-package"),
|
||||
jsonNames(pkg.TerraformLockProviderEntry{}, "terraform-lock-provider-entry"),
|
||||
jsonNames(pkg.DotnetPackagesLockEntry{}, "dotnet-packages-lock-entry"),
|
||||
jsonNames(pkg.CondaMetaPackage{}, "conda-metadata-entry", "CondaPackageMetadata"),
|
||||
)
|
||||
|
||||
func expandLegacyNameVariants(names ...string) []string {
|
||||
|
||||
@@ -347,6 +347,12 @@ func Test_JSONName_JSONLegacyName(t *testing.T) {
|
||||
expectedJSONName: "go-module-entry",
|
||||
expectedLegacyName: "GolangModMetadata",
|
||||
},
|
||||
{
|
||||
name: "GolangSourceMetadata",
|
||||
metadata: pkg.GolangSourceEntry{},
|
||||
expectedJSONName: "go-source-entry",
|
||||
expectedLegacyName: "go-source-entry",
|
||||
},
|
||||
{
|
||||
name: "HackageStackYamlLockMetadata",
|
||||
metadata: pkg.HackageStackYamlLockEntry{},
|
||||
|
||||
@@ -4,6 +4,7 @@ package license
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/github/go-spdx/v2/spdxexp"
|
||||
|
||||
@@ -17,6 +18,18 @@ const (
|
||||
Concluded Type = "concluded"
|
||||
)
|
||||
|
||||
// trimFileSuffix removes common file extensions from the end of a string
|
||||
func trimFileSuffix(s string) string {
|
||||
suffixes := []string{".txt", ".pdf", ".html", ".htm", ".md", ".markdown", ".rst", ".doc", ".docx", ".rtf", ".tex", ".xml", ".json"}
|
||||
lower := strings.ToLower(s)
|
||||
for _, suffix := range suffixes {
|
||||
if strings.HasSuffix(lower, suffix) {
|
||||
return s[:len(s)-len(suffix)]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func ParseExpression(expression string) (ex string, err error) {
|
||||
// https://github.com/anchore/syft/issues/1837
|
||||
// The current spdx library can panic when parsing some expressions
|
||||
@@ -28,10 +41,26 @@ func ParseExpression(expression string) (ex string, err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
// Try with the original expression first
|
||||
licenseID, exists := spdxlicense.ID(expression)
|
||||
if exists {
|
||||
return licenseID, nil
|
||||
}
|
||||
|
||||
// Check if the expression is a URL and try to look it up
|
||||
if info, found := spdxlicense.LicenseByURL(expression); found {
|
||||
return info.ID, nil
|
||||
}
|
||||
|
||||
// Try with trimmed file suffix
|
||||
trimmed := trimFileSuffix(expression)
|
||||
if trimmed != expression {
|
||||
// Try as a URL with the trimmed version
|
||||
if info, found := spdxlicense.LicenseByURL(trimmed); found {
|
||||
return info.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If it doesn't exist initially in the SPDX list it might be a more complex expression
|
||||
// ignored variable is any invalid expressions
|
||||
// TODO: contribute to spdxexp to expose deprecated license IDs
|
||||
|
||||
@@ -75,6 +75,12 @@ func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Pac
|
||||
newPackages:
|
||||
for i := range newPkgs {
|
||||
newPkg := &newPkgs[i]
|
||||
purlType := pkg.TypeFromPURL(newPkg.PURL)
|
||||
// for certain results, such as hashicorp vault we are returning a golang PURL, so we can use Golang package type,
|
||||
// despite not having the known metadata, this should result in downstream grype matching to use the golang matcher
|
||||
if purlType != pkg.UnknownPkg {
|
||||
newPkg.Type = purlType
|
||||
}
|
||||
for j := range packages {
|
||||
p := &packages[j]
|
||||
// consolidate identical packages found in different locations or by different classifiers
|
||||
@@ -92,6 +98,9 @@ func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Pac
|
||||
|
||||
// mergePackages merges information from the extra package into the target package
|
||||
func mergePackages(target *pkg.Package, extra *pkg.Package) {
|
||||
if extra.Type != pkg.BinaryPkg && target.Type == pkg.BinaryPkg {
|
||||
target.Type = extra.Type
|
||||
}
|
||||
// add the locations
|
||||
target.Locations.Add(extra.Locations.ToSlice()...)
|
||||
// update the metadata to indicate which classifiers were used
|
||||
|
||||
@@ -775,39 +775,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("java-binary-oracle", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-jre-ibm/1.8.0_391/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Version: "1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ibm/java@1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-ibm", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-ibm-8-jre/1.8.0_451/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Version: "1.8.0-_2025_04_14_02_37-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ibm/java@1.8.0-_2025_04_14_02_37-b00",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-ibm", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-ibm-8-sdk-alpine/1.8.0_321/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java_sdk",
|
||||
Version: "1.8.0-foreman_2022_01_20_09_33-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ibm/java_sdk@1.8.0-foreman_2022_01_20_09_33-b00",
|
||||
Locations: locations("jdb"),
|
||||
Metadata: metadata("java-sdk-binary-ibm", "jdb"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-jdk-openjdk/21.0.2+13-LTS/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -1007,6 +974,28 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("consul-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "vault/1.20.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "github.com/hashicorp/vault",
|
||||
Version: "1.20.2",
|
||||
Type: "golang",
|
||||
PURL: "pkg:golang/github.com/hashicorp/vault@1.20.2",
|
||||
Locations: locations("vault"),
|
||||
Metadata: metadata("hashicorp-vault-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "vault/1.19.4/linux-arm64",
|
||||
expected: pkg.Package{
|
||||
Name: "github.com/hashicorp/vault",
|
||||
Version: "1.19.4",
|
||||
Type: "golang",
|
||||
PURL: "pkg:golang/github.com/hashicorp/vault@1.19.4",
|
||||
Locations: locations("vault"),
|
||||
Metadata: metadata("hashicorp-vault-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "erlang/25.3.2.6/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -1194,6 +1183,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("openssl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "openssl/1.1.1zb/linux-arm64",
|
||||
expected: pkg.Package{
|
||||
Name: "openssl",
|
||||
Version: "1.1.1zb",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/openssl@1.1.1zb",
|
||||
Locations: locations("openssl"),
|
||||
Metadata: metadata("openssl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "gcc/12.3.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -1370,6 +1370,28 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("chrome-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "ffmpeg/7.1.1/darwin-arm64",
|
||||
expected: pkg.Package{
|
||||
Name: "ffmpeg",
|
||||
Version: "7.1.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ffmpeg@7.1.1",
|
||||
Locations: locations("ffmpeg"),
|
||||
Metadata: metadata("ffmpeg-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "ffmpeg/6.1.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "ffmpeg",
|
||||
Version: "6.1.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ffmpeg@6.1.1",
|
||||
Locations: locations("ffmpeg"),
|
||||
Metadata: metadata("ffmpeg-binary"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -448,6 +448,16 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
PURL: mustPURL("pkg:golang/github.com/hashicorp/consul@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:hashicorp:consul:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "hashicorp-vault-binary",
|
||||
FileGlob: "**/vault",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// revoke1.18.0
|
||||
`(?m)revoke(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
Package: "github.com/hashicorp/vault",
|
||||
PURL: mustPURL("pkg:golang/github.com/hashicorp/vault@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:hashicorp:vault:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "nginx-binary",
|
||||
FileGlob: "**/nginx",
|
||||
@@ -483,7 +493,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// [NUL]OpenSSL 3.1.4'
|
||||
// [NUL]OpenSSL 1.1.1w'
|
||||
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+([a-z]|-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
|
||||
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+([a-z]+|-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
|
||||
),
|
||||
Package: "openssl",
|
||||
PURL: mustPURL("pkg:generic/openssl@version"),
|
||||
@@ -617,6 +627,18 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
PURL: mustPURL("pkg:generic/chrome@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:google:chrome:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "ffmpeg-binary",
|
||||
FileGlob: "**/ffmpeg",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// Pattern found in the binary: "%s version 7.1.1" or "%s version 6.0"
|
||||
// When executed outputs: "ffmpeg version 7.1.1" or "ffmpeg version 6.0"
|
||||
`(?m)%s version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)`,
|
||||
),
|
||||
Package: "ffmpeg",
|
||||
PURL: mustPURL("pkg:generic/ffmpeg@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ffmpeg:ffmpeg:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
}
|
||||
|
||||
return append(classifiers, defaultJavaClassifiers()...)
|
||||
|
||||
@@ -122,6 +122,23 @@ func defaultJavaClassifiers() []binutils.Classifier {
|
||||
PURL: mustPURL("pkg:generic/oracle/graalvm@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:graalvm_for_jdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "jdb-binary-openjdk-zulu",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
binutils.MatchPath("**/*zulu*/**"),
|
||||
binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]jdb[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
`(?m)(java|jdb)\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
m.FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]jdb[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+(java|jdb)`),
|
||||
),
|
||||
),
|
||||
Package: "zulu",
|
||||
PURL: mustPURL("pkg:generic/azul/zulu@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:azul:zulu:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-jdb-binary-openjdk",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
@@ -137,23 +154,6 @@ func defaultJavaClassifiers() []binutils.Classifier {
|
||||
PURL: mustPURL("pkg:generic/oracle/openjdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "jdb-binary-openjdk-zulu",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
binutils.MatchPath("**/*zulu*/**"),
|
||||
binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
m.FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+openjdk\x00java`),
|
||||
),
|
||||
),
|
||||
Package: "zulu",
|
||||
PURL: mustPURL("pkg:generic/azul/zulu@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:azul:zulu:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-sdk-binary-ibm",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func Test_JavaBinaryImage(t *testing.T) {
|
||||
// no match for platform in manifest on Apple Silicon
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
|
||||
t.Skip("Java binary images not supported on Apple Silicon (darwin/arm64)")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
image string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
image: "image-java-binary",
|
||||
expected: []string{
|
||||
"java @ 1.8.0-foreman_2022_09_22_15_30-b00 (/staged/positive/ibm/java)",
|
||||
"jre @ 19.0.1+10-21 (/staged/positive/oracle-macos/java)",
|
||||
"openjdk @ 1.8.0_352-b08 (/staged/positive/openjdk/java)",
|
||||
"openjdk @ 11.0.17+8-LTS (/staged/positive/openjdk-lts/java)",
|
||||
},
|
||||
},
|
||||
{
|
||||
image: "image-java-zulu-8",
|
||||
expected: []string{
|
||||
"zulu @ 1.8.0_462-b08 (/usr/lib/jvm/zulu8-ca-amd64/bin/jdb)",
|
||||
},
|
||||
},
|
||||
{
|
||||
image: "image-java-zulu-21",
|
||||
expected: []string{
|
||||
"zulu @ 21.0.8+9-LTS (/usr/lib/jvm/zulu21-ca-amd64/bin/java)",
|
||||
},
|
||||
},
|
||||
{
|
||||
image: "image-java-ibm-8",
|
||||
expected: []string{
|
||||
"java @ 1.8.0-foreman_2023_10_12_13_27-b00 (/opt/ibm/java/jre/bin/java)",
|
||||
},
|
||||
},
|
||||
{
|
||||
image: "image-java-ibm-jre-8",
|
||||
expected: []string{
|
||||
"java @ 1.8.0-_2025_04_14_02_37-b00 (/opt/ibm/java/jre/bin/java)",
|
||||
},
|
||||
},
|
||||
{
|
||||
image: "image-java-ibm-sdk-8",
|
||||
expected: []string{
|
||||
"java_sdk @ 1.8.0-foreman_2022_01_20_09_33-b00 (/opt/ibm/java/bin/jdb)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.image, func(t *testing.T) {
|
||||
c := NewClassifierCataloger(ClassifierCatalogerConfig{
|
||||
Classifiers: defaultJavaClassifiers(),
|
||||
})
|
||||
pkgtest.NewCatalogTester().
|
||||
WithImageResolver(t, tt.image).
|
||||
ExpectsPackageStrings(tt.expected).
|
||||
TestCataloger(t, c)
|
||||
})
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+11
@@ -0,0 +1,11 @@
|
||||
name: vault
|
||||
offset: 191891817
|
||||
length: 420
|
||||
snippetSha256: fd8eb7979e1e7bbc254f2f7a26f6a70ecb479cc4f50c804f3b0789bc0e229097
|
||||
fileSha256: a4754a343e619adef87a1d7e1368a0709ba29802fefbe8d70105c11085becd43
|
||||
|
||||
### byte snippet to follow ###
|
||||
ctstreamPragma</a>.
|
||||
:httpssocks Locked%s.logCT_LOGLOCAL0@level[INFO][WARN]env:///v1/%sDELETEcreateErrorserrorsclient/data/lookupactualAcceptregionawskmsocikmspkcs11%s%s%scloud:%s.%s:revoke1.19.4)(nil))(%#v) {...}frenchCommonArabicBrahmiCarianChakmaCopticGothicHangulHatranHebrewKaithiKhojkiLepchaLycianLydianRejangSyriacTai_LeTangsaTangutTeluguThaanaWanchoYezidiHyphen\u%04x\U%08xgetentpasswd390625%s
|
||||
%s
|
||||
%q: %wnumber\uff
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
name: vault
|
||||
offset: 213670425
|
||||
length: 220
|
||||
snippetSha256: d9b23916d00e8085a4066974641ef77962f2e0aae6f4244688d2c4fe4286c92b
|
||||
fileSha256: fb484f1b458e5d2fd28270b406a8b26b28d040dddcaa1d099aa9ee4ac927b730
|
||||
|
||||
### byte snippet to follow ###
|
||||
ctstreamPragma</a>.
|
||||
:httpssocks Locked%s.logCT_LOGLOCAL0@level[INFO][WARN]env:///v1/%sDELETEcreateErrorserrorsclient/data/lookupactualAcceptregionawskmsocikmspkcs11%s%s%scloud:%s.%s:revoke1.20.2)(nil))(%#v) {...}frenchCo
|
||||
@@ -187,33 +187,6 @@ from-images:
|
||||
paths:
|
||||
- /usr/local/apache2/bin/httpd
|
||||
|
||||
- name: java-jre-ibm
|
||||
version: 1.8.0_391
|
||||
images:
|
||||
- ref: ibmjava:8@sha256:05ef6b0f754aa3a8cebcec36260a70c234a217b21240a998604f33459037bc08
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /opt/ibm/java/jre/bin/java
|
||||
- /opt/ibm/java/jre/lib/amd64/jli/libjli.so
|
||||
|
||||
- name: java-ibm-8-jre
|
||||
version: 1.8.0_451
|
||||
images:
|
||||
- ref: ibmjava:8-jre@sha256:3588cd1cc9b8646fe03b3b15210e69b1b520f1321f8518b69c0e7013d702fd23
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /opt/ibm/java/jre/bin/java
|
||||
- /opt/ibm/java/jre/lib/amd64/jli/libjli.so
|
||||
|
||||
- name: java-ibm-8-sdk-alpine
|
||||
version: 1.8.0_321
|
||||
images:
|
||||
- ref: ibmjava:8-sdk-alpine@sha256:4f8ad2029e78f7b91721745a77fc6011a7c0e09b9edeffb6b20b6ec34a6e63cd
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /opt/ibm/java/bin/jdb
|
||||
- /opt/ibm/java/lib/amd64/jli/libjli.so
|
||||
|
||||
- version: 10.6.15
|
||||
images:
|
||||
- ref: mariadb:10.6.15@sha256:92d499d9e02e92dc55c8160ef4004aa07f2e835197b18864ed214ca441e0dcfc
|
||||
@@ -724,6 +697,20 @@ from-images:
|
||||
paths:
|
||||
- /bin/consul
|
||||
|
||||
- version: 1.20.2
|
||||
images:
|
||||
- ref: hashicorp/vault@sha256:5cd2003247e0a574a66c66aee1916b1e9e7f99640298f2e61271a8842d2d2a19
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /bin/vault
|
||||
|
||||
- version: 1.19.4
|
||||
images:
|
||||
- ref: hashicorp/vault@sha256:b5f675b0bf681568cd7354c697fe4ce953024312d6d23ee7216deda60c192bad
|
||||
platform: linux/arm64
|
||||
paths:
|
||||
- /bin/vault
|
||||
|
||||
- version: 3.0.2
|
||||
images:
|
||||
- ref: fluent/fluent-bit:3.0.2-amd64@sha256:7e6fe8efd51dda0739e355f58bf5e3b1623cbf2d4a23c06c7a365d9553e2d242
|
||||
@@ -823,3 +810,23 @@ from-images:
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /usr/local/bin/jq
|
||||
|
||||
- name: ffmpeg
|
||||
version: 8.0
|
||||
images:
|
||||
- ref: linuxserver/ffmpeg:version-8.0-cli@sha256:9d7e80710e7f11a276a4185f07e5e81db26fc7027ea70d1591f93ddf58e36ab4
|
||||
platform: linux/amd64
|
||||
- ref: linuxserver/ffmpeg:arm64v8-version-8.0-cli@sha256:0b6cf2e15a78094396f23a7cb3afaf7dffcad195573a2b97f190eae1ee1a0420
|
||||
platform: linux/arm64
|
||||
paths:
|
||||
- /usr/local/bin/ffmpeg
|
||||
|
||||
- name: ffmpeg
|
||||
version: 7.1.1
|
||||
images:
|
||||
- ref: linuxserver/ffmpeg:7.1.1@sha256:aea59a11c54291ac456bb2d67000445e5a8994f70bc3d96cdc29f022fbbf89fb
|
||||
platform: linux/amd64
|
||||
- ref: linuxserver/ffmpeg:arm64v8-7.1.1@sha256:b2aa507f205ea2dc32369c55d0f93b5649f1b39e42d9e6cd27bd53def7a63655
|
||||
platform: linux/arm64
|
||||
paths:
|
||||
- /usr/local/bin/ffmpeg
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
FROM anchore/test_images:syft_bin-cf22714@sha256:c27b02c6322180fd8a7a3097d2b430bfdf9ea52ecf136edf258458e82f2c6f21
|
||||
@@ -0,0 +1,6 @@
|
||||
FROM ibmjava:8@sha256:05ef6b0f754aa3a8cebcec36260a70c234a217b21240a998604f33459037bc08 AS builder
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /opt/ibm/java/jre/bin/java /opt/ibm/java/jre/bin/
|
||||
COPY --from=builder /opt/ibm/java/jre/lib/amd64/jli/libjli.so /opt/ibm/java/jre/lib/amd64/jli/
|
||||
@@ -0,0 +1,6 @@
|
||||
FROM ibmjava:8-jre@sha256:3588cd1cc9b8646fe03b3b15210e69b1b520f1321f8518b69c0e7013d702fd23 AS builder
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /opt/ibm/java/jre/bin/java /opt/ibm/java/jre/bin/
|
||||
COPY --from=builder /opt/ibm/java/jre/lib/amd64/jli/libjli.so /opt/ibm/java/jre/lib/amd64/jli/
|
||||
@@ -0,0 +1,6 @@
|
||||
FROM ibmjava:8-sdk-alpine@sha256:4f8ad2029e78f7b91721745a77fc6011a7c0e09b9edeffb6b20b6ec34a6e63cd AS builder
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /opt/ibm/java/bin/jdb /opt/ibm/java/bin/
|
||||
COPY --from=builder /opt/ibm/java/lib/amd64/jli/libjli.so /opt/ibm/java/lib/amd64/jli/
|
||||
@@ -0,0 +1,5 @@
|
||||
FROM azul/zulu-openjdk:21-jre-headless@sha256:581ebc852fcd9b52a5979b9b90dbe7cca2736bc1ecf1b9bd08ec41212d6675a4 AS builder
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /usr/lib/jvm/zulu21-ca-amd64/bin/java /usr/lib/jvm/zulu21-ca-amd64/bin/
|
||||
@@ -0,0 +1,5 @@
|
||||
FROM azul/zulu-openjdk:8-latest@sha256:7e3116bf36566e046b763b4a33f410f07f591bc84c391aae4f7891f5ecbb6764 AS builder
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /usr/lib/jvm/zulu8-ca-amd64/bin/jdb /usr/lib/jvm/zulu8-ca-amd64/bin/
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/internal"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/internal/config"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
const Path = "config.yaml"
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/OneOfOne/xxhash"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
type BinaryFromImage struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
type SnippetMetadata struct {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Package conda provides a concrete Cataloger implementation for packages within the Conda ecosystem.
|
||||
*/
|
||||
package conda
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewCondaMetaCataloger returns a new cataloger object for Conda environments by parsing the package metadata files in conda-meta.
|
||||
func NewCondaMetaCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("conda-meta-cataloger").
|
||||
WithParserByGlobs(parseCondaMetaJSON, "**/conda-meta/*.json")
|
||||
}
|
||||
|
||||
func parseCondaMetaJSON(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
dec := json.NewDecoder(reader)
|
||||
var meta pkg.CondaMetaPackage
|
||||
if err := dec.Decode(&meta); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse conda-meta package file at %s: %w", reader.RealPath, err)
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: meta.Name,
|
||||
Version: meta.Version,
|
||||
Locations: file.NewLocationSet(reader.Location),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, meta.License, reader.Location),
|
||||
),
|
||||
Language: pkg.UnknownLanguage,
|
||||
Type: pkg.CondaPkg,
|
||||
Metadata: meta,
|
||||
}
|
||||
p.SetID()
|
||||
|
||||
return []pkg.Package{
|
||||
p,
|
||||
}, nil, nil
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package conda
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func Test_CondaCataloger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
expectedPackages []pkg.Package
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "multiple packages in conda meta (python, c binaries, ...)",
|
||||
fixture: "test-fixtures/conda-meta-python-c-etc",
|
||||
wantErr: require.NoError,
|
||||
expectedPackages: []pkg.Package{
|
||||
{
|
||||
Name: "jupyterlab",
|
||||
Version: "4.4.3",
|
||||
FoundBy: "conda-meta-cataloger",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json"),
|
||||
),
|
||||
Language: pkg.UnknownLanguage,
|
||||
Type: pkg.CondaPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "BSD-3-Clause", file.NewLocation("conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json")),
|
||||
),
|
||||
Metadata: pkg.CondaMetaPackage{
|
||||
Name: "jupyterlab",
|
||||
Version: "4.4.3",
|
||||
Build: "pyhd8ed1ab_0",
|
||||
BuildNumber: 0,
|
||||
Channel: "https://conda.anaconda.org/conda-forge/",
|
||||
Subdir: "noarch",
|
||||
Noarch: "python",
|
||||
License: "BSD-3-Clause",
|
||||
LicenseFamily: "BSD",
|
||||
MD5: "4861a0c2a5a5d0481a450a9dfaf9febe",
|
||||
SHA256: "fc0235a71d852734fe92183a78cb91827367573450eba82465ae522c64230736",
|
||||
Size: 8236973,
|
||||
Timestamp: 1748273017680,
|
||||
Filename: "jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
|
||||
URL: "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
|
||||
ExtractedPackageDir: "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
|
||||
Depends: []string{
|
||||
"async-lru >=1.0.0",
|
||||
"httpx >=0.25.0",
|
||||
"importlib-metadata >=4.8.3",
|
||||
"ipykernel >=6.5.0",
|
||||
"jinja2 >=3.0.3",
|
||||
"jupyter-lsp >=2.0.0",
|
||||
"jupyter_core",
|
||||
"jupyter_server >=2.4.0,<3",
|
||||
"jupyterlab_server >=2.27.1,<3",
|
||||
"notebook-shim >=0.2",
|
||||
"packaging",
|
||||
"python >=3.9",
|
||||
"setuptools >=41.1.0",
|
||||
"tomli >=1.2.2",
|
||||
"tornado >=6.2.0",
|
||||
"traitlets",
|
||||
},
|
||||
Files: []string{
|
||||
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
|
||||
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json",
|
||||
},
|
||||
PathsData: &pkg.CondaPathsData{
|
||||
PathsVersion: 1,
|
||||
Paths: []pkg.CondaPathData{
|
||||
{
|
||||
Path: "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
|
||||
PathType: "hardlink",
|
||||
SHA256: "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
|
||||
SHA256InPrefix: "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
|
||||
SizeInBytes: 935,
|
||||
},
|
||||
{
|
||||
Path: "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json",
|
||||
PathType: "hardlink",
|
||||
SHA256: "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
|
||||
SHA256InPrefix: "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
|
||||
SizeInBytes: 1565,
|
||||
},
|
||||
},
|
||||
},
|
||||
Link: &pkg.CondaLink{
|
||||
Source: "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
|
||||
Type: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "zlib",
|
||||
Version: "1.2.11",
|
||||
FoundBy: "conda-meta-cataloger",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("conda-meta/zlib-1.2.11-h90dfc92_1014.json"),
|
||||
),
|
||||
Language: pkg.UnknownLanguage,
|
||||
Type: pkg.CondaPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Zlib", file.NewLocation("conda-meta/zlib-1.2.11-h90dfc92_1014.json")),
|
||||
),
|
||||
Metadata: pkg.CondaMetaPackage{
|
||||
Arch: "arm64",
|
||||
Name: "zlib",
|
||||
Version: "1.2.11",
|
||||
Build: "h90dfc92_1014",
|
||||
BuildNumber: 1014,
|
||||
Channel: "https://conda.anaconda.org/conda-forge/",
|
||||
Subdir: "osx-arm64",
|
||||
Noarch: "",
|
||||
License: "Zlib",
|
||||
LicenseFamily: "Other",
|
||||
MD5: "348a30b1350c9d91a4dbf05f5e46e0bb",
|
||||
SHA256: "a70c028fd3b9af1d7ea3d7099d810f3d2588096237bb472db331a51a36f931c0",
|
||||
Size: 86757,
|
||||
Timestamp: 1648307332172,
|
||||
Filename: "zlib-1.2.11-h90dfc92_1014.tar.bz2",
|
||||
URL: "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2",
|
||||
ExtractedPackageDir: "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
|
||||
Depends: []string{
|
||||
"libzlib 1.2.11 h90dfc92_1014",
|
||||
},
|
||||
Files: []string{
|
||||
"include/zconf.h",
|
||||
"include/zlib.h",
|
||||
"lib/pkgconfig/zlib.pc",
|
||||
"lib/libz.a",
|
||||
"lib/libz.dylib",
|
||||
},
|
||||
PathsData: &pkg.CondaPathsData{
|
||||
PathsVersion: 1,
|
||||
Paths: []pkg.CondaPathData{
|
||||
{
|
||||
Path: "include/zconf.h",
|
||||
PathType: "hardlink",
|
||||
SHA256: "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
|
||||
SHA256InPrefix: "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
|
||||
SizeInBytes: 16262,
|
||||
},
|
||||
{
|
||||
Path: "include/zlib.h",
|
||||
PathType: "hardlink",
|
||||
SHA256: "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
|
||||
SHA256InPrefix: "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
|
||||
SizeInBytes: 96239,
|
||||
},
|
||||
{
|
||||
Path: "lib/pkgconfig/zlib.pc",
|
||||
PathType: "hardlink",
|
||||
SHA256: "357773df3c44a5ebd77fdadd0869b5b06394bbf556c2d6c9736dd53e9df3b2c2",
|
||||
SHA256InPrefix: "5b4eb6062f97875eaadb3b6c7cf8cfeff3808798ecbf2bfc095f18dcecc509bf",
|
||||
SizeInBytes: 285,
|
||||
},
|
||||
{
|
||||
Path: "lib/libz.a",
|
||||
PathType: "hardlink",
|
||||
SHA256: "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
|
||||
SHA256InPrefix: "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
|
||||
SizeInBytes: 107128,
|
||||
},
|
||||
{
|
||||
Path: "lib/libz.dylib",
|
||||
PathType: "softlink",
|
||||
SHA256: "67ed489e2f378880f72fb1c0d1cc916e184b9632eccf2b5c1b34ddf01ed1701c",
|
||||
SHA256InPrefix: "09e47dbc60aa970e153913fe84551ad7f5aa51c21907591340a0cc999adab859",
|
||||
SizeInBytes: 122478,
|
||||
},
|
||||
},
|
||||
},
|
||||
Link: &pkg.CondaLink{
|
||||
Source: "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
|
||||
Type: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "badly formatted conda meta json file",
|
||||
fixture: "test-fixtures/conda-meta-bad-json",
|
||||
expectedPackages: nil,
|
||||
wantErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "failed to parse conda-meta package file at conda-meta/package-1.2.3-pyhd8ed1ab_0.json")
|
||||
require.Contains(t, err.Error(), "invalid character")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, test.fixture).
|
||||
Expects(test.expectedPackages, nil).
|
||||
WithErrorAssertion(test.wantErr).
|
||||
TestCataloger(t, NewCondaMetaCataloger())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCondaMetaPackageMetadata_FileOwner(t *testing.T) {
|
||||
tests := []struct {
|
||||
metadata pkg.CondaMetaPackage
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
metadata: pkg.CondaMetaPackage{
|
||||
Files: []string{
|
||||
"include/zconf.h",
|
||||
"include/zlib.h",
|
||||
"lib/pkgconfig/zlib.pc",
|
||||
"lib/libz.a",
|
||||
"lib/libz.dylib",
|
||||
},
|
||||
},
|
||||
expected: []string{
|
||||
"include/zconf.h",
|
||||
"include/zlib.h",
|
||||
"lib/libz.a",
|
||||
"lib/libz.dylib",
|
||||
"lib/pkgconfig/zlib.pc",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(strings.Join(test.expected, ","), func(t *testing.T) {
|
||||
actual := test.metadata.OwnedFiles()
|
||||
for _, d := range deep.Equal(test.expected, actual) {
|
||||
t.Errorf("diff: %+v", d)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
not json, will cause a parsing error
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"build": "pyhd8ed1ab_0",
|
||||
"build_number": 0,
|
||||
"depends": [
|
||||
"async-lru >=1.0.0",
|
||||
"httpx >=0.25.0",
|
||||
"importlib-metadata >=4.8.3",
|
||||
"ipykernel >=6.5.0",
|
||||
"jinja2 >=3.0.3",
|
||||
"jupyter-lsp >=2.0.0",
|
||||
"jupyter_core",
|
||||
"jupyter_server >=2.4.0,<3",
|
||||
"jupyterlab_server >=2.27.1,<3",
|
||||
"notebook-shim >=0.2",
|
||||
"packaging",
|
||||
"python >=3.9",
|
||||
"setuptools >=41.1.0",
|
||||
"tomli >=1.2.2",
|
||||
"tornado >=6.2.0",
|
||||
"traitlets"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"license_family": "BSD",
|
||||
"md5": "4861a0c2a5a5d0481a450a9dfaf9febe",
|
||||
"name": "jupyterlab",
|
||||
"noarch": "python",
|
||||
"sha256": "fc0235a71d852734fe92183a78cb91827367573450eba82465ae522c64230736",
|
||||
"size": 8236973,
|
||||
"subdir": "noarch",
|
||||
"timestamp": 1748273017680,
|
||||
"version": "4.4.3",
|
||||
"fn": "jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
|
||||
"url": "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
|
||||
"channel": "https://conda.anaconda.org/conda-forge/",
|
||||
"extracted_package_dir": "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
|
||||
"files": [
|
||||
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
|
||||
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json"
|
||||
],
|
||||
"paths_data": {
|
||||
"paths_version": 1,
|
||||
"paths": [
|
||||
{
|
||||
"_path": "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
|
||||
"path_type": "hardlink",
|
||||
"sha256": "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
|
||||
"sha256_in_prefix": "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
|
||||
"size_in_bytes": 935
|
||||
},
|
||||
{
|
||||
"_path": "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json",
|
||||
"path_type": "hardlink",
|
||||
"sha256": "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
|
||||
"sha256_in_prefix": "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
|
||||
"size_in_bytes": 1565
|
||||
}
|
||||
]
|
||||
},
|
||||
"link": {
|
||||
"source": "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
|
||||
"type": 1
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"arch": "arm64",
|
||||
"build": "h90dfc92_1014",
|
||||
"build_number": 1014,
|
||||
"depends": [
|
||||
"libzlib 1.2.11 h90dfc92_1014"
|
||||
],
|
||||
"license": "Zlib",
|
||||
"license_family": "Other",
|
||||
"md5": "348a30b1350c9d91a4dbf05f5e46e0bb",
|
||||
"name": "zlib",
|
||||
"platform": "osx",
|
||||
"sha256": "a70c028fd3b9af1d7ea3d7099d810f3d2588096237bb472db331a51a36f931c0",
|
||||
"size": 86757,
|
||||
"subdir": "osx-arm64",
|
||||
"timestamp": 1648307332172,
|
||||
"version": "1.2.11",
|
||||
"fn": "zlib-1.2.11-h90dfc92_1014.tar.bz2",
|
||||
"url": "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2",
|
||||
"channel": "https://conda.anaconda.org/conda-forge/",
|
||||
"extracted_package_dir": "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
|
||||
"files": [
|
||||
"include/zconf.h",
|
||||
"include/zlib.h",
|
||||
"lib/pkgconfig/zlib.pc",
|
||||
"lib/libz.a",
|
||||
"lib/libz.dylib"
|
||||
],
|
||||
"paths_data": {
|
||||
"paths_version": 1,
|
||||
"paths": [
|
||||
{
|
||||
"_path": "include/zconf.h",
|
||||
"path_type": "hardlink",
|
||||
"sha256": "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
|
||||
"sha256_in_prefix": "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
|
||||
"size_in_bytes": 16262
|
||||
},
|
||||
{
|
||||
"_path": "include/zlib.h",
|
||||
"path_type": "hardlink",
|
||||
"sha256": "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
|
||||
"sha256_in_prefix": "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
|
||||
"size_in_bytes": 96239
|
||||
},
|
||||
{
|
||||
"_path": "lib/pkgconfig/zlib.pc",
|
||||
"path_type": "hardlink",
|
||||
"sha256": "357773df3c44a5ebd77fdadd0869b5b06394bbf556c2d6c9736dd53e9df3b2c2",
|
||||
"sha256_in_prefix": "5b4eb6062f97875eaadb3b6c7cf8cfeff3808798ecbf2bfc095f18dcecc509bf",
|
||||
"size_in_bytes": 285,
|
||||
"file_mode": "text",
|
||||
"prefix_placeholder": "/Users/runner/miniforge3/conda-bld/zlib-split_1648307175973/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehol"
|
||||
},
|
||||
{
|
||||
"_path": "lib/libz.a",
|
||||
"path_type": "hardlink",
|
||||
"sha256": "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
|
||||
"sha256_in_prefix": "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
|
||||
"size_in_bytes": 107128
|
||||
},
|
||||
{
|
||||
"_path": "lib/libz.dylib",
|
||||
"path_type": "softlink",
|
||||
"sha256": "67ed489e2f378880f72fb1c0d1cc916e184b9632eccf2b5c1b34ddf01ed1701c",
|
||||
"sha256_in_prefix": "09e47dbc60aa970e153913fe84551ad7f5aa51c21907591340a0cc999adab859",
|
||||
"size_in_bytes": 122478
|
||||
}
|
||||
]
|
||||
},
|
||||
"link": {
|
||||
"source": "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
|
||||
"type": 1
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
|
||||
@@ -53,6 +53,7 @@ func (p resolvingProcessorWrapper) process(ctx context.Context, resolver file.Re
|
||||
type Cataloger struct {
|
||||
processors []processExecutor
|
||||
requesters []requester
|
||||
checks []func() error
|
||||
upstreamCataloger string
|
||||
}
|
||||
|
||||
@@ -127,6 +128,11 @@ func (c *Cataloger) WithResolvingProcessors(processors ...ResolvingProcessor) *C
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Cataloger) WithChecks(checks ...func() error) *Cataloger {
|
||||
c.checks = append(c.checks, checks...)
|
||||
return c
|
||||
}
|
||||
|
||||
func makeRequests(parser Parser, locations []file.Location) []request {
|
||||
var requests []request
|
||||
for _, l := range locations {
|
||||
@@ -152,6 +158,12 @@ func (c *Cataloger) Name() string {
|
||||
|
||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing the catalog source.
|
||||
func (c *Cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
for _, check := range c.checks {
|
||||
if err := check(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var packages []pkg.Package
|
||||
var relationships []artifact.Relationship
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"io"
|
||||
"regexp"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
// resolveModuleLicenses finds and parses license files for Go modules
|
||||
func resolveModuleLicenses(ctx context.Context, pkgInfos []pkgInfo, fs afero.Fs) pkg.LicenseSet {
|
||||
licenses := pkg.NewLicenseSet()
|
||||
|
||||
for _, info := range pkgInfos {
|
||||
modDir, pkgDir, err := getAbsolutePkgPaths(info)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
licenseFiles, err := findAllLicenseCandidatesUpwards(pkgDir, licenseRegexp, modDir, fs)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, f := range licenseFiles {
|
||||
contents, err := fs.Open(f)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
licenses.Add(pkg.NewLicensesFromReadCloserWithContext(ctx, file.NewLocationReadCloser(file.Location{}, contents))...)
|
||||
_ = contents.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return licenses
|
||||
}
|
||||
|
||||
/*
|
||||
findAllLicenseCandidatesUpwards performs a bubble-up search per package:
|
||||
1. pkgInfos represents a sparse vertical distribution of packages within modules
|
||||
2. we get more pkgInfos for free when the build configuration is updated
|
||||
|
||||
The recursion terminates via two conditions:
|
||||
- When dir is outside stopAt boundary (happy case)
|
||||
- When reaching filesystem root where parent == dir (edge case)
|
||||
|
||||
Note: The code does NOT follow symlinks. It returns a slice of absolute paths that
|
||||
represent license file matches that are resolved independently of the bubble-up.
|
||||
|
||||
When we should consider redesign tip to stem:
|
||||
- Reduced filesystem calls: Single traversal vs multiple per-package
|
||||
- Path deduplication: Avoids re-scanning common parent directories
|
||||
- Better for wide module structures: Efficient when many packages share parent paths
|
||||
- We need to consider the case here where nested modules are visited by accident and licenses
|
||||
are erroneously associated to a 'parent module'; bubble up currently prevents this
|
||||
*/
|
||||
func findAllLicenseCandidatesUpwards(dir string, r *regexp.Regexp, stopAt string, fs afero.Fs) ([]string, error) {
|
||||
// Validate that both paths are absolute
|
||||
if !filepath.IsAbs(dir) {
|
||||
return nil, fmt.Errorf("dir must be an absolute path, got: %s", dir)
|
||||
}
|
||||
if !filepath.IsAbs(stopAt) {
|
||||
return nil, fmt.Errorf("stopAt must be an absolute path, got: %s", stopAt)
|
||||
}
|
||||
|
||||
licenses, err := findLicenseCandidates(dir, r, stopAt, fs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure we return an empty slice rather than nil for consistency
|
||||
if licenses == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
return licenses, nil
|
||||
}
|
||||
|
||||
func findLicenseCandidates(dir string, r *regexp.Regexp, stopAt string, fs afero.Fs) ([]string, error) {
|
||||
// stop if we've gone outside the stopAt directory
|
||||
if !strings.HasPrefix(dir, stopAt) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
licenses, err := findLicensesInDir(dir, r, fs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parent := filepath.Dir(dir)
|
||||
// can't go any higher up the directory tree: "/" case
|
||||
if parent == dir {
|
||||
return licenses, nil
|
||||
}
|
||||
|
||||
// search parent directory and combine results
|
||||
parentLicenses, err := findLicenseCandidates(parent, r, stopAt, fs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Combine current directory licenses with parent directory licenses
|
||||
return append(licenses, parentLicenses...), nil
|
||||
}
|
||||
|
||||
func getAbsolutePkgPaths(info pkgInfo) (modDir string, pkgDir string, err error) {
|
||||
pkgDir, err = filepath.Abs(info.pkgDir)
|
||||
if err != nil {
|
||||
return modDir, pkgDir, err
|
||||
}
|
||||
|
||||
modDir, err = filepath.Abs(info.moduleDir)
|
||||
if err != nil {
|
||||
return modDir, pkgDir, err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pkgDir, modDir) {
|
||||
return modDir, pkgDir, fmt.Errorf("modDir %s should contain pkgDir %s", modDir, pkgDir)
|
||||
}
|
||||
|
||||
return modDir, pkgDir, nil
|
||||
}
|
||||
|
||||
func findLicensesInDir(dir string, r *regexp.Regexp, fs afero.Fs) ([]string, error) {
|
||||
var licenses []string
|
||||
|
||||
dirContents, err := afero.ReadDir(fs, dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, f := range dirContents {
|
||||
if f.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
if r.MatchString(f.Name()) {
|
||||
path := filepath.Join(dir, f.Name())
|
||||
licenses = append(licenses, path)
|
||||
}
|
||||
}
|
||||
|
||||
return licenses, nil
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFindAllLicenseCandidatesUpwards(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFS func(afero.Fs)
|
||||
startDir string
|
||||
stopAt string
|
||||
expectedFiles []string
|
||||
expectedError bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "normal traversal up to root",
|
||||
startDir: "/project/subdir/deeper",
|
||||
stopAt: "/project",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project/subdir/deeper", 0755)
|
||||
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
|
||||
afero.WriteFile(fs, "/project/foobar", []byte("MIT"), 0644)
|
||||
afero.WriteFile(fs, "/project/subdir/LICENSE.txt", []byte("Apache"), 0644)
|
||||
afero.WriteFile(fs, "/project/subdir/deeper/COPYING", []byte("GPL"), 0644)
|
||||
},
|
||||
expectedFiles: []string{
|
||||
"/project/subdir/deeper/COPYING",
|
||||
"/project/subdir/LICENSE.txt",
|
||||
"/project/LICENSE",
|
||||
},
|
||||
description: "Should find all license files traversing upward",
|
||||
},
|
||||
{
|
||||
name: "stops at boundary directory",
|
||||
startDir: "/project/subdir/deeper",
|
||||
stopAt: "/project/subdir",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project/subdir/deeper", 0755)
|
||||
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
|
||||
afero.WriteFile(fs, "/project/subdir/LICENSE.txt", []byte("Apache"), 0644)
|
||||
afero.WriteFile(fs, "/project/subdir/deeper/COPYING", []byte("GPL"), 0644)
|
||||
},
|
||||
expectedFiles: []string{
|
||||
"/project/subdir/deeper/COPYING",
|
||||
"/project/subdir/LICENSE.txt",
|
||||
},
|
||||
description: "Should stop at stopAt boundary and not find LICENSE in /project",
|
||||
},
|
||||
{
|
||||
name: "handles non-existent directory",
|
||||
startDir: "/nonexistent",
|
||||
stopAt: "/",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
// Don't create the directory
|
||||
},
|
||||
expectedError: true,
|
||||
description: "Should return error for non-existent directory",
|
||||
},
|
||||
{
|
||||
name: "handles empty directory tree",
|
||||
startDir: "/empty/dir/tree",
|
||||
stopAt: "/empty",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/empty/dir/tree", 0755)
|
||||
// No license files
|
||||
},
|
||||
expectedFiles: []string{},
|
||||
description: "Should return empty slice when no license files found",
|
||||
},
|
||||
{
|
||||
name: "handles directory at filesystem root",
|
||||
startDir: "/",
|
||||
stopAt: "/",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
afero.WriteFile(fs, "/LICENSE", []byte("MIT"), 0644)
|
||||
},
|
||||
expectedFiles: []string{"/LICENSE"},
|
||||
description: "Should handle traversal starting at root",
|
||||
},
|
||||
{
|
||||
name: "ignores directories with license-like names",
|
||||
startDir: "/project/subdir",
|
||||
stopAt: "/project",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project/subdir", 0755)
|
||||
fs.MkdirAll("/project/LICENSE_DIR", 0755) // Directory, should be ignored
|
||||
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
|
||||
},
|
||||
expectedFiles: []string{"/project/LICENSE"},
|
||||
description: "Should ignore directories even if they match license pattern",
|
||||
},
|
||||
{
|
||||
name: "startDir equals stopAt",
|
||||
startDir: "/project",
|
||||
stopAt: "/project",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project", 0755)
|
||||
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
|
||||
},
|
||||
expectedFiles: []string{"/project/LICENSE"},
|
||||
description: "Should handle case where start equals stop directory",
|
||||
},
|
||||
{
|
||||
name: "startDir is parent of stopAt (returns empty)",
|
||||
startDir: "/",
|
||||
stopAt: "/project",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project", 0755)
|
||||
afero.WriteFile(fs, "/LICENSE", []byte("MIT"), 0644)
|
||||
},
|
||||
expectedFiles: []string{},
|
||||
description: "Should return empty when startDir is above stopAt",
|
||||
},
|
||||
{
|
||||
name: "very deep nesting",
|
||||
startDir: "/a/b/c/d/e/f/g/h/i/j",
|
||||
stopAt: "/a",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/a/b/c/d/e/f/g/h/i/j", 0755)
|
||||
afero.WriteFile(fs, "/a/LICENSE", []byte("MIT"), 0644)
|
||||
afero.WriteFile(fs, "/a/b/c/d/e/NOTICE", []byte("Notice"), 0644)
|
||||
},
|
||||
expectedFiles: []string{
|
||||
"/a/b/c/d/e/NOTICE",
|
||||
"/a/LICENSE",
|
||||
},
|
||||
description: "Should handle deep directory nesting without stack overflow",
|
||||
},
|
||||
{
|
||||
name: "relative dir path rejected",
|
||||
startDir: "project/subdir",
|
||||
stopAt: "/project",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project/subdir", 0755)
|
||||
},
|
||||
expectedError: true,
|
||||
description: "Should reject relative dir path",
|
||||
},
|
||||
{
|
||||
name: "relative stopAt path rejected",
|
||||
startDir: "/project/subdir",
|
||||
stopAt: "project",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project/subdir", 0755)
|
||||
},
|
||||
expectedError: true,
|
||||
description: "Should reject relative stopAt path",
|
||||
},
|
||||
{
|
||||
name: "stopAt is descendant of startDir",
|
||||
startDir: "/project",
|
||||
stopAt: "/project/subdir/deeper",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project/subdir/deeper", 0755)
|
||||
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
|
||||
},
|
||||
expectedFiles: []string{},
|
||||
description: "Should return empty when stopAt is below startDir",
|
||||
},
|
||||
{
|
||||
name: "disjoint paths",
|
||||
startDir: "/foo/bar",
|
||||
stopAt: "/baz/qux",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/foo/bar", 0755)
|
||||
fs.MkdirAll("/baz/qux", 0755)
|
||||
afero.WriteFile(fs, "/foo/bar/LICENSE", []byte("MIT"), 0644)
|
||||
afero.WriteFile(fs, "/LICENSE", []byte("Root"), 0644)
|
||||
},
|
||||
expectedFiles: []string{},
|
||||
description: "Should return empty for completely disjoint paths",
|
||||
},
|
||||
{
|
||||
name: "empty stopAt rejected",
|
||||
startDir: "/project/deep/path",
|
||||
stopAt: "",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project/deep/path", 0755)
|
||||
},
|
||||
expectedError: true,
|
||||
description: "Should reject empty stopAt string",
|
||||
},
|
||||
{
|
||||
name: "empty startDir rejected",
|
||||
startDir: "",
|
||||
stopAt: "/project",
|
||||
setupFS: func(fs afero.Fs) {
|
||||
fs.MkdirAll("/project", 0755)
|
||||
},
|
||||
expectedError: true,
|
||||
description: "Should reject empty startDir string",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create in-memory filesystem
|
||||
fs := afero.NewMemMapFs()
|
||||
tt.setupFS(fs)
|
||||
|
||||
// Run the function
|
||||
result, err := findAllLicenseCandidatesUpwards(tt.startDir, licenseRegexp, tt.stopAt, fs)
|
||||
|
||||
// Check error expectation
|
||||
if tt.expectedError {
|
||||
assert.Error(t, err, tt.description)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err, tt.description)
|
||||
assert.Equal(t, tt.expectedFiles, result, tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,30 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/tools/go/packages"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
var (
|
||||
licenseRegexp = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING|NOTICE).*$`)
|
||||
)
|
||||
|
||||
type goModCataloger struct {
|
||||
licenseResolver goLicenseResolver
|
||||
}
|
||||
@@ -28,59 +38,322 @@ func newGoModCataloger(opts CatalogerConfig) *goModCataloger {
|
||||
}
|
||||
}
|
||||
|
||||
// parseGoModFile takes a go.mod and lists all packages discovered.
|
||||
//
|
||||
//nolint:funlen
|
||||
// parseGoModFile takes a go.mod and tries to resolve and lists all packages discovered.
|
||||
func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
packages := make(map[string]pkg.Package)
|
||||
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read go module: %w", err)
|
||||
}
|
||||
|
||||
f, err := modfile.Parse(reader.RealPath, contents, nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse go module: %w", err)
|
||||
}
|
||||
|
||||
modDir := filepath.Dir(string(reader.Location.Reference().RealPath))
|
||||
digests, err := parseGoSumFile(resolver, reader)
|
||||
if err != nil {
|
||||
log.Debugf("unable to get go.sum: %v", err)
|
||||
}
|
||||
|
||||
for _, m := range f.Require {
|
||||
lics := c.licenseResolver.getLicenses(ctx, resolver, m.Mod.Path, m.Mod.Version)
|
||||
packages[m.Mod.Path] = pkg.Package{
|
||||
Name: m.Mod.Path,
|
||||
Version: m.Mod.Version,
|
||||
Licenses: pkg.NewLicenseSet(lics...),
|
||||
Locations: file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(m.Mod.Path, m.Mod.Version),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{
|
||||
H1Digest: digests[fmt.Sprintf("%s %s", m.Mod.Path, m.Mod.Version)],
|
||||
},
|
||||
// source analysis using go toolchain if available
|
||||
syftSourcePackages, sourceModules, sourceDependencies, unknownErr := c.loadPackages(modDir, reader.Location)
|
||||
catalogedModules, sourceModuleToPkg := c.catalogModules(ctx, syftSourcePackages, sourceModules, reader, digests)
|
||||
relationships := buildModuleRelationships(catalogedModules, sourceDependencies, sourceModuleToPkg)
|
||||
|
||||
// base case go.mod file parsing
|
||||
modFile, err := c.parseModFileContents(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// only use mod packages NOT found in source analysis
|
||||
goModPackages := c.createGoModPackages(ctx, resolver, modFile, sourceModules, reader, digests)
|
||||
c.applyReplaceDirectives(ctx, resolver, modFile, goModPackages, reader, digests)
|
||||
c.applyExcludeDirectives(modFile, goModPackages)
|
||||
|
||||
finalPkgs := c.assembleResults(catalogedModules, goModPackages)
|
||||
return finalPkgs, relationships, unknownErr
|
||||
}
|
||||
|
||||
// loadPackages uses golang.org/x/tools/go/packages to get dependency information.
|
||||
func (c *goModCataloger) loadPackages(modDir string, loc file.Location) (pkgs map[string][]pkgInfo, modules map[string]*packages.Module, dependencies map[string][]string, unknownErr error) {
|
||||
cfg := &packages.Config{
|
||||
// Mode flags control what information is loaded for each package.
|
||||
// Performance impact increases significantly with each additional flag:
|
||||
//
|
||||
// packages.NeedModule - Required for module metadata (path, version, replace directives).
|
||||
// Essential for SBOM generation. Minimal performance impact.
|
||||
//
|
||||
// packages.NeedName - Required for package names & package Path. Minimal performance impact.
|
||||
// Needed to identify packages and filter out standard library packages.
|
||||
//
|
||||
// packages.NeedFiles - Loads source file paths for each package.
|
||||
// Moderate performance impact as it requires filesystem traversal.
|
||||
// Required for license discovery.
|
||||
//
|
||||
// packages.NeedDeps - Loads the dependency graph between packages.
|
||||
// High performance impact as it builds the complete import graph.
|
||||
// Critical for generating accurate dependency relationships in SBOM.
|
||||
//
|
||||
// packages.NeedImports - Loads import information for each package.
|
||||
// High performance impact, especially with large codebases.
|
||||
// Required for building module-to-module dependency mappings.
|
||||
//
|
||||
// Adding flags like NeedTypes, NeedSyntax, or NeedTypesInfo would dramatically
|
||||
// increase memory usage and processing time (10x+ slower) but are not needed
|
||||
// for SBOM generation as we only require dependency and module metadata.
|
||||
Mode: packages.NeedModule | packages.NeedName | packages.NeedFiles | packages.NeedDeps | packages.NeedImports,
|
||||
Dir: modDir,
|
||||
Tests: true,
|
||||
}
|
||||
|
||||
// From Go documentation: "all" expands to all packages in the main module
|
||||
// and their dependencies, including dependencies needed by tests.
|
||||
//
|
||||
// The special pattern "all" specifies all the active modules,
|
||||
// first the main module and then dependencies sorted by module path.
|
||||
// A pattern containing "..." specifies the active modules whose module paths match the pattern.
|
||||
// On implementation we could not find a test case that differentiated between all and ...
|
||||
// There may be a case where ... is non inclusive so we default to all for the inclusive guarantee
|
||||
rootPkgs, err := packages.Load(cfg, "all")
|
||||
if err != nil {
|
||||
log.Debugf("error loading packages: %v", err)
|
||||
}
|
||||
|
||||
// Check for any errors in loading
|
||||
for _, p := range rootPkgs {
|
||||
if len(p.Errors) > 0 {
|
||||
// Log errors but continue processing
|
||||
for _, e := range p.Errors {
|
||||
log.Debugf("package load error for %s: %v", p.PkgPath, e)
|
||||
unknownErr = unknown.Append(unknownErr, loc, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove any old packages and replace with new ones...
|
||||
for _, m := range f.Replace {
|
||||
lics := c.licenseResolver.getLicenses(ctx, resolver, m.New.Path, m.New.Version)
|
||||
// note: dependencies have already pruned local imports and only focuses on module => module dependencies
|
||||
return c.visitPackages(rootPkgs, loc, unknownErr)
|
||||
}
|
||||
|
||||
// the old path and new path may be the same, in which case this is a noop,
|
||||
// but if they're different we need to remove the old package.
|
||||
// note that we may change the path but we should always reference the new version (since the old version
|
||||
// cannot be trusted as a correct value).
|
||||
type pkgInfo struct {
|
||||
// pkgPath is the import path of the package.
|
||||
pkgPath string
|
||||
// modulePath is the module path of the package.
|
||||
modulePath string
|
||||
// pkgDir is the directory containing the package's source code.
|
||||
pkgDir string
|
||||
// moduleDir is the directory containing the module's source code.
|
||||
moduleDir string
|
||||
}
|
||||
|
||||
// visitPackages processes Go module import graphs to get all modules
|
||||
func (c *goModCataloger) visitPackages(
|
||||
rootPkgs []*packages.Package,
|
||||
loc file.Location,
|
||||
uke error,
|
||||
) (pkgs map[string][]pkgInfo, modules map[string]*packages.Module, dependencies map[string][]string, unknownErr error) {
|
||||
modules = make(map[string]*packages.Module)
|
||||
// note: packages are specific to inside the module - they do not include transitive pkgInfo
|
||||
// packages is used for identifying licensing documents for modules that could contain multiple licenses
|
||||
// dependencies cover transitive module imports; see p.Imports array in packages.Visit
|
||||
pkgs = make(map[string][]pkgInfo)
|
||||
// dependencies are module => module dependencies
|
||||
dependencies = make(map[string][]string)
|
||||
// persist unknown errs from previous parts of the catalog
|
||||
unknownErr = uke
|
||||
// closure (p *Package) bool
|
||||
// return bool determines whether the imports of package p are visited.
|
||||
packages.Visit(rootPkgs, func(p *packages.Package) bool {
|
||||
if len(p.Errors) > 0 {
|
||||
for _, err := range p.Errors {
|
||||
unknownErr = unknown.Append(unknownErr, loc, err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// skip for common causes
|
||||
if shouldSkipVisit(p) {
|
||||
return false
|
||||
}
|
||||
|
||||
// different from above; we still might want to visit imports
|
||||
// ignoring a package shouldn't end walking the tree
|
||||
// since we need to get the full picture for license discovery
|
||||
// for _, prefix := range c.config.IgnorePaths {
|
||||
// if strings.HasPrefix(p.PkgPath, prefix) {
|
||||
// return c.config.IncludeIgnoredDeps
|
||||
// }
|
||||
//}
|
||||
pkgDir := resolvePkgDir(p)
|
||||
if pkgDir == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
module := newModule(p.Module)
|
||||
if module.Dir == "" {
|
||||
// We continue processing even when module.Dir is empty because we still want to:
|
||||
// 1. Extract module dependencies from p.Imports for dependency graph construction
|
||||
// 2. Create syft packages with available metadata (name, version, etc.)
|
||||
// 3. Build relationships between modules even without complete filesystem info
|
||||
// Not having the DIR here just means that we're not going to process the licenses
|
||||
|
||||
// Common causes for module.Dir being empty:
|
||||
// - Vendored dependencies where Go toolchain loses some module metadata
|
||||
// - Replace directives pointing to non-existent or inaccessible paths
|
||||
// A known cause is that the module is vendored, so some information is lost.
|
||||
isVendored := strings.Contains(pkgDir, "/vendor/")
|
||||
if !isVendored {
|
||||
log.Debugf("module %s does not have dir and it's not vendored", module.Path)
|
||||
}
|
||||
}
|
||||
|
||||
// extract module dependencies
|
||||
for _, imp := range p.Imports {
|
||||
if imp.Module != nil && imp.Module.Path != module.Path {
|
||||
if dependencies[module.Path] == nil {
|
||||
dependencies[module.Path] = []string{imp.Module.Path}
|
||||
} else {
|
||||
dependencies[module.Path] = append(dependencies[module.Path], imp.Module.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
pkgs[module.Path] = append(pkgs[module.Path], pkgInfo{
|
||||
pkgPath: p.PkgPath,
|
||||
modulePath: module.Path,
|
||||
pkgDir: pkgDir,
|
||||
moduleDir: module.Dir,
|
||||
})
|
||||
modules[p.Module.Path] = module
|
||||
|
||||
return true
|
||||
}, nil)
|
||||
return pkgs, modules, dependencies, unknownErr
|
||||
}
|
||||
|
||||
// create syft packages from Go modules found by the go toolchain
|
||||
func (c *goModCataloger) catalogModules(
|
||||
ctx context.Context,
|
||||
pkgs map[string][]pkgInfo,
|
||||
modules map[string]*packages.Module,
|
||||
reader file.LocationReadCloser,
|
||||
digests map[string]string,
|
||||
) ([]pkg.Package, map[string]artifact.Identifiable) {
|
||||
syftPackages := make([]pkg.Package, 0)
|
||||
moduleToPackage := make(map[string]artifact.Identifiable)
|
||||
|
||||
for _, m := range modules {
|
||||
if isRelativeImportOrMain(m.Path) {
|
||||
// relativeImport modules are already accounted for by their full module paths at other portions of syft's cataloging
|
||||
// example: something like ../../ found as a module for go.mod b, which is sub to go.mod a is accounted for
|
||||
// in another call to the goModCataloger when go.mod a is parsed
|
||||
// local modules that use a "main" heuristic, no module naming (sometimes common pre go module support)
|
||||
// are also not built as syft packages
|
||||
continue
|
||||
}
|
||||
|
||||
pkgInfos := pkgs[m.Path]
|
||||
moduleLicenses := resolveModuleLicenses(ctx, pkgInfos, afero.NewOsFs())
|
||||
// we do out of source lookups for module parsing
|
||||
// locations are NOT included in the SBOM because of this
|
||||
goModulePkg := pkg.Package{
|
||||
Name: m.Path,
|
||||
Version: m.Version,
|
||||
Locations: file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: moduleLicenses,
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
PURL: packageURL(m.Path, m.Version),
|
||||
Metadata: createSourceMetadata(digests[fmt.Sprintf("%s %s", m.Path, m.Version)]),
|
||||
}
|
||||
goModulePkg.SetID()
|
||||
|
||||
moduleToPackage[m.Path] = goModulePkg
|
||||
syftPackages = append(syftPackages, goModulePkg)
|
||||
}
|
||||
|
||||
return syftPackages, moduleToPackage
|
||||
}
|
||||
|
||||
// buildModuleRelationships creates artifact relationships between Go modules.
|
||||
func buildModuleRelationships(
|
||||
syftPkgs []pkg.Package,
|
||||
dependencies map[string][]string,
|
||||
moduleToPkg map[string]artifact.Identifiable,
|
||||
) []artifact.Relationship {
|
||||
var rels []artifact.Relationship
|
||||
seen := make(map[string]struct{})
|
||||
|
||||
for _, fromPkg := range syftPkgs {
|
||||
for _, dep := range dependencies[fromPkg.Name] {
|
||||
if dep == fromPkg.Name {
|
||||
continue
|
||||
}
|
||||
toPkg, ok := moduleToPkg[dep]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := string(fromPkg.ID()) + string(toPkg.ID())
|
||||
if _, exists := seen[key]; exists {
|
||||
continue
|
||||
}
|
||||
|
||||
rels = append(rels, artifact.Relationship{
|
||||
From: toPkg, // dep
|
||||
To: fromPkg, // parent
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
seen[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return rels
|
||||
}
|
||||
|
||||
func (c *goModCataloger) parseModFileContents(reader file.LocationReadCloser) (*modfile.File, error) {
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read go module: %w", err)
|
||||
}
|
||||
|
||||
f, err := modfile.Parse(reader.RealPath, contents, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse go module: %w", err)
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// note this handles the deduplication from source by checking if the mod path exists in the sourceModules map
|
||||
func (c *goModCataloger) createGoModPackages(ctx context.Context, resolver file.Resolver, modFile *modfile.File, sourceModules map[string]*packages.Module, reader file.LocationReadCloser, digests map[string]string) map[string]pkg.Package {
|
||||
goModPackages := make(map[string]pkg.Package)
|
||||
|
||||
for _, m := range modFile.Require {
|
||||
if _, exists := sourceModules[m.Mod.Path]; !exists {
|
||||
lics := c.licenseResolver.getLicenses(ctx, resolver, m.Mod.Path, m.Mod.Version)
|
||||
goModPkg := pkg.Package{
|
||||
Name: m.Mod.Path,
|
||||
Version: m.Mod.Version,
|
||||
Licenses: pkg.NewLicenseSet(lics...),
|
||||
Locations: file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(m.Mod.Path, m.Mod.Version),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{
|
||||
H1Digest: digests[fmt.Sprintf("%s %s", m.Mod.Path, m.Mod.Version)],
|
||||
},
|
||||
}
|
||||
goModPkg.SetID()
|
||||
goModPackages[m.Mod.Path] = goModPkg
|
||||
}
|
||||
}
|
||||
|
||||
return goModPackages
|
||||
}
|
||||
|
||||
// applyReplaceDirectives processes replace directives from go.mod
|
||||
func (c *goModCataloger) applyReplaceDirectives(ctx context.Context, resolver file.Resolver, modFile *modfile.File, goModPackages map[string]pkg.Package, reader file.LocationReadCloser, digests map[string]string) {
|
||||
for _, m := range modFile.Replace {
|
||||
lics := c.licenseResolver.getLicenses(ctx, resolver, m.New.Path, m.New.Version)
|
||||
var finalPath string
|
||||
if !strings.HasPrefix(m.New.Path, ".") && !strings.HasPrefix(m.New.Path, "/") {
|
||||
finalPath = m.New.Path
|
||||
delete(packages, m.Old.Path)
|
||||
delete(goModPackages, m.Old.Path)
|
||||
} else {
|
||||
finalPath = m.Old.Path
|
||||
}
|
||||
packages[finalPath] = pkg.Package{
|
||||
goModPkg := pkg.Package{
|
||||
Name: finalPath,
|
||||
Version: m.New.Version,
|
||||
Licenses: pkg.NewLicenseSet(lics...),
|
||||
@@ -92,26 +365,31 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
|
||||
H1Digest: digests[fmt.Sprintf("%s %s", finalPath, m.New.Version)],
|
||||
},
|
||||
}
|
||||
goModPkg.SetID()
|
||||
goModPackages[finalPath] = goModPkg
|
||||
}
|
||||
}
|
||||
|
||||
// remove any packages from the exclude fields
|
||||
for _, m := range f.Exclude {
|
||||
delete(packages, m.Mod.Path)
|
||||
func (c *goModCataloger) applyExcludeDirectives(modFile *modfile.File, goModPackages map[string]pkg.Package) {
|
||||
for _, m := range modFile.Exclude {
|
||||
delete(goModPackages, m.Mod.Path)
|
||||
}
|
||||
}
|
||||
|
||||
pkgsSlice := make([]pkg.Package, len(packages))
|
||||
idx := 0
|
||||
for _, p := range packages {
|
||||
p.SetID()
|
||||
pkgsSlice[idx] = p
|
||||
idx++
|
||||
func (c *goModCataloger) assembleResults(catalogedPkgs []pkg.Package, goModPackages map[string]pkg.Package) []pkg.Package {
|
||||
pkgsSlice := make([]pkg.Package, 0)
|
||||
|
||||
pkgsSlice = append(pkgsSlice, catalogedPkgs...)
|
||||
|
||||
for _, p := range goModPackages {
|
||||
pkgsSlice = append(pkgsSlice, p)
|
||||
}
|
||||
|
||||
sort.SliceStable(pkgsSlice, func(i, j int) bool {
|
||||
return pkgsSlice[i].Name < pkgsSlice[j].Name
|
||||
})
|
||||
|
||||
return pkgsSlice, nil, nil
|
||||
return pkgsSlice
|
||||
}
|
||||
|
||||
func parseGoSumFile(resolver file.Resolver, reader file.LocationReadCloser) (map[string]string, error) {
|
||||
@@ -151,3 +429,94 @@ func parseGoSumFile(resolver file.Resolver, reader file.LocationReadCloser) (map
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// createSourceMetadata creates metadata for packages found through source analysis using build.Default
|
||||
func createSourceMetadata(h1Digest string) pkg.GolangSourceEntry {
|
||||
return pkg.GolangSourceEntry{
|
||||
H1Digest: h1Digest,
|
||||
OperatingSystem: build.Default.GOOS,
|
||||
Architecture: build.Default.GOARCH,
|
||||
BuildTags: strings.Join(build.Default.BuildTags, ","),
|
||||
CgoEnabled: build.Default.CgoEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
func resolvePkgDir(p *packages.Package) string {
|
||||
switch {
|
||||
case len(p.GoFiles) > 0:
|
||||
return filepath.Dir(p.GoFiles[0])
|
||||
case len(p.CompiledGoFiles) > 0:
|
||||
return filepath.Dir(p.CompiledGoFiles[0])
|
||||
case len(p.OtherFiles) > 0:
|
||||
return filepath.Dir(p.OtherFiles[0])
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func shouldSkipVisit(p *packages.Package) bool {
|
||||
// skip packages that don't have module info
|
||||
if p.Module == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// skip stdlib
|
||||
if isStdLib(p) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isStdLib returns true if this package is part of the Go standard library.
|
||||
func isStdLib(pkg *packages.Package) bool {
|
||||
if pkg.Name == "unsafe" {
|
||||
// Special case unsafe stdlib, because it does not contain go files.
|
||||
return true
|
||||
}
|
||||
if len(pkg.GoFiles) == 0 {
|
||||
return false
|
||||
}
|
||||
prefix := build.Default.GOROOT
|
||||
sep := string(filepath.Separator)
|
||||
if !strings.HasSuffix(prefix, sep) {
|
||||
prefix += sep
|
||||
}
|
||||
return strings.HasPrefix(pkg.GoFiles[0], prefix)
|
||||
}
|
||||
|
||||
// handle replace directives
|
||||
func newModule(mod *packages.Module) *packages.Module {
|
||||
// Example of a module with replace directive: k8s.io/kubernetes => k8s.io/kubernetes v1.11.1
|
||||
// {
|
||||
// "Path": "k8s.io/kubernetes",
|
||||
// "Version": "v0.17.9",
|
||||
// "Replace": {
|
||||
// "Path": "k8s.io/kubernetes",
|
||||
// "Version": "v1.11.1",
|
||||
// "Time": "2018-07-17T04:20:29Z",
|
||||
// "Dir": "/home/gongyuan_kubeflow_org/go/pkg/mod/k8s.io/kubernetes@v1.11.1",
|
||||
// "GoMod": "/home/gongyuan_kubeflow_org/go/pkg/mod/cache/download/k8s.io/kubernetes/@v/v1.11.1.mod"
|
||||
// },
|
||||
// "Dir": "/home/gongyuan_kubeflow_org/go/pkg/mod/k8s.io/kubernetes@v1.11.1",
|
||||
// "GoMod": "/home/gongyuan_kubeflow_org/go/pkg/mod/cache/download/k8s.io/kubernetes/@v/v1.11.1.mod"
|
||||
// }
|
||||
// handle replace directives
|
||||
// Note, we specifically want to replace version field.
|
||||
// Haven't confirmed, but we may also need to override the
|
||||
// entire struct when using replace directive with local folders.
|
||||
tmp := *mod
|
||||
if tmp.Replace != nil {
|
||||
tmp = *tmp.Replace
|
||||
}
|
||||
|
||||
return &tmp
|
||||
}
|
||||
|
||||
func isRelativeImportOrMain(p string) bool {
|
||||
if p == "main" {
|
||||
return true
|
||||
}
|
||||
// true for ".", "..", "./...", "../..."
|
||||
return build.IsLocalImport(p)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/fileresolver"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -15,13 +20,13 @@ func TestParseGoMod(t *testing.T) {
|
||||
expected []pkg.Package
|
||||
}{
|
||||
{
|
||||
fixture: "test-fixtures/one-package",
|
||||
fixture: "test-fixtures/go-mod-fixtures/one-package/go.mod",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/bmatcuk/doublestar",
|
||||
Version: "v1.3.1",
|
||||
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v1.3.1",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/one-package")),
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/one-package/go.mod")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
@@ -29,28 +34,28 @@ func TestParseGoMod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
fixture: "test-fixtures/relative-replace",
|
||||
fixture: "test-fixtures/go-mod-fixtures/relative-replace/go.mod",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/aws/aws-sdk-go-v2",
|
||||
Version: "",
|
||||
PURL: "pkg:golang/github.com/aws/aws-sdk-go-v2",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/relative-replace")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/relative-replace/go.mod")),
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
fixture: "test-fixtures/many-packages",
|
||||
fixture: "test-fixtures/go-mod-fixtures/many-packages/go.mod",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/anchore/archiver/v3",
|
||||
Version: "v3.5.2",
|
||||
PURL: "pkg:golang/github.com/anchore/archiver@v3.5.2#v3",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
@@ -59,7 +64,7 @@ func TestParseGoMod(t *testing.T) {
|
||||
Name: "github.com/anchore/go-testutils",
|
||||
Version: "v0.0.0-20200624184116-66aa578126db",
|
||||
PURL: "pkg:golang/github.com/anchore/go-testutils@v0.0.0-20200624184116-66aa578126db",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
@@ -68,7 +73,7 @@ func TestParseGoMod(t *testing.T) {
|
||||
Name: "github.com/anchore/go-version",
|
||||
Version: "v1.2.2-0.20200701162849-18adb9c92b9b",
|
||||
PURL: "pkg:golang/github.com/anchore/go-version@v1.2.2-0.20200701162849-18adb9c92b9b",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
@@ -77,7 +82,7 @@ func TestParseGoMod(t *testing.T) {
|
||||
Name: "github.com/anchore/stereoscope",
|
||||
Version: "v0.0.0-20200706164556-7cf39d7f4639",
|
||||
PURL: "pkg:golang/github.com/anchore/stereoscope@v0.0.0-20200706164556-7cf39d7f4639",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
@@ -86,7 +91,7 @@ func TestParseGoMod(t *testing.T) {
|
||||
Name: "github.com/bmatcuk/doublestar",
|
||||
Version: "v8.8.8",
|
||||
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v8.8.8",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
@@ -95,7 +100,7 @@ func TestParseGoMod(t *testing.T) {
|
||||
Name: "github.com/go-test/deep",
|
||||
Version: "v1.0.6",
|
||||
PURL: "pkg:golang/github.com/go-test/deep@v1.0.6",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
@@ -179,3 +184,156 @@ func Test_corruptGoMod(t *testing.T) {
|
||||
WithError().
|
||||
TestCataloger(t, c)
|
||||
}
|
||||
|
||||
func Test_parseGoSource_packageResolution(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixturePath string
|
||||
config CatalogerConfig
|
||||
expectedPkgs []string
|
||||
expectedRels []string
|
||||
expectedLicenses map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "go-source with direct, transitive, and deps of transitive",
|
||||
fixturePath: filepath.Join("test-fixtures", "go-source"),
|
||||
expectedPkgs: []string{
|
||||
"anchore.io/not/real @ (go.mod)",
|
||||
"github.com/davecgh/go-spew @ v1.1.1 (go.mod)",
|
||||
"github.com/go-viper/mapstructure/v2 @ v2.2.1 (go.mod)",
|
||||
"github.com/google/uuid @ v1.6.0 (go.mod)",
|
||||
"github.com/pmezard/go-difflib @ v1.0.0 (go.mod)",
|
||||
"github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
|
||||
"github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
|
||||
"github.com/sourcegraph/conc @ v0.3.0 (go.mod)",
|
||||
"github.com/spf13/afero @ v1.12.0 (go.mod)",
|
||||
"github.com/spf13/cast @ v1.7.1 (go.mod)",
|
||||
"github.com/spf13/pflag @ v1.0.6 (go.mod)",
|
||||
"github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod)",
|
||||
"github.com/subosito/gotenv @ v1.6.0 (go.mod)",
|
||||
"go.uber.org/multierr @ v1.10.0 (go.mod)",
|
||||
"go.uber.org/zap @ v1.27.0 (go.mod)",
|
||||
"golang.org/x/sys @ v0.33.0 (go.mod)",
|
||||
"golang.org/x/text @ v0.21.0 (go.mod)",
|
||||
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod)",
|
||||
"github.com/fsnotify/fsnotify @ v1.8.0 (go.mod)",
|
||||
"github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod)",
|
||||
"github.com/frankban/quicktest @ v1.14.6 (go.mod)",
|
||||
"github.com/google/go-cmp @ v0.6.0 (go.mod)",
|
||||
"github.com/kr/pretty @ v0.3.1 (go.mod)",
|
||||
"github.com/kr/text @ v0.2.0 (go.mod)",
|
||||
"github.com/rogpeppe/go-internal @ v1.9.0 (go.mod)",
|
||||
"go.uber.org/goleak @ v1.3.0 (go.mod)",
|
||||
"gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod)",
|
||||
},
|
||||
expectedRels: []string{
|
||||
"github.com/davecgh/go-spew @ v1.1.1 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
|
||||
"github.com/frankban/quicktest @ v1.14.6 (go.mod) [dependency-of] github.com/spf13/cast @ v1.7.1 (go.mod)",
|
||||
"github.com/fsnotify/fsnotify @ v1.8.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/go-viper/mapstructure/v2 @ v2.2.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/google/go-cmp @ v0.6.0 (go.mod) [dependency-of] github.com/frankban/quicktest @ v1.14.6 (go.mod)",
|
||||
"github.com/google/uuid @ v1.6.0 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
|
||||
"github.com/kr/pretty @ v0.3.1 (go.mod) [dependency-of] github.com/frankban/quicktest @ v1.14.6 (go.mod)",
|
||||
"github.com/kr/pretty @ v0.3.1 (go.mod) [dependency-of] gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod)",
|
||||
"github.com/kr/text @ v0.2.0 (go.mod) [dependency-of] github.com/kr/pretty @ v0.3.1 (go.mod)",
|
||||
"github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/pmezard/go-difflib @ v1.0.0 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
|
||||
"github.com/rogpeppe/go-internal @ v1.9.0 (go.mod) [dependency-of] github.com/kr/pretty @ v0.3.1 (go.mod)",
|
||||
"github.com/sagikazarmark/locafero @ v0.7.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/sirupsen/logrus @ v1.9.3 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
|
||||
"github.com/sourcegraph/conc @ v0.3.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
|
||||
"github.com/spf13/afero @ v1.12.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
|
||||
"github.com/spf13/afero @ v1.12.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/spf13/cast @ v1.7.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/spf13/pflag @ v1.0.6 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/spf13/viper @ v1.20.1 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sourcegraph/conc @ v0.3.0 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/subosito/gotenv @ v1.6.0 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] go.uber.org/multierr @ v1.10.0 (go.mod)",
|
||||
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
|
||||
"github.com/subosito/gotenv @ v1.6.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"go.uber.org/goleak @ v1.3.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
|
||||
"go.uber.org/multierr @ v1.10.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
|
||||
"go.uber.org/zap @ v1.27.0 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
|
||||
"golang.org/x/sys @ v0.33.0 (go.mod) [dependency-of] github.com/fsnotify/fsnotify @ v1.8.0 (go.mod)",
|
||||
"golang.org/x/sys @ v0.33.0 (go.mod) [dependency-of] github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
|
||||
"golang.org/x/text @ v0.21.0 (go.mod) [dependency-of] github.com/spf13/afero @ v1.12.0 (go.mod)",
|
||||
"golang.org/x/text @ v0.21.0 (go.mod) [dependency-of] github.com/subosito/gotenv @ v1.6.0 (go.mod)",
|
||||
"gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod) [dependency-of] gopkg.in/yaml.v3 @ v3.0.1 (go.mod)",
|
||||
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
|
||||
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
|
||||
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
|
||||
},
|
||||
expectedLicenses: map[string][]string{
|
||||
"github.com/fsnotify/fsnotify": {"BSD-3-Clause"},
|
||||
"github.com/go-viper/mapstructure/v2": {"MIT"},
|
||||
"github.com/google/uuid": {"BSD-3-Clause"},
|
||||
"github.com/pelletier/go-toml/v2": {"MIT"},
|
||||
"github.com/sagikazarmark/locafero": {"MIT"},
|
||||
"github.com/sirupsen/logrus": {"MIT"},
|
||||
"github.com/sourcegraph/conc": {"MIT"},
|
||||
"github.com/spf13/afero": {"Apache-2.0"},
|
||||
"github.com/spf13/cast": {"MIT"},
|
||||
"github.com/spf13/pflag": {"BSD-3-Clause"},
|
||||
"github.com/spf13/viper": {"MIT"},
|
||||
"github.com/subosito/gotenv": {"MIT"},
|
||||
"go.uber.org/multierr": {"MIT"},
|
||||
"go.uber.org/zap": {"MIT"},
|
||||
"golang.org/x/sys": {"BSD-3-Clause"},
|
||||
"golang.org/x/text": {"BSD-3-Clause"},
|
||||
"gopkg.in/yaml.v3": {"Apache-2.0", "MIT"},
|
||||
"github.com/davecgh/go-spew": {"ISC"},
|
||||
"github.com/pmezard/go-difflib": {"BSD-3-Clause"},
|
||||
"github.com/stretchr/testify": {"MIT"},
|
||||
"github.com/frankban/quicktest": {"MIT"},
|
||||
"github.com/google/go-cmp": {"BSD-3-Clause"},
|
||||
"github.com/kr/text": {"MIT"},
|
||||
"github.com/kr/pretty": {"MIT"},
|
||||
"github.com/rogpeppe/go-internal": {"BSD-3-Clause"},
|
||||
"go.uber.org/goleak": {"MIT"},
|
||||
"gopkg.in/check.v1": {"BSD-2-Clause"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, tt.fixturePath).
|
||||
ExpectsPackageStrings(tt.expectedPkgs).
|
||||
ExpectsRelationshipStrings(tt.expectedRels).
|
||||
ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
for _, p := range pkgs {
|
||||
if metadata, ok := p.Metadata.(pkg.GolangSourceEntry); ok {
|
||||
// Validate that GolangSourceEntry metadata is present but don't assert on specific field values
|
||||
// since these might vary across development machines
|
||||
require.IsType(t, pkg.GolangSourceEntry{}, metadata, "expected GolangSourceEntry metadata for package %s", p.Name)
|
||||
// Verify that the metadata struct is populated (non-zero values indicate go source method was used)
|
||||
require.NotEmpty(t, metadata, "GolangSourceEntry metadata should not be empty for package %s", p.Name)
|
||||
}
|
||||
}
|
||||
}).
|
||||
ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
actualLicenses := make(map[string][]string)
|
||||
for _, p := range pkgs {
|
||||
for _, l := range p.Licenses.ToSlice() {
|
||||
if actualLicenses[p.Name] == nil {
|
||||
actualLicenses[p.Name] = make([]string, 0)
|
||||
}
|
||||
actualLicenses[p.Name] = append(actualLicenses[p.Name], l.Value)
|
||||
}
|
||||
}
|
||||
if diff := cmp.Diff(tt.expectedLicenses, actualLicenses); diff != "" {
|
||||
t.Errorf("mismatch in licenses (-want +got):\n%s", diff)
|
||||
}
|
||||
}).
|
||||
TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
github.com/bmatcuk/doublestar v1.3.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"anchore.io/not/real/pk1"
|
||||
"anchore.io/not/real/pk2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pk1.Test()
|
||||
pk2.Test()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"anchore.io/not/real/pk1"
|
||||
"anchore.io/not/real/pk3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pk1.Test()
|
||||
pk3.Zap()
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
module anchore.io/not/real
|
||||
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/viper v1.20.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.uber.org/zap v1.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user