Compare commits

...
Author SHA1 Message Date
Christopher Phillips 66bf43c0cc test: java binary skip for apple Silicon
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-08-26 17:57:21 -04:00
anchore-actions-token-generator[bot]andspiffcs 2d8e337d34 chore(deps): update anchore dependencies (#4169)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-08-26 17:04:20 -04:00
Christopher Angelo PhillipsandAlex Goodman 13ffeeb3d0 feat: combine go module file and go source discovery into single cataloger (#4127)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-08-26 19:35:44 +00:00
n-bes 170c4c41f4 use go.yaml.in/yaml (#4157)
Signed-off-by: Nikita Besperstov <n.bes@pm.me>
2025-08-26 11:24:23 -04:00
dependabot[bot] 7dc7c01c5c chore(deps): bump github.com/diskfs/go-diskfs (#4159)
Bumps [github.com/diskfs/go-diskfs](https://github.com/diskfs/go-diskfs) from 1.6.1-0.20250601133945-2af1c7ece24c to 1.7.0.
- [Commits](https://github.com/diskfs/go-diskfs/commits/v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/diskfs/go-diskfs
  dependency-version: 1.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-26 09:00:02 -04:00
dependabot[bot] 9f07fa4a68 chore(deps): bump github.com/stretchr/testify from 1.10.0 to 1.11.0 (#4160)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-version: 1.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-26 08:59:49 -04:00
anchore-actions-token-generator[bot]andspiffcs 37b2c0391b chore(deps): update tools to latest versions (#4154)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-08-25 09:57:45 -04:00
Alan Pope ada74a8121 Feature: Add ffmpeg binary cataloger (#3994)
* Add ffmpeg binary cataloger

Signed-off-by: Alan Pope <alan.pope@anchore.com>

* add linux-amd64 snippet and test

Signed-off-by: Alan Pope <alan.pope@anchore.com>

* Widen scope of regex to two digit version numbers

Signed-off-by: Alan Pope <alan.pope@anchore.com>

* Add full test fixtures for ffmpeg

Signed-off-by: Alan Pope <alan.pope@anchore.com>

---------

Signed-off-by: Alan Pope <alan.pope@anchore.com>
2025-08-25 07:50:04 -04:00
Keith Zantow ca21ccf21d chore: redhat cataloger error when sqlite not regsitered (#4150)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-08-21 14:55:47 +00:00
dependabot[bot] 26792fc12d chore(deps): bump github/codeql-action from 3.29.10 to 3.29.11 (#4149)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.10 to 3.29.11.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/96f518a34f7a870018057716cc4d7a5c014bd61c...3c3833e0f8c1c83d449a7478aa59c036a9165498)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-21 10:43:25 -04:00
Simeon StoykovandChristopher Phillips a433045d51 feat: basic Conda ecosystem support (#4002)
----------------------------------------------------------------
Signed-off-by: Simeon Stoykov <simeon.stoykov@quantco.com>
Signed-off-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-08-19 22:37:27 -04:00
dependabot[bot] 8e51e8d995 chore(deps): bump github/codeql-action from 3.29.9 to 3.29.10 (#4145)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.9 to 3.29.10.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/df559355d593797519d70b90fc8edd5db049e7a2...96f518a34f7a870018057716cc4d7a5c014bd61c)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 15:29:36 -04:00
anchore-actions-token-generator[bot]andwagoodman ba2eb5701f chore(deps): update CPE dictionary index (#4143)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-08-18 10:14:39 -04:00
dependabot[bot] c4292ad79b chore(deps): bump github.com/hashicorp/go-getter from 1.7.8 to 1.7.9 (#4144)
Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.7.8 to 1.7.9.
- [Release notes](https://github.com/hashicorp/go-getter/releases)
- [Changelog](https://github.com/hashicorp/go-getter/blob/main/.goreleaser.yml)
- [Commits](https://github.com/hashicorp/go-getter/compare/v1.7.8...v1.7.9)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-getter
  dependency-version: 1.7.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 11:42:13 +01:00
dependabot[bot] 0e669faecd chore(deps): bump anchore/sbom-action from 0.20.4 to 0.20.5 (#4141)
Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.20.4 to 0.20.5.
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/sbom-action/compare/7b36ad622f042cab6f59a75c2ac24ccb256e9b45...da167eac915b4e86f08b264dbdbc867b61be6f0c)

---
updated-dependencies:
- dependency-name: anchore/sbom-action
  dependency-version: 0.20.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 10:22:24 -04:00
anchore-actions-token-generator[bot]andspiffcs 10ea022fe7 chore(deps): update tools to latest versions (#4139)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-08-14 13:34:24 -04:00
Alan PopeandChristopher Phillips 87e1d8cb87 feat: add support for authors, maintainers, and contributors in package.json. (#4003)
Fixes #2250
---------
Signed-off-by: Alan Pope <alan.pope@anchore.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-08-13 17:55:15 -04:00
80 changed files with 9022 additions and 134 deletions
+2 -2
View File
@@ -26,7 +26,7 @@ tools:
# used for linting
- name: golangci-lint
version:
want: v2.3.1
want: v2.4.0
method: github-release
with:
repo: golangci/golangci-lint
@@ -98,7 +98,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.76.2
want: v2.78.0
method: github-release
with:
repo: cli/cli
+3 -3
View File
@@ -47,7 +47,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 #v3.29.5
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 #v3.29.5
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -58,7 +58,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@df559355d593797519d70b90fc8edd5db049e7a2 #v3.29.5
uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 #v3.29.5
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -72,4 +72,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 #v3.29.5
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 #v3.29.5
+1 -1
View File
@@ -161,7 +161,7 @@ jobs:
# for updating brew formula in anchore/homebrew-syft
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
- uses: anchore/sbom-action@7b36ad622f042cab6f59a75c2ac24ccb256e9b45 #v0.20.4
- uses: anchore/sbom-action@da167eac915b4e86f08b264dbdbc867b61be6f0c #v0.20.5
continue-on-error: true
with:
file: go.mod
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/clio"
"github.com/anchore/fangs"
@@ -87,6 +87,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
definedPkgs.Remove(string(pkg.TerraformPkg))
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // we have coverage for pear instead
definedPkgs.Remove(string(pkg.CondaPkg))
var cases []testCase
cases = append(cases, commonTestCases...)
@@ -159,6 +160,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
definedPkgs.Remove(string(pkg.Rpkg))
definedPkgs.Remove(string(pkg.UnknownPkg))
definedPkgs.Remove(string(pkg.CondaPkg))
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // this is covered as pear packages
// for directory scans we should not expect to see any of the following package types
+2 -1
View File
@@ -6,7 +6,8 @@ import (
"fmt"
"os"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
_ "modernc.org/sqlite" // required for rpmdb and other features
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/cataloging"
+2
View File
@@ -5,6 +5,8 @@ import (
"fmt"
"os"
_ "modernc.org/sqlite" // required for rpmdb and other features
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/format"
"github.com/anchore/syft/syft/format/syftjson"
+2
View File
@@ -5,6 +5,8 @@ import (
"encoding/json"
"os"
_ "modernc.org/sqlite" // required for rpmdb and other features
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
+2
View File
@@ -4,6 +4,8 @@ import (
"context"
"os"
_ "modernc.org/sqlite" // required for rpmdb and other features
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/format/syftjson"
)
+10 -10
View File
@@ -24,7 +24,7 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
github.com/anchore/stereoscope v0.1.8
github.com/anchore/stereoscope v0.1.9-0.20250826202322-ef061ea78385
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
@@ -35,7 +35,7 @@ require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/dave/jennifer v1.7.1
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c
github.com/diskfs/go-diskfs v1.7.0
github.com/distribution/reference v0.6.0
github.com/dustin/go-humanize v1.0.1
github.com/elliotchance/phpserialize v1.4.0
@@ -53,7 +53,7 @@ require (
github.com/google/uuid v1.6.0
github.com/gookit/color v1.5.4
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-getter v1.7.8
github.com/hashicorp/go-getter v1.7.9
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/hcl/v2 v2.24.0
github.com/iancoleman/strcase v0.3.0
@@ -80,7 +80,7 @@ require (
github.com/spdx/tools-golang v0.5.5
github.com/spf13/afero v1.14.0
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.11.0
github.com/vbatts/go-mtree v0.5.4
github.com/vifraa/gopom v1.0.0
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
@@ -88,10 +88,10 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
golang.org/x/mod v0.27.0
golang.org/x/net v0.43.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.38.2
)
@@ -132,7 +132,7 @@ require (
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.7.27 // indirect
github.com/containerd/containerd v1.7.28 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
github.com/containerd/continuity v0.4.4 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
@@ -149,7 +149,7 @@ require (
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.3.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
@@ -200,7 +200,6 @@ require (
github.com/minio/minlz v1.0.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
@@ -274,8 +273,8 @@ require (
golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.35.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.35.0
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/api v0.203.0 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
@@ -284,6 +283,7 @@ require (
google.golang.org/grpc v1.67.3 // indirect
google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
+16 -16
View File
@@ -694,8 +694,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.1.8 h1:K1NRFUqGy6z/jp4ADHNXZWbZ+xd6fPr0Kn4gYp1zm1c=
github.com/anchore/stereoscope v0.1.8/go.mod h1:VA9zyFcUzN7GIFsXfe8lj3Z6Ocs4CP5QZqbmFc1I7ag=
github.com/anchore/stereoscope v0.1.9-0.20250826202322-ef061ea78385 h1:icCqbvAKGZXf29lEi8JmwvHVCBCYkiyZMuSnk+5ajYo=
github.com/anchore/stereoscope v0.1.9-0.20250826202322-ef061ea78385/go.mod h1:0UCjLz5MdPNiH9F0h2tSNf3yGF6/MnK8ZCPo0YfDQVc=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
@@ -812,8 +812,8 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII=
github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0=
github.com/containerd/containerd v1.7.28 h1:Nsgm1AtcmEh4AHAJ4gGlNSaKgXiNccU270Dnf81FQ3c=
github.com/containerd/containerd v1.7.28/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
@@ -851,8 +851,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c h1:Vg+RNk+3Kwbe3wUYsgXsk43+7oyOJ5tRDzOrcpV40yQ=
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
@@ -865,8 +865,8 @@ github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjY
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -1133,8 +1133,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.7.8 h1:mshVHx1Fto0/MydBekWan5zUipGq7jO0novchgMmSiY=
github.com/hashicorp/go-getter v1.7.8/go.mod h1:2c6CboOEb9jG6YvmC9xdD+tyAFsrUaJPedwXDGr0TM4=
github.com/hashicorp/go-getter v1.7.9 h1:G9gcjrDixz7glqJ+ll5IWvggSBR+R0B54DSRt4qfdC4=
github.com/hashicorp/go-getter v1.7.9/go.mod h1:dyFCmT1AQkDfOIt9NH8pw9XBDqNrIKJT5ylbpi7zPNE=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -1295,8 +1295,6 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -1509,8 +1507,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
@@ -1623,6 +1621,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1982,8 +1982,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+1 -1
View File
@@ -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.37"
JSONSchemaVersion = "16.0.39"
)
+2
View File
@@ -7,6 +7,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/arch"
"github.com/anchore/syft/syft/pkg/cataloger/binary"
bitnamiSbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/bitnami"
"github.com/anchore/syft/syft/pkg/cataloger/conda"
"github.com/anchore/syft/syft/pkg/cataloger/cpp"
"github.com/anchore/syft/syft/pkg/cataloger/dart"
"github.com/anchore/syft/syft/pkg/cataloger/debian"
@@ -171,6 +172,7 @@ func DefaultPackageTaskFactories() Factories {
newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"),
newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"),
newSimplePackageTaskFactory(homebrew.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "homebrew"),
newSimplePackageTaskFactory(conda.NewCondaMetaCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.PackageTag, "conda"),
// deprecated catalogers ////////////////////////////////////////
// these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+170 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.37/document",
"$id": "anchore.io/schema/syft/json/16.0.39/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -394,6 +394,146 @@
"checksum"
]
},
"CondaLink": {
"properties": {
"source": {
"type": "string"
},
"type": {
"type": "integer"
}
},
"type": "object",
"required": [
"source",
"type"
]
},
"CondaMetadataEntry": {
"properties": {
"arch": {
"type": "string"
},
"name": {
"type": "string"
},
"version": {
"type": "string"
},
"build": {
"type": "string"
},
"build_number": {
"type": "integer"
},
"channel": {
"type": "string"
},
"subdir": {
"type": "string"
},
"noarch": {
"type": "string"
},
"license": {
"type": "string"
},
"license_family": {
"type": "string"
},
"md5": {
"type": "string"
},
"sha256": {
"type": "string"
},
"size": {
"type": "integer"
},
"timestamp": {
"type": "integer"
},
"fn": {
"type": "string"
},
"url": {
"type": "string"
},
"extracted_package_dir": {
"type": "string"
},
"depends": {
"items": {
"type": "string"
},
"type": "array"
},
"files": {
"items": {
"type": "string"
},
"type": "array"
},
"paths_data": {
"$ref": "#/$defs/CondaPathsData"
},
"link": {
"$ref": "#/$defs/CondaLink"
}
},
"type": "object",
"required": [
"name",
"version",
"build",
"build_number"
]
},
"CondaPathData": {
"properties": {
"_path": {
"type": "string"
},
"path_type": {
"type": "string"
},
"sha256": {
"type": "string"
},
"sha256_in_prefix": {
"type": "string"
},
"size_in_bytes": {
"type": "integer"
}
},
"type": "object",
"required": [
"_path",
"path_type",
"sha256",
"sha256_in_prefix",
"size_in_bytes"
]
},
"CondaPathsData": {
"properties": {
"paths_version": {
"type": "integer"
},
"paths": {
"items": {
"$ref": "#/$defs/CondaPathData"
},
"type": "array"
}
},
"type": "object",
"required": [
"paths_version",
"paths"
]
},
"Coordinates": {
"properties": {
"path": {
@@ -1096,6 +1236,29 @@
},
"type": "object"
},
"GoSourceEntry": {
"properties": {
"h1Digest": {
"type": "string"
},
"os": {
"type": "string"
},
"architecture": {
"type": "string"
},
"buildTags": {
"type": "string"
},
"cgoEnabled": {
"type": "boolean"
}
},
"type": "object",
"required": [
"cgoEnabled"
]
},
"HaskellHackageStackEntry": {
"properties": {
"pkgHash": {
@@ -1893,6 +2056,9 @@
{
"$ref": "#/$defs/CocoaPodfileLockEntry"
},
{
"$ref": "#/$defs/CondaMetadataEntry"
},
{
"$ref": "#/$defs/DartPubspec"
},
@@ -1932,6 +2098,9 @@
{
"$ref": "#/$defs/GoModuleEntry"
},
{
"$ref": "#/$defs/GoSourceEntry"
},
{
"$ref": "#/$defs/HaskellHackageStackEntry"
},
@@ -18,6 +18,7 @@ func Test_OriginatorSupplier(t *testing.T) {
pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently)
pkg.ConanfileEntry{},
pkg.ConaninfoEntry{},
pkg.CondaMetaPackage{},
pkg.DartPubspecLockEntry{},
pkg.DartPubspec{},
pkg.DotnetDepsEntry{},
@@ -27,6 +28,7 @@ func Test_OriginatorSupplier(t *testing.T) {
pkg.ErlangRebarLockEntry{},
pkg.GolangBinaryBuildinfoEntry{},
pkg.GolangModuleEntry{},
pkg.GolangSourceEntry{},
pkg.HomebrewFormula{},
pkg.HackageStackYamlLockEntry{},
pkg.HackageStackYamlEntry{},
@@ -48,6 +48,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from installed cocoapods manifest file"
case pkg.ConanPkg:
answer = "acquired package info from conan manifest"
case pkg.CondaPkg:
answer = "acquired package info from conda metadata"
case pkg.PortagePkg:
answer = "acquired package info from portage DB"
case pkg.HackagePkg:
@@ -191,6 +191,14 @@ func Test_SourceInfo(t *testing.T) {
"from conan manifest",
},
},
{
input: pkg.Package{
Type: pkg.CondaPkg,
},
expected: []string{
"from conda metadata",
},
},
{
input: pkg.Package{
Type: pkg.PortagePkg,
@@ -16,6 +16,7 @@ func AllTypes() []any {
pkg.ConanV2LockEntry{},
pkg.ConanfileEntry{},
pkg.ConaninfoEntry{},
pkg.CondaMetaPackage{},
pkg.DartPubspec{},
pkg.DartPubspecLockEntry{},
pkg.DotnetDepsEntry{},
@@ -29,6 +30,7 @@ func AllTypes() []any {
pkg.GitHubActionsUseStatement{},
pkg.GolangBinaryBuildinfoEntry{},
pkg.GolangModuleEntry{},
pkg.GolangSourceEntry{},
pkg.HackageStackYamlEntry{},
pkg.HackageStackYamlLockEntry{},
pkg.HomebrewFormula{},
+2
View File
@@ -82,6 +82,7 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.GitHubActionsUseStatement{}, "github-actions-use-statement"),
jsonNames(pkg.GolangBinaryBuildinfoEntry{}, "go-module-buildinfo-entry", "GolangBinMetadata", "GolangMetadata"),
jsonNames(pkg.GolangModuleEntry{}, "go-module-entry", "GolangModMetadata"),
jsonNames(pkg.GolangSourceEntry{}, "go-source-entry"),
jsonNames(pkg.HackageStackYamlLockEntry{}, "haskell-hackage-stack-lock-entry", "HackageMetadataType"),
jsonNamesWithoutLookup(pkg.HackageStackYamlEntry{}, "haskell-hackage-stack-entry", "HackageMetadataType"), // the legacy value is split into two types, where the other is preferred
jsonNames(pkg.JavaArchive{}, "java-archive", "JavaMetadata"),
@@ -119,6 +120,7 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.LuaRocksPackage{}, "luarocks-package"),
jsonNames(pkg.TerraformLockProviderEntry{}, "terraform-lock-provider-entry"),
jsonNames(pkg.DotnetPackagesLockEntry{}, "dotnet-packages-lock-entry"),
jsonNames(pkg.CondaMetaPackage{}, "conda-metadata-entry", "CondaPackageMetadata"),
)
func expandLegacyNameVariants(names ...string) []string {
@@ -347,6 +347,12 @@ func Test_JSONName_JSONLegacyName(t *testing.T) {
expectedJSONName: "go-module-entry",
expectedLegacyName: "GolangModMetadata",
},
{
name: "GolangSourceMetadata",
metadata: pkg.GolangSourceEntry{},
expectedJSONName: "go-source-entry",
expectedLegacyName: "go-source-entry",
},
{
name: "HackageStackYamlLockMetadata",
metadata: pkg.HackageStackYamlLockEntry{},
@@ -1370,6 +1370,28 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("chrome-binary"),
},
},
{
logicalFixture: "ffmpeg/7.1.1/darwin-arm64",
expected: pkg.Package{
Name: "ffmpeg",
Version: "7.1.1",
Type: "binary",
PURL: "pkg:generic/ffmpeg@7.1.1",
Locations: locations("ffmpeg"),
Metadata: metadata("ffmpeg-binary"),
},
},
{
logicalFixture: "ffmpeg/6.1.1/linux-amd64",
expected: pkg.Package{
Name: "ffmpeg",
Version: "6.1.1",
Type: "binary",
PURL: "pkg:generic/ffmpeg@6.1.1",
Locations: locations("ffmpeg"),
Metadata: metadata("ffmpeg-binary"),
},
},
}
for _, test := range tests {
+12
View File
@@ -627,6 +627,18 @@ func DefaultClassifiers() []binutils.Classifier {
PURL: mustPURL("pkg:generic/chrome@version"),
CPEs: singleCPE("cpe:2.3:a:google:chrome:*:*:*:*:*:*:*:*"),
},
{
Class: "ffmpeg-binary",
FileGlob: "**/ffmpeg",
EvidenceMatcher: m.FileContentsVersionMatcher(
// Pattern found in the binary: "%s version 7.1.1" or "%s version 6.0"
// When executed outputs: "ffmpeg version 7.1.1" or "ffmpeg version 6.0"
`(?m)%s version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)`,
),
Package: "ffmpeg",
PURL: mustPURL("pkg:generic/ffmpeg@version"),
CPEs: singleCPE("cpe:2.3:a:ffmpeg:ffmpeg:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
}
return append(classifiers, defaultJavaClassifiers()...)
@@ -1,12 +1,18 @@
package binary
import (
"runtime"
"testing"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func Test_JavaBinaryImage(t *testing.T) {
// no match for platform in manifest on Apple Silicon
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
t.Skip("Java binary images not supported on Apple Silicon (darwin/arm64)")
}
tests := []struct {
image string
expected []string
@@ -810,3 +810,23 @@ from-images:
platform: linux/amd64
paths:
- /usr/local/bin/jq
- name: ffmpeg
version: 8.0
images:
- ref: linuxserver/ffmpeg:version-8.0-cli@sha256:9d7e80710e7f11a276a4185f07e5e81db26fc7027ea70d1591f93ddf58e36ab4
platform: linux/amd64
- ref: linuxserver/ffmpeg:arm64v8-version-8.0-cli@sha256:0b6cf2e15a78094396f23a7cb3afaf7dffcad195573a2b97f190eae1ee1a0420
platform: linux/arm64
paths:
- /usr/local/bin/ffmpeg
- name: ffmpeg
version: 7.1.1
images:
- ref: linuxserver/ffmpeg:7.1.1@sha256:aea59a11c54291ac456bb2d67000445e5a8994f70bc3d96cdc29f022fbbf89fb
platform: linux/amd64
- ref: linuxserver/ffmpeg:arm64v8-7.1.1@sha256:b2aa507f205ea2dc32369c55d0f93b5649f1b39e42d9e6cd27bd53def7a63655
platform: linux/arm64
paths:
- /usr/local/bin/ffmpeg
@@ -11,7 +11,7 @@ import (
"strings"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/internal"
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/internal/config"
@@ -8,7 +8,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/scylladb/go-set/strset"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
)
const Path = "config.yaml"
@@ -6,7 +6,7 @@ import (
"strings"
"github.com/OneOfOne/xxhash"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
)
type BinaryFromImage struct {
@@ -5,7 +5,7 @@ import (
"os"
"strings"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
)
type SnippetMetadata struct {
+46
View File
@@ -0,0 +1,46 @@
/*
Package conda provides a concrete Cataloger implementation for packages within the Conda ecosystem.
*/
package conda
import (
"context"
"encoding/json"
"fmt"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
// NewCondaMetaCataloger returns a new cataloger object for Conda environments by parsing the package metadata files in conda-meta.
func NewCondaMetaCataloger() pkg.Cataloger {
return generic.NewCataloger("conda-meta-cataloger").
WithParserByGlobs(parseCondaMetaJSON, "**/conda-meta/*.json")
}
func parseCondaMetaJSON(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
dec := json.NewDecoder(reader)
var meta pkg.CondaMetaPackage
if err := dec.Decode(&meta); err != nil {
return nil, nil, fmt.Errorf("failed to parse conda-meta package file at %s: %w", reader.RealPath, err)
}
p := pkg.Package{
Name: meta.Name,
Version: meta.Version,
Locations: file.NewLocationSet(reader.Location),
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, meta.License, reader.Location),
),
Language: pkg.UnknownLanguage,
Type: pkg.CondaPkg,
Metadata: meta,
}
p.SetID()
return []pkg.Package{
p,
}, nil, nil
}
+250
View File
@@ -0,0 +1,250 @@
package conda
import (
"context"
"strings"
"testing"
"github.com/go-test/deep"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func Test_CondaCataloger(t *testing.T) {
ctx := context.TODO()
tests := []struct {
name string
fixture string
expectedPackages []pkg.Package
wantErr require.ErrorAssertionFunc
}{
{
name: "multiple packages in conda meta (python, c binaries, ...)",
fixture: "test-fixtures/conda-meta-python-c-etc",
wantErr: require.NoError,
expectedPackages: []pkg.Package{
{
Name: "jupyterlab",
Version: "4.4.3",
FoundBy: "conda-meta-cataloger",
Locations: file.NewLocationSet(
file.NewLocation("conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json"),
),
Language: pkg.UnknownLanguage,
Type: pkg.CondaPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "BSD-3-Clause", file.NewLocation("conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json")),
),
Metadata: pkg.CondaMetaPackage{
Name: "jupyterlab",
Version: "4.4.3",
Build: "pyhd8ed1ab_0",
BuildNumber: 0,
Channel: "https://conda.anaconda.org/conda-forge/",
Subdir: "noarch",
Noarch: "python",
License: "BSD-3-Clause",
LicenseFamily: "BSD",
MD5: "4861a0c2a5a5d0481a450a9dfaf9febe",
SHA256: "fc0235a71d852734fe92183a78cb91827367573450eba82465ae522c64230736",
Size: 8236973,
Timestamp: 1748273017680,
Filename: "jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
URL: "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
ExtractedPackageDir: "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
Depends: []string{
"async-lru >=1.0.0",
"httpx >=0.25.0",
"importlib-metadata >=4.8.3",
"ipykernel >=6.5.0",
"jinja2 >=3.0.3",
"jupyter-lsp >=2.0.0",
"jupyter_core",
"jupyter_server >=2.4.0,<3",
"jupyterlab_server >=2.27.1,<3",
"notebook-shim >=0.2",
"packaging",
"python >=3.9",
"setuptools >=41.1.0",
"tomli >=1.2.2",
"tornado >=6.2.0",
"traitlets",
},
Files: []string{
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json",
},
PathsData: &pkg.CondaPathsData{
PathsVersion: 1,
Paths: []pkg.CondaPathData{
{
Path: "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
PathType: "hardlink",
SHA256: "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
SHA256InPrefix: "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
SizeInBytes: 935,
},
{
Path: "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json",
PathType: "hardlink",
SHA256: "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
SHA256InPrefix: "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
SizeInBytes: 1565,
},
},
},
Link: &pkg.CondaLink{
Source: "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
Type: 1,
},
},
},
{
Name: "zlib",
Version: "1.2.11",
FoundBy: "conda-meta-cataloger",
Locations: file.NewLocationSet(
file.NewLocation("conda-meta/zlib-1.2.11-h90dfc92_1014.json"),
),
Language: pkg.UnknownLanguage,
Type: pkg.CondaPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "Zlib", file.NewLocation("conda-meta/zlib-1.2.11-h90dfc92_1014.json")),
),
Metadata: pkg.CondaMetaPackage{
Arch: "arm64",
Name: "zlib",
Version: "1.2.11",
Build: "h90dfc92_1014",
BuildNumber: 1014,
Channel: "https://conda.anaconda.org/conda-forge/",
Subdir: "osx-arm64",
Noarch: "",
License: "Zlib",
LicenseFamily: "Other",
MD5: "348a30b1350c9d91a4dbf05f5e46e0bb",
SHA256: "a70c028fd3b9af1d7ea3d7099d810f3d2588096237bb472db331a51a36f931c0",
Size: 86757,
Timestamp: 1648307332172,
Filename: "zlib-1.2.11-h90dfc92_1014.tar.bz2",
URL: "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2",
ExtractedPackageDir: "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
Depends: []string{
"libzlib 1.2.11 h90dfc92_1014",
},
Files: []string{
"include/zconf.h",
"include/zlib.h",
"lib/pkgconfig/zlib.pc",
"lib/libz.a",
"lib/libz.dylib",
},
PathsData: &pkg.CondaPathsData{
PathsVersion: 1,
Paths: []pkg.CondaPathData{
{
Path: "include/zconf.h",
PathType: "hardlink",
SHA256: "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
SHA256InPrefix: "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
SizeInBytes: 16262,
},
{
Path: "include/zlib.h",
PathType: "hardlink",
SHA256: "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
SHA256InPrefix: "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
SizeInBytes: 96239,
},
{
Path: "lib/pkgconfig/zlib.pc",
PathType: "hardlink",
SHA256: "357773df3c44a5ebd77fdadd0869b5b06394bbf556c2d6c9736dd53e9df3b2c2",
SHA256InPrefix: "5b4eb6062f97875eaadb3b6c7cf8cfeff3808798ecbf2bfc095f18dcecc509bf",
SizeInBytes: 285,
},
{
Path: "lib/libz.a",
PathType: "hardlink",
SHA256: "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
SHA256InPrefix: "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
SizeInBytes: 107128,
},
{
Path: "lib/libz.dylib",
PathType: "softlink",
SHA256: "67ed489e2f378880f72fb1c0d1cc916e184b9632eccf2b5c1b34ddf01ed1701c",
SHA256InPrefix: "09e47dbc60aa970e153913fe84551ad7f5aa51c21907591340a0cc999adab859",
SizeInBytes: 122478,
},
},
},
Link: &pkg.CondaLink{
Source: "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
Type: 1,
},
},
},
},
},
{
name: "badly formatted conda meta json file",
fixture: "test-fixtures/conda-meta-bad-json",
expectedPackages: nil,
wantErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) {
require.Error(t, err)
require.Contains(t, err.Error(), "failed to parse conda-meta package file at conda-meta/package-1.2.3-pyhd8ed1ab_0.json")
require.Contains(t, err.Error(), "invalid character")
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pkgtest.NewCatalogTester().
FromDirectory(t, test.fixture).
Expects(test.expectedPackages, nil).
WithErrorAssertion(test.wantErr).
TestCataloger(t, NewCondaMetaCataloger())
})
}
}
func TestCondaMetaPackageMetadata_FileOwner(t *testing.T) {
tests := []struct {
metadata pkg.CondaMetaPackage
expected []string
}{
{
metadata: pkg.CondaMetaPackage{
Files: []string{
"include/zconf.h",
"include/zlib.h",
"lib/pkgconfig/zlib.pc",
"lib/libz.a",
"lib/libz.dylib",
},
},
expected: []string{
"include/zconf.h",
"include/zlib.h",
"lib/libz.a",
"lib/libz.dylib",
"lib/pkgconfig/zlib.pc",
},
},
}
for _, test := range tests {
t.Run(strings.Join(test.expected, ","), func(t *testing.T) {
actual := test.metadata.OwnedFiles()
for _, d := range deep.Equal(test.expected, actual) {
t.Errorf("diff: %+v", d)
}
})
}
}
@@ -0,0 +1 @@
not json, will cause a parsing error
@@ -0,0 +1,63 @@
{
"build": "pyhd8ed1ab_0",
"build_number": 0,
"depends": [
"async-lru >=1.0.0",
"httpx >=0.25.0",
"importlib-metadata >=4.8.3",
"ipykernel >=6.5.0",
"jinja2 >=3.0.3",
"jupyter-lsp >=2.0.0",
"jupyter_core",
"jupyter_server >=2.4.0,<3",
"jupyterlab_server >=2.27.1,<3",
"notebook-shim >=0.2",
"packaging",
"python >=3.9",
"setuptools >=41.1.0",
"tomli >=1.2.2",
"tornado >=6.2.0",
"traitlets"
],
"license": "BSD-3-Clause",
"license_family": "BSD",
"md5": "4861a0c2a5a5d0481a450a9dfaf9febe",
"name": "jupyterlab",
"noarch": "python",
"sha256": "fc0235a71d852734fe92183a78cb91827367573450eba82465ae522c64230736",
"size": 8236973,
"subdir": "noarch",
"timestamp": 1748273017680,
"version": "4.4.3",
"fn": "jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
"url": "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda",
"channel": "https://conda.anaconda.org/conda-forge/",
"extracted_package_dir": "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
"files": [
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
"lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json"
],
"paths_data": {
"paths_version": 1,
"paths": [
{
"_path": "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json",
"path_type": "hardlink",
"sha256": "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
"sha256_in_prefix": "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27",
"size_in_bytes": 935
},
{
"_path": "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json",
"path_type": "hardlink",
"sha256": "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
"sha256_in_prefix": "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8",
"size_in_bytes": 1565
}
]
},
"link": {
"source": "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0",
"type": 1
}
}
@@ -0,0 +1,75 @@
{
"arch": "arm64",
"build": "h90dfc92_1014",
"build_number": 1014,
"depends": [
"libzlib 1.2.11 h90dfc92_1014"
],
"license": "Zlib",
"license_family": "Other",
"md5": "348a30b1350c9d91a4dbf05f5e46e0bb",
"name": "zlib",
"platform": "osx",
"sha256": "a70c028fd3b9af1d7ea3d7099d810f3d2588096237bb472db331a51a36f931c0",
"size": 86757,
"subdir": "osx-arm64",
"timestamp": 1648307332172,
"version": "1.2.11",
"fn": "zlib-1.2.11-h90dfc92_1014.tar.bz2",
"url": "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2",
"channel": "https://conda.anaconda.org/conda-forge/",
"extracted_package_dir": "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
"files": [
"include/zconf.h",
"include/zlib.h",
"lib/pkgconfig/zlib.pc",
"lib/libz.a",
"lib/libz.dylib"
],
"paths_data": {
"paths_version": 1,
"paths": [
{
"_path": "include/zconf.h",
"path_type": "hardlink",
"sha256": "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
"sha256_in_prefix": "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541",
"size_in_bytes": 16262
},
{
"_path": "include/zlib.h",
"path_type": "hardlink",
"sha256": "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
"sha256_in_prefix": "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8",
"size_in_bytes": 96239
},
{
"_path": "lib/pkgconfig/zlib.pc",
"path_type": "hardlink",
"sha256": "357773df3c44a5ebd77fdadd0869b5b06394bbf556c2d6c9736dd53e9df3b2c2",
"sha256_in_prefix": "5b4eb6062f97875eaadb3b6c7cf8cfeff3808798ecbf2bfc095f18dcecc509bf",
"size_in_bytes": 285,
"file_mode": "text",
"prefix_placeholder": "/Users/runner/miniforge3/conda-bld/zlib-split_1648307175973/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehol"
},
{
"_path": "lib/libz.a",
"path_type": "hardlink",
"sha256": "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
"sha256_in_prefix": "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0",
"size_in_bytes": 107128
},
{
"_path": "lib/libz.dylib",
"path_type": "softlink",
"sha256": "67ed489e2f378880f72fb1c0d1cc916e184b9632eccf2b5c1b34ddf01ed1701c",
"sha256_in_prefix": "09e47dbc60aa970e153913fe84551ad7f5aa51c21907591340a0cc999adab859",
"size_in_bytes": 122478
}
]
},
"link": {
"source": "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014",
"type": 1
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"context"
"fmt"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -7,7 +7,7 @@ import (
"regexp"
"sort"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
+12
View File
@@ -53,6 +53,7 @@ func (p resolvingProcessorWrapper) process(ctx context.Context, resolver file.Re
type Cataloger struct {
processors []processExecutor
requesters []requester
checks []func() error
upstreamCataloger string
}
@@ -127,6 +128,11 @@ func (c *Cataloger) WithResolvingProcessors(processors ...ResolvingProcessor) *C
return c
}
func (c *Cataloger) WithChecks(checks ...func() error) *Cataloger {
c.checks = append(c.checks, checks...)
return c
}
func makeRequests(parser Parser, locations []file.Location) []request {
var requests []request
for _, l := range locations {
@@ -152,6 +158,12 @@ func (c *Cataloger) Name() string {
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing the catalog source.
func (c *Cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
for _, check := range c.checks {
if err := check(); err != nil {
return nil, nil, err
}
}
var packages []pkg.Package
var relationships []artifact.Relationship
@@ -5,7 +5,7 @@ import (
"fmt"
"io"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
@@ -6,7 +6,7 @@ import (
"io"
"regexp"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
+149
View File
@@ -0,0 +1,149 @@
package golang
import (
"context"
"fmt"
"path/filepath"
"regexp"
"strings"
"github.com/spf13/afero"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
// resolveModuleLicenses finds and parses license files for Go modules
func resolveModuleLicenses(ctx context.Context, pkgInfos []pkgInfo, fs afero.Fs) pkg.LicenseSet {
licenses := pkg.NewLicenseSet()
for _, info := range pkgInfos {
modDir, pkgDir, err := getAbsolutePkgPaths(info)
if err != nil {
continue
}
licenseFiles, err := findAllLicenseCandidatesUpwards(pkgDir, licenseRegexp, modDir, fs)
if err != nil {
continue
}
for _, f := range licenseFiles {
contents, err := fs.Open(f)
if err != nil {
continue
}
licenses.Add(pkg.NewLicensesFromReadCloserWithContext(ctx, file.NewLocationReadCloser(file.Location{}, contents))...)
_ = contents.Close()
}
}
return licenses
}
/*
findAllLicenseCandidatesUpwards performs a bubble-up search per package:
1. pkgInfos represents a sparse vertical distribution of packages within modules
2. we get more pkgInfos for free when the build configuration is updated
The recursion terminates via two conditions:
- When dir is outside stopAt boundary (happy case)
- When reaching filesystem root where parent == dir (edge case)
Note: The code does NOT follow symlinks. It returns a slice of absolute paths that
represent license file matches that are resolved independently of the bubble-up.
When we should consider redesign tip to stem:
- Reduced filesystem calls: Single traversal vs multiple per-package
- Path deduplication: Avoids re-scanning common parent directories
- Better for wide module structures: Efficient when many packages share parent paths
- We need to consider the case here where nested modules are visited by accident and licenses
are erroneously associated to a 'parent module'; bubble up currently prevents this
*/
func findAllLicenseCandidatesUpwards(dir string, r *regexp.Regexp, stopAt string, fs afero.Fs) ([]string, error) {
// Validate that both paths are absolute
if !filepath.IsAbs(dir) {
return nil, fmt.Errorf("dir must be an absolute path, got: %s", dir)
}
if !filepath.IsAbs(stopAt) {
return nil, fmt.Errorf("stopAt must be an absolute path, got: %s", stopAt)
}
licenses, err := findLicenseCandidates(dir, r, stopAt, fs)
if err != nil {
return nil, err
}
// Ensure we return an empty slice rather than nil for consistency
if licenses == nil {
return []string{}, nil
}
return licenses, nil
}
func findLicenseCandidates(dir string, r *regexp.Regexp, stopAt string, fs afero.Fs) ([]string, error) {
// stop if we've gone outside the stopAt directory
if !strings.HasPrefix(dir, stopAt) {
return []string{}, nil
}
licenses, err := findLicensesInDir(dir, r, fs)
if err != nil {
return nil, err
}
parent := filepath.Dir(dir)
// can't go any higher up the directory tree: "/" case
if parent == dir {
return licenses, nil
}
// search parent directory and combine results
parentLicenses, err := findLicenseCandidates(parent, r, stopAt, fs)
if err != nil {
return nil, err
}
// Combine current directory licenses with parent directory licenses
return append(licenses, parentLicenses...), nil
}
func getAbsolutePkgPaths(info pkgInfo) (modDir string, pkgDir string, err error) {
pkgDir, err = filepath.Abs(info.pkgDir)
if err != nil {
return modDir, pkgDir, err
}
modDir, err = filepath.Abs(info.moduleDir)
if err != nil {
return modDir, pkgDir, err
}
if !strings.HasPrefix(pkgDir, modDir) {
return modDir, pkgDir, fmt.Errorf("modDir %s should contain pkgDir %s", modDir, pkgDir)
}
return modDir, pkgDir, nil
}
func findLicensesInDir(dir string, r *regexp.Regexp, fs afero.Fs) ([]string, error) {
var licenses []string
dirContents, err := afero.ReadDir(fs, dir)
if err != nil {
return nil, err
}
for _, f := range dirContents {
if f.IsDir() {
continue
}
if r.MatchString(f.Name()) {
path := filepath.Join(dir, f.Name())
licenses = append(licenses, path)
}
}
return licenses, nil
}
@@ -0,0 +1,220 @@
package golang
import (
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindAllLicenseCandidatesUpwards(t *testing.T) {
tests := []struct {
name string
setupFS func(afero.Fs)
startDir string
stopAt string
expectedFiles []string
expectedError bool
description string
}{
{
name: "normal traversal up to root",
startDir: "/project/subdir/deeper",
stopAt: "/project",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project/subdir/deeper", 0755)
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
afero.WriteFile(fs, "/project/foobar", []byte("MIT"), 0644)
afero.WriteFile(fs, "/project/subdir/LICENSE.txt", []byte("Apache"), 0644)
afero.WriteFile(fs, "/project/subdir/deeper/COPYING", []byte("GPL"), 0644)
},
expectedFiles: []string{
"/project/subdir/deeper/COPYING",
"/project/subdir/LICENSE.txt",
"/project/LICENSE",
},
description: "Should find all license files traversing upward",
},
{
name: "stops at boundary directory",
startDir: "/project/subdir/deeper",
stopAt: "/project/subdir",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project/subdir/deeper", 0755)
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
afero.WriteFile(fs, "/project/subdir/LICENSE.txt", []byte("Apache"), 0644)
afero.WriteFile(fs, "/project/subdir/deeper/COPYING", []byte("GPL"), 0644)
},
expectedFiles: []string{
"/project/subdir/deeper/COPYING",
"/project/subdir/LICENSE.txt",
},
description: "Should stop at stopAt boundary and not find LICENSE in /project",
},
{
name: "handles non-existent directory",
startDir: "/nonexistent",
stopAt: "/",
setupFS: func(fs afero.Fs) {
// Don't create the directory
},
expectedError: true,
description: "Should return error for non-existent directory",
},
{
name: "handles empty directory tree",
startDir: "/empty/dir/tree",
stopAt: "/empty",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/empty/dir/tree", 0755)
// No license files
},
expectedFiles: []string{},
description: "Should return empty slice when no license files found",
},
{
name: "handles directory at filesystem root",
startDir: "/",
stopAt: "/",
setupFS: func(fs afero.Fs) {
afero.WriteFile(fs, "/LICENSE", []byte("MIT"), 0644)
},
expectedFiles: []string{"/LICENSE"},
description: "Should handle traversal starting at root",
},
{
name: "ignores directories with license-like names",
startDir: "/project/subdir",
stopAt: "/project",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project/subdir", 0755)
fs.MkdirAll("/project/LICENSE_DIR", 0755) // Directory, should be ignored
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
},
expectedFiles: []string{"/project/LICENSE"},
description: "Should ignore directories even if they match license pattern",
},
{
name: "startDir equals stopAt",
startDir: "/project",
stopAt: "/project",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project", 0755)
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
},
expectedFiles: []string{"/project/LICENSE"},
description: "Should handle case where start equals stop directory",
},
{
name: "startDir is parent of stopAt (returns empty)",
startDir: "/",
stopAt: "/project",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project", 0755)
afero.WriteFile(fs, "/LICENSE", []byte("MIT"), 0644)
},
expectedFiles: []string{},
description: "Should return empty when startDir is above stopAt",
},
{
name: "very deep nesting",
startDir: "/a/b/c/d/e/f/g/h/i/j",
stopAt: "/a",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/a/b/c/d/e/f/g/h/i/j", 0755)
afero.WriteFile(fs, "/a/LICENSE", []byte("MIT"), 0644)
afero.WriteFile(fs, "/a/b/c/d/e/NOTICE", []byte("Notice"), 0644)
},
expectedFiles: []string{
"/a/b/c/d/e/NOTICE",
"/a/LICENSE",
},
description: "Should handle deep directory nesting without stack overflow",
},
{
name: "relative dir path rejected",
startDir: "project/subdir",
stopAt: "/project",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project/subdir", 0755)
},
expectedError: true,
description: "Should reject relative dir path",
},
{
name: "relative stopAt path rejected",
startDir: "/project/subdir",
stopAt: "project",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project/subdir", 0755)
},
expectedError: true,
description: "Should reject relative stopAt path",
},
{
name: "stopAt is descendant of startDir",
startDir: "/project",
stopAt: "/project/subdir/deeper",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project/subdir/deeper", 0755)
afero.WriteFile(fs, "/project/LICENSE", []byte("MIT"), 0644)
},
expectedFiles: []string{},
description: "Should return empty when stopAt is below startDir",
},
{
name: "disjoint paths",
startDir: "/foo/bar",
stopAt: "/baz/qux",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/foo/bar", 0755)
fs.MkdirAll("/baz/qux", 0755)
afero.WriteFile(fs, "/foo/bar/LICENSE", []byte("MIT"), 0644)
afero.WriteFile(fs, "/LICENSE", []byte("Root"), 0644)
},
expectedFiles: []string{},
description: "Should return empty for completely disjoint paths",
},
{
name: "empty stopAt rejected",
startDir: "/project/deep/path",
stopAt: "",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project/deep/path", 0755)
},
expectedError: true,
description: "Should reject empty stopAt string",
},
{
name: "empty startDir rejected",
startDir: "",
stopAt: "/project",
setupFS: func(fs afero.Fs) {
fs.MkdirAll("/project", 0755)
},
expectedError: true,
description: "Should reject empty startDir string",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create in-memory filesystem
fs := afero.NewMemMapFs()
tt.setupFS(fs)
// Run the function
result, err := findAllLicenseCandidatesUpwards(tt.startDir, licenseRegexp, tt.stopAt, fs)
// Check error expectation
if tt.expectedError {
assert.Error(t, err, tt.description)
return
}
require.NoError(t, err, tt.description)
assert.Equal(t, tt.expectedFiles, result, tt.description)
})
}
}
+416 -47
View File
@@ -4,20 +4,30 @@ import (
"bufio"
"context"
"fmt"
"go/build"
"io"
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/spf13/afero"
"golang.org/x/mod/modfile"
"golang.org/x/tools/go/packages"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
var (
licenseRegexp = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING|NOTICE).*$`)
)
type goModCataloger struct {
licenseResolver goLicenseResolver
}
@@ -28,59 +38,322 @@ func newGoModCataloger(opts CatalogerConfig) *goModCataloger {
}
}
// parseGoModFile takes a go.mod and lists all packages discovered.
//
//nolint:funlen
// parseGoModFile takes a go.mod and tries to resolve and lists all packages discovered.
func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
packages := make(map[string]pkg.Package)
contents, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to read go module: %w", err)
}
f, err := modfile.Parse(reader.RealPath, contents, nil)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse go module: %w", err)
}
modDir := filepath.Dir(string(reader.Location.Reference().RealPath))
digests, err := parseGoSumFile(resolver, reader)
if err != nil {
log.Debugf("unable to get go.sum: %v", err)
}
for _, m := range f.Require {
lics := c.licenseResolver.getLicenses(ctx, resolver, m.Mod.Path, m.Mod.Version)
packages[m.Mod.Path] = pkg.Package{
Name: m.Mod.Path,
Version: m.Mod.Version,
Licenses: pkg.NewLicenseSet(lics...),
Locations: file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
PURL: packageURL(m.Mod.Path, m.Mod.Version),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{
H1Digest: digests[fmt.Sprintf("%s %s", m.Mod.Path, m.Mod.Version)],
},
// source analysis using go toolchain if available
syftSourcePackages, sourceModules, sourceDependencies, unknownErr := c.loadPackages(modDir, reader.Location)
catalogedModules, sourceModuleToPkg := c.catalogModules(ctx, syftSourcePackages, sourceModules, reader, digests)
relationships := buildModuleRelationships(catalogedModules, sourceDependencies, sourceModuleToPkg)
// base case go.mod file parsing
modFile, err := c.parseModFileContents(reader)
if err != nil {
return nil, nil, err
}
// only use mod packages NOT found in source analysis
goModPackages := c.createGoModPackages(ctx, resolver, modFile, sourceModules, reader, digests)
c.applyReplaceDirectives(ctx, resolver, modFile, goModPackages, reader, digests)
c.applyExcludeDirectives(modFile, goModPackages)
finalPkgs := c.assembleResults(catalogedModules, goModPackages)
return finalPkgs, relationships, unknownErr
}
// loadPackages uses golang.org/x/tools/go/packages to get dependency information.
func (c *goModCataloger) loadPackages(modDir string, loc file.Location) (pkgs map[string][]pkgInfo, modules map[string]*packages.Module, dependencies map[string][]string, unknownErr error) {
cfg := &packages.Config{
// Mode flags control what information is loaded for each package.
// Performance impact increases significantly with each additional flag:
//
// packages.NeedModule - Required for module metadata (path, version, replace directives).
// Essential for SBOM generation. Minimal performance impact.
//
// packages.NeedName - Required for package names & package Path. Minimal performance impact.
// Needed to identify packages and filter out standard library packages.
//
// packages.NeedFiles - Loads source file paths for each package.
// Moderate performance impact as it requires filesystem traversal.
// Required for license discovery.
//
// packages.NeedDeps - Loads the dependency graph between packages.
// High performance impact as it builds the complete import graph.
// Critical for generating accurate dependency relationships in SBOM.
//
// packages.NeedImports - Loads import information for each package.
// High performance impact, especially with large codebases.
// Required for building module-to-module dependency mappings.
//
// Adding flags like NeedTypes, NeedSyntax, or NeedTypesInfo would dramatically
// increase memory usage and processing time (10x+ slower) but are not needed
// for SBOM generation as we only require dependency and module metadata.
Mode: packages.NeedModule | packages.NeedName | packages.NeedFiles | packages.NeedDeps | packages.NeedImports,
Dir: modDir,
Tests: true,
}
// From Go documentation: "all" expands to all packages in the main module
// and their dependencies, including dependencies needed by tests.
//
// The special pattern "all" specifies all the active modules,
// first the main module and then dependencies sorted by module path.
// A pattern containing "..." specifies the active modules whose module paths match the pattern.
// On implementation we could not find a test case that differentiated between all and ...
// There may be a case where ... is non inclusive so we default to all for the inclusive guarantee
rootPkgs, err := packages.Load(cfg, "all")
if err != nil {
log.Debugf("error loading packages: %v", err)
}
// Check for any errors in loading
for _, p := range rootPkgs {
if len(p.Errors) > 0 {
// Log errors but continue processing
for _, e := range p.Errors {
log.Debugf("package load error for %s: %v", p.PkgPath, e)
unknownErr = unknown.Append(unknownErr, loc, err)
}
}
}
// remove any old packages and replace with new ones...
for _, m := range f.Replace {
lics := c.licenseResolver.getLicenses(ctx, resolver, m.New.Path, m.New.Version)
// note: dependencies have already pruned local imports and only focuses on module => module dependencies
return c.visitPackages(rootPkgs, loc, unknownErr)
}
// the old path and new path may be the same, in which case this is a noop,
// but if they're different we need to remove the old package.
// note that we may change the path but we should always reference the new version (since the old version
// cannot be trusted as a correct value).
type pkgInfo struct {
// pkgPath is the import path of the package.
pkgPath string
// modulePath is the module path of the package.
modulePath string
// pkgDir is the directory containing the package's source code.
pkgDir string
// moduleDir is the directory containing the module's source code.
moduleDir string
}
// visitPackages processes Go module import graphs to get all modules
func (c *goModCataloger) visitPackages(
rootPkgs []*packages.Package,
loc file.Location,
uke error,
) (pkgs map[string][]pkgInfo, modules map[string]*packages.Module, dependencies map[string][]string, unknownErr error) {
modules = make(map[string]*packages.Module)
// note: packages are specific to inside the module - they do not include transitive pkgInfo
// packages is used for identifying licensing documents for modules that could contain multiple licenses
// dependencies cover transitive module imports; see p.Imports array in packages.Visit
pkgs = make(map[string][]pkgInfo)
// dependencies are module => module dependencies
dependencies = make(map[string][]string)
// persist unknown errs from previous parts of the catalog
unknownErr = uke
// closure (p *Package) bool
// return bool determines whether the imports of package p are visited.
packages.Visit(rootPkgs, func(p *packages.Package) bool {
if len(p.Errors) > 0 {
for _, err := range p.Errors {
unknownErr = unknown.Append(unknownErr, loc, err)
}
return false
}
// skip for common causes
if shouldSkipVisit(p) {
return false
}
// different from above; we still might want to visit imports
// ignoring a package shouldn't end walking the tree
// since we need to get the full picture for license discovery
// for _, prefix := range c.config.IgnorePaths {
// if strings.HasPrefix(p.PkgPath, prefix) {
// return c.config.IncludeIgnoredDeps
// }
//}
pkgDir := resolvePkgDir(p)
if pkgDir == "" {
return true
}
module := newModule(p.Module)
if module.Dir == "" {
// We continue processing even when module.Dir is empty because we still want to:
// 1. Extract module dependencies from p.Imports for dependency graph construction
// 2. Create syft packages with available metadata (name, version, etc.)
// 3. Build relationships between modules even without complete filesystem info
// Not having the DIR here just means that we're not going to process the licenses
// Common causes for module.Dir being empty:
// - Vendored dependencies where Go toolchain loses some module metadata
// - Replace directives pointing to non-existent or inaccessible paths
// A known cause is that the module is vendored, so some information is lost.
isVendored := strings.Contains(pkgDir, "/vendor/")
if !isVendored {
log.Debugf("module %s does not have dir and it's not vendored", module.Path)
}
}
// extract module dependencies
for _, imp := range p.Imports {
if imp.Module != nil && imp.Module.Path != module.Path {
if dependencies[module.Path] == nil {
dependencies[module.Path] = []string{imp.Module.Path}
} else {
dependencies[module.Path] = append(dependencies[module.Path], imp.Module.Path)
}
}
}
pkgs[module.Path] = append(pkgs[module.Path], pkgInfo{
pkgPath: p.PkgPath,
modulePath: module.Path,
pkgDir: pkgDir,
moduleDir: module.Dir,
})
modules[p.Module.Path] = module
return true
}, nil)
return pkgs, modules, dependencies, unknownErr
}
// create syft packages from Go modules found by the go toolchain
func (c *goModCataloger) catalogModules(
ctx context.Context,
pkgs map[string][]pkgInfo,
modules map[string]*packages.Module,
reader file.LocationReadCloser,
digests map[string]string,
) ([]pkg.Package, map[string]artifact.Identifiable) {
syftPackages := make([]pkg.Package, 0)
moduleToPackage := make(map[string]artifact.Identifiable)
for _, m := range modules {
if isRelativeImportOrMain(m.Path) {
// relativeImport modules are already accounted for by their full module paths at other portions of syft's cataloging
// example: something like ../../ found as a module for go.mod b, which is sub to go.mod a is accounted for
// in another call to the goModCataloger when go.mod a is parsed
// local modules that use a "main" heuristic, no module naming (sometimes common pre go module support)
// are also not built as syft packages
continue
}
pkgInfos := pkgs[m.Path]
moduleLicenses := resolveModuleLicenses(ctx, pkgInfos, afero.NewOsFs())
// we do out of source lookups for module parsing
// locations are NOT included in the SBOM because of this
goModulePkg := pkg.Package{
Name: m.Path,
Version: m.Version,
Locations: file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
Licenses: moduleLicenses,
Language: pkg.Go,
Type: pkg.GoModulePkg,
PURL: packageURL(m.Path, m.Version),
Metadata: createSourceMetadata(digests[fmt.Sprintf("%s %s", m.Path, m.Version)]),
}
goModulePkg.SetID()
moduleToPackage[m.Path] = goModulePkg
syftPackages = append(syftPackages, goModulePkg)
}
return syftPackages, moduleToPackage
}
// buildModuleRelationships creates artifact relationships between Go modules.
func buildModuleRelationships(
syftPkgs []pkg.Package,
dependencies map[string][]string,
moduleToPkg map[string]artifact.Identifiable,
) []artifact.Relationship {
var rels []artifact.Relationship
seen := make(map[string]struct{})
for _, fromPkg := range syftPkgs {
for _, dep := range dependencies[fromPkg.Name] {
if dep == fromPkg.Name {
continue
}
toPkg, ok := moduleToPkg[dep]
if !ok {
continue
}
key := string(fromPkg.ID()) + string(toPkg.ID())
if _, exists := seen[key]; exists {
continue
}
rels = append(rels, artifact.Relationship{
From: toPkg, // dep
To: fromPkg, // parent
Type: artifact.DependencyOfRelationship,
})
seen[key] = struct{}{}
}
}
return rels
}
func (c *goModCataloger) parseModFileContents(reader file.LocationReadCloser) (*modfile.File, error) {
contents, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("failed to read go module: %w", err)
}
f, err := modfile.Parse(reader.RealPath, contents, nil)
if err != nil {
return nil, fmt.Errorf("failed to parse go module: %w", err)
}
return f, nil
}
// note this handles the deduplication from source by checking if the mod path exists in the sourceModules map
func (c *goModCataloger) createGoModPackages(ctx context.Context, resolver file.Resolver, modFile *modfile.File, sourceModules map[string]*packages.Module, reader file.LocationReadCloser, digests map[string]string) map[string]pkg.Package {
goModPackages := make(map[string]pkg.Package)
for _, m := range modFile.Require {
if _, exists := sourceModules[m.Mod.Path]; !exists {
lics := c.licenseResolver.getLicenses(ctx, resolver, m.Mod.Path, m.Mod.Version)
goModPkg := pkg.Package{
Name: m.Mod.Path,
Version: m.Mod.Version,
Licenses: pkg.NewLicenseSet(lics...),
Locations: file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
PURL: packageURL(m.Mod.Path, m.Mod.Version),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{
H1Digest: digests[fmt.Sprintf("%s %s", m.Mod.Path, m.Mod.Version)],
},
}
goModPkg.SetID()
goModPackages[m.Mod.Path] = goModPkg
}
}
return goModPackages
}
// applyReplaceDirectives processes replace directives from go.mod
func (c *goModCataloger) applyReplaceDirectives(ctx context.Context, resolver file.Resolver, modFile *modfile.File, goModPackages map[string]pkg.Package, reader file.LocationReadCloser, digests map[string]string) {
for _, m := range modFile.Replace {
lics := c.licenseResolver.getLicenses(ctx, resolver, m.New.Path, m.New.Version)
var finalPath string
if !strings.HasPrefix(m.New.Path, ".") && !strings.HasPrefix(m.New.Path, "/") {
finalPath = m.New.Path
delete(packages, m.Old.Path)
delete(goModPackages, m.Old.Path)
} else {
finalPath = m.Old.Path
}
packages[finalPath] = pkg.Package{
goModPkg := pkg.Package{
Name: finalPath,
Version: m.New.Version,
Licenses: pkg.NewLicenseSet(lics...),
@@ -92,26 +365,31 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
H1Digest: digests[fmt.Sprintf("%s %s", finalPath, m.New.Version)],
},
}
goModPkg.SetID()
goModPackages[finalPath] = goModPkg
}
}
// remove any packages from the exclude fields
for _, m := range f.Exclude {
delete(packages, m.Mod.Path)
func (c *goModCataloger) applyExcludeDirectives(modFile *modfile.File, goModPackages map[string]pkg.Package) {
for _, m := range modFile.Exclude {
delete(goModPackages, m.Mod.Path)
}
}
pkgsSlice := make([]pkg.Package, len(packages))
idx := 0
for _, p := range packages {
p.SetID()
pkgsSlice[idx] = p
idx++
func (c *goModCataloger) assembleResults(catalogedPkgs []pkg.Package, goModPackages map[string]pkg.Package) []pkg.Package {
pkgsSlice := make([]pkg.Package, 0)
pkgsSlice = append(pkgsSlice, catalogedPkgs...)
for _, p := range goModPackages {
pkgsSlice = append(pkgsSlice, p)
}
sort.SliceStable(pkgsSlice, func(i, j int) bool {
return pkgsSlice[i].Name < pkgsSlice[j].Name
})
return pkgsSlice, nil, nil
return pkgsSlice
}
func parseGoSumFile(resolver file.Resolver, reader file.LocationReadCloser) (map[string]string, error) {
@@ -151,3 +429,94 @@ func parseGoSumFile(resolver file.Resolver, reader file.LocationReadCloser) (map
return out, nil
}
// createSourceMetadata creates metadata for packages found through source analysis using build.Default
func createSourceMetadata(h1Digest string) pkg.GolangSourceEntry {
return pkg.GolangSourceEntry{
H1Digest: h1Digest,
OperatingSystem: build.Default.GOOS,
Architecture: build.Default.GOARCH,
BuildTags: strings.Join(build.Default.BuildTags, ","),
CgoEnabled: build.Default.CgoEnabled,
}
}
func resolvePkgDir(p *packages.Package) string {
switch {
case len(p.GoFiles) > 0:
return filepath.Dir(p.GoFiles[0])
case len(p.CompiledGoFiles) > 0:
return filepath.Dir(p.CompiledGoFiles[0])
case len(p.OtherFiles) > 0:
return filepath.Dir(p.OtherFiles[0])
default:
return ""
}
}
func shouldSkipVisit(p *packages.Package) bool {
// skip packages that don't have module info
if p.Module == nil {
return true
}
// skip stdlib
if isStdLib(p) {
return true
}
return false
}
// isStdLib returns true if this package is part of the Go standard library.
func isStdLib(pkg *packages.Package) bool {
if pkg.Name == "unsafe" {
// Special case unsafe stdlib, because it does not contain go files.
return true
}
if len(pkg.GoFiles) == 0 {
return false
}
prefix := build.Default.GOROOT
sep := string(filepath.Separator)
if !strings.HasSuffix(prefix, sep) {
prefix += sep
}
return strings.HasPrefix(pkg.GoFiles[0], prefix)
}
// handle replace directives
func newModule(mod *packages.Module) *packages.Module {
// Example of a module with replace directive: k8s.io/kubernetes => k8s.io/kubernetes v1.11.1
// {
// "Path": "k8s.io/kubernetes",
// "Version": "v0.17.9",
// "Replace": {
// "Path": "k8s.io/kubernetes",
// "Version": "v1.11.1",
// "Time": "2018-07-17T04:20:29Z",
// "Dir": "/home/gongyuan_kubeflow_org/go/pkg/mod/k8s.io/kubernetes@v1.11.1",
// "GoMod": "/home/gongyuan_kubeflow_org/go/pkg/mod/cache/download/k8s.io/kubernetes/@v/v1.11.1.mod"
// },
// "Dir": "/home/gongyuan_kubeflow_org/go/pkg/mod/k8s.io/kubernetes@v1.11.1",
// "GoMod": "/home/gongyuan_kubeflow_org/go/pkg/mod/cache/download/k8s.io/kubernetes/@v/v1.11.1.mod"
// }
// handle replace directives
// Note, we specifically want to replace version field.
// Haven't confirmed, but we may also need to override the
// entire struct when using replace directive with local folders.
tmp := *mod
if tmp.Replace != nil {
tmp = *tmp.Replace
}
return &tmp
}
func isRelativeImportOrMain(p string) bool {
if p == "main" {
return true
}
// true for ".", "..", "./...", "../..."
return build.IsLocalImport(p)
}
+169 -11
View File
@@ -1,8 +1,13 @@
package golang
import (
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/fileresolver"
"github.com/anchore/syft/syft/pkg"
@@ -15,13 +20,13 @@ func TestParseGoMod(t *testing.T) {
expected []pkg.Package
}{
{
fixture: "test-fixtures/one-package",
fixture: "test-fixtures/go-mod-fixtures/one-package/go.mod",
expected: []pkg.Package{
{
Name: "github.com/bmatcuk/doublestar",
Version: "v1.3.1",
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v1.3.1",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/one-package")),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/one-package/go.mod")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
@@ -29,28 +34,28 @@ func TestParseGoMod(t *testing.T) {
},
},
{
fixture: "test-fixtures/relative-replace",
fixture: "test-fixtures/go-mod-fixtures/relative-replace/go.mod",
expected: []pkg.Package{
{
Name: "github.com/aws/aws-sdk-go-v2",
Version: "",
PURL: "pkg:golang/github.com/aws/aws-sdk-go-v2",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/relative-replace")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/relative-replace/go.mod")),
Metadata: pkg.GolangModuleEntry{},
},
},
},
{
fixture: "test-fixtures/many-packages",
fixture: "test-fixtures/go-mod-fixtures/many-packages/go.mod",
expected: []pkg.Package{
{
Name: "github.com/anchore/archiver/v3",
Version: "v3.5.2",
PURL: "pkg:golang/github.com/anchore/archiver@v3.5.2#v3",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
@@ -59,7 +64,7 @@ func TestParseGoMod(t *testing.T) {
Name: "github.com/anchore/go-testutils",
Version: "v0.0.0-20200624184116-66aa578126db",
PURL: "pkg:golang/github.com/anchore/go-testutils@v0.0.0-20200624184116-66aa578126db",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
@@ -68,7 +73,7 @@ func TestParseGoMod(t *testing.T) {
Name: "github.com/anchore/go-version",
Version: "v1.2.2-0.20200701162849-18adb9c92b9b",
PURL: "pkg:golang/github.com/anchore/go-version@v1.2.2-0.20200701162849-18adb9c92b9b",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
@@ -77,7 +82,7 @@ func TestParseGoMod(t *testing.T) {
Name: "github.com/anchore/stereoscope",
Version: "v0.0.0-20200706164556-7cf39d7f4639",
PURL: "pkg:golang/github.com/anchore/stereoscope@v0.0.0-20200706164556-7cf39d7f4639",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
@@ -86,7 +91,7 @@ func TestParseGoMod(t *testing.T) {
Name: "github.com/bmatcuk/doublestar",
Version: "v8.8.8",
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v8.8.8",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
@@ -95,7 +100,7 @@ func TestParseGoMod(t *testing.T) {
Name: "github.com/go-test/deep",
Version: "v1.0.6",
PURL: "pkg:golang/github.com/go-test/deep@v1.0.6",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/many-packages")),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
@@ -179,3 +184,156 @@ func Test_corruptGoMod(t *testing.T) {
WithError().
TestCataloger(t, c)
}
func Test_parseGoSource_packageResolution(t *testing.T) {
tests := []struct {
name string
fixturePath string
config CatalogerConfig
expectedPkgs []string
expectedRels []string
expectedLicenses map[string][]string
}{
{
name: "go-source with direct, transitive, and deps of transitive",
fixturePath: filepath.Join("test-fixtures", "go-source"),
expectedPkgs: []string{
"anchore.io/not/real @ (go.mod)",
"github.com/davecgh/go-spew @ v1.1.1 (go.mod)",
"github.com/go-viper/mapstructure/v2 @ v2.2.1 (go.mod)",
"github.com/google/uuid @ v1.6.0 (go.mod)",
"github.com/pmezard/go-difflib @ v1.0.0 (go.mod)",
"github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
"github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
"github.com/sourcegraph/conc @ v0.3.0 (go.mod)",
"github.com/spf13/afero @ v1.12.0 (go.mod)",
"github.com/spf13/cast @ v1.7.1 (go.mod)",
"github.com/spf13/pflag @ v1.0.6 (go.mod)",
"github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod)",
"github.com/subosito/gotenv @ v1.6.0 (go.mod)",
"go.uber.org/multierr @ v1.10.0 (go.mod)",
"go.uber.org/zap @ v1.27.0 (go.mod)",
"golang.org/x/sys @ v0.33.0 (go.mod)",
"golang.org/x/text @ v0.21.0 (go.mod)",
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod)",
"github.com/fsnotify/fsnotify @ v1.8.0 (go.mod)",
"github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod)",
"github.com/frankban/quicktest @ v1.14.6 (go.mod)",
"github.com/google/go-cmp @ v0.6.0 (go.mod)",
"github.com/kr/pretty @ v0.3.1 (go.mod)",
"github.com/kr/text @ v0.2.0 (go.mod)",
"github.com/rogpeppe/go-internal @ v1.9.0 (go.mod)",
"go.uber.org/goleak @ v1.3.0 (go.mod)",
"gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod)",
},
expectedRels: []string{
"github.com/davecgh/go-spew @ v1.1.1 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
"github.com/frankban/quicktest @ v1.14.6 (go.mod) [dependency-of] github.com/spf13/cast @ v1.7.1 (go.mod)",
"github.com/fsnotify/fsnotify @ v1.8.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/go-viper/mapstructure/v2 @ v2.2.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/google/go-cmp @ v0.6.0 (go.mod) [dependency-of] github.com/frankban/quicktest @ v1.14.6 (go.mod)",
"github.com/google/uuid @ v1.6.0 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
"github.com/kr/pretty @ v0.3.1 (go.mod) [dependency-of] github.com/frankban/quicktest @ v1.14.6 (go.mod)",
"github.com/kr/pretty @ v0.3.1 (go.mod) [dependency-of] gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod)",
"github.com/kr/text @ v0.2.0 (go.mod) [dependency-of] github.com/kr/pretty @ v0.3.1 (go.mod)",
"github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/pmezard/go-difflib @ v1.0.0 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
"github.com/rogpeppe/go-internal @ v1.9.0 (go.mod) [dependency-of] github.com/kr/pretty @ v0.3.1 (go.mod)",
"github.com/sagikazarmark/locafero @ v0.7.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/sirupsen/logrus @ v1.9.3 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
"github.com/sourcegraph/conc @ v0.3.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
"github.com/spf13/afero @ v1.12.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
"github.com/spf13/afero @ v1.12.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/spf13/cast @ v1.7.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/spf13/pflag @ v1.0.6 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/spf13/viper @ v1.20.1 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sourcegraph/conc @ v0.3.0 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/subosito/gotenv @ v1.6.0 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] go.uber.org/multierr @ v1.10.0 (go.mod)",
"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
"github.com/subosito/gotenv @ v1.6.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"go.uber.org/goleak @ v1.3.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
"go.uber.org/multierr @ v1.10.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
"go.uber.org/zap @ v1.27.0 (go.mod) [dependency-of] anchore.io/not/real @ (go.mod)",
"golang.org/x/sys @ v0.33.0 (go.mod) [dependency-of] github.com/fsnotify/fsnotify @ v1.8.0 (go.mod)",
"golang.org/x/sys @ v0.33.0 (go.mod) [dependency-of] github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
"golang.org/x/text @ v0.21.0 (go.mod) [dependency-of] github.com/spf13/afero @ v1.12.0 (go.mod)",
"golang.org/x/text @ v0.21.0 (go.mod) [dependency-of] github.com/subosito/gotenv @ v1.6.0 (go.mod)",
"gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod) [dependency-of] gopkg.in/yaml.v3 @ v3.0.1 (go.mod)",
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
},
expectedLicenses: map[string][]string{
"github.com/fsnotify/fsnotify": {"BSD-3-Clause"},
"github.com/go-viper/mapstructure/v2": {"MIT"},
"github.com/google/uuid": {"BSD-3-Clause"},
"github.com/pelletier/go-toml/v2": {"MIT"},
"github.com/sagikazarmark/locafero": {"MIT"},
"github.com/sirupsen/logrus": {"MIT"},
"github.com/sourcegraph/conc": {"MIT"},
"github.com/spf13/afero": {"Apache-2.0"},
"github.com/spf13/cast": {"MIT"},
"github.com/spf13/pflag": {"BSD-3-Clause"},
"github.com/spf13/viper": {"MIT"},
"github.com/subosito/gotenv": {"MIT"},
"go.uber.org/multierr": {"MIT"},
"go.uber.org/zap": {"MIT"},
"golang.org/x/sys": {"BSD-3-Clause"},
"golang.org/x/text": {"BSD-3-Clause"},
"gopkg.in/yaml.v3": {"Apache-2.0", "MIT"},
"github.com/davecgh/go-spew": {"ISC"},
"github.com/pmezard/go-difflib": {"BSD-3-Clause"},
"github.com/stretchr/testify": {"MIT"},
"github.com/frankban/quicktest": {"MIT"},
"github.com/google/go-cmp": {"BSD-3-Clause"},
"github.com/kr/text": {"MIT"},
"github.com/kr/pretty": {"MIT"},
"github.com/rogpeppe/go-internal": {"BSD-3-Clause"},
"go.uber.org/goleak": {"MIT"},
"gopkg.in/check.v1": {"BSD-2-Clause"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pkgtest.NewCatalogTester().
FromDirectory(t, tt.fixturePath).
ExpectsPackageStrings(tt.expectedPkgs).
ExpectsRelationshipStrings(tt.expectedRels).
ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
for _, p := range pkgs {
if metadata, ok := p.Metadata.(pkg.GolangSourceEntry); ok {
// Validate that GolangSourceEntry metadata is present but don't assert on specific field values
// since these might vary across development machines
require.IsType(t, pkg.GolangSourceEntry{}, metadata, "expected GolangSourceEntry metadata for package %s", p.Name)
// Verify that the metadata struct is populated (non-zero values indicate go source method was used)
require.NotEmpty(t, metadata, "GolangSourceEntry metadata should not be empty for package %s", p.Name)
}
}
}).
ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
actualLicenses := make(map[string][]string)
for _, p := range pkgs {
for _, l := range p.Licenses.ToSlice() {
if actualLicenses[p.Name] == nil {
actualLicenses[p.Name] = make([]string, 0)
}
actualLicenses[p.Name] = append(actualLicenses[p.Name], l.Value)
}
}
if diff := cmp.Diff(tt.expectedLicenses, actualLicenses); diff != "" {
t.Errorf("mismatch in licenses (-want +got):\n%s", diff)
}
}).
TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
})
}
}
@@ -0,0 +1 @@
github.com/bmatcuk/doublestar v1.3.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
@@ -0,0 +1,11 @@
package main
import (
"anchore.io/not/real/pk1"
"anchore.io/not/real/pk2"
)
func main() {
pk1.Test()
pk2.Test()
}
@@ -0,0 +1,11 @@
package main
import (
"anchore.io/not/real/pk1"
"anchore.io/not/real/pk3"
)
func main() {
pk1.Test()
pk3.Zap()
}
@@ -0,0 +1,29 @@
module anchore.io/not/real
go 1.24.3
require (
github.com/google/uuid v1.6.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.10.0
go.uber.org/zap v1.27.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
@@ -0,0 +1,60 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -0,0 +1,17 @@
package pk1
import (
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
func Test() {
return
}
func NewID() string {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
return uuid.New().String()
}
@@ -0,0 +1,12 @@
package pk1
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewID(t *testing.T) {
id := NewID()
assert.NotEmpty(t, id)
}
@@ -0,0 +1,5 @@
package pk2
func Test() {
return
}
@@ -0,0 +1,20 @@
package pk3
import (
"time"
"github.com/spf13/viper"
"go.uber.org/zap"
)
func Zap() {
sugar := zap.NewExample().Sugar()
defer sugar.Sync()
sugar.Infow("failed to fetch URL",
"url", "http://example.com",
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("failed to fetch URL: %s", "http://example.com")
viper.SetDefault("ContentDir", "content")
}
@@ -6,7 +6,7 @@ import (
"io"
"strings"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
@@ -5,7 +5,7 @@ import (
"fmt"
"io"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
@@ -7402,6 +7402,9 @@
"activedemand": [
"cpe:2.3:a:jumpdemand:activedemand:*:*:*:*:*:wordpress:*:*"
],
"activity-link-preview-for-buddypress": [
"cpe:2.3:a:wbcomdesigns:activity_link_preview_for_buddypress:*:*:*:*:*:wordpress:*:*"
],
"activity-reactions-for-buddypress": [
"cpe:2.3:a:areteit:activity_reactions_for_buddypress:*:*:*:*:*:wordpress:*:*"
],
@@ -8324,6 +8327,9 @@
"cpe:2.3:a:ays-pro:popup_box:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:ays-pro:popup_box:*:*:*:*:pro:wordpress:*:*"
],
"azan": [
"cpe:2.3:a:wp-master:azan:*:*:*:*:*:wordpress:*:*"
],
"azindex": [
"cpe:2.3:a:azindex_project:azindex:*:*:*:*:*:wordpress:*:*"
],
@@ -8421,6 +8427,9 @@
"cpe:2.3:a:brainstormforce:cards_for_beaver_builder:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:ultimateaddons:cards_for_beaver_builder:*:*:*:*:*:wordpress:*:*"
],
"bb-plugin": [
"cpe:2.3:a:fastlinemedia:beaver_builder:*:*:*:*:lite:wordpress:*:*"
],
"bbp-core": [
"cpe:2.3:a:spider-themes:bbp_core:*:*:*:*:*:wordpress:*:*"
],
@@ -10937,11 +10946,13 @@
"cpe:2.3:a:elementor:elementor_page_builder:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:elementor:elementor_page_builder:*:*:*:*:pro:wordpress:*:*",
"cpe:2.3:a:elementor:page_builder:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:elementor:website_builder:*:*:*:*:free:wordpress:*:*"
"cpe:2.3:a:elementor:website_builder:*:*:*:*:free:wordpress:*:*",
"cpe:2.3:a:elementor:website_builder:*:*:*:*:pro:wordpress:*:*"
],
"elementor-pro": [
"cpe:2.3:a:elementor:elementor_page_builder:*:*:*:*:pro:wordpress:*:*",
"cpe:2.3:a:elementor:elementor_pro:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:elementor:elementor_pro:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:elementor:website_builder:*:*:*:*:pro:wordpress:*:*"
],
"elementskit": [
"cpe:2.3:a:wpmet:elementskit:*:*:*:*:pro:wordpress:*:*"
@@ -17113,7 +17124,8 @@
"cpe:2.3:a:sitepact:contact_form_7_extension_for_klaviyo:*:*:*:*:*:wordpress:*:*"
],
"sitepress-multilingual-cms": [
"cpe:2.3:a:onthegosystems:sitepress-multilingual-cms:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:onthegosystems:sitepress-multilingual-cms:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wpml:wpml:*:*:*:*:*:wordpress:*:*"
],
"sitesupercharger": [
"cpe:2.3:a:marketingheroes:sitesupercharger:*:*:*:*:*:wordpress:*:*"
@@ -19473,6 +19485,9 @@
"wordlift": [
"cpe:2.3:a:wordlift:wordlift:*:*:*:*:*:wordpress:*:*"
],
"wordpress-23-related-posts-plugin": [
"cpe:2.3:a:sovrn:wordpress_related_posts:*:*:*:*:*:wordpress:*:*"
],
"wordpress-access-control": [
"cpe:2.3:a:brandonwamboldt:wordpress_access_control:*:*:*:*:*:wordpress:*:*"
],
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"sync"
@@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
stereofile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/internal/cmptest"
"github.com/anchore/syft/internal/licenses"
@@ -130,11 +132,14 @@ func (p *CatalogTester) FromFile(t *testing.T, path string) *CatalogTester {
return p
}
absPath, err := filepath.Abs(path)
require.NoError(t, err)
fixture, err := os.Open(path)
require.NoError(t, err)
p.reader = file.LocationReadCloser{
Location: file.NewLocation(fixture.Name()),
Location: file.NewVirtualLocationFromDirectory(fixture.Name(), fixture.Name(), *stereofile.NewFileReference(stereofile.Path(absPath))),
ReadCloser: fixture,
}
return p
+34 -1
View File
@@ -25,6 +25,39 @@ func newPackageJSONPackage(ctx context.Context, u packageJSON, indexLocation fil
}
license := pkg.NewLicensesFromLocationWithContext(ctx, indexLocation, licenseCandidates...)
// Handle author, authors, contributors, and maintainers fields
var authorParts []string
// Add a single author field if it exists
if u.Author.Name != "" || u.Author.Email != "" || u.Author.URL != "" {
if authStr := u.Author.AuthorString(); authStr != "" {
authorParts = append(authorParts, authStr)
}
}
// Add authors field if it exists
if len(u.Authors) > 0 {
if authorsStr := u.Authors.String(); authorsStr != "" {
authorParts = append(authorParts, authorsStr)
}
}
// Add contributors field if it exists
if len(u.Contributors) > 0 {
if contributorsStr := u.Contributors.String(); contributorsStr != "" {
authorParts = append(authorParts, contributorsStr)
}
}
// Add maintainers field if it exists
if len(u.Maintainers) > 0 {
if maintainersStr := u.Maintainers.String(); maintainersStr != "" {
authorParts = append(authorParts, maintainersStr)
}
}
authorInfo := strings.Join(authorParts, ", ")
p := pkg.Package{
Name: u.Name,
Version: u.Version,
@@ -37,7 +70,7 @@ func newPackageJSONPackage(ctx context.Context, u packageJSON, indexLocation fil
Name: u.Name,
Version: u.Version,
Description: u.Description,
Author: u.Author.AuthorString(),
Author: authorInfo,
Homepage: u.Homepage,
URL: u.Repository.URL,
Private: u.Private,
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"regexp"
"strings"
"github.com/go-viper/mapstructure/v2"
@@ -24,7 +25,10 @@ var _ generic.Parser = parsePackageJSON
type packageJSON struct {
Version string `json:"version"`
Latest []string `json:"latest"`
Author author `json:"author"`
Author person `json:"author"`
Authors people `json:"authors"`
Contributors people `json:"contributors"`
Maintainers people `json:"maintainers"`
License json.RawMessage `json:"license"`
Licenses json.RawMessage `json:"licenses"`
Name string `json:"name"`
@@ -35,12 +39,14 @@ type packageJSON struct {
Private bool `json:"private"`
}
type author struct {
type person struct {
Name string `json:"name" mapstructure:"name"`
Email string `json:"email" mapstructure:"email"`
URL string `json:"url" mapstructure:"url"`
}
type people []person
type repository struct {
Type string `json:"type" mapstructure:"type"`
URL string `json:"url" mapstructure:"url"`
@@ -76,9 +82,9 @@ func parsePackageJSON(ctx context.Context, _ file.Resolver, _ *generic.Environme
return pkgs, nil, nil
}
func (a *author) UnmarshalJSON(b []byte) error {
func (p *person) UnmarshalJSON(b []byte) error {
var authorStr string
var auth author
var auth person
if err := json.Unmarshal(b, &authorStr); err == nil {
// successfully parsed as a string, now parse that string into fields
@@ -97,18 +103,18 @@ func (a *author) UnmarshalJSON(b []byte) error {
}
}
*a = auth
*p = auth
return nil
}
func (a *author) AuthorString() string {
result := a.Name
if a.Email != "" {
result += fmt.Sprintf(" <%s>", a.Email)
func (p *person) AuthorString() string {
result := p.Name
if p.Email != "" {
result += fmt.Sprintf(" <%s>", p.Email)
}
if a.URL != "" {
result += fmt.Sprintf(" (%s)", a.URL)
if p.URL != "" {
result += fmt.Sprintf(" (%s)", p.URL)
}
return result
}
@@ -210,3 +216,58 @@ func pathContainsNodeModulesDirectory(p string) bool {
}
return false
}
func (p *people) UnmarshalJSON(b []byte) error {
// Try to unmarshal as an array of strings
var authorStrings []string
if err := json.Unmarshal(b, &authorStrings); err == nil {
// Successfully parsed as an array of strings
auths := make([]person, len(authorStrings))
for i, authorStr := range authorStrings {
// Parse each string into author fields
fields := internal.MatchNamedCaptureGroups(authorPattern, authorStr)
var auth person
if err := mapstructure.Decode(fields, &auth); err != nil {
return fmt.Errorf("unable to decode package.json author: %w", err)
}
// Trim whitespace from name if it was parsed
if auth.Name != "" {
auth.Name = strings.TrimSpace(auth.Name)
}
auths[i] = auth
}
*p = auths
return nil
}
// Try to unmarshal as an array of objects
var authorObjs []map[string]interface{}
if err := json.Unmarshal(b, &authorObjs); err == nil {
// Successfully parsed as an array of objects
auths := make([]person, len(authorObjs))
for i, fields := range authorObjs {
var auth person
if err := mapstructure.Decode(fields, &auth); err != nil {
return fmt.Errorf("unable to decode package.json author object: %w", err)
}
auths[i] = auth
}
*p = auths
return nil
}
// If we get here, it means neither format matched
return fmt.Errorf("unable to parse package.json authors field: expected array of strings or array of objects")
}
func (p people) String() string {
if len(p) == 0 {
return ""
}
authorStrings := make([]string, len(p))
for i, auth := range p {
authorStrings[i] = auth.AuthorString()
}
return strings.Join(authorStrings, ", ")
}
@@ -152,7 +152,7 @@ func TestParsePackageJSON(t *testing.T) {
Metadata: pkg.NpmPackage{
Name: "function-bind",
Version: "1.1.1",
Author: "Raynos <raynos2@gmail.com>",
Author: "Raynos <raynos2@gmail.com>, Raynos, Jordan Harband (https://github.com/ljharb)",
Homepage: "https://github.com/Raynos/function-bind",
URL: "git://github.com/Raynos/function-bind.git",
Description: "Implementation of Function.prototype.bind",
@@ -202,6 +202,132 @@ func TestParsePackageJSON(t *testing.T) {
},
},
},
{
Fixture: "test-fixtures/pkg-json/package-authors-array.json",
ExpectedPkg: pkg.Package{
Name: "npm",
Version: "6.14.6",
PURL: "pkg:npm/npm@6.14.6",
Type: pkg.NpmPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-authors-array.json")),
),
Language: pkg.JavaScript,
Metadata: pkg.NpmPackage{
Name: "npm",
Version: "6.14.6",
Author: "Harry Potter <hp@hogwards.com> (http://youknowwho.com/), John Smith <j.smith@something.com> (http://awebsite.com/)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Description: "a package manager for JavaScript",
},
},
},
{
Fixture: "test-fixtures/pkg-json/package-authors-objects.json",
ExpectedPkg: pkg.Package{
Name: "npm",
Version: "6.14.6",
PURL: "pkg:npm/npm@6.14.6",
Type: pkg.NpmPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-authors-objects.json")),
),
Language: pkg.JavaScript,
Metadata: pkg.NpmPackage{
Name: "npm",
Version: "6.14.6",
Author: "Harry Potter <hp@hogwards.com> (http://youknowwho.com/), John Smith <j.smith@something.com> (http://awebsite.com/)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Description: "a package manager for JavaScript",
},
},
},
{
Fixture: "test-fixtures/pkg-json/package-both-author-and-authors.json",
ExpectedPkg: pkg.Package{
Name: "npm",
Version: "6.14.6",
PURL: "pkg:npm/npm@6.14.6",
Type: pkg.NpmPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-both-author-and-authors.json")),
),
Language: pkg.JavaScript,
Metadata: pkg.NpmPackage{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me), Harry Potter <hp@hogwards.com> (http://youknowwho.com/), John Smith <j.smith@something.com> (http://awebsite.com/)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Description: "a package manager for JavaScript",
},
},
},
{
Fixture: "test-fixtures/pkg-json/package-contributors.json",
ExpectedPkg: pkg.Package{
Name: "npm",
Version: "6.14.6",
PURL: "pkg:npm/npm@6.14.6",
Type: pkg.NpmPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-contributors.json")),
),
Language: pkg.JavaScript,
Metadata: pkg.NpmPackage{
Name: "npm",
Version: "6.14.6",
Author: "Alice Contributor <alice@example.com>, Bob Helper <bob@example.com>",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Description: "a package manager for JavaScript",
},
},
},
{
Fixture: "test-fixtures/pkg-json/package-maintainers.json",
ExpectedPkg: pkg.Package{
Name: "npm",
Version: "6.14.6",
PURL: "pkg:npm/npm@6.14.6",
Type: pkg.NpmPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-maintainers.json")),
),
Language: pkg.JavaScript,
Metadata: pkg.NpmPackage{
Name: "npm",
Version: "6.14.6",
Author: "Charlie Maintainer <charlie@example.com>, Diana Keeper <diana@example.com>",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Description: "a package manager for JavaScript",
},
},
},
{
Fixture: "test-fixtures/pkg-json/package-all-author-fields.json",
ExpectedPkg: pkg.Package{
Name: "npm",
Version: "6.14.6",
PURL: "pkg:npm/npm@6.14.6",
Type: pkg.NpmPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-all-author-fields.json")),
),
Language: pkg.JavaScript,
Metadata: pkg.NpmPackage{
Name: "npm",
Version: "6.14.6",
Author: "Main Author <main@example.com>, Second Author <second@example.com>, Contrib One <contrib1@example.com>, Maintainer One <maintain1@example.com>",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Description: "a package manager for JavaScript",
},
},
},
}
for _, test := range tests {
@@ -8,7 +8,7 @@ import (
"strconv"
"strings"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
@@ -0,0 +1,24 @@
{
"version": "6.14.6",
"name": "npm",
"description": "a package manager for JavaScript",
"homepage": "https://docs.npmjs.com/",
"author": "Main Author <main@example.com>",
"authors": [
"Second Author <second@example.com>"
],
"contributors": [
"Contrib One <contrib1@example.com>"
],
"maintainers": [
{
"name": "Maintainer One",
"email": "maintain1@example.com"
}
],
"repository": {
"type": "git",
"url": "https://github.com/npm/cli"
},
"license": "Artistic-2.0"
}
@@ -0,0 +1,15 @@
{
"version": "6.14.6",
"name": "npm",
"description": "a package manager for JavaScript",
"homepage": "https://docs.npmjs.com/",
"authors": [
"Harry Potter <hp@hogwards.com> (http://youknowwho.com/)",
"John Smith <j.smith@something.com> (http://awebsite.com/)"
],
"repository": {
"type": "git",
"url": "https://github.com/npm/cli"
},
"license": "Artistic-2.0"
}
@@ -0,0 +1,23 @@
{
"version": "6.14.6",
"name": "npm",
"description": "a package manager for JavaScript",
"homepage": "https://docs.npmjs.com/",
"authors": [
{
"name": "Harry Potter",
"email": "hp@hogwards.com",
"url": "http://youknowwho.com/"
},
{
"name": "John Smith",
"email": "j.smith@something.com",
"url": "http://awebsite.com/"
}
],
"repository": {
"type": "git",
"url": "https://github.com/npm/cli"
},
"license": "Artistic-2.0"
}
@@ -0,0 +1,16 @@
{
"version": "6.14.6",
"name": "npm",
"description": "a package manager for JavaScript",
"homepage": "https://docs.npmjs.com/",
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
"authors": [
"Harry Potter <hp@hogwards.com> (http://youknowwho.com/)",
"John Smith <j.smith@something.com> (http://awebsite.com/)"
],
"repository": {
"type": "git",
"url": "https://github.com/npm/cli"
},
"license": "Artistic-2.0"
}
@@ -0,0 +1,15 @@
{
"version": "6.14.6",
"name": "npm",
"description": "a package manager for JavaScript",
"homepage": "https://docs.npmjs.com/",
"contributors": [
"Alice Contributor <alice@example.com>",
"Bob Helper <bob@example.com>"
],
"repository": {
"type": "git",
"url": "https://github.com/npm/cli"
},
"license": "Artistic-2.0"
}
@@ -0,0 +1,21 @@
{
"version": "6.14.6",
"name": "npm",
"description": "a package manager for JavaScript",
"homepage": "https://docs.npmjs.com/",
"maintainers": [
{
"name": "Charlie Maintainer",
"email": "charlie@example.com"
},
{
"name": "Diana Keeper",
"email": "diana@example.com"
}
],
"repository": {
"type": "git",
"url": "https://github.com/npm/cli"
},
"license": "Artistic-2.0"
}
+6 -10
View File
@@ -5,8 +5,8 @@ package redhat
import (
"database/sql"
"fmt"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
@@ -15,15 +15,11 @@ import (
// NewDBCataloger returns a new RPM DB cataloger object.
func NewDBCataloger() pkg.Cataloger {
// check if a sqlite driver is available
if !isSqliteDriverAvailable() {
log.Debugf("sqlite driver is not available, newer RPM databases might not be cataloged")
}
return generic.NewCataloger("rpm-db-cataloger").
WithParserByGlobs(parseRpmDB, pkg.RpmDBGlob).
WithParserByGlobs(parseRpmManifest, pkg.RpmManifestGlob).
WithProcessors(dependency.Processor(dbEntryDependencySpecifier), denySelfReferences)
WithProcessors(dependency.Processor(dbEntryDependencySpecifier), denySelfReferences).
WithChecks(ensureSqliteDriverAvailable)
}
func denySelfReferences(pkgs []pkg.Package, rels []artifact.Relationship, err error) ([]pkg.Package, []artifact.Relationship, error) {
@@ -47,11 +43,11 @@ func NewArchiveCataloger() pkg.Cataloger {
WithParserByGlobs(parseRpmArchive, "**/*.rpm")
}
func isSqliteDriverAvailable() bool {
func ensureSqliteDriverAvailable() error {
db, err := sql.Open("sqlite", ":memory:")
if err != nil {
return false
return fmt.Errorf("sqlite driver is required for cataloging newer RPM databases, none registered: %v", err)
}
_ = db.Close()
return true
return nil
}
@@ -0,0 +1,22 @@
package sqlitetest
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/internal/fileresolver"
"github.com/anchore/syft/syft/pkg/cataloger/redhat"
)
func Test_noSQLiteDriverError(t *testing.T) {
// this test package does must not import the sqlite library
file := "../test-fixtures/Packages"
resolver, err := fileresolver.NewFromFile(file, file)
require.NoError(t, err)
cataloger := redhat.NewDBCataloger()
_, _, err = cataloger.Catalog(context.TODO(), resolver)
require.ErrorContains(t, err, "sqlite")
}
@@ -6,7 +6,7 @@ import (
"io"
"strings"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
+61
View File
@@ -0,0 +1,61 @@
package pkg
import (
"sort"
"github.com/scylladb/go-set/strset"
)
type CondaPathData struct {
Path string `json:"_path"`
PathType string `json:"path_type"`
SHA256 string `json:"sha256"`
SHA256InPrefix string `json:"sha256_in_prefix"`
SizeInBytes int64 `json:"size_in_bytes"`
}
type CondaPathsData struct {
PathsVersion int `json:"paths_version"`
Paths []CondaPathData `json:"paths"`
}
type CondaLink struct {
Source string `json:"source"`
Type int `json:"type"`
}
type CondaMetaPackage struct {
Arch string `json:"arch,omitempty"`
Name string `json:"name"`
Version string `json:"version"`
Build string `json:"build"`
BuildNumber int `json:"build_number"`
Channel string `json:"channel,omitempty"`
Subdir string `json:"subdir,omitempty"`
Noarch string `json:"noarch,omitempty"`
License string `json:"license,omitempty"`
LicenseFamily string `json:"license_family,omitempty"`
MD5 string `json:"md5,omitempty"`
SHA256 string `json:"sha256,omitempty"`
Size int64 `json:"size,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Filename string `json:"fn,omitempty"`
URL string `json:"url,omitempty"`
ExtractedPackageDir string `json:"extracted_package_dir,omitempty"`
Depends []string `json:"depends,omitempty"`
Files []string `json:"files,omitempty"`
PathsData *CondaPathsData `json:"paths_data,omitempty"`
Link *CondaLink `json:"link,omitempty"`
}
func (m CondaMetaPackage) OwnedFiles() (result []string) {
s := strset.New()
for _, f := range m.Files {
if f != "" {
s.Add(f)
}
}
result = s.List()
sort.Strings(result)
return result
}
+9
View File
@@ -15,3 +15,12 @@ type GolangBinaryBuildinfoEntry struct {
type GolangModuleEntry struct {
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
}
// GolangSourceEntry represents all captured data for a Golang package found through source analysis
type GolangSourceEntry struct {
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
OperatingSystem string `json:"os,omitempty" cyclonedx:"os"`
Architecture string `json:"architecture,omitempty" cyclonedx:"architecture"`
BuildTags string `json:"buildTags,omitempty" cyclonedx:"buildTags"`
CgoEnabled bool `json:"cgoEnabled" cyclonedx:"cgoEnabled"`
}
+6
View File
@@ -16,6 +16,7 @@ const (
BitnamiPkg Type = "bitnami"
CocoapodsPkg Type = "pod"
ConanPkg Type = "conan"
CondaPkg Type = "conda"
DartPubPkg Type = "dart-pub"
DebPkg Type = "deb"
DotnetPkg Type = "dotnet"
@@ -59,6 +60,7 @@ var AllPkgs = []Type{
BitnamiPkg,
CocoapodsPkg,
ConanPkg,
CondaPkg,
DartPubPkg,
DebPkg,
DotnetPkg,
@@ -109,6 +111,8 @@ func (t Type) PackageURLType() string {
return packageurl.TypeCocoapods
case ConanPkg:
return packageurl.TypeConan
case CondaPkg:
return packageurl.TypeGeneric
case DartPubPkg:
return packageurl.TypePub
case DebPkg:
@@ -206,6 +210,8 @@ func TypeByName(name string) Type {
return GemPkg
case "cargo", "crate":
return RustPkg
case "conda":
return CondaPkg
case packageurl.TypePub:
return DartPubPkg
case "dotnet": // here to support legacy use cases
+5
View File
@@ -130,6 +130,11 @@ func TestTypeFromPURL(t *testing.T) {
purl: "pkg:opam/ocaml-base-compiler@5.2.0",
expected: OpamPkg,
},
{
name: "conda",
purl: "pkg:generic/conda@1.2.3",
expected: CondaPkg,
},
}
var pkgTypes = strset.New()
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"testing"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
"go.yaml.in/yaml/v3"
)
func Test_configLoading(t *testing.T) {