Compare commits

...
16 Commits
Author SHA1 Message Date
anchore-actions-token-generator[bot]andspiffcs dd30c99bc2 chore(deps): update bootstrap tools to latest versions (#1728)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: spiffcs <spiffcs@users.noreply.github.com>
2023-04-11 10:13:14 -04:00
Shane Dell 16ebcb2455 Add support for nar files. (#1727)
- Update README.md to show that nar is now supported.
- Created a java-archives/example.nar so that the tests wouldn't break.
- Add nar glob and as an option for pkgType.

Closes #1701

Signed-off-by: Shane Dell <shanedell100@gmail.com>
2023-04-11 09:41:49 -04:00
Alex Goodman 305838582b add highlevel details about catalogers (#1726)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2023-04-10 13:11:22 -04:00
dependabot[bot] a260fb2774 chore(deps): bump golang.org/x/net from 0.8.0 to 0.9.0 (#1722)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.8.0 to 0.9.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.8.0...v0.9.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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>
2023-04-07 15:58:21 -04:00
anchore-actions-token-generator[bot]andkzantow f83cae35f2 chore(deps): update stereoscope to e95d60a265e384df29b7a139f5c5402d6ad72e06 (#1721)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: kzantow <kzantow@users.noreply.github.com>
2023-04-07 08:48:17 -04:00
Henry Sachs 0fed17f1c8 feat: gradle lockfile support (#1719)
Signed-off-by: Henry Sachs <Henry.Sachs@deutschebahn.com>
2023-04-06 14:58:28 -04:00
dependabot[bot] da44db92e9 chore(deps): bump github.com/docker/docker (#1715) 2023-04-06 13:44:51 +00:00
dependabot[bot] 4a499c946e chore(deps): bump golang.org/x/mod from 0.9.0 to 0.10.0 (#1713) 2023-04-06 13:44:41 +00:00
dependabot[bot] 99c28a94a4 chore(deps): bump golang.org/x/term from 0.6.0 to 0.7.0 (#1714) 2023-04-06 13:36:16 +00:00
dependabot[bot] f7ac4e98af chore(deps): bump github.com/spf13/cobra from 1.6.1 to 1.7.0 (#1716)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.6.1...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  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>
2023-04-06 09:34:59 -04:00
dependabot[bot] 394ec8d215 chore(deps): bump peter-evans/create-pull-request from 4 to 5 (#1712)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4 to 5.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v4...v5)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 19:04:26 -04:00
Keith Zantow 7845381331 chore: update tools-golang to v0.5.0 (#1717)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-04-05 13:59:52 -04:00
7464079a09 Add Nix cataloger (#1696)
* Add Basic Nix Cataloger

Signed-off-by: Julio Tain Sueiras <juliosueiras@gmail.com>

* Update nix def for the latest syft definition

Signed-off-by: Julio Tain Sueiras <juliosueiras@gmail.com>

* capture nix package files on pkg.NixStoreMetadata

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* fix unit tests and linting

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update JSON schema

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* address review comments

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* Update syft/pkg/cataloger/nix/parse_nix_store_path_test.go

Co-authored-by: Florian Klink <flokli@flokli.de>
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* support unstable version conventions

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update json schema relative to main branch

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update syft json with v7.1.1 schema

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* fix CLI tests

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* remove extra continue statement

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add Nix to list of supported ecosystems

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

---------

Signed-off-by: Julio Tain Sueiras <juliosueiras@gmail.com>
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
Co-authored-by: Julio Tain Sueiras <juliosueiras@gmail.com>
Co-authored-by: Florian Klink <flokli@flokli.de>
2023-04-04 10:53:56 -04:00
Alex Goodman 8a574c9ed9 refactor spdx tooling test to reduce intermittent failures (#1707)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2023-04-03 18:43:28 +00:00
Alex Goodman 681d250fdc Capture file ownership relationships from portage ecosystem (#1702)
* add portage as file owners

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* fix tests

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* fix linting

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update json schema with NPM files

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

---------

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2023-04-03 09:46:18 -04:00
Keith Zantow 2022ffa0e5 chore: update deprecated set-output calls (#1705)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-04-03 09:36:11 -04:00
56 changed files with 4552 additions and 192 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
OUTPUT="${OUTPUT//'%'/'%25'}" # URL encode all '%' characters
OUTPUT="${OUTPUT//$'\n'/'%0A'}" # URL encode all '\n' characters
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
echo "::set-output name=result::$OUTPUT"
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v3
with:
+1 -1
View File
@@ -58,7 +58,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@v4
- uses: peter-evans/create-pull-request@v5
with:
signoff: true
delete-branch: true
@@ -29,7 +29,7 @@ jobs:
go mod tidy
# export the version for use with create-pull-request
echo "::set-output name=LATEST_VERSION::$LATEST_VERSION"
echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT
id: latest-version
- uses: tibdex/github-app-token@v1
@@ -38,7 +38,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@v4
- uses: peter-evans/create-pull-request@v5
with:
signoff: true
delete-branch: true
+9 -1
View File
@@ -119,7 +119,15 @@ sequenceDiagram
Catalogers are the way in which syft is able to identify and construct packages given some amount of source metadata.
For example, Syft can locate and process `package-lock.json` files when performing filesystem scans.
See: [how to specify file globs](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21)
and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) fora quick review.
and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) for a quick review.
From a high level catalogers have the following properties:
- They are independent from one another. The java cataloger has no idea of the processes, assumptions, or results of the python cataloger, for example.
- They do not know what source is being analyzed. Are we analyzing a local directory? an image? if so, the squashed representation or all layers? The catalogers do not know the answers to these questions. Only that there is an interface to query for file paths and contents from an underlying "source" being scanned.
- Packages created by the cataloger should not be mutated after they are created. There is one exception made for adding CPEs to a package after the cataloging phase, but that will most likely be moved back into the cataloger in the future.
#### Building a new Cataloger
+1 -1
View File
@@ -14,7 +14,7 @@ GOLANGCILINT_VERSION := v1.52.2
GOSIMPORTS_VERSION := v0.3.8
BOUNCER_VERSION := v0.4.0
CHRONICLE_VERSION := v0.6.0
GORELEASER_VERSION := v1.16.2
GORELEASER_VERSION := v1.17.0
YAJSV_VERSION := v1.4.1
COSIGN_VERSION := v1.13.1
QUILL_VERSION := v0.2.0
+2 -1
View File
@@ -42,9 +42,10 @@ For commercial support options with Syft or Grype, please [contact Anchore](http
- Erlang (rebar3)
- Go (go.mod, Go binaries)
- Haskell (cabal, stack)
- Java (jar, ear, war, par, sar, native-image)
- Java (jar, ear, war, par, sar, nar, native-image)
- JavaScript (npm, yarn)
- Jenkins Plugins (jpi, hpi)
- Nix (outputs in /nix/store)
- PHP (composer)
- Python (wheel, egg, poetry, requirements.txt)
- Red Hat (rpm)
+9 -9
View File
@@ -31,9 +31,9 @@ require (
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.3.1
github.com/sirupsen/logrus v1.9.0
github.com/spdx/tools-golang v0.5.0-rc1
github.com/spdx/tools-golang v0.5.0
github.com/spf13/afero v1.9.5
github.com/spf13/cobra v1.6.1
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.2
@@ -42,9 +42,9 @@ require (
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/mod v0.9.0
golang.org/x/net v0.8.0
golang.org/x/term v0.6.0
golang.org/x/mod v0.10.0
golang.org/x/net v0.9.0
golang.org/x/term v0.7.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -52,8 +52,8 @@ require (
github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce
github.com/Masterminds/sprig/v3 v3.2.3
github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8
github.com/anchore/stereoscope v0.0.0-20230323161519-d7551b7f46f5
github.com/docker/docker v23.0.2+incompatible
github.com/anchore/stereoscope v0.0.0-20230406143206-e95d60a265e3
github.com/docker/docker v23.0.3+incompatible
github.com/google/go-containerregistry v0.14.0
github.com/google/licensecheck v0.3.1
github.com/invopop/jsonschema v0.7.0
@@ -127,8 +127,8 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
+18 -20
View File
@@ -90,8 +90,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.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8=
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.0-20230323161519-d7551b7f46f5 h1:2I4m/L4INVVyv0yBRuCadwxM+nuJoaKj9iBNPyXbSO4=
github.com/anchore/stereoscope v0.0.0-20230323161519-d7551b7f46f5/go.mod h1:IihP/SUVHP94PBwIP2bepOB/c0MVadcII7lxo13Ijzs=
github.com/anchore/stereoscope v0.0.0-20230406143206-e95d60a265e3 h1:F662TNGcpBfB1ZmCJJUot4EBHGxCKvTPvub01qaWxX0=
github.com/anchore/stereoscope v0.0.0-20230406143206-e95d60a265e3/go.mod h1:u44RadbOJB103GwtYzJ2Kdad9J+SmZMW98jCYBJkSzs=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@@ -150,8 +150,8 @@ github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy
github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v23.0.2+incompatible h1:q81C2qQ/EhPm8COZMUGOQYh4qLv4Xu6CXELJ3WK/mlU=
github.com/docker/docker v23.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -258,7 +258,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw=
@@ -345,7 +344,6 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So=
@@ -519,8 +517,8 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.5.0-rc1 h1:ooCSe48QatlidqEFd+nSI308tyeNTR6NJvauUj3ApX8=
github.com/spdx/tools-golang v0.5.0-rc1/go.mod h1:LI6onw172PdO57Ob/hgnLDD4Y2PMnroeNT3wO/2WJJI=
github.com/spdx/tools-golang v0.5.0 h1:/fqihV2Jna7fmow65dHpgKNsilgLK7ICpd2tkCnPEyY=
github.com/spdx/tools-golang v0.5.0/go.mod h1:kkGlrSXXfHwuSzHQZJRV3aKu9ZXCq/MSf2+xyiJH1lM=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
@@ -530,8 +528,8 @@ github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -674,8 +672,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -722,8 +720,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -829,15 +827,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -848,8 +846,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+1 -1
View File
@@ -6,5 +6,5 @@ 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 = "7.0.1"
JSONSchemaVersion = "7.1.1"
)
+1
View File
@@ -45,6 +45,7 @@ type artifactMetadataContainer struct {
Hackage pkg.HackageMetadata
Java pkg.JavaMetadata
KbPackage pkg.KbPackageMetadata
Nix pkg.NixStoreMetadata
NpmPackage pkg.NpmPackageJSONMetadata
NpmPackageLock pkg.NpmPackageLockJSONMetadata
MixLock pkg.MixLockMetadata
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -45,6 +45,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from cabal or stack manifest files"
case pkg.HexPkg:
answer = "acquired package info from rebar3 or mix manifest file"
case pkg.NixPkg:
answer = "acquired package info from nix store path"
default:
answer = "acquired package info from the following paths"
}
@@ -199,6 +199,14 @@ func Test_SourceInfo(t *testing.T) {
"from rebar3 or mix manifest file",
},
},
{
input: pkg.Package{
Type: pkg.NixPkg,
},
expected: []string{
"from nix store path",
},
},
}
var pkgTypes []pkg.Type
for _, test := range tests {
@@ -89,7 +89,7 @@
}
},
"schema": {
"version": "6.2.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json"
"version": "7.1.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
}
}
@@ -185,7 +185,7 @@
}
},
"schema": {
"version": "6.2.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json"
"version": "7.1.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
}
}
@@ -112,7 +112,7 @@
}
},
"schema": {
"version": "6.2.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json"
"version": "7.1.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
}
}
+6
View File
@@ -23,6 +23,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
"github.com/anchore/syft/syft/pkg/cataloger/nix"
"github.com/anchore/syft/syft/pkg/cataloger/php"
"github.com/anchore/syft/syft/pkg/cataloger/portage"
"github.com/anchore/syft/syft/pkg/cataloger/python"
@@ -51,6 +52,7 @@ func ImageCatalogers(cfg Config) []pkg.Cataloger {
golang.NewGoModuleBinaryCataloger(cfg.Go()),
dotnet.NewDotnetDepsCataloger(),
portage.NewPortageCataloger(),
nix.NewStoreCataloger(),
sbom.NewSBOMCataloger(),
binary.NewCataloger(),
}, cfg.Catalogers)
@@ -71,6 +73,7 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(),
java.NewNativeImageCataloger(),
java.NewJavaGradleLockfileCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModuleBinaryCataloger(cfg.Go()),
golang.NewGoModFileCataloger(cfg.Go()),
@@ -85,6 +88,7 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
binary.NewCataloger(),
elixir.NewMixLockCataloger(),
erlang.NewRebarLockCataloger(),
nix.NewStoreCataloger(),
}, cfg.Catalogers)
}
@@ -104,6 +108,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(),
java.NewNativeImageCataloger(),
java.NewJavaGradleLockfileCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModuleBinaryCataloger(cfg.Go()),
golang.NewGoModFileCataloger(cfg.Go()),
@@ -121,6 +126,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
binary.NewCataloger(),
elixir.NewMixLockCataloger(),
erlang.NewRebarLockCataloger(),
nix.NewStoreCataloger(),
}, cfg.Catalogers)
}
+1 -1
View File
@@ -108,7 +108,7 @@ func (a archiveFilename) extension() string {
func (a archiveFilename) pkgType() pkg.Type {
switch strings.ToLower(a.extension()) {
case "jar", "war", "ear", "lpkg", "par", "sar":
case "jar", "war", "ear", "lpkg", "par", "sar", "nar":
return pkg.JavaPkg
case "jpi", "hpi":
return pkg.JenkinsPluginPkg
@@ -79,6 +79,13 @@ func TestExtractInfoFromJavaArchiveFilename(t *testing.T) {
name: "pkg-extra-field-maven",
ty: pkg.JavaPkg,
},
{
filename: "pkg-extra-field-maven-4.3.2-rc1.nar",
version: "4.3.2-rc1",
extension: "nar",
name: "pkg-extra-field-maven",
ty: pkg.JavaPkg,
},
{
filename: "/some/path/pkg-extra-field-maven-4.3.2-rc1.jpi",
version: "4.3.2-rc1",
@@ -24,6 +24,7 @@ var archiveFormatGlobs = []string{
"**/*.ear",
"**/*.par",
"**/*.sar",
"**/*.nar",
"**/*.jpi",
"**/*.hpi",
"**/*.lpkg", // Zip-compressed package used to deploy applications
@@ -159,10 +159,39 @@ func TestParseJar(t *testing.T) {
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Main-Class": "hello.HelloWorld",
},
},
},
},
"joda-time": {
Name: "joda-time",
Version: "2.2",
PURL: "pkg:maven/joda-time/joda-time@2.2",
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
// ensure that nested packages with different names than that of the parent are appended as
// a suffix on the virtual path
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar:joda-time",
PomProperties: &pkg.PomProperties{
Path: "META-INF/maven/joda-time/joda-time/pom.properties",
GroupID: "joda-time",
ArtifactID: "joda-time",
Version: "2.2",
},
PomProject: &pkg.PomProject{
Path: "META-INF/maven/joda-time/joda-time/pom.xml",
GroupID: "joda-time",
ArtifactID: "joda-time",
Version: "2.2",
Name: "Joda time",
Description: "Date and time library to replace JDK date handling",
URL: "http://joda-time.sourceforge.net",
},
},
},
},
},
{
+8
View File
@@ -31,3 +31,11 @@ func NewJavaPomCataloger() *generic.Cataloger {
return generic.NewCataloger("java-pom-cataloger").
WithParserByGlobs(parserPomXML, "**/pom.xml")
}
// NewJavaGradleLockfileCataloger returns a cataloger capable of parsing
// dependencies from a gradle.lockfile file.
// older versions of lockfiles aren't supported yet
func NewJavaGradleLockfileCataloger() *generic.Cataloger {
return generic.NewCataloger("java-gradle-lockfile-cataloger").
WithParserByGlobs(parseGradleLockfile, gradleLockfileGlob)
}
@@ -21,6 +21,7 @@ func Test_ArchiveCataloger_Globs(t *testing.T) {
"java-archives/example.ear",
"java-archives/example.par",
"java-archives/example.sar",
"java-archives/example.nar",
"java-archives/example.jpi",
"java-archives/example.hpi",
"java-archives/example.lpkg",
@@ -0,0 +1,63 @@
package java
import (
"bufio"
"strings"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source"
)
const gradleLockfileGlob = "**/gradle.lockfile*"
// Dependency represents a single dependency in the gradle.lockfile file
type LockfileDependency struct {
Group string
Name string
Version string
}
func parseGradleLockfile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var pkgs []pkg.Package
// Create a new scanner to read the file
scanner := bufio.NewScanner(reader)
// Create slices to hold the dependencies and plugins
dependencies := []LockfileDependency{}
// Loop over all lines in the file
for scanner.Scan() {
line := scanner.Text()
// Trim leading and trailing whitespace from the line
line = strings.TrimSpace(line)
groupNameVersion := line
groupNameVersion = strings.Split(groupNameVersion, "=")[0]
parts := strings.Split(groupNameVersion, ":")
// we have a version directly specified
if len(parts) == 3 {
// Create a new Dependency struct and add it to the dependencies slice
dep := LockfileDependency{Group: parts[0], Name: parts[1], Version: parts[2]}
dependencies = append(dependencies, dep)
}
}
// map the dependencies
for _, dep := range dependencies {
mappedPkg := pkg.Package{
Name: dep.Name,
Version: dep.Version,
Locations: source.NewLocationSet(reader.Location),
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
}
pkgs = append(pkgs, mappedPkg)
}
return pkgs, nil, nil
}
@@ -0,0 +1,52 @@
package java
import (
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source"
)
func Test_parserGradleLockfile(t *testing.T) {
tests := []struct {
input string
expected []pkg.Package
}{
{
input: "test-fixtures/gradle/gradle.lockfile",
expected: []pkg.Package{
{
Name: "hamcrest-core",
Version: "1.3",
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
},
{
Name: "joda-time",
Version: "2.2",
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
},
{
Name: "junit",
Version: "4.12",
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
},
},
},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
for i := range test.expected {
test.expected[i].Locations.Add(source.NewLocation(test.input))
}
pkgtest.TestFileParser(t, test.input, parseGradleLockfile, test.expected, nil)
})
}
}
@@ -0,0 +1 @@
example archive
@@ -0,0 +1 @@
.gradle
@@ -0,0 +1,58 @@
plugins {
id 'java'
id 'eclipse'
id 'application'
}
mainClassName = 'hello.HelloWorld'
dependencyLocking {
lockAllConfigurations()
}
// tag::repositories[]
repositories {
mavenCentral()
}
// end::repositories[]
// tag::dependencies[]
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
implementation "joda-time:joda-time:2.2"
testImplementation "junit:junit:4.12"
}
// end::dependencies[]
// tag::jar[]
jar {
archivesBaseName = 'example-java-app-gradle'
version = '0.1.0'
manifest {
attributes(
'Main-Class': 'hello.HelloWorld'
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
// end::jar[]
// tag::wrapper[]
// end::wrapper[]
// to invoke: gradle resolveAndLockAll --write-locks
tasks.register('resolveAndLockAll') {
notCompatibleWithConfigurationCache("Filters configurations at execution time")
doFirst {
assert gradle.startParameter.writeDependencyLocks
}
doLast {
configurations.findAll {
// Add any custom filtering on the configurations to be resolved
it.canBeResolved
}.each { it.resolve() }
}
}
@@ -0,0 +1,7 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
joda-time:joda-time:2.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
junit:junit:4.12=testCompileClasspath,testRuntimeClasspath
org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath
empty=annotationProcessor,testAnnotationProcessor
@@ -4,7 +4,7 @@ set -uxe
# note: this can be easily done in a 1-liner, however circle CI does NOT allow volume mounts from the host in docker executors (since they are on remote hosts, where the host files are inaccessible)
PKGSDIR=$1
CTRID=$(docker create -u "$(id -u):$(id -g)" -v /example-java-app -w /example-java-app gradle:6.8.3-jdk gradle build)
CTRID=$(docker create -u "$(id -u):$(id -g)" -v /example-java-app -w /example-java-app gradle:8.0.2-jdk gradle build)
function cleanup() {
docker rm "${CTRID}"
@@ -1,31 +1,58 @@
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'
plugins {
id 'java'
id 'eclipse'
id 'application'
}
mainClassName = 'hello.HelloWorld'
dependencyLocking {
lockAllConfigurations()
}
// tag::repositories[]
repositories {
mavenCentral()
}
// end::repositories[]
// tag::jar[]
jar {
baseName = 'example-java-app-gradle'
version = '0.1.0'
}
// end::jar[]
// tag::dependencies[]
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile "joda-time:joda-time:2.2"
testCompile "junit:junit:4.12"
implementation "joda-time:joda-time:2.2"
testImplementation "junit:junit:4.12"
}
// end::dependencies[]
// tag::jar[]
jar {
archivesBaseName = 'example-java-app-gradle'
version = '0.1.0'
manifest {
attributes(
'Main-Class': 'hello.HelloWorld'
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
// end::jar[]
// tag::wrapper[]
// end::wrapper[]
// end::wrapper[]
// to invoke: gradle resolveAndLockAll --write-locks
tasks.register('resolveAndLockAll') {
notCompatibleWithConfigurationCache("Filters configurations at execution time")
doFirst {
assert gradle.startParameter.writeDependencyLocks
}
doLast {
configurations.findAll {
// Add any custom filtering on the configurations to be resolved
it.canBeResolved
}.each { it.resolve() }
}
}
@@ -0,0 +1,7 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
joda-time:joda-time:2.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
junit:junit:4.12=testCompileClasspath,testRuntimeClasspath
org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath
empty=annotationProcessor,testAnnotationProcessor
+8 -7
View File
@@ -28,13 +28,14 @@ func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Pack
Type: pkg.NpmPkg,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: u.Name,
Version: u.Version,
Author: u.Author.AuthorString(),
Homepage: u.Homepage,
URL: u.Repository.URL,
Licenses: licenses,
Private: u.Private,
Name: u.Name,
Version: u.Version,
Author: u.Author.AuthorString(),
Homepage: u.Homepage,
URL: u.Repository.URL,
Licenses: licenses,
Private: u.Private,
Description: u.Description,
},
}
@@ -26,12 +26,13 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"Artistic-2.0"},
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"Artistic-2.0"},
Description: "a package manager for JavaScript",
},
},
},
@@ -46,12 +47,13 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"ISC"},
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"ISC"},
Description: "a package manager for JavaScript",
},
},
},
@@ -66,12 +68,13 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"MIT", "Apache-2.0"},
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"MIT", "Apache-2.0"},
Description: "a package manager for JavaScript",
},
},
},
@@ -86,12 +89,13 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: nil,
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: nil,
Description: "a package manager for JavaScript",
},
},
},
@@ -106,12 +110,13 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{},
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{},
Description: "a package manager for JavaScript",
},
},
},
@@ -126,12 +131,13 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"Artistic-2.0"},
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"Artistic-2.0"},
Description: "a package manager for JavaScript",
},
},
},
@@ -146,12 +152,13 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "function-bind",
Version: "1.1.1",
Author: "Raynos <raynos2@gmail.com>",
Homepage: "https://github.com/Raynos/function-bind",
URL: "git://github.com/Raynos/function-bind.git",
Licenses: []string{"MIT"},
Name: "function-bind",
Version: "1.1.1",
Author: "Raynos <raynos2@gmail.com>",
Homepage: "https://github.com/Raynos/function-bind",
URL: "git://github.com/Raynos/function-bind.git",
Licenses: []string{"MIT"},
Description: "Implementation of Function.prototype.bind",
},
},
},
@@ -166,13 +173,14 @@ func TestParsePackageJSON(t *testing.T) {
Language: pkg.JavaScript,
MetadataType: pkg.NpmPackageJSONMetadataType,
Metadata: pkg.NpmPackageJSONMetadata{
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"Artistic-2.0"},
Private: true,
Name: "npm",
Version: "6.14.6",
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
Homepage: "https://docs.npmjs.com/",
URL: "https://github.com/npm/cli",
Licenses: []string{"Artistic-2.0"},
Private: true,
Description: "a package manager for JavaScript",
},
},
},
+101
View File
@@ -0,0 +1,101 @@
package nix
import (
"fmt"
"github.com/bmatcuk/doublestar/v4"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
const (
catalogerName = "nix-store-cataloger"
nixStoreGlob = "**/nix/store/*"
)
// StoreCataloger finds package outputs installed in the Nix store location (/nix/store/*).
type StoreCataloger struct{}
func NewStoreCataloger() *StoreCataloger {
return &StoreCataloger{}
}
func (c *StoreCataloger) Name() string {
return catalogerName
}
func (c *StoreCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
// we want to search for only directories, which isn't possible via the stereoscope API, so we need to apply the glob manually on all returned paths
var pkgs []pkg.Package
var filesByPath = make(map[string]*source.LocationSet)
for location := range resolver.AllLocations() {
matchesStorePath, err := doublestar.Match(nixStoreGlob, location.RealPath)
if err != nil {
return nil, nil, fmt.Errorf("failed to match nix store path: %w", err)
}
parentStorePath := findParentNixStorePath(location.RealPath)
if parentStorePath != "" {
if _, ok := filesByPath[parentStorePath]; !ok {
s := source.NewLocationSet()
filesByPath[parentStorePath] = &s
}
filesByPath[parentStorePath].Add(location)
}
if !matchesStorePath {
continue
}
storePath := parseNixStorePath(location.RealPath)
if storePath == nil || !storePath.isValidPackage() {
continue
}
p := newNixStorePackage(*storePath, location)
pkgs = append(pkgs, p)
}
// add file sets to packages
for i := range pkgs {
p := &pkgs[i]
locations := p.Locations.ToSlice()
if len(locations) == 0 {
log.WithFields("package", p.Name).Warn("nix package has no evidence locations associated")
continue
}
parentStorePath := locations[0].RealPath
files, ok := filesByPath[parentStorePath]
if !ok {
log.WithFields("path", parentStorePath, "nix-store-path", parentStorePath).Warn("found a nix store file for a non-existent package")
continue
}
appendFiles(p, files.ToSlice()...)
}
return pkgs, nil, nil
}
func appendFiles(p *pkg.Package, location ...source.Location) {
metadata, ok := p.Metadata.(pkg.NixStoreMetadata)
if !ok {
log.WithFields("package", p.Name).Warn("nix package metadata missing")
return
}
for _, l := range location {
metadata.Files = append(metadata.Files, l.RealPath)
}
if metadata.Files == nil {
// note: we always have an allocated collection for output
metadata.Files = []string{}
}
p.Metadata = metadata
p.SetID()
}
+55
View File
@@ -0,0 +1,55 @@
package nix
import (
"testing"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source"
)
func TestCataloger_Catalog(t *testing.T) {
tests := []struct {
fixture string
wantPkgs []pkg.Package
wantRel []artifact.Relationship
}{
{
fixture: "test-fixtures/fixture-1",
wantPkgs: []pkg.Package{
{
Name: "glibc",
Version: "2.34-210",
PURL: "pkg:nix/glibc@2.34-210?output=bin&outputhash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
Locations: source.NewLocationSet(source.NewLocation("nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin")),
FoundBy: catalogerName,
Type: pkg.NixPkg,
MetadataType: pkg.NixStoreMetadataType,
Metadata: pkg.NixStoreMetadata{
OutputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
Output: "bin",
Files: []string{
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/lib",
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/lib/glibc.so",
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share",
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man",
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man/glibc.1",
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.fixture, func(t *testing.T) {
c := NewStoreCataloger()
pkgtest.NewCatalogTester().
FromDirectory(t, tt.fixture).
Expects(tt.wantPkgs, tt.wantRel).
TestCataloger(t, c)
})
}
}
+59
View File
@@ -0,0 +1,59 @@
package nix
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
func newNixStorePackage(storePath nixStorePath, locations ...source.Location) pkg.Package {
p := pkg.Package{
Name: storePath.name,
Version: storePath.version,
FoundBy: catalogerName,
Locations: source.NewLocationSet(locations...),
Type: pkg.NixPkg,
PURL: packageURL(storePath),
MetadataType: pkg.NixStoreMetadataType,
Metadata: pkg.NixStoreMetadata{
OutputHash: storePath.outputHash,
Output: storePath.output,
},
}
p.SetID()
return p
}
func packageURL(storePath nixStorePath) string {
var qualifiers packageurl.Qualifiers
if storePath.output != "" {
// since there is no nix pURL type yet, this is a guess, however, it is reasonable to assume that
// if only a single output is installed the pURL should be able to express this.
qualifiers = append(qualifiers,
packageurl.Qualifier{
Key: "output",
Value: storePath.output,
},
)
}
if storePath.outputHash != "" {
// it's not immediately clear if the hash found in the store path should be encoded in the pURL
qualifiers = append(qualifiers,
packageurl.Qualifier{
Key: "outputhash",
Value: storePath.outputHash,
},
)
}
pURL := packageurl.NewPackageURL(
// TODO: nix pURL type has not been accepted yet (only proposed at this time)
"nix",
"",
storePath.name,
storePath.version,
qualifiers,
"")
return pURL.ToString()
}
+49
View File
@@ -0,0 +1,49 @@
package nix
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_packageURL(t *testing.T) {
tests := []struct {
name string
storePath nixStorePath
want string
}{
{
name: "name + version",
storePath: nixStorePath{
name: "glibc",
version: "2.34",
},
want: "pkg:nix/glibc@2.34",
},
{
name: "hash qualifier",
storePath: nixStorePath{
name: "glibc",
version: "2.34",
outputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
},
want: "pkg:nix/glibc@2.34?outputhash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
},
{
name: "output qualifier",
storePath: nixStorePath{
name: "glibc",
version: "2.34",
outputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
output: "bin",
},
want: "pkg:nix/glibc@2.34?output=bin&outputhash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, packageURL(tt.storePath))
})
}
}
@@ -0,0 +1,134 @@
package nix
import (
"fmt"
"path"
"regexp"
"strings"
)
var (
numericPattern = regexp.MustCompile(`\d`)
// attempts to find the right-most example of something that appears to be a version (semver or otherwise)
// example input: h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin
// example output:
// version: "2.34-210"
// major: "2"
// minor: "34"
// patch: "210"
// (there are other capture groups, but they can be ignored)
rightMostVersionIshPattern = regexp.MustCompile(`-(?P<version>(?P<major>[0-9][a-zA-Z0-9]*)(\.(?P<minor>[0-9][a-zA-Z0-9]*))?(\.(?P<patch>0|[1-9][a-zA-Z0-9]*)){0,3}(?:-(?P<prerelease>\d*[.a-zA-Z-][.0-9a-zA-Z-]*)*)?(?:\+(?P<metadata>[.0-9a-zA-Z-]+(?:\.[.0-9a-zA-Z-]+)*))?)`)
unstableVersion = regexp.MustCompile(`-(?P<version>unstable-\d{4}-\d{2}-\d{2})$`)
)
// checkout the package naming conventions here: https://nixos.org/manual/nixpkgs/stable/#sec-package-naming
type nixStorePath struct {
outputHash string
name string
version string
output string
}
func (p nixStorePath) isValidPackage() bool {
return p.name != "" && p.version != ""
}
func findParentNixStorePath(source string) string {
source = strings.TrimRight(source, "/")
indicator := "nix/store/"
start := strings.Index(source, indicator)
if start == -1 {
return ""
}
startOfHash := start + len(indicator)
nextField := strings.Index(source[startOfHash:], "/")
if nextField == -1 {
return ""
}
startOfSubPath := startOfHash + nextField
return source[0:startOfSubPath]
}
func parseNixStorePath(source string) *nixStorePath {
if strings.HasSuffix(source, ".drv") {
// ignore derivations
return nil
}
source = path.Base(source)
versionStartIdx, versionIsh, prerelease := findVersionIsh(source)
if versionStartIdx == -1 {
return nil
}
hashName := strings.TrimSuffix(source[0:versionStartIdx], "-")
hashNameFields := strings.Split(hashName, "-")
if len(hashNameFields) < 2 {
return nil
}
hash, name := hashNameFields[0], strings.Join(hashNameFields[1:], "-")
prereleaseFields := strings.Split(prerelease, "-")
lastPrereleaseField := prereleaseFields[len(prereleaseFields)-1]
var version = versionIsh
var output string
if !hasNumeric(lastPrereleaseField) {
// this last prerelease field is probably a nix output
version = strings.TrimSuffix(versionIsh, fmt.Sprintf("-%s", lastPrereleaseField))
output = lastPrereleaseField
}
return &nixStorePath{
outputHash: hash,
name: name,
version: version,
output: output,
}
}
func hasNumeric(s string) bool {
return numericPattern.MatchString(s)
}
func findVersionIsh(input string) (int, string, string) {
// we want to return the index of the start of the "version" group (the first capture group).
// note that the match indices are in the form of [start, end, start, end, ...]. Also note that the
// capture group for version in both regexes are the same index, but if the regexes are changed
// this code will start to fail.
versionGroup := 1
match := unstableVersion.FindAllStringSubmatchIndex(input, -1)
if len(match) > 0 && len(match[0]) > 0 {
return match[0][versionGroup*2], input[match[0][versionGroup*2]:match[0][(versionGroup*2)+1]], ""
}
match = rightMostVersionIshPattern.FindAllStringSubmatchIndex(input, -1)
if len(match) == 0 || len(match[0]) == 0 {
return -1, "", ""
}
var version string
versionStart, versionStop := match[0][versionGroup*2], match[0][(versionGroup*2)+1]
if versionStart != -1 || versionStop != -1 {
version = input[versionStart:versionStop]
}
prereleaseGroup := 7
var prerelease string
prereleaseStart, prereleaseStop := match[0][prereleaseGroup*2], match[0][(prereleaseGroup*2)+1]
if prereleaseStart != -1 && prereleaseStop != -1 {
prerelease = input[prereleaseStart:prereleaseStop]
}
return versionStart,
version,
prerelease
}
@@ -0,0 +1,304 @@
package nix
import (
"path"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_findVersionIsh(t *testing.T) {
// note: only the package version fields are tested here, the name is tested in parseNixStorePath below.
tests := []struct {
name string
input string
wantIdx int
wantVersion string
wantPreRelease string
}{
{
name: "no version",
input: "5q7vxm9lc4b9hifc3br4sr8dy7f2h0qa-source",
wantIdx: -1,
wantVersion: "",
wantPreRelease: "",
},
{
name: "semver with overbite into output",
input: "/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin",
wantIdx: 50,
wantVersion: "2.34-210-bin",
wantPreRelease: "210-bin",
},
{
name: "multiple versions",
input: "5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
wantIdx: 53,
wantVersion: "2.33",
wantPreRelease: "",
},
{
name: "name ends with number",
input: "55nswyz8335lk954y1ccx6as2jbq1z8f-libfido2-1.10.0",
wantIdx: 42,
wantVersion: "1.10.0",
wantPreRelease: "",
},
{
name: "major-minor only",
input: "q8gnp7r8475p52k9gmdzsrcddw5hirbn-gdbm-1.23",
wantIdx: 38,
wantVersion: "1.23",
wantPreRelease: "",
},
{
name: "0-prefixed version field",
input: "r705jm2icczpnmfccby3fzfrckfjakx3-perl5.34.1-URI-5.05",
wantIdx: 48,
wantVersion: "5.05",
wantPreRelease: "",
},
{
name: "prerelease with alpha prefix",
input: "v48s6iddb518j9lc1pk3rcn3x8c2ff0j-bash-interactive-5.1-p16",
wantIdx: 50,
wantVersion: "5.1-p16",
wantPreRelease: "p16",
},
{
name: "0-major version",
input: "x2f9x5q6qrs6cssx09ylxqyg9q2isi1z-aws-c-http-0.6.15",
wantIdx: 44,
wantVersion: "0.6.15",
wantPreRelease: "",
},
{
name: "several version fields",
// note: this package version is fictitious
input: "z24qs6f5d1mmwdp73n1jfc3swj4v2c5s-krb5-1.19.3.9.10",
wantIdx: 38,
wantVersion: "1.19.3.9.10",
wantPreRelease: "",
},
{
name: "skip drv + major only version",
input: "z0fqylhisz47krxv8fd0izm1i2qbswfr-readline63-006.drv",
wantIdx: 44,
wantVersion: "006",
wantPreRelease: "",
},
{
name: "prerelease with multiple dashes",
input: "zkgyp2vra0bgqm0dv1qi514l5fd0aksx-bash-interactive-5.1-p16-man",
wantIdx: 50,
wantVersion: "5.1-p16-man",
wantPreRelease: "p16-man",
},
{
name: "date as major version",
input: "0amf0d1dymv9gqcyhhjb9j0l8sn00c56-libedit-20210910-3.1",
wantIdx: 41,
wantVersion: "20210910-3.1",
wantPreRelease: "3.1",
},
{
name: "long name",
input: "0296qxvn30z9b2ah1g5p97k5wr9k8y78-busybox-static-x86_64-unknown-linux-musl-1.35.0",
wantIdx: 74,
wantVersion: "1.35.0",
wantPreRelease: "",
},
{
// this accounts for https://nixos.org/manual/nixpkgs/stable/#sec-package-naming
// > If a package is not a release but a commit from a repository, then the version attribute must
// > be the date of that (fetched) commit. The date must be in "unstable-YYYY-MM-DD" format.
// example: https://github.com/NixOS/nixpkgs/blob/798e23beab9b5cba4d6f05e8b243e1d4535770f3/pkgs/servers/webdav-server-rs/default.nix#L14
name: "unstable version",
input: "q5dhwzcn82by5ndc7g0q83wsnn13qkqw-webdav-server-rs-unstable-2021-08-16",
wantIdx: 50,
wantVersion: "unstable-2021-08-16",
wantPreRelease: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotIdx, gotVersion, gotPreRelease := findVersionIsh(tt.input)
assert.Equal(t, tt.wantIdx, gotIdx)
assert.Equal(t, tt.wantVersion, gotVersion)
assert.Equal(t, tt.wantPreRelease, gotPreRelease)
})
}
}
func Test_parseNixStorePath(t *testing.T) {
tests := []struct {
source string
want *nixStorePath
}{
{
source: "/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin",
want: &nixStorePath{
outputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
name: "glibc",
version: "2.34-210",
output: "bin",
},
},
{
source: "/nix/store/0296qxvn30z9b2ah1g5p97k5wr9k8y78-busybox-static-x86_64-unknown-linux-musl-1.35.0",
want: &nixStorePath{
outputHash: "0296qxvn30z9b2ah1g5p97k5wr9k8y78",
name: "busybox-static-x86_64-unknown-linux-musl",
version: "1.35.0",
},
},
{
source: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
want: &nixStorePath{
outputHash: "5zzrvdmlkc5rh3k5862krd3wfb3pqhyf",
name: "perl5.34.1-TimeDate",
version: "2.33",
},
},
{
source: "/nix/store/q38q8ng57zwjg1h15ry5zx0lb0xyax4b-libcap-2.63-lib",
want: &nixStorePath{
outputHash: "q38q8ng57zwjg1h15ry5zx0lb0xyax4b",
name: "libcap",
version: "2.63",
output: "lib",
},
},
{
source: "/nix/store/p0y8fbpbqr2jm5zfrdll0rgyg2lvp5g2-util-linux-minimal-2.37.4-bin",
want: &nixStorePath{
outputHash: "p0y8fbpbqr2jm5zfrdll0rgyg2lvp5g2",
name: "util-linux-minimal",
version: "2.37.4",
output: "bin",
},
},
{
source: "/nix/store/z24qs6f5d1mmwdp73n1jfc3swj4v2c5s-krb5-1.19.3.9.10",
want: &nixStorePath{
outputHash: "z24qs6f5d1mmwdp73n1jfc3swj4v2c5s",
name: "krb5",
version: "1.19.3.9.10",
},
},
{
source: "/nix/store/zkgyp2vra0bgqm0dv1qi514l5fd0aksx-bash-interactive-5.1-p16-man",
want: &nixStorePath{
outputHash: "zkgyp2vra0bgqm0dv1qi514l5fd0aksx",
name: "bash-interactive",
version: "5.1-p16",
output: "man",
},
},
{
source: "/nix/store/nwf2y0nc48ybim56308cr5ccvwkabcqc-openssl-1.1.1q",
want: &nixStorePath{
outputHash: "nwf2y0nc48ybim56308cr5ccvwkabcqc",
name: "openssl",
version: "1.1.1q",
},
},
{
source: "/nix/store/nwv742f1bxv6g78hy9yc6slxdbxlmqhb-kmod-29",
want: &nixStorePath{
outputHash: "nwv742f1bxv6g78hy9yc6slxdbxlmqhb",
name: "kmod",
version: "29",
},
},
{
source: "/nix/store/n83qx7m848kg51lcjchwbkmlgdaxfckf-tzdata-2022a",
want: &nixStorePath{
outputHash: "n83qx7m848kg51lcjchwbkmlgdaxfckf",
name: "tzdata",
version: "2022a",
},
},
{
source: "'/nix/store/q5dhwzcn82by5ndc7g0q83wsnn13qkqw-webdav-server-rs-unstable-2021-08-16",
want: &nixStorePath{
outputHash: "q5dhwzcn82by5ndc7g0q83wsnn13qkqw",
name: "webdav-server-rs",
version: "unstable-2021-08-16",
},
},
// negative cases...
{
source: "'z33yk02rsr6b4rb56lgb80bnvxx6yw39-?id=21ee35dde73aec5eba35290587d479218c6dd824.drv'",
},
{
source: "/nix/store/yzahni8aig6mdrvcsccgwm2515lcpi5q-git-minimal-2.36.0.drv",
},
{
source: "/nix/store/z9yvxs0s3xdkp5jgmzis4g50bfq3dgvm-0018-pkg-config-derive-prefix-from-prefix.patch",
},
{
source: "/nix/store/w3hl7zrmc9qvzadc0k7cp9ysxiyz88j6-base-system",
},
{
source: "/nix/store/zz1lc28x25fcx6al6xwk3dk8kp7wx47y-Test-RequiresInternet-0.05.tar.gz.drv",
},
}
for _, tt := range tests {
t.Run(path.Base(tt.source), func(t *testing.T) {
assert.Equal(t, tt.want, parseNixStorePath(tt.source))
})
}
}
func Test_parentNixStorePath(t *testing.T) {
tests := []struct {
name string
source string
want string
}{
{
name: "exact path from absolute root",
source: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
want: "",
},
{
name: "exact path from relative root",
source: "nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
want: "",
},
{
name: "clean paths",
source: "//nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33///",
want: "",
},
{
name: "relative root with subdir file",
source: "nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33/bin/perl-timedate",
want: "nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
},
{
name: "absolute root with with subdir file",
source: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33/bin/perl-timedate",
want: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
},
{
name: "nexted root with with subdir file",
source: "/somewhere/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33/bin/perl-timedate",
want: "/somewhere/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, findParentNixStorePath(tt.source))
})
}
}
@@ -0,0 +1,2 @@
# this is not a real binary, just a small text file
!nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/lib/glibc.so
+17 -2
View File
@@ -2,8 +2,23 @@ package pkg
// GemMetadata represents all metadata parsed from the gemspec file
type GemMetadata struct {
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
// note regarding if Files can contribute to GemMetadata being able to implement FileOwner: this list is a
// "logical" list of files, not a list of paths that can be used to find the files without additional processing.
//
// For example: The "bundler" gem has a file entry of:
// "lib/bundler/vendor/uri/lib/uri/ldap.rb"
// but the actual file is located at:
// "/usr/local/lib/ruby/3.2.0/bundler/vendor/uri/lib/uri/ldap.rb"
// which do not match (the "lib" prefix is missing even for relative processing).
//
// without additional information about:
// - the gem installation path
// - the ruby installation path
// - the ruby version
// - environment variables (e.g. GEM_HOME) that may affect the gem installation path
// ... we can't reliably determine the full path to the file on disk, thus cannot implement FileOwner (...yet...).
Files []string `mapstructure:"files" json:"files,omitempty"`
Authors []string `mapstructure:"authors" json:"authors,omitempty"`
Licenses []string `mapstructure:"licenses" json:"licenses,omitempty"`
+4
View File
@@ -9,6 +9,7 @@ type MetadataType string
const (
// this is the full set of data shapes that can be represented within the pkg.Package.Metadata field
UnknownMetadataType MetadataType = "UnknownMetadata"
AlpmMetadataType MetadataType = "AlpmMetadata"
ApkMetadataType MetadataType = "ApkMetadata"
@@ -26,6 +27,7 @@ const (
JavaMetadataType MetadataType = "JavaMetadata"
KbPackageMetadataType MetadataType = "KbPackageMetadata"
MixLockMetadataType MetadataType = "MixLockMetadataType"
NixStoreMetadataType MetadataType = "NixStoreMetadata"
NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata"
NpmPackageLockJSONMetadataType MetadataType = "NpmPackageLockJsonMetadata"
PhpComposerJSONMetadataType MetadataType = "PhpComposerJsonMetadata"
@@ -54,6 +56,7 @@ var AllMetadataTypes = []MetadataType{
JavaMetadataType,
KbPackageMetadataType,
MixLockMetadataType,
NixStoreMetadataType,
NpmPackageJSONMetadataType,
NpmPackageLockJSONMetadataType,
PhpComposerJSONMetadataType,
@@ -82,6 +85,7 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{
JavaMetadataType: reflect.TypeOf(JavaMetadata{}),
KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}),
MixLockMetadataType: reflect.TypeOf(MixLockMetadata{}),
NixStoreMetadataType: reflect.TypeOf(NixStoreMetadata{}),
NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}),
NpmPackageLockJSONMetadataType: reflect.TypeOf(NpmPackageLockJSONMetadata{}),
PhpComposerJSONMetadataType: reflect.TypeOf(PhpComposerJSONMetadata{}),
+25
View File
@@ -0,0 +1,25 @@
package pkg
import (
"sort"
"github.com/scylladb/go-set/strset"
)
type NixStoreMetadata struct {
// OutputHash is the prefix of the nix store basename path
OutputHash string `mapstructure:"outputHash" json:"outputHash"`
// Output allows for optionally specifying the specific nix package output this package represents (for packages that support multiple outputs).
// Note: the default output for a package is an empty string, so will not be present in the output.
Output string `mapstructure:"output" json:"output,omitempty"`
// Files is a listing a files that are under the nix/store path for this package
Files []string `mapstructure:"files" json:"files"`
}
func (m NixStoreMetadata) OwnedFiles() (result []string) {
result = strset.New(m.Files...).List()
sort.Strings(result)
return
}
-1
View File
@@ -4,7 +4,6 @@ package pkg
type NpmPackageJSONMetadata struct {
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
Files []string `mapstructure:"files" json:"files,omitempty"`
Author string `mapstructure:"author" json:"author"`
Licenses []string `mapstructure:"licenses" json:"licenses"`
Homepage string `mapstructure:"homepage" json:"homepage"`
+18
View File
@@ -1,9 +1,15 @@
package pkg
import (
"sort"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/file"
)
var _ FileOwner = (*PortageMetadata)(nil)
// PortageMetadata represents all captured data for a Package package DB entry.
type PortageMetadata struct {
InstalledSize int `mapstructure:"InstalledSize" json:"installedSize" cyclonedx:"installedSize"`
@@ -15,3 +21,15 @@ type PortageFileRecord struct {
Path string `json:"path"`
Digest *file.Digest `json:"digest,omitempty"`
}
func (m PortageMetadata) OwnedFiles() (result []string) {
s := strset.New()
for _, f := range m.Files {
if f.Path != "" {
s.Add(f.Path)
}
}
result = s.List()
sort.Strings(result)
return result
}
+6
View File
@@ -26,6 +26,7 @@ const (
JavaPkg Type = "java-archive"
JenkinsPluginPkg Type = "jenkins-plugin"
KbPkg Type = "msrc-kb"
NixPkg Type = "nix"
NpmPkg Type = "npm"
PhpComposerPkg Type = "php-composer"
PortagePkg Type = "portage"
@@ -51,6 +52,7 @@ var AllPkgs = []Type{
JavaPkg,
JenkinsPluginPkg,
KbPkg,
NixPkg,
NpmPkg,
PhpComposerPkg,
PortagePkg,
@@ -92,6 +94,8 @@ func (t Type) PackageURLType() string {
return packageurl.TypePyPi
case PortagePkg:
return "portage"
case NixPkg:
return "nix"
case NpmPkg:
return packageurl.TypeNPM
case RpmPkg:
@@ -151,6 +155,8 @@ func TypeByName(name string) Type {
return PortagePkg
case packageurl.TypeHex:
return HexPkg
case "nix":
return NixPkg
default:
return UnknownPkg
}
+4
View File
@@ -83,6 +83,10 @@ func TestTypeFromPURL(t *testing.T) {
purl: "pkg:hex/hpax/hpax@0.1.1",
expected: HexPkg,
},
{
purl: "pkg:nix/glibc@2.34?hash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
expected: NixPkg,
},
}
var pkgTypes []string
+4 -4
View File
@@ -97,7 +97,7 @@ func TestPackagesCmdFlags(t *testing.T) {
name: "squashed-scope-flag",
args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage},
assertions: []traitAssertion{
assertPackageCount(34),
assertPackageCount(35),
assertSuccessfulReturnCode,
},
},
@@ -214,7 +214,7 @@ func TestPackagesCmdFlags(t *testing.T) {
// the application config in the log matches that of what we expect to have been configured.
assertInOutput("parallelism: 2"),
assertInOutput("parallelism=2"),
assertPackageCount(34),
assertPackageCount(35),
assertSuccessfulReturnCode,
},
},
@@ -225,7 +225,7 @@ func TestPackagesCmdFlags(t *testing.T) {
// the application config in the log matches that of what we expect to have been configured.
assertInOutput("parallelism: 1"),
assertInOutput("parallelism=1"),
assertPackageCount(34),
assertPackageCount(35),
assertSuccessfulReturnCode,
},
},
@@ -239,7 +239,7 @@ func TestPackagesCmdFlags(t *testing.T) {
assertions: []traitAssertion{
assertNotInOutput("secret_password"),
assertNotInOutput("secret_key_path"),
assertPackageCount(34),
assertPackageCount(35),
assertSuccessfulReturnCode,
},
},
+46 -70
View File
@@ -10,82 +10,53 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/stereoscope/pkg/imagetest"
)
func TestSpdxValidationTooling(t *testing.T) {
img := imagetest.GetFixtureImage(t, "docker-archive", "image-java-spdx-tools")
require.NotEmpty(t, img.Metadata.Tags)
imgTag := img.Metadata.Tags[0]
images := []string{
"alpine:3.17.3@sha256:b6ca290b6b4cdcca5b3db3ffa338ee0285c11744b4a6abaa9627746ee3291d8d",
"photon:3.0@sha256:888675e193418d924feea262cf639c46532b63c2027a39fd3ac75383b3c1130e",
"debian:stable@sha256:729c2433e196207749a86f1d86e0106822041bb280b4200cf7a4db97608f6d3a",
}
env := map[string]string{
"SYFT_FILE_METADATA_CATALOGER_ENABLED": "true",
"SYFT_FILE_CONTENTS_CATALOGER_ENABLED": "true",
"SYFT_FILE_METADATA_DIGESTS": "sha1",
}
tests := []struct {
name string
syftArgs []string
images []string
setup func(t *testing.T)
env map[string]string
assertions []traitAssertion
name string
syftArgs []string
images []string
setup func(t *testing.T)
env map[string]string
}{
{
name: "spdx validation tooling tag value",
syftArgs: []string{"packages", "-o", "spdx"},
images: []string{"alpine:latest", "photon:3.0", "debian:latest"},
env: map[string]string{
"SYFT_FILE_METADATA_CATALOGER_ENABLED": "true",
"SYFT_FILE_CONTENTS_CATALOGER_ENABLED": "true",
"SYFT_FILE_METADATA_DIGESTS": "sha1",
},
setup: func(t *testing.T) {
cwd, err := os.Getwd()
require.NoError(t, err)
fixturesPath := filepath.Join(cwd, "test-fixtures", "image-java-spdx-tools")
buildCmd := exec.Command("make", "build")
buildCmd.Dir = fixturesPath
buildCmd.Stdout = os.Stdout
buildCmd.Stderr = os.Stderr
err = buildCmd.Run()
require.NoError(t, err)
},
assertions: []traitAssertion{
assertSuccessfulReturnCode,
},
images: images,
env: env,
},
{
name: "spdx validation tooling json",
syftArgs: []string{"packages", "-o", "spdx-json"},
images: []string{"alpine:latest", "photon:3.0", "debian:latest"},
env: map[string]string{
"SYFT_FILE_METADATA_CATALOGER_ENABLED": "true",
"SYFT_FILE_CONTENTS_CATALOGER_ENABLED": "true",
"SYFT_FILE_METADATA_DIGESTS": "sha1",
},
setup: func(t *testing.T) {
cwd, err := os.Getwd()
require.NoError(t, err)
fixturesPath := filepath.Join(cwd, "test-fixtures", "image-java-spdx-tools")
buildCmd := exec.Command("make", "build")
buildCmd.Dir = fixturesPath
err = buildCmd.Run()
require.NoError(t, err)
},
assertions: []traitAssertion{
assertSuccessfulReturnCode,
},
images: images,
env: env,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// build the validation image
test.setup(t)
dir := t.TempDir()
for _, image := range test.images {
for _, image := range test.images {
t.Run(test.name+"_"+image, func(t *testing.T) {
args := append(test.syftArgs, image)
cmd, stdout, stderr := runSyft(t, test.env, args...)
for _, traitFn := range test.assertions {
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
}
cwd, err := os.Getwd()
require.NoError(t, err)
f, err := os.CreateTemp(dir, "temp")
require.NoError(t, err)
var suffix string
if strings.Contains(test.name, "json") {
@@ -94,23 +65,28 @@ func TestSpdxValidationTooling(t *testing.T) {
suffix = ".spdx"
}
// spdx tooling only takes a file with suffix spdx
rename := path.Join(path.Dir(f.Name()), fmt.Sprintf("%s%s", path.Base(f.Name()), suffix))
err = os.Rename(f.Name(), rename)
require.NoError(t, err)
dir := t.TempDir()
sbomPath := filepath.Join(dir, fmt.Sprintf("sbom%s", suffix))
// write file for validation
_, err = f.Write([]byte(stdout))
args = append(args, "--file", sbomPath)
cmd, _, stderr := runSyft(t, test.env, args...)
if cmd.ProcessState.ExitCode() != 0 {
t.Fatalf("failed to run syft: %s", stderr)
}
cwd, err := os.Getwd()
require.NoError(t, err)
// validate against spdx java tooling
fileArg := fmt.Sprintf("FILE=%s", rename)
mountArg := fmt.Sprintf("BASE=%s", path.Base(rename))
fileArg := fmt.Sprintf("DIR=%s", dir)
mountArg := fmt.Sprintf("BASE=%s", path.Base(sbomPath))
imageArg := fmt.Sprintf("IMAGE=%s", imgTag)
validateCmd := exec.Command("make", "validate", fileArg, mountArg)
validateCmd := exec.Command("make", "validate", fileArg, mountArg, imageArg)
validateCmd.Dir = filepath.Join(cwd, "test-fixtures", "image-java-spdx-tools")
runAndShow(t, validateCmd)
}
})
})
}
}
}
@@ -1,8 +1,10 @@
all: build validate
IMAGE := "spdx-java-tools:latest"
.PHONY: build
build:
docker build -t spdx-java-tools:latest .
validate:
docker run --rm -v ${FILE}:/home/build/${BASE} spdx-java-tools:latest Verify ${BASE}
docker run --rm -v $(DIR):/home/build/ $(IMAGE) Verify /home/build/$(BASE)
@@ -390,4 +390,11 @@ var commonTestCases = []testCase{
"example-jenkins-plugin": "1.0-SNAPSHOT",
},
},
{
name: "find nix store packages",
pkgType: pkg.NixPkg,
pkgInfo: map[string]string{
"glibc": "2.34-210",
},
},
}