mirror of
https://github.com/anchore/syft.git
synced 2026-10-23 03:22:21 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c1f087fd7 |
+1
-1
@@ -90,7 +90,7 @@ tools:
|
||||
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.39.2
|
||||
want: v2.39.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
|
||||
+1
-77
@@ -1,78 +1,2 @@
|
||||
enforce-v0: true # don't make breaking-change label bump major version before 1.0.
|
||||
title: ""
|
||||
|
||||
github:
|
||||
host: github.com
|
||||
include-issue-pr-authors: true
|
||||
include-issue-prs: true
|
||||
include-issues-not-planned: false
|
||||
include-prs: true
|
||||
include-issues: true
|
||||
include-unlabeled-issues: true
|
||||
include-unlabeled-prs: true
|
||||
issues-require-linked-prs: false
|
||||
consider-pr-merge-commits: true
|
||||
|
||||
exclude-labels:
|
||||
- duplicate
|
||||
- question
|
||||
- invalid
|
||||
- wontfix
|
||||
- wont-fix
|
||||
- release-ignore
|
||||
- changelog-ignore
|
||||
- ignore
|
||||
|
||||
changes:
|
||||
|
||||
- name: security-fixes
|
||||
title: Security Fixes
|
||||
semver-field: patch
|
||||
labels:
|
||||
- security
|
||||
- vulnerability
|
||||
|
||||
- name: added-feature
|
||||
title: Added Features
|
||||
semver-field: minor
|
||||
labels:
|
||||
- enhancement
|
||||
- feature
|
||||
- minor
|
||||
|
||||
- name: bug-fix
|
||||
title: Bug Fixes
|
||||
semver-field: patch
|
||||
labels:
|
||||
- bug
|
||||
- fix
|
||||
- bug-fix
|
||||
- patch
|
||||
|
||||
- name: breaking-feature
|
||||
title: Breaking Changes
|
||||
semver-field: major
|
||||
labels:
|
||||
- breaking
|
||||
- backwards-incompatible
|
||||
- breaking-change
|
||||
- breaking-feature
|
||||
- major
|
||||
- detected-breaking-change
|
||||
|
||||
- name: removed-feature
|
||||
title: Removed Features
|
||||
semver-field: major
|
||||
labels:
|
||||
- removed
|
||||
|
||||
- name: deprecated-feature
|
||||
title: Deprecated Features
|
||||
semver-field: minor
|
||||
labels:
|
||||
- deprecated
|
||||
|
||||
- name: unknown
|
||||
title: Additional Changes
|
||||
semver-field: ""
|
||||
labels: []
|
||||
title: ""
|
||||
@@ -8,12 +8,6 @@ import re
|
||||
|
||||
DRY_RUN = False
|
||||
|
||||
JSON_SCHEMA_LABEL = "json-schema"
|
||||
|
||||
# note: we can't use "breaking-change" as the label since that might be applied manually by a user. This is a
|
||||
# distinct label that we can use to indicate that the label was applied (or removed) by automation.
|
||||
BREAKING_CHANGE_LABEL = "detected-breaking-change"
|
||||
|
||||
|
||||
def main(changed_files: str | None = None, merge_base_schema_files: str | None = None):
|
||||
global DRY_RUN
|
||||
@@ -73,18 +67,17 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
|
||||
# if there is a new or modified schema, we should add the "json-schema" label to the PR...
|
||||
if new_schema_files or removed_or_modified_schema_files:
|
||||
print("\nAdding json-schema label...")
|
||||
add_label(pr_number, JSON_SCHEMA_LABEL)
|
||||
|
||||
add_label(pr_number, "json-schema")
|
||||
else:
|
||||
remove_label(pr_number, JSON_SCHEMA_LABEL)
|
||||
remove_label(pr_number, "json-schema")
|
||||
|
||||
# new schema files should be scrutinized, comparing the latest and added versions to see if it's a breaking
|
||||
# change (major version bump). Warn about it on the PR via adding a breaking-change label...
|
||||
if is_breaking_change(new_schema_files, og_json_schema_files[-1]):
|
||||
print("\nBreaking change detected...")
|
||||
add_label(pr_number, BREAKING_CHANGE_LABEL)
|
||||
add_label(pr_number, "breaking-change")
|
||||
else:
|
||||
remove_label(pr_number, BREAKING_CHANGE_LABEL)
|
||||
remove_label(pr_number, "breaking-change")
|
||||
|
||||
# modifying an existing schema could be a breaking change, we should warn about it on the PR via a comment...
|
||||
# removing schema files should never be allowed, we should warn about it on the PR via a comment...
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 #v2.22.8
|
||||
uses: github/codeql-action/init@66b90a5db151a8042fa97405c6cf843bbe433f7b #v2.22.7
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -56,7 +56,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@407ffafae6a767df3e0230c3df91b6443ae8df75 #v2.22.8
|
||||
uses: github/codeql-action/autobuild@66b90a5db151a8042fa97405c6cf843bbe433f7b #v2.22.7
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -70,4 +70,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 #v2.22.8
|
||||
uses: github/codeql-action/analyze@66b90a5db151a8042fa97405c6cf843bbe433f7b #v2.22.7
|
||||
|
||||
@@ -141,7 +141,7 @@ jobs:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
- uses: anchore/sbom-action@fd74a6fb98a204a1ad35bbfae0122c1a302ff88b #v0.15.0
|
||||
- uses: anchore/sbom-action@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1 #v0.14.3
|
||||
continue-on-error: true
|
||||
with:
|
||||
artifact-name: sbom.spdx.json
|
||||
|
||||
@@ -91,8 +91,5 @@ func create(id clio.Identification, out io.Writer) (clio.Application, *cobra.Com
|
||||
cranecmd.NewCmdAuthLogin(id.Name), // syft login uses the same command as crane
|
||||
)
|
||||
|
||||
// explicitly set Cobra output to the real stdout to write things like errors and help
|
||||
rootCmd.SetOut(out)
|
||||
|
||||
return app, rootCmd
|
||||
}
|
||||
|
||||
+116
-128
@@ -2,7 +2,6 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
@@ -12,14 +11,15 @@ import (
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/format/cyclonedxjson"
|
||||
"github.com/anchore/syft/syft/format/spdxjson"
|
||||
"github.com/anchore/syft/syft/format/spdxtagvalue"
|
||||
@@ -33,7 +33,6 @@ const (
|
||||
`
|
||||
attestSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp
|
||||
attestHelp = attestExample + attestSchemeHelp
|
||||
cosignBinName = "cosign"
|
||||
)
|
||||
|
||||
type attestOptions struct {
|
||||
@@ -47,7 +46,24 @@ type attestOptions struct {
|
||||
func Attest(app clio.Application) *cobra.Command {
|
||||
id := app.ID()
|
||||
|
||||
opts := defaultAttestOptions()
|
||||
opts := &attestOptions{
|
||||
UpdateCheck: options.DefaultUpdateCheck(),
|
||||
Output: options.Output{
|
||||
AllowMultipleOutputs: false,
|
||||
AllowableOptions: []string{
|
||||
string(syftjson.ID),
|
||||
string(cyclonedxjson.ID),
|
||||
string(spdxjson.ID),
|
||||
string(spdxtagvalue.ID),
|
||||
},
|
||||
Outputs: []string{syftjson.ID.String()},
|
||||
OutputFile: options.OutputFile{ // nolint:staticcheck
|
||||
Enabled: false, // explicitly not allowed
|
||||
},
|
||||
Format: options.DefaultFormat(),
|
||||
},
|
||||
Catalog: options.DefaultCatalog(),
|
||||
}
|
||||
|
||||
// template format explicitly not allowed
|
||||
opts.Format.Template.Enabled = false
|
||||
@@ -66,96 +82,83 @@ func Attest(app clio.Application) *cobra.Command {
|
||||
restoreStdout := ui.CaptureStdoutToTraceLog()
|
||||
defer restoreStdout()
|
||||
|
||||
return runAttest(id, &opts, args[0])
|
||||
return runAttest(id, opts, args[0])
|
||||
},
|
||||
}, &opts)
|
||||
}
|
||||
|
||||
func defaultAttestOptions() attestOptions {
|
||||
return attestOptions{
|
||||
Output: defaultAttestOutputOptions(),
|
||||
UpdateCheck: options.DefaultUpdateCheck(),
|
||||
Catalog: options.DefaultCatalog(),
|
||||
}
|
||||
}
|
||||
|
||||
func defaultAttestOutputOptions() options.Output {
|
||||
return options.Output{
|
||||
AllowMultipleOutputs: false,
|
||||
AllowToFile: false,
|
||||
AllowableOptions: []string{
|
||||
string(syftjson.ID),
|
||||
string(cyclonedxjson.ID),
|
||||
string(spdxjson.ID),
|
||||
string(spdxtagvalue.ID),
|
||||
},
|
||||
Outputs: []string{syftjson.ID.String()},
|
||||
OutputFile: options.OutputFile{ // nolint:staticcheck
|
||||
Enabled: false, // explicitly not allowed
|
||||
},
|
||||
Format: options.DefaultFormat(),
|
||||
}
|
||||
}, opts)
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
func runAttest(id clio.Identification, opts *attestOptions, userInput string) error {
|
||||
// TODO: what other validation here besides binary name?
|
||||
if !commandExists(cosignBinName) {
|
||||
return fmt.Errorf("'syft attest' requires cosign to be installed, however it does not appear to be on PATH")
|
||||
_, err := exec.LookPath("cosign")
|
||||
if err != nil {
|
||||
// when cosign is not installed the error will be rendered like so:
|
||||
// 2023/06/30 08:31:52 error during command execution: 'syft attest' requires cosign to be installed: exec: "cosign": executable file not found in $PATH
|
||||
return fmt.Errorf("'syft attest' requires cosign to be installed: %w", err)
|
||||
}
|
||||
|
||||
s, err := buildSBOM(id, &opts.Catalog, userInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to build SBOM: %w", err)
|
||||
}
|
||||
|
||||
// this is the file that will contain the SBOM being attested
|
||||
f, err := os.CreateTemp("", "syft-attest-")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create temp file: %w", err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
s, err := generateSBOMForAttestation(id, &opts.Catalog, userInput)
|
||||
writer, err := opts.SBOMWriter()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to build SBOM: %w", err)
|
||||
return fmt.Errorf("unable to create SBOM writer: %w", err)
|
||||
}
|
||||
|
||||
if err = writeSBOMToFormattedFile(s, f, opts); err != nil {
|
||||
return fmt.Errorf("unable to write SBOM to file: %w", err)
|
||||
if err := writer.Write(*s); err != nil {
|
||||
return fmt.Errorf("unable to write SBOM to temp file: %w", err)
|
||||
}
|
||||
|
||||
if err = createAttestation(f.Name(), opts, userInput); err != nil {
|
||||
return err
|
||||
// TODO: what other validation here besides binary name?
|
||||
cmd := "cosign"
|
||||
if !commandExists(cmd) {
|
||||
return fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
|
||||
}
|
||||
|
||||
bus.Notify("Attestation has been created, please check your registry for the output or use the cosign command:")
|
||||
bus.Notify(fmt.Sprintf("cosign download attestation %s", userInput))
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSBOMToFormattedFile(s *sbom.SBOM, sbomFile io.Writer, opts *attestOptions) error {
|
||||
if sbomFile == nil {
|
||||
return fmt.Errorf("no output file provided")
|
||||
outputNames := opts.OutputNameSet()
|
||||
var outputName string
|
||||
switch outputNames.Size() {
|
||||
case 0:
|
||||
return fmt.Errorf("no output format specified")
|
||||
case 1:
|
||||
outputName = outputNames.List()[0]
|
||||
default:
|
||||
return fmt.Errorf("multiple output formats specified: %s", strings.Join(outputNames.List(), ", "))
|
||||
}
|
||||
|
||||
encs, err := opts.Format.Encoders()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create encoders: %w", err)
|
||||
// Select Cosign predicate type based on defined output type
|
||||
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
|
||||
var predicateType string
|
||||
switch strings.ToLower(outputName) {
|
||||
case "cyclonedx-json":
|
||||
predicateType = "cyclonedx"
|
||||
case "spdx-tag-value", "spdx-tv":
|
||||
predicateType = "spdx"
|
||||
case "spdx-json", "json":
|
||||
predicateType = "spdxjson"
|
||||
default:
|
||||
predicateType = "custom"
|
||||
}
|
||||
|
||||
encoders := format.NewEncoderCollection(encs...)
|
||||
encoder := encoders.GetByString(opts.Outputs[0])
|
||||
if encoder == nil {
|
||||
return fmt.Errorf("unable to find encoder for %q", opts.Outputs[0])
|
||||
args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
|
||||
if opts.Attest.Key != "" {
|
||||
args = append(args, "--key", opts.Attest.Key.String())
|
||||
}
|
||||
|
||||
if err = encoder.Encode(sbomFile, *s); err != nil {
|
||||
return fmt.Errorf("unable to encode SBOM: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createAttestation(sbomFilepath string, opts *attestOptions, userInput string) error {
|
||||
execCmd, err := attestCommand(sbomFilepath, opts, userInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to craft attest command: %w", err)
|
||||
execCmd := exec.Command(cmd, args...)
|
||||
execCmd.Env = os.Environ()
|
||||
if opts.Attest.Key != "" {
|
||||
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", opts.Attest.Password))
|
||||
} else {
|
||||
// no key provided, use cosign's keyless mode
|
||||
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
|
||||
}
|
||||
|
||||
log.WithFields("cmd", strings.Join(execCmd.Args, " ")).Trace("creating attestation")
|
||||
@@ -198,67 +201,59 @@ func createAttestation(sbomFilepath string, opts *attestOptions, userInput strin
|
||||
}
|
||||
|
||||
mon.SetCompleted()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func attestCommand(sbomFilepath string, opts *attestOptions, userInput string) (*exec.Cmd, error) {
|
||||
outputNames := opts.OutputNameSet()
|
||||
var outputName string
|
||||
switch outputNames.Size() {
|
||||
case 0:
|
||||
return nil, fmt.Errorf("no output format specified")
|
||||
case 1:
|
||||
outputName = outputNames.List()[0]
|
||||
default:
|
||||
return nil, fmt.Errorf("multiple output formats specified: %s", strings.Join(outputNames.List(), ", "))
|
||||
func buildSBOM(id clio.Identification, opts *options.Catalog, userInput string) (*sbom.SBOM, error) {
|
||||
cfg := source.DetectConfig{
|
||||
DefaultImageSource: opts.DefaultImagePullSource,
|
||||
}
|
||||
|
||||
args := []string{"attest", userInput, "--predicate", sbomFilepath, "--type", predicateType(outputName), "-y"}
|
||||
if opts.Attest.Key != "" {
|
||||
args = append(args, "--key", opts.Attest.Key.String())
|
||||
}
|
||||
|
||||
execCmd := exec.Command(cosignBinName, args...)
|
||||
execCmd.Env = os.Environ()
|
||||
if opts.Attest.Key != "" {
|
||||
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", opts.Attest.Password))
|
||||
} else {
|
||||
// no key provided, use cosign's keyless mode
|
||||
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
|
||||
}
|
||||
|
||||
return execCmd, nil
|
||||
}
|
||||
|
||||
func predicateType(outputName string) string {
|
||||
// Select Cosign predicate type based on defined output type
|
||||
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
|
||||
switch strings.ToLower(outputName) {
|
||||
case "cyclonedx-json":
|
||||
return "cyclonedx"
|
||||
case "spdx-tag-value", "spdx-tv":
|
||||
return "spdx"
|
||||
case "spdx-json", "json":
|
||||
return "spdxjson"
|
||||
default:
|
||||
return "custom"
|
||||
}
|
||||
}
|
||||
|
||||
func generateSBOMForAttestation(id clio.Identification, opts *options.Catalog, userInput string) (*sbom.SBOM, error) {
|
||||
src, err := getSource(opts, userInput, onlyContainerImages)
|
||||
|
||||
detection, err := source.Detect(userInput, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not deteremine source: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if src != nil {
|
||||
if err := src.Close(); err != nil {
|
||||
log.Tracef("unable to close source: %+v", err)
|
||||
}
|
||||
if detection.IsContainerImage() {
|
||||
return nil, fmt.Errorf("attestations are only supported for oci images at this time")
|
||||
}
|
||||
|
||||
var platform *image.Platform
|
||||
|
||||
if opts.Platform != "" {
|
||||
platform, err = image.NewPlatform(opts.Platform)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid platform: %w", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
hashers, err := file.Hashers(opts.Source.File.Digests...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid hash: %w", err)
|
||||
}
|
||||
|
||||
src, err := detection.NewSource(
|
||||
source.DetectionSourceConfig{
|
||||
Alias: source.Alias{
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
},
|
||||
RegistryOptions: opts.Registry.ToOptions(),
|
||||
Platform: platform,
|
||||
Exclude: source.ExcludeConfig{
|
||||
Paths: opts.Exclusions,
|
||||
},
|
||||
DigestAlgorithms: hashers,
|
||||
BasePath: opts.BasePath,
|
||||
},
|
||||
)
|
||||
|
||||
if src != nil {
|
||||
defer src.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
||||
}
|
||||
|
||||
s, err := generateSBOM(id, src, opts)
|
||||
if err != nil {
|
||||
@@ -272,13 +267,6 @@ func generateSBOMForAttestation(id clio.Identification, opts *options.Catalog, u
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func onlyContainerImages(d *source.Detection) error {
|
||||
if !d.IsContainerImage() {
|
||||
return fmt.Errorf("attestations are only supported for oci images at this time")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func commandExists(cmd string) bool {
|
||||
_, err := exec.LookPath(cmd)
|
||||
return err == nil
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_writeSBOMToFormattedFile(t *testing.T) {
|
||||
type args struct {
|
||||
s *sbom.SBOM
|
||||
opts *attestOptions
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantSbomFile string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "go case",
|
||||
args: args{
|
||||
opts: &attestOptions{
|
||||
Output: func() options.Output {
|
||||
def := defaultAttestOutputOptions()
|
||||
def.Outputs = []string{"syft-json"}
|
||||
return def
|
||||
}(),
|
||||
},
|
||||
s: &sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{},
|
||||
Relationships: nil,
|
||||
Source: source.Description{
|
||||
ID: "source-id",
|
||||
Name: "source-name",
|
||||
Version: "source-version",
|
||||
},
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: "syft-test",
|
||||
Version: "non-version",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantSbomFile: `{
|
||||
"artifacts": [],
|
||||
"artifactRelationships": [],
|
||||
"source": {
|
||||
"id": "source-id",
|
||||
"name": "source-name",
|
||||
"version": "source-version",
|
||||
"type": "",
|
||||
"metadata": null
|
||||
},
|
||||
"distro": {},
|
||||
"descriptor": {
|
||||
"name": "syft-test",
|
||||
"version": "non-version"
|
||||
},
|
||||
"schema": {}
|
||||
}`,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sbomFile := &bytes.Buffer{}
|
||||
|
||||
err := writeSBOMToFormattedFile(tt.args.s, sbomFile, tt.args.opts)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("writeSBOMToFormattedFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// redact the schema block
|
||||
re := regexp.MustCompile(`(?s)"schema":\W*\{.*?},?`)
|
||||
subject := re.ReplaceAllString(sbomFile.String(), `"schema":{}`)
|
||||
|
||||
assert.JSONEq(t, tt.wantSbomFile, subject)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_attestCommand(t *testing.T) {
|
||||
cmdPrefix := cosignBinName
|
||||
lp, err := exec.LookPath(cosignBinName)
|
||||
if err == nil {
|
||||
cmdPrefix = lp
|
||||
}
|
||||
|
||||
fullCmd := func(args string) string {
|
||||
return fmt.Sprintf("%s %s", cmdPrefix, args)
|
||||
}
|
||||
|
||||
type args struct {
|
||||
sbomFilepath string
|
||||
opts attestOptions
|
||||
userInput string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantCmd string
|
||||
wantEnvVars map[string]string
|
||||
notEnvVars []string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "with key and password",
|
||||
args: args{
|
||||
userInput: "myimage",
|
||||
sbomFilepath: "/tmp/sbom-filepath.json",
|
||||
opts: func() attestOptions {
|
||||
def := defaultAttestOptions()
|
||||
def.Outputs = []string{"syft-json"}
|
||||
def.Attest.Key = "key"
|
||||
def.Attest.Password = "password"
|
||||
return def
|
||||
}(),
|
||||
},
|
||||
wantCmd: fullCmd("attest myimage --predicate /tmp/sbom-filepath.json --type custom -y --key key"),
|
||||
wantEnvVars: map[string]string{
|
||||
"COSIGN_PASSWORD": "password",
|
||||
},
|
||||
notEnvVars: []string{
|
||||
"COSIGN_EXPERIMENTAL", // only for keyless
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keyless",
|
||||
args: args{
|
||||
userInput: "myimage",
|
||||
sbomFilepath: "/tmp/sbom-filepath.json",
|
||||
opts: func() attestOptions {
|
||||
def := defaultAttestOptions()
|
||||
def.Outputs = []string{"syft-json"}
|
||||
return def
|
||||
}(),
|
||||
},
|
||||
wantCmd: fullCmd("attest myimage --predicate /tmp/sbom-filepath.json --type custom -y"),
|
||||
wantEnvVars: map[string]string{
|
||||
"COSIGN_EXPERIMENTAL": "1",
|
||||
},
|
||||
notEnvVars: []string{
|
||||
"COSIGN_PASSWORD",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = require.NoError
|
||||
}
|
||||
|
||||
got, err := attestCommand(tt.args.sbomFilepath, &tt.args.opts, tt.args.userInput)
|
||||
tt.wantErr(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
require.NotNil(t, got)
|
||||
assert.Equal(t, tt.wantCmd, got.String())
|
||||
|
||||
gotEnv := strset.New(got.Env...)
|
||||
|
||||
for k, v := range tt.wantEnvVars {
|
||||
assert.True(t, gotEnv.Has(fmt.Sprintf("%s=%s", k, v)))
|
||||
}
|
||||
|
||||
for _, k := range tt.notEnvVars {
|
||||
for _, env := range got.Env {
|
||||
fields := strings.Split(env, "=")
|
||||
if fields[0] == k {
|
||||
t.Errorf("attestCommand() unexpected environment variable %s", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_predicateType(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "cyclonedx-json",
|
||||
want: "cyclonedx",
|
||||
},
|
||||
{
|
||||
name: "spdx-tag-value",
|
||||
want: "spdx",
|
||||
},
|
||||
{
|
||||
name: "spdx-tv",
|
||||
want: "spdx",
|
||||
},
|
||||
{
|
||||
name: "spdx-json",
|
||||
want: "spdxjson",
|
||||
},
|
||||
{
|
||||
name: "json",
|
||||
want: "spdxjson",
|
||||
},
|
||||
{
|
||||
name: "syft-json",
|
||||
want: "custom",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, predicateType(tt.name), "predicateType(%v)", tt.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildSBOMForAttestation(t *testing.T) {
|
||||
// note: this test is only meant to test that the filter function is wired
|
||||
// and not the correctness of the function in depth
|
||||
type args struct {
|
||||
id clio.Identification
|
||||
opts *options.Catalog
|
||||
userInput string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *sbom.SBOM
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "do not allow directory scans",
|
||||
args: args{
|
||||
opts: func() *options.Catalog {
|
||||
def := defaultAttestOptions()
|
||||
return &def.Catalog
|
||||
}(),
|
||||
userInput: "dir:/tmp/something",
|
||||
},
|
||||
wantErr: require.Error,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = require.NoError
|
||||
}
|
||||
_, err := generateSBOMForAttestation(tt.args.id, tt.args.opts, tt.args.userInput)
|
||||
tt.wantErr(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -117,10 +117,51 @@ func runPackages(id clio.Identification, opts *packagesOptions, userInput string
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := getSource(&opts.Catalog, userInput)
|
||||
detection, err := source.Detect(
|
||||
userInput,
|
||||
source.DetectConfig{
|
||||
DefaultImageSource: opts.DefaultImagePullSource,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not deteremine source: %w", err)
|
||||
}
|
||||
|
||||
var platform *image.Platform
|
||||
|
||||
if opts.Platform != "" {
|
||||
platform, err = image.NewPlatform(opts.Platform)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid platform: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
hashers, err := file.Hashers(opts.Source.File.Digests...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid hash: %w", err)
|
||||
}
|
||||
|
||||
src, err := detection.NewSource(
|
||||
source.DetectionSourceConfig{
|
||||
Alias: source.Alias{
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
},
|
||||
RegistryOptions: opts.Registry.ToOptions(),
|
||||
Platform: platform,
|
||||
Exclude: source.ExcludeConfig{
|
||||
Paths: opts.Exclusions,
|
||||
},
|
||||
DigestAlgorithms: hashers,
|
||||
BasePath: opts.BasePath,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
if userInput == "power-user" {
|
||||
bus.Notify("Note: the 'power-user' command has been removed.")
|
||||
}
|
||||
return fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -147,63 +188,6 @@ func runPackages(id clio.Identification, opts *packagesOptions, userInput string
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSource(opts *options.Catalog, userInput string, filters ...func(*source.Detection) error) (source.Source, error) {
|
||||
detection, err := source.Detect(
|
||||
userInput,
|
||||
source.DetectConfig{
|
||||
DefaultImageSource: opts.DefaultImagePullSource,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not deteremine source: %w", err)
|
||||
}
|
||||
|
||||
for _, filter := range filters {
|
||||
if err := filter(detection); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var platform *image.Platform
|
||||
|
||||
if opts.Platform != "" {
|
||||
platform, err = image.NewPlatform(opts.Platform)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid platform: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
hashers, err := file.Hashers(opts.Source.File.Digests...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid hash: %w", err)
|
||||
}
|
||||
|
||||
src, err := detection.NewSource(
|
||||
source.DetectionSourceConfig{
|
||||
Alias: source.Alias{
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
},
|
||||
RegistryOptions: opts.Registry.ToOptions(),
|
||||
Platform: platform,
|
||||
Exclude: source.ExcludeConfig{
|
||||
Paths: opts.Exclusions,
|
||||
},
|
||||
DigestAlgorithms: hashers,
|
||||
BasePath: opts.BasePath,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if userInput == "power-user" {
|
||||
bus.Notify("Note: the 'power-user' command has been removed.")
|
||||
}
|
||||
return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
||||
}
|
||||
|
||||
return src, nil
|
||||
}
|
||||
|
||||
func generateSBOM(id clio.Identification, src source.Source, opts *options.Catalog) (*sbom.SBOM, error) {
|
||||
tasks, err := eventloop.Tasks(opts)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
||||
)
|
||||
|
||||
func Root(app clio.Application, packagesCmd *cobra.Command) *cobra.Command {
|
||||
@@ -22,9 +21,6 @@ func Root(app clio.Application, packagesCmd *cobra.Command) *cobra.Command {
|
||||
Example: packagesCmd.Example,
|
||||
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
restoreStdout := ui.CaptureStdoutToTraceLog()
|
||||
defer restoreStdout()
|
||||
|
||||
return runPackages(id, opts, args[0])
|
||||
},
|
||||
}, opts)
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/fangs"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
||||
golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||
javaCataloger "github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||
@@ -127,24 +126,19 @@ func (cfg Catalog) ToCatalogerConfig() cataloger.Config {
|
||||
},
|
||||
Catalogers: cfg.Catalogers,
|
||||
Parallelism: cfg.Parallelism,
|
||||
Golang: golangCataloger.DefaultCatalogerConfig().
|
||||
Golang: golangCataloger.NewGoCatalogerOpts().
|
||||
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
|
||||
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
|
||||
WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
|
||||
WithProxy(cfg.Golang.Proxy).
|
||||
WithNoProxy(cfg.Golang.NoProxy),
|
||||
LinuxKernel: kernel.LinuxKernelCatalogerConfig{
|
||||
LinuxKernel: kernel.LinuxCatalogerConfig{
|
||||
CatalogModules: cfg.LinuxKernel.CatalogModules,
|
||||
},
|
||||
Java: javaCataloger.DefaultArchiveCatalogerConfig().
|
||||
Java: javaCataloger.DefaultCatalogerOpts().
|
||||
WithUseNetwork(cfg.Java.UseNetwork).
|
||||
WithMavenBaseURL(cfg.Java.MavenURL).
|
||||
WithArchiveTraversal(
|
||||
cataloging.ArchiveSearchConfig{
|
||||
IncludeIndexedArchives: cfg.Package.SearchIndexedArchives,
|
||||
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
|
||||
},
|
||||
cfg.Java.MaxParentRecursiveDepth),
|
||||
WithMavenURL(cfg.Java.MavenURL).
|
||||
WithMaxParentRecursiveDepth(cfg.Java.MaxParentRecursiveDepth),
|
||||
Python: pythonCataloger.CatalogerConfig{
|
||||
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
|
||||
},
|
||||
|
||||
@@ -32,12 +32,8 @@ func (o FormatCyclonedxJSON) formatEncoders() ([]sbom.FormatEncoder, error) {
|
||||
}
|
||||
|
||||
func (o FormatCyclonedxJSON) buildConfig(version string) cyclonedxjson.EncoderConfig {
|
||||
var pretty bool
|
||||
if o.Pretty != nil {
|
||||
pretty = *o.Pretty
|
||||
}
|
||||
return cyclonedxjson.EncoderConfig{
|
||||
Version: version,
|
||||
Pretty: pretty,
|
||||
Pretty: *o.Pretty,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,8 @@ func (o FormatCyclonedxXML) formatEncoders() ([]sbom.FormatEncoder, error) {
|
||||
}
|
||||
|
||||
func (o FormatCyclonedxXML) buildConfig(version string) cyclonedxxml.EncoderConfig {
|
||||
var pretty bool
|
||||
if o.Pretty != nil {
|
||||
pretty = *o.Pretty
|
||||
}
|
||||
return cyclonedxxml.EncoderConfig{
|
||||
Version: version,
|
||||
Pretty: pretty,
|
||||
Pretty: *o.Pretty,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,8 @@ func (o FormatSPDXJSON) formatEncoders() ([]sbom.FormatEncoder, error) {
|
||||
}
|
||||
|
||||
func (o FormatSPDXJSON) buildConfig(v string) spdxjson.EncoderConfig {
|
||||
var pretty bool
|
||||
if o.Pretty != nil {
|
||||
pretty = *o.Pretty
|
||||
}
|
||||
return spdxjson.EncoderConfig{
|
||||
Version: v,
|
||||
Pretty: pretty,
|
||||
Pretty: *o.Pretty,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,8 @@ func (o FormatSyftJSON) formatEncoders() ([]sbom.FormatEncoder, error) {
|
||||
}
|
||||
|
||||
func (o FormatSyftJSON) buildConfig() syftjson.EncoderConfig {
|
||||
var pretty bool
|
||||
if o.Pretty != nil {
|
||||
pretty = *o.Pretty
|
||||
}
|
||||
return syftjson.EncoderConfig{
|
||||
Legacy: o.Legacy,
|
||||
Pretty: pretty,
|
||||
Pretty: *o.Pretty,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ var _ interface {
|
||||
type Output struct {
|
||||
AllowableOptions []string `yaml:"-" json:"-" mapstructure:"-"`
|
||||
AllowMultipleOutputs bool `yaml:"-" json:"-" mapstructure:"-"`
|
||||
AllowToFile bool `yaml:"-" json:"-" mapstructure:"-"`
|
||||
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
|
||||
OutputFile `yaml:",inline" json:"" mapstructure:",squash"`
|
||||
Format `yaml:"format" json:"format" mapstructure:"format"`
|
||||
@@ -39,7 +38,6 @@ type Output struct {
|
||||
func DefaultOutput() Output {
|
||||
return Output{
|
||||
AllowMultipleOutputs: true,
|
||||
AllowToFile: true,
|
||||
Outputs: []string{string(table.ID)},
|
||||
OutputFile: OutputFile{
|
||||
Enabled: true,
|
||||
@@ -88,14 +86,6 @@ func (o Output) SBOMWriter() (sbom.Writer, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !o.AllowToFile {
|
||||
for _, opt := range o.Outputs {
|
||||
if strings.Contains(opt, "=") {
|
||||
return nil, fmt.Errorf("file output is not allowed ('-o format=path' should be '-o format')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return makeSBOMWriter(o.Outputs, o.File, encoders)
|
||||
}
|
||||
|
||||
|
||||
@@ -179,25 +179,3 @@ func Test_EncoderCollection_ByString_IDOnly_Defaults(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_OutputHonorsAllowFile(t *testing.T) {
|
||||
o := DefaultOutput()
|
||||
|
||||
t.Run("file is not allowed", func(t *testing.T) {
|
||||
o.AllowToFile = false
|
||||
o.Outputs = []string{"table=/tmp/somefile"}
|
||||
|
||||
w, err := o.SBOMWriter()
|
||||
assert.Nil(t, w)
|
||||
assert.ErrorContains(t, err, "file output is not allowed")
|
||||
})
|
||||
|
||||
t.Run("file is allowed", func(t *testing.T) {
|
||||
o.AllowToFile = true
|
||||
o.Outputs = []string{"table=/tmp/somefile"}
|
||||
|
||||
w, err := o.SBOMWriter()
|
||||
assert.NotNil(t, w)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
github.com/github/go-spdx/v2 v2.2.0
|
||||
github.com/gkampitakis/go-snaps v0.4.12
|
||||
github.com/go-git/go-billy/v5 v5.5.0
|
||||
github.com/go-git/go-git/v5 v5.10.1
|
||||
github.com/go-git/go-git/v5 v5.10.0
|
||||
github.com/go-test/deep v1.1.0
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/go-containerregistry v0.16.1
|
||||
@@ -61,7 +61,7 @@ require (
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
|
||||
github.com/sergi/go-diff v1.3.1
|
||||
github.com/spdx/tools-golang v0.5.3
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/afero v1.10.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/vbatts/go-mtree v0.5.3
|
||||
@@ -71,7 +71,7 @@ require (
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
||||
golang.org/x/mod v0.14.0
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/net v0.18.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.27.0
|
||||
)
|
||||
@@ -87,6 +87,7 @@ require (
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
github.com/adrg/xdg v0.4.0 // indirect
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
@@ -181,7 +182,7 @@ require (
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/skeema/knownhosts v1.2.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
@@ -207,17 +208,17 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.16.0 // indirect
|
||||
golang.org/x/crypto v0.15.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/term v0.14.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
|
||||
google.golang.org/grpc v1.58.3 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
|
||||
@@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
@@ -15,6 +16,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
@@ -45,6 +47,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
@@ -83,6 +86,8 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
|
||||
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
|
||||
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
|
||||
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -288,8 +293,8 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk=
|
||||
github.com/go-git/go-git/v5 v5.10.1/go.mod h1:uEuHjxkHap8kAl//V5F/nNWwqIYtP/402ddd05mp0wg=
|
||||
github.com/go-git/go-git/v5 v5.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ=
|
||||
github.com/go-git/go-git/v5 v5.10.0/go.mod h1:1FOZ/pQnqw24ghP2n7cunVl0ON55BsjPYvhWHvZGhoo=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -386,6 +391,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
@@ -403,6 +409,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
@@ -511,6 +518,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
|
||||
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
|
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
@@ -630,6 +639,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
@@ -697,8 +707,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
|
||||
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
@@ -707,8 +717,8 @@ github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQr
|
||||
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
|
||||
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
@@ -842,14 +852,16 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -940,8 +952,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
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=
|
||||
@@ -972,8 +984,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1021,6 +1033,7 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1028,6 +1041,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1057,16 +1071,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
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=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
||||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||
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=
|
||||
@@ -1084,8 +1098,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -1135,6 +1149,7 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
@@ -1225,7 +1240,9 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
@@ -1253,10 +1270,10 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
|
||||
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
|
||||
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA=
|
||||
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1284,8 +1301,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
|
||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package cataloging
|
||||
|
||||
type ArchiveSearchConfig struct {
|
||||
IncludeIndexedArchives bool `yaml:"include-indexed-archives" json:"include-indexed-archives" mapstructure:"include-indexed-archives"`
|
||||
IncludeUnindexedArchives bool `yaml:"include-unindexed-archives" json:"include-unindexed-archives" mapstructure:"include-unindexed-archives"`
|
||||
}
|
||||
@@ -27,16 +27,9 @@ func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordina
|
||||
locations = func() <-chan file.Location {
|
||||
ch := make(chan file.Location)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
close(ch)
|
||||
for _, c := range coordinates {
|
||||
locs, err := resolver.FilesByPath(c.RealPath)
|
||||
if err != nil {
|
||||
log.Warn("unable to get file locations for path %q: %w", c.RealPath, err)
|
||||
continue
|
||||
}
|
||||
for _, loc := range locs {
|
||||
ch <- loc
|
||||
}
|
||||
ch <- file.NewLocationFromCoordinates(c)
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
|
||||
@@ -21,13 +21,19 @@ func TestFileMetadataCataloger(t *testing.T) {
|
||||
c := NewCataloger()
|
||||
|
||||
src, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create source: %+v", err)
|
||||
}
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create resolver: %+v", err)
|
||||
}
|
||||
|
||||
actual, err := c.Catalog(resolver)
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
t.Fatalf("could not catalog: %+v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
@@ -150,67 +156,3 @@ func TestFileMetadataCataloger(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFileMetadataCataloger_GivenCoordinates(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
c := NewCataloger()
|
||||
|
||||
src, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
exists bool
|
||||
expected file.Metadata
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
path: "/file-1.txt",
|
||||
exists: true,
|
||||
expected: file.Metadata{
|
||||
FileInfo: stereoscopeFile.ManualInfo{
|
||||
NameValue: "file-1.txt",
|
||||
ModeValue: 0644,
|
||||
SizeValue: 7,
|
||||
},
|
||||
Path: "/file-1.txt",
|
||||
Type: stereoscopeFile.TypeRegular,
|
||||
UserID: 1,
|
||||
GroupID: 2,
|
||||
MIMEType: "text/plain",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.path, func(t *testing.T) {
|
||||
_, ref, err := img.SquashedTree().File(stereoscopeFile.Path(test.path))
|
||||
require.NoError(t, err)
|
||||
|
||||
l := file.NewLocationFromImage(test.path, *ref.Reference, img)
|
||||
|
||||
// note: an important difference between this test and the previous is that this test is using a list
|
||||
// of specific coordinates to catalog
|
||||
actual, err := c.Catalog(resolver, l.Coordinates)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, actual, 1)
|
||||
|
||||
if _, ok := actual[l.Coordinates]; ok {
|
||||
// we're not interested in keeping the test fixtures up to date with the latest file modification times
|
||||
// thus ModTime is not under test
|
||||
fi := test.expected.FileInfo.(stereoscopeFile.ManualInfo)
|
||||
fi.ModTimeValue = actual[l.Coordinates].ModTime()
|
||||
test.expected.FileInfo = fi
|
||||
}
|
||||
|
||||
assert.True(t, test.expected.Equal(actual[l.Coordinates]))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package model
|
||||
|
||||
// Document represents the syft cataloging findings as a JSON document
|
||||
type Document struct {
|
||||
Artifacts []Package `json:"artifacts"` // Artifacts is the list of packages discovered and placed into the catalog
|
||||
ArtifactRelationships []Relationship `json:"artifactRelationships"`
|
||||
Files []File `json:"files,omitempty"` // note: must have omitempty
|
||||
Source Source `json:"source"` // Source represents the original object that was cataloged
|
||||
Distro LinuxRelease `json:"distro"` // Distro represents the Linux distribution that was detected from the source
|
||||
Descriptor Descriptor `json:"descriptor"` // Descriptor is a block containing self-describing information about syft
|
||||
Schema Schema `json:"schema"` // Schema is a block reserved for defining the version for the shape of this JSON document and where to find the schema document to validate the shape
|
||||
Schema Schema `json:"schema"` // Schema is a block reserved for defining the version for the shape of this JSON document and where to find the schema document to validate the shape
|
||||
Descriptor Descriptor `json:"descriptor"` // Descriptor is a block containing self-describing information about syft
|
||||
Source Source `json:"source"` // Source represents the original object that was cataloged
|
||||
Distro LinuxRelease `json:"distro"` // Distro represents the Linux distribution that was detected from the source
|
||||
Packages []Package `json:"packages"` // Packages is the list of packages discovered
|
||||
Relationships []Relationship `json:"relationships"` // Relationships is a list of package-to-package, package-to-file, and file-to-package relationships
|
||||
Files []File `json:"files,omitempty"` // Files is a list of all files at their real path and any associated metadata about that file
|
||||
}
|
||||
|
||||
// Descriptor describes what created the document as well as surrounding metadata
|
||||
|
||||
@@ -35,12 +35,12 @@ func metadataType(metadata interface{}, legacy bool) string {
|
||||
// ToFormatModel transforms the sbom import a format-specific model.
|
||||
func ToFormatModel(s sbom.SBOM, cfg EncoderConfig) model.Document {
|
||||
return model.Document{
|
||||
Artifacts: toPackageModels(s.Artifacts.Packages, cfg),
|
||||
ArtifactRelationships: toRelationshipModel(s.Relationships),
|
||||
Files: toFile(s),
|
||||
Source: toSourceModel(s.Source),
|
||||
Distro: toLinuxReleaser(s.Artifacts.LinuxDistribution),
|
||||
Descriptor: toDescriptor(s.Descriptor),
|
||||
Packages: toPackageModels(s.Artifacts.Packages, cfg),
|
||||
Relationships: toRelationshipModel(s.Relationships),
|
||||
Files: toFile(s),
|
||||
Source: toSourceModel(s.Source),
|
||||
Distro: toLinuxReleaser(s.Artifacts.LinuxDistribution),
|
||||
Descriptor: toDescriptor(s.Descriptor),
|
||||
Schema: model.Schema{
|
||||
Version: internal.JSONSchemaVersion,
|
||||
URL: fmt.Sprintf("https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-%s.json", internal.JSONSchemaVersion),
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
func toSyftModel(doc model.Document) *sbom.SBOM {
|
||||
idAliases := make(map[string]string)
|
||||
|
||||
catalog := toSyftCatalog(doc.Artifacts, idAliases)
|
||||
catalog := toSyftCatalog(doc.Packages, idAliases)
|
||||
|
||||
fileArtifacts := toSyftFiles(doc.Files)
|
||||
|
||||
@@ -39,7 +39,7 @@ func toSyftModel(doc model.Document) *sbom.SBOM {
|
||||
},
|
||||
Source: *toSyftSourceData(doc.Source),
|
||||
Descriptor: toSyftDescriptor(doc.Descriptor),
|
||||
Relationships: warnConversionErrors(toSyftRelationships(&doc, catalog, doc.ArtifactRelationships, idAliases)),
|
||||
Relationships: warnConversionErrors(toSyftRelationships(&doc, catalog, doc.Relationships, idAliases)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ func Test_idsHaveChanged(t *testing.T) {
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{Path: "some/path"},
|
||||
},
|
||||
Artifacts: []model.Package{
|
||||
Packages: []model.Package{
|
||||
{
|
||||
PackageBasicData: model.PackageBasicData{
|
||||
ID: "1",
|
||||
@@ -191,7 +191,7 @@ func Test_idsHaveChanged(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ArtifactRelationships: []model.Relationship{
|
||||
Relationships: []model.Relationship{
|
||||
{
|
||||
Parent: "1",
|
||||
Child: "2",
|
||||
|
||||
@@ -37,17 +37,11 @@ func NewCollection(pkgs ...Package) *Collection {
|
||||
|
||||
// PackageCount returns the total number of packages that have been added.
|
||||
func (c *Collection) PackageCount() int {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return len(c.byID)
|
||||
}
|
||||
|
||||
// Package returns the package with the given ID.
|
||||
func (c *Collection) Package(id artifact.ID) *Package {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
v, exists := c.byID[id]
|
||||
if !exists {
|
||||
return nil
|
||||
@@ -63,31 +57,16 @@ func (c *Collection) Package(id artifact.ID) *Package {
|
||||
|
||||
// PackagesByPath returns all packages that were discovered from the given path.
|
||||
func (c *Collection) PackagesByPath(path string) []Package {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return c.packages(c.idsByPath[path].slice)
|
||||
return c.Packages(c.idsByPath[path].slice)
|
||||
}
|
||||
|
||||
// PackagesByName returns all packages that were discovered with a matching name.
|
||||
func (c *Collection) PackagesByName(name string) []Package {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return c.packages(c.idsByName[name].slice)
|
||||
return c.Packages(c.idsByName[name].slice)
|
||||
}
|
||||
|
||||
// Packages returns all packages for the given ID.
|
||||
func (c *Collection) Packages(ids []artifact.ID) (result []Package) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return c.packages(ids)
|
||||
}
|
||||
|
||||
func (c *Collection) packages(ids []artifact.ID) (result []Package) {
|
||||
// note: read lock must be held by caller
|
||||
|
||||
for _, i := range ids {
|
||||
p, exists := c.byID[i]
|
||||
if exists {
|
||||
@@ -126,8 +105,6 @@ func (c *Collection) Add(pkgs ...Package) {
|
||||
}
|
||||
|
||||
func (c *Collection) addToIndex(p Package) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
c.byID[p.id] = p
|
||||
c.addNameToIndex(p)
|
||||
c.addTypeToIndex(p)
|
||||
@@ -135,24 +112,18 @@ func (c *Collection) addToIndex(p Package) {
|
||||
}
|
||||
|
||||
func (c *Collection) addNameToIndex(p Package) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
nameIndex := c.idsByName[p.Name]
|
||||
nameIndex.add(p.id)
|
||||
c.idsByName[p.Name] = nameIndex
|
||||
}
|
||||
|
||||
func (c *Collection) addTypeToIndex(p Package) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
typeIndex := c.idsByType[p.Type]
|
||||
typeIndex.add(p.id)
|
||||
c.idsByType[p.Type] = typeIndex
|
||||
}
|
||||
|
||||
func (c *Collection) addPathsToIndex(p Package) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
observedPaths := strset.New()
|
||||
for _, l := range p.Locations.ToSlice() {
|
||||
if l.RealPath != "" && !observedPaths.Has(l.RealPath) {
|
||||
@@ -167,8 +138,6 @@ func (c *Collection) addPathsToIndex(p Package) {
|
||||
}
|
||||
|
||||
func (c *Collection) addPathToIndex(id artifact.ID, path string) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
pathIndex := c.idsByPath[path]
|
||||
pathIndex.add(id)
|
||||
c.idsByPath[path] = pathIndex
|
||||
@@ -192,24 +161,18 @@ func (c *Collection) Delete(ids ...artifact.ID) {
|
||||
}
|
||||
|
||||
func (c *Collection) deleteNameFromIndex(p Package) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
nameIndex := c.idsByName[p.Name]
|
||||
nameIndex.delete(p.id)
|
||||
c.idsByName[p.Name] = nameIndex
|
||||
}
|
||||
|
||||
func (c *Collection) deleteTypeFromIndex(p Package) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
typeIndex := c.idsByType[p.Type]
|
||||
typeIndex.delete(p.id)
|
||||
c.idsByType[p.Type] = typeIndex
|
||||
}
|
||||
|
||||
func (c *Collection) deletePathsFromIndex(p Package) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
observedPaths := strset.New()
|
||||
for _, l := range p.Locations.ToSlice() {
|
||||
if l.RealPath != "" && !observedPaths.Has(l.RealPath) {
|
||||
@@ -224,8 +187,6 @@ func (c *Collection) deletePathsFromIndex(p Package) {
|
||||
}
|
||||
|
||||
func (c *Collection) deletePathFromIndex(id artifact.ID, path string) {
|
||||
// note: write lock must be held by caller
|
||||
|
||||
pathIndex := c.idsByPath[path]
|
||||
pathIndex.delete(id)
|
||||
if len(pathIndex.slice) == 0 {
|
||||
@@ -240,15 +201,10 @@ func (c *Collection) Enumerate(types ...Type) <-chan Package {
|
||||
channel := make(chan Package)
|
||||
go func() {
|
||||
defer close(channel)
|
||||
|
||||
if c == nil {
|
||||
// we should allow enumerating from a catalog that was never created (which will result in no packages enumerated)
|
||||
return
|
||||
}
|
||||
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
for ty, ids := range c.idsByType {
|
||||
if len(types) != 0 {
|
||||
found := false
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// NewDBCataloger returns a new cataloger object initialized for Alpine package DB flat-file stores.
|
||||
func NewDBCataloger() pkg.Cataloger {
|
||||
func NewDBCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("apk-db-cataloger").
|
||||
WithParserByGlobs(parseApkDB, pkg.ApkDBGlob)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// NewDBCataloger returns a new cataloger object initialized for arch linux pacman database flat-file stores.
|
||||
func NewDBCataloger() pkg.Cataloger {
|
||||
func NewDBCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("alpm-db-cataloger").
|
||||
WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
const catalogerName = "binary-cataloger"
|
||||
|
||||
func NewCataloger() pkg.Cataloger {
|
||||
func NewCataloger() *Cataloger {
|
||||
return &Cataloger{}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,42 +72,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("postgresql-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-mysql-8.0.34",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/mysql-8.0.34",
|
||||
expected: pkg.Package{
|
||||
Name: "mysql",
|
||||
Version: "8.0.34",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql@8.0.34",
|
||||
Locations: locations("mysql"),
|
||||
Metadata: metadata("mysql-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-mysql-5.6.51",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/mysql-5.6.51",
|
||||
expected: pkg.Package{
|
||||
Name: "mysql",
|
||||
Version: "5.6.51",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql@5.6.51",
|
||||
Locations: locations("mysql"),
|
||||
Metadata: metadata("mysql-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-mariadb-10.6.15",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/mariadb-10.6.15",
|
||||
expected: pkg.Package{
|
||||
Name: "mariadb",
|
||||
Version: "10.6.15",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mariadb@10.6.15",
|
||||
Locations: locations("mariadb"),
|
||||
Metadata: metadata("mariadb-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-traefik-2.9.6",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/traefik-2.9.6",
|
||||
@@ -348,42 +312,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-redis-7.0.14",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/redis-server-7.0.14",
|
||||
expected: pkg.Package{
|
||||
Name: "redis",
|
||||
Version: "7.0.14",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@7.0.14",
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-redis-7.2.3-amd64",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/redis-server-7.2.3-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "redis",
|
||||
Version: "7.2.3",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@7.2.3",
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-redis-7.2.3-arm64",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/redis-server-7.2.3-arm64",
|
||||
expected: pkg.Package{
|
||||
Name: "redis",
|
||||
Version: "7.2.3",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@7.2.3",
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-libpython3.7.so",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/python-binary-lib-3.7",
|
||||
|
||||
@@ -58,10 +58,8 @@ var defaultClassifiers = []classifier{
|
||||
{
|
||||
Class: "redis-binary",
|
||||
FileGlob: "**/redis-server",
|
||||
EvidenceMatcher: evidenceMatchers(
|
||||
fileContentsVersionMatcher(`(?s)payload %5.*?(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}`),
|
||||
fileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12}-[0-9]{19}\x00.*?payload %5`),
|
||||
),
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
`(?s)payload %5.*(?P<version>\d.\d\.\d\d*?)[a-z0-9]{12}-[0-9]{19}`),
|
||||
Package: "redis",
|
||||
PURL: mustPURL("pkg:generic/redis@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:redislabs:redis:*:*:*:*:*:*:*:*"),
|
||||
@@ -69,18 +67,10 @@ var defaultClassifiers = []classifier{
|
||||
{
|
||||
Class: "java-binary-openjdk",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: evidenceMatchers(
|
||||
fileContentsVersionMatcher(
|
||||
// covers linux/amd64
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`,
|
||||
),
|
||||
fileContentsVersionMatcher(
|
||||
// covers linux/arm64
|
||||
`(?m)(?P<release>[0-9]+[.0-9]*)(\x00)+(?P<version>[0-9]+[^\x00]+)(\x00)+openjdk(\x00)+java\x00`,
|
||||
),
|
||||
),
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
Package: "java",
|
||||
PURL: mustPURL("pkg:generic/java@version"),
|
||||
// TODO the updates might need to be part of the CPE, like: 1.8.0:update152
|
||||
@@ -219,26 +209,6 @@ var defaultClassifiers = []classifier{
|
||||
Package: "postgresql",
|
||||
PURL: mustPURL("pkg:generic/postgresql@version"),
|
||||
},
|
||||
{
|
||||
Class: "mysql-binary",
|
||||
FileGlob: "**/mysql",
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
// ../../mysql-8.0.34
|
||||
// /mysql-5.6.51/bld/client
|
||||
`(?m).*/mysql-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
|
||||
Package: "mysql",
|
||||
PURL: mustPURL("pkg:generic/mysql@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "mariadb-binary",
|
||||
FileGlob: "**/mariadb",
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
// 10.6.15-MariaDB
|
||||
`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
|
||||
Package: "mariadb",
|
||||
PURL: mustPURL("pkg:generic/mariadb@version"),
|
||||
},
|
||||
{
|
||||
Class: "rust-standard-library-linux",
|
||||
FileGlob: "**/libstd-????????????????.so",
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
The binary snippet was gathered with:
|
||||
|
||||
```bash
|
||||
$ cat ./original-mariadb | strings | grep '-MariaDB'
|
||||
# assert you can see the value
|
||||
|
||||
|
||||
$ xxd ./original-mariadb | grep '\-MariaDB'
|
||||
# get the address...
|
||||
|
||||
|
||||
$ xxd -s 0x003dd5c0 -l 40 ./original-mariadb
|
||||
|
||||
003dd5c0: 2900 4c69 6e75 7800 3130 2e36 2e31 352d ).Linux.10.6.15-
|
||||
003dd5d0: 4d61 7269 6144 4200 7265 6164 6c69 6e65 MariaDB.readline
|
||||
003dd5e0: 0078 3836 5f36 3400 .x86_64.
|
||||
|
||||
|
||||
$ dd if=./original-mariadb of=mariadb bs=1 skip=$((0x003dd5c0)) count=40
|
||||
|
||||
40+0 records in
|
||||
40+0 records out
|
||||
40 bytes transferred in 0.000264 secs (151515 bytes/sec)
|
||||
|
||||
|
||||
$ xxd mariadb
|
||||
|
||||
00000000: 2900 4c69 6e75 7800 3130 2e36 2e31 352d ).Linux.10.6.15-
|
||||
00000010: 4d61 7269 6144 4200 7265 6164 6c69 6e65 MariaDB.readline
|
||||
00000020: 0078 3836 5f36 3400 .x86_64.
|
||||
```
|
||||
Binary file not shown.
@@ -1,39 +0,0 @@
|
||||
The binary snippet was gathered with:
|
||||
|
||||
```bash
|
||||
$ cat ./original-mysql | strings | grep '5.6.51'
|
||||
# assert you can see the value
|
||||
|
||||
|
||||
$ xxd ./original-mysql | grep '5.6.51'
|
||||
# get the address...
|
||||
|
||||
|
||||
$ xxd -s 0x008f13d0 -l 100 original-mysql
|
||||
|
||||
008f13d0: 2d62 6163 6b75 702d 7265 7374 6f72 6572 -backup-restorer
|
||||
008f13e0: 2d6d 7973 716c 2d35 2e36 2f6d 7973 716c -mysql-5.6/mysql
|
||||
008f13f0: 2d35 2e36 2e35 312f 636c 6965 6e74 2f63 -5.6.51/client/c
|
||||
008f1400: 6f6d 706c 6574 696f 6e5f 6861 7368 2e63 ompletion_hash.c
|
||||
008f1410: 6300 2f76 6172 2f76 6361 702f 6461 7461 c./var/vcap/data
|
||||
008f1420: 2f63 6f6d 7069 6c65 2f64 6174 6162 6173 /compile/databas
|
||||
008f1430: 652d 6261 e-ba
|
||||
|
||||
|
||||
$ dd if=./original-mysql of=mysql bs=1 skip=$((0x008f13d0)) count=100
|
||||
|
||||
100+0 records in
|
||||
100+0 records out
|
||||
100 bytes transferred in 0.000642 secs (155763 bytes/sec)
|
||||
|
||||
|
||||
$ xxd mysql
|
||||
|
||||
00000000: 2d62 6163 6b75 702d 7265 7374 6f72 6572 -backup-restorer
|
||||
00000010: 2d6d 7973 716c 2d35 2e36 2f6d 7973 716c -mysql-5.6/mysql
|
||||
00000020: 2d35 2e36 2e35 312f 636c 6965 6e74 2f63 -5.6.51/client/c
|
||||
00000030: 6f6d 706c 6574 696f 6e5f 6861 7368 2e63 ompletion_hash.c
|
||||
00000040: 6300 2f76 6172 2f76 6361 702f 6461 7461 c./var/vcap/data
|
||||
00000050: 2f63 6f6d 7069 6c65 2f64 6174 6162 6173 /compile/databas
|
||||
00000060: 652d 6261 e-ba
|
||||
```
|
||||
Binary file not shown.
@@ -1,40 +0,0 @@
|
||||
The binary snippet was gathered with:
|
||||
|
||||
```bash
|
||||
$ cat ./original-mysql | strings | grep '8.0.34'
|
||||
# assert you can see the value
|
||||
|
||||
|
||||
$ xxd ./original-mysql | grep '8.0.34'
|
||||
# get the address...
|
||||
|
||||
|
||||
$ xxd -s 0x0014cd20 -l 100 original-mysql
|
||||
|
||||
0014cd20: 2069 7320 616c 7265 6164 7920 6c6f 6164 is already load
|
||||
0014cd30: 6564 0000 0000 0000 2e2e 2f2e 2e2f 6d79 ed......../../my
|
||||
0014cd40: 7371 6c2d 382e 302e 3334 2f73 716c 2d63 sql-8.0.34/sql-c
|
||||
0014cd50: 6f6d 6d6f 6e2f 636c 6965 6e74 5f70 6c75 ommon/client_plu
|
||||
0014cd60: 6769 6e2e 6363 002f 7573 722f 6c6f 6361 gin.cc./usr/loca
|
||||
0014cd70: 6c2f 6d79 7371 6c2f 6c69 622f 706c 7567 l/mysql/lib/plug
|
||||
0014cd80: 696e 0049 in.I
|
||||
|
||||
|
||||
|
||||
$ dd if=./original-mysql of=mysql bs=1 skip=$((0x0014cd20)) count=100
|
||||
|
||||
100+0 records in
|
||||
100+0 records out
|
||||
100 bytes transferred in 0.000519 secs (192678 bytes/sec)
|
||||
|
||||
|
||||
$ xxd mysql
|
||||
|
||||
00000000: 2069 7320 616c 7265 6164 7920 6c6f 6164 is already load
|
||||
00000010: 6564 0000 0000 0000 2e2e 2f2e 2e2f 6d79 ed......../../my
|
||||
00000020: 7371 6c2d 382e 302e 3334 2f73 716c 2d63 sql-8.0.34/sql-c
|
||||
00000030: 6f6d 6d6f 6e2f 636c 6965 6e74 5f70 6c75 ommon/client_plu
|
||||
00000040: 6769 6e2e 6363 002f 7573 722f 6c6f 6361 gin.cc./usr/loca
|
||||
00000050: 6c2f 6d79 7371 6c2f 6c69 622f 706c 7567 l/mysql/lib/plug
|
||||
00000060: 696e 0049 in.I
|
||||
```
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
package cataloger
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
|
||||
@@ -11,10 +10,10 @@ import (
|
||||
// TODO: these field naming vs helper function naming schemes are inconsistent.
|
||||
type Config struct {
|
||||
Search SearchConfig
|
||||
Golang golang.CatalogerConfig
|
||||
LinuxKernel kernel.LinuxKernelCatalogerConfig
|
||||
Golang golang.GoCatalogerOpts
|
||||
LinuxKernel kernel.LinuxCatalogerConfig
|
||||
Python python.CatalogerConfig
|
||||
Java java.ArchiveCatalogerConfig
|
||||
Java java.CatalogerOpts
|
||||
Catalogers []string
|
||||
Parallelism int
|
||||
ExcludeBinaryOverlapByOwnership bool
|
||||
@@ -26,7 +25,7 @@ func DefaultConfig() Config {
|
||||
Parallelism: 1,
|
||||
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
||||
Python: python.DefaultCatalogerConfig(),
|
||||
Java: java.DefaultArchiveCatalogerConfig(),
|
||||
Java: java.DefaultCatalogerOpts(),
|
||||
ExcludeBinaryOverlapByOwnership: true,
|
||||
}
|
||||
}
|
||||
@@ -34,14 +33,12 @@ func DefaultConfig() Config {
|
||||
// JavaConfig merges relevant config values from Config to return a java.Config struct.
|
||||
// Values like IncludeUnindexedArchives and IncludeIndexedArchives are used across catalogers
|
||||
// and are not specific to Java requiring this merge.
|
||||
func (c Config) JavaConfig() java.ArchiveCatalogerConfig {
|
||||
return java.ArchiveCatalogerConfig{
|
||||
ArchiveSearchConfig: cataloging.ArchiveSearchConfig{
|
||||
IncludeUnindexedArchives: c.Search.IncludeUnindexedArchives,
|
||||
IncludeIndexedArchives: c.Search.IncludeIndexedArchives,
|
||||
},
|
||||
func (c Config) JavaConfig() java.Config {
|
||||
return java.Config{
|
||||
SearchUnindexedArchives: c.Search.IncludeUnindexedArchives,
|
||||
SearchIndexedArchives: c.Search.IncludeIndexedArchives,
|
||||
UseNetwork: c.Java.UseNetwork,
|
||||
MavenBaseURL: c.Java.MavenBaseURL,
|
||||
MavenBaseURL: c.Java.MavenURL,
|
||||
MaxParentRecursiveDepth: c.Java.MaxParentRecursiveDepth,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,18 @@ Package cpp provides a concrete Cataloger implementations for the C/C++ language
|
||||
package cpp
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewConanCataloger returns a new C/C++ conanfile.txt and conan.lock cataloger object.
|
||||
func NewConanCataloger() pkg.Cataloger {
|
||||
func NewConanCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("conan-cataloger").
|
||||
WithParserByGlobs(parseConanfile, "**/conanfile.txt").
|
||||
WithParserByGlobs(parseConanlock, "**/conan.lock")
|
||||
}
|
||||
|
||||
// NewConanInfoCataloger returns a new C/C++ conaninfo.txt cataloger object.
|
||||
func NewConanInfoCataloger() pkg.Cataloger {
|
||||
func NewConanInfoCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("conan-info-cataloger").
|
||||
WithParserByGlobs(parseConaninfo, "**/conaninfo.txt")
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ Package dart provides a concrete Cataloger implementations for the Dart language
|
||||
package dart
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewPubspecLockCataloger returns a new Dartlang cataloger object base on pubspec lock files.
|
||||
func NewPubspecLockCataloger() pkg.Cataloger {
|
||||
func NewPubspecLockCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("dart-pubspec-lock-cataloger").
|
||||
WithParserByGlobs(parsePubspecLock, "**/pubspec.lock")
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ Package debian provides a concrete Cataloger implementation relating to packages
|
||||
package debian
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewDBCataloger returns a new Deb package cataloger capable of parsing DPKG status DB flat-file stores.
|
||||
func NewDBCataloger() pkg.Cataloger {
|
||||
func NewDBCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("dpkg-db-cataloger").
|
||||
// note: these globs have been intentionally split up in order to improve search performance,
|
||||
// please do NOT combine into: "**/var/lib/dpkg/{status,status.d/*}"
|
||||
|
||||
@@ -4,18 +4,17 @@ Package dotnet provides a concrete Cataloger implementation relating to packages
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewDotnetDepsCataloger returns a new Dotnet cataloger object base on deps json files.
|
||||
func NewDotnetDepsCataloger() pkg.Cataloger {
|
||||
func NewDotnetDepsCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("dotnet-deps-cataloger").
|
||||
WithParserByGlobs(parseDotnetDeps, "**/*.deps.json")
|
||||
}
|
||||
|
||||
// NewDotnetPortableExecutableCataloger returns a new Dotnet cataloger object base on portable executable files.
|
||||
func NewDotnetPortableExecutableCataloger() pkg.Cataloger {
|
||||
func NewDotnetPortableExecutableCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("dotnet-portable-executable-cataloger").
|
||||
WithParserByGlobs(parseDotnetPortableExecutable, "**/*.dll", "**/*.exe")
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package dotnet
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ func TestCataloger_Globs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
cataloger pkg.Cataloger
|
||||
cataloger *generic.Cataloger
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
|
||||
@@ -26,29 +26,27 @@ func parseDotnetPortableExecutable(_ file.Resolver, _ *generic.Environment, f fi
|
||||
|
||||
peFile, err := pe.NewBytes(by, &pe.Options{})
|
||||
if err != nil {
|
||||
// TODO: known-unknown
|
||||
log.Tracef("unable to create PE instance for file '%s': %v", f.RealPath, err)
|
||||
return nil, nil, nil
|
||||
return nil, nil, fmt.Errorf("unable to create PE file instance: %w", err)
|
||||
}
|
||||
|
||||
err = peFile.Parse()
|
||||
if err != nil {
|
||||
// TODO: known-unknown
|
||||
log.Tracef("unable to parse PE file '%s': %v", f.RealPath, err)
|
||||
return nil, nil, nil
|
||||
return nil, nil, fmt.Errorf("unable to parse PE file: %w", err)
|
||||
}
|
||||
|
||||
versionResources, err := peFile.ParseVersionResources()
|
||||
if err != nil {
|
||||
// TODO: known-unknown
|
||||
log.Tracef("unable to parse version resources in PE file: %s: %v", f.RealPath, err)
|
||||
// this is not a fatal error, just log and continue
|
||||
// TODO: consider this case for "known unknowns" (same goes for cases below)
|
||||
log.Tracef("unable to parse version resources in PE file: %s", f.RealPath)
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
dotNetPkg, err := buildDotNetPackage(versionResources, f)
|
||||
if err != nil {
|
||||
// TODO: known-unknown
|
||||
log.Tracef("unable to build dotnet package: %v", err)
|
||||
// this is not a fatal error, just log and continue
|
||||
// TODO: consider this case for "known unknowns" (same goes for cases below)
|
||||
log.Tracef("unable to build dotnet package: %w", err)
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ Package elixir provides a concrete Cataloger implementation relating to packages
|
||||
package elixir
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewMixLockCataloger returns a cataloger object for Elixir mix.lock files.
|
||||
func NewMixLockCataloger() pkg.Cataloger {
|
||||
func NewMixLockCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("elixir-mix-lock-cataloger").
|
||||
WithParserByGlobs(parseMixLock, "**/mix.lock")
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ Package erlang provides a concrete Cataloger implementation relating to packages
|
||||
package erlang
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewRebarLockCataloger returns a new cataloger instance for Erlang rebar.lock files.
|
||||
func NewRebarLockCataloger() pkg.Cataloger {
|
||||
func NewRebarLockCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("erlang-rebar-lock-cataloger").
|
||||
WithParserByGlobs(parseRebarLock, "**/rebar.lock")
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ Package gentoo provides a concrete Cataloger implementation related to packages
|
||||
package gentoo
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewPortageCataloger returns a new cataloger object initialized for Gentoo Portage package manager files (a flat-file store).
|
||||
func NewPortageCataloger() pkg.Cataloger {
|
||||
func NewPortageCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("portage-cataloger").
|
||||
WithParserByGlobs(parsePortageContents, "**/var/db/pkg/*/*/CONTENTS")
|
||||
}
|
||||
|
||||
@@ -3,20 +3,17 @@ Package githubactions provides a concrete Cataloger implementation for GitHub Ac
|
||||
*/
|
||||
package githubactions
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
import "github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
|
||||
// NewActionUsageCataloger returns GitHub Actions used within workflows and composite actions.
|
||||
func NewActionUsageCataloger() pkg.Cataloger {
|
||||
func NewActionUsageCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("github-actions-usage-cataloger").
|
||||
WithParserByGlobs(parseWorkflowForActionUsage, "**/.github/workflows/*.yaml", "**/.github/workflows/*.yml").
|
||||
WithParserByGlobs(parseCompositeActionForActionUsage, "**/.github/actions/*/action.yml", "**/.github/actions/*/action.yaml")
|
||||
}
|
||||
|
||||
// NewWorkflowUsageCataloger returns shared workflows used within workflows.
|
||||
func NewWorkflowUsageCataloger() pkg.Cataloger {
|
||||
func NewWorkflowUsageCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("github-action-workflow-usage-cataloger").
|
||||
WithParserByGlobs(parseWorkflowForWorkflowUsage, "**/.github/workflows/*.yaml", "**/.github/workflows/*.yml")
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package githubactions
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ func TestCataloger_Globs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
cataloger pkg.Cataloger
|
||||
cataloger *generic.Cataloger
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
@@ -19,28 +20,31 @@ import (
|
||||
var versionCandidateGroups = regexp.MustCompile(`(?P<version>\d+(\.\d+)?(\.\d+)?)(?P<candidate>\w*)`)
|
||||
|
||||
// NewGoModuleFileCataloger returns a new cataloger object that searches within go.mod files.
|
||||
func NewGoModuleFileCataloger(opts CatalogerConfig) pkg.Cataloger {
|
||||
func NewGoModuleFileCataloger(opts GoCatalogerOpts) pkg.Cataloger {
|
||||
c := goModCataloger{
|
||||
licenses: newGoLicenses(opts),
|
||||
}
|
||||
return &progressingCataloger{
|
||||
progress: c.licenses.progress,
|
||||
cataloger: generic.NewCataloger("go-module-file-cataloger").
|
||||
WithParserByGlobs(c.parseGoModFile, "**/go.mod"),
|
||||
}
|
||||
}
|
||||
|
||||
// NewGoModuleBinaryCataloger returns a new cataloger object that searches within binaries built by the go compiler.
|
||||
func NewGoModuleBinaryCataloger(opts CatalogerConfig) pkg.Cataloger {
|
||||
func NewGoModuleBinaryCataloger(opts GoCatalogerOpts) pkg.Cataloger {
|
||||
c := goBinaryCataloger{
|
||||
licenses: newGoLicenses(opts),
|
||||
}
|
||||
return &progressingCataloger{
|
||||
progress: c.licenses.progress,
|
||||
cataloger: generic.NewCataloger("go-module-binary-cataloger").
|
||||
WithParserByMimeTypes(c.parseGoBinary, internal.ExecutableMIMETypeSet.List()...),
|
||||
}
|
||||
}
|
||||
|
||||
type progressingCataloger struct {
|
||||
progress *monitor.CatalogerTask
|
||||
cataloger *generic.Cataloger
|
||||
}
|
||||
|
||||
@@ -49,6 +53,7 @@ func (p *progressingCataloger) Name() string {
|
||||
}
|
||||
|
||||
func (p *progressingCataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
defer p.progress.SetCompleted()
|
||||
pkgs, relationships, err := p.cataloger.Catalog(resolver)
|
||||
goCompilerPkgs := []pkg.Package{}
|
||||
totalLocations := file.NewLocationSet()
|
||||
@@ -71,7 +76,6 @@ func (p *progressingCataloger) Catalog(resolver file.Resolver) ([]pkg.Package, [
|
||||
pkgs = append(pkgs, goCompilerPkgs...)
|
||||
return pkgs, relationships, err
|
||||
}
|
||||
|
||||
func newGoStdLib(version string, location file.LocationSet) *pkg.Package {
|
||||
stdlibCpe, err := generateStdlibCpe(version)
|
||||
if err != nil {
|
||||
|
||||
@@ -30,7 +30,7 @@ func Test_Mod_Cataloger_Globs(t *testing.T) {
|
||||
FromDirectory(t, test.fixture).
|
||||
ExpectsResolverContentQueries(test.expected).
|
||||
IgnoreUnfulfilledPathResponses("src/go.sum").
|
||||
TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
|
||||
TestCataloger(t, NewGoModuleFileCataloger(GoCatalogerOpts{}))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func Test_Binary_Cataloger_Globs(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, test.fixture).
|
||||
ExpectsResolverContentQueries(test.expected).
|
||||
TestCataloger(t, NewGoModuleBinaryCataloger(CatalogerConfig{}))
|
||||
TestCataloger(t, NewGoModuleBinaryCataloger(GoCatalogerOpts{}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultProxies = "https://proxy.golang.org,direct"
|
||||
directProxyOnly = "direct"
|
||||
)
|
||||
|
||||
var (
|
||||
directProxiesOnly = []string{directProxyOnly}
|
||||
)
|
||||
|
||||
type CatalogerConfig struct {
|
||||
SearchLocalModCacheLicenses bool `yaml:"search-local-mod-cache-licenses" json:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
||||
LocalModCacheDir string `yaml:"local-mod-cache-dir" json:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
|
||||
SearchRemoteLicenses bool `yaml:"search-remote-licenses" json:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
Proxies []string `yaml:"proxies,omitempty" json:"proxies,omitempty" mapstructure:"proxies"`
|
||||
NoProxy []string `yaml:"no-proxy,omitempty" json:"no-proxy,omitempty" mapstructure:"no-proxy"`
|
||||
}
|
||||
|
||||
// DefaultCatalogerConfig create a CatalogerConfig with default options, which includes:
|
||||
// - setting the default remote proxy if none is provided
|
||||
// - setting the default no proxy if none is provided
|
||||
// - setting the default local module cache dir if none is provided
|
||||
func DefaultCatalogerConfig() CatalogerConfig {
|
||||
g := CatalogerConfig{}
|
||||
|
||||
// first process the proxy settings
|
||||
if len(g.Proxies) == 0 {
|
||||
goProxy := os.Getenv("GOPROXY")
|
||||
if goProxy == "" {
|
||||
goProxy = defaultProxies
|
||||
}
|
||||
g = g.WithProxy(goProxy)
|
||||
}
|
||||
|
||||
// next process the gonoproxy settings
|
||||
if len(g.NoProxy) == 0 {
|
||||
goPrivate := os.Getenv("GOPRIVATE")
|
||||
goNoProxy := os.Getenv("GONOPROXY")
|
||||
// we only use the env var if it was not set explicitly
|
||||
if goPrivate != "" {
|
||||
g.NoProxy = append(g.NoProxy, strings.Split(goPrivate, ",")...)
|
||||
}
|
||||
|
||||
// next process the goprivate settings; we always add those
|
||||
if goNoProxy != "" {
|
||||
g.NoProxy = append(g.NoProxy, strings.Split(goNoProxy, ",")...)
|
||||
}
|
||||
}
|
||||
|
||||
if g.LocalModCacheDir == "" {
|
||||
goPath := os.Getenv("GOPATH")
|
||||
|
||||
if goPath == "" {
|
||||
homeDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
log.Debug("unable to determine user home dir: %v", err)
|
||||
} else {
|
||||
goPath = path.Join(homeDir, "go")
|
||||
}
|
||||
}
|
||||
if goPath != "" {
|
||||
g.LocalModCacheDir = path.Join(goPath, "pkg", "mod")
|
||||
}
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func (g CatalogerConfig) WithSearchLocalModCacheLicenses(input bool) CatalogerConfig {
|
||||
g.SearchLocalModCacheLicenses = input
|
||||
return g
|
||||
}
|
||||
|
||||
func (g CatalogerConfig) WithLocalModCacheDir(input string) CatalogerConfig {
|
||||
if input == "" {
|
||||
return g
|
||||
}
|
||||
g.LocalModCacheDir = input
|
||||
return g
|
||||
}
|
||||
|
||||
func (g CatalogerConfig) WithSearchRemoteLicenses(input bool) CatalogerConfig {
|
||||
g.SearchRemoteLicenses = input
|
||||
return g
|
||||
}
|
||||
|
||||
func (g CatalogerConfig) WithProxy(input string) CatalogerConfig {
|
||||
if input == "" {
|
||||
return g
|
||||
}
|
||||
if input == "off" {
|
||||
input = directProxyOnly
|
||||
}
|
||||
g.Proxies = strings.Split(input, ",")
|
||||
return g
|
||||
}
|
||||
|
||||
func (g CatalogerConfig) WithNoProxy(input string) CatalogerConfig {
|
||||
if input == "" {
|
||||
return g
|
||||
}
|
||||
g.NoProxy = strings.Split(input, ",")
|
||||
return g
|
||||
}
|
||||
@@ -29,16 +29,16 @@ import (
|
||||
)
|
||||
|
||||
type goLicenses struct {
|
||||
opts CatalogerConfig
|
||||
opts GoCatalogerOpts
|
||||
localModCacheResolver file.WritableResolver
|
||||
progress *monitor.CatalogerTask
|
||||
lowerLicenseFileNames *strset.Set
|
||||
}
|
||||
|
||||
func newGoLicenses(opts CatalogerConfig) goLicenses {
|
||||
func newGoLicenses(opts GoCatalogerOpts) goLicenses {
|
||||
return goLicenses{
|
||||
opts: opts,
|
||||
localModCacheResolver: modCacheResolver(opts.LocalModCacheDir),
|
||||
localModCacheResolver: modCacheResolver(opts.localModCacheDir),
|
||||
progress: &monitor.CatalogerTask{
|
||||
SubStatus: true,
|
||||
RemoveOnCompletion: true,
|
||||
@@ -107,7 +107,7 @@ func (c *goLicenses) getLicenses(resolver file.Resolver, moduleName, moduleVersi
|
||||
}
|
||||
|
||||
func (c *goLicenses) getLicensesFromLocal(moduleName, moduleVersion string) ([]pkg.License, error) {
|
||||
if !c.opts.SearchLocalModCacheLicenses {
|
||||
if !c.opts.searchLocalModCacheLicenses {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -117,11 +117,11 @@ func (c *goLicenses) getLicensesFromLocal(moduleName, moduleVersion string) ([]p
|
||||
}
|
||||
|
||||
func (c *goLicenses) getLicensesFromRemote(moduleName, moduleVersion string) ([]pkg.License, error) {
|
||||
if !c.opts.SearchRemoteLicenses {
|
||||
if !c.opts.searchRemoteLicenses {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
proxies := remotesForModule(c.opts.Proxies, c.opts.NoProxy, moduleName)
|
||||
proxies := remotesForModule(c.opts.proxies, c.opts.noProxy, moduleName)
|
||||
|
||||
fsys, err := getModule(c.progress, proxies, moduleName, moduleVersion)
|
||||
if err != nil {
|
||||
@@ -231,18 +231,15 @@ func getModule(progress *monitor.CatalogerTask, proxies []string, moduleName, mo
|
||||
func getModuleProxy(progress *monitor.CatalogerTask, proxy string, moduleName string, moduleVersion string) (out fs.FS, _ error) {
|
||||
u := fmt.Sprintf("%s/%s/@v/%s.zip", proxy, moduleName, moduleVersion)
|
||||
progress.SetValue(u)
|
||||
|
||||
// get the module zip
|
||||
resp, err := http.Get(u) //nolint:gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
u = fmt.Sprintf("%s/%s/@v/%s.zip", proxy, strings.ToLower(moduleName), moduleVersion)
|
||||
progress.SetValue(u)
|
||||
|
||||
// try lowercasing it; some packages have mixed casing that really messes up the proxy
|
||||
resp, err = http.Get(u) //nolint:gosec
|
||||
if err != nil {
|
||||
@@ -253,23 +250,19 @@ func getModuleProxy(progress *monitor.CatalogerTask, proxy string, moduleName st
|
||||
return nil, fmt.Errorf("failed to get module zip: %s", resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// read the zip
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err = zip.NewReader(bytes.NewReader(b), resp.ContentLength)
|
||||
versionPath := findVersionPath(out, ".")
|
||||
out = getSubFS(out, versionPath)
|
||||
|
||||
return out, err
|
||||
}
|
||||
|
||||
func findVersionPath(f fs.FS, dir string) string {
|
||||
list, _ := fs.ReadDir(f, dir)
|
||||
|
||||
for _, entry := range list {
|
||||
name := entry.Name()
|
||||
if strings.Contains(name, "@") {
|
||||
@@ -280,7 +273,6 @@ func findVersionPath(f fs.FS, dir string) string {
|
||||
return path.Join(name, found)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -290,9 +282,7 @@ func getModuleRepository(progress *monitor.CatalogerTask, moduleName string, mod
|
||||
if len(parts) > 2 {
|
||||
repoName = fmt.Sprintf("%s/%s/%s", parts[0], parts[1], parts[2])
|
||||
}
|
||||
|
||||
progress.SetValue(fmt.Sprintf("git: %s", repoName))
|
||||
|
||||
f := memfs.New()
|
||||
buf := &bytes.Buffer{}
|
||||
_, err := git.Clone(memory.NewStorage(), f, &git.CloneOptions{
|
||||
@@ -302,7 +292,6 @@ func getModuleRepository(progress *monitor.CatalogerTask, moduleName string, mod
|
||||
Depth: 1,
|
||||
Progress: buf,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w -- %s", err, buf.String())
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ func Test_LocalLicenseSearch(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
l := newGoLicenses(
|
||||
CatalogerConfig{
|
||||
SearchLocalModCacheLicenses: true,
|
||||
LocalModCacheDir: path.Join(wd, "test-fixtures", "licenses", "pkg", "mod"),
|
||||
GoCatalogerOpts{
|
||||
searchLocalModCacheLicenses: true,
|
||||
localModCacheDir: path.Join(wd, "test-fixtures", "licenses", "pkg", "mod"),
|
||||
},
|
||||
)
|
||||
licenses, err := l.getLicenses(fileresolver.Empty{}, test.name, test.version)
|
||||
@@ -154,10 +154,10 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
l := newGoLicenses(CatalogerConfig{
|
||||
SearchRemoteLicenses: true,
|
||||
Proxies: []string{server.URL},
|
||||
LocalModCacheDir: modDir,
|
||||
l := newGoLicenses(GoCatalogerOpts{
|
||||
searchRemoteLicenses: true,
|
||||
proxies: []string{server.URL},
|
||||
localModCacheDir: modDir,
|
||||
})
|
||||
|
||||
licenses, err := l.getLicenses(fileresolver.Empty{}, test.name, test.version)
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultProxies = "https://proxy.golang.org,direct"
|
||||
directProxyOnly = "direct"
|
||||
)
|
||||
|
||||
var (
|
||||
directProxiesOnly = []string{directProxyOnly}
|
||||
)
|
||||
|
||||
type GoCatalogerOpts struct {
|
||||
searchLocalModCacheLicenses bool
|
||||
localModCacheDir string
|
||||
searchRemoteLicenses bool
|
||||
proxies []string
|
||||
noProxy []string
|
||||
}
|
||||
|
||||
func (g GoCatalogerOpts) WithSearchLocalModCacheLicenses(input bool) GoCatalogerOpts {
|
||||
g.searchLocalModCacheLicenses = input
|
||||
return g
|
||||
}
|
||||
|
||||
func (g GoCatalogerOpts) WithLocalModCacheDir(input string) GoCatalogerOpts {
|
||||
if input == "" {
|
||||
return g
|
||||
}
|
||||
g.localModCacheDir = input
|
||||
return g
|
||||
}
|
||||
|
||||
func (g GoCatalogerOpts) WithSearchRemoteLicenses(input bool) GoCatalogerOpts {
|
||||
g.searchRemoteLicenses = input
|
||||
return g
|
||||
}
|
||||
|
||||
func (g GoCatalogerOpts) WithProxy(input string) GoCatalogerOpts {
|
||||
if input == "" {
|
||||
return g
|
||||
}
|
||||
if input == "off" {
|
||||
input = directProxyOnly
|
||||
}
|
||||
g.proxies = strings.Split(input, ",")
|
||||
return g
|
||||
}
|
||||
|
||||
func (g GoCatalogerOpts) WithNoProxy(input string) GoCatalogerOpts {
|
||||
if input == "" {
|
||||
return g
|
||||
}
|
||||
g.noProxy = strings.Split(input, ",")
|
||||
return g
|
||||
}
|
||||
|
||||
// NewGoCatalogerOpts create a GoCatalogerOpts with default options, which includes:
|
||||
// - setting the default remote proxy if none is provided
|
||||
// - setting the default no proxy if none is provided
|
||||
// - setting the default local module cache dir if none is provided
|
||||
func NewGoCatalogerOpts() GoCatalogerOpts {
|
||||
g := GoCatalogerOpts{}
|
||||
|
||||
// first process the proxy settings
|
||||
if len(g.proxies) == 0 {
|
||||
goProxy := os.Getenv("GOPROXY")
|
||||
if goProxy == "" {
|
||||
goProxy = defaultProxies
|
||||
}
|
||||
g = g.WithProxy(goProxy)
|
||||
}
|
||||
|
||||
// next process the gonoproxy settings
|
||||
if len(g.noProxy) == 0 {
|
||||
goPrivate := os.Getenv("GOPRIVATE")
|
||||
goNoProxy := os.Getenv("GONOPROXY")
|
||||
// we only use the env var if it was not set explicitly
|
||||
if goPrivate != "" {
|
||||
g.noProxy = append(g.noProxy, strings.Split(goPrivate, ",")...)
|
||||
}
|
||||
|
||||
// next process the goprivate settings; we always add those
|
||||
if goNoProxy != "" {
|
||||
g.noProxy = append(g.noProxy, strings.Split(goNoProxy, ",")...)
|
||||
}
|
||||
}
|
||||
|
||||
if g.localModCacheDir == "" {
|
||||
goPath := os.Getenv("GOPATH")
|
||||
|
||||
if goPath == "" {
|
||||
homeDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
log.Debug("unable to determine user home dir: %v", err)
|
||||
} else {
|
||||
goPath = path.Join(homeDir, "go")
|
||||
}
|
||||
}
|
||||
if goPath != "" {
|
||||
g.localModCacheDir = path.Join(goPath, "pkg", "mod")
|
||||
}
|
||||
}
|
||||
return g
|
||||
}
|
||||
+14
-14
@@ -34,7 +34,7 @@ func Test_Options(t *testing.T) {
|
||||
name string
|
||||
env map[string]string
|
||||
opts opts
|
||||
expected CatalogerConfig
|
||||
expected GoCatalogerOpts
|
||||
}{
|
||||
{
|
||||
name: "set via env defaults",
|
||||
@@ -45,12 +45,12 @@ func Test_Options(t *testing.T) {
|
||||
"GONOPROXY": "no.proxy",
|
||||
},
|
||||
opts: opts{},
|
||||
expected: CatalogerConfig{
|
||||
SearchLocalModCacheLicenses: false,
|
||||
LocalModCacheDir: "/go/pkg/mod",
|
||||
SearchRemoteLicenses: false,
|
||||
Proxies: []string{"https://my.proxy"},
|
||||
NoProxy: []string{"my.private", "no.proxy"},
|
||||
expected: GoCatalogerOpts{
|
||||
searchLocalModCacheLicenses: false,
|
||||
localModCacheDir: "/go/pkg/mod",
|
||||
searchRemoteLicenses: false,
|
||||
proxies: []string{"https://my.proxy"},
|
||||
noProxy: []string{"my.private", "no.proxy"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -68,12 +68,12 @@ func Test_Options(t *testing.T) {
|
||||
proxy: "https://alt.proxy,direct",
|
||||
noProxy: "alt.no.proxy",
|
||||
},
|
||||
expected: CatalogerConfig{
|
||||
SearchLocalModCacheLicenses: true,
|
||||
LocalModCacheDir: "/go-cache",
|
||||
SearchRemoteLicenses: true,
|
||||
Proxies: []string{"https://alt.proxy", "direct"},
|
||||
NoProxy: []string{"alt.no.proxy"},
|
||||
expected: GoCatalogerOpts{
|
||||
searchLocalModCacheLicenses: true,
|
||||
localModCacheDir: "/go-cache",
|
||||
searchRemoteLicenses: true,
|
||||
proxies: []string{"https://alt.proxy", "direct"},
|
||||
noProxy: []string{"alt.no.proxy"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func Test_Options(t *testing.T) {
|
||||
for k, v := range test.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
got := DefaultCatalogerConfig().
|
||||
got := NewGoCatalogerOpts().
|
||||
WithSearchLocalModCacheLicenses(test.opts.local).
|
||||
WithLocalModCacheDir(test.opts.cacheDir).
|
||||
WithSearchRemoteLicenses(test.opts.remote).
|
||||
@@ -142,7 +142,7 @@ func Test_GoSumHashes(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, test.fixture).
|
||||
Expects(test.expected, nil).
|
||||
TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
|
||||
TestCataloger(t, NewGoModuleFileCataloger(GoCatalogerOpts{}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ Package haskell provides a concrete Cataloger implementation relating to package
|
||||
package haskell
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
// This hints at splitting these into multiple catalogers, but for now we'll keep them together.
|
||||
|
||||
// NewHackageCataloger returns a new Haskell cataloger object.
|
||||
func NewHackageCataloger() pkg.Cataloger {
|
||||
func NewHackageCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("haskell-cataloger").
|
||||
WithParserByGlobs(parseStackYaml, "**/stack.yaml").
|
||||
WithParserByGlobs(parseStackLock, "**/stack.yaml.lock").
|
||||
|
||||
@@ -53,14 +53,14 @@ type archiveParser struct {
|
||||
contentPath string
|
||||
fileInfo archiveFilename
|
||||
detectNested bool
|
||||
cfg ArchiveCatalogerConfig
|
||||
cfg Config
|
||||
}
|
||||
|
||||
type genericArchiveParserAdapter struct {
|
||||
cfg ArchiveCatalogerConfig
|
||||
cfg Config
|
||||
}
|
||||
|
||||
func newGenericArchiveParserAdapter(cfg ArchiveCatalogerConfig) genericArchiveParserAdapter {
|
||||
func newGenericArchiveParserAdapter(cfg Config) genericArchiveParserAdapter {
|
||||
return genericArchiveParserAdapter{cfg: cfg}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func uniquePkgKey(groupID string, p *pkg.Package) string {
|
||||
|
||||
// newJavaArchiveParser returns a new java archive parser object for the given archive. Can be configured to discover
|
||||
// and parse nested archives or ignore them.
|
||||
func newJavaArchiveParser(reader file.LocationReadCloser, detectNested bool, cfg ArchiveCatalogerConfig) (*archiveParser, func(), error) {
|
||||
func newJavaArchiveParser(reader file.LocationReadCloser, detectNested bool, cfg Config) (*archiveParser, func(), error) {
|
||||
// fetch the last element of the virtual path
|
||||
virtualElements := strings.Split(reader.Path(), ":")
|
||||
currentFilepath := virtualElements[len(virtualElements)-1]
|
||||
@@ -338,7 +338,7 @@ func artifactIDMatchesFilename(artifactID, fileName string) bool {
|
||||
return strings.HasPrefix(artifactID, fileName) || strings.HasSuffix(fileName, artifactID)
|
||||
}
|
||||
|
||||
func findPomLicenses(pomProjectObject *parsedPomProject, cfg ArchiveCatalogerConfig) {
|
||||
func findPomLicenses(pomProjectObject *parsedPomProject, cfg Config) {
|
||||
// If we don't have any licenses until now, and if we have a parent Pom, then we'll check the parent pom in maven central for licenses.
|
||||
if pomProjectObject != nil && pomProjectObject.Parent != nil && len(pomProjectObject.Licenses) == 0 {
|
||||
parentLicenses, err := recursivelyFindLicensesFromParentPom(
|
||||
@@ -373,11 +373,11 @@ func formatMavenPomURL(groupID, artifactID, version, mavenBaseURL string) (reque
|
||||
return requestURL, err
|
||||
}
|
||||
|
||||
func recursivelyFindLicensesFromParentPom(groupID, artifactID, version string, cfg ArchiveCatalogerConfig) ([]string, error) {
|
||||
func recursivelyFindLicensesFromParentPom(groupID, artifactID, version string, cfg Config) ([]string, error) {
|
||||
var licenses []string
|
||||
// As there can be nested parent poms, we'll recursively check for licenses until we reach the max depth
|
||||
for i := 0; i < cfg.MaxParentRecursiveDepth; i++ {
|
||||
parentPom, err := getPomFromMavenRepo(groupID, artifactID, version, cfg.MavenBaseURL)
|
||||
parentPom, err := getPomFromMavenCentral(groupID, artifactID, version, cfg.MavenBaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -395,7 +395,7 @@ func recursivelyFindLicensesFromParentPom(groupID, artifactID, version string, c
|
||||
return licenses, nil
|
||||
}
|
||||
|
||||
func getPomFromMavenRepo(groupID, artifactID, version, mavenBaseURL string) (*gopom.Project, error) {
|
||||
func getPomFromMavenCentral(groupID, artifactID, version, mavenBaseURL string) (*gopom.Project, error) {
|
||||
requestURL, err := formatMavenPomURL(groupID, artifactID, version, mavenBaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -542,7 +542,7 @@ func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) (
|
||||
|
||||
// discoverPkgsFromZip finds Java archives within Java archives, returning all listed Java packages found and
|
||||
// associating each discovered package to the given parent package.
|
||||
func discoverPkgsFromZip(location file.Location, archivePath, contentPath string, fileManifest intFile.ZipFileManifest, parentPkg *pkg.Package, cfg ArchiveCatalogerConfig) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func discoverPkgsFromZip(location file.Location, archivePath, contentPath string, fileManifest intFile.ZipFileManifest, parentPkg *pkg.Package, cfg Config) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
// search and parse pom.properties files & fetch the contents
|
||||
openers, err := intFile.ExtractFromZipToUniqueTempFile(archivePath, contentPath, fileManifest.GlobMatch(false, archiveFormatGlobs...)...)
|
||||
if err != nil {
|
||||
@@ -553,7 +553,7 @@ func discoverPkgsFromZip(location file.Location, archivePath, contentPath string
|
||||
}
|
||||
|
||||
// discoverPkgsFromOpeners finds Java archives within the given files and associates them with the given parent package.
|
||||
func discoverPkgsFromOpeners(location file.Location, openers map[string]intFile.Opener, parentPkg *pkg.Package, cfg ArchiveCatalogerConfig) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func discoverPkgsFromOpeners(location file.Location, openers map[string]intFile.Opener, parentPkg *pkg.Package, cfg Config) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var pkgs []pkg.Package
|
||||
var relationships []artifact.Relationship
|
||||
|
||||
@@ -582,7 +582,7 @@ func discoverPkgsFromOpeners(location file.Location, openers map[string]intFile.
|
||||
}
|
||||
|
||||
// discoverPkgsFromOpener finds Java archives within the given file.
|
||||
func discoverPkgsFromOpener(location file.Location, pathWithinArchive string, archiveOpener intFile.Opener, cfg ArchiveCatalogerConfig) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func discoverPkgsFromOpener(location file.Location, pathWithinArchive string, archiveOpener intFile.Opener, cfg Config) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
archiveReadCloser, err := archiveOpener.Open()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to open archived file from tempdir: %w", err)
|
||||
@@ -669,7 +669,7 @@ func pomProjectByParentPath(archivePath string, location file.Location, extractP
|
||||
|
||||
// newPackageFromMavenData processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
|
||||
// associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not.
|
||||
func newPackageFromMavenData(pomProperties pkg.JavaPomProperties, parsedPomProject *parsedPomProject, parentPkg *pkg.Package, location file.Location, cfg ArchiveCatalogerConfig) *pkg.Package {
|
||||
func newPackageFromMavenData(pomProperties pkg.JavaPomProperties, parsedPomProject *parsedPomProject, parentPkg *pkg.Package, location file.Location, cfg Config) *pkg.Package {
|
||||
// keep the artifact name within the virtual path if this package does not match the parent package
|
||||
vPathSuffix := ""
|
||||
groupID := ""
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestSearchMavenForLicenses(t *testing.T) {
|
||||
name string
|
||||
fixture string
|
||||
detectNested bool
|
||||
config ArchiveCatalogerConfig
|
||||
config Config
|
||||
requestPath string
|
||||
requestHandlers []handlerPath
|
||||
expectedLicenses []pkg.License
|
||||
@@ -87,7 +87,7 @@ func TestSearchMavenForLicenses(t *testing.T) {
|
||||
name: "searchMavenForLicenses returns the expected licenses when search is set to true",
|
||||
fixture: "opensaml-core-3.4.6",
|
||||
detectNested: false,
|
||||
config: ArchiveCatalogerConfig{
|
||||
config: Config{
|
||||
UseNetwork: true,
|
||||
MavenBaseURL: url,
|
||||
MaxParentRecursiveDepth: 2,
|
||||
@@ -161,7 +161,7 @@ func TestFormatMavenURL(t *testing.T) {
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
requestURL, err := formatMavenPomURL(tc.groupID, tc.artifactID, tc.version, mavenBaseURL)
|
||||
requestURL, err := formatMavenPomURL(tc.groupID, tc.artifactID, tc.version, MavenBaseURL)
|
||||
assert.NoError(t, err, "expected no err; got %w", err)
|
||||
assert.Equal(t, tc.expected, requestURL)
|
||||
})
|
||||
@@ -401,7 +401,7 @@ func TestParseJar(t *testing.T) {
|
||||
parser, cleanupFn, err := newJavaArchiveParser(file.LocationReadCloser{
|
||||
Location: file.NewLocation(fixture.Name()),
|
||||
ReadCloser: fixture,
|
||||
}, false, ArchiveCatalogerConfig{UseNetwork: false})
|
||||
}, false, Config{UseNetwork: false})
|
||||
defer cleanupFn()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -667,7 +667,7 @@ func TestParseNestedJar(t *testing.T) {
|
||||
|
||||
fixture, err := os.Open(test.fixture)
|
||||
require.NoError(t, err)
|
||||
gap := newGenericArchiveParserAdapter(ArchiveCatalogerConfig{})
|
||||
gap := newGenericArchiveParserAdapter(Config{})
|
||||
|
||||
actual, _, err := gap.parseJavaArchive(nil, nil, file.LocationReadCloser{
|
||||
Location: file.NewLocation(fixture.Name()),
|
||||
@@ -1089,7 +1089,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
||||
}
|
||||
test.expectedParent.Locations = locations
|
||||
|
||||
actualPackage := newPackageFromMavenData(test.props, test.project, test.parent, file.NewLocation(virtualPath), DefaultArchiveCatalogerConfig())
|
||||
actualPackage := newPackageFromMavenData(test.props, test.project, test.parent, file.NewLocation(virtualPath), Config{})
|
||||
if test.expectedPackage == nil {
|
||||
require.Nil(t, actualPackage)
|
||||
} else {
|
||||
@@ -1309,7 +1309,7 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gap := newGenericArchiveParserAdapter(ArchiveCatalogerConfig{})
|
||||
gap := newGenericArchiveParserAdapter(Config{})
|
||||
if tt.assignParent {
|
||||
assignParent(&tt.expectedPkgs[0], tt.expectedPkgs[1:]...)
|
||||
}
|
||||
|
||||
@@ -4,24 +4,23 @@ Package java provides a concrete Cataloger implementation for packages relating
|
||||
package java
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewArchiveCataloger returns a new Java archive cataloger object for detecting packages with archives (jar, war, ear, par, sar, jpi, hpi, and native-image formats)
|
||||
func NewArchiveCataloger(cfg ArchiveCatalogerConfig) *generic.Cataloger {
|
||||
func NewArchiveCataloger(cfg Config) *generic.Cataloger {
|
||||
gap := newGenericArchiveParserAdapter(cfg)
|
||||
|
||||
c := generic.NewCataloger("java-archive-cataloger").
|
||||
WithParserByGlobs(gap.parseJavaArchive, archiveFormatGlobs...)
|
||||
|
||||
if cfg.IncludeIndexedArchives {
|
||||
if cfg.SearchIndexedArchives {
|
||||
// java archives wrapped within zip files
|
||||
gzp := newGenericZipWrappedJavaArchiveParser(cfg)
|
||||
c.WithParserByGlobs(gzp.parseZipWrappedJavaArchive, genericZipGlobs...)
|
||||
}
|
||||
|
||||
if cfg.IncludeUnindexedArchives {
|
||||
if cfg.SearchUnindexedArchives {
|
||||
// java archives wrapped within tar files
|
||||
gtp := newGenericTarWrappedJavaArchiveParser(cfg)
|
||||
c.WithParserByGlobs(gtp.parseTarWrappedJavaArchive, genericTarGlobs...)
|
||||
@@ -31,14 +30,14 @@ func NewArchiveCataloger(cfg ArchiveCatalogerConfig) *generic.Cataloger {
|
||||
|
||||
// NewPomCataloger returns a cataloger capable of parsing dependencies from a pom.xml file.
|
||||
// Pom files list dependencies that maybe not be locally installed yet.
|
||||
func NewPomCataloger() pkg.Cataloger {
|
||||
func NewPomCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("java-pom-cataloger").
|
||||
WithParserByGlobs(parserPomXML, "**/pom.xml")
|
||||
}
|
||||
|
||||
// NewGradleLockfileCataloger returns a cataloger capable of parsing dependencies from a gradle.lockfile file.
|
||||
// Note: Older versions of lockfiles aren't supported yet
|
||||
func NewGradleLockfileCataloger() pkg.Cataloger {
|
||||
func NewGradleLockfileCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("java-gradle-lockfile-cataloger").
|
||||
WithParserByGlobs(parseGradleLockfile, gradleLockfileGlob)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package java
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
@@ -55,16 +54,10 @@ func Test_ArchiveCataloger_Globs(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, test.fixture).
|
||||
ExpectsResolverContentQueries(test.expected).
|
||||
TestCataloger(t,
|
||||
NewArchiveCataloger(
|
||||
ArchiveCatalogerConfig{
|
||||
ArchiveSearchConfig: cataloging.ArchiveSearchConfig{
|
||||
IncludeIndexedArchives: true,
|
||||
IncludeUnindexedArchives: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
TestCataloger(t, NewArchiveCataloger(Config{
|
||||
SearchUnindexedArchives: true,
|
||||
SearchIndexedArchives: true,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,9 @@
|
||||
package java
|
||||
|
||||
import "github.com/anchore/syft/syft/cataloging"
|
||||
|
||||
const mavenBaseURL = "https://repo1.maven.org/maven2"
|
||||
|
||||
type ArchiveCatalogerConfig struct {
|
||||
cataloging.ArchiveSearchConfig `yaml:",inline" json:"" mapstructure:",squash"`
|
||||
UseNetwork bool `yaml:"use-network" json:"use-network" mapstructure:"use-network"`
|
||||
MavenBaseURL string `yaml:"maven-base-url" json:"maven-base-url" mapstructure:"maven-base-url"`
|
||||
MaxParentRecursiveDepth int `yaml:"max-parent-recursive-depth" json:"max-parent-recursive-depth" mapstructure:"max-parent-recursive-depth"`
|
||||
}
|
||||
|
||||
func DefaultArchiveCatalogerConfig() ArchiveCatalogerConfig {
|
||||
return ArchiveCatalogerConfig{
|
||||
ArchiveSearchConfig: cataloging.ArchiveSearchConfig{
|
||||
IncludeIndexedArchives: true,
|
||||
IncludeUnindexedArchives: false,
|
||||
},
|
||||
UseNetwork: false,
|
||||
MavenBaseURL: mavenBaseURL,
|
||||
MaxParentRecursiveDepth: 5,
|
||||
}
|
||||
}
|
||||
|
||||
func (j ArchiveCatalogerConfig) WithUseNetwork(input bool) ArchiveCatalogerConfig {
|
||||
j.UseNetwork = input
|
||||
return j
|
||||
}
|
||||
|
||||
func (j ArchiveCatalogerConfig) WithMavenBaseURL(input string) ArchiveCatalogerConfig {
|
||||
if input != "" {
|
||||
j.MavenBaseURL = input
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func (j ArchiveCatalogerConfig) WithArchiveTraversal(search cataloging.ArchiveSearchConfig, maxDepth int) ArchiveCatalogerConfig {
|
||||
if maxDepth > 0 {
|
||||
j.MaxParentRecursiveDepth = maxDepth
|
||||
}
|
||||
j.ArchiveSearchConfig = search
|
||||
return j
|
||||
type Config struct {
|
||||
SearchUnindexedArchives bool
|
||||
SearchIndexedArchives bool
|
||||
UseNetwork bool
|
||||
MavenBaseURL string
|
||||
MaxParentRecursiveDepth int
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ const nativeImageInvalidIndexError = "parsing the executable file generated an i
|
||||
const nativeImageMissingExportedDataDirectoryError = "exported data directory is missing"
|
||||
|
||||
// newNativeImageCataloger returns a new Native Image cataloger object.
|
||||
func NewNativeImageCataloger() pkg.Cataloger {
|
||||
func NewNativeImageCataloger() *NativeImageCataloger {
|
||||
return &NativeImageCataloger{}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package java
|
||||
|
||||
const MavenBaseURL = "https://repo1.maven.org/maven2"
|
||||
|
||||
type CatalogerOpts struct {
|
||||
UseNetwork bool
|
||||
MavenURL string
|
||||
MaxParentRecursiveDepth int
|
||||
}
|
||||
|
||||
func (j CatalogerOpts) WithUseNetwork(input bool) CatalogerOpts {
|
||||
j.UseNetwork = input
|
||||
return j
|
||||
}
|
||||
|
||||
func (j CatalogerOpts) WithMavenURL(input string) CatalogerOpts {
|
||||
if input != "" {
|
||||
j.MavenURL = input
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func (j CatalogerOpts) WithMaxParentRecursiveDepth(input int) CatalogerOpts {
|
||||
if input > 0 {
|
||||
j.MaxParentRecursiveDepth = input
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func DefaultCatalogerOpts() CatalogerOpts {
|
||||
return CatalogerOpts{
|
||||
UseNetwork: false,
|
||||
MavenURL: MavenBaseURL,
|
||||
MaxParentRecursiveDepth: 5,
|
||||
}
|
||||
}
|
||||
@@ -47,10 +47,10 @@ var genericTarGlobs = []string{
|
||||
// a file listing within the archive you must decompress the entire archive and seek through all of the entries.
|
||||
|
||||
type genericTarWrappedJavaArchiveParser struct {
|
||||
cfg ArchiveCatalogerConfig
|
||||
cfg Config
|
||||
}
|
||||
|
||||
func newGenericTarWrappedJavaArchiveParser(cfg ArchiveCatalogerConfig) genericTarWrappedJavaArchiveParser {
|
||||
func newGenericTarWrappedJavaArchiveParser(cfg Config) genericTarWrappedJavaArchiveParser {
|
||||
return genericTarWrappedJavaArchiveParser{
|
||||
cfg: cfg,
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func (gtp genericTarWrappedJavaArchiveParser) parseTarWrappedJavaArchive(_ file.
|
||||
return discoverPkgsFromTar(reader.Location, archivePath, contentPath, gtp.cfg)
|
||||
}
|
||||
|
||||
func discoverPkgsFromTar(location file.Location, archivePath, contentPath string, cfg ArchiveCatalogerConfig) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func discoverPkgsFromTar(location file.Location, archivePath, contentPath string, cfg Config) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
openers, err := intFile.ExtractGlobsFromTarToUniqueTempFile(archivePath, contentPath, archiveFormatGlobs...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to extract files from tar: %w", err)
|
||||
|
||||
@@ -40,7 +40,7 @@ func Test_parseTarWrappedJavaArchive(t *testing.T) {
|
||||
t.Fatalf("failed to open fixture: %+v", err)
|
||||
}
|
||||
|
||||
gtp := newGenericTarWrappedJavaArchiveParser(ArchiveCatalogerConfig{})
|
||||
gtp := newGenericTarWrappedJavaArchiveParser(Config{})
|
||||
actualPkgs, _, err := gtp.parseTarWrappedJavaArchive(nil, nil, file.LocationReadCloser{
|
||||
Location: file.NewLocation(test.fixture),
|
||||
ReadCloser: fixture,
|
||||
|
||||
@@ -19,10 +19,10 @@ var genericZipGlobs = []string{
|
||||
// parseZipWrappedJavaArchive is a parser function for java archive contents contained within arbitrary zip files.
|
||||
|
||||
type genericZipWrappedJavaArchiveParser struct {
|
||||
cfg ArchiveCatalogerConfig
|
||||
cfg Config
|
||||
}
|
||||
|
||||
func newGenericZipWrappedJavaArchiveParser(cfg ArchiveCatalogerConfig) genericZipWrappedJavaArchiveParser {
|
||||
func newGenericZipWrappedJavaArchiveParser(cfg Config) genericZipWrappedJavaArchiveParser {
|
||||
return genericZipWrappedJavaArchiveParser{
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func Test_parseZipWrappedJavaArchive(t *testing.T) {
|
||||
t.Fatalf("failed to open fixture: %+v", err)
|
||||
}
|
||||
|
||||
gzp := newGenericZipWrappedJavaArchiveParser(ArchiveCatalogerConfig{})
|
||||
gzp := newGenericZipWrappedJavaArchiveParser(Config{})
|
||||
|
||||
actualPkgs, _, err := gzp.parseZipWrappedJavaArchive(nil, nil, file.LocationReadCloser{
|
||||
Location: file.NewLocation(test.fixture),
|
||||
|
||||
@@ -4,18 +4,17 @@ Package javascript provides a concrete Cataloger implementation for packages rel
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewPackageCataloger returns a new cataloger object for NPM.
|
||||
func NewPackageCataloger() pkg.Cataloger {
|
||||
func NewPackageCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("javascript-package-cataloger").
|
||||
WithParserByGlobs(parsePackageJSON, "**/package.json")
|
||||
}
|
||||
|
||||
// NewLockCataloger returns a new cataloger object for NPM (and NPM-adjacent, such as yarn) lock files.
|
||||
func NewLockCataloger() pkg.Cataloger {
|
||||
func NewLockCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("javascript-lock-cataloger").
|
||||
WithParserByGlobs(parsePackageLock, "**/package-lock.json").
|
||||
WithParserByGlobs(parseYarnLock, "**/yarn.lock").
|
||||
|
||||
@@ -15,16 +15,16 @@ import (
|
||||
|
||||
var _ pkg.Cataloger = (*LinuxKernelCataloger)(nil)
|
||||
|
||||
type LinuxKernelCatalogerConfig struct {
|
||||
CatalogModules bool `yaml:"catalog-modules" json:"catalog-modules" mapstructure:"catalog-modules"`
|
||||
type LinuxCatalogerConfig struct {
|
||||
CatalogModules bool
|
||||
}
|
||||
|
||||
type LinuxKernelCataloger struct {
|
||||
cfg LinuxKernelCatalogerConfig
|
||||
cfg LinuxCatalogerConfig
|
||||
}
|
||||
|
||||
func DefaultLinuxCatalogerConfig() LinuxKernelCatalogerConfig {
|
||||
return LinuxKernelCatalogerConfig{
|
||||
func DefaultLinuxCatalogerConfig() LinuxCatalogerConfig {
|
||||
return LinuxCatalogerConfig{
|
||||
CatalogModules: true,
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ var kernelModuleGlobs = []string{
|
||||
}
|
||||
|
||||
// NewLinuxKernelCataloger returns a new kernel files cataloger object.
|
||||
func NewLinuxKernelCataloger(cfg LinuxKernelCatalogerConfig) *LinuxKernelCataloger {
|
||||
func NewLinuxKernelCataloger(cfg LinuxCatalogerConfig) *LinuxKernelCataloger {
|
||||
return &LinuxKernelCataloger{
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func Test_KernelCataloger(t *testing.T) {
|
||||
Expects(expectedPkgs, expectedRelationships).
|
||||
TestCataloger(t,
|
||||
NewLinuxKernelCataloger(
|
||||
LinuxKernelCatalogerConfig{
|
||||
LinuxCatalogerConfig{
|
||||
CatalogModules: true,
|
||||
},
|
||||
),
|
||||
|
||||
@@ -19,7 +19,7 @@ const catalogerName = "nix-store-cataloger"
|
||||
// StoreCataloger finds package outputs installed in the Nix store location (/nix/store/*).
|
||||
type StoreCataloger struct{}
|
||||
|
||||
func NewStoreCataloger() pkg.Cataloger {
|
||||
func NewStoreCataloger() *StoreCataloger {
|
||||
return &StoreCataloger{}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ Package php provides a concrete Cataloger implementation relating to packages wi
|
||||
package php
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
@@ -12,13 +11,13 @@ import (
|
||||
// semantic meanings. The lock file represents what should be installed, whereas the installed file represents what is installed.
|
||||
|
||||
// NewComposerInstalledCataloger returns a new cataloger for PHP installed.json files.
|
||||
func NewComposerInstalledCataloger() pkg.Cataloger {
|
||||
func NewComposerInstalledCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("php-composer-installed-cataloger").
|
||||
WithParserByGlobs(parseInstalledJSON, "**/installed.json")
|
||||
}
|
||||
|
||||
// NewComposerLockCataloger returns a new cataloger for PHP composer.lock files.
|
||||
func NewComposerLockCataloger() pkg.Cataloger {
|
||||
func NewComposerLockCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("php-composer-lock-cataloger").
|
||||
WithParserByGlobs(parseComposerLock, "**/composer.lock")
|
||||
}
|
||||
|
||||
@@ -4,14 +4,13 @@ Package python provides a concrete Cataloger implementation relating to packages
|
||||
package python
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
const eggInfoGlob = "**/*.egg-info"
|
||||
|
||||
type CatalogerConfig struct {
|
||||
GuessUnpinnedRequirements bool `yaml:"guess-unpinned-requirements" json:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"`
|
||||
GuessUnpinnedRequirements bool
|
||||
}
|
||||
|
||||
func DefaultCatalogerConfig() CatalogerConfig {
|
||||
@@ -31,7 +30,7 @@ func NewPackageCataloger(cfg CatalogerConfig) *generic.Cataloger {
|
||||
}
|
||||
|
||||
// NewInstalledPackageCataloger returns a new cataloger for python packages within egg or wheel installation directories.
|
||||
func NewInstalledPackageCataloger() pkg.Cataloger {
|
||||
func NewInstalledPackageCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("python-installed-package-cataloger").
|
||||
WithParserByGlobs(
|
||||
parseWheelOrEgg,
|
||||
|
||||
@@ -4,12 +4,11 @@ Package r provides a concrete Cataloger implementation relating to packages with
|
||||
package r
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewPackageCataloger returns a new R cataloger object based on detection of R package DESCRIPTION files.
|
||||
func NewPackageCataloger() pkg.Cataloger {
|
||||
func NewPackageCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("r-package-cataloger").
|
||||
WithParserByGlobs(parseDescriptionFile, "**/DESCRIPTION")
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// NewDBCataloger returns a new RPM DB cataloger object.
|
||||
func NewDBCataloger() pkg.Cataloger {
|
||||
func NewDBCataloger() *generic.Cataloger {
|
||||
// check if a sqlite driver is available
|
||||
if !isSqliteDriverAvailable() {
|
||||
log.Warnf("sqlite driver is not available, newer RPM databases might not be cataloged")
|
||||
@@ -24,7 +24,7 @@ func NewDBCataloger() pkg.Cataloger {
|
||||
}
|
||||
|
||||
// NewArchiveCataloger returns a new RPM file cataloger object.
|
||||
func NewArchiveCataloger() pkg.Cataloger {
|
||||
func NewArchiveCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("rpm-archive-cataloger").
|
||||
WithParserByGlobs(parseRpmArchive, "**/*.rpm")
|
||||
}
|
||||
|
||||
@@ -4,24 +4,23 @@ Package ruby provides a concrete Cataloger implementation relating to packages w
|
||||
package ruby
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewGemFileLockCataloger returns a new Bundler cataloger object tailored for parsing index-oriented files (e.g. Gemfile.lock).
|
||||
func NewGemFileLockCataloger() pkg.Cataloger {
|
||||
func NewGemFileLockCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("ruby-gemfile-cataloger").
|
||||
WithParserByGlobs(parseGemFileLockEntries, "**/Gemfile.lock")
|
||||
}
|
||||
|
||||
// NewInstalledGemSpecCataloger returns a new Bundler cataloger object tailored for detecting installations of gems (e.g. Gemspec).
|
||||
func NewInstalledGemSpecCataloger() pkg.Cataloger {
|
||||
func NewInstalledGemSpecCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("ruby-installed-gemspec-cataloger").
|
||||
WithParserByGlobs(parseGemSpecEntries, "**/specifications/**/*.gemspec")
|
||||
}
|
||||
|
||||
// NewGemSpecCataloger looks for gems without the additional requirement of the gem being installed.
|
||||
func NewGemSpecCataloger() pkg.Cataloger {
|
||||
func NewGemSpecCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("ruby-gemspec-cataloger").
|
||||
WithParserByGlobs(parseGemSpecEntries, "**/*.gemspec")
|
||||
}
|
||||
@@ -5,19 +5,18 @@ package rust
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewCargoLockCataloger returns a new Rust Cargo lock file cataloger object.
|
||||
func NewCargoLockCataloger() pkg.Cataloger {
|
||||
func NewCargoLockCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("rust-cargo-lock-cataloger").
|
||||
WithParserByGlobs(parseCargoLock, "**/Cargo.lock")
|
||||
}
|
||||
|
||||
// NewAuditBinaryCataloger returns a new Rust auditable binary cataloger object that can detect dependencies
|
||||
// in binaries produced with https://github.com/Shnatsel/rust-audit
|
||||
func NewAuditBinaryCataloger() pkg.Cataloger {
|
||||
func NewAuditBinaryCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("cargo-auditable-binary-cataloger").
|
||||
WithParserByMimeTypes(parseAuditBinary, internal.ExecutableMIMETypeSet.List()...)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
const catalogerName = "sbom-cataloger"
|
||||
|
||||
// NewCataloger returns a new SBOM cataloger object loaded from saved SBOM JSON.
|
||||
func NewCataloger() pkg.Cataloger {
|
||||
func NewCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger(catalogerName).
|
||||
WithParserByGlobs(parseSBOM,
|
||||
"**/*.syft.json",
|
||||
|
||||
@@ -4,17 +4,16 @@ Package swift provides a concrete Cataloger implementation relating to packages
|
||||
package swift
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
func NewSwiftPackageManagerCataloger() pkg.Cataloger {
|
||||
func NewSwiftPackageManagerCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("swift-package-manager-cataloger").
|
||||
WithParserByGlobs(parsePackageResolved, "**/Package.resolved", "**/.package.resolved")
|
||||
}
|
||||
|
||||
// NewCocoapodsCataloger returns a new Swift Cocoapods lock file cataloger object.
|
||||
func NewCocoapodsCataloger() pkg.Cataloger {
|
||||
func NewCocoapodsCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("cocoapods-cataloger").
|
||||
WithParserByGlobs(parsePodfileLock, "**/Podfile.lock")
|
||||
}
|
||||
|
||||
@@ -159,10 +159,11 @@ func (s FileSource) FileResolver(_ Scope) (file.Resolver, error) {
|
||||
}
|
||||
isArchiveAnalysis := fi.IsDir()
|
||||
|
||||
absParentDir, err := absoluteSymlinkFreePathToParent(s.analysisPath)
|
||||
absAnalysisPath, err := filepath.Abs(s.analysisPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to get absolute path for analysis path=%q: %w", s.analysisPath, err)
|
||||
}
|
||||
absParentDir := filepath.Dir(absAnalysisPath)
|
||||
|
||||
var res *fileresolver.Directory
|
||||
if isArchiveAnalysis {
|
||||
@@ -209,18 +210,6 @@ func (s FileSource) FileResolver(_ Scope) (file.Resolver, error) {
|
||||
return s.resolver, nil
|
||||
}
|
||||
|
||||
func absoluteSymlinkFreePathToParent(path string) (string, error) {
|
||||
absAnalysisPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to get absolute path for analysis path=%q: %w", path, err)
|
||||
}
|
||||
dereferencedAbsAnalysisPath, err := filepath.EvalSymlinks(absAnalysisPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to get absolute path for analysis path=%q: %w", path, err)
|
||||
}
|
||||
return filepath.Dir(dereferencedAbsAnalysisPath), nil
|
||||
}
|
||||
|
||||
func (s *FileSource) Close() error {
|
||||
if s.closer == nil {
|
||||
return nil
|
||||
|
||||
@@ -48,22 +48,6 @@ func TestNewFromFile(t *testing.T) {
|
||||
},
|
||||
expRefs: 1,
|
||||
},
|
||||
{
|
||||
desc: "normal path",
|
||||
input: "test-fixtures/actual-path/empty",
|
||||
testPathFn: func(resolver file.Resolver) ([]file.Location, error) {
|
||||
return resolver.FilesByPath("empty")
|
||||
},
|
||||
expRefs: 1,
|
||||
},
|
||||
{
|
||||
desc: "path containing symlink",
|
||||
input: "test-fixtures/symlink/empty",
|
||||
testPathFn: func(resolver file.Resolver) ([]file.Location, error) {
|
||||
return resolver.FilesByPath("empty")
|
||||
},
|
||||
expRefs: 1,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
|
||||
@@ -7,11 +7,11 @@ type Scope string
|
||||
|
||||
const (
|
||||
// UnknownScope is the default scope
|
||||
UnknownScope Scope = "unknown-scope"
|
||||
UnknownScope Scope = "UnknownScope"
|
||||
// SquashedScope indicates to only catalog content visible from the squashed filesystem representation (what can be seen only within the container at runtime)
|
||||
SquashedScope Scope = "squashed"
|
||||
SquashedScope Scope = "Squashed"
|
||||
// AllLayersScope indicates to catalog content on all layers, regardless if it is visible from the container at runtime.
|
||||
AllLayersScope Scope = "all-layers"
|
||||
AllLayersScope Scope = "AllLayers"
|
||||
)
|
||||
|
||||
// AllScopes is a slice containing all possible scope options
|
||||
@@ -23,9 +23,9 @@ var AllScopes = []Scope{
|
||||
// ParseScope returns a scope as indicated from the given string.
|
||||
func ParseScope(userStr string) Scope {
|
||||
switch strings.ToLower(userStr) {
|
||||
case SquashedScope.String():
|
||||
case strings.ToLower(SquashedScope.String()):
|
||||
return SquashedScope
|
||||
case "alllayers", AllLayersScope.String():
|
||||
case "all-layers", strings.ToLower(AllLayersScope.String()):
|
||||
return AllLayersScope
|
||||
}
|
||||
return UnknownScope
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package source
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseScope(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want Scope
|
||||
}{
|
||||
// go cases
|
||||
{
|
||||
name: "squashed",
|
||||
want: SquashedScope,
|
||||
},
|
||||
{
|
||||
name: "all-layers",
|
||||
want: AllLayersScope,
|
||||
},
|
||||
// fall back to unknown
|
||||
{
|
||||
name: "make-believe",
|
||||
want: UnknownScope,
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
want: UnknownScope,
|
||||
},
|
||||
{
|
||||
name: " ",
|
||||
want: UnknownScope,
|
||||
},
|
||||
// to support the original value
|
||||
|
||||
{
|
||||
name: "Squashed",
|
||||
want: SquashedScope,
|
||||
},
|
||||
{
|
||||
name: "AllLayers",
|
||||
want: AllLayersScope,
|
||||
},
|
||||
// case insensitive
|
||||
{
|
||||
name: "alLlaYerS",
|
||||
want: AllLayersScope,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, ParseScope(tt.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
actual-path
|
||||
@@ -1,14 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_RequestedPathIncludesSymlink(t *testing.T) {
|
||||
// path contains a symlink
|
||||
path := "test-fixtures/image-pkg-coverage/pkgs/java/example-java-app-maven-0.1.0.jar"
|
||||
_, stdout, _ := runSyft(t, nil, "packages", path)
|
||||
assert.Contains(t, stdout, "example-java-app-maven")
|
||||
}
|
||||
@@ -78,9 +78,10 @@ func TestConvertCmd(t *testing.T) {
|
||||
}()
|
||||
|
||||
opts := &commands.ConvertOptions{
|
||||
Output: options.DefaultOutput(),
|
||||
Output: options.Output{
|
||||
Outputs: []string{fmt.Sprintf("%s=%s", test.format.ID().String(), formatFile.Name())},
|
||||
},
|
||||
}
|
||||
opts.Outputs = []string{fmt.Sprintf("%s=%s", test.format.ID().String(), formatFile.Name())}
|
||||
require.NoError(t, opts.PostLoad())
|
||||
|
||||
// stdout reduction of test noise
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestJavaPURLs(t *testing.T) {
|
||||
// syft anchore/test_images:java-56d52bc -o template -t /tmp/test.templ | grep 'pkg:maven' | sort | uniq >> test/integration/java_purl_test.go
|
||||
// where the template is:
|
||||
/*
|
||||
{{ range .Artifacts}}"{{.Name}}@{{.Version}}":"{{.PURL}}",
|
||||
{{ range .Packages}}"{{.Name}}@{{.Version}}":"{{.PURL}}",
|
||||
{{ end }}
|
||||
*/
|
||||
// The map was then hand-edited for correctness by comparing to Maven Central.
|
||||
@@ -159,12 +159,12 @@ var expectedPURLs = map[string]string{
|
||||
"sezpoz@1.7": "pkg:maven/net.java.sezpoz/sezpoz@1.7",
|
||||
"slave@": "pkg:maven/slave/slave",
|
||||
"slide-webdavlib@2.1": "pkg:maven/slide-webdavlib/slide-webdavlib@2.1",
|
||||
"spring-aop@2.5": "pkg:maven/org.springframework/spring-aop@2.5",
|
||||
"spring-aop@2.5": "pkg:maven/org.springframework.bundle.spring.aop/spring-aop@2.5",
|
||||
"spring-beans@2.5": "pkg:maven/org.springframework/spring-beans@2.5",
|
||||
"spring-context@2.5": "pkg:maven/org.springframework/spring-context@2.5",
|
||||
"spring-context@2.5": "pkg:maven/org.springframework.bundle.spring.context/spring-context@2.5",
|
||||
"spring-core@2.5": "pkg:maven/org.springframework/spring-core@2.5",
|
||||
"spring-dao@1.2.9": "pkg:maven/org.springframework/spring-dao@1.2.9",
|
||||
"spring-jdbc@1.2.9": "pkg:maven/org.springframework/spring-jdbc@1.2.9",
|
||||
"spring-dao@1.2.9": "pkg:maven/spring-dao/spring-dao@1.2.9",
|
||||
"spring-jdbc@1.2.9": "pkg:maven/spring-jdbc/spring-jdbc@1.2.9",
|
||||
"spring-web@2.5": "pkg:maven/org.springframework/spring-web@2.5",
|
||||
"ssh-slaves@0.14": "pkg:maven/org.jvnet.hudson.plugins/ssh-slaves@0.14",
|
||||
"stapler-adjunct-timeline@1.2": "pkg:maven/org.kohsuke.stapler/stapler-adjunct-timeline@1.2",
|
||||
|
||||
@@ -37,7 +37,7 @@ func TestPackageOwnershipRelationships(t *testing.T) {
|
||||
t.Fatalf("unable to decode json doc: %+v", err)
|
||||
}
|
||||
|
||||
if len(doc.ArtifactRelationships) == 0 {
|
||||
if len(doc.Relationships) == 0 {
|
||||
t.Errorf("expected to find relationships between packages but found none")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user