mirror of
https://github.com/anchore/syft.git
synced 2026-10-26 03:52:22 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
356f7c92b4 | ||
|
|
6377465440 | ||
|
|
3ad91f2678 | ||
|
|
5ef83fdc79 | ||
|
|
06ff1a353a | ||
|
|
326ec57d4a | ||
|
|
48e5672a87 | ||
|
|
63171b55dd | ||
|
|
acc473fc30 | ||
|
|
a978966cad | ||
|
|
928511ea0f | ||
|
|
33b72ccbf8 | ||
|
|
2995c3c4fd | ||
|
|
170ac079d4 | ||
|
|
108a5dae9b | ||
|
|
cdf1e0bacb | ||
|
|
0c3b8ca4ed | ||
|
|
434b6ad506 | ||
|
|
3598cb4f8f | ||
|
|
8260bce057 | ||
|
|
578ac9cf2d | ||
|
|
a1b23bd57d | ||
|
|
65cadda486 | ||
|
|
a909e3cec9 | ||
|
|
8e62ff9831 | ||
|
|
96ee2db875 | ||
|
|
98b700e83c | ||
|
|
9803db2949 | ||
|
|
17ef243956 | ||
|
|
3ac7369068 | ||
|
|
4d4efa4963 | ||
|
|
a7da2270c7 | ||
|
|
6288530835 | ||
|
|
25d3c06962 | ||
|
|
79b71be0ee | ||
|
|
25ae7bf55f | ||
|
|
e72dec8e9e | ||
|
|
3398e72066 | ||
|
|
d26a5c4d30 | ||
|
|
84576b93e1 | ||
|
|
737c4e44c5 | ||
|
|
7444a9f976 | ||
|
|
8683cba081 | ||
|
|
c0f43e5e2d | ||
|
|
397cf210de |
+3
-3
@@ -26,7 +26,7 @@ tools:
|
||||
# used for linting
|
||||
- name: golangci-lint
|
||||
version:
|
||||
want: v1.55.2
|
||||
want: v1.56.2
|
||||
method: github-release
|
||||
with:
|
||||
repo: golangci/golangci-lint
|
||||
@@ -103,7 +103,7 @@ tools:
|
||||
# used for running all local and CI tasks
|
||||
- name: task
|
||||
version:
|
||||
want: v3.34.1
|
||||
want: v3.35.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: go-task/task
|
||||
@@ -111,7 +111,7 @@ tools:
|
||||
# used for triggering a release
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.43.1
|
||||
want: v2.44.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
enforce-v0: true # don't make breaking-change label bump major version before 1.0.
|
||||
enforce-v0: false
|
||||
title: ""
|
||||
|
||||
github:
|
||||
|
||||
@@ -20,7 +20,7 @@ permissions:
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04-4core-16gb
|
||||
|
||||
permissions:
|
||||
security-events: write
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 #v3.24.0
|
||||
uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea #v3.24.5
|
||||
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@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 #v3.24.0
|
||||
uses: github/codeql-action/autobuild@47b3d888fe66b639e431abf22ebca059152f1eea #v3.24.5
|
||||
|
||||
# ℹ️ 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@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 #v3.24.0
|
||||
uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea #v3.24.5
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
echo -n "commit=$(git rev-parse HEAD)" | tee -a $GITHUB_OUTPUT
|
||||
|
||||
- name: Update Homebrew formula
|
||||
uses: dawidd6/action-homebrew-bump-formula@75ed025ff3ad1d617862838b342b06d613a0ddf3 # v3.10.1
|
||||
uses: dawidd6/action-homebrew-bump-formula@baf2b60c51fc1f8453c884b0c61052668a71bd1d # v3.11.0
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW_TOKEN }}
|
||||
org: anchore
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 #v6.0.0
|
||||
- uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc #v6.0.1
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 #v6.0.0
|
||||
- uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc #v6.0.1
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 #v6.0.0
|
||||
- uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc #v6.0.1
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -54,6 +54,7 @@ For commercial support options with Syft or Grype, please [contact Anchore](http
|
||||
- Ruby (gem)
|
||||
- Rust (cargo.lock)
|
||||
- Swift (cocoapods, swift-package-manager)
|
||||
- Wordpress plugins
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -121,7 +122,8 @@ syft <image> --scope all-layers
|
||||
|
||||
### Supported sources
|
||||
|
||||
Syft can generate an SBOM from a variety of sources:
|
||||
Syft can generate an SBOM from a variety of sources including images, files, directories, and archives. Syft will attempt to
|
||||
determine the type of source based on provided input, for example:
|
||||
|
||||
```bash
|
||||
# catalog a container image archive (from the result of `docker image save ...`, `podman save ...`, or `skopeo copy` commands)
|
||||
@@ -134,26 +136,24 @@ syft path/to/image.sif
|
||||
syft path/to/dir
|
||||
```
|
||||
|
||||
Sources can be explicitly provided with a scheme:
|
||||
To explicitly specify the source behavior, use the `--from` flag. Allowable options are:
|
||||
|
||||
```
|
||||
docker:yourrepo/yourimage:tag use images from the Docker daemon
|
||||
podman:yourrepo/yourimage:tag use images from the Podman daemon
|
||||
containerd:yourrepo/yourimage:tag use images from the Containerd daemon
|
||||
docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
|
||||
oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
||||
oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
|
||||
singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk
|
||||
dir:path/to/yourproject read directly from a path on disk (any directory)
|
||||
file:path/to/yourproject/file read directly from a path on disk (any single file)
|
||||
registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required)
|
||||
docker use images from the Docker daemon
|
||||
podman use images from the Podman daemon
|
||||
containerd use images from the Containerd daemon
|
||||
docker-archive use a tarball from disk for archives created from "docker save"
|
||||
oci-archive use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
||||
oci-dir read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
|
||||
singularity read directly from a Singularity Image Format (SIF) container on disk
|
||||
dir read directly from a path on disk (any directory)
|
||||
file read directly from a path on disk (any single file)
|
||||
registry pull image directly from a registry (no container runtime required)
|
||||
```
|
||||
If a source is not provided and Syft identifies the input as a potential image reference, Syft will attempt to resolve it using:
|
||||
the Docker, Podman, and Containerd daemons followed by direct registry access, in that order.
|
||||
|
||||
If an image source is not provided and cannot be detected from the given reference it is assumed the image should be pulled from the Docker daemon.
|
||||
If docker is not present, then the Podman daemon is attempted next, followed by reaching out directly to the image registry last.
|
||||
|
||||
|
||||
This default behavior can be overridden with the `default-image-pull-source` configuration option (See [Configuration](https://github.com/anchore/syft#configuration) for more details).
|
||||
This default behavior can be overridden with the `default-image-pull-source` configuration option (See [Configuration](#configuration) for more details).
|
||||
|
||||
|
||||
### File selection
|
||||
@@ -228,12 +228,12 @@ syft <a directory> --select-catalogers "python"
|
||||
|
||||
Use the default set of catalogers and add a cataloger to the set:
|
||||
```bash
|
||||
syft ... --catalogers "+sbom-cataloger"
|
||||
syft ... --select-catalogers "+sbom-cataloger"
|
||||
```
|
||||
|
||||
Use the default set of catalogers but remove any catalogers that deal with RPMs:
|
||||
```bash
|
||||
syft ... --catalogers "-rpm"
|
||||
syft ... --select-catalogers "-rpm"
|
||||
```
|
||||
|
||||
Only scan with catalogers that:
|
||||
@@ -305,7 +305,7 @@ Where the `formats` available are:
|
||||
|
||||
## Using templates
|
||||
|
||||
Syft lets you define custom output formats, using [Go templates](https://pkg.go.dev/text/template). Here's how it works:
|
||||
Syft lets you define custom output formats, using [Go templates](https://pkg.go.dev/text/template) relative to the Syft JSON output. Here's how it works:
|
||||
|
||||
- Define your format as a Go template, and save this template as a file.
|
||||
|
||||
@@ -319,9 +319,9 @@ Syft lets you define custom output formats, using [Go templates](https://pkg.go.
|
||||
|
||||
Here's what the `csv.tmpl` file might look like:
|
||||
```gotemplate
|
||||
"Package","Version Installed","Found by"
|
||||
{{- range .Artifacts}}
|
||||
"{{.Name}}","{{.Version}}","{{.FoundBy}}"
|
||||
"Package","Version Installed", "Found by"
|
||||
{{- range .artifacts}}
|
||||
"{{.name}}","{{.version}}","{{.foundBy}}"
|
||||
{{- end}}
|
||||
```
|
||||
|
||||
@@ -338,6 +338,9 @@ Syft also includes a vast array of utility templating functions from [sprig](htt
|
||||
|
||||
Lastly, Syft has custom templating functions defined in `./syft/format/template/encoder.go` to help parse the passed-in JSON structs.
|
||||
|
||||
> [!NOTE]
|
||||
> If you have templates being used before Syft v0.102.0 that are no longer working. This is because templating keys were relative to the internal go structs before this version whereas now the keys are relative to the Syft JSON output. To get the legacy behavior back you can set the `format.template.legacy` option to `true` in your configuration.
|
||||
|
||||
## Multiple outputs
|
||||
|
||||
Syft can also output _multiple_ files in differing formats by appending
|
||||
@@ -688,6 +691,24 @@ golang:
|
||||
# SYFT_GOLANG_NOPROXY env var
|
||||
no-proxy: ""
|
||||
|
||||
# the go main module version discovered from binaries built with the go compiler will
|
||||
# always show (devel) as the version. Use these options to control heuristics to guess
|
||||
# a more accurate version from the binary.
|
||||
main-module-version:
|
||||
|
||||
# look for LD flags that appear to be setting a version (e.g. -X main.version=1.0.0)
|
||||
# SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_LD_FLAGS env var
|
||||
from-ld-flags: true
|
||||
|
||||
# use the build settings (e.g. vcs.version & vcs.time) to craft a v0 pseudo version
|
||||
# (e.g. v0.0.0-20220308212642-53e6d0aaf6fb) when a more accurate version cannot be found otherwise.
|
||||
# SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_BUILD_SETTINGS env var
|
||||
from-build-settings: true
|
||||
|
||||
# search for semver-like strings in the binary contents.
|
||||
# SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_CONTENTS env var
|
||||
from-contents: true
|
||||
|
||||
java:
|
||||
maven-url: "https://repo1.maven.org/maven2"
|
||||
max-parent-recursive-depth: 5
|
||||
|
||||
@@ -48,7 +48,7 @@ func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig
|
||||
return nil
|
||||
},
|
||||
).
|
||||
WithPostRuns(func(state *clio.State, err error) {
|
||||
WithPostRuns(func(_ *clio.State, _ error) {
|
||||
stereoscope.Cleanup()
|
||||
})
|
||||
return clioCfg
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/syft/cmd/syft/internal/options"
|
||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
||||
"github.com/anchore/syft/internal"
|
||||
@@ -26,7 +27,6 @@ import (
|
||||
"github.com/anchore/syft/syft/format/spdxtagvalue"
|
||||
"github.com/anchore/syft/syft/format/syftjson"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -247,7 +247,11 @@ func predicateType(outputName string) string {
|
||||
}
|
||||
|
||||
func generateSBOMForAttestation(ctx context.Context, id clio.Identification, opts *options.Catalog, userInput string) (*sbom.SBOM, error) {
|
||||
src, err := getSource(opts, userInput, onlyContainerImages)
|
||||
if len(opts.From) > 1 || (len(opts.From) == 1 && opts.From[0] != stereoscope.RegistryTag) {
|
||||
return nil, fmt.Errorf("attest requires use of an OCI registry directly, one or more of the specified sources is unsupported: %v", opts.From)
|
||||
}
|
||||
|
||||
src, err := getSource(ctx, opts, userInput, stereoscope.RegistryTag)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -273,13 +277,6 @@ func generateSBOMForAttestation(ctx context.Context, id clio.Identification, opt
|
||||
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
|
||||
|
||||
@@ -46,7 +46,7 @@ func CatalogerList(app clio.Application) *cobra.Command {
|
||||
return app.SetupCommand(&cobra.Command{
|
||||
Use: "list [OPTIONS]",
|
||||
Short: "List available catalogers",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return runCatalogerList(opts)
|
||||
},
|
||||
}, opts)
|
||||
|
||||
@@ -47,7 +47,7 @@ func Convert(app clio.Application) *cobra.Command {
|
||||
}),
|
||||
Args: validateConvertArgs,
|
||||
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
restoreStdout := ui.CaptureStdoutToTraceLog()
|
||||
defer restoreStdout()
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/go-collections"
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/cmd/syft/internal/options"
|
||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
||||
@@ -24,6 +26,7 @@ import (
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -162,14 +165,23 @@ func validateArgs(cmd *cobra.Command, args []string, error string) error {
|
||||
return cobra.MaximumNArgs(1)(cmd, args)
|
||||
}
|
||||
|
||||
// nolint:funlen
|
||||
func runScan(ctx context.Context, id clio.Identification, opts *scanOptions, userInput string) error {
|
||||
writer, err := opts.SBOMWriter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := getSource(&opts.Catalog, userInput)
|
||||
sources := opts.From
|
||||
if len(sources) == 0 {
|
||||
// extract a scheme if it matches any provider tag; this is a holdover for compatibility, using the --from flag is recommended
|
||||
explicitSource, newUserInput := stereoscope.ExtractSchemeSource(userInput, allSourceProviderTags()...)
|
||||
if explicitSource != "" {
|
||||
sources = append(sources, explicitSource)
|
||||
userInput = newUserInput
|
||||
}
|
||||
}
|
||||
|
||||
src, err := getSource(ctx, &opts.Catalog, userInput, sources...)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -199,23 +211,21 @@ func runScan(ctx context.Context, id clio.Identification, opts *scanOptions, use
|
||||
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.Source.Image.DefaultPullSource,
|
||||
},
|
||||
)
|
||||
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
|
||||
}
|
||||
}
|
||||
func getSource(ctx context.Context, opts *options.Catalog, userInput string, sources ...string) (source.Source, error) {
|
||||
cfg := syft.DefaultGetSourceConfig().
|
||||
WithRegistryOptions(opts.Registry.ToOptions()).
|
||||
WithAlias(source.Alias{
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
}).
|
||||
WithExcludeConfig(source.ExcludeConfig{
|
||||
Paths: opts.Exclusions,
|
||||
}).
|
||||
WithBasePath(opts.Source.BasePath).
|
||||
WithSources(sources...).
|
||||
WithDefaultImagePullSource(opts.Source.Image.DefaultPullSource)
|
||||
|
||||
var err error
|
||||
var platform *image.Platform
|
||||
|
||||
if opts.Platform != "" {
|
||||
@@ -223,29 +233,22 @@ func getSource(opts *options.Catalog, userInput string, filters ...func(*source.
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid platform: %w", err)
|
||||
}
|
||||
cfg = cfg.WithPlatform(platform)
|
||||
}
|
||||
|
||||
hashers, err := file.Hashers(opts.Source.File.Digests...)
|
||||
if opts.Source.File.Digests != nil {
|
||||
hashers, err := file.Hashers(opts.Source.File.Digests...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid hash algorithm: %w", err)
|
||||
}
|
||||
cfg = cfg.WithDigestAlgorithms(hashers...)
|
||||
}
|
||||
|
||||
src, err := syft.GetSource(ctx, userInput, cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid hash algorithm: %w", err)
|
||||
return nil, fmt.Errorf("could not determine source: %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.Source.BasePath,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if userInput == "power-user" {
|
||||
bus.Notify("Note: the 'power-user' command has been removed.")
|
||||
@@ -445,3 +448,7 @@ func getHintPhrase(expErr task.ErrInvalidExpression) string {
|
||||
func trimOperation(x string) string {
|
||||
return strings.TrimLeft(x, "+-")
|
||||
}
|
||||
|
||||
func allSourceProviderTags() []string {
|
||||
return collections.TaggedValueSet[source.Provider]{}.Join(sourceproviders.All("", nil)...).Tags()
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ var latestAppVersionURL = struct {
|
||||
}
|
||||
|
||||
func applicationUpdateCheck(id clio.Identification, check *options.UpdateCheck) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
return func(_ *cobra.Command, _ []string) error {
|
||||
if check.CheckForAppUpdate {
|
||||
checkForApplicationUpdate(id)
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ type Catalog struct {
|
||||
|
||||
// configuration for the source (the subject being analyzed)
|
||||
Registry registryConfig `yaml:"registry" json:"registry" mapstructure:"registry"`
|
||||
From []string `yaml:"from" json:"from" mapstructure:"from"`
|
||||
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
|
||||
Source sourceConfig `yaml:"source" json:"source" mapstructure:"source"`
|
||||
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
||||
@@ -62,6 +63,7 @@ func DefaultCatalog() Catalog {
|
||||
Scope: source.SquashedScope.String(),
|
||||
Package: defaultPackageConfig(),
|
||||
LinuxKernel: defaultLinuxKernelConfig(),
|
||||
Golang: defaultGolangConfig(),
|
||||
File: defaultFileConfig(),
|
||||
Relationships: defaultRelationshipsConfig(),
|
||||
Source: defaultSourceConfig(),
|
||||
@@ -125,13 +127,19 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
||||
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
|
||||
}
|
||||
return pkgcataloging.Config{
|
||||
Binary: binary.DefaultCatalogerConfig(),
|
||||
Binary: binary.DefaultClassifierCatalogerConfig(),
|
||||
Golang: golang.DefaultCatalogerConfig().
|
||||
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
|
||||
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
|
||||
WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
|
||||
WithProxy(cfg.Golang.Proxy).
|
||||
WithNoProxy(cfg.Golang.NoProxy),
|
||||
WithNoProxy(cfg.Golang.NoProxy).
|
||||
WithMainModuleVersion(
|
||||
golang.DefaultMainModuleVersionConfig().
|
||||
WithFromContents(cfg.Golang.MainModuleVersion.FromContents).
|
||||
WithFromBuildSettings(cfg.Golang.MainModuleVersion.FromBuildSettings).
|
||||
WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags),
|
||||
),
|
||||
JavaScript: javascript.DefaultCatalogerConfig().
|
||||
WithSearchRemoteLicenses(cfg.JavaScript.SearchRemoteLicenses).
|
||||
WithNpmBaseURL(cfg.JavaScript.NpmBaseURL),
|
||||
@@ -156,6 +164,9 @@ func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
|
||||
flags.StringVarP(&cfg.Scope, "scope", "s",
|
||||
fmt.Sprintf("selection of layers to catalog, options=%v", validScopeValues))
|
||||
|
||||
flags.StringArrayVarP(&cfg.From, "from", "",
|
||||
"specify the source behavior to use (e.g. docker, registry, oci-dir, ...)")
|
||||
|
||||
flags.StringVarP(&cfg.Platform, "platform", "",
|
||||
"an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')")
|
||||
|
||||
@@ -213,6 +224,8 @@ func (cfg *Catalog) PostLoad() error {
|
||||
return out
|
||||
}
|
||||
|
||||
cfg.From = flatten(cfg.From)
|
||||
|
||||
cfg.Catalogers = flatten(cfg.Catalogers)
|
||||
cfg.DefaultCatalogers = flatten(cfg.DefaultCatalogers)
|
||||
cfg.SelectCatalogers = flatten(cfg.SelectCatalogers)
|
||||
|
||||
@@ -3,6 +3,7 @@ package options
|
||||
import (
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/format/spdxtagvalue"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
@@ -41,9 +42,10 @@ func (o Format) Encoders() ([]sbom.FormatEncoder, error) {
|
||||
return format.EncodersConfig{
|
||||
Template: o.Template.config(),
|
||||
SyftJSON: o.SyftJSON.config(),
|
||||
SPDXJSON: o.SPDXJSON.config(format.AllVersions), // we support multiple versions, not just a single version
|
||||
CyclonedxJSON: o.CyclonedxJSON.config(format.AllVersions), // we support multiple versions, not just a single version
|
||||
CyclonedxXML: o.CyclonedxXML.config(format.AllVersions), // we support multiple versions, not just a single version
|
||||
SPDXJSON: o.SPDXJSON.config(format.AllVersions), // we support multiple versions, not just a single version
|
||||
SPDXTagValue: spdxtagvalue.EncoderConfig{Version: format.AllVersions}, // we support multiple versions, not just a single version
|
||||
CyclonedxJSON: o.CyclonedxJSON.config(format.AllVersions), // we support multiple versions, not just a single version
|
||||
CyclonedxXML: o.CyclonedxXML.config(format.AllVersions), // we support multiple versions, not just a single version
|
||||
}.Encoders()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,38 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||
)
|
||||
|
||||
type golangConfig struct {
|
||||
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
||||
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
|
||||
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
|
||||
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
|
||||
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
||||
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
|
||||
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
|
||||
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
|
||||
MainModuleVersion golangMainModuleVersionConfig `json:"main-module-version" yaml:"main-module-version" mapstructure:"main-module-version"`
|
||||
}
|
||||
|
||||
type golangMainModuleVersionConfig struct {
|
||||
FromLDFlags bool `json:"from-ld-flags" yaml:"from-ld-flags" mapstructure:"from-ld-flags"`
|
||||
FromContents bool `json:"from-contents" yaml:"from-contents" mapstructure:"from-contents"`
|
||||
FromBuildSettings bool `json:"from-build-settings" yaml:"from-build-settings" mapstructure:"from-build-settings"`
|
||||
}
|
||||
|
||||
func defaultGolangConfig() golangConfig {
|
||||
def := golang.DefaultCatalogerConfig()
|
||||
return golangConfig{
|
||||
SearchLocalModCacheLicenses: def.SearchLocalModCacheLicenses,
|
||||
LocalModCacheDir: def.LocalModCacheDir,
|
||||
SearchRemoteLicenses: def.SearchRemoteLicenses,
|
||||
Proxy: strings.Join(def.Proxies, ","),
|
||||
NoProxy: strings.Join(def.NoProxy, ","),
|
||||
MainModuleVersion: golangMainModuleVersionConfig{
|
||||
FromLDFlags: def.MainModuleVersion.FromLDFlags,
|
||||
FromContents: def.MainModuleVersion.FromContents,
|
||||
FromBuildSettings: def.MainModuleVersion.FromBuildSettings,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
type sourceConfig struct {
|
||||
@@ -25,12 +27,13 @@ type imageSource struct {
|
||||
}
|
||||
|
||||
func defaultSourceConfig() sourceConfig {
|
||||
var digests []string
|
||||
for _, alg := range sourceproviders.DefaultConfig().DigestAlgorithms {
|
||||
digests = append(digests, alg.String())
|
||||
}
|
||||
return sourceConfig{
|
||||
File: fileSource{
|
||||
Digests: []string{"sha256"},
|
||||
},
|
||||
Image: imageSource{
|
||||
DefaultPullSource: "",
|
||||
Digests: digests,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,4 +452,12 @@ var commonTestCases = []testCase{
|
||||
"glibc": "2.34-210",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find wordpress plugins",
|
||||
pkgType: pkg.WordpressPluginPkg,
|
||||
pkgLanguage: pkg.PHP,
|
||||
pkgInfo: map[string]string{
|
||||
"Akismet Anti-spam: Spam Protection": "5.3",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -22,15 +22,11 @@ func BenchmarkImagePackageCatalogers(b *testing.B) {
|
||||
tarPath := imagetest.GetFixtureImageTarPath(b, fixtureImageName)
|
||||
|
||||
// get the source object for the image
|
||||
userInput := "docker-archive:" + tarPath
|
||||
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
|
||||
require.NoError(b, err)
|
||||
|
||||
theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig())
|
||||
theSource, err := syft.GetSource(context.Background(), tarPath, syft.DefaultGetSourceConfig().WithSources("docker-archive"))
|
||||
require.NoError(b, err)
|
||||
|
||||
b.Cleanup(func() {
|
||||
theSource.Close()
|
||||
require.NoError(b, theSource.Close())
|
||||
})
|
||||
|
||||
// build the SBOM
|
||||
@@ -123,7 +119,6 @@ func TestPkgCoverageImage(t *testing.T) {
|
||||
}
|
||||
t.Fatalf("unexpected package count: %d!=%d", pkgCount, len(c.pkgInfo))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -211,7 +206,6 @@ func TestPkgCoverageDirectory(t *testing.T) {
|
||||
}
|
||||
t.Fatalf("unexpected package count: %d!=%d", actualPkgCount, len(test.pkgInfo))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -249,7 +243,6 @@ func TestPkgCoverageImage_HasEvidence(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
for a := range sbom.Artifacts.Packages.Enumerate(c.pkgType) {
|
||||
assert.NotEmpty(t, a.Locations.ToSlice(), "package %q has no locations (type=%q)", a.Name, a.Type)
|
||||
for _, l := range a.Locations.ToSlice() {
|
||||
@@ -259,7 +252,6 @@ func TestPkgCoverageImage_HasEvidence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -279,7 +271,6 @@ func TestPkgCoverageDirectory_HasEvidence(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
for a := range sbom.Artifacts.Packages.Enumerate(c.pkgType) {
|
||||
assert.NotEmpty(t, a.Locations.ToSlice(), "package %q has no locations (type=%q)", a.Name, a.Type)
|
||||
for _, l := range a.Locations.ToSlice() {
|
||||
@@ -289,7 +280,6 @@ func TestPkgCoverageDirectory_HasEvidence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ func Test_packageCatalogerExports(t *testing.T) {
|
||||
for pkg, expected := range expectAtLeast {
|
||||
actual, ok := exports[pkg]
|
||||
require.True(t, ok, pkg)
|
||||
require.True(t, expected.IsSubset(actual.Names()), pkg)
|
||||
if !assert.True(t, actual.Names().IsSubset(expected), pkg) {
|
||||
t.Logf("missing: %s", strset.SymmetricDifference(expected, actual.Names()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Akismet
|
||||
*/
|
||||
/*
|
||||
Plugin Name:Akismet Anti-spam: Spam Protection
|
||||
Plugin URI: https://akismet.com/
|
||||
Description: Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from spam</strong>. Akismet Anti-spam keeps your site protected even while you sleep. To get started: activate the Akismet plugin and then go to your Akismet Settings page to set up your API key.
|
||||
Version: 5.3
|
||||
Requires at least: 5.8
|
||||
Requires PHP: 5.6.20
|
||||
Author: Automattic - Anti-spam Team
|
||||
Author URI: https://automattic.com/wordpress-plugins/
|
||||
License: GPLv2 or later
|
||||
Text Domain: akismet
|
||||
*/
|
||||
// rest of plugin's code ...
|
||||
@@ -34,17 +34,13 @@ func catalogFixtureImageWithConfig(t *testing.T, fixtureImageName string, cfg *s
|
||||
// get the fixture image tar file
|
||||
imagetest.GetFixtureImage(t, "docker-archive", fixtureImageName)
|
||||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||
userInput := "docker-archive:" + tarPath
|
||||
|
||||
// get the source to build an SBOM against
|
||||
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig())
|
||||
theSource, err := syft.GetSource(context.Background(), tarPath, syft.DefaultGetSourceConfig().WithSources("docker-archive"))
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
theSource.Close()
|
||||
require.NoError(t, theSource.Close())
|
||||
})
|
||||
|
||||
s, err := syft.CreateSBOM(context.Background(), theSource, cfg)
|
||||
@@ -71,14 +67,10 @@ func catalogDirectoryWithConfig(t *testing.T, dir string, cfg *syft.CreateSBOMCo
|
||||
cfg.CatalogerSelection = cfg.CatalogerSelection.WithDefaults(pkgcataloging.DirectoryTag)
|
||||
|
||||
// get the source to build an sbom against
|
||||
userInput := "dir:" + dir
|
||||
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig())
|
||||
theSource, err := syft.GetSource(context.Background(), dir, syft.DefaultGetSourceConfig().WithSources("dir"))
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
theSource.Close()
|
||||
require.NoError(t, theSource.Close())
|
||||
})
|
||||
|
||||
// build the SBOM
|
||||
|
||||
@@ -44,17 +44,7 @@ func imageReference() string {
|
||||
func getSource(input string) source.Source {
|
||||
fmt.Println("detecting source type for input:", input, "...")
|
||||
|
||||
detection, err := source.Detect(input,
|
||||
source.DetectConfig{
|
||||
DefaultImageSource: "docker",
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
src, err := detection.NewSource(source.DefaultDetectionSourceConfig())
|
||||
src, err := syft.GetSource(context.Background(), input, nil)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -37,17 +37,7 @@ func imageReference() string {
|
||||
}
|
||||
|
||||
func getSource(input string) source.Source {
|
||||
detection, err := source.Detect(input,
|
||||
source.DetectConfig{
|
||||
DefaultImageSource: "docker",
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
src, err := detection.NewSource(source.DefaultDetectionSourceConfig())
|
||||
src, err := syft.GetSource(context.Background(), input, nil)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const defaultImage = "alpine:3.19"
|
||||
|
||||
func main() {
|
||||
// automagically get a source.Source for arbitrary string input
|
||||
src := getSource(imageReference())
|
||||
|
||||
// catalog the given source and return a SBOM
|
||||
// let's explicitly use catalogers that are:
|
||||
// - for installed software
|
||||
// - used in the directory scan
|
||||
sbom := getSBOM(src, pkgcataloging.InstalledTag, pkgcataloging.DirectoryTag)
|
||||
|
||||
// Show a basic catalogers and input configuration used
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(sbom.Descriptor.Configuration); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func imageReference() string {
|
||||
// read an image string reference from the command line or use a default
|
||||
if len(os.Args) > 1 {
|
||||
return os.Args[1]
|
||||
}
|
||||
return defaultImage
|
||||
}
|
||||
|
||||
func getSource(input string) source.Source {
|
||||
src, err := syft.GetSource(context.Background(), input, nil)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return src
|
||||
}
|
||||
|
||||
func getSBOM(src source.Source, defaultTags ...string) sbom.SBOM {
|
||||
cfg := syft.DefaultCreateSBOMConfig().
|
||||
WithCatalogerSelection(
|
||||
// here you can sub-select, add, remove catalogers from the default selection...
|
||||
// or replace the default selection entirely!
|
||||
pkgcataloging.NewSelectionRequest().
|
||||
WithDefaults(defaultTags...),
|
||||
)
|
||||
|
||||
s, err := syft.CreateSBOM(context.Background(), src, cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return *s
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/go-collections"
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -28,18 +33,18 @@ import (
|
||||
const defaultImage = "alpine:3.19"
|
||||
|
||||
func main() {
|
||||
detection, err := source.Detect(
|
||||
imageReference(),
|
||||
source.DetectConfig{
|
||||
DefaultImageSource: "docker",
|
||||
},
|
||||
)
|
||||
userInput := imageReference()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
// parse the scheme against the known set of schemes
|
||||
schemeSource, newUserInput := stereoscope.ExtractSchemeSource(userInput, allSourceTags()...)
|
||||
|
||||
// set up the GetSourceConfig
|
||||
getSourceCfg := syft.DefaultGetSourceConfig()
|
||||
if schemeSource != "" {
|
||||
getSourceCfg = getSourceCfg.WithSources(schemeSource)
|
||||
userInput = newUserInput
|
||||
}
|
||||
|
||||
src, err := detection.NewSource(source.DefaultDetectionSourceConfig())
|
||||
src, err := syft.GetSource(context.Background(), userInput, getSourceCfg)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -60,3 +65,7 @@ func imageReference() string {
|
||||
}
|
||||
return defaultImage
|
||||
}
|
||||
|
||||
func allSourceTags() []string {
|
||||
return collections.TaggedValueSet[source.Provider]{}.Join(sourceproviders.All("", nil)...).Tags()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/image/oci"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -16,22 +18,15 @@ import (
|
||||
const defaultImage = "alpine:3.19"
|
||||
|
||||
func main() {
|
||||
platform, err := image.NewPlatform("linux/amd64")
|
||||
// using oci.Registry causes the lookup to always use the registry, there are several other "Source" options here
|
||||
img, err := stereoscope.GetImageFromSource(context.Background(), imageReference(), oci.Registry, stereoscope.WithPlatform("linux/amd64"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
src, err := source.NewFromStereoscopeImage(
|
||||
source.StereoscopeImageConfig{
|
||||
Reference: imageReference(),
|
||||
From: image.OciRegistrySource, // always use the registry, there are several other "Source" options here
|
||||
Platform: platform,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: imageReference(),
|
||||
})
|
||||
|
||||
// Show a basic description of the source to the screen
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
|
||||
@@ -9,14 +9,15 @@ require (
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/acobaugh/osrelease v0.1.0
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9
|
||||
github.com/anchore/clio v0.0.0-20240131202212-9eba61247448
|
||||
github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65
|
||||
github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537
|
||||
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
|
||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20240202171727-877e1747d426
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240202153536-bfa15e446f06
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240221144950-cf0e754f5b56
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
// we are hinting brotli to latest due to warning when installing archiver v3:
|
||||
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
|
||||
@@ -53,10 +54,11 @@ require (
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/moby/sys/mountinfo v0.7.1
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/saferwall/pe v1.4.8
|
||||
github.com/saferwall/pe v1.5.2
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
|
||||
github.com/sanity-io/litter v1.5.5
|
||||
github.com/sassoftware/go-rpmutils v0.3.0
|
||||
@@ -75,16 +77,15 @@ require (
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
||||
go.uber.org/goleak v1.3.0
|
||||
golang.org/x/mod v0.15.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/net v0.21.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.28.0
|
||||
modernc.org/sqlite v1.29.2
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
@@ -136,13 +137,13 @@ require (
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
@@ -160,13 +161,13 @@ require (
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
|
||||
github.com/opencontainers/runc v1.1.12 // indirect
|
||||
@@ -215,13 +216,13 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.19.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.18.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.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/term v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/tools v0.17.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
|
||||
@@ -229,15 +230,12 @@ require (
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.29.0 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.41.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
||||
retract (
|
||||
|
||||
@@ -52,8 +52,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
@@ -93,10 +93,12 @@ github.com/anchore/archiver/v3 v3.5.2 h1:Bjemm2NzuRhmHy3m0lRe5tNoClB9A4zYyDV58Pa
|
||||
github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU=
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw=
|
||||
github.com/anchore/clio v0.0.0-20240131202212-9eba61247448 h1:ZgecmkxhH5im+9jPs7Ra1Thmv/p4IBDsoCFD6W8pENg=
|
||||
github.com/anchore/clio v0.0.0-20240131202212-9eba61247448/go.mod h1:t5Mld8naKcG8RTPjW/2n7bfyBKFl1A6PvtXw+v64gK0=
|
||||
github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65 h1:u9XrEabKlGPsrmRvAER+kUKkwXiJfLyqGhmOTFsXjX4=
|
||||
github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65/go.mod h1:8Jr7CjmwFVcBPtkJdTpaAGHimoGJGfbExypjzOu87Og=
|
||||
github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b h1:L/djgY7ZbZ/38+wUtdkk398W3PIBJLkt1N8nU/7e47A=
|
||||
github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b/go.mod h1:TLcE0RE5+8oIx2/NPWem/dq1DeaMoC+fPEH7hoSzPLo=
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q=
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
|
||||
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a h1:nJ2G8zWKASyVClGVgG7sfM5mwoZlZ2zYpIzN2OhjWkw=
|
||||
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg=
|
||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU=
|
||||
@@ -109,8 +111,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20240202171727-877e1747d426 h1:agoiZchSf1Nnnos1azwIg5hk5Ao9TzZNBD9++AChGEg=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20240202171727-877e1747d426/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240202153536-bfa15e446f06 h1:3NAS33Bqrw87wckTff6/yXYDL1h3Wm6OiqaR1kcvW10=
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240202153536-bfa15e446f06/go.mod h1:uydT2ful8TY7Hr1WH1V1ZecSq/2TqXpAsGkMiy7lxD0=
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240221144950-cf0e754f5b56 h1:iHvTXZA+qEozPGRRuW1Mv7r7w2fHeJdzWDx+YsSIbyg=
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240221144950-cf0e754f5b56/go.mod h1:evQiJMQG56Z7/L5uhA8kfhhjF6ESJUZzUH9ms6bQ2Co=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
@@ -438,6 +440,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
@@ -480,8 +484,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 h1:WdAeg/imY2JFPc/9CST4bZ80nNJbiBFCAdSZCSgrS5Y=
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953/go.mod h1:6o+UrvuZWc4UTyBhQf0LGjW9Ld7qJxLz/OqvSOWWlEc=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@@ -569,8 +571,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
||||
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
|
||||
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
||||
@@ -593,6 +595,8 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
@@ -666,8 +670,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/saferwall/pe v1.4.8 h1:ey/L8FGBMrJ1Xh+Rltj1MAFPZ4LOQYGJqNa5B1Na6B0=
|
||||
github.com/saferwall/pe v1.4.8/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s=
|
||||
github.com/saferwall/pe v1.5.2 h1:h5lLtLsyxGHQ9dN6cd8EfeLEBEo5gdqJpkuw4o4vTMY=
|
||||
github.com/saferwall/pe v1.5.2/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
@@ -857,8 +861,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
||||
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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
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=
|
||||
@@ -869,8 +873,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -948,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.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
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=
|
||||
@@ -980,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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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=
|
||||
@@ -1061,15 +1065,15 @@ 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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
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=
|
||||
@@ -1145,8 +1149,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1339,34 +1343,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs=
|
||||
modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
|
||||
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||
modernc.org/sqlite v1.29.2 h1:xgBSyA3gemwgP31PWFfFjtBorQNYpeypGdoSDjXhrgI=
|
||||
modernc.org/sqlite v1.29.2/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
+2
-1
@@ -694,6 +694,7 @@ main() (
|
||||
set +u
|
||||
if [ -z "${TEST_INSTALL_SH}" ]; then
|
||||
set -u
|
||||
exit $(main "$@")
|
||||
main "$@"
|
||||
exit $?
|
||||
fi
|
||||
set -u
|
||||
@@ -3,5 +3,5 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.0.3"
|
||||
JSONSchemaVersion = "16.0.4"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at 2023-10-05 22:45:16.827263 -0400 EDT m=+0.138031521
|
||||
// This file was generated by robots at 2024-02-09 10:57:33.980847 -0500 EST m=+0.165923154
|
||||
// using data from https://spdx.org/licenses/licenses.json
|
||||
package spdxlicense
|
||||
|
||||
const Version = "3.22"
|
||||
const Version = "3.23"
|
||||
|
||||
var licenseIDs = map[string]string{
|
||||
"0bsd": "0BSD",
|
||||
@@ -13,6 +13,7 @@ var licenseIDs = map[string]string{
|
||||
"adobe2006": "Adobe-2006",
|
||||
"adobe2006.0": "Adobe-2006",
|
||||
"adobe2006.0.0": "Adobe-2006",
|
||||
"adobedisplaypostscript": "Adobe-Display-PostScript",
|
||||
"adobeglyph": "Adobe-Glyph",
|
||||
"adobeutopia": "Adobe-Utopia",
|
||||
"adsl": "ADSL",
|
||||
@@ -57,6 +58,7 @@ var licenseIDs = map[string]string{
|
||||
"aladdin": "Aladdin",
|
||||
"amdplpa": "AMDPLPA",
|
||||
"aml": "AML",
|
||||
"amlglslang": "AML-glslang",
|
||||
"ampas": "AMPAS",
|
||||
"antlrpd": "ANTLR-PD",
|
||||
"antlrpdfallback": "ANTLR-PD-fallback",
|
||||
@@ -108,6 +110,7 @@ var licenseIDs = map[string]string{
|
||||
"baekmuk": "Baekmuk",
|
||||
"bahyph": "Bahyph",
|
||||
"barr": "Barr",
|
||||
"bcryptsolardesigner": "bcrypt-Solar-Designer",
|
||||
"beerware": "Beerware",
|
||||
"bitstreamcharter": "Bitstream-Charter",
|
||||
"bitstreamvera": "Bitstream-Vera",
|
||||
@@ -122,6 +125,9 @@ var licenseIDs = map[string]string{
|
||||
"blueoak1.0.0": "BlueOak-1.0.0",
|
||||
"boehmgc": "Boehm-GC",
|
||||
"borceux": "Borceux",
|
||||
"briangladman2.0.0clause": "Brian-Gladman-2-Clause",
|
||||
"briangladman2.0clause": "Brian-Gladman-2-Clause",
|
||||
"briangladman2clause": "Brian-Gladman-2-Clause",
|
||||
"briangladman3.0.0clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3.0clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3clause": "Brian-Gladman-3-Clause",
|
||||
@@ -129,21 +135,25 @@ var licenseIDs = map[string]string{
|
||||
"bsd1.0clause": "BSD-1-Clause",
|
||||
"bsd1clause": "BSD-1-Clause",
|
||||
"bsd2.0.0clause": "BSD-2-Clause",
|
||||
"bsd2.0.0clausedarwin": "BSD-2-Clause-Darwin",
|
||||
"bsd2.0.0clausefreebsd": "BSD-2-Clause-Views",
|
||||
"bsd2.0.0clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2.0.0clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2.0.0clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd2.0clause": "BSD-2-Clause",
|
||||
"bsd2.0clausedarwin": "BSD-2-Clause-Darwin",
|
||||
"bsd2.0clausefreebsd": "BSD-2-Clause-Views",
|
||||
"bsd2.0clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2.0clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2.0clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd2clause": "BSD-2-Clause",
|
||||
"bsd2clausedarwin": "BSD-2-Clause-Darwin",
|
||||
"bsd2clausefreebsd": "BSD-2-Clause-Views",
|
||||
"bsd2clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd3.0.0clause": "BSD-3-Clause",
|
||||
"bsd3.0.0clauseacpica": "BSD-3-Clause-acpica",
|
||||
"bsd3.0.0clauseattribution": "BSD-3-Clause-Attribution",
|
||||
"bsd3.0.0clauseclear": "BSD-3-Clause-Clear",
|
||||
"bsd3.0.0clauseflex": "BSD-3-Clause-flex",
|
||||
@@ -157,6 +167,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3.0.0clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3.0.0clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3.0clause": "BSD-3-Clause",
|
||||
"bsd3.0clauseacpica": "BSD-3-Clause-acpica",
|
||||
"bsd3.0clauseattribution": "BSD-3-Clause-Attribution",
|
||||
"bsd3.0clauseclear": "BSD-3-Clause-Clear",
|
||||
"bsd3.0clauseflex": "BSD-3-Clause-flex",
|
||||
@@ -170,6 +181,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3.0clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3.0clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3clause": "BSD-3-Clause",
|
||||
"bsd3clauseacpica": "BSD-3-Clause-acpica",
|
||||
"bsd3clauseattribution": "BSD-3-Clause-Attribution",
|
||||
"bsd3clauseclear": "BSD-3-Clause-Clear",
|
||||
"bsd3clauseflex": "BSD-3-Clause-flex",
|
||||
@@ -201,8 +213,12 @@ var licenseIDs = map[string]string{
|
||||
"bsdattributionhpnddisclaimer": "BSD-Attribution-HPND-disclaimer",
|
||||
"bsdinfernonettverk": "BSD-Inferno-Nettverk",
|
||||
"bsdprotection": "BSD-Protection",
|
||||
"bsdsourcebeginningfile": "BSD-Source-beginning-file",
|
||||
"bsdsourcecode": "BSD-Source-Code",
|
||||
"bsdsystemics": "BSD-Systemics",
|
||||
"bsdsystemicsw3.0.0works": "BSD-Systemics-W3Works",
|
||||
"bsdsystemicsw3.0works": "BSD-Systemics-W3Works",
|
||||
"bsdsystemicsw3works": "BSD-Systemics-W3Works",
|
||||
"bsl1": "BSL-1.0",
|
||||
"bsl1.0": "BSL-1.0",
|
||||
"bsl1.0.0": "BSL-1.0",
|
||||
@@ -220,6 +236,7 @@ var licenseIDs = map[string]string{
|
||||
"cal1.0combinedworkexception": "CAL-1.0-Combined-Work-Exception",
|
||||
"cal1combinedworkexception": "CAL-1.0-Combined-Work-Exception",
|
||||
"caldera": "Caldera",
|
||||
"calderanopreamble": "Caldera-no-preamble",
|
||||
"catosl1": "CATOSL-1.1",
|
||||
"catosl1.1": "CATOSL-1.1",
|
||||
"catosl1.1.0": "CATOSL-1.1",
|
||||
@@ -241,16 +258,19 @@ var licenseIDs = map[string]string{
|
||||
"ccby3.0": "CC-BY-3.0",
|
||||
"ccby3.0.0": "CC-BY-3.0",
|
||||
"ccby3.0.0at": "CC-BY-3.0-AT",
|
||||
"ccby3.0.0au": "CC-BY-3.0-AU",
|
||||
"ccby3.0.0de": "CC-BY-3.0-DE",
|
||||
"ccby3.0.0igo": "CC-BY-3.0-IGO",
|
||||
"ccby3.0.0nl": "CC-BY-3.0-NL",
|
||||
"ccby3.0.0us": "CC-BY-3.0-US",
|
||||
"ccby3.0at": "CC-BY-3.0-AT",
|
||||
"ccby3.0au": "CC-BY-3.0-AU",
|
||||
"ccby3.0de": "CC-BY-3.0-DE",
|
||||
"ccby3.0igo": "CC-BY-3.0-IGO",
|
||||
"ccby3.0nl": "CC-BY-3.0-NL",
|
||||
"ccby3.0us": "CC-BY-3.0-US",
|
||||
"ccby3at": "CC-BY-3.0-AT",
|
||||
"ccby3au": "CC-BY-3.0-AU",
|
||||
"ccby3de": "CC-BY-3.0-DE",
|
||||
"ccby3igo": "CC-BY-3.0-IGO",
|
||||
"ccby3nl": "CC-BY-3.0-NL",
|
||||
@@ -420,6 +440,7 @@ var licenseIDs = map[string]string{
|
||||
"clartistic": "ClArtistic",
|
||||
"clips": "Clips",
|
||||
"cmumach": "CMU-Mach",
|
||||
"cmumachnodoc": "CMU-Mach-nodoc",
|
||||
"cnrijython": "CNRI-Jython",
|
||||
"cnripython": "CNRI-Python",
|
||||
"cnripythongplcompatible": "CNRI-Python-GPL-Compatible",
|
||||
@@ -456,6 +477,9 @@ var licenseIDs = map[string]string{
|
||||
"cuda1.0": "C-UDA-1.0",
|
||||
"cuda1.0.0": "C-UDA-1.0",
|
||||
"curl": "curl",
|
||||
"dec3.0.0clause": "DEC-3-Clause",
|
||||
"dec3.0clause": "DEC-3-Clause",
|
||||
"dec3clause": "DEC-3-Clause",
|
||||
"dfsl1": "D-FSL-1.0",
|
||||
"dfsl1.0": "D-FSL-1.0",
|
||||
"dfsl1.0.0": "D-FSL-1.0",
|
||||
@@ -471,6 +495,8 @@ var licenseIDs = map[string]string{
|
||||
"drl1": "DRL-1.0",
|
||||
"drl1.0": "DRL-1.0",
|
||||
"drl1.0.0": "DRL-1.0",
|
||||
"drl1.1": "DRL-1.1",
|
||||
"drl1.1.0": "DRL-1.1",
|
||||
"dsdp": "DSDP",
|
||||
"dtoa": "dtoa",
|
||||
"dvipdfm": "dvipdfm",
|
||||
@@ -526,12 +552,14 @@ var licenseIDs = map[string]string{
|
||||
"freebsddoc": "FreeBSD-DOC",
|
||||
"freeimage": "FreeImage",
|
||||
"fsfap": "FSFAP",
|
||||
"fsfapnowarrantydisclaimer": "FSFAP-no-warranty-disclaimer",
|
||||
"fsful": "FSFUL",
|
||||
"fsfullr": "FSFULLR",
|
||||
"fsfullrwd": "FSFULLRWD",
|
||||
"ftl": "FTL",
|
||||
"furuseth": "Furuseth",
|
||||
"fwlw": "fwlw",
|
||||
"gcrdocs": "GCR-docs",
|
||||
"gd": "GD",
|
||||
"gfdl1": "GFDL-1.1-only",
|
||||
"gfdl1+": "GFDL-1.1-or-later",
|
||||
@@ -672,7 +700,9 @@ var licenseIDs = map[string]string{
|
||||
"gsoap1.3.0b": "gSOAP-1.3b",
|
||||
"gsoap1.3b": "gSOAP-1.3b",
|
||||
"gsoap1b": "gSOAP-1.3b",
|
||||
"gtkbook": "gtkbook",
|
||||
"haskellreport": "HaskellReport",
|
||||
"hdparm": "hdparm",
|
||||
"hippocratic2": "Hippocratic-2.1",
|
||||
"hippocratic2.1": "Hippocratic-2.1",
|
||||
"hippocratic2.1.0": "Hippocratic-2.1",
|
||||
@@ -688,8 +718,13 @@ var licenseIDs = map[string]string{
|
||||
"hpnddocsell": "HPND-doc-sell",
|
||||
"hpndexportus": "HPND-export-US",
|
||||
"hpndexportusmodify": "HPND-export-US-modify",
|
||||
"hpndfenneberglivingston": "HPND-Fenneberg-Livingston",
|
||||
"hpndinriaimag": "HPND-INRIA-IMAG",
|
||||
"hpndkevlinhenney": "HPND-Kevlin-Henney",
|
||||
"hpndmarkuskuhn": "HPND-Markus-Kuhn",
|
||||
"hpndmitdisclaimer": "HPND-MIT-disclaimer",
|
||||
"hpndpbmplus": "HPND-Pbmplus",
|
||||
"hpndsellmitdisclaimerxserver": "HPND-sell-MIT-disclaimer-xserver",
|
||||
"hpndsellregexpr": "HPND-sell-regexpr",
|
||||
"hpndsellvariant": "HPND-sell-variant",
|
||||
"hpndsellvariantmitdisclaimer": "HPND-sell-variant-MIT-disclaimer",
|
||||
@@ -719,6 +754,7 @@ var licenseIDs = map[string]string{
|
||||
"ipl1.0": "IPL-1.0",
|
||||
"ipl1.0.0": "IPL-1.0",
|
||||
"isc": "ISC",
|
||||
"iscveillard": "ISC-Veillard",
|
||||
"jam": "Jam",
|
||||
"jasper2": "JasPer-2.0",
|
||||
"jasper2.0": "JasPer-2.0",
|
||||
@@ -802,6 +838,7 @@ var licenseIDs = map[string]string{
|
||||
"linuxmanpagescopyleftvar": "Linux-man-pages-copyleft-var",
|
||||
"linuxopenib": "Linux-OpenIB",
|
||||
"loop": "LOOP",
|
||||
"lpddocument": "LPD-document",
|
||||
"lpl1": "LPL-1.0",
|
||||
"lpl1.0": "LPL-1.0",
|
||||
"lpl1.0.0": "LPL-1.0",
|
||||
@@ -828,7 +865,14 @@ var licenseIDs = map[string]string{
|
||||
"lzmasdk9.22": "LZMA-SDK-9.22",
|
||||
"lzmasdk9.22.0": "LZMA-SDK-9.22",
|
||||
"lzmasdk9to9.20": "LZMA-SDK-9.11-to-9.20",
|
||||
"mackerras3.0.0clause": "Mackerras-3-Clause",
|
||||
"mackerras3.0.0clauseacknowledgment": "Mackerras-3-Clause-acknowledgment",
|
||||
"mackerras3.0clause": "Mackerras-3-Clause",
|
||||
"mackerras3.0clauseacknowledgment": "Mackerras-3-Clause-acknowledgment",
|
||||
"mackerras3clause": "Mackerras-3-Clause",
|
||||
"mackerras3clauseacknowledgment": "Mackerras-3-Clause-acknowledgment",
|
||||
"magaz": "magaz",
|
||||
"mailprio": "mailprio",
|
||||
"makeindex": "MakeIndex",
|
||||
"martinbirgmeier": "Martin-Birgmeier",
|
||||
"mcpheeslideshow": "McPhee-slideshow",
|
||||
@@ -1008,6 +1052,8 @@ var licenseIDs = map[string]string{
|
||||
"openpbs2.3": "OpenPBS-2.3",
|
||||
"openpbs2.3.0": "OpenPBS-2.3",
|
||||
"openssl": "OpenSSL",
|
||||
"opensslstandalone": "OpenSSL-standalone",
|
||||
"openvision": "OpenVision",
|
||||
"opl1": "OPL-1.0",
|
||||
"opl1.0": "OPL-1.0",
|
||||
"opl1.0.0": "OPL-1.0",
|
||||
@@ -1051,6 +1097,7 @@ var licenseIDs = map[string]string{
|
||||
"php3.0.0": "PHP-3.0",
|
||||
"php3.01": "PHP-3.01",
|
||||
"php3.01.0": "PHP-3.01",
|
||||
"pixar": "Pixar",
|
||||
"plexus": "Plexus",
|
||||
"pnmstitch": "pnmstitch",
|
||||
"polyformnoncommercial1": "PolyForm-Noncommercial-1.0.0",
|
||||
@@ -1077,6 +1124,7 @@ var licenseIDs = map[string]string{
|
||||
"qpl1.0.0inria2004": "QPL-1.0-INRIA-2004",
|
||||
"qpl1.0inria2004": "QPL-1.0-INRIA-2004",
|
||||
"qpl1inria2004": "QPL-1.0-INRIA-2004",
|
||||
"radvd": "radvd",
|
||||
"rdisc": "Rdisc",
|
||||
"rhecos1": "RHeCos-1.1",
|
||||
"rhecos1.1": "RHeCos-1.1",
|
||||
@@ -1094,6 +1142,9 @@ var licenseIDs = map[string]string{
|
||||
"ruby": "Ruby",
|
||||
"saxpath": "Saxpath",
|
||||
"saxpd": "SAX-PD",
|
||||
"saxpd2": "SAX-PD-2.0",
|
||||
"saxpd2.0": "SAX-PD-2.0",
|
||||
"saxpd2.0.0": "SAX-PD-2.0",
|
||||
"scea": "SCEA",
|
||||
"schemereport": "SchemeReport",
|
||||
"sendmail": "Sendmail",
|
||||
@@ -1129,6 +1180,7 @@ var licenseIDs = map[string]string{
|
||||
"smppl": "SMPPL",
|
||||
"snia": "SNIA",
|
||||
"snprintf": "snprintf",
|
||||
"softsurfer": "softSurfer",
|
||||
"soundex": "Soundex",
|
||||
"spencer86": "Spencer-86",
|
||||
"spencer86.0": "Spencer-86",
|
||||
@@ -1145,6 +1197,7 @@ var licenseIDs = map[string]string{
|
||||
"sshkeyscan": "ssh-keyscan",
|
||||
"sshopenssh": "SSH-OpenSSH",
|
||||
"sshshort": "SSH-short",
|
||||
"ssleaystandalone": "SSLeay-standalone",
|
||||
"sspl1": "SSPL-1.0",
|
||||
"sspl1.0": "SSPL-1.0",
|
||||
"sspl1.0.0": "SSPL-1.0",
|
||||
@@ -1152,6 +1205,7 @@ var licenseIDs = map[string]string{
|
||||
"sugarcrm1": "SugarCRM-1.1.3",
|
||||
"sugarcrm1.1": "SugarCRM-1.1.3",
|
||||
"sugarcrm1.1.3": "SugarCRM-1.1.3",
|
||||
"sunppp": "Sun-PPP",
|
||||
"sunpro": "SunPro",
|
||||
"swl": "SWL",
|
||||
"swrule": "swrule",
|
||||
@@ -1162,6 +1216,9 @@ var licenseIDs = map[string]string{
|
||||
"tcl": "TCL",
|
||||
"tcpwrappers": "TCP-wrappers",
|
||||
"termreadkey": "TermReadKey",
|
||||
"tgppl1": "TGPPL-1.0",
|
||||
"tgppl1.0": "TGPPL-1.0",
|
||||
"tgppl1.0.0": "TGPPL-1.0",
|
||||
"tmate": "TMate",
|
||||
"torque1": "TORQUE-1.1",
|
||||
"torque1.1": "TORQUE-1.1",
|
||||
@@ -1184,6 +1241,10 @@ var licenseIDs = map[string]string{
|
||||
"ucl1.0": "UCL-1.0",
|
||||
"ucl1.0.0": "UCL-1.0",
|
||||
"ulem": "ulem",
|
||||
"umichmerit": "UMich-Merit",
|
||||
"unicode3": "Unicode-3.0",
|
||||
"unicode3.0": "Unicode-3.0",
|
||||
"unicode3.0.0": "Unicode-3.0",
|
||||
"unicodedfs2015": "Unicode-DFS-2015",
|
||||
"unicodedfs2015.0": "Unicode-DFS-2015",
|
||||
"unicodedfs2015.0.0": "Unicode-DFS-2015",
|
||||
@@ -1236,6 +1297,7 @@ var licenseIDs = map[string]string{
|
||||
"xfree861.1": "XFree86-1.1",
|
||||
"xfree861.1.0": "XFree86-1.1",
|
||||
"xinetd": "xinetd",
|
||||
"xkeyboardconfigzinoviev": "xkeyboard-config-Zinoviev",
|
||||
"xlock": "xlock",
|
||||
"xnet": "Xnet",
|
||||
"xpp": "xpp",
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// TODO: add tui element here?
|
||||
|
||||
func NewEnvironmentTask() Task {
|
||||
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
release := linux.IdentifyRelease(resolver)
|
||||
if release != nil {
|
||||
builder.SetLinuxDistribution(*release)
|
||||
|
||||
@@ -108,7 +108,7 @@ func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config)
|
||||
|
||||
cat := executable.NewCataloger(cfg)
|
||||
|
||||
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
result, err := cat.Catalog(resolver)
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/rust"
|
||||
sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/swift"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/wordpress"
|
||||
)
|
||||
|
||||
//nolint:funlen
|
||||
@@ -112,7 +113,7 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
||||
// other package catalogers ///////////////////////////////////////////////////////////////////////////
|
||||
newPackageTaskFactory(
|
||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||
return binary.NewCataloger(cfg.PackagesConfig.Binary)
|
||||
return binary.NewClassifierCataloger(cfg.PackagesConfig.Binary)
|
||||
},
|
||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary",
|
||||
),
|
||||
@@ -125,5 +126,6 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "linux", "kernel",
|
||||
),
|
||||
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, "sbom"), // note: not evidence of installed packages
|
||||
newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func (s sourceIdentifierAdapter) ID() artifact.ID {
|
||||
}
|
||||
|
||||
func NewRelationshipsTask(cfg cataloging.RelationshipsConfig, src source.Description) Task {
|
||||
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
fn := func(_ context.Context, _ file.Resolver, builder sbomsync.Builder) error {
|
||||
relationship.Finalize(
|
||||
builder,
|
||||
cfg,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2242,6 +2242,12 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"cpes": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/CPE"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"licenses": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/License"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.3/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.4/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -1493,6 +1493,9 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/SwiftPackageManagerLockEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/WordpressPluginEntry"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2268,6 +2271,23 @@
|
||||
"revision"
|
||||
]
|
||||
},
|
||||
"WordpressPluginEntry": {
|
||||
"properties": {
|
||||
"pluginInstallDirectory": {
|
||||
"type": "string"
|
||||
},
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"authorUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"pluginInstallDirectory"
|
||||
]
|
||||
},
|
||||
"cpes": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/CPE"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Binary binary.CatalogerConfig `yaml:"binary" json:"binary" mapstructure:"binary"`
|
||||
Binary binary.ClassifierCatalogerConfig `yaml:"binary" json:"binary" mapstructure:"binary"`
|
||||
Golang golang.CatalogerConfig `yaml:"golang" json:"golang" mapstructure:"golang"`
|
||||
JavaArchive java.ArchiveCatalogerConfig `yaml:"java-archive" json:"java-archive" mapstructure:"java-archive"`
|
||||
JavaScript javascript.CatalogerConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
|
||||
@@ -20,7 +20,7 @@ type Config struct {
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Binary: binary.DefaultCatalogerConfig(),
|
||||
Binary: binary.DefaultClassifierCatalogerConfig(),
|
||||
Golang: golang.DefaultCatalogerConfig(),
|
||||
LinuxKernel: kernel.DefaultLinuxKernelCatalogerConfig(),
|
||||
Python: python.DefaultCatalogerConfig(),
|
||||
@@ -28,7 +28,7 @@ func DefaultConfig() Config {
|
||||
}
|
||||
}
|
||||
|
||||
func (c Config) WithBinaryConfig(cfg binary.CatalogerConfig) Config {
|
||||
func (c Config) WithBinaryConfig(cfg binary.ClassifierCatalogerConfig) Config {
|
||||
c.Binary = cfg
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/task"
|
||||
@@ -44,9 +45,34 @@ func DefaultCreateSBOMConfig() *CreateSBOMConfig {
|
||||
Files: filecataloging.DefaultConfig(),
|
||||
Parallelism: 1,
|
||||
packageTaskFactories: task.DefaultPackageTaskFactories(),
|
||||
|
||||
// library consumers are free to override the tool values to fit their needs, however, we have some sane defaults
|
||||
// to ensure that SBOMs generated don't have missing tool metadata.
|
||||
ToolName: "syft",
|
||||
ToolVersion: syftVersion(),
|
||||
}
|
||||
}
|
||||
|
||||
func syftVersion() string {
|
||||
// extract the syft version from the go module info from the current binary that is running. This is useful for
|
||||
// library consumers to at least encode the version of syft that was used to generate the SBOM. Note: we don't
|
||||
// use the version info from main because it's baked in with ldflags, which we don't control for library consumers.
|
||||
// This approach won't work in all cases though, such as when the binary is stripped of the buildinfo section.
|
||||
|
||||
buildInfo, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, d := range buildInfo.Deps {
|
||||
if d.Path == "github.com/anchore/syft" && d.Version != "(devel)" {
|
||||
return d.Version
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// WithTool allows for setting the specific name, version, and any additional configuration that is not captured
|
||||
// in the syft default API configuration. This could cover inputs for catalogers that were user-provided, thus,
|
||||
// is not visible to the syft API, but would be useful to see in the SBOM output.
|
||||
@@ -319,9 +345,9 @@ func (c *CreateSBOMConfig) Create(ctx context.Context, src source.Source) (*sbom
|
||||
|
||||
func findDefaultTag(src source.Description) (string, error) {
|
||||
switch m := src.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
case source.ImageMetadata:
|
||||
return pkgcataloging.ImageTag, nil
|
||||
case source.FileSourceMetadata, source.DirectorySourceMetadata:
|
||||
case source.FileMetadata, source.DirectoryMetadata:
|
||||
return pkgcataloging.DirectoryTag, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unable to determine default cataloger tag for source type=%T", m)
|
||||
|
||||
@@ -62,15 +62,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
}
|
||||
|
||||
imgSrc := source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{},
|
||||
Metadata: source.ImageMetadata{},
|
||||
}
|
||||
|
||||
dirSrc := source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{},
|
||||
Metadata: source.DirectoryMetadata{},
|
||||
}
|
||||
|
||||
fileSrc := source.Description{
|
||||
Metadata: source.FileSourceMetadata{},
|
||||
Metadata: source.FileMetadata{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@@ -437,21 +437,21 @@ func Test_findDefaultTag(t *testing.T) {
|
||||
{
|
||||
name: "image",
|
||||
src: source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{},
|
||||
Metadata: source.ImageMetadata{},
|
||||
},
|
||||
want: pkgcataloging.ImageTag,
|
||||
},
|
||||
{
|
||||
name: "directory",
|
||||
src: source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{},
|
||||
Metadata: source.DirectoryMetadata{},
|
||||
},
|
||||
want: pkgcataloging.DirectoryTag,
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
src: source.Description{
|
||||
Metadata: source.FileSourceMetadata{},
|
||||
Metadata: source.FileMetadata{},
|
||||
},
|
||||
want: pkgcataloging.DirectoryTag, // not a mistake...
|
||||
},
|
||||
|
||||
@@ -66,7 +66,15 @@ func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.E
|
||||
log.WithFields("error", err).Warnf("unable to get file contents for %q", loc.RealPath)
|
||||
continue
|
||||
}
|
||||
exec, err := processExecutable(loc, reader.(unionreader.UnionReader))
|
||||
|
||||
uReader, err := unionreader.GetUnionReader(reader)
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Warnf("unable to get union reader for %q", loc.RealPath)
|
||||
continue
|
||||
}
|
||||
|
||||
exec, err := processExecutable(loc, uReader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warnf("unable to process executable %q", loc.RealPath)
|
||||
}
|
||||
@@ -149,7 +157,7 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
|
||||
|
||||
securityFeatures, err := findSecurityFeatures(format, reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warnf("unable to determine security features for %q", loc.RealPath)
|
||||
log.WithFields("error", err).Tracef("unable to determine security features for %q", loc.RealPath)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -228,10 +236,12 @@ func findSecurityFeatures(format file.ExecutableFormat, reader unionreader.Union
|
||||
switch format { //nolint: gocritic
|
||||
case file.ELF:
|
||||
return findELFSecurityFeatures(reader) //nolint: gocritic
|
||||
// case file.PE:
|
||||
// return findPESecurityFeatures(reader)
|
||||
// case file.MachO:
|
||||
// return findMachOSecurityFeatures(reader)
|
||||
case file.PE:
|
||||
// return findPESecurityFeatures(reader)
|
||||
return nil, nil
|
||||
case file.MachO:
|
||||
// return findMachOSecurityFeatures(reader)
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported executable format: %q", format)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func hasAnyDynamicSymbols(file *elf.File, symbolNames ...string) *bool {
|
||||
dynSyms, err := file.DynamicSymbols()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Warn("unable to read dynamic symbols from elf file")
|
||||
log.WithFields("error", err).Trace("unable to read dynamic symbols from elf file")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func hasElfDynFlag(f *elf.File, flag elf.DynFlag) bool {
|
||||
vals, err := f.DynValue(elf.DT_FLAGS)
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Warn("unable to read DT_FLAGS from elf file")
|
||||
log.WithFields("error", err).Trace("unable to read DT_FLAGS from elf file")
|
||||
return false
|
||||
}
|
||||
for _, val := range vals {
|
||||
@@ -127,7 +127,7 @@ func hasElfDynFlag1(f *elf.File, flag elf.DynFlag1) bool {
|
||||
vals, err := f.DynValue(elf.DT_FLAGS_1)
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Warn("unable to read DT_FLAGS_1 from elf file")
|
||||
log.WithFields("error", err).Trace("unable to read DT_FLAGS_1 from elf file")
|
||||
return false
|
||||
}
|
||||
for _, val := range vals {
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
intFile "github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
)
|
||||
|
||||
func testDigests(t testing.TB, root string, files []string, hashes ...crypto.Hash) map[file.Coordinates][]file.Digest {
|
||||
@@ -77,7 +79,7 @@ func TestDigestsCataloger(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := NewCataloger(test.digests)
|
||||
|
||||
src, err := source.NewFromDirectoryPath("test-fixtures/last/")
|
||||
src, err := directorysource.NewFromPath("test-fixtures/last/")
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
@@ -96,10 +98,9 @@ func TestDigestsCataloger_MixFileTypes(t *testing.T) {
|
||||
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
src, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create source: %+v", err)
|
||||
}
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: testImage,
|
||||
})
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
if err != nil {
|
||||
@@ -169,8 +170,9 @@ func TestFileDigestCataloger_GivenCoordinates(t *testing.T) {
|
||||
|
||||
c := NewCataloger([]crypto.Hash{crypto.SHA256})
|
||||
|
||||
src, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
|
||||
require.NoError(t, err)
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: testImage,
|
||||
})
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
)
|
||||
|
||||
func TestFileMetadataCataloger(t *testing.T) {
|
||||
@@ -21,8 +22,9 @@ func TestFileMetadataCataloger(t *testing.T) {
|
||||
|
||||
c := NewCataloger()
|
||||
|
||||
src, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
|
||||
require.NoError(t, err)
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: testImage,
|
||||
})
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
@@ -159,8 +161,9 @@ func TestFileMetadataCataloger_GivenCoordinates(t *testing.T) {
|
||||
|
||||
c := NewCataloger()
|
||||
|
||||
src, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
|
||||
require.NoError(t, err)
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: testImage,
|
||||
})
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
)
|
||||
|
||||
func Test_allRegularFiles(t *testing.T) {
|
||||
@@ -28,8 +30,9 @@ func Test_allRegularFiles(t *testing.T) {
|
||||
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
s, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
|
||||
require.NoError(t, err)
|
||||
s := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: testImage,
|
||||
})
|
||||
|
||||
r, err := s.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
@@ -42,7 +45,7 @@ func Test_allRegularFiles(t *testing.T) {
|
||||
{
|
||||
name: "directory",
|
||||
setup: func() file.Resolver {
|
||||
s, err := source.NewFromDirectoryPath("test-fixtures/symlinked-root/nested/link-root")
|
||||
s, err := directorysource.NewFromPath("test-fixtures/symlinked-root/nested/link-root")
|
||||
require.NoError(t, err)
|
||||
r, err := s.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cyclonedxhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -84,8 +85,9 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component {
|
||||
}
|
||||
return []cyclonedx.Component{
|
||||
{
|
||||
Type: cyclonedx.ComponentTypeOS,
|
||||
// FIXME is it idiomatic to be using SWID here for specific name and version information?
|
||||
BOMRef: toOSBomRef(distro.ID, distro.VersionID),
|
||||
Type: cyclonedx.ComponentTypeOS,
|
||||
// is it idiomatic to be using SWID here for specific name and version information?
|
||||
SWID: &cyclonedx.SWID{
|
||||
TagID: distro.ID,
|
||||
Name: distro.ID,
|
||||
@@ -94,7 +96,7 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component {
|
||||
Description: distro.PrettyName,
|
||||
Name: distro.ID,
|
||||
Version: distro.VersionID,
|
||||
// TODO should we add a PURL?
|
||||
// should we add a PURL?
|
||||
CPE: formatCPE(distro.CPEName),
|
||||
ExternalReferences: eRefs,
|
||||
Properties: properties,
|
||||
@@ -102,6 +104,16 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component {
|
||||
}
|
||||
}
|
||||
|
||||
func toOSBomRef(name string, version string) string {
|
||||
if name == "" {
|
||||
return "os:unknown"
|
||||
}
|
||||
if version == "" {
|
||||
return fmt.Sprintf("os:%s", name)
|
||||
}
|
||||
return fmt.Sprintf("os:%s@%s", name, version)
|
||||
}
|
||||
|
||||
func formatCPE(cpeString string) string {
|
||||
c, err := cpe.NewAttributes(cpeString)
|
||||
if err != nil {
|
||||
@@ -196,7 +208,7 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
|
||||
}
|
||||
|
||||
func toBomProperties(srcMetadata source.Description) *[]cyclonedx.Property {
|
||||
metadata, ok := srcMetadata.Metadata.(source.StereoscopeImageSourceMetadata)
|
||||
metadata, ok := srcMetadata.Metadata.(source.ImageMetadata)
|
||||
if ok {
|
||||
props := helpers.EncodeProperties(metadata.Labels, "syft:image:labels")
|
||||
return &props
|
||||
@@ -208,7 +220,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
name := srcMetadata.Name
|
||||
version := srcMetadata.Version
|
||||
switch metadata := srcMetadata.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
case source.ImageMetadata:
|
||||
if name == "" {
|
||||
name = metadata.UserInput
|
||||
}
|
||||
@@ -225,7 +237,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
case source.DirectorySourceMetadata:
|
||||
case source.DirectoryMetadata:
|
||||
if name == "" {
|
||||
name = metadata.Path
|
||||
}
|
||||
@@ -240,7 +252,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
case source.FileSourceMetadata:
|
||||
case source.FileMetadata:
|
||||
if name == "" {
|
||||
name = metadata.Path
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
@@ -159,7 +160,7 @@ func Test_toBomDescriptor(t *testing.T) {
|
||||
name: "test-image",
|
||||
version: "1.0.0",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
Labels: map[string]string{
|
||||
"key1": "value1",
|
||||
},
|
||||
@@ -234,3 +235,89 @@ func Test_toBomDescriptor(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toOsComponent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
release linux.Release
|
||||
expected cyclonedx.Component
|
||||
}{
|
||||
{
|
||||
name: "basic os component",
|
||||
release: linux.Release{
|
||||
ID: "myLinux",
|
||||
VersionID: "myVersion",
|
||||
},
|
||||
expected: cyclonedx.Component{
|
||||
BOMRef: "os:myLinux@myVersion",
|
||||
Type: cyclonedx.ComponentTypeOS,
|
||||
Name: "myLinux",
|
||||
Version: "myVersion",
|
||||
SWID: &cyclonedx.SWID{
|
||||
TagID: "myLinux",
|
||||
Name: "myLinux",
|
||||
Version: "myVersion",
|
||||
},
|
||||
Properties: &[]cyclonedx.Property{
|
||||
{
|
||||
Name: "syft:distro:id",
|
||||
Value: "myLinux",
|
||||
},
|
||||
{
|
||||
Name: "syft:distro:versionID",
|
||||
Value: "myVersion",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
gotSlice := toOSComponent(&test.release)
|
||||
require.Len(t, gotSlice, 1)
|
||||
got := gotSlice[0]
|
||||
require.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toOSBomRef(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
osName string
|
||||
osVersion string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no name or version specified",
|
||||
osName: "",
|
||||
osVersion: "",
|
||||
expected: "os:unknown",
|
||||
},
|
||||
{
|
||||
name: "no version specified",
|
||||
osName: "my-name",
|
||||
osVersion: "",
|
||||
expected: "os:my-name",
|
||||
},
|
||||
{
|
||||
name: "no name specified",
|
||||
osName: "",
|
||||
osVersion: "my-version",
|
||||
expected: "os:unknown",
|
||||
},
|
||||
{
|
||||
name: "both name and version specified",
|
||||
osName: "my-name",
|
||||
osVersion: "my-version",
|
||||
expected: "os:my-name@my-version",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := toOSBomRef(test.osName, test.osVersion)
|
||||
require.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
purpose := ""
|
||||
var checksums []spdx.Checksum
|
||||
switch m := s.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
case source.ImageMetadata:
|
||||
prefix = prefixImage
|
||||
purpose = spdxPrimaryPurposeContainer
|
||||
|
||||
@@ -211,11 +211,11 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
}
|
||||
}
|
||||
|
||||
case source.DirectorySourceMetadata:
|
||||
case source.DirectoryMetadata:
|
||||
prefix = prefixDirectory
|
||||
purpose = spdxPrimaryPurposeFile
|
||||
|
||||
case source.FileSourceMetadata:
|
||||
case source.FileMetadata:
|
||||
prefix = prefixFile
|
||||
purpose = spdxPrimaryPurposeFile
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func Test_toFormatModel(t *testing.T) {
|
||||
Source: source.Description{
|
||||
Name: "alpine",
|
||||
Version: "sha256:d34db33f",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:latest",
|
||||
ManifestDigest: "sha256:d34db33f",
|
||||
},
|
||||
@@ -106,7 +106,7 @@ func Test_toFormatModel(t *testing.T) {
|
||||
in: sbom.SBOM{
|
||||
Source: source.Description{
|
||||
Name: "some/directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/directory",
|
||||
},
|
||||
},
|
||||
@@ -170,7 +170,7 @@ func Test_toFormatModel(t *testing.T) {
|
||||
Source: source.Description{
|
||||
Name: "path/to/some.file",
|
||||
Version: "sha256:d34db33f",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "path/to/some.file",
|
||||
Digests: []file.Digest{
|
||||
{
|
||||
|
||||
@@ -146,7 +146,7 @@ func containerSource(p *spdx.Package) source.Description {
|
||||
ID: id,
|
||||
Name: p.PackageName,
|
||||
Version: p.PackageVersion,
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: container,
|
||||
ID: id,
|
||||
Layers: nil, // TODO handle formats with nested layer packages like Tern and K8s BOM tool
|
||||
@@ -187,7 +187,7 @@ func fileSource(p *spdx.Package) source.Description {
|
||||
func fileSourceMetadata(p *spdx.Package) (any, string) {
|
||||
version := p.PackageVersion
|
||||
|
||||
m := source.FileSourceMetadata{
|
||||
m := source.FileMetadata{
|
||||
Path: p.PackageName,
|
||||
}
|
||||
// if this is a Syft SBOM, we might have output a digest as the version
|
||||
@@ -206,7 +206,7 @@ func fileSourceMetadata(p *spdx.Package) (any, string) {
|
||||
}
|
||||
|
||||
func directorySourceMetadata(p *spdx.Package) (any, string) {
|
||||
return source.DirectorySourceMetadata{
|
||||
return source.DirectoryMetadata{
|
||||
Path: p.PackageName,
|
||||
Base: "",
|
||||
}, p.PackageVersion
|
||||
@@ -229,15 +229,15 @@ func extractSourceFromNamespace(ns string) source.Description {
|
||||
switch p {
|
||||
case helpers.InputFile:
|
||||
return source.Description{
|
||||
Metadata: source.FileSourceMetadata{},
|
||||
Metadata: source.FileMetadata{},
|
||||
}
|
||||
case helpers.InputImage:
|
||||
return source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{},
|
||||
Metadata: source.ImageMetadata{},
|
||||
}
|
||||
case helpers.InputDirectory:
|
||||
return source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{},
|
||||
Metadata: source.DirectoryMetadata{},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,15 +199,15 @@ func TestExtractSourceFromNamespaces(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
namespace: "https://anchore.com/syft/file/d42b01d0-7325-409b-b03f-74082935c4d3",
|
||||
expected: source.FileSourceMetadata{},
|
||||
expected: source.FileMetadata{},
|
||||
},
|
||||
{
|
||||
namespace: "https://anchore.com/syft/image/d42b01d0-7325-409b-b03f-74082935c4d3",
|
||||
expected: source.StereoscopeImageSourceMetadata{},
|
||||
expected: source.ImageMetadata{},
|
||||
},
|
||||
{
|
||||
namespace: "https://anchore.com/syft/dir/d42b01d0-7325-409b-b03f-74082935c4d3",
|
||||
expected: source.DirectorySourceMetadata{},
|
||||
expected: source.DirectoryMetadata{},
|
||||
},
|
||||
{
|
||||
namespace: "https://another-host/blob/123",
|
||||
@@ -460,7 +460,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
name: "image source",
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-Image-some-image",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
ID: "DocumentRoot-Image-some-image",
|
||||
UserInput: "some-image:some-tag",
|
||||
ManifestDigest: "sha256:ab8b83234bc28f28d8e",
|
||||
@@ -476,7 +476,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-Directory-.",
|
||||
Name: ".",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
@@ -488,7 +488,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-Directory-my-app",
|
||||
Name: "my-app",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "my-app",
|
||||
},
|
||||
},
|
||||
@@ -499,7 +499,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
name: "file source",
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-File-my-app.exe",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "my-app.exe",
|
||||
Digests: []file.Digest{
|
||||
{
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref":"redacted",
|
||||
"type": "operating-system",
|
||||
"name": "debian",
|
||||
"version": "1.2.3",
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref":"redacted",
|
||||
"type": "operating-system",
|
||||
"name": "debian",
|
||||
"version": "1.2.3",
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="operating-system">
|
||||
<component bom-ref="redacted" type="operating-system">
|
||||
<name>debian</name>
|
||||
<version>1.2.3</version>
|
||||
<description>debian</description>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="operating-system">
|
||||
<component bom-ref="redacted" type="operating-system">
|
||||
<name>debian</name>
|
||||
<version>1.2.3</version>
|
||||
<description>debian</description>
|
||||
|
||||
@@ -116,16 +116,16 @@ func toPath(s source.Description, p pkg.Package) string {
|
||||
}
|
||||
packagePath = strings.TrimPrefix(packagePath, "/")
|
||||
switch metadata := s.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
case source.ImageMetadata:
|
||||
image := strings.ReplaceAll(metadata.UserInput, ":/", "//")
|
||||
return fmt.Sprintf("%s:/%s", image, packagePath)
|
||||
case source.FileSourceMetadata:
|
||||
case source.FileMetadata:
|
||||
path := trimRelative(metadata.Path)
|
||||
if isArchive(metadata.Path) {
|
||||
return fmt.Sprintf("%s:/%s", path, packagePath)
|
||||
}
|
||||
return path
|
||||
case source.DirectorySourceMetadata:
|
||||
case source.DirectoryMetadata:
|
||||
path := trimRelative(metadata.Path)
|
||||
if path != "" {
|
||||
return fmt.Sprintf("%s/%s", path, packagePath)
|
||||
|
||||
@@ -22,7 +22,7 @@ func sbomFixture() sbom.SBOM {
|
||||
Name: "syft",
|
||||
},
|
||||
Source: source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "ubuntu:18.04",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
@@ -150,27 +150,27 @@ func Test_toGithubModel(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "current directory",
|
||||
metadata: source.DirectorySourceMetadata{Path: "."},
|
||||
metadata: source.DirectoryMetadata{Path: "."},
|
||||
testPath: "etc",
|
||||
},
|
||||
{
|
||||
name: "relative directory",
|
||||
metadata: source.DirectorySourceMetadata{Path: "./artifacts"},
|
||||
metadata: source.DirectoryMetadata{Path: "./artifacts"},
|
||||
testPath: "artifacts/etc",
|
||||
},
|
||||
{
|
||||
name: "absolute directory",
|
||||
metadata: source.DirectorySourceMetadata{Path: "/artifacts"},
|
||||
metadata: source.DirectoryMetadata{Path: "/artifacts"},
|
||||
testPath: "/artifacts/etc",
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
metadata: source.FileSourceMetadata{Path: "./executable"},
|
||||
metadata: source.FileMetadata{Path: "./executable"},
|
||||
testPath: "executable",
|
||||
},
|
||||
{
|
||||
name: "archive",
|
||||
metadata: source.FileSourceMetadata{Path: "./archive.tar.gz"},
|
||||
metadata: source.FileMetadata{Path: "./archive.tar.gz"},
|
||||
testPath: "archive.tar.gz:/etc",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
ID: "",
|
||||
// TODO: can we decode alias name-version somehow? (it isn't be encoded in the first place yet)
|
||||
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: c.Name,
|
||||
ID: c.BOMRef,
|
||||
ManifestDigest: c.Version,
|
||||
@@ -235,7 +235,7 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
// TODO: this is lossy... we can't know if this is a file or a directory
|
||||
return source.Description{
|
||||
ID: "",
|
||||
Metadata: source.FileSourceMetadata{Path: c.Name},
|
||||
Metadata: source.FileMetadata{Path: c.Name},
|
||||
}
|
||||
}
|
||||
return source.Description{}
|
||||
|
||||
@@ -10,11 +10,11 @@ func DocumentName(src source.Description) string {
|
||||
}
|
||||
|
||||
switch metadata := src.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
case source.ImageMetadata:
|
||||
return metadata.UserInput
|
||||
case source.DirectorySourceMetadata:
|
||||
case source.DirectoryMetadata:
|
||||
return metadata.Path
|
||||
case source.FileSourceMetadata:
|
||||
case source.FileMetadata:
|
||||
return metadata.Path
|
||||
default:
|
||||
return "unknown"
|
||||
|
||||
@@ -23,7 +23,7 @@ func Test_DocumentName(t *testing.T) {
|
||||
{
|
||||
name: "image",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "image-repo/name:tag",
|
||||
ID: "id",
|
||||
ManifestDigest: "digest",
|
||||
@@ -34,14 +34,14 @@ func Test_DocumentName(t *testing.T) {
|
||||
{
|
||||
name: "directory",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{Path: "some/path/to/place"},
|
||||
Metadata: source.DirectoryMetadata{Path: "some/path/to/place"},
|
||||
},
|
||||
expected: "some/path/to/place",
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.FileSourceMetadata{Path: "some/path/to/place"},
|
||||
Metadata: source.FileMetadata{Path: "some/path/to/place"},
|
||||
},
|
||||
expected: "some/path/to/place",
|
||||
},
|
||||
@@ -49,7 +49,7 @@ func Test_DocumentName(t *testing.T) {
|
||||
name: "named",
|
||||
srcMetadata: source.Description{
|
||||
Name: "some/name",
|
||||
Metadata: source.FileSourceMetadata{Path: "some/path/to/place"},
|
||||
Metadata: source.FileMetadata{Path: "some/path/to/place"},
|
||||
},
|
||||
expected: "some/name",
|
||||
},
|
||||
|
||||
@@ -27,11 +27,11 @@ func DocumentNamespace(name string, src source.Description, desc sbom.Descriptor
|
||||
name = cleanName(name)
|
||||
input := "unknown-source-type"
|
||||
switch src.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
case source.ImageMetadata:
|
||||
input = InputImage
|
||||
case source.DirectorySourceMetadata:
|
||||
case source.DirectoryMetadata:
|
||||
input = InputDirectory
|
||||
case source.FileSourceMetadata:
|
||||
case source.FileMetadata:
|
||||
input = InputFile
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ func Test_documentNamespace(t *testing.T) {
|
||||
name: "image",
|
||||
inputName: "my-name",
|
||||
src: source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "image-repo/name:tag",
|
||||
ID: "id",
|
||||
ManifestDigest: "digest",
|
||||
@@ -37,7 +37,7 @@ func Test_documentNamespace(t *testing.T) {
|
||||
name: "directory",
|
||||
inputName: "my-name",
|
||||
src: source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
},
|
||||
@@ -47,7 +47,7 @@ func Test_documentNamespace(t *testing.T) {
|
||||
name: "file",
|
||||
inputName: "my-name",
|
||||
src: source.Description{
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -60,6 +60,8 @@ func SourceInfo(p pkg.Package) string {
|
||||
answer = "acquired package info from resolved Swift package manifest"
|
||||
case pkg.GithubActionPkg, pkg.GithubActionWorkflowPkg:
|
||||
answer = "acquired package info from GitHub Actions workflow file or composite action file"
|
||||
case pkg.WordpressPluginPkg:
|
||||
answer = "acquired package info from found wordpress plugin PHP source files"
|
||||
default:
|
||||
answer = "acquired package info from the following paths"
|
||||
}
|
||||
|
||||
@@ -263,6 +263,14 @@ func Test_SourceInfo(t *testing.T) {
|
||||
"from GitHub Actions workflow file or composite action file",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.WordpressPluginPkg,
|
||||
},
|
||||
expected: []string{
|
||||
"acquired package info from found wordpress plugin PHP source files",
|
||||
},
|
||||
},
|
||||
}
|
||||
var pkgTypes []pkg.Type
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
)
|
||||
|
||||
func DirectoryInput(t testing.TB, dir string) sbom.SBOM {
|
||||
@@ -22,8 +22,8 @@ func DirectoryInput(t testing.TB, dir string) sbom.SBOM {
|
||||
|
||||
require.NoError(t, os.MkdirAll(path, 0755))
|
||||
|
||||
src, err := source.NewFromDirectory(
|
||||
source.DirectoryConfig{
|
||||
src, err := directorysource.New(
|
||||
directorysource.Config{
|
||||
Path: path,
|
||||
Base: dir,
|
||||
},
|
||||
@@ -63,8 +63,8 @@ func DirectoryInputWithAuthorField(t testing.TB) sbom.SBOM {
|
||||
|
||||
require.NoError(t, os.MkdirAll(path, 0755))
|
||||
|
||||
src, err := source.NewFromDirectory(
|
||||
source.DirectoryConfig{
|
||||
src, err := directorysource.New(
|
||||
directorysource.Config{
|
||||
Path: path,
|
||||
Base: dir,
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/filetree"
|
||||
@@ -16,7 +15,7 @@ import (
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
)
|
||||
|
||||
func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBOM {
|
||||
@@ -42,8 +41,9 @@ func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBO
|
||||
// this is a hard coded value that is not given by the fixture helper and must be provided manually
|
||||
img.Metadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
|
||||
|
||||
src, err := source.NewFromStereoscopeImageObject(img, "user-image-input", nil)
|
||||
assert.NoError(t, err)
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: "user-image-input",
|
||||
})
|
||||
|
||||
return sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestSPDXJSONSPDXIDs(t *testing.T) {
|
||||
Relationships: nil,
|
||||
Source: source.Description{
|
||||
Name: "foobar/baz", // in this case, foobar is used as the spdx document name
|
||||
Metadata: source.DirectorySourceMetadata{},
|
||||
Metadata: source.DirectoryMetadata{},
|
||||
},
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: "syft",
|
||||
|
||||
@@ -263,7 +263,7 @@ func Test_encodeDecodeFileMetadata(t *testing.T) {
|
||||
ID: "some-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "/some-file-source-path",
|
||||
Digests: []file.Digest{
|
||||
{
|
||||
|
||||
@@ -260,7 +260,7 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
},
|
||||
Source: source.Description{
|
||||
ID: "c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-image-input",
|
||||
ID: "sha256:c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0",
|
||||
ManifestDigest: "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
||||
@@ -269,7 +269,7 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
"stereoscope-fixture-image-simple:85066c51088bdd274f7a89e99e00490f666c49e72ffc955707cd6e18f0e22c5b",
|
||||
},
|
||||
Size: 38,
|
||||
Layers: []source.StereoscopeLayerMetadata{
|
||||
Layers: []source.LayerMetadata{
|
||||
{
|
||||
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
Digest: "sha256:3de16c5b8659a2e8d888b8ded8427be7a5686a3c8c4e4dd30de20f362827285b",
|
||||
|
||||
@@ -89,7 +89,7 @@ func extractPreSchemaV9Metadata(t string, target []byte) (interface{}, error) {
|
||||
cleanTarget = string(target)
|
||||
}
|
||||
|
||||
return source.DirectorySourceMetadata{
|
||||
return source.DirectoryMetadata{
|
||||
Path: cleanTarget,
|
||||
}, nil
|
||||
|
||||
@@ -99,12 +99,12 @@ func extractPreSchemaV9Metadata(t string, target []byte) (interface{}, error) {
|
||||
cleanTarget = string(target)
|
||||
}
|
||||
|
||||
return source.FileSourceMetadata{
|
||||
return source.FileMetadata{
|
||||
Path: cleanTarget,
|
||||
}, nil
|
||||
|
||||
case "image":
|
||||
var payload source.StereoscopeImageSourceMetadata
|
||||
var payload source.ImageMetadata
|
||||
if err := json.Unmarshal(target, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestSource_UnmarshalJSON(t *testing.T) {
|
||||
expected: &Source{
|
||||
ID: "foobar",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "/var/lib/foo",
|
||||
//Base: "/nope", // note: should be ignored entirely
|
||||
},
|
||||
@@ -67,14 +67,14 @@ func TestSource_UnmarshalJSON(t *testing.T) {
|
||||
expected: &Source{
|
||||
ID: "foobar",
|
||||
Type: "image",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:3.10",
|
||||
ID: "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a",
|
||||
ManifestDigest: "sha256:e515aad2ed234a5072c4d2ef86a1cb77d5bfe4b11aa865d9214875734c4eeb3c",
|
||||
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
|
||||
Tags: []string{},
|
||||
Size: 5576169,
|
||||
Layers: []source.StereoscopeLayerMetadata{
|
||||
Layers: []source.LayerMetadata{
|
||||
{
|
||||
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
Digest: "sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
|
||||
@@ -124,7 +124,7 @@ func TestSource_UnmarshalJSON(t *testing.T) {
|
||||
expected: &Source{
|
||||
ID: "foobar",
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "/var/lib/foo/go.mod",
|
||||
Digests: []file.Digest{
|
||||
{
|
||||
@@ -188,7 +188,7 @@ func TestSource_UnmarshalJSON_PreSchemaV9(t *testing.T) {
|
||||
expectedSource: &Source{
|
||||
ID: "foobar",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "/var/lib/foo",
|
||||
},
|
||||
},
|
||||
@@ -204,7 +204,7 @@ func TestSource_UnmarshalJSON_PreSchemaV9(t *testing.T) {
|
||||
expectedSource: &Source{
|
||||
ID: "foobar",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "/var/lib/foo",
|
||||
},
|
||||
},
|
||||
@@ -239,14 +239,14 @@ func TestSource_UnmarshalJSON_PreSchemaV9(t *testing.T) {
|
||||
expectedSource: &Source{
|
||||
ID: "foobar",
|
||||
Type: "image",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:3.10",
|
||||
ID: "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a",
|
||||
ManifestDigest: "sha256:e515aad2ed234a5072c4d2ef86a1cb77d5bfe4b11aa865d9214875734c4eeb3c",
|
||||
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
|
||||
Tags: []string{},
|
||||
Size: 5576169,
|
||||
Layers: []source.StereoscopeLayerMetadata{
|
||||
Layers: []source.LayerMetadata{
|
||||
{
|
||||
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
Digest: "sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
|
||||
@@ -288,7 +288,7 @@ func TestSource_UnmarshalJSON_PreSchemaV9(t *testing.T) {
|
||||
expectedSource: &Source{
|
||||
ID: "foobar",
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "/var/lib/foo/go.mod",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package syftjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/iancoleman/strcase"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
)
|
||||
|
||||
type schema struct {
|
||||
Schema string `json:"$schema"`
|
||||
ID string `json:"$id"`
|
||||
Ref string `json:"$ref"`
|
||||
Defs map[string]properties `json:"$defs"`
|
||||
}
|
||||
|
||||
type properties struct {
|
||||
Properties map[string]any `json:"properties"`
|
||||
Type string `json:"type"`
|
||||
Required []string `json:"required"`
|
||||
}
|
||||
|
||||
func (p properties) fields() []string {
|
||||
var result []string
|
||||
|
||||
for k := range p.Properties {
|
||||
result = append(result, k)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func Test_JSONSchemaConventions(t *testing.T) {
|
||||
// read schema/json/schema-latest.json
|
||||
// look at all attributes and ensure that all fields are camelCase
|
||||
// we want to strictly follow https://google.github.io/styleguide/javaguide.html#s5.3-camel-case
|
||||
//
|
||||
// > Convert the phrase to plain ASCII and remove any apostrophes. For example, "Müller's algorithm" might become "Muellers algorithm".
|
||||
// > Divide this result into words, splitting on spaces and any remaining punctuation (typically hyphens).
|
||||
// > Recommended: if any word already has a conventional camel-case appearance in common usage, split this into its constituent parts (e.g., "AdWords" becomes "ad words"). Note that a word such as "iOS" is not really in camel case per se; it defies any convention, so this recommendation does not apply.
|
||||
// > Now lowercase everything (including acronyms), then uppercase only the first character of:
|
||||
// > ... each word, to yield upper camel case, or
|
||||
// > ... each word except the first, to yield lower camel case
|
||||
// > Finally, join all the words into a single identifier.
|
||||
//
|
||||
// This means that acronyms should be treated as words (e.g. "HttpServer" not "HTTPServer")
|
||||
|
||||
root, err := packagemetadata.RepoRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
contents, err := os.ReadFile(filepath.Join(root, "schema", "json", "schema-latest.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
var s schema
|
||||
require.NoError(t, json.Unmarshal(contents, &s))
|
||||
|
||||
require.NotEmpty(t, s.Defs)
|
||||
|
||||
for name, def := range s.Defs {
|
||||
checkAndConvertFields(t, name, def.fields())
|
||||
}
|
||||
}
|
||||
|
||||
func checkAndConvertFields(t *testing.T, path string, properties []string) {
|
||||
for _, fieldName := range properties {
|
||||
if pass, exp := isFollowingConvention(path, fieldName); !pass {
|
||||
t.Logf("%s: has non camel case field: %q (expected %q)", path, fieldName, exp)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func isFollowingConvention(path, fieldName string) (bool, string) {
|
||||
exp := strcase.ToLowerCamel(fieldName)
|
||||
result := exp == fieldName
|
||||
|
||||
exception := func(exceptions ...string) (bool, string) {
|
||||
for _, e := range exceptions {
|
||||
if e == fieldName {
|
||||
return true, fieldName
|
||||
}
|
||||
}
|
||||
return result, exp
|
||||
}
|
||||
|
||||
// add exceptions as needed... these are grandfathered in and will be addressed in a future breaking schema change
|
||||
// ideally in the future there will be no exceptions to the camel case convention for fields
|
||||
switch path {
|
||||
case "Coordinates", "Location":
|
||||
return exception("layerID")
|
||||
case "MicrosoftKbPatch":
|
||||
return exception("product_id")
|
||||
case "HaskellHackageStackLockEntry":
|
||||
return exception("snapshotURL")
|
||||
case "LinuxRelease":
|
||||
return exception("imageID", "supportURL", "privacyPolicyURL", "versionID", "variantID", "homeURL", "buildID", "bugReportURL")
|
||||
case "CConanLockV2Entry":
|
||||
return exception("packageID")
|
||||
case "CConanInfoEntry":
|
||||
return exception("package_id")
|
||||
case "PhpComposerInstalledEntry", "PhpComposerLockEntry":
|
||||
return exception("notification-url", "require-dev")
|
||||
case "LinuxKernelArchive":
|
||||
return exception("rwRootFS")
|
||||
case "CConanLockEntry":
|
||||
return exception("build_requires", "py_requires", "package_id")
|
||||
case "FileMetadataEntry":
|
||||
return exception("userID", "groupID")
|
||||
case "DartPubspecLockEntry":
|
||||
return exception("hosted_url", "vcs_url")
|
||||
case "ELFSecurityFeatures":
|
||||
return exception("relRO")
|
||||
}
|
||||
return result, exp
|
||||
}
|
||||
@@ -303,7 +303,7 @@ func toSourceModel(src source.Description) model.Source {
|
||||
Metadata: src.Metadata,
|
||||
}
|
||||
|
||||
if metadata, ok := src.Metadata.(source.StereoscopeImageSourceMetadata); ok {
|
||||
if metadata, ok := src.Metadata.(source.ImageMetadata); ok {
|
||||
// ensure that empty collections are not shown as null
|
||||
if metadata.RepoDigests == nil {
|
||||
metadata.RepoDigests = []string{}
|
||||
|
||||
@@ -26,7 +26,7 @@ func Test_toSourceModel_IgnoreBase(t *testing.T) {
|
||||
name: "directory",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -59,7 +59,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -69,7 +69,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -81,7 +81,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -92,7 +92,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -105,7 +105,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
@@ -117,7 +117,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "image",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
@@ -133,7 +133,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
name: "directory - no name/version",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -141,7 +141,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -151,7 +151,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
name: "file - no name/version",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -160,7 +160,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -171,7 +171,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
name: "image - no name/version",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
@@ -181,7 +181,7 @@ func Test_toSourceModel(t *testing.T) {
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Type: "image",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
|
||||
@@ -35,7 +35,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -44,7 +44,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
ID: "the-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -57,7 +57,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -67,7 +67,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
ID: "the-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -81,7 +81,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "image",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
@@ -92,7 +92,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
ID: "the-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
@@ -107,14 +107,14 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
src: model.Source{
|
||||
ID: "the-id",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
},
|
||||
expected: &source.Description{
|
||||
ID: "the-id",
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
@@ -125,7 +125,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
src: model.Source{
|
||||
ID: "the-id",
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -133,7 +133,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
},
|
||||
expected: &source.Description{
|
||||
ID: "the-id",
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
MIMEType: "text/plain",
|
||||
@@ -145,7 +145,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
src: model.Source{
|
||||
ID: "the-id",
|
||||
Type: "image",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
@@ -154,7 +154,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
},
|
||||
expected: &source.Description{
|
||||
ID: "the-id",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "user-input",
|
||||
ID: "id...",
|
||||
ManifestDigest: "digest...",
|
||||
@@ -178,7 +178,7 @@ func Test_idsHaveChanged(t *testing.T) {
|
||||
s := toSyftModel(model.Document{
|
||||
Source: model.Source{
|
||||
Type: "file",
|
||||
Metadata: source.FileSourceMetadata{Path: "some/path"},
|
||||
Metadata: source.FileMetadata{Path: "some/path"},
|
||||
},
|
||||
Artifacts: []model.Package{
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
@@ -61,7 +62,9 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
}
|
||||
return false
|
||||
})
|
||||
rows = removeDuplicateRows(rows)
|
||||
|
||||
columns = append(columns, "") // add a column for duplicate annotations
|
||||
rows = markDuplicateRows(rows)
|
||||
|
||||
table := tablewriter.NewWriter(writer)
|
||||
|
||||
@@ -84,19 +87,38 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeDuplicateRows(items [][]string) [][]string {
|
||||
seen := map[string][]string{}
|
||||
func markDuplicateRows(items [][]string) [][]string {
|
||||
seen := map[string]int{}
|
||||
var result [][]string
|
||||
|
||||
for _, v := range items {
|
||||
key := strings.Join(v, "|")
|
||||
if seen[key] != nil {
|
||||
if _, ok := seen[key]; ok {
|
||||
// dup!
|
||||
seen[key]++
|
||||
continue
|
||||
}
|
||||
|
||||
seen[key] = v
|
||||
seen[key] = 1
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
style := lipgloss.NewStyle().Foreground(lipgloss.Color("#777777"))
|
||||
for i, v := range result {
|
||||
key := strings.Join(v, "|")
|
||||
// var name string
|
||||
var annotation string
|
||||
switch seen[key] {
|
||||
case 0, 1:
|
||||
case 2:
|
||||
annotation = "(+1 duplicate)"
|
||||
default:
|
||||
annotation = fmt.Sprintf("(+%d duplicates)", seen[key]-1)
|
||||
}
|
||||
|
||||
annotation = style.Render(annotation)
|
||||
result[i] = append(v, annotation)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestTableEncoder(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestRemoveDuplicateRows(t *testing.T) {
|
||||
func Test_markDuplicateRows(t *testing.T) {
|
||||
data := [][]string{
|
||||
{"1", "2", "3"},
|
||||
{"a", "b", "c"},
|
||||
@@ -35,13 +35,13 @@ func TestRemoveDuplicateRows(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := [][]string{
|
||||
{"1", "2", "3"},
|
||||
{"a", "b", "c"},
|
||||
{"4", "5", "6"},
|
||||
{"1", "2", "1"},
|
||||
{"1", "2", "3", "(+2 duplicates)"},
|
||||
{"a", "b", "c", "(+1 duplicate)"},
|
||||
{"4", "5", "6", ""},
|
||||
{"1", "2", "1", ""},
|
||||
}
|
||||
|
||||
actual := removeDuplicateRows(data)
|
||||
actual := markDuplicateRows(data)
|
||||
|
||||
if diffs := deep.Equal(expected, actual); len(diffs) > 0 {
|
||||
t.Errorf("found diffs!")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
NAME VERSION TYPE
|
||||
package-1 1.0.1 python
|
||||
package-2 2.0.1 deb
|
||||
NAME VERSION TYPE
|
||||
package-1 1.0.1 python
|
||||
package-2 2.0.1 deb
|
||||
|
||||
@@ -38,11 +38,11 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
w.Init(writer, 0, 8, 0, '\t', tabwriter.AlignRight)
|
||||
|
||||
switch metadata := s.Source.Metadata.(type) {
|
||||
case source.DirectorySourceMetadata:
|
||||
case source.DirectoryMetadata:
|
||||
fmt.Fprintf(w, "[Path: %s]\n", metadata.Path)
|
||||
case source.FileSourceMetadata:
|
||||
case source.FileMetadata:
|
||||
fmt.Fprintf(w, "[Path: %s]\n", metadata.Path)
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
case source.ImageMetadata:
|
||||
fmt.Fprintln(w, "[Image]")
|
||||
|
||||
for idx, l := range metadata.Layers {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package syft
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// GetSource uses all of Syft's known source providers to attempt to resolve the user input to a usable source.Source
|
||||
func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (source.Source, error) {
|
||||
if cfg == nil {
|
||||
cfg = DefaultGetSourceConfig()
|
||||
}
|
||||
|
||||
providers, err := cfg.getProviders(userInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var fileNotfound error
|
||||
|
||||
// call each source provider until we find a valid source
|
||||
for _, p := range providers {
|
||||
src, err := p.Provide(ctx)
|
||||
if err != nil {
|
||||
err = eachError(err, func(err error) error {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
fileNotfound = err
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if src != nil {
|
||||
// if we have a non-image type and platform is specified, it's an error
|
||||
if cfg.SourceProviderConfig.Platform != nil {
|
||||
meta := src.Describe().Metadata
|
||||
switch meta.(type) {
|
||||
case *source.ImageMetadata, source.ImageMetadata:
|
||||
default:
|
||||
return src, fmt.Errorf("platform specified with non-image source")
|
||||
}
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
}
|
||||
|
||||
if fileNotfound != nil {
|
||||
errs = append([]error{fileNotfound}, errs...)
|
||||
}
|
||||
return nil, sourceError(userInput, errs...)
|
||||
}
|
||||
|
||||
func sourceError(userInput string, errs ...error) error {
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return fmt.Errorf("an error occurred attempting to resolve '%s': %w", userInput, errs[0])
|
||||
}
|
||||
errorTexts := ""
|
||||
for _, e := range errs {
|
||||
errorTexts += fmt.Sprintf("\n - %s", e)
|
||||
}
|
||||
return fmt.Errorf("errors occurred attempting to resolve '%s':%s", userInput, errorTexts)
|
||||
}
|
||||
|
||||
func eachError(err error, fn func(error) error) error {
|
||||
out := fn(err)
|
||||
// unwrap singly wrapped errors
|
||||
if e, ok := err.(interface {
|
||||
Unwrap() error
|
||||
}); ok {
|
||||
wrapped := e.Unwrap()
|
||||
got := eachError(wrapped, fn)
|
||||
// return the outer error if received the same wrapped error
|
||||
if errors.Is(got, wrapped) {
|
||||
return err
|
||||
}
|
||||
return got
|
||||
}
|
||||
// unwrap errors from errors.Join
|
||||
if errs, ok := err.(interface {
|
||||
Unwrap() []error
|
||||
}); ok {
|
||||
for _, e := range errs.Unwrap() {
|
||||
e = eachError(e, fn)
|
||||
if e != nil {
|
||||
out = errors.Join(out, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package syft
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/go-collections"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
type GetSourceConfig struct {
|
||||
// SourceProviderConfig may optionally be provided to be used when constructing the default set of source providers, unused if All specified
|
||||
SourceProviderConfig *sourceproviders.Config
|
||||
|
||||
// Sources is an explicit list of source names to use, in order, to attempt to locate a source
|
||||
Sources []string
|
||||
|
||||
// DefaultImagePullSource will cause a particular image pull source to be used as the first pull source, followed by other pull sources
|
||||
DefaultImagePullSource string
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithAlias(alias source.Alias) *GetSourceConfig {
|
||||
c.SourceProviderConfig = c.SourceProviderConfig.WithAlias(alias)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithRegistryOptions(registryOptions *image.RegistryOptions) *GetSourceConfig {
|
||||
c.SourceProviderConfig = c.SourceProviderConfig.WithRegistryOptions(registryOptions)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithPlatform(platform *image.Platform) *GetSourceConfig {
|
||||
c.SourceProviderConfig = c.SourceProviderConfig.WithPlatform(platform)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithExcludeConfig(excludeConfig source.ExcludeConfig) *GetSourceConfig {
|
||||
c.SourceProviderConfig = c.SourceProviderConfig.WithExcludeConfig(excludeConfig)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithDigestAlgorithms(algorithms ...crypto.Hash) *GetSourceConfig {
|
||||
c.SourceProviderConfig = c.SourceProviderConfig.WithDigestAlgorithms(algorithms...)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithBasePath(basePath string) *GetSourceConfig {
|
||||
c.SourceProviderConfig = c.SourceProviderConfig.WithBasePath(basePath)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithSources(sources ...string) *GetSourceConfig {
|
||||
c.Sources = sources
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) WithDefaultImagePullSource(defaultImagePullSource string) *GetSourceConfig {
|
||||
c.DefaultImagePullSource = defaultImagePullSource
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) getProviders(userInput string) ([]source.Provider, error) {
|
||||
providers := collections.TaggedValueSet[source.Provider]{}.Join(sourceproviders.All(userInput, c.SourceProviderConfig)...)
|
||||
|
||||
// if the "default image pull source" is set, we move this as the first pull source
|
||||
if c.DefaultImagePullSource != "" {
|
||||
base := providers.Remove(sourceproviders.PullTag)
|
||||
pull := providers.Select(sourceproviders.PullTag)
|
||||
def := pull.Select(c.DefaultImagePullSource)
|
||||
if len(def) == 0 {
|
||||
return nil, fmt.Errorf("invalid DefaultImagePullSource: %s; available values are: %v", c.DefaultImagePullSource, pull.Tags())
|
||||
}
|
||||
providers = base.Join(def...).Join(pull...)
|
||||
}
|
||||
|
||||
// narrow the sources to those explicitly requested generally by a user
|
||||
if len(c.Sources) > 0 {
|
||||
// select the explicitly provided sources, in order
|
||||
providers = providers.Select(c.Sources...)
|
||||
}
|
||||
|
||||
return providers.Values(), nil
|
||||
}
|
||||
|
||||
func DefaultGetSourceConfig() *GetSourceConfig {
|
||||
return &GetSourceConfig{
|
||||
SourceProviderConfig: sourceproviders.DefaultConfig(),
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,6 @@ import (
|
||||
"github.com/anchore/syft/syft/internal/windows"
|
||||
)
|
||||
|
||||
var unixSystemRuntimePrefixes = []string{
|
||||
"/proc",
|
||||
"/dev",
|
||||
"/sys",
|
||||
}
|
||||
|
||||
var ErrSkipPath = errors.New("skip path")
|
||||
|
||||
var _ file.Resolver = (*Directory)(nil)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/sys/mountinfo"
|
||||
"github.com/wagoodman/go-partybus"
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
@@ -21,7 +22,7 @@ import (
|
||||
"github.com/anchore/syft/syft/internal/windows"
|
||||
)
|
||||
|
||||
type PathIndexVisitor func(string, os.FileInfo, error) error
|
||||
type PathIndexVisitor func(string, string, os.FileInfo, error) error
|
||||
|
||||
type directoryIndexer struct {
|
||||
path string
|
||||
@@ -34,12 +35,18 @@ type directoryIndexer struct {
|
||||
|
||||
func newDirectoryIndexer(path, base string, visitors ...PathIndexVisitor) *directoryIndexer {
|
||||
i := &directoryIndexer{
|
||||
path: path,
|
||||
base: base,
|
||||
tree: filetree.New(),
|
||||
index: filetree.NewIndex(),
|
||||
pathIndexVisitors: append([]PathIndexVisitor{requireFileInfo, disallowByFileType, disallowUnixSystemRuntimePath}, visitors...),
|
||||
errPaths: make(map[string]error),
|
||||
path: path,
|
||||
base: base,
|
||||
tree: filetree.New(),
|
||||
index: filetree.NewIndex(),
|
||||
pathIndexVisitors: append(
|
||||
[]PathIndexVisitor{
|
||||
requireFileInfo,
|
||||
disallowByFileType,
|
||||
newUnixSystemMountFinder().disallowUnixSystemRuntimePath},
|
||||
visitors...,
|
||||
),
|
||||
errPaths: make(map[string]error),
|
||||
}
|
||||
|
||||
// these additional stateful visitors should be the first thing considered when walking / indexing
|
||||
@@ -176,6 +183,13 @@ func isRealPath(root string) (bool, error) {
|
||||
func (r *directoryIndexer) indexBranch(root string, stager *progress.Stage) ([]string, error) {
|
||||
rootRealPath, err := filepath.EvalSymlinks(root)
|
||||
if err != nil {
|
||||
var pathErr *os.PathError
|
||||
if errors.As(err, &pathErr) {
|
||||
// we can't index the path, but we shouldn't consider this to be fatal
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("root", root, "error", err).Trace("unable to evaluate symlink while indexing branch")
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -239,14 +253,14 @@ func allContainedPaths(p string) []string {
|
||||
return all
|
||||
}
|
||||
|
||||
func (r *directoryIndexer) indexPath(path string, info os.FileInfo, err error) (string, error) {
|
||||
func (r *directoryIndexer) indexPath(givenPath string, info os.FileInfo, err error) (string, error) {
|
||||
// ignore any path which a filter function returns true
|
||||
for _, filterFn := range r.pathIndexVisitors {
|
||||
if filterFn == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if filterErr := filterFn(path, info, err); filterErr != nil {
|
||||
if filterErr := filterFn(r.base, givenPath, info, err); filterErr != nil {
|
||||
if errors.Is(filterErr, fs.SkipDir) {
|
||||
// signal to walk() to skip this directory entirely (even if we're processing a file)
|
||||
return "", filterErr
|
||||
@@ -258,24 +272,24 @@ func (r *directoryIndexer) indexPath(path string, info os.FileInfo, err error) (
|
||||
|
||||
if info == nil {
|
||||
// walk may not be able to provide a FileInfo object, don't allow for this to stop indexing; keep track of the paths and continue.
|
||||
r.errPaths[path] = fmt.Errorf("no file info observable at path=%q", path)
|
||||
r.errPaths[givenPath] = fmt.Errorf("no file info observable at path=%q", givenPath)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// here we check to see if we need to normalize paths to posix on the way in coming from windows
|
||||
if windows.HostRunningOnWindows() {
|
||||
path = windows.ToPosix(path)
|
||||
givenPath = windows.ToPosix(givenPath)
|
||||
}
|
||||
|
||||
newRoot, err := r.addPathToIndex(path, info)
|
||||
if r.isFileAccessErr(path, err) {
|
||||
newRoot, err := r.addPathToIndex(givenPath, info)
|
||||
if r.isFileAccessErr(givenPath, err) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return newRoot, nil
|
||||
}
|
||||
|
||||
func (r *directoryIndexer) disallowFileAccessErr(path string, _ os.FileInfo, err error) error {
|
||||
func (r *directoryIndexer) disallowFileAccessErr(_, path string, _ os.FileInfo, err error) error {
|
||||
if r.isFileAccessErr(path, err) {
|
||||
return ErrSkipPath
|
||||
}
|
||||
@@ -422,7 +436,7 @@ func (r directoryIndexer) hasBeenIndexed(p string) (bool, *file.Metadata) {
|
||||
return true, &entry.Metadata
|
||||
}
|
||||
|
||||
func (r *directoryIndexer) disallowRevisitingVisitor(path string, _ os.FileInfo, _ error) error {
|
||||
func (r *directoryIndexer) disallowRevisitingVisitor(_, path string, _ os.FileInfo, _ error) error {
|
||||
// this prevents visiting:
|
||||
// - link destinations twice, once for the real file and another through the virtual path
|
||||
// - infinite link cycles
|
||||
@@ -436,14 +450,58 @@ func (r *directoryIndexer) disallowRevisitingVisitor(path string, _ os.FileInfo,
|
||||
return nil
|
||||
}
|
||||
|
||||
func disallowUnixSystemRuntimePath(path string, _ os.FileInfo, _ error) error {
|
||||
if internal.HasAnyOfPrefixes(path, unixSystemRuntimePrefixes...) {
|
||||
type unixSystemMountFinder struct {
|
||||
disallowedMountPaths []string
|
||||
}
|
||||
|
||||
func newUnixSystemMountFinder() unixSystemMountFinder {
|
||||
infos, err := mountinfo.GetMounts(nil)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warnf("unable to get system mounts")
|
||||
return unixSystemMountFinder{}
|
||||
}
|
||||
|
||||
return unixSystemMountFinder{
|
||||
disallowedMountPaths: keepUnixSystemMountPaths(infos),
|
||||
}
|
||||
}
|
||||
|
||||
func keepUnixSystemMountPaths(infos []*mountinfo.Info) []string {
|
||||
var mountPaths []string
|
||||
for _, info := range infos {
|
||||
if info == nil {
|
||||
continue
|
||||
}
|
||||
// we're only interested in ignoring the logical filesystems typically found at these mount points:
|
||||
// - /proc
|
||||
// - procfs
|
||||
// - proc
|
||||
// - /sys
|
||||
// - sysfs
|
||||
// - /dev
|
||||
// - devfs - BSD/darwin flavored systems and old linux systems
|
||||
// - devtmpfs - driver core maintained /dev tmpfs
|
||||
// - udev - userspace implementation that replaced devfs
|
||||
// - tmpfs - used for /dev in special instances (within a container)
|
||||
|
||||
switch info.FSType {
|
||||
case "proc", "procfs", "sysfs", "devfs", "devtmpfs", "udev", "tmpfs":
|
||||
log.WithFields("mountpoint", info.Mountpoint).Debug("ignoring system mountpoint")
|
||||
|
||||
mountPaths = append(mountPaths, info.Mountpoint)
|
||||
}
|
||||
}
|
||||
return mountPaths
|
||||
}
|
||||
|
||||
func (f unixSystemMountFinder) disallowUnixSystemRuntimePath(_, path string, _ os.FileInfo, _ error) error {
|
||||
if internal.HasAnyOfPrefixes(path, f.disallowedMountPaths...) {
|
||||
return fs.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func disallowByFileType(_ string, info os.FileInfo, _ error) error {
|
||||
func disallowByFileType(_, _ string, info os.FileInfo, _ error) error {
|
||||
if info == nil {
|
||||
// we can't filter out by filetype for non-existent files
|
||||
return nil
|
||||
@@ -458,7 +516,7 @@ func disallowByFileType(_ string, info os.FileInfo, _ error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func requireFileInfo(_ string, info os.FileInfo, _ error) error {
|
||||
func requireFileInfo(_, _ string, info os.FileInfo, _ error) error {
|
||||
if info == nil {
|
||||
return ErrSkipPath
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -135,7 +137,7 @@ func TestDirectoryIndexer_handleFileAccessErr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_IncludeRootPathInIndex(t *testing.T) {
|
||||
filterFn := func(path string, _ os.FileInfo, _ error) error {
|
||||
filterFn := func(_, path string, _ os.FileInfo, _ error) error {
|
||||
if path != "/" {
|
||||
return fs.SkipDir
|
||||
}
|
||||
@@ -222,9 +224,21 @@ func TestDirectoryIndexer_index(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_index_survive_badSymlink(t *testing.T) {
|
||||
// test-fixtures/bad-symlinks
|
||||
// ├── root
|
||||
// │ ├── place
|
||||
// │ │ └── fd -> ../somewhere/self/fd
|
||||
// │ └── somewhere
|
||||
// ...
|
||||
indexer := newDirectoryIndexer("test-fixtures/bad-symlinks/root/place/fd", "test-fixtures/bad-symlinks/root/place/fd")
|
||||
_, _, err := indexer.build()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_SkipsAlreadyVisitedLinkDestinations(t *testing.T) {
|
||||
var observedPaths []string
|
||||
pathObserver := func(p string, _ os.FileInfo, _ error) error {
|
||||
pathObserver := func(_, p string, _ os.FileInfo, _ error) error {
|
||||
fields := strings.Split(p, "test-fixtures/symlinks-prune-indexing")
|
||||
if len(fields) < 2 {
|
||||
return nil
|
||||
@@ -383,3 +397,210 @@ func Test_allContainedPaths(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_relativePath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
basePath string
|
||||
givenPath string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "root: same relative path",
|
||||
basePath: "a/b/c",
|
||||
givenPath: "a/b/c",
|
||||
want: "/",
|
||||
},
|
||||
{
|
||||
name: "root: same absolute path",
|
||||
basePath: "/a/b/c",
|
||||
givenPath: "/a/b/c",
|
||||
want: "/",
|
||||
},
|
||||
{
|
||||
name: "contained path: relative",
|
||||
basePath: "a/b/c",
|
||||
givenPath: "a/b/c/dev",
|
||||
want: "/dev",
|
||||
},
|
||||
{
|
||||
name: "contained path: absolute",
|
||||
basePath: "/a/b/c",
|
||||
givenPath: "/a/b/c/dev",
|
||||
want: "/dev",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, relativePath(tt.basePath, tt.givenPath))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func relativePath(basePath, givenPath string) string {
|
||||
var relPath string
|
||||
var relErr error
|
||||
|
||||
if basePath != "" {
|
||||
relPath, relErr = filepath.Rel(basePath, givenPath)
|
||||
cleanPath := filepath.Clean(relPath)
|
||||
if relErr == nil {
|
||||
if cleanPath == "." {
|
||||
relPath = string(filepath.Separator)
|
||||
} else {
|
||||
relPath = cleanPath
|
||||
}
|
||||
}
|
||||
if !filepath.IsAbs(relPath) {
|
||||
relPath = string(filepath.Separator) + relPath
|
||||
}
|
||||
}
|
||||
|
||||
if relErr != nil || basePath == "" {
|
||||
relPath = givenPath
|
||||
}
|
||||
|
||||
return relPath
|
||||
}
|
||||
|
||||
func Test_disallowUnixSystemRuntimePath(t *testing.T) {
|
||||
unixSubject := unixSystemMountFinder{
|
||||
// mock out detecting the mount points
|
||||
disallowedMountPaths: []string{"/proc", "/sys", "/dev"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
base string
|
||||
expected error
|
||||
}{
|
||||
{
|
||||
name: "relative path to proc is allowed",
|
||||
path: "proc/place",
|
||||
},
|
||||
{
|
||||
name: "relative path within proc is not allowed",
|
||||
path: "/proc/place",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
name: "path exactly to proc is not allowed",
|
||||
path: "/proc",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
name: "similar to proc",
|
||||
path: "/pro/c",
|
||||
},
|
||||
{
|
||||
name: "similar to proc",
|
||||
path: "/pro",
|
||||
},
|
||||
{
|
||||
name: "dev is not allowed",
|
||||
path: "/dev",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
name: "sys is not allowed",
|
||||
path: "/sys",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
name: "unrelated allowed path",
|
||||
path: "/something/sys",
|
||||
},
|
||||
{
|
||||
name: "do not consider base when matching paths (non-matching)",
|
||||
base: "/a/b/c",
|
||||
path: "/a/b/c/dev",
|
||||
},
|
||||
{
|
||||
name: "do not consider base when matching paths (matching)",
|
||||
base: "/a/b/c",
|
||||
path: "/dev",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.path, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, unixSubject.disallowUnixSystemRuntimePath(test.base, test.path, nil, nil))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_keepUnixSystemMountPaths(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
infos []*mountinfo.Info
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "all valid filesystems",
|
||||
infos: []*mountinfo.Info{
|
||||
{
|
||||
Mountpoint: "/etc/hostname",
|
||||
FSType: "/dev/vda1",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/sys/fs/cgroup",
|
||||
FSType: "cgroup",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/",
|
||||
FSType: "overlay",
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "no valid filesystems",
|
||||
infos: []*mountinfo.Info{
|
||||
{
|
||||
Mountpoint: "/proc",
|
||||
FSType: "proc",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/proc-2",
|
||||
FSType: "procfs",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/sys",
|
||||
FSType: "sysfs",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/dev",
|
||||
FSType: "devfs",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/dev-u",
|
||||
FSType: "udev",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/dev-tmp",
|
||||
FSType: "devtmpfs",
|
||||
},
|
||||
{
|
||||
Mountpoint: "/run",
|
||||
FSType: "tmpfs",
|
||||
},
|
||||
},
|
||||
want: []string{
|
||||
"/proc",
|
||||
"/proc-2",
|
||||
"/sys",
|
||||
"/dev",
|
||||
"/dev-u",
|
||||
"/dev-tmp",
|
||||
"/run",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, keepUnixSystemMountPaths(tt.infos))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,7 +918,7 @@ func Test_isUnallowableFileType(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, disallowByFileType("dont/care", test.info, nil))
|
||||
assert.Equal(t, test.expected, disallowByFileType("", "dont/care", test.info, nil))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -999,7 +999,7 @@ func Test_IndexingNestedSymLinks(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_IndexingNestedSymLinks_ignoredIndexes(t *testing.T) {
|
||||
filterFn := func(path string, _ os.FileInfo, _ error) error {
|
||||
filterFn := func(_, path string, _ os.FileInfo, _ error) error {
|
||||
if strings.HasSuffix(path, string(filepath.Separator)+"readme") {
|
||||
return ErrSkipPath
|
||||
}
|
||||
@@ -1108,47 +1108,6 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isUnixSystemRuntimePath(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
expected error
|
||||
}{
|
||||
{
|
||||
path: "proc/place",
|
||||
},
|
||||
{
|
||||
path: "/proc/place",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
path: "/proc",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
path: "/pro/c",
|
||||
},
|
||||
{
|
||||
path: "/pro",
|
||||
},
|
||||
{
|
||||
path: "/dev",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
path: "/sys",
|
||||
expected: fs.SkipDir,
|
||||
},
|
||||
{
|
||||
path: "/something/sys",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.path, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, disallowUnixSystemRuntimePath(test.path, nil, nil))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SymlinkLoopWithGlobsShouldResolve(t *testing.T) {
|
||||
test := func(t *testing.T) {
|
||||
resolver, err := NewFromDirectory("./test-fixtures/symlinks-loop", "")
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../somewhere/self/fd
|
||||
@@ -232,7 +232,7 @@ func (u UnindexedDirectory) AllLocations(ctx context.Context) <-chan file.Locati
|
||||
errWalkCanceled := fmt.Errorf("walk canceled")
|
||||
go func() {
|
||||
defer close(out)
|
||||
err := afero.Walk(u.fs, u.absPath("."), func(p string, info fs.FileInfo, err error) error {
|
||||
err := afero.Walk(u.fs, u.absPath("."), func(p string, _ fs.FileInfo, _ error) error {
|
||||
p = strings.TrimPrefix(p, u.dir)
|
||||
if p == "" {
|
||||
return nil
|
||||
|
||||
@@ -46,6 +46,7 @@ func AllTypes() []any {
|
||||
pkg.RustBinaryAuditEntry{},
|
||||
pkg.RustCargoLockEntry{},
|
||||
pkg.SwiftPackageManagerResolvedEntry{},
|
||||
pkg.WordpressPluginEntry{},
|
||||
pkg.YarnLockEntry{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.SwiftPackageManagerResolvedEntry{}, "swift-package-manager-lock-entry", "SwiftPackageManagerMetadata"),
|
||||
jsonNames(pkg.RustCargoLockEntry{}, "rust-cargo-lock-entry", "RustCargoPackageMetadata"),
|
||||
jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred
|
||||
jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"),
|
||||
)
|
||||
|
||||
func expandLegacyNameVariants(names ...string) []string {
|
||||
|
||||
@@ -6,5 +6,5 @@ import "github.com/anchore/syft/syft/source"
|
||||
|
||||
// AllTypes returns a list of all source metadata types that syft supports (that are represented in the source.Description.Metadata field).
|
||||
func AllTypes() []any {
|
||||
return []any{source.DirectorySourceMetadata{}, source.FileSourceMetadata{}, source.StereoscopeImageSourceMetadata{}}
|
||||
return []any{source.DirectoryMetadata{}, source.FileMetadata{}, source.ImageMetadata{}}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
)
|
||||
|
||||
var jsonNameFromType = map[reflect.Type][]string{
|
||||
reflect.TypeOf(source.DirectorySourceMetadata{}): {"directory", "dir"},
|
||||
reflect.TypeOf(source.FileSourceMetadata{}): {"file"},
|
||||
reflect.TypeOf(source.StereoscopeImageSourceMetadata{}): {"image"},
|
||||
reflect.TypeOf(source.DirectoryMetadata{}): {"directory", "dir"},
|
||||
reflect.TypeOf(source.FileMetadata{}): {"file"},
|
||||
reflect.TypeOf(source.ImageMetadata{}): {"image"},
|
||||
}
|
||||
|
||||
func AllTypeNames() []string {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Chdir(t *testing.T, dir string) {
|
||||
t.Helper()
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get working directory: %v", err)
|
||||
}
|
||||
|
||||
err = os.Chdir(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to chdir to '%s': %v", dir, err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.Chdir(wd))
|
||||
})
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
// unionReader is a single interface with all reading functions needed by multi-arch binary catalogers
|
||||
// UnionReader is a single interface with all reading functions needed by multi-arch binary catalogers
|
||||
// cataloger.
|
||||
type UnionReader interface {
|
||||
io.Reader
|
||||
@@ -18,7 +18,7 @@ type UnionReader interface {
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// getReaders extracts one or more io.ReaderAt objects representing binaries that can be processed (multiple binaries in the case for multi-architecture binaries).
|
||||
// GetReaders extracts one or more io.ReaderAt objects representing binaries that can be processed (multiple binaries in the case for multi-architecture binaries).
|
||||
func GetReaders(f UnionReader) ([]io.ReaderAt, error) {
|
||||
if macho.IsUniversalMachoBinary(f) {
|
||||
machoReaders, err := macho.ExtractReaders(f)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
)
|
||||
|
||||
func TestIdentifyRelease(t *testing.T) {
|
||||
@@ -336,7 +337,9 @@ func TestIdentifyRelease(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
s, err := source.NewFromDirectoryPath(test.fixture)
|
||||
s, err := directorysource.New(directorysource.Config{
|
||||
Path: test.fixture,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err := s.FileResolver(source.SquashedScope)
|
||||
|
||||
@@ -13,25 +13,25 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
const catalogerName = "binary-cataloger"
|
||||
const catalogerName = "binary-classifier-cataloger"
|
||||
|
||||
type CatalogerConfig struct {
|
||||
type ClassifierCatalogerConfig struct {
|
||||
Classifiers []Classifier `yaml:"classifiers" json:"classifiers" mapstructure:"classifiers"`
|
||||
}
|
||||
|
||||
func DefaultCatalogerConfig() CatalogerConfig {
|
||||
return CatalogerConfig{
|
||||
func DefaultClassifierCatalogerConfig() ClassifierCatalogerConfig {
|
||||
return ClassifierCatalogerConfig{
|
||||
Classifiers: DefaultClassifiers(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewCataloger(cfg CatalogerConfig) pkg.Cataloger {
|
||||
func NewClassifierCataloger(cfg ClassifierCatalogerConfig) pkg.Cataloger {
|
||||
return &cataloger{
|
||||
classifiers: cfg.Classifiers,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg CatalogerConfig) MarshalJSON() ([]byte, error) {
|
||||
func (cfg ClassifierCatalogerConfig) MarshalJSON() ([]byte, error) {
|
||||
// only keep the class names
|
||||
var names []string
|
||||
for _, cls := range cfg.Classifiers {
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/testutil"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
)
|
||||
|
||||
var mustUseOriginalBinaries = flag.Bool("must-use-original-binaries", false, "force the use of binaries for testing (instead of snippets)")
|
||||
@@ -862,6 +864,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("openssl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "openssl/1.1.1w/linux-arm64",
|
||||
expected: pkg.Package{
|
||||
Name: "openssl",
|
||||
Version: "1.1.1w",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/openssl@1.1.1w",
|
||||
Locations: locations("openssl"),
|
||||
Metadata: metadata("openssl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "gcc/12.3.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -888,7 +901,7 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.logicalFixture, func(t *testing.T) {
|
||||
c := NewCataloger(DefaultCatalogerConfig())
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
// logicalFixture is the logical path to the full binary or snippet. This is relative to the test-fixtures/classifiers/snippets
|
||||
// or test-fixtures/classifiers/bin directory . Snippets are searched for first, and if not found, then existing binaries are
|
||||
@@ -896,7 +909,7 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
// full binaries are tested (no snippets), and if no binary is found the test will be skipped.
|
||||
path := testutil.SnippetOrBinary(t, test.logicalFixture, *mustUseOriginalBinaries)
|
||||
|
||||
src, err := source.NewFromDirectoryPath(path)
|
||||
src, err := directorysource.NewFromPath(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
@@ -933,11 +946,12 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := NewCataloger(DefaultCatalogerConfig())
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage)
|
||||
src, err := source.NewFromStereoscopeImageObject(img, test.fixtureImage, nil)
|
||||
require.NoError(t, err)
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: test.fixtureImage,
|
||||
})
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
@@ -964,9 +978,9 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
||||
c := NewCataloger(DefaultCatalogerConfig())
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
src, err := source.NewFromDirectoryPath("test-fixtures/classifiers/negative")
|
||||
src, err := directorysource.NewFromPath("test-fixtures/classifiers/negative")
|
||||
assert.NoError(t, err)
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
@@ -1006,13 +1020,13 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
config CatalogerConfig
|
||||
config ClassifierCatalogerConfig
|
||||
fixtureDir string
|
||||
expected *pkg.Package
|
||||
}{
|
||||
{
|
||||
name: "empty-negative",
|
||||
config: CatalogerConfig{
|
||||
config: ClassifierCatalogerConfig{
|
||||
Classifiers: []Classifier{},
|
||||
},
|
||||
fixtureDir: "test-fixtures/custom/go-1.14",
|
||||
@@ -1020,7 +1034,7 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "default-positive",
|
||||
config: CatalogerConfig{
|
||||
config: ClassifierCatalogerConfig{
|
||||
Classifiers: defaultClassifers,
|
||||
},
|
||||
fixtureDir: "test-fixtures/custom/go-1.14",
|
||||
@@ -1028,7 +1042,7 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "nodefault-negative",
|
||||
config: CatalogerConfig{
|
||||
config: ClassifierCatalogerConfig{
|
||||
Classifiers: []Classifier{fooClassifier},
|
||||
},
|
||||
fixtureDir: "test-fixtures/custom/go-1.14",
|
||||
@@ -1036,7 +1050,7 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "default-extended-positive",
|
||||
config: CatalogerConfig{
|
||||
config: ClassifierCatalogerConfig{
|
||||
Classifiers: append(
|
||||
append([]Classifier{}, defaultClassifers...),
|
||||
fooClassifier,
|
||||
@@ -1047,7 +1061,7 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "default-custom-negative",
|
||||
config: CatalogerConfig{
|
||||
config: ClassifierCatalogerConfig{
|
||||
|
||||
Classifiers: append(
|
||||
append([]Classifier{}, defaultClassifers...),
|
||||
@@ -1066,7 +1080,7 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "default-cutsom-positive",
|
||||
config: CatalogerConfig{
|
||||
config: ClassifierCatalogerConfig{
|
||||
Classifiers: append(
|
||||
append([]Classifier{}, defaultClassifers...),
|
||||
fooClassifier,
|
||||
@@ -1078,9 +1092,9 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := NewCataloger(test.config)
|
||||
c := NewClassifierCataloger(test.config)
|
||||
|
||||
src, err := source.NewFromDirectoryPath(test.fixtureDir)
|
||||
src, err := directorysource.NewFromPath(test.fixtureDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
@@ -1249,7 +1263,7 @@ func (p *panicyResolver) FileMetadataByLocation(_ file.Location) (file.Metadata,
|
||||
var _ file.Resolver = (*panicyResolver)(nil)
|
||||
|
||||
func Test_Cataloger_ResilientToErrors(t *testing.T) {
|
||||
c := NewCataloger(DefaultCatalogerConfig())
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
resolver := &panicyResolver{}
|
||||
_, _, err := c.Catalog(context.Background(), resolver)
|
||||
@@ -1261,13 +1275,13 @@ func TestCatalogerConfig_MarshalJSON(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg CatalogerConfig
|
||||
cfg ClassifierCatalogerConfig
|
||||
want string
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "only show names of classes",
|
||||
cfg: CatalogerConfig{
|
||||
cfg: ClassifierCatalogerConfig{
|
||||
Classifiers: []Classifier{
|
||||
{
|
||||
Class: "class",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user