mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-08 05:40:49 -08:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eadc6fb641 | ||
|
|
cc489b1af5 | ||
|
|
013f71a6a3 | ||
|
|
113a5b2162 | ||
|
|
733e5ac1fb | ||
|
|
d311e49bc3 | ||
|
|
cf1a7bf30b | ||
|
|
d465d9d1e0 | ||
|
|
0af225ccf1 | ||
|
|
6f64d55180 | ||
|
|
8c27430a2f | ||
|
|
c2b46d3c20 | ||
|
|
4368f11e0d | ||
|
|
5ec62f8636 |
6
.github/workflows/auto-update-labels.yaml
vendored
6
.github/workflows/auto-update-labels.yaml
vendored
@@ -5,7 +5,8 @@ on:
|
||||
- 'misc/triage/labels.yaml'
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.22'
|
||||
jobs:
|
||||
deploy:
|
||||
name: Auto-update labels
|
||||
@@ -17,7 +18,8 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
# cf. https://github.com/aquasecurity/trivy/pull/6711
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install aqua tools
|
||||
uses: aquaproj/aqua-installer@v3.0.0
|
||||
|
||||
3
.github/workflows/reusable-release.yaml
vendored
3
.github/workflows/reusable-release.yaml
vendored
@@ -14,6 +14,7 @@ on:
|
||||
|
||||
env:
|
||||
GH_USER: "aqua-bot"
|
||||
GO_VERSION: '1.22'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -76,7 +77,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false # Disable cache to avoid free space issues during `Post Setup Go` step.
|
||||
|
||||
- name: Generate SBOM
|
||||
|
||||
15
.github/workflows/test.yaml
vendored
15
.github/workflows/test.yaml
vendored
@@ -7,6 +7,8 @@ on:
|
||||
- 'mkdocs.yml'
|
||||
- 'LICENSE'
|
||||
merge_group:
|
||||
env:
|
||||
GO_VERSION: '1.22'
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
@@ -30,8 +32,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: go mod tidy
|
||||
run: |
|
||||
go mod tidy
|
||||
@@ -84,7 +85,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install tools
|
||||
uses: aquaproj/aqua-installer@v3.0.0
|
||||
@@ -113,7 +114,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install tools
|
||||
uses: aquaproj/aqua-installer@v3.0.0
|
||||
@@ -133,7 +134,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install tools
|
||||
uses: aquaproj/aqua-installer@v3.0.0
|
||||
@@ -164,7 +165,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Install tools
|
||||
uses: aquaproj/aqua-installer@v3.0.0
|
||||
with:
|
||||
@@ -198,7 +199,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Determine GoReleaser ID
|
||||
id: goreleaser_id
|
||||
|
||||
@@ -71,7 +71,7 @@ trivy kubernetes [flags] [CONTEXT]
|
||||
--list-all-pkgs enabling the option will output all packages regardless of vulnerability
|
||||
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
|
||||
--no-progress suppress progress bar
|
||||
--node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.0.9")
|
||||
--node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.2.1")
|
||||
--node-collector-namespace string specify the namespace in which the node-collector job should be deployed (default "trivy-temp")
|
||||
--offline-scan do not issue API requests to identify dependencies
|
||||
-o, --output string output file name
|
||||
|
||||
@@ -107,7 +107,7 @@ The image config is converted into Dockerfile and Trivy handles it as Dockerfile
|
||||
See [here](../scanner/misconfiguration/index.md) for the detail of Dockerfile scanning.
|
||||
|
||||
It is disabled by default.
|
||||
You can enable it with `--image-config-scanners config`.
|
||||
You can enable it with `--image-config-scanners misconfig`.
|
||||
|
||||
```
|
||||
$ trivy image --image-config-scanners misconfig [YOUR_IMAGE_NAME]
|
||||
|
||||
26
go.mod
26
go.mod
@@ -1,8 +1,8 @@
|
||||
module github.com/aquasecurity/trivy
|
||||
|
||||
go 1.22
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.0
|
||||
toolchain go1.22.2
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
|
||||
@@ -27,7 +27,7 @@ require (
|
||||
github.com/aquasecurity/trivy-checks v0.10.5-0.20240430045208-6cc735de6b9e
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d
|
||||
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48
|
||||
github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240425111126-a549f8de71bb
|
||||
github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240516051533-4c5a4aad13b7
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.11
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.11
|
||||
@@ -115,7 +115,8 @@ require (
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
|
||||
google.golang.org/protobuf v1.34.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.29.3
|
||||
helm.sh/helm/v3 v3.14.2
|
||||
k8s.io/api v0.30.0
|
||||
k8s.io/utils v0.0.0-20231127182322-b307cd553661
|
||||
modernc.org/sqlite v1.29.7
|
||||
)
|
||||
@@ -137,7 +138,6 @@ require (
|
||||
github.com/zclconf/go-cty v1.14.4
|
||||
github.com/zclconf/go-cty-yaml v1.0.3
|
||||
golang.org/x/crypto v0.22.0
|
||||
helm.sh/helm/v3 v3.14.2
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
@@ -177,7 +177,7 @@ require (
|
||||
github.com/antchfx/xpath v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.51.16 // indirect
|
||||
github.com/aws/aws-sdk-go v1.53.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
|
||||
@@ -410,14 +410,14 @@ require (
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.29.0 // indirect
|
||||
k8s.io/apimachinery v0.29.3 // indirect
|
||||
k8s.io/apimachinery v0.30.0 // indirect
|
||||
k8s.io/apiserver v0.29.0 // indirect
|
||||
k8s.io/cli-runtime v0.29.3 // indirect
|
||||
k8s.io/client-go v0.29.3 // indirect
|
||||
k8s.io/component-base v0.29.3 // indirect
|
||||
k8s.io/klog/v2 v2.120.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/kubectl v0.29.3 // indirect
|
||||
k8s.io/cli-runtime v0.30.0 // indirect
|
||||
k8s.io/client-go v0.30.0 // indirect
|
||||
k8s.io/component-base v0.30.0 // indirect
|
||||
k8s.io/klog/v2 v2.120.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/kubectl v0.30.0 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.49.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
|
||||
48
go.sum
48
go.sum
@@ -781,8 +781,8 @@ github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d h1:fjI9mkoTU
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d/go.mod h1:cj9/QmD9N3OZnKQMp+/DvdV+ym3HyIkd4e+F0ZM3ZGs=
|
||||
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI=
|
||||
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8=
|
||||
github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240425111126-a549f8de71bb h1:U07awOdXGT8NMwTPVuXkL/cKZyvO4PuG+VX1oIvsuiQ=
|
||||
github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240425111126-a549f8de71bb/go.mod h1:+NJBTgQErUmq21Ag71q/EuXZKIP+/OJvBAR0G+YUkKo=
|
||||
github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240516051533-4c5a4aad13b7 h1:bLmh/xuC/7abvt9S/xnODTQRu8fW6BhFHS6Cmbn0RNU=
|
||||
github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240516051533-4c5a4aad13b7/go.mod h1:HSpAJE8Y5Cjjg0Aw/0lqd3vMihN/FxBEj/f/7yDi/Uc=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -796,8 +796,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aws/aws-sdk-go v1.51.16 h1:vnWKK8KjbftEkuPX8bRj3WHsLy1uhotn0eXptpvrxJI=
|
||||
github.com/aws/aws-sdk-go v1.51.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go v1.53.0 h1:MMo1x1ggPPxDfHMXJnQudTbGXYlD4UigUAud1DJxPVo=
|
||||
github.com/aws/aws-sdk-go v1.53.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to=
|
||||
@@ -1853,15 +1853,15 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
|
||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE=
|
||||
github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk=
|
||||
github.com/open-policy-agent/opa v0.64.1 h1:n8IJTYlFWzqiOYx+JiawbErVxiqAyXohovcZxYbskxQ=
|
||||
github.com/open-policy-agent/opa v0.64.1/go.mod h1:j4VeLorVpKipnkQ2TDjWshEuV3cvP/rHzQhYaraUXZY=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
@@ -3089,32 +3089,32 @@ honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
|
||||
k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
|
||||
k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
|
||||
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
|
||||
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
|
||||
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
|
||||
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
|
||||
k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0=
|
||||
k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc=
|
||||
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
|
||||
k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU=
|
||||
k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU=
|
||||
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
|
||||
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
|
||||
k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
|
||||
k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
|
||||
k8s.io/apiserver v0.29.0 h1:Y1xEMjJkP+BIi0GSEv1BBrf1jLU9UPfAnnGGbbDdp7o=
|
||||
k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM=
|
||||
k8s.io/cli-runtime v0.29.3 h1:r68rephmmytoywkw2MyJ+CxjpasJDQY7AGc3XY2iv1k=
|
||||
k8s.io/cli-runtime v0.29.3/go.mod h1:aqVUsk86/RhaGJwDhHXH0jcdqBrgdF3bZWk4Z9D4mkM=
|
||||
k8s.io/cli-runtime v0.30.0 h1:0vn6/XhOvn1RJ2KJOC6IRR2CGqrpT6QQF4+8pYpWQ48=
|
||||
k8s.io/cli-runtime v0.30.0/go.mod h1:vATpDMATVTMA79sZ0YUCzlMelf6rUjoBzlp+RnoM+cg=
|
||||
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
|
||||
k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
|
||||
k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
|
||||
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
|
||||
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
|
||||
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
|
||||
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
|
||||
k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
|
||||
k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
|
||||
k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
|
||||
k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo=
|
||||
k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio=
|
||||
k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
|
||||
k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
|
||||
k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
|
||||
k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
|
||||
k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
|
||||
@@ -3122,13 +3122,13 @@ k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.120.0 h1:z+q5mfovBj1fKFxiRzsa2DsJLPIVMk/KFL81LMOfK+8=
|
||||
k8s.io/klog/v2 v2.120.0/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/kubectl v0.29.3 h1:RuwyyIU42MAISRIePaa8Q7A3U74Q9P4MoJbDFz9o3us=
|
||||
k8s.io/kubectl v0.29.3/go.mod h1:yCxfY1dbwgVdEt2zkJ6d5NNLOhhWgTyrqACIoFhpdd4=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/kubectl v0.30.0 h1:xbPvzagbJ6RNYVMVuiHArC1grrV5vSmmIcSZuCdzRyk=
|
||||
k8s.io/kubectl v0.30.0/go.mod h1:zgolRw2MQXLPwmic2l/+iHs239L49fhSeICuMhQQXTI=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI=
|
||||
|
||||
48
integration/testdata/conan.json.golden
vendored
48
integration/testdata/conan.json.golden
vendored
@@ -21,6 +21,30 @@
|
||||
"Class": "lang-pkgs",
|
||||
"Type": "conan",
|
||||
"Packages": [
|
||||
{
|
||||
"ID": "poco/1.9.4",
|
||||
"Name": "poco",
|
||||
"Identifier": {
|
||||
"PURL": "pkg:conan/poco@1.9.4",
|
||||
"UID": "312753cebe80c0eb"
|
||||
},
|
||||
"Version": "1.9.4",
|
||||
"Relationship": "direct",
|
||||
"DependsOn": [
|
||||
"pcre/8.43",
|
||||
"zlib/1.2.12",
|
||||
"expat/2.4.8",
|
||||
"sqlite3/3.39.2",
|
||||
"openssl/1.1.1q"
|
||||
],
|
||||
"Layer": {},
|
||||
"Locations": [
|
||||
{
|
||||
"StartLine": 12,
|
||||
"EndLine": 25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": "bzip2/1.0.8",
|
||||
"Name": "bzip2",
|
||||
@@ -97,30 +121,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": "poco/1.9.4",
|
||||
"Name": "poco",
|
||||
"Identifier": {
|
||||
"PURL": "pkg:conan/poco@1.9.4",
|
||||
"UID": "312753cebe80c0eb"
|
||||
},
|
||||
"Version": "1.9.4",
|
||||
"Relationship": "direct",
|
||||
"DependsOn": [
|
||||
"pcre/8.43",
|
||||
"zlib/1.2.12",
|
||||
"expat/2.4.8",
|
||||
"sqlite3/3.39.2",
|
||||
"openssl/1.1.1q"
|
||||
],
|
||||
"Layer": {},
|
||||
"Locations": [
|
||||
{
|
||||
"StartLine": 12,
|
||||
"EndLine": 25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": "sqlite3/3.39.2",
|
||||
"Name": "sqlite3",
|
||||
|
||||
22
integration/testdata/poetry.json.golden
vendored
22
integration/testdata/poetry.json.golden
vendored
@@ -35,6 +35,17 @@
|
||||
],
|
||||
"Layer": {}
|
||||
},
|
||||
{
|
||||
"ID": "werkzeug@0.14",
|
||||
"Name": "werkzeug",
|
||||
"Identifier": {
|
||||
"PURL": "pkg:pypi/werkzeug@0.14",
|
||||
"UID": "4176be111ad01070"
|
||||
},
|
||||
"Version": "0.14",
|
||||
"Relationship": "direct",
|
||||
"Layer": {}
|
||||
},
|
||||
{
|
||||
"ID": "colorama@0.4.6",
|
||||
"Name": "colorama",
|
||||
@@ -46,17 +57,6 @@
|
||||
"Indirect": true,
|
||||
"Relationship": "indirect",
|
||||
"Layer": {}
|
||||
},
|
||||
{
|
||||
"ID": "werkzeug@0.14",
|
||||
"Name": "werkzeug",
|
||||
"Identifier": {
|
||||
"PURL": "pkg:pypi/werkzeug@0.14",
|
||||
"UID": "4176be111ad01070"
|
||||
},
|
||||
"Version": "0.14",
|
||||
"Relationship": "direct",
|
||||
"Layer": {}
|
||||
}
|
||||
],
|
||||
"Vulnerabilities": [
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -44,27 +43,27 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("conan"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseV1(lock LockFile) ([]types.Library, []types.Dependency, error) {
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
func (p *Parser) parseV1(lock LockFile) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var pkgs []ftypes.Package
|
||||
var deps []ftypes.Dependency
|
||||
var directDeps []string
|
||||
if root, ok := lock.GraphLock.Nodes["0"]; ok {
|
||||
directDeps = root.Requires
|
||||
}
|
||||
|
||||
// Parse packages
|
||||
parsed := make(map[string]types.Library)
|
||||
parsed := make(map[string]ftypes.Package)
|
||||
for i, node := range lock.GraphLock.Nodes {
|
||||
if node.Ref == "" {
|
||||
continue
|
||||
}
|
||||
lib, err := toLibrary(node.Ref, node.StartLine, node.EndLine)
|
||||
pkg, err := toPackage(node.Ref, node.StartLine, node.EndLine)
|
||||
if err != nil {
|
||||
p.logger.Debug("Parse ref error", log.Err(err))
|
||||
continue
|
||||
@@ -72,14 +71,14 @@ func (p *Parser) parseV1(lock LockFile) ([]types.Library, []types.Dependency, er
|
||||
|
||||
// Determine if the package is a direct dependency or not
|
||||
direct := slices.Contains(directDeps, i)
|
||||
lib.Relationship = lo.Ternary(direct, types.RelationshipDirect, types.RelationshipIndirect)
|
||||
pkg.Relationship = lo.Ternary(direct, ftypes.RelationshipDirect, ftypes.RelationshipIndirect)
|
||||
|
||||
parsed[i] = lib
|
||||
parsed[i] = pkg
|
||||
}
|
||||
|
||||
// Parse dependency graph
|
||||
for i, node := range lock.GraphLock.Nodes {
|
||||
lib, ok := parsed[i]
|
||||
pkg, ok := parsed[i]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -91,33 +90,33 @@ func (p *Parser) parseV1(lock LockFile) ([]types.Library, []types.Dependency, er
|
||||
}
|
||||
}
|
||||
if len(childDeps) != 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
ID: lib.ID,
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkg.ID,
|
||||
DependsOn: childDeps,
|
||||
})
|
||||
}
|
||||
|
||||
libs = append(libs, lib)
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
return libs, deps, nil
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseV2(lock LockFile) ([]types.Library, []types.Dependency, error) {
|
||||
var libs []types.Library
|
||||
func (p *Parser) parseV2(lock LockFile) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var pkgs []ftypes.Package
|
||||
|
||||
for _, req := range lock.Requires {
|
||||
lib, err := toLibrary(req.Dependency, req.StartLine, req.EndLine)
|
||||
pkg, err := toPackage(req.Dependency, req.StartLine, req.EndLine)
|
||||
if err != nil {
|
||||
p.logger.Debug("Creating library entry from requirement failed", err)
|
||||
p.logger.Debug("Creating package entry from requirement failed", err)
|
||||
continue
|
||||
}
|
||||
|
||||
libs = append(libs, lib)
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
return libs, []types.Dependency{}, nil
|
||||
return pkgs, []ftypes.Dependency{}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lock LockFile
|
||||
|
||||
input, err := io.ReadAll(r)
|
||||
@@ -153,16 +152,16 @@ func parsePackage(text string) (string, string, error) {
|
||||
return ss[0], ss[1], nil
|
||||
}
|
||||
|
||||
func toLibrary(pkg string, startLine, endLine int) (types.Library, error) {
|
||||
func toPackage(pkg string, startLine, endLine int) (ftypes.Package, error) {
|
||||
name, version, err := parsePackage(pkg)
|
||||
if err != nil {
|
||||
return types.Library{}, err
|
||||
return ftypes.Package{}, err
|
||||
}
|
||||
return types.Library{
|
||||
return ftypes.Package{
|
||||
ID: dependency.ID(ftypes.Conan, name, version),
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: startLine,
|
||||
EndLine: endLine,
|
||||
|
||||
@@ -3,65 +3,64 @@ package conan_test
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/c/conan"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string // Test input file
|
||||
wantLibs []types.Library
|
||||
wantDeps []types.Dependency
|
||||
wantPkgs []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
inputFile: "testdata/happy_v1_case1.lock",
|
||||
wantLibs: []types.Library{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "pkga/0.0.1",
|
||||
Name: "pkga",
|
||||
Version: "0.0.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 13,
|
||||
EndLine: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "pkgb/system",
|
||||
Name: "pkgb",
|
||||
Version: "system",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 23,
|
||||
EndLine: 29,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "pkgc/0.1.1",
|
||||
Name: "pkgc",
|
||||
Version: "0.1.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 30,
|
||||
EndLine: 35,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "pkgb/system",
|
||||
Name: "pkgb",
|
||||
Version: "system",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 23,
|
||||
EndLine: 29,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantDeps: []types.Dependency{
|
||||
wantDeps: []ftypes.Dependency{
|
||||
{
|
||||
ID: "pkga/0.0.1",
|
||||
DependsOn: []string{
|
||||
@@ -73,13 +72,13 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "happy path. lock file with revisions support",
|
||||
inputFile: "testdata/happy_v1_case2.lock",
|
||||
wantLibs: []types.Library{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "openssl/3.0.3",
|
||||
Name: "openssl",
|
||||
Version: "3.0.3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 12,
|
||||
EndLine: 22,
|
||||
@@ -90,8 +89,8 @@ func TestParse(t *testing.T) {
|
||||
ID: "zlib/1.2.12",
|
||||
Name: "zlib",
|
||||
Version: "1.2.12",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 23,
|
||||
EndLine: 30,
|
||||
@@ -99,7 +98,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantDeps: []types.Dependency{
|
||||
wantDeps: []ftypes.Dependency{
|
||||
{
|
||||
ID: "openssl/3.0.3",
|
||||
DependsOn: []string{
|
||||
@@ -111,12 +110,12 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "happy path conan v2",
|
||||
inputFile: "testdata/happy_v2.lock",
|
||||
wantLibs: []types.Library{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "matrix/1.3",
|
||||
Name: "matrix",
|
||||
Version: "1.3",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
@@ -127,7 +126,7 @@ func TestParse(t *testing.T) {
|
||||
ID: "sound32/1.0",
|
||||
Name: "sound32",
|
||||
Version: "1.0",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 4,
|
||||
EndLine: 4,
|
||||
@@ -135,7 +134,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantDeps: []types.Dependency{},
|
||||
wantDeps: []ftypes.Dependency{},
|
||||
},
|
||||
{
|
||||
name: "happy path. lock file without dependencies",
|
||||
@@ -153,18 +152,12 @@ func TestParse(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
gotLibs, gotDeps, err := conan.NewParser().Parse(f)
|
||||
gotPkgs, gotDeps, err := conan.NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Slice(gotLibs, func(i, j int) bool {
|
||||
ret := strings.Compare(gotLibs[i].Name, gotLibs[j].Name)
|
||||
if ret != 0 {
|
||||
return ret < 0
|
||||
}
|
||||
return gotLibs[i].Version < gotLibs[j].Version
|
||||
})
|
||||
sort.Sort(ftypes.Packages(gotPkgs))
|
||||
|
||||
assert.Equal(t, tt.wantLibs, gotLibs)
|
||||
assert.Equal(t, tt.wantPkgs, gotPkgs)
|
||||
assert.Equal(t, tt.wantDeps, gotDeps)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,13 +9,17 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/go-version/pkg/version"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type environment struct {
|
||||
Dependencies []Dependency `yaml:"dependencies"`
|
||||
Entries []Entry `yaml:"dependencies"`
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Dependencies []Dependency
|
||||
}
|
||||
|
||||
type Dependency struct {
|
||||
@@ -28,44 +32,46 @@ type Parser struct {
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("conda"),
|
||||
once: sync.Once{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var env environment
|
||||
if err := yaml.NewDecoder(r).Decode(&env); err != nil {
|
||||
return nil, nil, xerrors.Errorf("unable to decode conda environment.yml file: %w", err)
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
for _, dep := range env.Dependencies {
|
||||
lib := p.toLibrary(dep)
|
||||
// Skip empty libs
|
||||
if lib.Name == "" {
|
||||
continue
|
||||
var pkgs ftypes.Packages
|
||||
for _, entry := range env.Entries {
|
||||
for _, dep := range entry.Dependencies {
|
||||
pkg := p.toPackage(dep)
|
||||
// Skip empty pkgs
|
||||
if pkg.Name == "" {
|
||||
continue
|
||||
}
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
libs = append(libs, lib)
|
||||
}
|
||||
|
||||
sort.Sort(types.Libraries(libs))
|
||||
return libs, nil, nil
|
||||
sort.Sort(pkgs)
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func (p *Parser) toLibrary(dep Dependency) types.Library {
|
||||
func (p *Parser) toPackage(dep Dependency) ftypes.Package {
|
||||
name, ver := p.parseDependency(dep.Value)
|
||||
if ver == "" {
|
||||
p.once.Do(func() {
|
||||
p.logger.Warn("Unable to detect the dependency versions from `environment.yml` as those versions are not pinned. Use `conda env export` to pin versions.")
|
||||
})
|
||||
}
|
||||
return types.Library{
|
||||
return ftypes.Package{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: dep.Line,
|
||||
EndLine: dep.Line,
|
||||
@@ -96,8 +102,40 @@ func (*Parser) parseDependency(line string) (string, string) {
|
||||
return name, parts[1]
|
||||
}
|
||||
|
||||
func (d *Dependency) UnmarshalYAML(node *yaml.Node) error {
|
||||
d.Value = node.Value
|
||||
d.Line = node.Line
|
||||
func (e *Entry) UnmarshalYAML(node *yaml.Node) error {
|
||||
var dependencies []Dependency
|
||||
// cf. https://github.com/go-yaml/yaml/blob/f6f7691b1fdeb513f56608cd2c32c51f8194bf51/resolve.go#L70-L81
|
||||
switch node.Tag {
|
||||
case "!!str":
|
||||
dependencies = append(dependencies, Dependency{
|
||||
Value: node.Value,
|
||||
Line: node.Line,
|
||||
})
|
||||
case "!!map":
|
||||
if node.Content != nil {
|
||||
// Map key is package manager (e.g. pip). So we need to store only map values (dependencies).
|
||||
// e.g. dependencies:
|
||||
// - pip:
|
||||
// - pandas==2.1.4
|
||||
if node.Content[1].Tag != "!!seq" { // Conda supports only map[string][]string format.
|
||||
return xerrors.Errorf("unsupported dependency type %q on line %d", node.Content[1].Tag, node.Content[1].Line)
|
||||
}
|
||||
|
||||
for _, depContent := range node.Content[1].Content {
|
||||
if depContent.Tag != "!!str" {
|
||||
return xerrors.Errorf("unsupported dependency type %q on line %d", depContent.Tag, depContent.Line)
|
||||
}
|
||||
|
||||
dependencies = append(dependencies, Dependency{
|
||||
Value: depContent.Value,
|
||||
Line: depContent.Line,
|
||||
})
|
||||
}
|
||||
}
|
||||
default:
|
||||
return xerrors.Errorf("unsupported dependency type %q on line %d", node.Tag, node.Line)
|
||||
}
|
||||
|
||||
e.Dependencies = dependencies
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,33 +8,43 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/conda/environment"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
input: "testdata/happy.yaml",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "_openmp_mutex",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 6,
|
||||
EndLine: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "asgiref",
|
||||
Version: "3.8.1",
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 21,
|
||||
EndLine: 21,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "blas",
|
||||
Version: "1.0",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
@@ -44,7 +54,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
Name: "bzip2",
|
||||
Version: "1.0.8",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 19,
|
||||
EndLine: 19,
|
||||
@@ -54,16 +64,26 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
Name: "ca-certificates",
|
||||
Version: "2024.2",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 7,
|
||||
EndLine: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "django",
|
||||
Version: "5.0.6",
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 22,
|
||||
EndLine: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ld_impl_linux-aarch64",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 8,
|
||||
EndLine: 8,
|
||||
@@ -72,7 +92,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "libblas",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 9,
|
||||
EndLine: 9,
|
||||
@@ -81,7 +101,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "libcblas",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 10,
|
||||
EndLine: 10,
|
||||
@@ -91,7 +111,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
Name: "libexpat",
|
||||
Version: "2.6.2",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 11,
|
||||
EndLine: 11,
|
||||
@@ -101,7 +121,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
Name: "libffi",
|
||||
Version: "3.4.2",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 12,
|
||||
EndLine: 12,
|
||||
@@ -110,7 +130,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "libgcc-ng",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 13,
|
||||
EndLine: 13,
|
||||
@@ -119,7 +139,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "libgfortran-ng",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 14,
|
||||
EndLine: 14,
|
||||
@@ -128,7 +148,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "libgfortran5",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 15,
|
||||
EndLine: 15,
|
||||
@@ -138,7 +158,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
Name: "libgomp",
|
||||
Version: "13.2.0",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 16,
|
||||
EndLine: 16,
|
||||
@@ -147,7 +167,7 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "liblapack",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 17,
|
||||
EndLine: 17,
|
||||
@@ -157,7 +177,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
Name: "libnsl",
|
||||
Version: "2.0.1",
|
||||
Locations: types.Locations{
|
||||
Locations: ftypes.Locations{
|
||||
{
|
||||
StartLine: 18,
|
||||
EndLine: 18,
|
||||
@@ -167,9 +187,24 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid_json",
|
||||
name: "invalid yaml file",
|
||||
input: "testdata/invalid.yaml",
|
||||
wantErr: "unable to decode conda environment.yml file",
|
||||
wantErr: "cannot unmarshal !!str `invalid` into environment.environment",
|
||||
},
|
||||
{
|
||||
name: "`dependency` field uses unsupported type",
|
||||
input: "testdata/wrong-deps-type.yaml",
|
||||
wantErr: `unsupported dependency type "!!int" on line 5`,
|
||||
},
|
||||
{
|
||||
name: "nested field uses unsupported type",
|
||||
input: "testdata/wrong-nested-type.yaml",
|
||||
wantErr: `unsupported dependency type "!!str" on line 5`,
|
||||
},
|
||||
{
|
||||
name: "nested dependency uses unsupported type",
|
||||
input: "testdata/wrong-nested-dep-type.yaml",
|
||||
wantErr: `unsupported dependency type "!!map" on line 6`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -17,5 +17,8 @@ dependencies:
|
||||
- liblapack=3.9.*=22_linuxaarch64_openblas
|
||||
- libnsl=2.0.1=h31becfc_0
|
||||
- bzip2=1.0.8=h998d150_5
|
||||
- pip:
|
||||
- asgiref==3.8.1
|
||||
- django==5.0.6
|
||||
|
||||
prefix: /opt/conda/envs/test-env
|
||||
|
||||
7
pkg/dependency/parser/conda/environment/testdata/wrong-deps-type.yaml
vendored
Normal file
7
pkg/dependency/parser/conda/environment/testdata/wrong-deps-type.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
name: test-env
|
||||
channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- 1
|
||||
|
||||
prefix: /opt/conda/envs/test-env
|
||||
9
pkg/dependency/parser/conda/environment/testdata/wrong-nested-dep-type.yaml
vendored
Normal file
9
pkg/dependency/parser/conda/environment/testdata/wrong-nested-dep-type.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
name: test-env
|
||||
channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- pip:
|
||||
- wrongType:
|
||||
- asgiref==3.8.1
|
||||
|
||||
prefix: /opt/conda/envs/test-env
|
||||
7
pkg/dependency/parser/conda/environment/testdata/wrong-nested-type.yaml
vendored
Normal file
7
pkg/dependency/parser/conda/environment/testdata/wrong-nested-type.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
name: test-env
|
||||
channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- pip: asgiref==3.8.1
|
||||
|
||||
prefix: /opt/conda/envs/test-env
|
||||
@@ -3,9 +3,10 @@ package meta
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
@@ -17,14 +18,14 @@ type packageJSON struct {
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
// Parse parses Anaconda (a.k.a. conda) environment metadata.
|
||||
// e.g. <conda-root>/envs/<env>/conda-meta/<package>.json
|
||||
// For details see https://conda.io/projects/conda/en/latest/user-guide/concepts/environments.html
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var data packageJSON
|
||||
err := json.NewDecoder(r).Decode(&data)
|
||||
if err != nil {
|
||||
@@ -35,11 +36,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("unable to parse conda package")
|
||||
}
|
||||
|
||||
return []types.Library{
|
||||
return []ftypes.Package{
|
||||
{
|
||||
Name: data.Name,
|
||||
Version: data.Version,
|
||||
License: data.License, // can be empty
|
||||
Name: data.Name,
|
||||
Version: data.Version,
|
||||
Licenses: lo.Ternary(data.License != "", []string{data.License}, nil),
|
||||
},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
@@ -8,25 +8,36 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/conda/meta"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "_libgcc_mutex",
|
||||
input: "testdata/_libgcc_mutex-0.1-main.json",
|
||||
want: []types.Library{{Name: "_libgcc_mutex", Version: "0.1"}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "_libgcc_mutex",
|
||||
Version: "0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "libgomp",
|
||||
input: "testdata/libgomp-11.2.0-h1234567_1.json",
|
||||
want: []types.Library{{Name: "libgomp", Version: "11.2.0", License: "GPL-3.0-only WITH GCC-exception-3.1"}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "libgomp",
|
||||
Version: "11.2.0",
|
||||
Licenses: []string{"GPL-3.0-only WITH GCC-exception-3.1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid_json",
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -19,7 +18,7 @@ const (
|
||||
// Parser is a parser for pubspec.lock
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
@@ -32,36 +31,36 @@ type Dep struct {
|
||||
Version string `yaml:"version"`
|
||||
}
|
||||
|
||||
func (p Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
l := &lock{}
|
||||
if err := yaml.NewDecoder(r).Decode(&l); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to decode pubspec.lock: %w", err)
|
||||
}
|
||||
var libs []types.Library
|
||||
var pkgs []ftypes.Package
|
||||
for name, dep := range l.Packages {
|
||||
// We would like to exclude dev dependencies, but we cannot identify
|
||||
// which indirect dependencies were introduced by dev dependencies
|
||||
// as there are 3 dependency types, "direct main", "direct dev" and "transitive".
|
||||
// It will be confusing if we exclude direct dev dependencies and include transitive dev dependencies.
|
||||
// We decided to keep all dev dependencies until Pub will add support for "transitive main" and "transitive dev".
|
||||
lib := types.Library{
|
||||
pkg := ftypes.Package{
|
||||
ID: dependency.ID(ftypes.Pub, name, dep.Version),
|
||||
Name: name,
|
||||
Version: dep.Version,
|
||||
Relationship: p.relationship(dep.Dependency),
|
||||
}
|
||||
libs = append(libs, lib)
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func (p Parser) relationship(dep string) types.Relationship {
|
||||
func (p Parser) relationship(dep string) ftypes.Relationship {
|
||||
switch dep {
|
||||
case directMain, directDev:
|
||||
return types.RelationshipDirect
|
||||
return ftypes.RelationshipDirect
|
||||
case transitiveDep:
|
||||
return types.RelationshipIndirect
|
||||
return ftypes.RelationshipIndirect
|
||||
}
|
||||
return types.RelationshipUnknown
|
||||
return ftypes.RelationshipUnknown
|
||||
}
|
||||
|
||||
@@ -10,37 +10,37 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/dart/pub"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
inputFile: "testdata/happy.lock",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
ID: "crypto@3.0.2",
|
||||
Name: "crypto",
|
||||
Version: "3.0.2",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "flutter_test@0.0.0",
|
||||
Name: "flutter_test",
|
||||
Version: "0.0.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "uuid@3.0.6",
|
||||
Name: "uuid",
|
||||
Version: "3.0.6",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
@@ -63,13 +63,13 @@ func TestParser_Parse(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
gotLibs, _, err := pub.NewParser().Parse(f)
|
||||
gotPkgs, _, err := pub.NewParser().Parse(f)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("Parse(%v)", tt.inputFile)) {
|
||||
return
|
||||
}
|
||||
|
||||
sort.Sort(types.Libraries(gotLibs))
|
||||
assert.Equal(t, tt.want, gotLibs)
|
||||
sort.Sort(ftypes.Packages(gotPkgs))
|
||||
assert.Equal(t, tt.want, gotPkgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/liamg/jfather"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -16,13 +16,13 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("dotnet"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var depsFile dotNetDependencies
|
||||
|
||||
input, err := io.ReadAll(r)
|
||||
@@ -33,7 +33,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("failed to decode .deps.json file: %w", err)
|
||||
}
|
||||
|
||||
var libraries []types.Library
|
||||
var pkgs []ftypes.Package
|
||||
for nameVer, lib := range depsFile.Libraries {
|
||||
if !strings.EqualFold(lib.Type, "package") {
|
||||
continue
|
||||
@@ -46,10 +46,10 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
continue
|
||||
}
|
||||
|
||||
libraries = append(libraries, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
Name: split[0],
|
||||
Version: split[1],
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: lib.StartLine,
|
||||
EndLine: lib.EndLine,
|
||||
@@ -58,7 +58,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
})
|
||||
}
|
||||
|
||||
return libraries, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
type dotNetDependencies struct {
|
||||
|
||||
@@ -4,25 +4,24 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
vectors := []struct {
|
||||
file string // Test input file
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
file: "testdata/ExampleApp1.deps.json",
|
||||
want: []types.Library{
|
||||
{Name: "Newtonsoft.Json", Version: "13.0.1", Locations: []types.Location{{StartLine: 33, EndLine: 39}}},
|
||||
want: []ftypes.Package{
|
||||
{Name: "Newtonsoft.Json", Version: "13.0.1", Locations: []ftypes.Location{{StartLine: 33, EndLine: 39}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -47,21 +46,8 @@ func TestParse(t *testing.T) {
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
ret := strings.Compare(got[i].Name, got[j].Name)
|
||||
if ret == 0 {
|
||||
return got[i].Version < got[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
|
||||
sort.Slice(tt.want, func(i, j int) bool {
|
||||
ret := strings.Compare(tt.want[i].Name, tt.want[j].Name)
|
||||
if ret == 0 {
|
||||
return tt.want[i].Version < tt.want[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
sort.Sort(ftypes.Packages(got))
|
||||
sort.Sort(ftypes.Packages(tt.want))
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func Parse(r io.Reader) (lib types.Library, err error) {
|
||||
func Parse(r io.Reader) (lib ftypes.Package, err error) {
|
||||
|
||||
// If wordpress file, open file and
|
||||
// find line with content
|
||||
@@ -68,10 +68,10 @@ func Parse(r io.Reader) (lib types.Library, err error) {
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil || version == "" {
|
||||
return types.Library{}, xerrors.New("version.php could not be parsed")
|
||||
return ftypes.Package{}, xerrors.New("version.php could not be parsed")
|
||||
}
|
||||
|
||||
return types.Library{
|
||||
return ftypes.Package{
|
||||
Name: "wordpress",
|
||||
Version: version,
|
||||
}, nil
|
||||
|
||||
@@ -8,18 +8,18 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParseWordPress(t *testing.T) {
|
||||
tests := []struct {
|
||||
file string // Test input file
|
||||
want types.Library
|
||||
want ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
file: "testdata/version.php",
|
||||
want: types.Library{
|
||||
want: ftypes.Package{
|
||||
Name: "wordpress",
|
||||
Version: "4.9.4-alpha",
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -39,23 +39,36 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("gobinary"),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse scans file to try to report the Go and module versions.
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
info, err := buildinfo.Read(r)
|
||||
if err != nil {
|
||||
return nil, nil, convertError(err)
|
||||
}
|
||||
|
||||
// Ex: "go1.22.3 X:boringcrypto"
|
||||
stdlibVersion := strings.TrimPrefix(info.GoVersion, "go")
|
||||
stdlibVersion, _, _ = strings.Cut(stdlibVersion, " ")
|
||||
|
||||
ldflags := p.ldFlags(info.Settings)
|
||||
libs := make([]types.Library, 0, len(info.Deps)+2)
|
||||
libs = append(libs, []types.Library{
|
||||
{
|
||||
pkgs := make(ftypes.Packages, 0, len(info.Deps)+2)
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
// Add the Go version used to build this binary.
|
||||
Name: "stdlib",
|
||||
Version: stdlibVersion,
|
||||
Relationship: ftypes.RelationshipDirect, // Considered a direct dependency as the main module depends on the standard packages.
|
||||
})
|
||||
|
||||
// There are times when gobinaries don't contain Main information.
|
||||
// e.g. `Go` binaries (e.g. `go`, `gofmt`, etc.)
|
||||
if info.Main.Path != "" {
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
// Add main module
|
||||
Name: info.Main.Path,
|
||||
// Only binaries installed with `go install` contain semver version of the main module.
|
||||
@@ -64,15 +77,9 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
// as a secondary source.
|
||||
// See https://github.com/aquasecurity/trivy/issues/1837#issuecomment-1832523477.
|
||||
Version: cmp.Or(p.checkVersion(info.Main.Path, info.Main.Version), p.ParseLDFlags(info.Main.Path, ldflags)),
|
||||
Relationship: types.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
// Add the Go version used to build this binary.
|
||||
Name: "stdlib",
|
||||
Version: strings.TrimPrefix(info.GoVersion, "go"),
|
||||
Relationship: types.RelationshipDirect, // Considered a direct dependency as the main module depends on the standard packages.
|
||||
},
|
||||
}...)
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
})
|
||||
}
|
||||
|
||||
for _, dep := range info.Deps {
|
||||
// binaries with old go version may incorrectly add module in Deps
|
||||
@@ -87,14 +94,14 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
mod = dep.Replace
|
||||
}
|
||||
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
Name: mod.Path,
|
||||
Version: p.checkVersion(mod.Path, mod.Version),
|
||||
})
|
||||
}
|
||||
|
||||
sort.Sort(types.Libraries(libs))
|
||||
return libs, nil, nil
|
||||
sort.Sort(pkgs)
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
// checkVersion detects `(devel)` versions, removes them and adds a debug message about it.
|
||||
@@ -159,7 +166,7 @@ func (p *Parser) ParseLDFlags(name string, flags []string) string {
|
||||
func isValidXKey(key string) bool {
|
||||
key = strings.ToLower(key)
|
||||
// The check for a 'ver' prefix enables the parser to pick up Trivy's own version value that's set.
|
||||
return strings.HasSuffix(key, "version") || strings.HasSuffix(key, "ver")
|
||||
return strings.HasSuffix(key, ".version") || strings.HasSuffix(key, ".ver")
|
||||
}
|
||||
|
||||
func isValidSemVer(ver string) bool {
|
||||
|
||||
@@ -8,20 +8,20 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/golang/binary"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
wantLibs := []types.Library{
|
||||
wantPkgs := []ftypes.Package{
|
||||
{
|
||||
Name: "github.com/aquasecurity/test",
|
||||
Version: "",
|
||||
Relationship: types.RelationshipRoot,
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
Name: "stdlib",
|
||||
Version: "1.15.2",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
Name: "github.com/aquasecurity/go-pep440-version",
|
||||
@@ -40,37 +40,37 @@ func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "ELF",
|
||||
inputFile: "testdata/test.elf",
|
||||
want: wantLibs,
|
||||
want: wantPkgs,
|
||||
},
|
||||
{
|
||||
name: "PE",
|
||||
inputFile: "testdata/test.exe",
|
||||
want: wantLibs,
|
||||
want: wantPkgs,
|
||||
},
|
||||
{
|
||||
name: "Mach-O",
|
||||
inputFile: "testdata/test.macho",
|
||||
want: wantLibs,
|
||||
want: wantPkgs,
|
||||
},
|
||||
{
|
||||
name: "with replace directive",
|
||||
inputFile: "testdata/replace.elf",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "github.com/ebati/trivy-mod-parse",
|
||||
Version: "",
|
||||
Relationship: types.RelationshipRoot,
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
Name: "stdlib",
|
||||
Version: "1.16.4",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
Name: "github.com/davecgh/go-spew",
|
||||
@@ -85,32 +85,43 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "with semver main module version",
|
||||
inputFile: "testdata/semver-main-module-version.macho",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "go.etcd.io/bbolt",
|
||||
Version: "v1.3.5",
|
||||
Relationship: types.RelationshipRoot,
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
Name: "stdlib",
|
||||
Version: "1.20.6",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with -ldflags=\"-X main.version=v1.0.0\"",
|
||||
inputFile: "testdata/main-version-via-ldflags.elf",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "github.com/aquasecurity/test",
|
||||
Version: "v1.0.0",
|
||||
Relationship: types.RelationshipRoot,
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
Name: "stdlib",
|
||||
Version: "1.22.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "goexperiment",
|
||||
inputFile: "testdata/goexperiment",
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "stdlib",
|
||||
Version: "1.22.1",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -227,6 +238,18 @@ func TestParser_ParseLDFlags(t *testing.T) {
|
||||
},
|
||||
want: "0.50.1",
|
||||
},
|
||||
{
|
||||
name: "with version with extra prefix",
|
||||
args: args{
|
||||
name: "github.com/argoproj/argo-cd/v2",
|
||||
flags: []string{
|
||||
"-s",
|
||||
"-w",
|
||||
"-X='github.com/argoproj/argo-cd/v2/common.kubectlVersion=v0.26.11'",
|
||||
},
|
||||
},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "with no flags",
|
||||
args: args{
|
||||
@@ -238,7 +261,7 @@ func TestParser_ParseLDFlags(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := binary.NewParser().(*binary.Parser)
|
||||
p := binary.NewParser()
|
||||
assert.Equal(t, tt.want, p.ParseLDFlags(tt.args.name, tt.args.flags))
|
||||
})
|
||||
}
|
||||
|
||||
BIN
pkg/dependency/parser/golang/binary/testdata/goexperiment
vendored
Executable file
BIN
pkg/dependency/parser/golang/binary/testdata/goexperiment
vendored
Executable file
Binary file not shown.
@@ -12,7 +12,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -34,17 +33,17 @@ type Parser struct {
|
||||
replace bool // 'replace' represents if the 'replace' directive should be taken into account.
|
||||
}
|
||||
|
||||
func NewParser(replace bool) types.Parser {
|
||||
func NewParser(replace bool) *Parser {
|
||||
return &Parser{
|
||||
replace: replace,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) GetExternalRefs(path string) []types.ExternalRef {
|
||||
func (p *Parser) GetExternalRefs(path string) []ftypes.ExternalRef {
|
||||
if url := resolveVCSUrl(path); url != "" {
|
||||
return []types.ExternalRef{
|
||||
return []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: url,
|
||||
},
|
||||
}
|
||||
@@ -67,8 +66,8 @@ func resolveVCSUrl(modulePath string) string {
|
||||
}
|
||||
|
||||
// Parse parses a go.mod file
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
libs := make(map[string]types.Library)
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
pkgs := make(map[string]ftypes.Package)
|
||||
|
||||
goModData, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
@@ -88,12 +87,12 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
// Main module
|
||||
if m := modFileParsed.Module; m != nil {
|
||||
ver := strings.TrimPrefix(m.Mod.Version, "v")
|
||||
libs[m.Mod.Path] = types.Library{
|
||||
pkgs[m.Mod.Path] = ftypes.Package{
|
||||
ID: packageID(m.Mod.Path, ver),
|
||||
Name: m.Mod.Path,
|
||||
Version: ver,
|
||||
ExternalReferences: p.GetExternalRefs(m.Mod.Path),
|
||||
Relationship: types.RelationshipRoot,
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +103,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
continue
|
||||
}
|
||||
ver := strings.TrimPrefix(require.Mod.Version, "v")
|
||||
libs[require.Mod.Path] = types.Library{
|
||||
pkgs[require.Mod.Path] = ftypes.Package{
|
||||
ID: packageID(require.Mod.Path, ver),
|
||||
Name: require.Mod.Path,
|
||||
Version: ver,
|
||||
Relationship: lo.Ternary(require.Indirect, types.RelationshipIndirect, types.RelationshipDirect),
|
||||
Relationship: lo.Ternary(require.Indirect, ftypes.RelationshipIndirect, ftypes.RelationshipDirect),
|
||||
ExternalReferences: p.GetExternalRefs(require.Mod.Path),
|
||||
}
|
||||
}
|
||||
@@ -116,8 +115,8 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
// No need to evaluate the 'replace' directive for indirect dependencies
|
||||
if p.replace {
|
||||
for _, rep := range modFileParsed.Replace {
|
||||
// Check if replaced path is actually in our libs.
|
||||
old, ok := libs[rep.Old.Path]
|
||||
// Check if replaced path is actually in our pkgs.
|
||||
old, ok := pkgs[rep.Old.Path]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -130,16 +129,16 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
// Only support replace directive with version on the right side.
|
||||
// Directive without version is a local path.
|
||||
if rep.New.Version == "" {
|
||||
// Delete old lib, since it's a local path now.
|
||||
delete(libs, rep.Old.Path)
|
||||
// Delete old pkg, since it's a local path now.
|
||||
delete(pkgs, rep.Old.Path)
|
||||
continue
|
||||
}
|
||||
|
||||
// Delete old lib, in case the path has changed.
|
||||
delete(libs, rep.Old.Path)
|
||||
// Delete old pkg, in case the path has changed.
|
||||
delete(pkgs, rep.Old.Path)
|
||||
|
||||
// Add replaced library to library register.
|
||||
libs[rep.New.Path] = types.Library{
|
||||
// Add replaced package to package register.
|
||||
pkgs[rep.New.Path] = ftypes.Package{
|
||||
ID: packageID(rep.New.Path, rep.New.Version[1:]),
|
||||
Name: rep.New.Path,
|
||||
Version: rep.New.Version[1:],
|
||||
@@ -149,7 +148,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
}
|
||||
}
|
||||
|
||||
return maps.Values(libs), nil, nil
|
||||
return maps.Values(pkgs), nil, nil
|
||||
}
|
||||
|
||||
// Check if the Go version is less than 1.17
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
@@ -16,7 +16,7 @@ func TestParse(t *testing.T) {
|
||||
name string
|
||||
file string
|
||||
replace bool
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
@@ -88,12 +88,8 @@ func TestParse(t *testing.T) {
|
||||
got, _, err := NewParser(tt.replace).Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].Name < got[j].Name
|
||||
})
|
||||
sort.Slice(tt.want, func(i, j int) bool {
|
||||
return tt.want[i].Name < tt.want[j].Name
|
||||
})
|
||||
sort.Sort(ftypes.Packages(got))
|
||||
sort.Sort(ftypes.Packages(tt.want))
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package mod
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
// execute go mod tidy in normal folder
|
||||
GoModNormal = []types.Library{
|
||||
GoModNormal = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -20,10 +20,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211224170007-df43bca6b6ff",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -32,16 +32,16 @@ var (
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "0.0.0-20200804184101-5ec99f83aff1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b",
|
||||
Name: "gopkg.in/yaml.v3",
|
||||
Version: "3.0.0-20210107192922-496545a6307b",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/go-yaml/yaml",
|
||||
},
|
||||
},
|
||||
@@ -49,14 +49,14 @@ var (
|
||||
}
|
||||
|
||||
// execute go mod tidy in replaced folder
|
||||
GoModReplaced = []types.Library{
|
||||
GoModReplaced = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -65,10 +65,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20220406074731-71021a481237",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -77,19 +77,19 @@ var (
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "0.0.0-20200804184101-5ec99f83aff1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
}
|
||||
|
||||
// execute go mod tidy in replaced folder
|
||||
GoModUnreplaced = []types.Library{
|
||||
GoModUnreplaced = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -98,10 +98,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211110174639-8257534ffed3",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211110174639-8257534ffed3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -110,19 +110,19 @@ var (
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "0.0.0-20200804184101-5ec99f83aff1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
}
|
||||
|
||||
// execute go mod tidy in replaced-with-version folder
|
||||
GoModReplacedWithVersion = []types.Library{
|
||||
GoModReplacedWithVersion = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -131,10 +131,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20220406074731-71021a481237",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -143,19 +143,19 @@ var (
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "0.0.0-20200804184101-5ec99f83aff1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
}
|
||||
|
||||
// execute go mod tidy in replaced-with-version-mismatch folder
|
||||
GoModReplacedWithVersionMismatch = []types.Library{
|
||||
GoModReplacedWithVersionMismatch = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -164,10 +164,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211224170007-df43bca6b6ff",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -176,16 +176,16 @@ var (
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "0.0.0-20200804184101-5ec99f83aff1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b",
|
||||
Name: "gopkg.in/yaml.v3",
|
||||
Version: "3.0.0-20210107192922-496545a6307b",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/go-yaml/yaml",
|
||||
},
|
||||
},
|
||||
@@ -193,14 +193,14 @@ var (
|
||||
}
|
||||
|
||||
// execute go mod tidy in replaced-with-local-path folder
|
||||
GoModReplacedWithLocalPath = []types.Library{
|
||||
GoModReplacedWithLocalPath = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -209,10 +209,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211224170007-df43bca6b6ff",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -221,10 +221,10 @@ var (
|
||||
ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b",
|
||||
Name: "gopkg.in/yaml.v3",
|
||||
Version: "3.0.0-20210107192922-496545a6307b",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/go-yaml/yaml",
|
||||
},
|
||||
},
|
||||
@@ -232,14 +232,14 @@ var (
|
||||
}
|
||||
|
||||
// execute go mod tidy in replaced-with-local-path-and-version folder
|
||||
GoModReplacedWithLocalPathAndVersion = []types.Library{
|
||||
GoModReplacedWithLocalPathAndVersion = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -248,10 +248,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211224170007-df43bca6b6ff",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -260,10 +260,10 @@ var (
|
||||
ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b",
|
||||
Name: "gopkg.in/yaml.v3",
|
||||
Version: "3.0.0-20210107192922-496545a6307b",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/go-yaml/yaml",
|
||||
},
|
||||
},
|
||||
@@ -271,14 +271,14 @@ var (
|
||||
}
|
||||
|
||||
// execute go mod tidy in replaced-with-local-path-and-version-mismatch folder
|
||||
GoModReplacedWithLocalPathAndVersionMismatch = []types.Library{
|
||||
GoModReplacedWithLocalPathAndVersionMismatch = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -287,10 +287,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211224170007-df43bca6b6ff",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -299,16 +299,16 @@ var (
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "0.0.0-20200804184101-5ec99f83aff1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b",
|
||||
Name: "gopkg.in/yaml.v3",
|
||||
Version: "3.0.0-20210107192922-496545a6307b",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/go-yaml/yaml",
|
||||
},
|
||||
},
|
||||
@@ -316,14 +316,14 @@ var (
|
||||
}
|
||||
|
||||
// execute go mod tidy in go116 folder
|
||||
GoMod116 = []types.Library{
|
||||
GoMod116 = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -332,10 +332,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211224170007-df43bca6b6ff",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
@@ -343,14 +343,14 @@ var (
|
||||
}
|
||||
|
||||
// execute go mod tidy in no-go-version folder
|
||||
GoModNoGoVersion = []types.Library{
|
||||
GoModNoGoVersion = []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/org/repo",
|
||||
Name: "github.com/org/repo",
|
||||
Relationship: types.RelationshipRoot,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/org/repo",
|
||||
},
|
||||
},
|
||||
@@ -359,10 +359,10 @@ var (
|
||||
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff",
|
||||
Name: "github.com/aquasecurity/go-dep-parser",
|
||||
Version: "0.0.0-20211224170007-df43bca6b6ff",
|
||||
Relationship: types.RelationshipDirect,
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefVCS,
|
||||
Type: ftypes.RefVCS,
|
||||
URL: "https://github.com/aquasecurity/go-dep-parser",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,21 +7,20 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
// Parse parses a go.sum file
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
var libs []types.Library
|
||||
uniqueLibs := make(map[string]string)
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var pkgs []ftypes.Package
|
||||
uniquePkgs := make(map[string]string)
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
@@ -33,19 +32,19 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
|
||||
// go.sum records and sorts all non-major versions
|
||||
// with the latest version as last entry
|
||||
uniqueLibs[s[0]] = strings.TrimSuffix(strings.TrimPrefix(s[1], "v"), "/go.mod")
|
||||
uniquePkgs[s[0]] = strings.TrimSuffix(strings.TrimPrefix(s[1], "v"), "/go.mod")
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, nil, xerrors.Errorf("scan error: %w", err)
|
||||
}
|
||||
|
||||
for k, v := range uniqueLibs {
|
||||
libs = append(libs, types.Library{
|
||||
for k, v := range uniquePkgs {
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: dependency.ID(ftypes.GoModule, k, v),
|
||||
Name: k,
|
||||
Version: v,
|
||||
})
|
||||
}
|
||||
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
vectors := []struct {
|
||||
file string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
{
|
||||
file: "testdata/gomod_normal.sum",
|
||||
@@ -48,12 +48,8 @@ func TestParse(t *testing.T) {
|
||||
got[i].ID = "" // Not compare IDs, tested in mod.TestModuleID()
|
||||
}
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].Name < got[j].Name
|
||||
})
|
||||
sort.Slice(v.want, func(i, j int) bool {
|
||||
return v.want[i].Name < v.want[j].Name
|
||||
})
|
||||
sort.Sort(ftypes.Packages(got))
|
||||
sort.Sort(ftypes.Packages(v.want))
|
||||
|
||||
assert.Equal(t, v.want, got)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package sum
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
// docker run --name gomod --rm -it golang:1.15 bash
|
||||
@@ -10,12 +10,12 @@ var (
|
||||
// go mod init github.com/org/repo
|
||||
// go get golang.org/x/xerrors
|
||||
// go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}'
|
||||
GoModNormal = []types.Library{
|
||||
GoModNormal = []ftypes.Package{
|
||||
{Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"},
|
||||
}
|
||||
|
||||
// https://github.com/uudashr/gopkgs/blob/616744904701ef01d868da4b66aad0e6856c361d/v2/go.sum
|
||||
GoModEmptyLine = []types.Library{
|
||||
GoModEmptyLine = []ftypes.Package{
|
||||
{Name: "github.com/karrick/godirwalk", Version: "1.12.0"},
|
||||
{Name: "github.com/pkg/errors", Version: "0.8.1"},
|
||||
}
|
||||
@@ -30,7 +30,7 @@ var (
|
||||
// go get github.com/stretchr/testify
|
||||
// go get github.com/BurntSushi/toml
|
||||
// go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}'
|
||||
GoModMany = []types.Library{
|
||||
GoModMany = []ftypes.Package{
|
||||
{Name: "github.com/BurntSushi/toml", Version: "0.3.1"},
|
||||
{Name: "github.com/cpuguy83/go-md2man/v2", Version: "2.0.0-20190314233015-f79a8a8ca69d"},
|
||||
{Name: "github.com/davecgh/go-spew", Version: "1.1.0"},
|
||||
@@ -53,7 +53,7 @@ var (
|
||||
// go mod init github.com/org/repo
|
||||
// go get github.com/aquasecurity/trivy
|
||||
// go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}'
|
||||
GoModTrivy = []types.Library{
|
||||
GoModTrivy = []ftypes.Package{
|
||||
{Name: "cloud.google.com/go", Version: "0.65.0"},
|
||||
{Name: "cloud.google.com/go/bigquery", Version: "1.8.0"},
|
||||
{Name: "cloud.google.com/go/datastore", Version: "1.1.0"},
|
||||
|
||||
@@ -6,19 +6,18 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
var libs []types.Library
|
||||
func (Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var pkgs []ftypes.Package
|
||||
scanner := bufio.NewScanner(r)
|
||||
var lineNum int
|
||||
for scanner.Scan() {
|
||||
@@ -36,19 +35,19 @@ func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, er
|
||||
|
||||
name := strings.Join(dep[:2], ":")
|
||||
version := strings.Split(dep[2], "=")[0] // remove classPaths
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: dependency.ID(ftypes.Gradle, name, version),
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: lineNum,
|
||||
EndLine: lineNum,
|
||||
},
|
||||
},
|
||||
Relationship: types.RelationshipUnknown,
|
||||
Relationship: ftypes.RelationshipUnknown,
|
||||
})
|
||||
|
||||
}
|
||||
return utils.UniqueLibraries(libs), nil, nil
|
||||
return utils.UniquePackages(pkgs), nil, nil
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ package lockfile
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -14,17 +13,17 @@ func TestParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
inputFile: "testdata/happy.lockfile",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
ID: "cglib:cglib-nodep:2.1.2",
|
||||
Name: "cglib:cglib-nodep",
|
||||
Version: "2.1.2",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 4,
|
||||
EndLine: 4,
|
||||
@@ -35,7 +34,7 @@ func TestParser_Parse(t *testing.T) {
|
||||
ID: "org.springframework:spring-asm:3.1.3.RELEASE",
|
||||
Name: "org.springframework:spring-asm",
|
||||
Version: "3.1.3.RELEASE",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
@@ -46,7 +45,7 @@ func TestParser_Parse(t *testing.T) {
|
||||
ID: "org.springframework:spring-beans:5.0.5.RELEASE",
|
||||
Name: "org.springframework:spring-beans",
|
||||
Version: "5.0.5.RELEASE",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 6,
|
||||
EndLine: 6,
|
||||
@@ -68,19 +67,9 @@ func TestParser_Parse(t *testing.T) {
|
||||
f, err := os.Open(tt.inputFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
libs, _, _ := parser.Parse(f)
|
||||
sortLibs(libs)
|
||||
assert.Equal(t, tt.want, libs)
|
||||
pkgs, _, _ := parser.Parse(f)
|
||||
sort.Sort(ftypes.Packages(pkgs))
|
||||
assert.Equal(t, tt.want, pkgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func sortLibs(libs []types.Library) {
|
||||
sort.Slice(libs, func(i, j int) bool {
|
||||
ret := strings.Compare(libs[i].Name, libs[j].Name)
|
||||
if ret == 0 {
|
||||
return libs[i].Version < libs[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -18,14 +17,14 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("mix"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
var libs []types.Library
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var pkgs []ftypes.Package
|
||||
scanner := bufio.NewScanner(r)
|
||||
var lineNumber int // It is used to save dependency location
|
||||
for scanner.Scan() {
|
||||
@@ -54,11 +53,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
continue
|
||||
}
|
||||
version := strings.Trim(ss[2], `"`)
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: dependency.ID(ftypes.Hex, name, version),
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: lineNumber,
|
||||
EndLine: lineNumber,
|
||||
@@ -67,5 +66,5 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
})
|
||||
|
||||
}
|
||||
return utils.UniqueLibraries(libs), nil, nil
|
||||
return utils.UniquePackages(pkgs), nil, nil
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ package mix
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -14,35 +13,55 @@ func TestParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
inputFile: "testdata/happy.mix.lock",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
ID: "bunt@0.2.0",
|
||||
Name: "bunt",
|
||||
Version: "0.2.0",
|
||||
Locations: []types.Location{{StartLine: 2, EndLine: 2}},
|
||||
ID: "bunt@0.2.0",
|
||||
Name: "bunt",
|
||||
Version: "0.2.0",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 2,
|
||||
EndLine: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "credo@1.6.6",
|
||||
Name: "credo",
|
||||
Version: "1.6.6",
|
||||
Locations: []types.Location{{StartLine: 3, EndLine: 3}},
|
||||
ID: "credo@1.6.6",
|
||||
Name: "credo",
|
||||
Version: "1.6.6",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 3,
|
||||
EndLine: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "file_system@0.2.10",
|
||||
Name: "file_system",
|
||||
Version: "0.2.10",
|
||||
Locations: []types.Location{{StartLine: 4, EndLine: 4}},
|
||||
ID: "file_system@0.2.10",
|
||||
Name: "file_system",
|
||||
Version: "0.2.10",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 4,
|
||||
EndLine: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "jason@1.3.0",
|
||||
Name: "jason",
|
||||
Version: "1.3.0",
|
||||
Locations: []types.Location{{StartLine: 5, EndLine: 5}},
|
||||
ID: "jason@1.3.0",
|
||||
Name: "jason",
|
||||
Version: "1.3.0",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -59,19 +78,9 @@ func TestParser_Parse(t *testing.T) {
|
||||
f, err := os.Open(tt.inputFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
libs, _, _ := parser.Parse(f)
|
||||
sortLibs(libs)
|
||||
assert.Equal(t, tt.want, libs)
|
||||
pkgs, _, _ := parser.Parse(f)
|
||||
sort.Sort(ftypes.Packages(pkgs))
|
||||
assert.Equal(t, tt.want, pkgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func sortLibs(libs []types.Library) {
|
||||
sort.Slice(libs, func(i, j int) bool {
|
||||
ret := strings.Compare(libs[i].Name, libs[j].Name)
|
||||
if ret == 0 {
|
||||
return libs[i].Version < libs[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -61,7 +61,7 @@ func WithSize(size int64) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func NewParser(c Client, opts ...Option) types.Parser {
|
||||
func NewParser(c Client, opts ...Option) *Parser {
|
||||
p := &Parser{
|
||||
logger: log.WithPrefix("jar"),
|
||||
client: c,
|
||||
@@ -74,15 +74,15 @@ func NewParser(c Client, opts ...Option) types.Parser {
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
libs, deps, err := p.parseArtifact(p.rootFilePath, p.size, r)
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
pkgs, deps, err := p.parseArtifact(p.rootFilePath, p.size, r)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("unable to parse %s: %w", p.rootFilePath, err)
|
||||
}
|
||||
return removeLibraryDuplicates(libs), deps, nil
|
||||
return removePackageDuplicates(pkgs), deps, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseArtifact(filePath string, size int64, r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) parseArtifact(filePath string, size int64, r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
p.logger.Debug("Parsing Java artifacts...", log.String("file", filePath))
|
||||
|
||||
// Try to extract artifactId and version from the file name
|
||||
@@ -90,14 +90,14 @@ func (p *Parser) parseArtifact(filePath string, size int64, r xio.ReadSeekerAt)
|
||||
fileName := filepath.Base(filePath)
|
||||
fileProps := parseFileName(filePath)
|
||||
|
||||
libs, m, foundPomProps, err := p.traverseZip(filePath, size, r, fileProps)
|
||||
pkgs, m, foundPomProps, err := p.traverseZip(filePath, size, r, fileProps)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("zip error: %w", err)
|
||||
}
|
||||
|
||||
// If pom.properties is found, it should be preferred than MANIFEST.MF.
|
||||
if foundPomProps {
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
manifestProps := m.properties(filePath)
|
||||
@@ -105,9 +105,9 @@ func (p *Parser) parseArtifact(filePath string, size int64, r xio.ReadSeekerAt)
|
||||
// In offline mode, we will not check if the artifact information is correct.
|
||||
if !manifestProps.Valid() {
|
||||
p.logger.Debug("Unable to identify POM in offline mode", log.String("file", fileName))
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
return append(libs, manifestProps.Library()), nil, nil
|
||||
return append(pkgs, manifestProps.Package()), nil, nil
|
||||
}
|
||||
|
||||
if manifestProps.Valid() {
|
||||
@@ -115,14 +115,14 @@ func (p *Parser) parseArtifact(filePath string, size int64, r xio.ReadSeekerAt)
|
||||
// We have to make sure that the artifact exists actually.
|
||||
if ok, _ := p.client.Exists(manifestProps.GroupID, manifestProps.ArtifactID); ok {
|
||||
// If groupId and artifactId are valid, they will be returned.
|
||||
return append(libs, manifestProps.Library()), nil, nil
|
||||
return append(pkgs, manifestProps.Package()), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If groupId and artifactId are not found, call Maven Central's search API with SHA-1 digest.
|
||||
props, err := p.searchBySHA1(r, filePath)
|
||||
if err == nil {
|
||||
return append(libs, props.Library()), nil, nil
|
||||
return append(pkgs, props.Package()), nil, nil
|
||||
} else if !errors.Is(err, ArtifactNotFoundErr) {
|
||||
return nil, nil, xerrors.Errorf("failed to search by SHA1: %w", err)
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func (p *Parser) parseArtifact(filePath string, size int64, r xio.ReadSeekerAt)
|
||||
|
||||
// Return when artifactId or version from the file name are empty
|
||||
if fileProps.ArtifactID == "" || fileProps.Version == "" {
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
// Try to search groupId by artifactId via sonatype API
|
||||
@@ -140,17 +140,17 @@ func (p *Parser) parseArtifact(filePath string, size int64, r xio.ReadSeekerAt)
|
||||
if err == nil {
|
||||
p.logger.Debug("POM was determined in a heuristic way", log.String("file", fileName),
|
||||
log.String("artifact", fileProps.String()))
|
||||
libs = append(libs, fileProps.Library())
|
||||
pkgs = append(pkgs, fileProps.Package())
|
||||
} else if !errors.Is(err, ArtifactNotFoundErr) {
|
||||
return nil, nil, xerrors.Errorf("failed to search by artifact id: %w", err)
|
||||
}
|
||||
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func (p *Parser) traverseZip(filePath string, size int64, r xio.ReadSeekerAt, fileProps Properties) (
|
||||
[]types.Library, manifest, bool, error) {
|
||||
var libs []types.Library
|
||||
[]ftypes.Package, manifest, bool, error) {
|
||||
var pkgs []ftypes.Package
|
||||
var m manifest
|
||||
var foundPomProps bool
|
||||
|
||||
@@ -166,9 +166,9 @@ func (p *Parser) traverseZip(filePath string, size int64, r xio.ReadSeekerAt, fi
|
||||
if err != nil {
|
||||
return nil, manifest{}, false, xerrors.Errorf("failed to parse %s: %w", fileInJar.Name, err)
|
||||
}
|
||||
// Validation of props to avoid getting libs with empty Name/Version
|
||||
// Validation of props to avoid getting packages with empty Name/Version
|
||||
if props.Valid() {
|
||||
libs = append(libs, props.Library())
|
||||
pkgs = append(pkgs, props.Package())
|
||||
|
||||
// Check if the pom.properties is for the original JAR/WAR/EAR
|
||||
if fileProps.ArtifactID == props.ArtifactID && fileProps.Version == props.Version {
|
||||
@@ -181,18 +181,18 @@ func (p *Parser) traverseZip(filePath string, size int64, r xio.ReadSeekerAt, fi
|
||||
return nil, manifest{}, false, xerrors.Errorf("failed to parse MANIFEST.MF: %w", err)
|
||||
}
|
||||
case isArtifact(fileInJar.Name):
|
||||
innerLibs, _, err := p.parseInnerJar(fileInJar, filePath) // TODO process inner deps
|
||||
innerPkgs, _, err := p.parseInnerJar(fileInJar, filePath) // TODO process inner deps
|
||||
if err != nil {
|
||||
p.logger.Debug("Failed to parse", log.String("file", fileInJar.Name), log.Err(err))
|
||||
continue
|
||||
}
|
||||
libs = append(libs, innerLibs...)
|
||||
pkgs = append(pkgs, innerPkgs...)
|
||||
}
|
||||
}
|
||||
return libs, m, foundPomProps, nil
|
||||
return pkgs, m, foundPomProps, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseInnerJar(zf *zip.File, rootPath string) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) parseInnerJar(zf *zip.File, rootPath string) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
fr, err := zf.Open()
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("unable to open %s: %w", zf.Name, err)
|
||||
@@ -221,12 +221,12 @@ func (p *Parser) parseInnerJar(zf *zip.File, rootPath string) ([]types.Library,
|
||||
}
|
||||
|
||||
// Parse jar/war/ear recursively
|
||||
innerLibs, innerDeps, err := p.parseArtifact(fullPath, int64(zf.UncompressedSize64), f)
|
||||
innerPkgs, innerDeps, err := p.parseArtifact(fullPath, int64(zf.UncompressedSize64), f)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to parse %s: %w", zf.Name, err)
|
||||
}
|
||||
|
||||
return innerLibs, innerDeps, nil
|
||||
return innerPkgs, innerDeps, nil
|
||||
}
|
||||
|
||||
func (p *Parser) searchBySHA1(r io.ReadSeeker, filePath string) (Properties, error) {
|
||||
@@ -438,8 +438,8 @@ func (m manifest) determineVersion() (string, error) {
|
||||
return strings.TrimSpace(version), nil
|
||||
}
|
||||
|
||||
func removeLibraryDuplicates(libs []types.Library) []types.Library {
|
||||
return lo.UniqBy(libs, func(lib types.Library) string {
|
||||
return fmt.Sprintf("%s::%s::%s", lib.Name, lib.Version, lib.FilePath)
|
||||
func removePackageDuplicates(pkgs []ftypes.Package) []ftypes.Package {
|
||||
return lo.UniqBy(pkgs, func(pkg ftypes.Package) string {
|
||||
return fmt.Sprintf("%s::%s::%s", pkg.Name, pkg.Version, pkg.FilePath)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/java/jar"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -23,7 +23,7 @@ var (
|
||||
// mvn dependency:list
|
||||
// mvn dependency:tree -Dscope=compile -Dscope=runtime | awk '/:tree/,/BUILD SUCCESS/' | awk 'NR > 1 { print }' | head -n -2 | awk '{print $NF}' | awk -F":" '{printf("{\""$1":"$2"\", \""$4 "\", \"\"},\n")}'
|
||||
// paths filled in manually
|
||||
wantMaven = []types.Library{
|
||||
wantMaven = []ftypes.Package{
|
||||
{
|
||||
Name: "com.example:web-app",
|
||||
Version: "1.0-SNAPSHOT",
|
||||
@@ -70,7 +70,7 @@ var (
|
||||
// docker run --rm --name test -it test bash
|
||||
// gradle app:dependencies --configuration implementation | grep "[+\]---" | cut -d" " -f2 | awk -F":" '{printf("{\""$1":"$2"\", \""$3"\", \"\"},\n")}'
|
||||
// paths filled in manually
|
||||
wantGradle = []types.Library{
|
||||
wantGradle = []ftypes.Package{
|
||||
{
|
||||
Name: "commons-dbcp:commons-dbcp",
|
||||
Version: "1.4",
|
||||
@@ -94,7 +94,7 @@ var (
|
||||
}
|
||||
|
||||
// manually created
|
||||
wantSHA1 = []types.Library{
|
||||
wantSHA1 = []ftypes.Package{
|
||||
{
|
||||
Name: "org.springframework:spring-core",
|
||||
Version: "5.3.3",
|
||||
@@ -103,7 +103,7 @@ var (
|
||||
}
|
||||
|
||||
// offline
|
||||
wantOffline = []types.Library{
|
||||
wantOffline = []ftypes.Package{
|
||||
{
|
||||
Name: "org.springframework:Spring Framework",
|
||||
Version: "2.5.6.SEC03",
|
||||
@@ -112,7 +112,7 @@ var (
|
||||
}
|
||||
|
||||
// manually created
|
||||
wantHeuristic = []types.Library{
|
||||
wantHeuristic = []ftypes.Package{
|
||||
{
|
||||
Name: "com.example:heuristic",
|
||||
Version: "1.0.0-SNAPSHOT",
|
||||
@@ -121,7 +121,7 @@ var (
|
||||
}
|
||||
|
||||
// manually created
|
||||
wantFatjar = []types.Library{
|
||||
wantFatjar = []ftypes.Package{
|
||||
{
|
||||
Name: "com.google.guava:failureaccess",
|
||||
Version: "1.0.1",
|
||||
@@ -150,7 +150,7 @@ var (
|
||||
}
|
||||
|
||||
// manually created
|
||||
wantNestedJar = []types.Library{
|
||||
wantNestedJar = []ftypes.Package{
|
||||
{
|
||||
Name: "test:nested",
|
||||
Version: "0.0.1",
|
||||
@@ -169,7 +169,7 @@ var (
|
||||
}
|
||||
|
||||
// manually created
|
||||
wantDuplicatesJar = []types.Library{
|
||||
wantDuplicatesJar = []ftypes.Package{
|
||||
{
|
||||
Name: "io.quarkus.gizmo:gizmo",
|
||||
Version: "1.1.1.Final",
|
||||
@@ -211,7 +211,7 @@ func TestParse(t *testing.T) {
|
||||
name string
|
||||
file string // Test input file
|
||||
offline bool
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
{
|
||||
name: "maven",
|
||||
@@ -319,12 +319,8 @@ func TestParse(t *testing.T) {
|
||||
got, _, err := p.Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].Name < got[j].Name
|
||||
})
|
||||
sort.Slice(v.want, func(i, j int) bool {
|
||||
return v.want[i].Name < v.want[j].Name
|
||||
})
|
||||
sort.Sort(ftypes.Packages(got))
|
||||
sort.Sort(ftypes.Packages(v.want))
|
||||
|
||||
assert.Equal(t, v.want, got)
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
var ArtifactNotFoundErr = xerrors.New("no artifact found")
|
||||
@@ -17,8 +17,8 @@ type Properties struct {
|
||||
FilePath string // path to file containing these props
|
||||
}
|
||||
|
||||
func (p Properties) Library() types.Library {
|
||||
return types.Library{
|
||||
func (p Properties) Package() ftypes.Package {
|
||||
return ftypes.Package{
|
||||
Name: fmt.Sprintf("%s:%s", p.GroupID, p.ArtifactID),
|
||||
Version: p.Version,
|
||||
FilePath: p.FilePath,
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
)
|
||||
|
||||
@@ -26,9 +26,9 @@ type artifact struct {
|
||||
Exclusions map[string]struct{}
|
||||
|
||||
Module bool
|
||||
Relationship types.Relationship
|
||||
Relationship ftypes.Relationship
|
||||
|
||||
Locations types.Locations
|
||||
Locations ftypes.Locations
|
||||
}
|
||||
|
||||
func newArtifact(groupID, artifactID, version string, licenses []string, props map[string]string) artifact {
|
||||
@@ -37,7 +37,7 @@ func newArtifact(groupID, artifactID, version string, licenses []string, props m
|
||||
ArtifactID: evaluateVariable(artifactID, props, nil),
|
||||
Version: newVersion(evaluateVariable(version, props, nil)),
|
||||
Licenses: licenses,
|
||||
Relationship: types.RelationshipIndirect, // default
|
||||
Relationship: ftypes.RelationshipIndirect, // default
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +49,6 @@ func (a artifact) Equal(o artifact) bool {
|
||||
return a.GroupID == o.GroupID || a.ArtifactID == o.ArtifactID || a.Version.String() == o.Version.String()
|
||||
}
|
||||
|
||||
func (a artifact) JoinLicenses() string {
|
||||
return strings.Join(a.Licenses, ", ")
|
||||
}
|
||||
|
||||
func (a artifact) ToPOMLicenses() pomLicenses {
|
||||
return pomLicenses{
|
||||
License: lo.Map(a.Licenses, func(lic string, _ int) pomLicense {
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -49,7 +48,7 @@ func WithReleaseRemoteRepos(repos []string) option {
|
||||
}
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
type Parser struct {
|
||||
logger *log.Logger
|
||||
rootPath string
|
||||
cache pomCache
|
||||
@@ -60,7 +59,7 @@ type parser struct {
|
||||
servers []Server
|
||||
}
|
||||
|
||||
func NewParser(filePath string, opts ...option) types.Parser {
|
||||
func NewParser(filePath string, opts ...option) *Parser {
|
||||
o := &options{
|
||||
offline: false,
|
||||
releaseRemoteRepos: []string{centralURL}, // Maven doesn't use central repository for snapshot dependencies
|
||||
@@ -77,7 +76,7 @@ func NewParser(filePath string, opts ...option) types.Parser {
|
||||
localRepository = filepath.Join(homeDir, ".m2", "repository")
|
||||
}
|
||||
|
||||
return &parser{
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("pom"),
|
||||
rootPath: filepath.Clean(filePath),
|
||||
cache: newPOMCache(),
|
||||
@@ -89,7 +88,7 @@ func NewParser(filePath string, opts ...option) types.Parser {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
content, err := parsePom(r)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to parse POM: %w", err)
|
||||
@@ -112,18 +111,18 @@ func (p *parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return p.parseRoot(root.artifact(), make(map[string]struct{}))
|
||||
}
|
||||
|
||||
func (p *parser) parseRoot(root artifact, uniqModules map[string]struct{}) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) parseRoot(root artifact, uniqModules map[string]struct{}) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
// Prepare a queue for dependencies
|
||||
queue := newArtifactQueue()
|
||||
|
||||
// Enqueue root POM
|
||||
root.Relationship = types.RelationshipRoot
|
||||
root.Relationship = ftypes.RelationshipRoot
|
||||
root.Module = false
|
||||
queue.enqueue(root)
|
||||
|
||||
var (
|
||||
libs []types.Library
|
||||
deps []types.Dependency
|
||||
pkgs ftypes.Packages
|
||||
deps ftypes.Dependencies
|
||||
rootDepManagement []pomDependency
|
||||
uniqArtifacts = make(map[string]artifact)
|
||||
uniqDeps = make(map[string][]string)
|
||||
@@ -141,12 +140,12 @@ func (p *parser) parseRoot(root artifact, uniqModules map[string]struct{}) ([]ty
|
||||
}
|
||||
uniqModules[art.String()] = struct{}{}
|
||||
|
||||
moduleLibs, moduleDeps, err := p.parseRoot(art, uniqModules)
|
||||
modulePkgs, moduleDeps, err := p.parseRoot(art, uniqModules)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
libs = append(libs, moduleLibs...)
|
||||
pkgs = append(pkgs, modulePkgs...)
|
||||
if moduleDeps != nil {
|
||||
deps = append(deps, moduleDeps...)
|
||||
}
|
||||
@@ -160,7 +159,7 @@ func (p *parser) parseRoot(root artifact, uniqModules map[string]struct{}) ([]ty
|
||||
}
|
||||
// mark artifact as Direct, if saved artifact is Direct
|
||||
// take a look `hard requirement for the specified version` test
|
||||
if uniqueArt.Relationship == types.RelationshipRoot || uniqueArt.Relationship == types.RelationshipDirect {
|
||||
if uniqueArt.Relationship == ftypes.RelationshipRoot || uniqueArt.Relationship == ftypes.RelationshipDirect {
|
||||
art.Relationship = uniqueArt.Relationship
|
||||
}
|
||||
// We don't need to overwrite dependency location for hard links
|
||||
@@ -174,13 +173,13 @@ func (p *parser) parseRoot(root artifact, uniqModules map[string]struct{}) ([]ty
|
||||
return nil, nil, xerrors.Errorf("resolve error (%s): %w", art, err)
|
||||
}
|
||||
|
||||
if art.Relationship == types.RelationshipRoot {
|
||||
if art.Relationship == ftypes.RelationshipRoot {
|
||||
// Managed dependencies in the root POM affect transitive dependencies
|
||||
rootDepManagement = p.resolveDepManagement(result.properties, result.dependencyManagement)
|
||||
|
||||
// mark its dependencies as "direct"
|
||||
result.dependencies = lo.Map(result.dependencies, func(dep artifact, _ int) artifact {
|
||||
dep.Relationship = types.RelationshipDirect
|
||||
dep.Relationship = ftypes.RelationshipDirect
|
||||
return dep
|
||||
})
|
||||
}
|
||||
@@ -219,37 +218,37 @@ func (p *parser) parseRoot(root artifact, uniqModules map[string]struct{}) ([]ty
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to []types.Library and []types.Dependency
|
||||
// Convert to []ftypes.Package and []ftypes.Dependency
|
||||
for name, art := range uniqArtifacts {
|
||||
lib := types.Library{
|
||||
pkg := ftypes.Package{
|
||||
ID: packageID(name, art.Version.String()),
|
||||
Name: name,
|
||||
Version: art.Version.String(),
|
||||
License: art.JoinLicenses(),
|
||||
Licenses: art.Licenses,
|
||||
Relationship: art.Relationship,
|
||||
Locations: art.Locations,
|
||||
}
|
||||
libs = append(libs, lib)
|
||||
pkgs = append(pkgs, pkg)
|
||||
|
||||
// Convert dependency names into dependency IDs
|
||||
dependsOn := lo.FilterMap(uniqDeps[lib.ID], func(dependOnName string, _ int) (string, bool) {
|
||||
dependsOn := lo.FilterMap(uniqDeps[pkg.ID], func(dependOnName string, _ int) (string, bool) {
|
||||
ver := depVersion(dependOnName, uniqArtifacts)
|
||||
return packageID(dependOnName, ver), ver != ""
|
||||
})
|
||||
|
||||
sort.Strings(dependsOn)
|
||||
if len(dependsOn) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
ID: lib.ID,
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkg.ID,
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(types.Libraries(libs))
|
||||
sort.Sort(types.Dependencies(deps))
|
||||
sort.Sort(pkgs)
|
||||
sort.Sort(deps)
|
||||
|
||||
return libs, deps, nil
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
// depVersion finds dependency in uniqArtifacts and return its version
|
||||
@@ -260,7 +259,7 @@ func depVersion(depName string, uniqArtifacts map[string]artifact) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *parser) parseModule(currentPath, relativePath string) (artifact, error) {
|
||||
func (p *Parser) parseModule(currentPath, relativePath string) (artifact, error) {
|
||||
// modulePath: "root/" + "module/" => "root/module"
|
||||
module, err := p.openRelativePom(currentPath, relativePath)
|
||||
if err != nil {
|
||||
@@ -280,7 +279,7 @@ func (p *parser) parseModule(currentPath, relativePath string) (artifact, error)
|
||||
return moduleArtifact, nil
|
||||
}
|
||||
|
||||
func (p *parser) resolve(art artifact, rootDepManagement []pomDependency) (analysisResult, error) {
|
||||
func (p *Parser) resolve(art artifact, rootDepManagement []pomDependency) (analysisResult, error) {
|
||||
// If the artifact is found in cache, it is returned.
|
||||
if result := p.cache.get(art); result != nil {
|
||||
return *result, nil
|
||||
@@ -319,7 +318,7 @@ type analysisOptions struct {
|
||||
lineNumber bool // Save line numbers
|
||||
}
|
||||
|
||||
func (p *parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) {
|
||||
func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) {
|
||||
if pom == nil || pom.content == nil {
|
||||
return analysisResult{}, nil
|
||||
}
|
||||
@@ -362,7 +361,7 @@ func (p *parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *parser) mergeDependencyManagements(depManagements ...[]pomDependency) []pomDependency {
|
||||
func (p *Parser) mergeDependencyManagements(depManagements ...[]pomDependency) []pomDependency {
|
||||
uniq := make(map[string]struct{})
|
||||
var depManagement []pomDependency
|
||||
// The preceding argument takes precedence.
|
||||
@@ -378,7 +377,7 @@ func (p *parser) mergeDependencyManagements(depManagements ...[]pomDependency) [
|
||||
return depManagement
|
||||
}
|
||||
|
||||
func (p *parser) parseDependencies(deps []pomDependency, props map[string]string, depManagement []pomDependency,
|
||||
func (p *Parser) parseDependencies(deps []pomDependency, props map[string]string, depManagement []pomDependency,
|
||||
opts analysisOptions) []artifact {
|
||||
// Imported POMs often have no dependencies, so dependencyManagement resolution can be skipped.
|
||||
if len(deps) == 0 {
|
||||
@@ -403,7 +402,7 @@ func (p *parser) parseDependencies(deps []pomDependency, props map[string]string
|
||||
return dependencies
|
||||
}
|
||||
|
||||
func (p *parser) resolveDepManagement(props map[string]string, depManagement []pomDependency) []pomDependency {
|
||||
func (p *Parser) resolveDepManagement(props map[string]string, depManagement []pomDependency) []pomDependency {
|
||||
var newDepManagement, imports []pomDependency
|
||||
for _, dep := range depManagement {
|
||||
// cf. https://howtodoinjava.com/maven/maven-dependency-scopes/#import
|
||||
@@ -437,7 +436,7 @@ func (p *parser) resolveDepManagement(props map[string]string, depManagement []p
|
||||
return newDepManagement
|
||||
}
|
||||
|
||||
func (p *parser) mergeDependencies(parent, child []artifact, exclusions map[string]struct{}) []artifact {
|
||||
func (p *Parser) mergeDependencies(parent, child []artifact, exclusions map[string]struct{}) []artifact {
|
||||
var deps []artifact
|
||||
unique := make(map[string]struct{})
|
||||
|
||||
@@ -471,7 +470,7 @@ func excludeDep(exclusions map[string]struct{}, art artifact) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *parser) parseParent(currentPath string, parent pomParent) (analysisResult, error) {
|
||||
func (p *Parser) parseParent(currentPath string, parent pomParent) (analysisResult, error) {
|
||||
// Pass nil properties so that variables in <parent> are not evaluated.
|
||||
target := newArtifact(parent.GroupId, parent.ArtifactId, parent.Version, nil, nil)
|
||||
// if version is property (e.g. ${revision}) - we still need to parse this pom
|
||||
@@ -503,7 +502,7 @@ func (p *parser) parseParent(currentPath string, parent pomParent) (analysisResu
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *parser) retrieveParent(currentPath, relativePath string, target artifact) (*pom, error) {
|
||||
func (p *Parser) retrieveParent(currentPath, relativePath string, target artifact) (*pom, error) {
|
||||
var errs error
|
||||
|
||||
// Try relativePath
|
||||
@@ -536,7 +535,7 @@ func (p *parser) retrieveParent(currentPath, relativePath string, target artifac
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
func (p *parser) tryRelativePath(parentArtifact artifact, currentPath, relativePath string) (*pom, error) {
|
||||
func (p *Parser) tryRelativePath(parentArtifact artifact, currentPath, relativePath string) (*pom, error) {
|
||||
pom, err := p.openRelativePom(currentPath, relativePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -563,7 +562,7 @@ func (p *parser) tryRelativePath(parentArtifact artifact, currentPath, relativeP
|
||||
return pom, nil
|
||||
}
|
||||
|
||||
func (p *parser) openRelativePom(currentPath, relativePath string) (*pom, error) {
|
||||
func (p *Parser) openRelativePom(currentPath, relativePath string) (*pom, error) {
|
||||
// e.g. child/pom.xml => child/
|
||||
dir := filepath.Dir(currentPath)
|
||||
|
||||
@@ -585,7 +584,7 @@ func (p *parser) openRelativePom(currentPath, relativePath string) (*pom, error)
|
||||
return pom, nil
|
||||
}
|
||||
|
||||
func (p *parser) openPom(filePath string) (*pom, error) {
|
||||
func (p *Parser) openPom(filePath string) (*pom, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("file open error (%s): %w", filePath, err)
|
||||
@@ -601,7 +600,7 @@ func (p *parser) openPom(filePath string) (*pom, error) {
|
||||
content: content,
|
||||
}, nil
|
||||
}
|
||||
func (p *parser) tryRepository(groupID, artifactID, version string) (*pom, error) {
|
||||
func (p *Parser) tryRepository(groupID, artifactID, version string) (*pom, error) {
|
||||
if version == "" {
|
||||
return nil, xerrors.Errorf("Version missing for %s:%s", groupID, artifactID)
|
||||
}
|
||||
@@ -627,14 +626,14 @@ func (p *parser) tryRepository(groupID, artifactID, version string) (*pom, error
|
||||
return nil, xerrors.Errorf("%s:%s:%s was not found in local/remote repositories", groupID, artifactID, version)
|
||||
}
|
||||
|
||||
func (p *parser) loadPOMFromLocalRepository(paths []string) (*pom, error) {
|
||||
func (p *Parser) loadPOMFromLocalRepository(paths []string) (*pom, error) {
|
||||
paths = append([]string{p.localRepository}, paths...)
|
||||
localPath := filepath.Join(paths...)
|
||||
|
||||
return p.openPom(localPath)
|
||||
}
|
||||
|
||||
func (p *parser) fetchPOMFromRemoteRepositories(paths []string, snapshot bool) (*pom, error) {
|
||||
func (p *Parser) fetchPOMFromRemoteRepositories(paths []string, snapshot bool) (*pom, error) {
|
||||
// Do not try fetching pom.xml from remote repositories in offline mode
|
||||
if p.offline {
|
||||
p.logger.Debug("Fetching the remote pom.xml is skipped")
|
||||
@@ -660,7 +659,7 @@ func (p *parser) fetchPOMFromRemoteRepositories(paths []string, snapshot bool) (
|
||||
return nil, xerrors.Errorf("the POM was not found in remote remoteRepositories")
|
||||
}
|
||||
|
||||
func (p *parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom, error) {
|
||||
func (p *Parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom, error) {
|
||||
repoURL, err := url.Parse(repo)
|
||||
if err != nil {
|
||||
p.logger.Error("URL parse error", log.String("repo", repo))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,9 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/x/slices"
|
||||
)
|
||||
|
||||
type pom struct {
|
||||
@@ -110,9 +111,9 @@ func (p pom) artifact() artifact {
|
||||
}
|
||||
|
||||
func (p pom) licenses() []string {
|
||||
return lo.FilterMap(p.content.Licenses.License, func(lic pomLicense, _ int) (string, bool) {
|
||||
return slices.ZeroToNil(lo.FilterMap(p.content.Licenses.License, func(lic pomLicense, _ int) (string, bool) {
|
||||
return lic.Name, lic.Name != ""
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
func (p pom) repositories(servers []Server) ([]string, []string) {
|
||||
@@ -286,9 +287,9 @@ func (d pomDependency) ToArtifact(opts analysisOptions) artifact {
|
||||
exclusions[fmt.Sprintf("%s:%s", e.GroupID, e.ArtifactID)] = struct{}{}
|
||||
}
|
||||
|
||||
var locations types.Locations
|
||||
var locations ftypes.Locations
|
||||
if opts.lineNumber {
|
||||
locations = types.Locations{
|
||||
locations = ftypes.Locations{
|
||||
{
|
||||
StartLine: d.StartLine,
|
||||
EndLine: d.EndLine,
|
||||
@@ -302,7 +303,7 @@ func (d pomDependency) ToArtifact(opts analysisOptions) artifact {
|
||||
Version: newVersion(d.Version),
|
||||
Exclusions: exclusions,
|
||||
Locations: locations,
|
||||
Relationship: types.RelationshipIndirect, // default
|
||||
Relationship: ftypes.RelationshipIndirect, // default
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
@@ -27,11 +27,11 @@ type primitiveDependency struct {
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var oldDeps map[string][]primitiveDependency
|
||||
var primMan primitiveManifest
|
||||
var manMetadata toml.MetaData
|
||||
@@ -68,19 +68,19 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
pkgParser := naivePkgParser{r: r}
|
||||
lineNumIdx := pkgParser.parse()
|
||||
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
var pkgs ftypes.Packages
|
||||
var deps ftypes.Dependencies
|
||||
for name, manifestDeps := range man.Dependencies {
|
||||
for _, manifestDep := range manifestDeps {
|
||||
version := depVersion(manifestDep.Version, man.JuliaVersion)
|
||||
pkgID := manifestDep.UUID
|
||||
lib := types.Library{
|
||||
pkg := ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
if pos, ok := lineNumIdx[manifestDep.UUID]; ok {
|
||||
lib.Locations = []types.Location{
|
||||
pkg.Locations = []ftypes.Location{
|
||||
{
|
||||
StartLine: pos.start,
|
||||
EndLine: pos.end,
|
||||
@@ -88,19 +88,19 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
}
|
||||
}
|
||||
|
||||
libs = append(libs, lib)
|
||||
pkgs = append(pkgs, pkg)
|
||||
|
||||
if len(manifestDep.DependsOn) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: manifestDep.DependsOn,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(types.Libraries(libs))
|
||||
sort.Sort(types.Dependencies(deps))
|
||||
return libs, deps, nil
|
||||
sort.Sort(pkgs)
|
||||
sort.Sort(deps)
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
// Returns the effective version of the `dep`.
|
||||
|
||||
@@ -8,26 +8,26 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string // Test input file
|
||||
want []types.Library
|
||||
wantDeps []types.Dependency
|
||||
want []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
name: "Manifest v1.6",
|
||||
file: "testdata/primary/Manifest_v1.6.toml",
|
||||
want: juliaV1_6Libs,
|
||||
want: juliaV1_6Pkgs,
|
||||
wantDeps: juliaV1_6Deps,
|
||||
},
|
||||
{
|
||||
name: "Manifest v1.8",
|
||||
file: "testdata/primary/Manifest_v1.8.toml",
|
||||
want: juliaV1_8Libs,
|
||||
want: juliaV1_8Pkgs,
|
||||
wantDeps: juliaV1_8Deps,
|
||||
},
|
||||
{
|
||||
@@ -45,13 +45,13 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "dep extensions v1.9",
|
||||
file: "testdata/dep_ext_v1.9/Manifest.toml",
|
||||
want: juliaV1_9DepExtLibs,
|
||||
want: juliaV1_9DepExtPkgs,
|
||||
wantDeps: nil,
|
||||
},
|
||||
{
|
||||
name: "shadowed dep v1.9",
|
||||
file: "testdata/shadowed_dep_v1.9/Manifest.toml",
|
||||
want: juliaV1_9ShadowedDepLibs,
|
||||
want: juliaV1_9ShadowedDepPkgs,
|
||||
wantDeps: juliaV1_9ShadowedDepDeps,
|
||||
},
|
||||
}
|
||||
@@ -61,13 +61,13 @@ func TestParse(t *testing.T) {
|
||||
f, err := os.Open(tt.file)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotLibs, gotDeps, err := NewParser().Parse(f)
|
||||
gotPkgs, gotDeps, err := NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Sort(types.Libraries(tt.want))
|
||||
assert.Equal(t, tt.want, gotLibs)
|
||||
sort.Sort(ftypes.Packages(tt.want))
|
||||
assert.Equal(t, tt.want, gotPkgs)
|
||||
if tt.wantDeps != nil {
|
||||
sort.Sort(types.Dependencies(tt.wantDeps))
|
||||
sort.Sort(ftypes.Dependencies(tt.wantDeps))
|
||||
assert.Equal(t, tt.wantDeps, gotDeps)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package julia
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
juliaV1_6Libs = []types.Library{
|
||||
{ID: "ade2ca70-3891-5945-98fb-dc099432e06a", Name: "Dates", Version: "unknown", Locations: []types.Location{{StartLine: 3, EndLine: 5}}},
|
||||
{ID: "682c06a0-de6a-54ab-a142-c8b1cf79cde6", Name: "JSON", Version: "0.21.4", Locations: []types.Location{{StartLine: 7, EndLine: 11}}},
|
||||
{ID: "a63ad114-7e13-5084-954f-fe012c677804", Name: "Mmap", Version: "unknown", Locations: []types.Location{{StartLine: 13, EndLine: 14}}},
|
||||
{ID: "69de0a69-1ddd-5017-9359-2bf0b02dc9f0", Name: "Parsers", Version: "2.4.2", Locations: []types.Location{{StartLine: 16, EndLine: 20}}},
|
||||
{ID: "de0858da-6303-5e67-8744-51eddeeeb8d7", Name: "Printf", Version: "unknown", Locations: []types.Location{{StartLine: 22, EndLine: 24}}},
|
||||
{ID: "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5", Name: "Unicode", Version: "unknown", Locations: []types.Location{{StartLine: 26, EndLine: 27}}},
|
||||
juliaV1_6Pkgs = []ftypes.Package{
|
||||
{ID: "ade2ca70-3891-5945-98fb-dc099432e06a", Name: "Dates", Version: "unknown", Locations: []ftypes.Location{{StartLine: 3, EndLine: 5}}},
|
||||
{ID: "682c06a0-de6a-54ab-a142-c8b1cf79cde6", Name: "JSON", Version: "0.21.4", Locations: []ftypes.Location{{StartLine: 7, EndLine: 11}}},
|
||||
{ID: "a63ad114-7e13-5084-954f-fe012c677804", Name: "Mmap", Version: "unknown", Locations: []ftypes.Location{{StartLine: 13, EndLine: 14}}},
|
||||
{ID: "69de0a69-1ddd-5017-9359-2bf0b02dc9f0", Name: "Parsers", Version: "2.4.2", Locations: []ftypes.Location{{StartLine: 16, EndLine: 20}}},
|
||||
{ID: "de0858da-6303-5e67-8744-51eddeeeb8d7", Name: "Printf", Version: "unknown", Locations: []ftypes.Location{{StartLine: 22, EndLine: 24}}},
|
||||
{ID: "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5", Name: "Unicode", Version: "unknown", Locations: []ftypes.Location{{StartLine: 26, EndLine: 27}}},
|
||||
}
|
||||
|
||||
juliaV1_6Deps = []types.Dependency{
|
||||
juliaV1_6Deps = []ftypes.Dependency{
|
||||
{ID: "ade2ca70-3891-5945-98fb-dc099432e06a", DependsOn: []string{"de0858da-6303-5e67-8744-51eddeeeb8d7"}},
|
||||
{ID: "682c06a0-de6a-54ab-a142-c8b1cf79cde6", DependsOn: []string{
|
||||
"4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5",
|
||||
@@ -24,23 +24,23 @@ var (
|
||||
{ID: "de0858da-6303-5e67-8744-51eddeeeb8d7", DependsOn: []string{"4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"}},
|
||||
}
|
||||
|
||||
juliaV1_8Libs = []types.Library{
|
||||
{ID: "ade2ca70-3891-5945-98fb-dc099432e06a", Name: "Dates", Version: "1.8.5", Locations: []types.Location{{StartLine: 7, EndLine: 9}}},
|
||||
{ID: "682c06a0-de6a-54ab-a142-c8b1cf79cde6", Name: "JSON", Version: "0.21.4", Locations: []types.Location{{StartLine: 11, EndLine: 15}}},
|
||||
{ID: "a63ad114-7e13-5084-954f-fe012c677804", Name: "Mmap", Version: "1.8.5", Locations: []types.Location{{StartLine: 17, EndLine: 18}}},
|
||||
{ID: "69de0a69-1ddd-5017-9359-2bf0b02dc9f0", Name: "Parsers", Version: "2.5.10", Locations: []types.Location{{StartLine: 20, EndLine: 24}}},
|
||||
{ID: "aea7be01-6a6a-4083-8856-8a6e6704d82a", Name: "PrecompileTools", Version: "1.1.1", Locations: []types.Location{{StartLine: 26, EndLine: 30}}},
|
||||
{ID: "21216c6a-2e73-6563-6e65-726566657250", Name: "Preferences", Version: "1.4.0", Locations: []types.Location{{StartLine: 32, EndLine: 36}}},
|
||||
{ID: "de0858da-6303-5e67-8744-51eddeeeb8d7", Name: "Printf", Version: "1.8.5", Locations: []types.Location{{StartLine: 38, EndLine: 40}}},
|
||||
{ID: "9a3f8284-a2c9-5f02-9a11-845980a1fd5c", Name: "Random", Version: "1.8.5", Locations: []types.Location{{StartLine: 42, EndLine: 44}}},
|
||||
{ID: "ea8e919c-243c-51af-8825-aaa63cd721ce", Name: "SHA", Version: "0.7.0", Locations: []types.Location{{StartLine: 46, EndLine: 48}}},
|
||||
{ID: "9e88b42a-f829-5b0c-bbe9-9e923198166b", Name: "Serialization", Version: "1.8.5", Locations: []types.Location{{StartLine: 50, EndLine: 51}}},
|
||||
{ID: "fa267f1f-6049-4f14-aa54-33bafae1ed76", Name: "TOML", Version: "1.0.0", Locations: []types.Location{{StartLine: 53, EndLine: 56}}},
|
||||
{ID: "cf7118a7-6976-5b1a-9a39-7adc72f591a4", Name: "UUIDs", Version: "1.8.5", Locations: []types.Location{{StartLine: 58, EndLine: 60}}},
|
||||
{ID: "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5", Name: "Unicode", Version: "1.8.5", Locations: []types.Location{{StartLine: 62, EndLine: 63}}},
|
||||
juliaV1_8Pkgs = []ftypes.Package{
|
||||
{ID: "ade2ca70-3891-5945-98fb-dc099432e06a", Name: "Dates", Version: "1.8.5", Locations: []ftypes.Location{{StartLine: 7, EndLine: 9}}},
|
||||
{ID: "682c06a0-de6a-54ab-a142-c8b1cf79cde6", Name: "JSON", Version: "0.21.4", Locations: []ftypes.Location{{StartLine: 11, EndLine: 15}}},
|
||||
{ID: "a63ad114-7e13-5084-954f-fe012c677804", Name: "Mmap", Version: "1.8.5", Locations: []ftypes.Location{{StartLine: 17, EndLine: 18}}},
|
||||
{ID: "69de0a69-1ddd-5017-9359-2bf0b02dc9f0", Name: "Parsers", Version: "2.5.10", Locations: []ftypes.Location{{StartLine: 20, EndLine: 24}}},
|
||||
{ID: "aea7be01-6a6a-4083-8856-8a6e6704d82a", Name: "PrecompileTools", Version: "1.1.1", Locations: []ftypes.Location{{StartLine: 26, EndLine: 30}}},
|
||||
{ID: "21216c6a-2e73-6563-6e65-726566657250", Name: "Preferences", Version: "1.4.0", Locations: []ftypes.Location{{StartLine: 32, EndLine: 36}}},
|
||||
{ID: "de0858da-6303-5e67-8744-51eddeeeb8d7", Name: "Printf", Version: "1.8.5", Locations: []ftypes.Location{{StartLine: 38, EndLine: 40}}},
|
||||
{ID: "9a3f8284-a2c9-5f02-9a11-845980a1fd5c", Name: "Random", Version: "1.8.5", Locations: []ftypes.Location{{StartLine: 42, EndLine: 44}}},
|
||||
{ID: "ea8e919c-243c-51af-8825-aaa63cd721ce", Name: "SHA", Version: "0.7.0", Locations: []ftypes.Location{{StartLine: 46, EndLine: 48}}},
|
||||
{ID: "9e88b42a-f829-5b0c-bbe9-9e923198166b", Name: "Serialization", Version: "1.8.5", Locations: []ftypes.Location{{StartLine: 50, EndLine: 51}}},
|
||||
{ID: "fa267f1f-6049-4f14-aa54-33bafae1ed76", Name: "TOML", Version: "1.0.0", Locations: []ftypes.Location{{StartLine: 53, EndLine: 56}}},
|
||||
{ID: "cf7118a7-6976-5b1a-9a39-7adc72f591a4", Name: "UUIDs", Version: "1.8.5", Locations: []ftypes.Location{{StartLine: 58, EndLine: 60}}},
|
||||
{ID: "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5", Name: "Unicode", Version: "1.8.5", Locations: []ftypes.Location{{StartLine: 62, EndLine: 63}}},
|
||||
}
|
||||
|
||||
juliaV1_8Deps = []types.Dependency{
|
||||
juliaV1_8Deps = []ftypes.Dependency{
|
||||
{ID: "ade2ca70-3891-5945-98fb-dc099432e06a", DependsOn: []string{"de0858da-6303-5e67-8744-51eddeeeb8d7"}},
|
||||
{ID: "682c06a0-de6a-54ab-a142-c8b1cf79cde6", DependsOn: []string{
|
||||
"4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5",
|
||||
@@ -61,17 +61,17 @@ var (
|
||||
{ID: "cf7118a7-6976-5b1a-9a39-7adc72f591a4", DependsOn: []string{"9a3f8284-a2c9-5f02-9a11-845980a1fd5c", "ea8e919c-243c-51af-8825-aaa63cd721ce"}},
|
||||
}
|
||||
|
||||
juliaV1_9DepExtLibs = []types.Library{
|
||||
{ID: "621f4979-c628-5d54-868e-fcf4e3e8185c", Name: "AbstractFFTs", Version: "1.3.1", Locations: []types.Location{{StartLine: 7, EndLine: 10}}},
|
||||
juliaV1_9DepExtPkgs = []ftypes.Package{
|
||||
{ID: "621f4979-c628-5d54-868e-fcf4e3e8185c", Name: "AbstractFFTs", Version: "1.3.1", Locations: []ftypes.Location{{StartLine: 7, EndLine: 10}}},
|
||||
}
|
||||
|
||||
juliaV1_9ShadowedDepLibs = []types.Library{
|
||||
{ID: "ead4f63c-334e-11e9-00e6-e7f0a5f21b60", Name: "A", Version: "1.9.0", Locations: []types.Location{{StartLine: 7, EndLine: 8}}},
|
||||
{ID: "f41f7b98-334e-11e9-1257-49272045fb24", Name: "B", Version: "1.9.0", Locations: []types.Location{{StartLine: 13, EndLine: 14}}},
|
||||
{ID: "edca9bc6-334e-11e9-3554-9595dbb4349c", Name: "B", Version: "1.9.0", Locations: []types.Location{{StartLine: 15, EndLine: 16}}},
|
||||
juliaV1_9ShadowedDepPkgs = []ftypes.Package{
|
||||
{ID: "ead4f63c-334e-11e9-00e6-e7f0a5f21b60", Name: "A", Version: "1.9.0", Locations: []ftypes.Location{{StartLine: 7, EndLine: 8}}},
|
||||
{ID: "f41f7b98-334e-11e9-1257-49272045fb24", Name: "B", Version: "1.9.0", Locations: []ftypes.Location{{StartLine: 13, EndLine: 14}}},
|
||||
{ID: "edca9bc6-334e-11e9-3554-9595dbb4349c", Name: "B", Version: "1.9.0", Locations: []ftypes.Location{{StartLine: 15, EndLine: 16}}},
|
||||
}
|
||||
|
||||
juliaV1_9ShadowedDepDeps = []types.Dependency{
|
||||
juliaV1_9ShadowedDepDeps = []ftypes.Dependency{
|
||||
{ID: "ead4f63c-334e-11e9-00e6-e7f0a5f21b60", DependsOn: []string{"f41f7b98-334e-11e9-1257-49272045fb24"}},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -56,13 +55,13 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("npm"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockFile LockFile
|
||||
input, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
@@ -72,20 +71,20 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("decode error: %w", err)
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
var pkgs []ftypes.Package
|
||||
var deps []ftypes.Dependency
|
||||
if lockFile.LockfileVersion == 1 {
|
||||
libs, deps = p.parseV1(lockFile.Dependencies, make(map[string]string))
|
||||
pkgs, deps = p.parseV1(lockFile.Dependencies, make(map[string]string))
|
||||
} else {
|
||||
libs, deps = p.parseV2(lockFile.Packages)
|
||||
pkgs, deps = p.parseV2(lockFile.Packages)
|
||||
}
|
||||
|
||||
return utils.UniqueLibraries(libs), uniqueDeps(deps), nil
|
||||
return utils.UniquePackages(pkgs), uniqueDeps(deps), nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseV2(packages map[string]Package) ([]types.Library, []types.Dependency) {
|
||||
libs := make(map[string]types.Library, len(packages)-1)
|
||||
var deps []types.Dependency
|
||||
func (p *Parser) parseV2(packages map[string]Package) ([]ftypes.Package, []ftypes.Dependency) {
|
||||
pkgs := make(map[string]ftypes.Package, len(packages)-1)
|
||||
var deps []ftypes.Dependency
|
||||
|
||||
// Resolve links first
|
||||
// https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#packages
|
||||
@@ -116,51 +115,51 @@ func (p *Parser) parseV2(packages map[string]Package) ([]types.Library, []types.
|
||||
}
|
||||
|
||||
pkgID := packageID(pkgName, pkg.Version)
|
||||
location := types.Location{
|
||||
location := ftypes.Location{
|
||||
StartLine: pkg.StartLine,
|
||||
EndLine: pkg.EndLine,
|
||||
}
|
||||
|
||||
var ref types.ExternalRef
|
||||
var ref ftypes.ExternalRef
|
||||
if pkg.Resolved != "" {
|
||||
ref = types.ExternalRef{
|
||||
Type: types.RefOther,
|
||||
ref = ftypes.ExternalRef{
|
||||
Type: ftypes.RefOther,
|
||||
URL: pkg.Resolved,
|
||||
}
|
||||
}
|
||||
|
||||
pkgIndirect := isIndirectLib(pkgPath, directDeps)
|
||||
pkgIndirect := isIndirectPkg(pkgPath, directDeps)
|
||||
|
||||
// There are cases when similar libraries use same dependencies
|
||||
// There are cases when similar packages use same dependencies
|
||||
// we need to add location for each these dependencies
|
||||
if savedLib, ok := libs[pkgID]; ok {
|
||||
savedLib.Dev = savedLib.Dev && pkg.Dev
|
||||
if savedLib.Relationship == types.RelationshipIndirect && !pkgIndirect {
|
||||
savedLib.Relationship = types.RelationshipDirect
|
||||
if savedPkg, ok := pkgs[pkgID]; ok {
|
||||
savedPkg.Dev = savedPkg.Dev && pkg.Dev
|
||||
if savedPkg.Relationship == ftypes.RelationshipIndirect && !pkgIndirect {
|
||||
savedPkg.Relationship = ftypes.RelationshipDirect
|
||||
}
|
||||
|
||||
if ref.URL != "" && !slices.Contains(savedLib.ExternalReferences, ref) {
|
||||
savedLib.ExternalReferences = append(savedLib.ExternalReferences, ref)
|
||||
sortExternalReferences(savedLib.ExternalReferences)
|
||||
if ref.URL != "" && !slices.Contains(savedPkg.ExternalReferences, ref) {
|
||||
savedPkg.ExternalReferences = append(savedPkg.ExternalReferences, ref)
|
||||
sortExternalReferences(savedPkg.ExternalReferences)
|
||||
}
|
||||
|
||||
savedLib.Locations = append(savedLib.Locations, location)
|
||||
sort.Sort(savedLib.Locations)
|
||||
savedPkg.Locations = append(savedPkg.Locations, location)
|
||||
sort.Sort(savedPkg.Locations)
|
||||
|
||||
libs[pkgID] = savedLib
|
||||
pkgs[pkgID] = savedPkg
|
||||
continue
|
||||
}
|
||||
|
||||
lib := types.Library{
|
||||
newPkg := ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: pkgName,
|
||||
Version: pkg.Version,
|
||||
Relationship: lo.Ternary(pkgIndirect, types.RelationshipIndirect, types.RelationshipDirect),
|
||||
Relationship: lo.Ternary(pkgIndirect, ftypes.RelationshipIndirect, ftypes.RelationshipDirect),
|
||||
Dev: pkg.Dev,
|
||||
ExternalReferences: lo.Ternary(ref.URL != "", []types.ExternalRef{ref}, nil),
|
||||
Locations: []types.Location{location},
|
||||
ExternalReferences: lo.Ternary(ref.URL != "", []ftypes.ExternalRef{ref}, nil),
|
||||
Locations: []ftypes.Location{location},
|
||||
}
|
||||
libs[pkgID] = lib
|
||||
pkgs[pkgID] = newPkg
|
||||
|
||||
// npm builds graph using optional deps. e.g.:
|
||||
// └─┬ watchpack@1.7.5
|
||||
@@ -179,15 +178,15 @@ func (p *Parser) parseV2(packages map[string]Package) ([]types.Library, []types.
|
||||
}
|
||||
|
||||
if len(dependsOn) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
ID: lib.ID,
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: newPkg.ID,
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return maps.Values(libs), deps
|
||||
return maps.Values(pkgs), deps
|
||||
}
|
||||
|
||||
// for local package npm uses links. e.g.:
|
||||
@@ -271,73 +270,73 @@ func findDependsOn(pkgPath, depName string, packages map[string]Package) (string
|
||||
return "", xerrors.Errorf("can't find dependsOn for %s", depName)
|
||||
}
|
||||
|
||||
func (p *Parser) parseV1(dependencies map[string]Dependency, versions map[string]string) ([]types.Library, []types.Dependency) {
|
||||
func (p *Parser) parseV1(dependencies map[string]Dependency, versions map[string]string) ([]ftypes.Package, []ftypes.Dependency) {
|
||||
// Update package name and version mapping.
|
||||
for pkgName, dep := range dependencies {
|
||||
// Overwrite the existing package version so that the nested version can take precedence.
|
||||
versions[pkgName] = dep.Version
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
var pkgs []ftypes.Package
|
||||
var deps []ftypes.Dependency
|
||||
for pkgName, dep := range dependencies {
|
||||
lib := types.Library{
|
||||
pkg := ftypes.Package{
|
||||
ID: packageID(pkgName, dep.Version),
|
||||
Name: pkgName,
|
||||
Version: dep.Version,
|
||||
Dev: dep.Dev,
|
||||
Relationship: types.RelationshipUnknown, // lockfile v1 schema doesn't have information about direct dependencies
|
||||
ExternalReferences: []types.ExternalRef{
|
||||
Relationship: ftypes.RelationshipUnknown, // lockfile v1 schema doesn't have information about direct dependencies
|
||||
ExternalReferences: []ftypes.ExternalRef{
|
||||
{
|
||||
Type: types.RefOther,
|
||||
Type: ftypes.RefOther,
|
||||
URL: dep.Resolved,
|
||||
},
|
||||
},
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: dep.StartLine,
|
||||
EndLine: dep.EndLine,
|
||||
},
|
||||
},
|
||||
}
|
||||
libs = append(libs, lib)
|
||||
pkgs = append(pkgs, pkg)
|
||||
|
||||
dependsOn := make([]string, 0, len(dep.Requires))
|
||||
for libName, requiredVer := range dep.Requires {
|
||||
for pName, requiredVer := range dep.Requires {
|
||||
// Try to resolve the version with nested dependencies first
|
||||
if resolvedDep, ok := dep.Dependencies[libName]; ok {
|
||||
libID := packageID(libName, resolvedDep.Version)
|
||||
dependsOn = append(dependsOn, libID)
|
||||
if resolvedDep, ok := dep.Dependencies[pName]; ok {
|
||||
pkgID := packageID(pName, resolvedDep.Version)
|
||||
dependsOn = append(dependsOn, pkgID)
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to resolve the version with the higher level dependencies
|
||||
if ver, ok := versions[libName]; ok {
|
||||
dependsOn = append(dependsOn, packageID(libName, ver))
|
||||
if ver, ok := versions[pName]; ok {
|
||||
dependsOn = append(dependsOn, packageID(pName, ver))
|
||||
continue
|
||||
}
|
||||
|
||||
// It should not reach here.
|
||||
p.logger.Warn("Unable to resolve the version",
|
||||
log.String("name", libName), log.String("version", requiredVer))
|
||||
log.String("name", pName), log.String("version", requiredVer))
|
||||
}
|
||||
|
||||
if len(dependsOn) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
ID: packageID(lib.Name, lib.Version),
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: packageID(pkg.Name, pkg.Version),
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
}
|
||||
|
||||
if dep.Dependencies != nil {
|
||||
// Recursion
|
||||
childLibs, childDeps := p.parseV1(dep.Dependencies, maps.Clone(versions))
|
||||
libs = append(libs, childLibs...)
|
||||
childpkgs, childDeps := p.parseV1(dep.Dependencies, maps.Clone(versions))
|
||||
pkgs = append(pkgs, childpkgs...)
|
||||
deps = append(deps, childDeps...)
|
||||
}
|
||||
}
|
||||
|
||||
return libs, deps
|
||||
return pkgs, deps
|
||||
}
|
||||
|
||||
func (p *Parser) pkgNameFromPath(pkgPath string) string {
|
||||
@@ -354,8 +353,8 @@ func (p *Parser) pkgNameFromPath(pkgPath string) string {
|
||||
return pkgPath
|
||||
}
|
||||
|
||||
func uniqueDeps(deps []types.Dependency) []types.Dependency {
|
||||
var uniqDeps []types.Dependency
|
||||
func uniqueDeps(deps []ftypes.Dependency) []ftypes.Dependency {
|
||||
var uniqDeps ftypes.Dependencies
|
||||
unique := make(map[string]struct{})
|
||||
|
||||
for _, dep := range deps {
|
||||
@@ -367,14 +366,14 @@ func uniqueDeps(deps []types.Dependency) []types.Dependency {
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(types.Dependencies(uniqDeps))
|
||||
sort.Sort(uniqDeps)
|
||||
return uniqDeps
|
||||
}
|
||||
|
||||
func isIndirectLib(pkgPath string, directDeps map[string]struct{}) bool {
|
||||
func isIndirectPkg(pkgPath string, directDeps map[string]struct{}) bool {
|
||||
// A project can contain 2 different versions of the same dependency.
|
||||
// e.g. `node_modules/string-width/node_modules/strip-ansi` and `node_modules/string-ansi`
|
||||
// direct dependencies always have root path (`node_modules/<lib_name>`)
|
||||
// direct dependencies always have root path (`node_modules/<pkg_name>`)
|
||||
if _, ok := directDeps[pkgPath]; ok {
|
||||
return false
|
||||
}
|
||||
@@ -411,7 +410,7 @@ func packageID(name, version string) string {
|
||||
return dependency.ID(ftypes.Npm, name, version)
|
||||
}
|
||||
|
||||
func sortExternalReferences(refs []types.ExternalRef) {
|
||||
func sortExternalReferences(refs []ftypes.ExternalRef) {
|
||||
sort.Slice(refs, func(i, j int) bool {
|
||||
if refs[i].Type != refs[j].Type {
|
||||
return refs[i].Type < refs[j].Type
|
||||
|
||||
@@ -7,44 +7,44 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string // Test input file
|
||||
want []types.Library
|
||||
wantDeps []types.Dependency
|
||||
want []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
name: "lock version v1",
|
||||
file: "testdata/package-lock_v1.json",
|
||||
want: npmV1Libs,
|
||||
want: npmV1Pkgs,
|
||||
wantDeps: npmDeps,
|
||||
},
|
||||
{
|
||||
name: "lock version v2",
|
||||
file: "testdata/package-lock_v2.json",
|
||||
want: npmV2Libs,
|
||||
want: npmV2Pkgs,
|
||||
wantDeps: npmDeps,
|
||||
},
|
||||
{
|
||||
name: "lock version v3",
|
||||
file: "testdata/package-lock_v3.json",
|
||||
want: npmV2Libs,
|
||||
want: npmV2Pkgs,
|
||||
wantDeps: npmDeps,
|
||||
},
|
||||
{
|
||||
name: "lock version v3 with workspace",
|
||||
file: "testdata/package-lock_v3_with_workspace.json",
|
||||
want: npmV3WithWorkspaceLibs,
|
||||
want: npmV3WithWorkspacePkgs,
|
||||
wantDeps: npmV3WithWorkspaceDeps,
|
||||
},
|
||||
{
|
||||
name: "lock file v3 contains same dev and non-dev dependencies",
|
||||
file: "testdata/package-lock_v3_with-same-dev-and-non-dev.json",
|
||||
want: npmV3WithSameDevAndNonDevLibs,
|
||||
want: npmV3WithSameDevAndNonDevPkgs,
|
||||
wantDeps: npmV3WithSameDevAndNonDevDeps,
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
@@ -26,7 +25,7 @@ type packageJSON struct {
|
||||
}
|
||||
|
||||
type Package struct {
|
||||
types.Library
|
||||
ftypes.Package
|
||||
Dependencies map[string]string
|
||||
OptionalDependencies map[string]string
|
||||
DevDependencies map[string]string
|
||||
@@ -57,11 +56,11 @@ func (p *Parser) Parse(r io.Reader) (Package, error) {
|
||||
}
|
||||
|
||||
return Package{
|
||||
Library: types.Library{
|
||||
ID: id,
|
||||
Name: pkgJSON.Name,
|
||||
Version: pkgJSON.Version,
|
||||
License: parseLicense(pkgJSON.License),
|
||||
Package: ftypes.Package{
|
||||
ID: id,
|
||||
Name: pkgJSON.Name,
|
||||
Version: pkgJSON.Version,
|
||||
Licenses: parseLicense(pkgJSON.License),
|
||||
},
|
||||
Dependencies: pkgJSON.Dependencies,
|
||||
OptionalDependencies: pkgJSON.OptionalDependencies,
|
||||
@@ -70,17 +69,21 @@ func (p *Parser) Parse(r io.Reader) (Package, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseLicense(val interface{}) string {
|
||||
func parseLicense(val interface{}) []string {
|
||||
// the license isn't always a string, check for legacy struct if not string
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
return v
|
||||
if v != "" {
|
||||
return []string{v}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if license, ok := v["type"]; ok {
|
||||
return license.(string)
|
||||
if s, ok := license.(string); ok && s != "" {
|
||||
return []string{s}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseWorkspaces returns slice of workspaces
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/packagejson"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
@@ -27,11 +27,11 @@ func TestParse(t *testing.T) {
|
||||
// npm install --save promise jquery
|
||||
// npm ls | grep -E -o "\S+@\S+" | awk -F@ 'NR>0 {printf("{\""$1"\", \""$2"\"},\n")}'
|
||||
want: packagejson.Package{
|
||||
Library: types.Library{
|
||||
ID: "bootstrap@5.0.2",
|
||||
Name: "bootstrap",
|
||||
Version: "5.0.2",
|
||||
License: "MIT",
|
||||
Package: ftypes.Package{
|
||||
ID: "bootstrap@5.0.2",
|
||||
Name: "bootstrap",
|
||||
Version: "5.0.2",
|
||||
Licenses: []string{"MIT"},
|
||||
},
|
||||
Dependencies: map[string]string{
|
||||
"js-tokens": "^4.0.0",
|
||||
@@ -53,11 +53,11 @@ func TestParse(t *testing.T) {
|
||||
name: "happy path - legacy license",
|
||||
inputFile: "testdata/legacy_package.json",
|
||||
want: packagejson.Package{
|
||||
Library: types.Library{
|
||||
ID: "angular@4.1.2",
|
||||
Name: "angular",
|
||||
Version: "4.1.2",
|
||||
License: "ISC",
|
||||
Package: ftypes.Package{
|
||||
ID: "angular@4.1.2",
|
||||
Name: "angular",
|
||||
Version: "4.1.2",
|
||||
Licenses: []string{"ISC"},
|
||||
},
|
||||
Dependencies: map[string]string{},
|
||||
DevDependencies: map[string]string{
|
||||
@@ -70,7 +70,7 @@ func TestParse(t *testing.T) {
|
||||
name: "happy path - version doesn't exist",
|
||||
inputFile: "testdata/without_version_package.json",
|
||||
want: packagejson.Package{
|
||||
Library: types.Library{
|
||||
Package: ftypes.Package{
|
||||
ID: "",
|
||||
Name: "angular",
|
||||
},
|
||||
@@ -80,7 +80,7 @@ func TestParse(t *testing.T) {
|
||||
name: "happy path - workspace as struct",
|
||||
inputFile: "testdata/workspace_as_map_package.json",
|
||||
want: packagejson.Package{
|
||||
Library: types.Library{
|
||||
Package: ftypes.Package{
|
||||
ID: "example@1.0.0",
|
||||
Name: "example",
|
||||
Version: "1.0.0",
|
||||
@@ -109,8 +109,8 @@ func TestParse(t *testing.T) {
|
||||
name: "without name and version",
|
||||
inputFile: "testdata/without_name_and_version_package.json",
|
||||
want: packagejson.Package{
|
||||
Library: types.Library{
|
||||
License: "MIT",
|
||||
Package: ftypes.Package{
|
||||
Licenses: []string{"MIT"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -162,7 +162,8 @@ func TestIsValidName(t *testing.T) {
|
||||
{
|
||||
name: "test@package",
|
||||
want: false,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "test?package",
|
||||
want: false,
|
||||
},
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/aquasecurity/go-version/pkg/semver"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -47,7 +46,7 @@ func NewParser() *Parser {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockFile LockFile
|
||||
if err := yaml.NewDecoder(r).Decode(&lockFile); err != nil {
|
||||
return nil, nil, xerrors.Errorf("decode error: %w", err)
|
||||
@@ -58,14 +57,14 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
libs, deps := p.parse(lockVer, lockFile)
|
||||
pkgs, deps := p.parse(lockVer, lockFile)
|
||||
|
||||
return libs, deps, nil
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parse(lockVer float64, lockFile LockFile) ([]types.Library, []types.Dependency) {
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
func (p *Parser) parse(lockVer float64, lockFile LockFile) ([]ftypes.Package, []ftypes.Dependency) {
|
||||
var pkgs []ftypes.Package
|
||||
var deps []ftypes.Dependency
|
||||
|
||||
// Dependency path is a path to a dependency with a specific set of resolved subdependencies.
|
||||
// cf. https://github.com/pnpm/spec/blob/ad27a225f81d9215becadfa540ef05fa4ad6dd60/dependency-path.md
|
||||
@@ -90,22 +89,22 @@ func (p *Parser) parse(lockVer float64, lockFile LockFile) ([]types.Library, []t
|
||||
dependencies = append(dependencies, packageID(depName, depVer))
|
||||
}
|
||||
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Relationship: lo.Ternary(isDirectLib(name, lockFile.Dependencies), types.RelationshipDirect, types.RelationshipIndirect),
|
||||
Relationship: lo.Ternary(isDirectPkg(name, lockFile.Dependencies), ftypes.RelationshipDirect, ftypes.RelationshipIndirect),
|
||||
})
|
||||
|
||||
if len(dependencies) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: dependencies,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return libs, deps
|
||||
return pkgs, deps
|
||||
}
|
||||
|
||||
func (p *Parser) parseLockfileVersion(lockFile LockFile) float64 {
|
||||
@@ -179,7 +178,7 @@ func (p *Parser) parseDepPath(depPath, versionSep string) (string, string) {
|
||||
return name, version
|
||||
}
|
||||
|
||||
func isDirectLib(name string, directDeps map[string]interface{}) bool {
|
||||
func isDirectPkg(name string, directDeps map[string]interface{}) bool {
|
||||
_, ok := directDeps[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -3,21 +3,20 @@ package pnpm
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string // Test input file
|
||||
want []types.Library
|
||||
wantDeps []types.Dependency
|
||||
want []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
@@ -65,39 +64,25 @@ func TestParse(t *testing.T) {
|
||||
got, deps, err := NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sortLibs(got)
|
||||
sortLibs(tt.want)
|
||||
|
||||
sort.Sort(ftypes.Packages(got))
|
||||
sort.Sort(ftypes.Packages(tt.want))
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
if tt.wantDeps != nil {
|
||||
sortDeps(deps)
|
||||
sortDeps(tt.wantDeps)
|
||||
sort.Sort(ftypes.Dependencies(deps))
|
||||
sort.Sort(ftypes.Dependencies(tt.wantDeps))
|
||||
for _, dep := range deps {
|
||||
sort.Strings(dep.DependsOn)
|
||||
}
|
||||
for _, dep := range tt.wantDeps {
|
||||
sort.Strings(dep.DependsOn)
|
||||
}
|
||||
assert.Equal(t, tt.wantDeps, deps)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func sortDeps(deps []types.Dependency) {
|
||||
sort.Slice(deps, func(i, j int) bool {
|
||||
return strings.Compare(deps[i].ID, deps[j].ID) < 0
|
||||
})
|
||||
|
||||
for i := range deps {
|
||||
sort.Strings(deps[i].DependsOn)
|
||||
}
|
||||
}
|
||||
|
||||
func sortLibs(libs []types.Library) {
|
||||
sort.Slice(libs, func(i, j int) bool {
|
||||
ret := strings.Compare(libs[i].Name, libs[j].Name)
|
||||
if ret == 0 {
|
||||
return libs[i].Version < libs[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
}
|
||||
|
||||
func Test_parsePackage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
package pnpm
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
// docker run --name node --rm -it node:16-alpine sh
|
||||
// npm install -g pnpm
|
||||
// pnpm add promise jquery
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: types.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmNormal = []types.Library{
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: ftypes.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmNormal = []ftypes.Package{
|
||||
{
|
||||
ID: "asap@2.0.6",
|
||||
Name: "asap",
|
||||
Version: "2.0.6",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "jquery@3.6.0",
|
||||
Name: "jquery",
|
||||
Version: "3.6.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "promise@8.1.0",
|
||||
Name: "promise",
|
||||
Version: "8.1.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
}
|
||||
pnpmNormalDeps = []types.Dependency{
|
||||
pnpmNormalDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "promise@8.1.0",
|
||||
DependsOn: []string{"asap@2.0.6"},
|
||||
@@ -38,46 +38,46 @@ var (
|
||||
// npm install -g pnpm
|
||||
// pnpm add react redux
|
||||
// pnpm add -D mocha
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: types.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmWithDev = []types.Library{
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: ftypes.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmWithDev = []ftypes.Package{
|
||||
{
|
||||
ID: "@babel/runtime@7.18.3",
|
||||
Name: "@babel/runtime",
|
||||
Version: "7.18.3",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "js-tokens@4.0.0",
|
||||
Name: "js-tokens",
|
||||
Version: "4.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "loose-envify@1.4.0",
|
||||
Name: "loose-envify",
|
||||
Version: "1.4.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "react@18.1.0",
|
||||
Name: "react",
|
||||
Version: "18.1.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "redux@4.2.0",
|
||||
Name: "redux",
|
||||
Version: "4.2.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "regenerator-runtime@0.13.9",
|
||||
Name: "regenerator-runtime",
|
||||
Version: "0.13.9",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
}
|
||||
pnpmWithDevDeps = []types.Dependency{
|
||||
pnpmWithDevDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "@babel/runtime@7.18.3",
|
||||
DependsOn: []string{"regenerator-runtime@0.13.9"},
|
||||
@@ -100,346 +100,346 @@ var (
|
||||
// npm install -g pnpm
|
||||
// pnpm add react redux lodash request chalk commander
|
||||
// pnpm add -D mocha
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: types.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmMany = []types.Library{
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: ftypes.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmMany = []ftypes.Package{
|
||||
{
|
||||
ID: "@babel/runtime@7.18.3",
|
||||
Name: "@babel/runtime",
|
||||
Version: "7.18.3",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "ajv@6.12.6",
|
||||
Name: "ajv",
|
||||
Version: "6.12.6",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
Version: "0.2.6",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "assert-plus@1.0.0",
|
||||
Name: "assert-plus",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "asynckit@0.4.0",
|
||||
Name: "asynckit",
|
||||
Version: "0.4.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "aws-sign2@0.7.0",
|
||||
Name: "aws-sign2",
|
||||
Version: "0.7.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "aws4@1.11.0",
|
||||
Name: "aws4",
|
||||
Version: "1.11.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "bcrypt-pbkdf@1.0.2",
|
||||
Name: "bcrypt-pbkdf",
|
||||
Version: "1.0.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "caseless@0.12.0",
|
||||
Name: "caseless",
|
||||
Version: "0.12.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "chalk@5.0.1",
|
||||
Name: "chalk",
|
||||
Version: "5.0.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "combined-stream@1.0.8",
|
||||
Name: "combined-stream",
|
||||
Version: "1.0.8",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "commander@9.3.0",
|
||||
Name: "commander",
|
||||
Version: "9.3.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "core-util-is@1.0.2",
|
||||
Name: "core-util-is",
|
||||
Version: "1.0.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "dashdash@1.14.1",
|
||||
Name: "dashdash",
|
||||
Version: "1.14.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "delayed-stream@1.0.0",
|
||||
Name: "delayed-stream",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "ecc-jsbn@0.1.2",
|
||||
Name: "ecc-jsbn",
|
||||
Version: "0.1.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "extend@3.0.2",
|
||||
Name: "extend",
|
||||
Version: "3.0.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "extsprintf@1.3.0",
|
||||
Name: "extsprintf",
|
||||
Version: "1.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "fast-deep-equal@3.1.3",
|
||||
Name: "fast-deep-equal",
|
||||
Version: "3.1.3",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "fast-json-stable-stringify@2.1.0",
|
||||
Name: "fast-json-stable-stringify",
|
||||
Version: "2.1.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "forever-agent@0.6.1",
|
||||
Name: "forever-agent",
|
||||
Version: "0.6.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "form-data@2.3.3",
|
||||
Name: "form-data",
|
||||
Version: "2.3.3",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "getpass@0.1.7",
|
||||
Name: "getpass",
|
||||
Version: "0.1.7",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "har-schema@2.0.0",
|
||||
Name: "har-schema",
|
||||
Version: "2.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "har-validator@5.1.5",
|
||||
Name: "har-validator",
|
||||
Version: "5.1.5",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "http-signature@1.2.0",
|
||||
Name: "http-signature",
|
||||
Version: "1.2.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "is-typedarray@1.0.0",
|
||||
Name: "is-typedarray",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "isstream@0.1.2",
|
||||
Name: "isstream",
|
||||
Version: "0.1.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "js-tokens@4.0.0",
|
||||
Name: "js-tokens",
|
||||
Version: "4.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "jsbn@0.1.1",
|
||||
Name: "jsbn",
|
||||
Version: "0.1.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "json-schema-traverse@0.4.1",
|
||||
Name: "json-schema-traverse",
|
||||
Version: "0.4.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "json-schema@0.4.0",
|
||||
Name: "json-schema",
|
||||
Version: "0.4.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "json-stringify-safe@5.0.1",
|
||||
Name: "json-stringify-safe",
|
||||
Version: "5.0.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "jsprim@1.4.2",
|
||||
Name: "jsprim",
|
||||
Version: "1.4.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "lodash@4.17.21",
|
||||
Name: "lodash",
|
||||
Version: "4.17.21",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "loose-envify@1.4.0",
|
||||
Name: "loose-envify",
|
||||
Version: "1.4.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "mime-db@1.52.0",
|
||||
Name: "mime-db",
|
||||
Version: "1.52.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "mime-types@2.1.35",
|
||||
Name: "mime-types",
|
||||
Version: "2.1.35",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "oauth-sign@0.9.0",
|
||||
Name: "oauth-sign",
|
||||
Version: "0.9.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "performance-now@2.1.0",
|
||||
Name: "performance-now",
|
||||
Version: "2.1.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "psl@1.8.0",
|
||||
Name: "psl",
|
||||
Version: "1.8.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "punycode@2.1.1",
|
||||
Name: "punycode",
|
||||
Version: "2.1.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "qs@6.5.3",
|
||||
Name: "qs",
|
||||
Version: "6.5.3",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "react@18.1.0",
|
||||
Name: "react",
|
||||
Version: "18.1.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "redux@4.2.0",
|
||||
Name: "redux",
|
||||
Version: "4.2.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "regenerator-runtime@0.13.9",
|
||||
Name: "regenerator-runtime",
|
||||
Version: "0.13.9",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "request@2.88.2",
|
||||
Name: "request",
|
||||
Version: "2.88.2",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "safe-buffer@5.2.1",
|
||||
Name: "safe-buffer",
|
||||
Version: "5.2.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "safer-buffer@2.1.2",
|
||||
Name: "safer-buffer",
|
||||
Version: "2.1.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "sshpk@1.17.0",
|
||||
Name: "sshpk",
|
||||
Version: "1.17.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "tough-cookie@2.5.0",
|
||||
Name: "tough-cookie",
|
||||
Version: "2.5.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "tunnel-agent@0.6.0",
|
||||
Name: "tunnel-agent",
|
||||
Version: "0.6.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "tweetnacl@0.14.5",
|
||||
Name: "tweetnacl",
|
||||
Version: "0.14.5",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "uri-js@4.4.1",
|
||||
Name: "uri-js",
|
||||
Version: "4.4.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "uuid@3.4.0",
|
||||
Name: "uuid",
|
||||
Version: "3.4.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "verror@1.10.0",
|
||||
Name: "verror",
|
||||
Version: "1.10.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
}
|
||||
pnpmManyDeps = []types.Dependency{
|
||||
pnpmManyDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "@babel/runtime@7.18.3",
|
||||
DependsOn: []string{"regenerator-runtime@0.13.9"},
|
||||
@@ -610,48 +610,48 @@ var (
|
||||
// pnpm update
|
||||
// pnpm add https://github.com/debug-js/debug/tarball/4.3.4
|
||||
// pnpm add https://codeload.github.com/zkochan/is-negative/tar.gz/2fa0531ab04e300a24ef4fd7fb3a280eccb7ccc5
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: types.RelationshipDirect},\n")}' | sort -u
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: ftypes.RelationshipDirect},\n")}' | sort -u
|
||||
// manually update `Indirect` fields
|
||||
pnpmArchives = []types.Library{
|
||||
pnpmArchives = []ftypes.Package{
|
||||
{
|
||||
ID: "asynckit@0.4.0",
|
||||
Name: "asynckit",
|
||||
Version: "0.4.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "debug@4.3.4",
|
||||
Name: "debug",
|
||||
Version: "4.3.4",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "is-negative@2.0.1",
|
||||
Name: "is-negative",
|
||||
Version: "2.0.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "lodash@4.17.21",
|
||||
Name: "lodash",
|
||||
Version: "4.17.21",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "ms@2.1.2",
|
||||
Name: "ms",
|
||||
Version: "2.1.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "package1@1.0.0",
|
||||
Name: "package1",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
}
|
||||
|
||||
pnpmArchivesDeps = []types.Dependency{
|
||||
pnpmArchivesDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "debug@4.3.4",
|
||||
DependsOn: []string{"ms@2.1.2"},
|
||||
@@ -665,7 +665,7 @@ var (
|
||||
// docker run --name node --rm -it node@sha256:710a2c192ca426e03e4f3ec1869e5c29db855eb6969b74e6c50fd270ffccd3f1 sh
|
||||
// npm install -g pnpm@8.5.1
|
||||
// pnpm add promise@8.1.0 jquery@3.6.0
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: types.RelationshipIndirect},\n")}' | sort -u
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: ftypes.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmV6 = pnpmNormal
|
||||
pnpmV6Deps = pnpmNormalDeps
|
||||
|
||||
@@ -673,46 +673,46 @@ var (
|
||||
// npm install -g pnpm@8.5.1
|
||||
// pnpm add react@18.1.0 redux@4.2.0
|
||||
// pnpm add -D mocha@10.0.0
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: types.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmV6WithDev = []types.Library{
|
||||
// pnpm list --prod --depth 10 | grep -E -o "\S+\s+[0-9]+(\.[0-9]+)+$" | awk '{printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\", Relationship: ftypes.RelationshipIndirect},\n")}' | sort -u
|
||||
pnpmV6WithDev = []ftypes.Package{
|
||||
{
|
||||
ID: "@babel/runtime@7.22.3",
|
||||
Name: "@babel/runtime",
|
||||
Version: "7.22.3",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "js-tokens@4.0.0",
|
||||
Name: "js-tokens",
|
||||
Version: "4.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "loose-envify@1.4.0",
|
||||
Name: "loose-envify",
|
||||
Version: "1.4.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
{
|
||||
ID: "react@18.1.0",
|
||||
Name: "react",
|
||||
Version: "18.1.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "redux@4.2.0",
|
||||
Name: "redux",
|
||||
Version: "4.2.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "regenerator-runtime@0.13.11",
|
||||
Name: "regenerator-runtime",
|
||||
Version: "0.13.11",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
}
|
||||
pnpmV6WithDevDeps = []types.Dependency{
|
||||
pnpmV6WithDevDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "@babel/runtime@7.22.3",
|
||||
DependsOn: []string{"regenerator-runtime@0.13.11"},
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -31,7 +30,7 @@ type Library struct {
|
||||
Patterns []string
|
||||
Name string
|
||||
Version string
|
||||
Location types.Location
|
||||
Location ftypes.Location
|
||||
}
|
||||
type Dependency struct {
|
||||
Pattern string
|
||||
@@ -128,14 +127,14 @@ func ignoreProtocol(protocol string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func parseResults(patternIDs map[string]string, dependsOn map[string][]string) (deps []types.Dependency) {
|
||||
func parseResults(patternIDs map[string]string, dependsOn map[string][]string) (deps []ftypes.Dependency) {
|
||||
// find dependencies by patterns
|
||||
for libID, depPatterns := range dependsOn {
|
||||
for pkgID, depPatterns := range dependsOn {
|
||||
depIDs := lo.Map(depPatterns, func(pattern string, index int) string {
|
||||
return patternIDs[pattern]
|
||||
})
|
||||
deps = append(deps, types.Dependency{
|
||||
ID: libID,
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: depIDs,
|
||||
})
|
||||
}
|
||||
@@ -146,7 +145,7 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("yarn"),
|
||||
}
|
||||
@@ -236,7 +235,7 @@ func (p *Parser) parseBlock(block []byte, lineNum int) (lib Library, deps []stri
|
||||
return Library{}, nil, scanner.LineNum(lineNum), nil
|
||||
}
|
||||
|
||||
lib.Location = types.Location{
|
||||
lib.Location = ftypes.Location{
|
||||
StartLine: lineNum + emptyLines,
|
||||
EndLine: scanner.LineNum(lineNum),
|
||||
}
|
||||
@@ -270,9 +269,9 @@ func parseDependency(line string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
lineNumber := 1
|
||||
var libs []types.Library
|
||||
var pkgs []ftypes.Package
|
||||
|
||||
// patternIDs holds mapping between patterns and library IDs
|
||||
// e.g. ajv@^6.5.5 => ajv@6.10.0
|
||||
@@ -291,21 +290,21 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
continue
|
||||
}
|
||||
|
||||
libID := packageID(lib.Name, lib.Version)
|
||||
libs = append(libs, types.Library{
|
||||
ID: libID,
|
||||
pkgID := packageID(lib.Name, lib.Version)
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: lib.Name,
|
||||
Version: lib.Version,
|
||||
Locations: []types.Location{lib.Location},
|
||||
Locations: []ftypes.Location{lib.Location},
|
||||
})
|
||||
|
||||
for _, pattern := range lib.Patterns {
|
||||
// e.g.
|
||||
// combined-stream@^1.0.6 => combined-stream@1.0.8
|
||||
// combined-stream@~1.0.6 => combined-stream@1.0.8
|
||||
patternIDs[pattern] = libID
|
||||
patternIDs[pattern] = pkgID
|
||||
if len(deps) > 0 {
|
||||
dependsOn[libID] = deps
|
||||
dependsOn[pkgID] = deps
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,7 +316,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
// Replace dependency patterns with library IDs
|
||||
// e.g. ajv@^6.5.5 => ajv@6.10.0
|
||||
deps := parseResults(patternIDs, dependsOn)
|
||||
return libs, deps, nil
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
func packageID(name, version string) string {
|
||||
|
||||
@@ -3,13 +3,12 @@ package yarn
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParsePattern(t *testing.T) {
|
||||
@@ -255,8 +254,8 @@ func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string // Test input file
|
||||
want []types.Library
|
||||
wantDeps []types.Dependency
|
||||
want []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
@@ -305,10 +304,10 @@ func TestParse(t *testing.T) {
|
||||
got, deps, err := NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sortLibs(got)
|
||||
sortLibs(tt.want)
|
||||
|
||||
sortPkgs(got)
|
||||
sortPkgs(tt.want)
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
if tt.wantDeps != nil {
|
||||
sortDeps(deps)
|
||||
sortDeps(tt.wantDeps)
|
||||
@@ -318,31 +317,16 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func sortDeps(deps []types.Dependency) {
|
||||
sort.Slice(deps, func(i, j int) bool {
|
||||
return strings.Compare(deps[i].ID, deps[j].ID) < 0
|
||||
})
|
||||
|
||||
for i := range deps {
|
||||
sort.Strings(deps[i].DependsOn)
|
||||
func sortPkgs(pkgs ftypes.Packages) {
|
||||
sort.Sort(pkgs)
|
||||
for _, pkg := range pkgs {
|
||||
sort.Sort(pkg.Locations)
|
||||
}
|
||||
}
|
||||
|
||||
func sortLibs(libs []types.Library) {
|
||||
sort.Slice(libs, func(i, j int) bool {
|
||||
ret := strings.Compare(libs[i].Name, libs[j].Name)
|
||||
if ret == 0 {
|
||||
return libs[i].Version < libs[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
for _, lib := range libs {
|
||||
sortLocations(lib.Locations)
|
||||
func sortDeps(deps ftypes.Dependencies) {
|
||||
sort.Sort(deps)
|
||||
for _, dep := range deps {
|
||||
sort.Strings(dep.DependsOn)
|
||||
}
|
||||
}
|
||||
|
||||
func sortLocations(locs []types.Location) {
|
||||
sort.Slice(locs, func(i, j int) bool {
|
||||
return locs[i].StartLine < locs[j].StartLine
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
package yarn
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
yarnHappy = []types.Library{
|
||||
{ID: "@babel/helper-regex@7.4.4", Name: "@babel/helper-regex", Version: "7.4.4", Locations: []types.Location{{StartLine: 4, EndLine: 9}}},
|
||||
{ID: "ansi-regex@2.1.1", Name: "ansi-regex", Version: "2.1.1", Locations: []types.Location{{StartLine: 11, EndLine: 14}}},
|
||||
{ID: "ansi-regex@3.0.0", Name: "ansi-regex", Version: "3.0.0", Locations: []types.Location{{StartLine: 16, EndLine: 19}}},
|
||||
{ID: "asap@2.0.6", Name: "asap", Version: "2.0.6", Locations: []types.Location{{StartLine: 21, EndLine: 24}}},
|
||||
{ID: "inherits@2.0.3", Name: "inherits", Version: "2.0.3", Locations: []types.Location{{StartLine: 26, EndLine: 29}}},
|
||||
{ID: "is-fullwidth-code-point@2.0.0", Name: "is-fullwidth-code-point", Version: "2.0.0", Locations: []types.Location{{StartLine: 31, EndLine: 34}}},
|
||||
{ID: "jquery@3.4.1", Name: "jquery", Version: "3.4.1", Locations: []types.Location{{StartLine: 41, EndLine: 44}}},
|
||||
{ID: "js-tokens@4.0.0", Name: "js-tokens", Version: "4.0.0", Locations: []types.Location{{StartLine: 36, EndLine: 39}}},
|
||||
{ID: "lodash@4.17.11", Name: "lodash", Version: "4.17.11", Locations: []types.Location{{StartLine: 46, EndLine: 49}}},
|
||||
{ID: "promise@8.0.3", Name: "promise", Version: "8.0.3", Locations: []types.Location{{StartLine: 51, EndLine: 56}}},
|
||||
{ID: "safe-buffer@5.1.2", Name: "safe-buffer", Version: "5.1.2", Locations: []types.Location{{StartLine: 58, EndLine: 61}}},
|
||||
{ID: "safer-buffer@2.1.2", Name: "safer-buffer", Version: "2.1.2", Locations: []types.Location{{StartLine: 63, EndLine: 66}}},
|
||||
{ID: "statuses@1.5.0", Name: "statuses", Version: "1.5.0", Locations: []types.Location{{StartLine: 68, EndLine: 71}}},
|
||||
{ID: "string-width@2.1.1", Name: "string-width", Version: "2.1.1", Locations: []types.Location{{StartLine: 73, EndLine: 79}}},
|
||||
{ID: "strip-ansi@3.0.1", Name: "strip-ansi", Version: "3.0.1", Locations: []types.Location{{StartLine: 82, EndLine: 87}}},
|
||||
{ID: "strip-ansi@4.0.0", Name: "strip-ansi", Version: "4.0.0", Locations: []types.Location{{StartLine: 89, EndLine: 94}}},
|
||||
{ID: "whatwg-fetch@3.0.0", Name: "whatwg-fetch", Version: "3.0.0", Locations: []types.Location{{StartLine: 96, EndLine: 99}}},
|
||||
{ID: "wide-align@1.1.3", Name: "wide-align", Version: "1.1.3", Locations: []types.Location{{StartLine: 101, EndLine: 106}}},
|
||||
yarnHappy = []ftypes.Package{
|
||||
{ID: "@babel/helper-regex@7.4.4", Name: "@babel/helper-regex", Version: "7.4.4", Locations: []ftypes.Location{{StartLine: 4, EndLine: 9}}},
|
||||
{ID: "ansi-regex@2.1.1", Name: "ansi-regex", Version: "2.1.1", Locations: []ftypes.Location{{StartLine: 11, EndLine: 14}}},
|
||||
{ID: "ansi-regex@3.0.0", Name: "ansi-regex", Version: "3.0.0", Locations: []ftypes.Location{{StartLine: 16, EndLine: 19}}},
|
||||
{ID: "asap@2.0.6", Name: "asap", Version: "2.0.6", Locations: []ftypes.Location{{StartLine: 21, EndLine: 24}}},
|
||||
{ID: "inherits@2.0.3", Name: "inherits", Version: "2.0.3", Locations: []ftypes.Location{{StartLine: 26, EndLine: 29}}},
|
||||
{ID: "is-fullwidth-code-point@2.0.0", Name: "is-fullwidth-code-point", Version: "2.0.0", Locations: []ftypes.Location{{StartLine: 31, EndLine: 34}}},
|
||||
{ID: "jquery@3.4.1", Name: "jquery", Version: "3.4.1", Locations: []ftypes.Location{{StartLine: 41, EndLine: 44}}},
|
||||
{ID: "js-tokens@4.0.0", Name: "js-tokens", Version: "4.0.0", Locations: []ftypes.Location{{StartLine: 36, EndLine: 39}}},
|
||||
{ID: "lodash@4.17.11", Name: "lodash", Version: "4.17.11", Locations: []ftypes.Location{{StartLine: 46, EndLine: 49}}},
|
||||
{ID: "promise@8.0.3", Name: "promise", Version: "8.0.3", Locations: []ftypes.Location{{StartLine: 51, EndLine: 56}}},
|
||||
{ID: "safe-buffer@5.1.2", Name: "safe-buffer", Version: "5.1.2", Locations: []ftypes.Location{{StartLine: 58, EndLine: 61}}},
|
||||
{ID: "safer-buffer@2.1.2", Name: "safer-buffer", Version: "2.1.2", Locations: []ftypes.Location{{StartLine: 63, EndLine: 66}}},
|
||||
{ID: "statuses@1.5.0", Name: "statuses", Version: "1.5.0", Locations: []ftypes.Location{{StartLine: 68, EndLine: 71}}},
|
||||
{ID: "string-width@2.1.1", Name: "string-width", Version: "2.1.1", Locations: []ftypes.Location{{StartLine: 73, EndLine: 79}}},
|
||||
{ID: "strip-ansi@3.0.1", Name: "strip-ansi", Version: "3.0.1", Locations: []ftypes.Location{{StartLine: 82, EndLine: 87}}},
|
||||
{ID: "strip-ansi@4.0.0", Name: "strip-ansi", Version: "4.0.0", Locations: []ftypes.Location{{StartLine: 89, EndLine: 94}}},
|
||||
{ID: "whatwg-fetch@3.0.0", Name: "whatwg-fetch", Version: "3.0.0", Locations: []ftypes.Location{{StartLine: 96, EndLine: 99}}},
|
||||
{ID: "wide-align@1.1.3", Name: "wide-align", Version: "1.1.3", Locations: []ftypes.Location{{StartLine: 101, EndLine: 106}}},
|
||||
}
|
||||
|
||||
yarnHappyDeps = []types.Dependency{
|
||||
yarnHappyDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "@babel/helper-regex@7.4.4",
|
||||
DependsOn: []string{
|
||||
@@ -64,26 +64,26 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
yarnV2Happy = []types.Library{
|
||||
{ID: "@types/color-name@1.1.1", Name: "@types/color-name", Version: "1.1.1", Locations: []types.Location{{StartLine: 8, EndLine: 13}}},
|
||||
{ID: "abbrev@1.1.1", Name: "abbrev", Version: "1.1.1", Locations: []types.Location{{StartLine: 15, EndLine: 20}}},
|
||||
{ID: "ansi-styles@3.2.1", Name: "ansi-styles", Version: "3.2.1", Locations: []types.Location{{StartLine: 22, EndLine: 29}}},
|
||||
{ID: "ansi-styles@4.2.1", Name: "ansi-styles", Version: "4.2.1", Locations: []types.Location{{StartLine: 31, EndLine: 39}}},
|
||||
{ID: "assert-plus@1.0.0", Name: "assert-plus", Version: "1.0.0", Locations: []types.Location{{StartLine: 41, EndLine: 46}}},
|
||||
{ID: "async@3.2.0", Name: "async", Version: "3.2.0", Locations: []types.Location{{StartLine: 48, EndLine: 53}}},
|
||||
{ID: "color-convert@1.9.3", Name: "color-convert", Version: "1.9.3", Locations: []types.Location{{StartLine: 63, EndLine: 70}}},
|
||||
{ID: "color-convert@2.0.1", Name: "color-convert", Version: "2.0.1", Locations: []types.Location{{StartLine: 72, EndLine: 79}}},
|
||||
{ID: "color-name@1.1.3", Name: "color-name", Version: "1.1.3", Locations: []types.Location{{StartLine: 81, EndLine: 86}}},
|
||||
{ID: "color-name@1.1.4", Name: "color-name", Version: "1.1.4", Locations: []types.Location{{StartLine: 88, EndLine: 93}}},
|
||||
{ID: "ipaddr.js@1.9.1", Name: "ipaddr.js", Version: "1.9.1", Locations: []types.Location{{StartLine: 104, EndLine: 109}}},
|
||||
{ID: "js-tokens@4.0.0", Name: "js-tokens", Version: "4.0.0", Locations: []types.Location{{StartLine: 111, EndLine: 116}}},
|
||||
{ID: "loose-envify@1.4.0", Name: "loose-envify", Version: "1.4.0", Locations: []types.Location{{StartLine: 118, EndLine: 127}}},
|
||||
{ID: "node-gyp@7.1.0", Name: "node-gyp", Version: "7.1.0", Locations: []types.Location{{StartLine: 129, EndLine: 136}}},
|
||||
{ID: "once@1.4.0", Name: "once", Version: "1.4.0", Locations: []types.Location{{StartLine: 138, EndLine: 145}}},
|
||||
{ID: "wrappy@1.0.2", Name: "wrappy", Version: "1.0.2", Locations: []types.Location{{StartLine: 147, EndLine: 152}}},
|
||||
yarnV2Happy = []ftypes.Package{
|
||||
{ID: "@types/color-name@1.1.1", Name: "@types/color-name", Version: "1.1.1", Locations: []ftypes.Location{{StartLine: 8, EndLine: 13}}},
|
||||
{ID: "abbrev@1.1.1", Name: "abbrev", Version: "1.1.1", Locations: []ftypes.Location{{StartLine: 15, EndLine: 20}}},
|
||||
{ID: "ansi-styles@3.2.1", Name: "ansi-styles", Version: "3.2.1", Locations: []ftypes.Location{{StartLine: 22, EndLine: 29}}},
|
||||
{ID: "ansi-styles@4.2.1", Name: "ansi-styles", Version: "4.2.1", Locations: []ftypes.Location{{StartLine: 31, EndLine: 39}}},
|
||||
{ID: "assert-plus@1.0.0", Name: "assert-plus", Version: "1.0.0", Locations: []ftypes.Location{{StartLine: 41, EndLine: 46}}},
|
||||
{ID: "async@3.2.0", Name: "async", Version: "3.2.0", Locations: []ftypes.Location{{StartLine: 48, EndLine: 53}}},
|
||||
{ID: "color-convert@1.9.3", Name: "color-convert", Version: "1.9.3", Locations: []ftypes.Location{{StartLine: 63, EndLine: 70}}},
|
||||
{ID: "color-convert@2.0.1", Name: "color-convert", Version: "2.0.1", Locations: []ftypes.Location{{StartLine: 72, EndLine: 79}}},
|
||||
{ID: "color-name@1.1.3", Name: "color-name", Version: "1.1.3", Locations: []ftypes.Location{{StartLine: 81, EndLine: 86}}},
|
||||
{ID: "color-name@1.1.4", Name: "color-name", Version: "1.1.4", Locations: []ftypes.Location{{StartLine: 88, EndLine: 93}}},
|
||||
{ID: "ipaddr.js@1.9.1", Name: "ipaddr.js", Version: "1.9.1", Locations: []ftypes.Location{{StartLine: 104, EndLine: 109}}},
|
||||
{ID: "js-tokens@4.0.0", Name: "js-tokens", Version: "4.0.0", Locations: []ftypes.Location{{StartLine: 111, EndLine: 116}}},
|
||||
{ID: "loose-envify@1.4.0", Name: "loose-envify", Version: "1.4.0", Locations: []ftypes.Location{{StartLine: 118, EndLine: 127}}},
|
||||
{ID: "node-gyp@7.1.0", Name: "node-gyp", Version: "7.1.0", Locations: []ftypes.Location{{StartLine: 129, EndLine: 136}}},
|
||||
{ID: "once@1.4.0", Name: "once", Version: "1.4.0", Locations: []ftypes.Location{{StartLine: 138, EndLine: 145}}},
|
||||
{ID: "wrappy@1.0.2", Name: "wrappy", Version: "1.0.2", Locations: []ftypes.Location{{StartLine: 147, EndLine: 152}}},
|
||||
}
|
||||
|
||||
yarnV2HappyDeps = []types.Dependency{
|
||||
yarnV2HappyDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "ansi-styles@3.2.1",
|
||||
DependsOn: []string{
|
||||
@@ -123,13 +123,13 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
yarnWithLocal = []types.Library{
|
||||
{ID: "asap@2.0.6", Name: "asap", Version: "2.0.6", Locations: []types.Location{{StartLine: 5, EndLine: 8}}},
|
||||
{ID: "jquery@3.4.1", Name: "jquery", Version: "3.4.1", Locations: []types.Location{{StartLine: 10, EndLine: 13}}},
|
||||
{ID: "promise@8.0.3", Name: "promise", Version: "8.0.3", Locations: []types.Location{{StartLine: 15, EndLine: 20}}},
|
||||
yarnWithLocal = []ftypes.Package{
|
||||
{ID: "asap@2.0.6", Name: "asap", Version: "2.0.6", Locations: []ftypes.Location{{StartLine: 5, EndLine: 8}}},
|
||||
{ID: "jquery@3.4.1", Name: "jquery", Version: "3.4.1", Locations: []ftypes.Location{{StartLine: 10, EndLine: 13}}},
|
||||
{ID: "promise@8.0.3", Name: "promise", Version: "8.0.3", Locations: []ftypes.Location{{StartLine: 15, EndLine: 20}}},
|
||||
}
|
||||
|
||||
yarnWithLocalDeps = []types.Dependency{
|
||||
yarnWithLocalDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "promise@8.0.3",
|
||||
DependsOn: []string{
|
||||
@@ -138,20 +138,20 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
yarnWithNpm = []types.Library{
|
||||
{ID: "jquery@3.6.0", Name: "jquery", Version: "3.6.0", Locations: []types.Location{{StartLine: 1, EndLine: 4}}},
|
||||
yarnWithNpm = []ftypes.Package{
|
||||
{ID: "jquery@3.6.0", Name: "jquery", Version: "3.6.0", Locations: []ftypes.Location{{StartLine: 1, EndLine: 4}}},
|
||||
}
|
||||
|
||||
yarnBadProtocol = []types.Library{
|
||||
{ID: "jquery@3.4.1", Name: "jquery", Version: "3.4.1", Locations: []types.Location{{StartLine: 4, EndLine: 7}}},
|
||||
yarnBadProtocol = []ftypes.Package{
|
||||
{ID: "jquery@3.4.1", Name: "jquery", Version: "3.4.1", Locations: []ftypes.Location{{StartLine: 4, EndLine: 7}}},
|
||||
}
|
||||
|
||||
yarnV2DepsWithProtocol = []types.Library{
|
||||
{ID: "debug@4.3.4", Name: "debug", Version: "4.3.4", Locations: []types.Location{{StartLine: 16, EndLine: 26}}},
|
||||
{ID: "ms@2.1.2", Name: "ms", Version: "2.1.2", Locations: []types.Location{{StartLine: 28, EndLine: 33}}},
|
||||
yarnV2DepsWithProtocol = []ftypes.Package{
|
||||
{ID: "debug@4.3.4", Name: "debug", Version: "4.3.4", Locations: []ftypes.Location{{StartLine: 16, EndLine: 26}}},
|
||||
{ID: "ms@2.1.2", Name: "ms", Version: "2.1.2", Locations: []ftypes.Location{{StartLine: 28, EndLine: 33}}},
|
||||
}
|
||||
|
||||
yarnV2DepsWithProtocolDeps = []types.Dependency{
|
||||
yarnV2DepsWithProtocolDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "debug@4.3.4",
|
||||
DependsOn: []string{"ms@2.1.2"},
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
@@ -25,29 +25,27 @@ type config struct {
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var cfgData config
|
||||
if err := xml.NewDecoder(r).Decode(&cfgData); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to decode .config file: %w", err)
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
var pkgs []ftypes.Package
|
||||
for _, pkg := range cfgData.Packages {
|
||||
if pkg.ID == "" || pkg.DevDependency {
|
||||
continue
|
||||
}
|
||||
|
||||
lib := types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
Name: pkg.ID,
|
||||
Version: pkg.Version,
|
||||
}
|
||||
|
||||
libs = append(libs, lib)
|
||||
})
|
||||
}
|
||||
|
||||
return utils.UniqueLibraries(libs), nil, nil
|
||||
return utils.UniquePackages(pkgs), nil, nil
|
||||
}
|
||||
|
||||
@@ -8,20 +8,20 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/nuget/config"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string // Test input file
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "Config",
|
||||
inputFile: "testdata/packages.config",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{Name: "Newtonsoft.Json", Version: "6.0.4"},
|
||||
{Name: "Microsoft.AspNet.WebApi", Version: "5.2.2"},
|
||||
},
|
||||
@@ -29,7 +29,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "with development dependency",
|
||||
inputFile: "testdata/dev_dependency.config",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{Name: "Newtonsoft.Json", Version: "8.0.3"},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -31,11 +30,11 @@ type Dependency struct {
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockFile LockFile
|
||||
input, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
@@ -45,7 +44,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("failed to decode packages.lock.json: %w", err)
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
var pkgs []ftypes.Package
|
||||
depsMap := make(map[string][]string)
|
||||
for _, targetContent := range lockFile.Targets {
|
||||
for packageName, packageContent := range targetContent {
|
||||
@@ -56,19 +55,19 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
|
||||
depId := packageID(packageName, packageContent.Resolved)
|
||||
|
||||
lib := types.Library{
|
||||
pkg := ftypes.Package{
|
||||
ID: depId,
|
||||
Name: packageName,
|
||||
Version: packageContent.Resolved,
|
||||
Relationship: lo.Ternary(packageContent.Type == "Direct", types.RelationshipDirect, types.RelationshipIndirect),
|
||||
Locations: []types.Location{
|
||||
Relationship: lo.Ternary(packageContent.Type == "Direct", ftypes.RelationshipDirect, ftypes.RelationshipIndirect),
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: packageContent.StartLine,
|
||||
EndLine: packageContent.EndLine,
|
||||
},
|
||||
},
|
||||
}
|
||||
libs = append(libs, lib)
|
||||
pkgs = append(pkgs, pkg)
|
||||
|
||||
var dependsOn []string
|
||||
|
||||
@@ -86,16 +85,16 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
}
|
||||
}
|
||||
|
||||
var deps []types.Dependency
|
||||
var deps []ftypes.Dependency
|
||||
for depId, dependsOn := range depsMap {
|
||||
dep := types.Dependency{
|
||||
dep := ftypes.Dependency{
|
||||
ID: depId,
|
||||
DependsOn: dependsOn,
|
||||
}
|
||||
deps = append(deps, dep)
|
||||
}
|
||||
|
||||
return utils.UniqueLibraries(libs), deps, nil
|
||||
return utils.UniquePackages(pkgs), deps, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSONWithMetadata needed to detect start and end lines of deps
|
||||
|
||||
@@ -10,14 +10,14 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
vectors := []struct {
|
||||
file string // Test input file
|
||||
want []types.Library
|
||||
wantDeps []types.Dependency
|
||||
want []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
file: "testdata/packages_lock_simple.json",
|
||||
@@ -49,21 +49,8 @@ func TestParse(t *testing.T) {
|
||||
got, deps, err := NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
ret := strings.Compare(got[i].Name, got[j].Name)
|
||||
if ret == 0 {
|
||||
return got[i].Version < got[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
|
||||
sort.Slice(v.want, func(i, j int) bool {
|
||||
ret := strings.Compare(v.want[i].Name, v.want[j].Name)
|
||||
if ret == 0 {
|
||||
return v.want[i].Version < v.want[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
sort.Sort(ftypes.Packages(got))
|
||||
sort.Sort(ftypes.Packages(v.want))
|
||||
|
||||
assert.Equal(t, v.want, got)
|
||||
|
||||
@@ -76,7 +63,7 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func sortDeps(deps []types.Dependency) {
|
||||
func sortDeps(deps []ftypes.Dependency) {
|
||||
sort.Slice(deps, func(i, j int) bool {
|
||||
return strings.Compare(deps[i].ID, deps[j].ID) < 0
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package lock
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
// docker run --rm -i -t mcr.microsoft.com/dotnet/sdk:latest
|
||||
@@ -11,13 +11,13 @@ var (
|
||||
// dotnet add package NuGet.Frameworks
|
||||
// dotnet restore --use-lock-file
|
||||
// cat packages.lock.json | jq -rc '.dependencies[] | keys[] as $k | "{\"\($k)\", \"\(.[$k] | .resolved)\", \"\"},"'
|
||||
nuGetSimple = []types.Library{
|
||||
nuGetSimple = []ftypes.Package{
|
||||
{
|
||||
ID: "Newtonsoft.Json@12.0.3",
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "12.0.3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 10,
|
||||
@@ -28,8 +28,8 @@ var (
|
||||
ID: "NuGet.Frameworks@5.7.0",
|
||||
Name: "NuGet.Frameworks",
|
||||
Version: "5.7.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 11,
|
||||
EndLine: 16,
|
||||
@@ -37,7 +37,7 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
nuGetSimpleDeps []types.Dependency
|
||||
nuGetSimpleDeps []ftypes.Dependency
|
||||
|
||||
// docker run --rm -i -t mcr.microsoft.com/dotnet/sdk:latest
|
||||
// apt -y update && apt -y install jq
|
||||
@@ -47,13 +47,13 @@ var (
|
||||
// dotnet add package NuGet.Frameworks
|
||||
// dotnet restore --use-lock-file
|
||||
// cat packages.lock.json | jq -rc '.dependencies[] | keys[] as $k | "{\"\($k)\", \"\(.[$k] | .resolved)\", \"\"},"'
|
||||
nuGetSubDependencies = []types.Library{
|
||||
nuGetSubDependencies = []ftypes.Package{
|
||||
{
|
||||
ID: "Microsoft.Extensions.ApiDescription.Server@3.0.0",
|
||||
Name: "Microsoft.Extensions.ApiDescription.Server",
|
||||
Version: "3.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 29,
|
||||
EndLine: 33,
|
||||
@@ -64,8 +64,8 @@ var (
|
||||
ID: "Microsoft.OpenApi@1.1.4",
|
||||
Name: "Microsoft.OpenApi",
|
||||
Version: "1.1.4",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 34,
|
||||
EndLine: 38,
|
||||
@@ -76,8 +76,8 @@ var (
|
||||
ID: "Newtonsoft.Json@12.0.3",
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "12.0.3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 10,
|
||||
@@ -88,8 +88,8 @@ var (
|
||||
ID: "NuGet.Frameworks@5.7.0",
|
||||
Name: "NuGet.Frameworks",
|
||||
Version: "5.7.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 11,
|
||||
EndLine: 16,
|
||||
@@ -100,8 +100,8 @@ var (
|
||||
ID: "Swashbuckle.AspNetCore@5.5.1",
|
||||
Name: "Swashbuckle.AspNetCore",
|
||||
Version: "5.5.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 17,
|
||||
EndLine: 28,
|
||||
@@ -112,8 +112,8 @@ var (
|
||||
ID: "Swashbuckle.AspNetCore.Swagger@5.5.1",
|
||||
Name: "Swashbuckle.AspNetCore.Swagger",
|
||||
Version: "5.5.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 39,
|
||||
EndLine: 46,
|
||||
@@ -124,8 +124,8 @@ var (
|
||||
ID: "Swashbuckle.AspNetCore.SwaggerGen@5.5.1",
|
||||
Name: "Swashbuckle.AspNetCore.SwaggerGen",
|
||||
Version: "5.5.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 47,
|
||||
EndLine: 54,
|
||||
@@ -136,8 +136,8 @@ var (
|
||||
ID: "Swashbuckle.AspNetCore.SwaggerUI@5.5.1",
|
||||
Name: "Swashbuckle.AspNetCore.SwaggerUI",
|
||||
Version: "5.5.1",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 55,
|
||||
EndLine: 59,
|
||||
@@ -145,7 +145,7 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
nuGetSubDependenciesDeps = []types.Dependency{
|
||||
nuGetSubDependenciesDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "Swashbuckle.AspNetCore.Swagger@5.5.1",
|
||||
DependsOn: []string{"Microsoft.OpenApi@1.1.4"},
|
||||
@@ -173,13 +173,13 @@ var (
|
||||
// dotnet add package AWSSDK.Core
|
||||
// dotnet restore --use-lock-file
|
||||
// cat packages.lock.json | jq -rc '.dependencies[] | keys[] as $k | "{\"\($k)\", \"\(.[$k] | .resolved)\", \"\"},"'
|
||||
nuGetLegacy = []types.Library{
|
||||
nuGetLegacy = []ftypes.Package{
|
||||
{
|
||||
ID: "AWSSDK.Core@3.5.1.30",
|
||||
Name: "AWSSDK.Core",
|
||||
Version: "3.5.1.30",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 10,
|
||||
@@ -190,8 +190,8 @@ var (
|
||||
ID: "Newtonsoft.Json@12.0.3",
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "12.0.3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 11,
|
||||
EndLine: 16,
|
||||
@@ -199,7 +199,7 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
nuGetLegacyDeps []types.Dependency
|
||||
nuGetLegacyDeps []ftypes.Dependency
|
||||
|
||||
// docker run --rm -i -t mcr.microsoft.com/dotnet/sdk:latest
|
||||
// apt -y update && apt -y install jq
|
||||
@@ -210,13 +210,13 @@ var (
|
||||
// dotnet restore --use-lock-file
|
||||
// dotnet add package AWSSDK.Core
|
||||
// cat packages.lock.json | jq -rc '.dependencies[] | keys[] as $k | "{\"\($k)\", \"\(.[$k] | .resolved)\", \"\"},"' | sort -u
|
||||
nuGetMultiTarget = []types.Library{
|
||||
nuGetMultiTarget = []ftypes.Package{
|
||||
{
|
||||
ID: "AWSSDK.Core@3.5.1.30",
|
||||
Name: "AWSSDK.Core",
|
||||
Version: "3.5.1.30",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 10,
|
||||
@@ -243,8 +243,8 @@ var (
|
||||
ID: "Microsoft.Bcl.AsyncInterfaces@1.1.0",
|
||||
Name: "Microsoft.Bcl.AsyncInterfaces",
|
||||
Version: "1.1.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 460,
|
||||
EndLine: 467,
|
||||
@@ -255,8 +255,8 @@ var (
|
||||
ID: "Microsoft.CSharp@4.3.0",
|
||||
Name: "Microsoft.CSharp",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 138,
|
||||
EndLine: 147,
|
||||
@@ -267,8 +267,8 @@ var (
|
||||
ID: "Microsoft.NETCore.Platforms@1.1.0",
|
||||
Name: "Microsoft.NETCore.Platforms",
|
||||
Version: "1.1.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 148,
|
||||
EndLine: 152,
|
||||
@@ -283,8 +283,8 @@ var (
|
||||
ID: "Microsoft.NETCore.Targets@1.1.0",
|
||||
Name: "Microsoft.NETCore.Targets",
|
||||
Version: "1.1.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 153,
|
||||
EndLine: 157,
|
||||
@@ -295,8 +295,8 @@ var (
|
||||
ID: "Microsoft.NETFramework.ReferenceAssemblies@1.0.0",
|
||||
Name: "Microsoft.NETFramework.ReferenceAssemblies",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 11,
|
||||
EndLine: 19,
|
||||
@@ -315,8 +315,8 @@ var (
|
||||
ID: "Microsoft.NETFramework.ReferenceAssemblies.net20@1.0.0",
|
||||
Name: "Microsoft.NETFramework.ReferenceAssemblies.net20",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 26,
|
||||
EndLine: 30,
|
||||
@@ -331,8 +331,8 @@ var (
|
||||
ID: "Microsoft.NETFramework.ReferenceAssemblies.net40@1.0.0",
|
||||
Name: "Microsoft.NETFramework.ReferenceAssemblies.net40",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 82,
|
||||
EndLine: 86,
|
||||
@@ -343,8 +343,8 @@ var (
|
||||
ID: "NETStandard.Library@1.6.1",
|
||||
Name: "NETStandard.Library",
|
||||
Version: "1.6.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 95,
|
||||
EndLine: 125,
|
||||
@@ -355,8 +355,8 @@ var (
|
||||
ID: "NETStandard.Library@2.0.3",
|
||||
Name: "NETStandard.Library",
|
||||
Version: "2.0.3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 445,
|
||||
EndLine: 453,
|
||||
@@ -367,8 +367,8 @@ var (
|
||||
ID: "Newtonsoft.Json@12.0.3",
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "12.0.3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 20,
|
||||
EndLine: 25,
|
||||
@@ -395,8 +395,8 @@ var (
|
||||
ID: "System.Collections@4.3.0",
|
||||
Name: "System.Collections",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 158,
|
||||
EndLine: 167,
|
||||
@@ -407,8 +407,8 @@ var (
|
||||
ID: "System.ComponentModel@4.3.0",
|
||||
Name: "System.ComponentModel",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 168,
|
||||
EndLine: 175,
|
||||
@@ -418,9 +418,9 @@ var (
|
||||
{
|
||||
ID: "System.ComponentModel.Primitives@4.3.0",
|
||||
Name: "System.ComponentModel.Primitives",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Version: "4.3.0",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 176,
|
||||
EndLine: 185,
|
||||
@@ -430,9 +430,9 @@ var (
|
||||
{
|
||||
ID: "System.ComponentModel.TypeConverter@4.3.0",
|
||||
Name: "System.ComponentModel.TypeConverter",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Version: "4.3.0",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 186,
|
||||
EndLine: 203,
|
||||
@@ -443,8 +443,8 @@ var (
|
||||
ID: "System.Diagnostics.Debug@4.3.0",
|
||||
Name: "System.Diagnostics.Debug",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 204,
|
||||
EndLine: 213,
|
||||
@@ -455,8 +455,8 @@ var (
|
||||
ID: "System.Diagnostics.Tools@4.3.0",
|
||||
Name: "System.Diagnostics.Tools",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 214,
|
||||
EndLine: 223,
|
||||
@@ -467,8 +467,8 @@ var (
|
||||
ID: "System.Dynamic.Runtime@4.3.0",
|
||||
Name: "System.Dynamic.Runtime",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 224,
|
||||
EndLine: 234,
|
||||
@@ -479,8 +479,8 @@ var (
|
||||
ID: "System.Globalization@4.3.0",
|
||||
Name: "System.Globalization",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 235,
|
||||
EndLine: 244,
|
||||
@@ -491,8 +491,8 @@ var (
|
||||
ID: "System.IO@4.3.0",
|
||||
Name: "System.IO",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 245,
|
||||
EndLine: 256,
|
||||
@@ -503,8 +503,8 @@ var (
|
||||
ID: "System.Linq@4.3.0",
|
||||
Name: "System.Linq",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 257,
|
||||
EndLine: 265,
|
||||
@@ -515,8 +515,8 @@ var (
|
||||
ID: "System.Linq.Expressions@4.3.0",
|
||||
Name: "System.Linq.Expressions",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 266,
|
||||
EndLine: 274,
|
||||
@@ -527,8 +527,8 @@ var (
|
||||
ID: "System.Net.Primitives@4.3.0",
|
||||
Name: "System.Net.Primitives",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 275,
|
||||
EndLine: 284,
|
||||
@@ -539,8 +539,8 @@ var (
|
||||
ID: "System.ObjectModel@4.3.0",
|
||||
Name: "System.ObjectModel",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 285,
|
||||
EndLine: 292,
|
||||
@@ -551,8 +551,8 @@ var (
|
||||
ID: "System.Reflection@4.3.0",
|
||||
Name: "System.Reflection",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 293,
|
||||
EndLine: 304,
|
||||
@@ -562,9 +562,9 @@ var (
|
||||
{
|
||||
ID: "System.Reflection.Extensions@4.3.0",
|
||||
Name: "System.Reflection.Extensions",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Version: "4.3.0",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 305,
|
||||
EndLine: 315,
|
||||
@@ -574,9 +574,9 @@ var (
|
||||
{
|
||||
ID: "System.Reflection.Primitives@4.3.0",
|
||||
Name: "System.Reflection.Primitives",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Version: "4.3.0",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 316,
|
||||
EndLine: 325,
|
||||
@@ -586,9 +586,9 @@ var (
|
||||
{
|
||||
ID: "System.Resources.ResourceManager@4.3.0",
|
||||
Name: "System.Resources.ResourceManager",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Version: "4.3.0",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 326,
|
||||
EndLine: 337,
|
||||
@@ -599,8 +599,8 @@ var (
|
||||
ID: "System.Runtime@4.3.0",
|
||||
Name: "System.Runtime",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 338,
|
||||
EndLine: 346,
|
||||
@@ -611,8 +611,8 @@ var (
|
||||
ID: "System.Runtime.CompilerServices.Unsafe@4.5.2",
|
||||
Name: "System.Runtime.CompilerServices.Unsafe",
|
||||
Version: "4.5.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 473,
|
||||
EndLine: 477,
|
||||
@@ -623,8 +623,8 @@ var (
|
||||
ID: "System.Runtime.Extensions@4.3.0",
|
||||
Name: "System.Runtime.Extensions",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 347,
|
||||
EndLine: 356,
|
||||
@@ -635,8 +635,8 @@ var (
|
||||
ID: "System.Runtime.Serialization.Primitives@4.3.0",
|
||||
Name: "System.Runtime.Serialization.Primitives",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 357,
|
||||
EndLine: 364,
|
||||
@@ -647,8 +647,8 @@ var (
|
||||
ID: "System.Text.Encoding@4.3.0",
|
||||
Name: "System.Text.Encoding",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 365,
|
||||
EndLine: 374,
|
||||
@@ -659,8 +659,8 @@ var (
|
||||
ID: "System.Text.Encoding.Extensions@4.3.0",
|
||||
Name: "System.Text.Encoding.Extensions",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 375,
|
||||
EndLine: 385,
|
||||
@@ -671,8 +671,8 @@ var (
|
||||
ID: "System.Text.RegularExpressions@4.3.0",
|
||||
Name: "System.Text.RegularExpressions",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 386,
|
||||
EndLine: 393,
|
||||
@@ -683,8 +683,8 @@ var (
|
||||
ID: "System.Threading@4.3.0",
|
||||
Name: "System.Threading",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 394,
|
||||
EndLine: 402,
|
||||
@@ -695,8 +695,8 @@ var (
|
||||
ID: "System.Threading.Tasks@4.3.0",
|
||||
Name: "System.Threading.Tasks",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 403,
|
||||
EndLine: 412,
|
||||
@@ -707,8 +707,8 @@ var (
|
||||
ID: "System.Threading.Tasks.Extensions@4.5.2",
|
||||
Name: "System.Threading.Tasks.Extensions",
|
||||
Version: "4.5.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 478,
|
||||
EndLine: 485,
|
||||
@@ -719,8 +719,8 @@ var (
|
||||
ID: "System.Xml.ReaderWriter@4.3.0",
|
||||
Name: "System.Xml.ReaderWriter",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 413,
|
||||
EndLine: 423,
|
||||
@@ -731,8 +731,8 @@ var (
|
||||
ID: "System.Xml.XDocument@4.3.0",
|
||||
Name: "System.Xml.XDocument",
|
||||
Version: "4.3.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 424,
|
||||
EndLine: 433,
|
||||
@@ -740,7 +740,7 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
nuGetMultiTargetDeps = []types.Dependency{
|
||||
nuGetMultiTargetDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "AWSSDK.Core@3.5.1.30",
|
||||
DependsOn: []string{"Microsoft.Bcl.AsyncInterfaces@1.1.0"},
|
||||
|
||||
@@ -8,12 +8,11 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type pkg struct {
|
||||
type Pkg struct {
|
||||
Version string `xml:"Version,attr"`
|
||||
UpdatePackageName string `xml:"Update,attr"`
|
||||
IncludePackageName string `xml:"Include,attr"`
|
||||
@@ -21,8 +20,8 @@ type pkg struct {
|
||||
|
||||
// https://github.com/dotnet/roslyn-tools/blob/b4c5220f5dfc4278847b6d38eff91cc1188f8066/src/RoslynInsertionTool/RoslynInsertionTool/CoreXT.cs#L150
|
||||
type itemGroup struct {
|
||||
PackageReferenceEntry []pkg `xml:"PackageReference"`
|
||||
PackageVersionEntry []pkg `xml:"PackageVersion"`
|
||||
PackageReferenceEntry []Pkg `xml:"PackageReference"`
|
||||
PackageVersionEntry []Pkg `xml:"PackageVersion"`
|
||||
}
|
||||
|
||||
type project struct {
|
||||
@@ -32,11 +31,11 @@ type project struct {
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p pkg) library() types.Library {
|
||||
func (p Pkg) Package() ftypes.Package {
|
||||
// Update attribute is considered legacy, so preferring Include
|
||||
name := p.UpdatePackageName
|
||||
if p.IncludePackageName != "" {
|
||||
@@ -45,20 +44,20 @@ func (p pkg) library() types.Library {
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
version := strings.TrimSpace(p.Version)
|
||||
return types.Library{
|
||||
return ftypes.Package{
|
||||
ID: dependency.ID(ftypes.NuGet, name, version),
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func shouldSkipLib(lib types.Library) bool {
|
||||
if lib.Name == "" || lib.Version == "" {
|
||||
func shouldSkipPkg(pkg ftypes.Package) bool {
|
||||
if pkg.Name == "" || pkg.Version == "" {
|
||||
return true
|
||||
}
|
||||
// *packages.props files don't contain variable resolution information.
|
||||
// So we need to skip them.
|
||||
if isVariable(lib.Name) || isVariable(lib.Version) {
|
||||
if isVariable(pkg.Name) || isVariable(pkg.Version) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -68,20 +67,20 @@ func isVariable(s string) bool {
|
||||
return strings.HasPrefix(s, "$(") && strings.HasSuffix(s, ")")
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var configData project
|
||||
if err := xml.NewDecoder(r).Decode(&configData); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to decode '*.packages.props' file: %w", err)
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
var pkgs []ftypes.Package
|
||||
for _, item := range configData.ItemGroups {
|
||||
for _, pkg := range append(item.PackageReferenceEntry, item.PackageVersionEntry...) {
|
||||
lib := pkg.library()
|
||||
if !shouldSkipLib(lib) {
|
||||
libs = append(libs, lib)
|
||||
pkg := pkg.Package()
|
||||
if !shouldSkipPkg(pkg) {
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return utils.UniqueLibraries(libs), nil, nil
|
||||
return utils.UniquePackages(pkgs), nil, nil
|
||||
}
|
||||
|
||||
@@ -8,20 +8,20 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
config "github.com/aquasecurity/trivy/pkg/dependency/parser/nuget/packagesprops"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string // Test input file
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "PackagesProps",
|
||||
inputFile: "testdata/packages.props",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{Name: "Microsoft.Extensions.Configuration", Version: "2.1.1", ID: "Microsoft.Extensions.Configuration@2.1.1"},
|
||||
{Name: "Microsoft.Extensions.DependencyInjection.Abstractions", Version: "2.2.1", ID: "Microsoft.Extensions.DependencyInjection.Abstractions@2.2.1"},
|
||||
{Name: "Microsoft.Extensions.Http", Version: "3.2.1", ID: "Microsoft.Extensions.Http@3.2.1"},
|
||||
@@ -30,7 +30,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "DirectoryPackagesProps",
|
||||
inputFile: "testdata/Directory.Packages.props",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{Name: "PackageOne", Version: "6.2.3", ID: "PackageOne@6.2.3"},
|
||||
{Name: "PackageThree", Version: "2.4.1", ID: "PackageThree@2.4.1"},
|
||||
{Name: "PackageTwo", Version: "6.0.0", ID: "PackageTwo@6.0.0"},
|
||||
@@ -39,7 +39,7 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "SeveralItemGroupElements",
|
||||
inputFile: "testdata/several_item_groups",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{Name: "PackageOne", Version: "6.2.3", ID: "PackageOne@6.2.3"},
|
||||
{Name: "PackageThree", Version: "2.4.1", ID: "PackageThree@2.4.1"},
|
||||
{Name: "PackageTwo", Version: "6.0.0", ID: "PackageTwo@6.0.0"},
|
||||
@@ -48,14 +48,14 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "VariablesAsNamesOrVersion",
|
||||
inputFile: "testdata/variables_and_empty",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{Name: "PackageFour", Version: "2.4.1", ID: "PackageFour@2.4.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NoItemGroupInXMLStructure",
|
||||
inputFile: "testdata/no_item_group.props",
|
||||
want: []types.Library(nil),
|
||||
want: []ftypes.Package(nil),
|
||||
},
|
||||
{
|
||||
name: "NoProject",
|
||||
|
||||
@@ -10,13 +10,12 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type lockFile struct {
|
||||
type LockFile struct {
|
||||
Packages []packageInfo `json:"packages"`
|
||||
}
|
||||
type packageInfo struct {
|
||||
@@ -32,14 +31,14 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("composer"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
var lockFile lockFile
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockFile LockFile
|
||||
input, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("read error: %w", err)
|
||||
@@ -48,61 +47,61 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("decode error: %w", err)
|
||||
}
|
||||
|
||||
libs := make(map[string]types.Library)
|
||||
pkgs := make(map[string]ftypes.Package)
|
||||
foundDeps := make(map[string][]string)
|
||||
for _, pkg := range lockFile.Packages {
|
||||
lib := types.Library{
|
||||
ID: dependency.ID(ftypes.Composer, pkg.Name, pkg.Version),
|
||||
Name: pkg.Name,
|
||||
Version: pkg.Version,
|
||||
Relationship: types.RelationshipUnknown, // composer.lock file doesn't have info about direct/indirect dependencies
|
||||
License: strings.Join(pkg.License, ", "),
|
||||
Locations: []types.Location{
|
||||
for _, lpkg := range lockFile.Packages {
|
||||
pkg := ftypes.Package{
|
||||
ID: dependency.ID(ftypes.Composer, lpkg.Name, lpkg.Version),
|
||||
Name: lpkg.Name,
|
||||
Version: lpkg.Version,
|
||||
Relationship: ftypes.RelationshipUnknown, // composer.lock file doesn't have info about direct/indirect dependencies
|
||||
Licenses: lpkg.License,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: pkg.StartLine,
|
||||
EndLine: pkg.EndLine,
|
||||
StartLine: lpkg.StartLine,
|
||||
EndLine: lpkg.EndLine,
|
||||
},
|
||||
},
|
||||
}
|
||||
libs[lib.Name] = lib
|
||||
pkgs[pkg.Name] = pkg
|
||||
|
||||
var dependsOn []string
|
||||
for depName := range pkg.Require {
|
||||
for depName := range lpkg.Require {
|
||||
// Require field includes required php version, skip this
|
||||
// Also skip PHP extensions
|
||||
if depName == "php" || strings.HasPrefix(depName, "ext") {
|
||||
continue
|
||||
}
|
||||
dependsOn = append(dependsOn, depName) // field uses range of versions, so later we will fill in the versions from the libraries
|
||||
dependsOn = append(dependsOn, depName) // field uses range of versions, so later we will fill in the versions from the packages
|
||||
}
|
||||
if len(dependsOn) > 0 {
|
||||
foundDeps[lib.ID] = dependsOn
|
||||
foundDeps[pkg.ID] = dependsOn
|
||||
}
|
||||
}
|
||||
|
||||
// fill deps versions
|
||||
var deps []types.Dependency
|
||||
for libID, depsOn := range foundDeps {
|
||||
var deps ftypes.Dependencies
|
||||
for pkgID, depsOn := range foundDeps {
|
||||
var dependsOn []string
|
||||
for _, depName := range depsOn {
|
||||
if lib, ok := libs[depName]; ok {
|
||||
dependsOn = append(dependsOn, lib.ID)
|
||||
if pkg, ok := pkgs[depName]; ok {
|
||||
dependsOn = append(dependsOn, pkg.ID)
|
||||
continue
|
||||
}
|
||||
p.logger.Debug("Unable to find version", log.String("name", depName))
|
||||
}
|
||||
sort.Strings(dependsOn)
|
||||
deps = append(deps, types.Dependency{
|
||||
ID: libID,
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
}
|
||||
|
||||
libSlice := maps.Values(libs)
|
||||
sort.Sort(types.Libraries(libSlice))
|
||||
sort.Sort(types.Dependencies(deps))
|
||||
pkgSlice := maps.Values(pkgs)
|
||||
sort.Sort(ftypes.Packages(pkgSlice))
|
||||
sort.Sort(deps)
|
||||
|
||||
return libSlice, deps, nil
|
||||
return pkgSlice, deps, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSONWithMetadata needed to detect start and end lines of deps
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package composer
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
@@ -13,15 +13,15 @@ var (
|
||||
// apk add jq
|
||||
// composer require guzzlehttp/guzzle:6.5.8
|
||||
// composer require pear/log:1.13.3 --dev
|
||||
// composer show -i --no-dev -f json | jq --sort-keys -rc '.installed[] | "{ID: \"\(.name)@\(.version)\", Name: \"\(.name)\", Version: \"\(.version)\", License: \"MIT\", Locations: []types.Location{{StartLine: , EndLine: }}},"'
|
||||
// composer show -i --no-dev -f json | jq --sort-keys -rc '.installed[] | "{ID: \"\(.name)@\(.version)\", Name: \"\(.name)\", Version: \"\(.version)\", License: \"MIT\", Locations: []ftypes.Location{{StartLine: , EndLine: }}},"'
|
||||
// locations are filled manually
|
||||
composerLibs = []types.Library{
|
||||
composerPkgs = []ftypes.Package{
|
||||
{
|
||||
ID: "guzzlehttp/guzzle@6.5.8",
|
||||
Name: "guzzlehttp/guzzle",
|
||||
Version: "6.5.8",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "guzzlehttp/guzzle@6.5.8",
|
||||
Name: "guzzlehttp/guzzle",
|
||||
Version: "6.5.8",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 9,
|
||||
EndLine: 123,
|
||||
@@ -29,11 +29,11 @@ var (
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "guzzlehttp/promises@1.5.2",
|
||||
Name: "guzzlehttp/promises",
|
||||
Version: "1.5.2",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "guzzlehttp/promises@1.5.2",
|
||||
Name: "guzzlehttp/promises",
|
||||
Version: "1.5.2",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 124,
|
||||
EndLine: 207,
|
||||
@@ -41,11 +41,11 @@ var (
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "guzzlehttp/psr7@1.9.0",
|
||||
Name: "guzzlehttp/psr7",
|
||||
Version: "1.9.0",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "guzzlehttp/psr7@1.9.0",
|
||||
Name: "guzzlehttp/psr7",
|
||||
Version: "1.9.0",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 208,
|
||||
EndLine: 317,
|
||||
@@ -53,11 +53,11 @@ var (
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "psr/http-message@1.0.1",
|
||||
Name: "psr/http-message",
|
||||
Version: "1.0.1",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "psr/http-message@1.0.1",
|
||||
Name: "psr/http-message",
|
||||
Version: "1.0.1",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 318,
|
||||
EndLine: 370,
|
||||
@@ -65,11 +65,11 @@ var (
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "ralouphie/getallheaders@3.0.3",
|
||||
Name: "ralouphie/getallheaders",
|
||||
Version: "3.0.3",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "ralouphie/getallheaders@3.0.3",
|
||||
Name: "ralouphie/getallheaders",
|
||||
Version: "3.0.3",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 371,
|
||||
EndLine: 414,
|
||||
@@ -77,11 +77,11 @@ var (
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "symfony/polyfill-intl-idn@v1.27.0",
|
||||
Name: "symfony/polyfill-intl-idn",
|
||||
Version: "v1.27.0",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "symfony/polyfill-intl-idn@v1.27.0",
|
||||
Name: "symfony/polyfill-intl-idn",
|
||||
Version: "v1.27.0",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 415,
|
||||
EndLine: 501,
|
||||
@@ -89,11 +89,11 @@ var (
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "symfony/polyfill-intl-normalizer@v1.27.0",
|
||||
Name: "symfony/polyfill-intl-normalizer",
|
||||
Version: "v1.27.0",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "symfony/polyfill-intl-normalizer@v1.27.0",
|
||||
Name: "symfony/polyfill-intl-normalizer",
|
||||
Version: "v1.27.0",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 502,
|
||||
EndLine: 585,
|
||||
@@ -101,11 +101,11 @@ var (
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "symfony/polyfill-php72@v1.27.0",
|
||||
Name: "symfony/polyfill-php72",
|
||||
Version: "v1.27.0",
|
||||
License: "MIT",
|
||||
Locations: []types.Location{
|
||||
ID: "symfony/polyfill-php72@v1.27.0",
|
||||
Name: "symfony/polyfill-php72",
|
||||
Version: "v1.27.0",
|
||||
Licenses: []string{"MIT"},
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 586,
|
||||
EndLine: 661,
|
||||
@@ -114,7 +114,7 @@ var (
|
||||
},
|
||||
}
|
||||
// dependencies are filled manually
|
||||
composerDeps = []types.Dependency{
|
||||
composerDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "guzzlehttp/guzzle@6.5.8",
|
||||
DependsOn: []string{
|
||||
@@ -144,13 +144,13 @@ func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string
|
||||
wantLibs []types.Library
|
||||
wantDeps []types.Dependency
|
||||
wantPkgs []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
file: "testdata/composer_happy.lock",
|
||||
wantLibs: composerLibs,
|
||||
wantPkgs: composerPkgs,
|
||||
wantDeps: composerDeps,
|
||||
},
|
||||
}
|
||||
@@ -161,10 +161,10 @@ func TestParse(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
gotLibs, gotDeps, err := NewParser().Parse(f)
|
||||
gotPkgs, gotDeps, err := NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.wantLibs, gotLibs)
|
||||
assert.Equal(t, tt.wantPkgs, gotPkgs)
|
||||
assert.Equal(t, tt.wantDeps, gotDeps)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ import (
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -18,7 +19,7 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("python"),
|
||||
}
|
||||
@@ -26,7 +27,7 @@ func NewParser() types.Parser {
|
||||
|
||||
// Parse parses egg and wheel metadata.
|
||||
// e.g. .egg-info/PKG-INFO and dist-info/METADATA
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
rd := textproto.NewReader(bufio.NewReader(r))
|
||||
h, err := rd.ReadMIMEHeader()
|
||||
if e := textproto.ProtocolError(""); errors.As(err, &e) {
|
||||
@@ -82,11 +83,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
license = "file://" + h.Get("License-File")
|
||||
}
|
||||
|
||||
return []types.Library{
|
||||
return []ftypes.Package{
|
||||
{
|
||||
Name: name,
|
||||
Version: version,
|
||||
License: license,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Licenses: lo.Ternary(license != "", []string{license}, nil),
|
||||
},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/python/packaging"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr bool
|
||||
}{
|
||||
// listing dependencies based on METADATA/PKG-INFO files
|
||||
@@ -33,16 +33,22 @@ func TestParse(t *testing.T) {
|
||||
// cd /usr/lib/python3.9/site-packages/setuptools-52.0.0-py3.9.egg-info/
|
||||
// cat PKG-INFO | grep -e "^Name:" -e "^Version:" -e "^License:" | cut -d" " -f2- | \
|
||||
// tr "\n" "\t" | awk -F "\t" '{printf("\{\""$1"\", \""$2"\", \""$3"\"\}\n")}'
|
||||
want: []types.Library{{Name: "setuptools", Version: "51.3.3", License: "UNKNOWN"}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "setuptools",
|
||||
Version: "51.3.3",
|
||||
Licenses: []string{"UNKNOWN"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "egg PKG-INFO with description containing non-RFC 7230 bytes",
|
||||
input: "testdata/unidecode-egg-info.PKG-INFO",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "Unidecode",
|
||||
Version: "0.4.1",
|
||||
License: "UNKNOWN",
|
||||
Name: "Unidecode",
|
||||
Version: "0.4.1",
|
||||
Licenses: []string{"UNKNOWN"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -55,7 +61,13 @@ func TestParse(t *testing.T) {
|
||||
// cd /usr/lib/python3.9/site-packages/
|
||||
// cat distlib-0.3.1-py3.9.egg-info | grep -e "^Name:" -e "^Version:" -e "^License:" | cut -d" " -f2- | \
|
||||
// tr "\n" "\t" | awk -F "\t" '{printf("\{\""$1"\", \""$2"\", \""$3"\"\}\n")}'
|
||||
want: []types.Library{{Name: "distlib", Version: "0.3.1", License: "Python license"}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "distlib",
|
||||
Version: "0.3.1",
|
||||
Licenses: []string{"Python license"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wheel METADATA",
|
||||
@@ -67,31 +79,53 @@ func TestParse(t *testing.T) {
|
||||
// find dist-infos/ | grep -v METADATA | xargs rm -R
|
||||
|
||||
// for single METADATA file with known name
|
||||
// cat "{{ libname }}.METADATA | grep -e "^Name:" -e "^Version:" -e "^License:" | cut -d" " -f2- | tr "\n" "\t" | awk -F "\t" '{printf("\{\""$1"\", \""$2"\", \""$3"\"\}\n")}'
|
||||
want: []types.Library{{Name: "simple", Version: "0.1.0", License: ""}},
|
||||
// cat "{{ libname }}.METADATA | grep -e "^Name:" -e "^Version:" -e "^Licenses: []string{" | cut -d" " -f2- | tr "\n" "\t" | awk -F "\t" '{printf("\{\""$1"\"}, \""$2"\", \""$3"\"\}\n")}'
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "simple",
|
||||
Version: "0.1.0",
|
||||
Licenses: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wheel METADATA",
|
||||
|
||||
// for single METADATA file with known name
|
||||
// cat "{{ libname }}.METADATA | grep -e "^Name:" -e "^Version:" -e "^License:" | cut -d" " -f2- | tr "\n" "\t" | awk -F "\t" '{printf("\{\""$1"\", \""$2"\", \""$3"\"\}\n")}'
|
||||
// cat "{{ libname }}.METADATA | grep -e "^Name:" -e "^Version:" -e "^Licenses: []string{" | cut -d" " -f2- | tr "\n" "\t" | awk -F "\t" '{printf("\{\""$1"\"}, \""$2"\", \""$3"\"\}\n")}'
|
||||
input: "testdata/distlib-0.3.1.METADATA",
|
||||
want: []types.Library{{Name: "distlib", Version: "0.3.1", License: "Python Software Foundation License"}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "distlib",
|
||||
Version: "0.3.1",
|
||||
Licenses: []string{"Python Software Foundation License"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wheel METADATA",
|
||||
// Input defines "Classifier: License" but it ends at "OSI Approved" which doesn't define any specific license, thus "License" field is added to results
|
||||
input: "testdata/asyncssh-2.14.2.METADATA",
|
||||
|
||||
want: []types.Library{{Name: "asyncssh", Version: "2.14.2", License: "Eclipse Public License v2.0"}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "asyncssh",
|
||||
Version: "2.14.2",
|
||||
Licenses: []string{"Eclipse Public License v2.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wheel METADATA",
|
||||
// Input defines multiple "Classifier: License"
|
||||
input: "testdata/pyphen-0.14.0.METADATA",
|
||||
|
||||
want: []types.Library{
|
||||
{Name: "pyphen", Version: "0.14.0", License: "GNU General Public License v2 or later (GPLv2+), GNU Lesser General Public License v2 or later (LGPLv2+), Mozilla Public License 1.1 (MPL 1.1)"},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "pyphen",
|
||||
Version: "0.14.0",
|
||||
Licenses: []string{"GNU General Public License v2 or later (GPLv2+), GNU Lesser General Public License v2 or later (LGPLv2+), Mozilla Public License 1.1 (MPL 1.1)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -102,33 +136,33 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
name: "with License-Expression field",
|
||||
input: "testdata/iniconfig-2.0.0.METADATA",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "iniconfig",
|
||||
Version: "2.0.0",
|
||||
License: "MIT",
|
||||
Name: "iniconfig",
|
||||
Version: "2.0.0",
|
||||
Licenses: []string{"MIT"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with an empty license field but with license in Classifier",
|
||||
input: "testdata/zipp-3.12.1.METADATA",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "zipp",
|
||||
Version: "3.12.1",
|
||||
License: "MIT License",
|
||||
Name: "zipp",
|
||||
Version: "3.12.1",
|
||||
Licenses: []string{"MIT License"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "without licenses, but with a license file (a license in Classifier was removed)",
|
||||
input: "testdata/networkx-3.0.METADATA",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "networkx",
|
||||
Version: "3.0",
|
||||
License: "file://LICENSE.txt",
|
||||
Name: "networkx",
|
||||
Version: "3.0",
|
||||
Licenses: []string{"file://LICENSE.txt"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
@@ -24,11 +24,11 @@ const (
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
// `requirements.txt` can use byte order marks (BOM)
|
||||
// e.g. on Windows `requirements.txt` can use UTF-16LE with BOM
|
||||
// We need to override them to avoid the file being read incorrectly
|
||||
@@ -36,7 +36,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
decodedReader := transform.NewReader(r, transformer)
|
||||
|
||||
scanner := bufio.NewScanner(decodedReader)
|
||||
var libs []types.Library
|
||||
var pkgs []ftypes.Package
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
line = strings.ReplaceAll(line, " ", "")
|
||||
@@ -49,7 +49,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
if len(s) != 2 {
|
||||
continue
|
||||
}
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
Name: s[0],
|
||||
Version: s[1],
|
||||
})
|
||||
@@ -57,7 +57,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, nil, xerrors.Errorf("scan error: %w", err)
|
||||
}
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func rStripByKey(line, key string) string {
|
||||
|
||||
@@ -8,13 +8,13 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
vectors := []struct {
|
||||
file string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
{
|
||||
file: "testdata/requirements_flask.txt",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package pip
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
requirementsFlask = []types.Library{
|
||||
requirementsFlask = []ftypes.Package{
|
||||
{Name: "click", Version: "8.0.0"},
|
||||
{Name: "Flask", Version: "2.0.0"},
|
||||
{Name: "itsdangerous", Version: "2.0.0"},
|
||||
@@ -12,45 +12,45 @@ var (
|
||||
{Name: "Werkzeug", Version: "2.0.0"},
|
||||
}
|
||||
|
||||
requirementsComments = []types.Library{
|
||||
requirementsComments = []ftypes.Package{
|
||||
{Name: "click", Version: "8.0.0"},
|
||||
{Name: "Flask", Version: "2.0.0"},
|
||||
{Name: "Jinja2", Version: "3.0.0"},
|
||||
{Name: "MarkupSafe", Version: "2.0.0"},
|
||||
}
|
||||
|
||||
requirementsSpaces = []types.Library{
|
||||
requirementsSpaces = []ftypes.Package{
|
||||
{Name: "click", Version: "8.0.0"},
|
||||
{Name: "Flask", Version: "2.0.0"},
|
||||
{Name: "itsdangerous", Version: "2.0.0"},
|
||||
{Name: "Jinja2", Version: "3.0.0"},
|
||||
}
|
||||
|
||||
requirementsNoVersion = []types.Library{
|
||||
requirementsNoVersion = []ftypes.Package{
|
||||
{Name: "Flask", Version: "2.0.0"},
|
||||
}
|
||||
|
||||
requirementsOperator = []types.Library{
|
||||
requirementsOperator = []ftypes.Package{
|
||||
{Name: "Django", Version: "2.3.4"},
|
||||
{Name: "SomeProject", Version: "5.4"},
|
||||
}
|
||||
|
||||
requirementsHash = []types.Library{
|
||||
requirementsHash = []ftypes.Package{
|
||||
{Name: "FooProject", Version: "1.2"},
|
||||
{Name: "Jinja2", Version: "3.0.0"},
|
||||
}
|
||||
|
||||
requirementsHyphens = []types.Library{
|
||||
requirementsHyphens = []ftypes.Package{
|
||||
{Name: "oauth2-client", Version: "4.0.0"},
|
||||
{Name: "python-gitlab", Version: "2.0.0"},
|
||||
}
|
||||
|
||||
requirementsExtras = []types.Library{
|
||||
requirementsExtras = []ftypes.Package{
|
||||
{Name: "pyjwt", Version: "2.1.0"},
|
||||
{Name: "celery", Version: "4.4.7"},
|
||||
}
|
||||
|
||||
requirementsUtf16le = []types.Library{
|
||||
requirementsUtf16le = []ftypes.Package{
|
||||
{Name: "attrs", Version: "20.3.0"},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/liamg/jfather"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
@@ -22,11 +22,11 @@ type dependency struct {
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockFile lockFile
|
||||
input, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
@@ -36,20 +36,20 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("failed to decode Pipenv.lock: %w", err)
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
for pkgName, dependency := range lockFile.Default {
|
||||
libs = append(libs, types.Library{
|
||||
var pkgs []ftypes.Package
|
||||
for pkgName, dep := range lockFile.Default {
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
Name: pkgName,
|
||||
Version: strings.TrimLeft(dependency.Version, "="),
|
||||
Locations: []types.Location{
|
||||
Version: strings.TrimLeft(dep.Version, "="),
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: dependency.StartLine,
|
||||
EndLine: dependency.EndLine,
|
||||
StartLine: dep.StartLine,
|
||||
EndLine: dep.EndLine,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return libs, nil, nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSONWithMetadata needed to detect start and end lines of deps
|
||||
|
||||
@@ -4,19 +4,18 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
vectors := []struct {
|
||||
file string // Test input file
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
{
|
||||
file: "testdata/Pipfile_normal.lock",
|
||||
@@ -40,21 +39,8 @@ func TestParse(t *testing.T) {
|
||||
got, _, err := NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
ret := strings.Compare(got[i].Name, got[j].Name)
|
||||
if ret == 0 {
|
||||
return got[i].Version < got[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
|
||||
sort.Slice(v.want, func(i, j int) bool {
|
||||
ret := strings.Compare(v.want[i].Name, v.want[j].Name)
|
||||
if ret == 0 {
|
||||
return v.want[i].Version < v.want[j].Version
|
||||
}
|
||||
return ret < 0
|
||||
})
|
||||
sort.Sort(ftypes.Packages(got))
|
||||
sort.Sort(ftypes.Packages(v.want))
|
||||
|
||||
assert.Equal(t, v.want, got)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package pipenv
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
// docker run --name pipenv --rm -it python:3.9-alpine sh
|
||||
@@ -11,13 +11,13 @@ var (
|
||||
// pipenv graph --json | jq -rc '.[] | "{\"\(.package.package_name | ascii_downcase)\", \"\(.package.installed_version)\", \"\"},"'
|
||||
// graph doesn't contain information about location of dependency in lock file.
|
||||
// add locations manually
|
||||
pipenvNormal = []types.Library{
|
||||
{Name: "urllib3", Version: "1.24.2", Locations: []types.Location{{StartLine: 65, EndLine: 71}}},
|
||||
{Name: "requests", Version: "2.21.0", Locations: []types.Location{{StartLine: 57, EndLine: 64}}},
|
||||
{Name: "pyyaml", Version: "5.1", Locations: []types.Location{{StartLine: 40, EndLine: 56}}},
|
||||
{Name: "idna", Version: "2.8", Locations: []types.Location{{StartLine: 33, EndLine: 39}}},
|
||||
{Name: "chardet", Version: "3.0.4", Locations: []types.Location{{StartLine: 26, EndLine: 32}}},
|
||||
{Name: "certifi", Version: "2019.3.9", Locations: []types.Location{{StartLine: 19, EndLine: 25}}},
|
||||
pipenvNormal = []ftypes.Package{
|
||||
{Name: "urllib3", Version: "1.24.2", Locations: []ftypes.Location{{StartLine: 65, EndLine: 71}}},
|
||||
{Name: "requests", Version: "2.21.0", Locations: []ftypes.Location{{StartLine: 57, EndLine: 64}}},
|
||||
{Name: "pyyaml", Version: "5.1", Locations: []ftypes.Location{{StartLine: 40, EndLine: 56}}},
|
||||
{Name: "idna", Version: "2.8", Locations: []ftypes.Location{{StartLine: 33, EndLine: 39}}},
|
||||
{Name: "chardet", Version: "3.0.4", Locations: []ftypes.Location{{StartLine: 26, EndLine: 32}}},
|
||||
{Name: "certifi", Version: "2019.3.9", Locations: []ftypes.Location{{StartLine: 19, EndLine: 25}}},
|
||||
}
|
||||
|
||||
// docker run --name pipenv --rm -it python:3.9-alpine bash
|
||||
@@ -28,17 +28,17 @@ var (
|
||||
// pipenv graph --json | jq -rc '.[] | "{\"\(.package.package_name | ascii_downcase)\", \"\(.package.installed_version)\", \"\"},"'
|
||||
// graph doesn't contain information about location of dependency in lock file.
|
||||
// add locations manually
|
||||
pipenvDjango = []types.Library{
|
||||
{Name: "urllib3", Version: "1.24.2", Locations: []types.Location{{StartLine: 95, EndLine: 101}}},
|
||||
{Name: "sqlparse", Version: "0.3.0", Locations: []types.Location{{StartLine: 88, EndLine: 94}}},
|
||||
{Name: "requests", Version: "2.21.0", Locations: []types.Location{{StartLine: 80, EndLine: 87}}},
|
||||
{Name: "pyyaml", Version: "5.1", Locations: []types.Location{{StartLine: 63, EndLine: 79}}},
|
||||
{Name: "pytz", Version: "2019.1", Locations: []types.Location{{StartLine: 56, EndLine: 62}}},
|
||||
{Name: "idna", Version: "2.8", Locations: []types.Location{{StartLine: 49, EndLine: 55}}},
|
||||
{Name: "djangorestframework", Version: "3.9.3", Locations: []types.Location{{StartLine: 41, EndLine: 48}}},
|
||||
{Name: "django", Version: "2.2", Locations: []types.Location{{StartLine: 33, EndLine: 40}}},
|
||||
{Name: "chardet", Version: "3.0.4", Locations: []types.Location{{StartLine: 26, EndLine: 32}}},
|
||||
{Name: "certifi", Version: "2019.3.9", Locations: []types.Location{{StartLine: 19, EndLine: 25}}},
|
||||
pipenvDjango = []ftypes.Package{
|
||||
{Name: "urllib3", Version: "1.24.2", Locations: []ftypes.Location{{StartLine: 95, EndLine: 101}}},
|
||||
{Name: "sqlparse", Version: "0.3.0", Locations: []ftypes.Location{{StartLine: 88, EndLine: 94}}},
|
||||
{Name: "requests", Version: "2.21.0", Locations: []ftypes.Location{{StartLine: 80, EndLine: 87}}},
|
||||
{Name: "pyyaml", Version: "5.1", Locations: []ftypes.Location{{StartLine: 63, EndLine: 79}}},
|
||||
{Name: "pytz", Version: "2019.1", Locations: []ftypes.Location{{StartLine: 56, EndLine: 62}}},
|
||||
{Name: "idna", Version: "2.8", Locations: []ftypes.Location{{StartLine: 49, EndLine: 55}}},
|
||||
{Name: "djangorestframework", Version: "3.9.3", Locations: []ftypes.Location{{StartLine: 41, EndLine: 48}}},
|
||||
{Name: "django", Version: "2.2", Locations: []ftypes.Location{{StartLine: 33, EndLine: 40}}},
|
||||
{Name: "chardet", Version: "3.0.4", Locations: []ftypes.Location{{StartLine: 26, EndLine: 32}}},
|
||||
{Name: "certifi", Version: "2019.3.9", Locations: []ftypes.Location{{StartLine: 19, EndLine: 25}}},
|
||||
}
|
||||
|
||||
// docker run --name pipenv --rm -it python:3.9-alpine bash
|
||||
@@ -49,30 +49,30 @@ var (
|
||||
// pipenv graph --json | jq -rc '.[] | "{\"\(.package.package_name | ascii_downcase)\", \"\(.package.installed_version)\", \"\"},"'
|
||||
// graph doesn't contain information about location of dependency in lock file.
|
||||
// add locations manually
|
||||
pipenvMany = []types.Library{
|
||||
{Name: "urllib3", Version: "1.24.2", Locations: []types.Location{{StartLine: 237, EndLine: 244}}},
|
||||
{Name: "sqlparse", Version: "0.3.0", Locations: []types.Location{{StartLine: 230, EndLine: 236}}},
|
||||
{Name: "six", Version: "1.12.0", Locations: []types.Location{{StartLine: 222, EndLine: 229}}},
|
||||
{Name: "simplejson", Version: "3.16.0", Locations: []types.Location{{StartLine: 204, EndLine: 221}}},
|
||||
{Name: "s3transfer", Version: "0.2.0", Locations: []types.Location{{StartLine: 197, EndLine: 203}}},
|
||||
{Name: "rsa", Version: "3.4.2", Locations: []types.Location{{StartLine: 190, EndLine: 196}}},
|
||||
{Name: "requests", Version: "2.21.0", Locations: []types.Location{{StartLine: 182, EndLine: 189}}},
|
||||
{Name: "pyyaml", Version: "3.13", Locations: []types.Location{{StartLine: 165, EndLine: 181}}},
|
||||
{Name: "pytz", Version: "2019.1", Locations: []types.Location{{StartLine: 158, EndLine: 164}}},
|
||||
{Name: "python-dateutil", Version: "2.8.0", Locations: []types.Location{{StartLine: 150, EndLine: 157}}},
|
||||
{Name: "pyasn1", Version: "0.4.5", Locations: []types.Location{{StartLine: 142, EndLine: 149}}},
|
||||
{Name: "markupsafe", Version: "1.1.1", Locations: []types.Location{{StartLine: 109, EndLine: 141}}},
|
||||
{Name: "jmespath", Version: "0.9.4", Locations: []types.Location{{StartLine: 102, EndLine: 108}}},
|
||||
{Name: "jinja2", Version: "2.10.1", Locations: []types.Location{{StartLine: 94, EndLine: 101}}},
|
||||
{Name: "idna", Version: "2.8", Locations: []types.Location{{StartLine: 87, EndLine: 93}}},
|
||||
{Name: "framework", Version: "0.1.0", Locations: []types.Location{{StartLine: 80, EndLine: 86}}},
|
||||
{Name: "docutils", Version: "0.14", Locations: []types.Location{{StartLine: 72, EndLine: 79}}},
|
||||
{Name: "djangorestframework", Version: "3.9.3", Locations: []types.Location{{StartLine: 64, EndLine: 71}}},
|
||||
{Name: "django", Version: "2.2", Locations: []types.Location{{StartLine: 56, EndLine: 63}}},
|
||||
{Name: "colorama", Version: "0.3.9", Locations: []types.Location{{StartLine: 49, EndLine: 55}}},
|
||||
{Name: "chardet", Version: "3.0.4", Locations: []types.Location{{StartLine: 42, EndLine: 48}}},
|
||||
{Name: "certifi", Version: "2019.3.9", Locations: []types.Location{{StartLine: 35, EndLine: 41}}},
|
||||
{Name: "botocore", Version: "1.12.137", Locations: []types.Location{{StartLine: 27, EndLine: 34}}},
|
||||
{Name: "awscli", Version: "1.16.147", Locations: []types.Location{{StartLine: 19, EndLine: 26}}},
|
||||
pipenvMany = []ftypes.Package{
|
||||
{Name: "urllib3", Version: "1.24.2", Locations: []ftypes.Location{{StartLine: 237, EndLine: 244}}},
|
||||
{Name: "sqlparse", Version: "0.3.0", Locations: []ftypes.Location{{StartLine: 230, EndLine: 236}}},
|
||||
{Name: "six", Version: "1.12.0", Locations: []ftypes.Location{{StartLine: 222, EndLine: 229}}},
|
||||
{Name: "simplejson", Version: "3.16.0", Locations: []ftypes.Location{{StartLine: 204, EndLine: 221}}},
|
||||
{Name: "s3transfer", Version: "0.2.0", Locations: []ftypes.Location{{StartLine: 197, EndLine: 203}}},
|
||||
{Name: "rsa", Version: "3.4.2", Locations: []ftypes.Location{{StartLine: 190, EndLine: 196}}},
|
||||
{Name: "requests", Version: "2.21.0", Locations: []ftypes.Location{{StartLine: 182, EndLine: 189}}},
|
||||
{Name: "pyyaml", Version: "3.13", Locations: []ftypes.Location{{StartLine: 165, EndLine: 181}}},
|
||||
{Name: "pytz", Version: "2019.1", Locations: []ftypes.Location{{StartLine: 158, EndLine: 164}}},
|
||||
{Name: "python-dateutil", Version: "2.8.0", Locations: []ftypes.Location{{StartLine: 150, EndLine: 157}}},
|
||||
{Name: "pyasn1", Version: "0.4.5", Locations: []ftypes.Location{{StartLine: 142, EndLine: 149}}},
|
||||
{Name: "markupsafe", Version: "1.1.1", Locations: []ftypes.Location{{StartLine: 109, EndLine: 141}}},
|
||||
{Name: "jmespath", Version: "0.9.4", Locations: []ftypes.Location{{StartLine: 102, EndLine: 108}}},
|
||||
{Name: "jinja2", Version: "2.10.1", Locations: []ftypes.Location{{StartLine: 94, EndLine: 101}}},
|
||||
{Name: "idna", Version: "2.8", Locations: []ftypes.Location{{StartLine: 87, EndLine: 93}}},
|
||||
{Name: "framework", Version: "0.1.0", Locations: []ftypes.Location{{StartLine: 80, EndLine: 86}}},
|
||||
{Name: "docutils", Version: "0.14", Locations: []ftypes.Location{{StartLine: 72, EndLine: 79}}},
|
||||
{Name: "djangorestframework", Version: "3.9.3", Locations: []ftypes.Location{{StartLine: 64, EndLine: 71}}},
|
||||
{Name: "django", Version: "2.2", Locations: []ftypes.Location{{StartLine: 56, EndLine: 63}}},
|
||||
{Name: "colorama", Version: "0.3.9", Locations: []ftypes.Location{{StartLine: 49, EndLine: 55}}},
|
||||
{Name: "chardet", Version: "3.0.4", Locations: []ftypes.Location{{StartLine: 42, EndLine: 48}}},
|
||||
{Name: "certifi", Version: "2019.3.9", Locations: []ftypes.Location{{StartLine: 35, EndLine: 41}}},
|
||||
{Name: "botocore", Version: "1.12.137", Locations: []ftypes.Location{{StartLine: 27, EndLine: 34}}},
|
||||
{Name: "awscli", Version: "1.16.147", Locations: []ftypes.Location{{StartLine: 19, EndLine: 26}}},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
version "github.com/aquasecurity/go-pep440-version"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -39,61 +38,61 @@ func NewParser() *Parser {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockfile Lockfile
|
||||
if _, err := toml.NewDecoder(r).Decode(&lockfile); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to decode poetry.lock: %w", err)
|
||||
}
|
||||
|
||||
// Keep all installed versions
|
||||
libVersions := p.parseVersions(lockfile)
|
||||
pkgVersions := p.parseVersions(lockfile)
|
||||
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
var pkgs []ftypes.Package
|
||||
var deps []ftypes.Dependency
|
||||
for _, pkg := range lockfile.Packages {
|
||||
if pkg.Category == "dev" {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgID := packageID(pkg.Name, pkg.Version)
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: pkg.Name,
|
||||
Version: pkg.Version,
|
||||
})
|
||||
|
||||
dependsOn := p.parseDependencies(pkg.Dependencies, libVersions)
|
||||
dependsOn := p.parseDependencies(pkg.Dependencies, pkgVersions)
|
||||
if len(dependsOn) != 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
}
|
||||
}
|
||||
return libs, deps, nil
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
// parseVersions stores all installed versions of libraries for use in dependsOn
|
||||
// as the dependencies of libraries use version range.
|
||||
// parseVersions stores all installed versions of packages for use in dependsOn
|
||||
// as the dependencies of packages use version range.
|
||||
func (p *Parser) parseVersions(lockfile Lockfile) map[string][]string {
|
||||
libVersions := make(map[string][]string)
|
||||
pkgVersions := make(map[string][]string)
|
||||
for _, pkg := range lockfile.Packages {
|
||||
if pkg.Category == "dev" {
|
||||
continue
|
||||
}
|
||||
if vers, ok := libVersions[pkg.Name]; ok {
|
||||
libVersions[pkg.Name] = append(vers, pkg.Version)
|
||||
if vers, ok := pkgVersions[pkg.Name]; ok {
|
||||
pkgVersions[pkg.Name] = append(vers, pkg.Version)
|
||||
} else {
|
||||
libVersions[pkg.Name] = []string{pkg.Version}
|
||||
pkgVersions[pkg.Name] = []string{pkg.Version}
|
||||
}
|
||||
}
|
||||
return libVersions
|
||||
return pkgVersions
|
||||
}
|
||||
|
||||
func (p *Parser) parseDependencies(deps map[string]any, libVersions map[string][]string) []string {
|
||||
func (p *Parser) parseDependencies(deps map[string]any, pkgVersions map[string][]string) []string {
|
||||
var dependsOn []string
|
||||
for name, versRange := range deps {
|
||||
if dep, err := p.parseDependency(name, versRange, libVersions); err != nil {
|
||||
if dep, err := p.parseDependency(name, versRange, pkgVersions); err != nil {
|
||||
p.logger.Debug("Failed to parse poetry dependency", log.Err(err))
|
||||
} else if dep != "" {
|
||||
dependsOn = append(dependsOn, dep)
|
||||
@@ -105,9 +104,9 @@ func (p *Parser) parseDependencies(deps map[string]any, libVersions map[string][
|
||||
return dependsOn
|
||||
}
|
||||
|
||||
func (p *Parser) parseDependency(name string, versRange any, libVersions map[string][]string) (string, error) {
|
||||
func (p *Parser) parseDependency(name string, versRange any, pkgVersions map[string][]string) (string, error) {
|
||||
name = normalizePkgName(name)
|
||||
vers, ok := libVersions[name]
|
||||
vers, ok := pkgVersions[name]
|
||||
if !ok {
|
||||
return "", xerrors.Errorf("no version found for %q", name)
|
||||
}
|
||||
|
||||
@@ -8,34 +8,34 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string
|
||||
wantLibs []types.Library
|
||||
wantDeps []types.Dependency
|
||||
wantPkgs []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
file: "testdata/poetry_normal.lock",
|
||||
wantLibs: poetryNormal,
|
||||
wantPkgs: poetryNormal,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "many",
|
||||
file: "testdata/poetry_many.lock",
|
||||
wantLibs: poetryMany,
|
||||
wantPkgs: poetryMany,
|
||||
wantDeps: poetryManyDeps,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "flask",
|
||||
file: "testdata/poetry_flask.lock",
|
||||
wantLibs: poetryFlask,
|
||||
wantPkgs: poetryFlask,
|
||||
wantDeps: poetryFlaskDeps,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
@@ -47,11 +47,11 @@ func TestParser_Parse(t *testing.T) {
|
||||
defer f.Close()
|
||||
|
||||
p := NewParser()
|
||||
gotLibs, gotDeps, err := p.Parse(f)
|
||||
gotPkgs, gotDeps, err := p.Parse(f)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("Parse(%v)", tt.file)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.wantLibs, gotLibs, "Parse(%v)", tt.file)
|
||||
assert.Equalf(t, tt.wantPkgs, gotPkgs, "Parse(%v)", tt.file)
|
||||
assert.Equalf(t, tt.wantDeps, gotDeps, "Parse(%v)", tt.file)
|
||||
})
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestParseDependency(t *testing.T) {
|
||||
name string
|
||||
packageName string
|
||||
versionRange interface{}
|
||||
libsVersions map[string][]string
|
||||
pkgsVersions map[string][]string
|
||||
want string
|
||||
wantErr string
|
||||
}{
|
||||
@@ -70,7 +70,7 @@ func TestParseDependency(t *testing.T) {
|
||||
name: "handle package name",
|
||||
packageName: "Test_project.Name",
|
||||
versionRange: "*",
|
||||
libsVersions: map[string][]string{
|
||||
pkgsVersions: map[string][]string{
|
||||
"test-project-name": {"1.0.0"},
|
||||
},
|
||||
want: "test-project-name@1.0.0",
|
||||
@@ -79,7 +79,7 @@ func TestParseDependency(t *testing.T) {
|
||||
name: "version range as string",
|
||||
packageName: "test",
|
||||
versionRange: ">=1.0.0",
|
||||
libsVersions: map[string][]string{
|
||||
pkgsVersions: map[string][]string{
|
||||
"test": {"2.0.0"},
|
||||
},
|
||||
want: "test@2.0.0",
|
||||
@@ -88,7 +88,7 @@ func TestParseDependency(t *testing.T) {
|
||||
name: "version range == *",
|
||||
packageName: "test",
|
||||
versionRange: "*",
|
||||
libsVersions: map[string][]string{
|
||||
pkgsVersions: map[string][]string{
|
||||
"test": {"3.0.0"},
|
||||
},
|
||||
want: "test@3.0.0",
|
||||
@@ -100,23 +100,23 @@ func TestParseDependency(t *testing.T) {
|
||||
"version": ">=4.8.3",
|
||||
"markers": "python_version < \"3.8\"",
|
||||
},
|
||||
libsVersions: map[string][]string{
|
||||
pkgsVersions: map[string][]string{
|
||||
"test": {"5.0.0"},
|
||||
},
|
||||
want: "test@5.0.0",
|
||||
},
|
||||
{
|
||||
name: "libsVersions doesn't contain required version",
|
||||
name: "pkgsVersions doesn't contain required version",
|
||||
packageName: "test",
|
||||
versionRange: ">=1.0.0",
|
||||
libsVersions: map[string][]string{},
|
||||
pkgsVersions: map[string][]string{},
|
||||
wantErr: "no version found",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewParser().parseDependency(tt.packageName, tt.versionRange, tt.libsVersions)
|
||||
got, err := NewParser().parseDependency(tt.packageName, tt.versionRange, tt.pkgsVersions)
|
||||
if tt.wantErr != "" {
|
||||
assert.ErrorContains(t, err, tt.wantErr)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package poetry
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
import ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
var (
|
||||
// docker run --name pipenv --rm -it python@sha256:e1141f10176d74d1a0e87a7c0a0a5a98dd98ec5ac12ce867768f40c6feae2fd9 sh
|
||||
@@ -10,7 +10,7 @@ var (
|
||||
// poetry new normal && cd normal
|
||||
// poetry add pypi@2.1
|
||||
// poetry show -a | awk '{gsub(/\(!\)/, ""); printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\"},\n") }'
|
||||
poetryNormal = []types.Library{
|
||||
poetryNormal = []ftypes.Package{
|
||||
{ID: "pypi@2.1", Name: "pypi", Version: "2.1"},
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ var (
|
||||
// poetry show -a | awk '{gsub(/\(!\)/, ""); printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\"},\n") }'
|
||||
// `--no-dev` flag uncorrected returns deps. Then need to remove `dev` deps manually
|
||||
// list of dev deps - cat poetry.lock | grep 'category = "dev"' -B 3
|
||||
poetryMany = []types.Library{
|
||||
poetryMany = []ftypes.Package{
|
||||
{ID: "attrs@22.2.0", Name: "attrs", Version: "22.2.0"},
|
||||
{ID: "backports-cached-property@1.0.2", Name: "backports-cached-property", Version: "1.0.2"},
|
||||
{ID: "build@0.10.0", Name: "build", Version: "0.10.0"},
|
||||
@@ -82,7 +82,7 @@ var (
|
||||
}
|
||||
|
||||
// cat poetry.lock | grep "\[package.dependencies\]" -B 3 -A 8 - it might help to complete this slice
|
||||
poetryManyDeps = []types.Dependency{
|
||||
poetryManyDeps = []ftypes.Dependency{
|
||||
{ID: "build@0.10.0", DependsOn: []string{"colorama@0.4.6", "importlib-metadata@6.0.0", "packaging@23.0", "pyproject-hooks@1.0.0", "tomli@2.0.1"}},
|
||||
{ID: "cachecontrol@0.12.11", DependsOn: []string{"lockfile@0.12.2", "msgpack@1.0.4", "requests@2.28.2"}},
|
||||
{ID: "cffi@1.15.1", DependsOn: []string{"pycparser@2.21"}},
|
||||
@@ -115,7 +115,7 @@ var (
|
||||
// poetry new web && cd web
|
||||
// poetry add flask@1.0.3
|
||||
// poetry show -a | awk '{gsub(/\(!\)/, ""); printf("{ID: \""$1"@"$2"\", Name: \""$1"\", Version: \""$2"\"},\n") }'
|
||||
poetryFlask = []types.Library{
|
||||
poetryFlask = []ftypes.Package{
|
||||
{ID: "click@8.1.3", Name: "click", Version: "8.1.3"},
|
||||
{ID: "colorama@0.4.6", Name: "colorama", Version: "0.4.6"},
|
||||
{ID: "flask@1.0.3", Name: "flask", Version: "1.0.3"},
|
||||
@@ -126,7 +126,7 @@ var (
|
||||
}
|
||||
|
||||
// cat poetry.lock | grep "\[package.dependencies\]" -B 3 -A 8 - it might help to complete this slice
|
||||
poetryFlaskDeps = []types.Dependency{
|
||||
poetryFlaskDeps = []ftypes.Dependency{
|
||||
{ID: "click@8.1.3", DependsOn: []string{"colorama@0.4.6"}},
|
||||
{ID: "flask@1.0.3", DependsOn: []string{"click@8.1.3", "itsdangerous@2.1.2", "jinja2@3.1.2", "werkzeug@2.2.3"}},
|
||||
{ID: "jinja2@3.1.2", DependsOn: []string{"markupsafe@2.1.2"}},
|
||||
|
||||
@@ -9,21 +9,20 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
libs := make(map[string]types.Library)
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
pkgs := make(map[string]ftypes.Package)
|
||||
var dependsOn, directDeps []string
|
||||
var deps []types.Dependency
|
||||
var deps []ftypes.Dependency
|
||||
var pkgID string
|
||||
|
||||
lineNum := 1
|
||||
@@ -34,7 +33,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
// Parse dependencies
|
||||
if countLeadingSpace(line) == 4 {
|
||||
if len(dependsOn) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
@@ -49,12 +48,12 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
version = strings.SplitN(version, "-", 2)[0] // drop platform (e.g. 1.13.6-x86_64-linux => 1.13.6)
|
||||
name := s[0]
|
||||
pkgID = packageID(name, version)
|
||||
libs[name] = types.Library{
|
||||
pkgs[name] = ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: lineNum,
|
||||
EndLine: lineNum,
|
||||
@@ -77,7 +76,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
}
|
||||
// append last dependency (if any)
|
||||
if len(dependsOn) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
@@ -85,17 +84,17 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
|
||||
// Identify which are direct dependencies
|
||||
for _, d := range directDeps {
|
||||
if l, ok := libs[d]; ok {
|
||||
l.Relationship = types.RelationshipDirect
|
||||
libs[d] = l
|
||||
if l, ok := pkgs[d]; ok {
|
||||
l.Relationship = ftypes.RelationshipDirect
|
||||
pkgs[d] = l
|
||||
}
|
||||
}
|
||||
|
||||
for i, dep := range deps {
|
||||
dependsOn = make([]string, 0)
|
||||
for _, pkgName := range dep.DependsOn {
|
||||
if lib, ok := libs[pkgName]; ok {
|
||||
dependsOn = append(dependsOn, packageID(pkgName, lib.Version))
|
||||
if pkg, ok := pkgs[pkgName]; ok {
|
||||
dependsOn = append(dependsOn, packageID(pkgName, pkg.Version))
|
||||
}
|
||||
}
|
||||
deps[i].DependsOn = dependsOn
|
||||
@@ -104,11 +103,9 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("scan error: %w", err)
|
||||
}
|
||||
|
||||
libSlice := maps.Values(libs)
|
||||
sort.Slice(libSlice, func(i, j int) bool {
|
||||
return libSlice[i].Name < libSlice[j].Name
|
||||
})
|
||||
return libSlice, deps, nil
|
||||
pkgSlice := maps.Values(pkgs)
|
||||
sort.Sort(ftypes.Packages(pkgSlice))
|
||||
return pkgSlice, deps, nil
|
||||
}
|
||||
|
||||
func countLeadingSpace(line string) int {
|
||||
|
||||
@@ -9,41 +9,17 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/ruby/bundler"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
var (
|
||||
NormalLibs = []types.Library{
|
||||
{
|
||||
ID: "coderay@1.1.2",
|
||||
Name: "coderay",
|
||||
Version: "1.1.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 4,
|
||||
EndLine: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "concurrent-ruby@1.1.5",
|
||||
Name: "concurrent-ruby",
|
||||
Version: "1.1.5",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
NormalPkgs = []ftypes.Package{
|
||||
{
|
||||
ID: "dotenv@2.7.2",
|
||||
Name: "dotenv",
|
||||
Version: "2.7.2",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 6,
|
||||
EndLine: 6,
|
||||
@@ -54,20 +30,56 @@ var (
|
||||
ID: "faker@1.9.3",
|
||||
Name: "faker",
|
||||
Version: "1.9.3",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 7,
|
||||
EndLine: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "pry@0.12.2",
|
||||
Name: "pry",
|
||||
Version: "0.12.2",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 12,
|
||||
EndLine: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "coderay@1.1.2",
|
||||
Name: "coderay",
|
||||
Version: "1.1.2",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 4,
|
||||
EndLine: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "concurrent-ruby@1.1.5",
|
||||
Name: "concurrent-ruby",
|
||||
Version: "1.1.5",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "i18n@1.6.0",
|
||||
Name: "i18n",
|
||||
Version: "1.6.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 9,
|
||||
EndLine: 9,
|
||||
@@ -78,28 +90,16 @@ var (
|
||||
ID: "method_source@0.9.2",
|
||||
Name: "method_source",
|
||||
Version: "0.9.2",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 11,
|
||||
EndLine: 11,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "pry@0.12.2",
|
||||
Name: "pry",
|
||||
Version: "0.12.2",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 12,
|
||||
EndLine: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
NormalDeps = []types.Dependency{
|
||||
NormalDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "faker@1.9.3",
|
||||
DependsOn: []string{"i18n@1.6.0"},
|
||||
@@ -116,37 +116,13 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
Bundler2Libs = []types.Library{
|
||||
{
|
||||
ID: "coderay@1.1.3",
|
||||
Name: "coderay",
|
||||
Version: "1.1.3",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 4,
|
||||
EndLine: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "concurrent-ruby@1.1.10",
|
||||
Name: "concurrent-ruby",
|
||||
Version: "1.1.10",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
Bundler2Pkgs = []ftypes.Package{
|
||||
{
|
||||
ID: "dotenv@2.7.6",
|
||||
Name: "dotenv",
|
||||
Version: "2.7.6",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 6,
|
||||
EndLine: 6,
|
||||
@@ -157,64 +133,88 @@ var (
|
||||
ID: "faker@2.21.0",
|
||||
Name: "faker",
|
||||
Version: "2.21.0",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 7,
|
||||
EndLine: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "i18n@1.10.0",
|
||||
Name: "i18n",
|
||||
Version: "1.10.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 9,
|
||||
EndLine: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "json@2.6.2",
|
||||
Name: "json",
|
||||
Version: "2.6.2",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 11,
|
||||
EndLine: 11,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "method_source@1.0.0",
|
||||
Name: "method_source",
|
||||
Version: "1.0.0",
|
||||
Relationship: types.RelationshipIndirect,
|
||||
Locations: []types.Location{
|
||||
{
|
||||
StartLine: 12,
|
||||
EndLine: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "pry@0.14.1",
|
||||
Name: "pry",
|
||||
Version: "0.14.1",
|
||||
Relationship: types.RelationshipDirect,
|
||||
Locations: []types.Location{
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 13,
|
||||
EndLine: 13,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "coderay@1.1.3",
|
||||
Name: "coderay",
|
||||
Version: "1.1.3",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 4,
|
||||
EndLine: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "concurrent-ruby@1.1.10",
|
||||
Name: "concurrent-ruby",
|
||||
Version: "1.1.10",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "i18n@1.10.0",
|
||||
Name: "i18n",
|
||||
Version: "1.10.0",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 9,
|
||||
EndLine: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "method_source@1.0.0",
|
||||
Name: "method_source",
|
||||
Version: "1.0.0",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 12,
|
||||
EndLine: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Bundler2Deps = []types.Dependency{
|
||||
Bundler2Deps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "faker@2.21.0",
|
||||
DependsOn: []string{"i18n@1.10.0"},
|
||||
@@ -237,28 +237,28 @@ func TestParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string
|
||||
wantLibs []types.Library
|
||||
wantDeps []types.Dependency
|
||||
wantPkgs []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
file: "testdata/Gemfile_normal.lock",
|
||||
wantLibs: NormalLibs,
|
||||
wantPkgs: NormalPkgs,
|
||||
wantDeps: NormalDeps,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "bundler2",
|
||||
file: "testdata/Gemfile_bundler2.lock",
|
||||
wantLibs: Bundler2Libs,
|
||||
wantPkgs: Bundler2Pkgs,
|
||||
wantDeps: Bundler2Deps,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "malformed",
|
||||
file: "testdata/Gemfile_malformed.lock",
|
||||
wantLibs: []types.Library{},
|
||||
wantPkgs: []ftypes.Package{},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
}
|
||||
@@ -269,11 +269,11 @@ func TestParser_Parse(t *testing.T) {
|
||||
defer f.Close()
|
||||
|
||||
p := &bundler.Parser{}
|
||||
gotLibs, gotDeps, err := p.Parse(f)
|
||||
gotPkgs, gotDeps, err := p.Parse(f)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("Parse(%v)", tt.file)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.wantLibs, gotLibs, "Parse(%v)", tt.file)
|
||||
assert.Equalf(t, tt.wantPkgs, gotPkgs, "Parse(%v)", tt.file)
|
||||
assert.Equalf(t, tt.wantDeps, gotDeps, "Parse(%v)", tt.file)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/licensing"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
@@ -43,11 +44,11 @@ var (
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) (libs []types.Library, deps []types.Dependency, err error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) (pkgs []ftypes.Package, deps []ftypes.Dependency, err error) {
|
||||
var newVar, name, version, license string
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
@@ -94,11 +95,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) (libs []types.Library, deps []types.D
|
||||
return nil, nil, xerrors.New("failed to parse gemspec")
|
||||
}
|
||||
|
||||
return []types.Library{
|
||||
return []ftypes.Package{
|
||||
{
|
||||
Name: name,
|
||||
Version: version,
|
||||
License: license,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Licenses: licensing.SplitLicenses(license),
|
||||
},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
@@ -8,50 +8,62 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/ruby/gemspec"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
inputFile: "testdata/normal00.gemspec",
|
||||
want: []types.Library{{
|
||||
Name: "rake",
|
||||
Version: "13.0.3",
|
||||
License: "MIT",
|
||||
}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "rake",
|
||||
Version: "13.0.3",
|
||||
Licenses: []string{"MIT"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "another variable name",
|
||||
inputFile: "testdata/normal01.gemspec",
|
||||
want: []types.Library{{
|
||||
Name: "async",
|
||||
Version: "1.25.0",
|
||||
}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "async",
|
||||
Version: "1.25.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "license",
|
||||
inputFile: "testdata/license.gemspec",
|
||||
want: []types.Library{{
|
||||
Name: "async",
|
||||
Version: "1.25.0",
|
||||
License: "MIT",
|
||||
}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "async",
|
||||
Version: "1.25.0",
|
||||
Licenses: []string{"MIT"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple licenses",
|
||||
inputFile: "testdata/multiple_licenses.gemspec",
|
||||
want: []types.Library{{
|
||||
Name: "test-unit",
|
||||
Version: "3.3.7",
|
||||
License: "Ruby, BSDL, PSFL",
|
||||
}},
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
Name: "test-unit",
|
||||
Version: "3.3.7",
|
||||
Licenses: []string{
|
||||
"Ruby",
|
||||
"BSDL",
|
||||
"PSFL",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "malformed variable name",
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
@@ -32,30 +31,30 @@ func convertError(err error) error {
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
// Parse scans files to try to report Rust crates and version injected into Rust binaries
|
||||
// via https://github.com/rust-secure-code/cargo-auditable
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
info, err := rustaudit.GetDependencyInfo(r)
|
||||
if err != nil {
|
||||
return nil, nil, convertError(err)
|
||||
}
|
||||
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
var pkgs []ftypes.Package
|
||||
var deps []ftypes.Dependency
|
||||
for _, pkg := range info.Packages {
|
||||
if pkg.Kind != rustaudit.Runtime {
|
||||
continue
|
||||
}
|
||||
pkgID := packageID(pkg.Name, pkg.Version)
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: pkg.Name,
|
||||
Version: pkg.Version,
|
||||
Relationship: lo.Ternary(pkg.Root, types.RelationshipRoot, types.RelationshipUnknown), // TODO: Determine the direct dependencies by checking the dependencies of the root crate
|
||||
Relationship: lo.Ternary(pkg.Root, ftypes.RelationshipRoot, ftypes.RelationshipUnknown), // TODO: Determine the direct dependencies by checking the dependencies of the root crate
|
||||
})
|
||||
|
||||
var childDeps []string
|
||||
@@ -66,14 +65,14 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
}
|
||||
}
|
||||
if len(childDeps) > 0 {
|
||||
deps = append(deps, types.Dependency{
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: pkgID,
|
||||
DependsOn: childDeps,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return libs, deps, nil
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
func packageID(name, version string) string {
|
||||
|
||||
@@ -8,28 +8,28 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/rust/binary"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
// Test binaries generated from cargo-auditable test fixture
|
||||
// https://github.com/rust-secure-code/cargo-auditable/tree/6b77151/cargo-auditable/tests/fixtures/workspace
|
||||
var (
|
||||
libs = []types.Library{
|
||||
pkgs = []ftypes.Package{
|
||||
{
|
||||
ID: "crate_with_features@0.1.0",
|
||||
Name: "crate_with_features",
|
||||
Version: "0.1.0",
|
||||
Relationship: types.RelationshipRoot,
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
ID: "library_crate@0.1.0",
|
||||
Name: "library_crate",
|
||||
Version: "0.1.0",
|
||||
Relationship: types.RelationshipUnknown,
|
||||
Relationship: ftypes.RelationshipUnknown,
|
||||
},
|
||||
}
|
||||
|
||||
deps = []types.Dependency{
|
||||
deps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "crate_with_features@0.1.0",
|
||||
DependsOn: []string{"library_crate@0.1.0"},
|
||||
@@ -41,26 +41,26 @@ func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []types.Library
|
||||
wantDeps []types.Dependency
|
||||
want []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "ELF",
|
||||
inputFile: "testdata/test.elf",
|
||||
want: libs,
|
||||
want: pkgs,
|
||||
wantDeps: deps,
|
||||
},
|
||||
{
|
||||
name: "PE",
|
||||
inputFile: "testdata/test.exe",
|
||||
want: libs,
|
||||
want: pkgs,
|
||||
wantDeps: deps,
|
||||
},
|
||||
{
|
||||
name: "Mach-O",
|
||||
inputFile: "testdata/test.macho",
|
||||
want: libs,
|
||||
want: pkgs,
|
||||
wantDeps: deps,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -30,13 +29,13 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("cargo"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockfile Lockfile
|
||||
decoder := toml.NewDecoder(r)
|
||||
if _, err := decoder.Decode(&lockfile); err != nil {
|
||||
@@ -52,21 +51,21 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
lineNumIdx := pkgParser.parse()
|
||||
|
||||
// We need to get version for unique dependencies for lockfile v3 from lockfile.Packages
|
||||
pkgs := lo.SliceToMap(lockfile.Packages, func(pkg cargoPkg) (string, cargoPkg) {
|
||||
pkgMap := lo.SliceToMap(lockfile.Packages, func(pkg cargoPkg) (string, cargoPkg) {
|
||||
return pkg.Name, pkg
|
||||
})
|
||||
|
||||
var libs []types.Library
|
||||
var deps []types.Dependency
|
||||
for _, pkg := range lockfile.Packages {
|
||||
pkgID := packageID(pkg.Name, pkg.Version)
|
||||
lib := types.Library{
|
||||
var pkgs ftypes.Packages
|
||||
var deps ftypes.Dependencies
|
||||
for _, lpkg := range lockfile.Packages {
|
||||
pkgID := packageID(lpkg.Name, lpkg.Version)
|
||||
pkg := ftypes.Package{
|
||||
ID: pkgID,
|
||||
Name: pkg.Name,
|
||||
Version: pkg.Version,
|
||||
Name: lpkg.Name,
|
||||
Version: lpkg.Version,
|
||||
}
|
||||
if pos, ok := lineNumIdx[pkgID]; ok {
|
||||
lib.Locations = []types.Location{
|
||||
pkg.Locations = []ftypes.Location{
|
||||
{
|
||||
StartLine: pos.start,
|
||||
EndLine: pos.end,
|
||||
@@ -74,17 +73,17 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
}
|
||||
}
|
||||
|
||||
libs = append(libs, lib)
|
||||
dep := p.parseDependencies(pkgID, pkg, pkgs)
|
||||
pkgs = append(pkgs, pkg)
|
||||
dep := p.parseDependencies(pkgID, lpkg, pkgMap)
|
||||
if dep != nil {
|
||||
deps = append(deps, *dep)
|
||||
}
|
||||
}
|
||||
sort.Sort(types.Libraries(libs))
|
||||
sort.Sort(types.Dependencies(deps))
|
||||
return libs, deps, nil
|
||||
sort.Sort(pkgs)
|
||||
sort.Sort(deps)
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
func (p *Parser) parseDependencies(pkgId string, pkg cargoPkg, pkgs map[string]cargoPkg) *types.Dependency {
|
||||
func (p *Parser) parseDependencies(pkgId string, pkg cargoPkg, pkgs map[string]cargoPkg) *ftypes.Dependency {
|
||||
var dependOn []string
|
||||
|
||||
for _, pkgDep := range pkg.Dependencies {
|
||||
@@ -118,7 +117,7 @@ func (p *Parser) parseDependencies(pkgId string, pkg cargoPkg, pkgs map[string]c
|
||||
}
|
||||
if len(dependOn) > 0 {
|
||||
sort.Strings(dependOn)
|
||||
return &types.Dependency{
|
||||
return &ftypes.Dependency{
|
||||
ID: pkgId,
|
||||
DependsOn: dependOn,
|
||||
}
|
||||
|
||||
@@ -5,77 +5,339 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
var (
|
||||
cargoNormalLibs = []types.Library{
|
||||
{ID: "normal@0.1.0", Name: "normal", Version: "0.1.0", Locations: []types.Location{{StartLine: 8, EndLine: 13}}},
|
||||
{ID: "libc@0.2.54", Name: "libc", Version: "0.2.54", Locations: []types.Location{{StartLine: 3, EndLine: 6}}},
|
||||
{ID: "typemap@0.3.3", Name: "typemap", Version: "0.3.3", Locations: []types.Location{{StartLine: 20, EndLine: 26}}},
|
||||
{ID: "url@1.7.2", Name: "url", Version: "1.7.2", Locations: []types.Location{{StartLine: 43, EndLine: 51}}},
|
||||
{ID: "unsafe-any@0.4.2", Name: "unsafe-any", Version: "0.4.2", Locations: []types.Location{{StartLine: 15, EndLine: 18}}},
|
||||
{ID: "matches@0.1.8", Name: "matches", Version: "0.1.8", Locations: []types.Location{{StartLine: 33, EndLine: 36}}},
|
||||
{ID: "idna@0.1.5", Name: "idna", Version: "0.1.5", Locations: []types.Location{{StartLine: 28, EndLine: 31}}},
|
||||
{ID: "percent-encoding@1.0.1", Name: "percent-encoding", Version: "1.0.1", Locations: []types.Location{{StartLine: 38, EndLine: 41}}},
|
||||
cargoNormalPkgs = []ftypes.Package{
|
||||
{
|
||||
ID: "normal@0.1.0",
|
||||
Name: "normal",
|
||||
Version: "0.1.0",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 8,
|
||||
EndLine: 13,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "libc@0.2.54",
|
||||
Name: "libc",
|
||||
Version: "0.2.54",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 3,
|
||||
EndLine: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "typemap@0.3.3",
|
||||
Name: "typemap",
|
||||
Version: "0.3.3",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 20,
|
||||
EndLine: 26,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "url@1.7.2",
|
||||
Name: "url",
|
||||
Version: "1.7.2",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 43,
|
||||
EndLine: 51,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "unsafe-any@0.4.2",
|
||||
Name: "unsafe-any",
|
||||
Version: "0.4.2",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 15,
|
||||
EndLine: 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "matches@0.1.8",
|
||||
Name: "matches",
|
||||
Version: "0.1.8",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 33,
|
||||
EndLine: 36,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "idna@0.1.5",
|
||||
Name: "idna",
|
||||
Version: "0.1.5",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 28,
|
||||
EndLine: 31,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "percent-encoding@1.0.1",
|
||||
Name: "percent-encoding",
|
||||
Version: "1.0.1",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 38,
|
||||
EndLine: 41,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cargoNormalDeps = []types.Dependency{
|
||||
cargoNormalDeps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "normal@0.1.0",
|
||||
DependsOn: []string{"libc@0.2.54"}},
|
||||
DependsOn: []string{"libc@0.2.54"},
|
||||
},
|
||||
{
|
||||
ID: "typemap@0.3.3",
|
||||
DependsOn: []string{"unsafe-any@0.4.2"},
|
||||
},
|
||||
{
|
||||
ID: "url@1.7.2",
|
||||
DependsOn: []string{"idna@0.1.5", "matches@0.1.8", "percent-encoding@1.0.1"},
|
||||
ID: "url@1.7.2",
|
||||
DependsOn: []string{
|
||||
"idna@0.1.5",
|
||||
"matches@0.1.8",
|
||||
"percent-encoding@1.0.1",
|
||||
},
|
||||
},
|
||||
}
|
||||
cargoMixedLibs = []types.Library{
|
||||
{ID: "normal@0.1.0", Name: "normal", Version: "0.1.0", Locations: []types.Location{{StartLine: 17, EndLine: 22}}},
|
||||
{ID: "libc@0.2.54", Name: "libc", Version: "0.2.54", Locations: []types.Location{{StartLine: 3, EndLine: 6}}},
|
||||
{ID: "typemap@0.3.3", Name: "typemap", Version: "0.3.3", Locations: []types.Location{{StartLine: 55, EndLine: 61}}},
|
||||
{ID: "url@1.7.2", Name: "url", Version: "1.7.2", Locations: []types.Location{{StartLine: 26, EndLine: 34}}},
|
||||
{ID: "unsafe-any@0.4.2", Name: "unsafe-any", Version: "0.4.2", Locations: []types.Location{{StartLine: 9, EndLine: 12}}},
|
||||
{ID: "matches@0.1.8", Name: "matches", Version: "0.1.8", Locations: []types.Location{{StartLine: 41, EndLine: 44}}},
|
||||
{ID: "idna@0.1.5", Name: "idna", Version: "0.1.5", Locations: []types.Location{{StartLine: 36, EndLine: 39}}},
|
||||
{ID: "percent-encoding@1.0.1", Name: "percent-encoding", Version: "1.0.1", Locations: []types.Location{{StartLine: 46, EndLine: 49}}},
|
||||
cargoMixedPkgs = []ftypes.Package{
|
||||
{
|
||||
ID: "normal@0.1.0",
|
||||
Name: "normal",
|
||||
Version: "0.1.0",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 17,
|
||||
EndLine: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "libc@0.2.54",
|
||||
Name: "libc",
|
||||
Version: "0.2.54",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 3,
|
||||
EndLine: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "typemap@0.3.3",
|
||||
Name: "typemap",
|
||||
Version: "0.3.3",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 55,
|
||||
EndLine: 61,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "url@1.7.2",
|
||||
Name: "url",
|
||||
Version: "1.7.2",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 26,
|
||||
EndLine: 34,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "unsafe-any@0.4.2",
|
||||
Name: "unsafe-any",
|
||||
Version: "0.4.2",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 9,
|
||||
EndLine: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "matches@0.1.8",
|
||||
Name: "matches",
|
||||
Version: "0.1.8",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 41,
|
||||
EndLine: 44,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "idna@0.1.5",
|
||||
Name: "idna",
|
||||
Version: "0.1.5",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 36,
|
||||
EndLine: 39,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "percent-encoding@1.0.1",
|
||||
Name: "percent-encoding",
|
||||
Version: "1.0.1",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 46,
|
||||
EndLine: 49,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cargoV3Pkgs = []ftypes.Package{
|
||||
{
|
||||
ID: "aho-corasick@0.7.20",
|
||||
Name: "aho-corasick",
|
||||
Version: "0.7.20",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 5,
|
||||
EndLine: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "app@0.1.0",
|
||||
Name: "app",
|
||||
Version: "0.1.0",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 14,
|
||||
EndLine: 21,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "libc@0.2.140",
|
||||
Name: "libc",
|
||||
Version: "0.2.140",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 23,
|
||||
EndLine: 27,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "memchr@1.0.2",
|
||||
Name: "memchr",
|
||||
Version: "1.0.2",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 29,
|
||||
EndLine: 36,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "memchr@2.5.0",
|
||||
Name: "memchr",
|
||||
Version: "2.5.0",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 38,
|
||||
EndLine: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "regex@1.7.3",
|
||||
Name: "regex",
|
||||
Version: "1.7.3",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 44,
|
||||
EndLine: 53,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "regex-syntax@0.5.6",
|
||||
Name: "regex-syntax",
|
||||
Version: "0.5.6",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 55,
|
||||
EndLine: 62,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "regex-syntax@0.6.29",
|
||||
Name: "regex-syntax",
|
||||
Version: "0.6.29",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 64,
|
||||
EndLine: 68,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "ucd-util@0.1.10",
|
||||
Name: "ucd-util",
|
||||
Version: "0.1.10",
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 70,
|
||||
EndLine: 74,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cargoV3Libs = []types.Library{
|
||||
{ID: "aho-corasick@0.7.20", Name: "aho-corasick", Version: "0.7.20", Locations: []types.Location{{StartLine: 5, EndLine: 12}}},
|
||||
{ID: "app@0.1.0", Name: "app", Version: "0.1.0", Locations: []types.Location{{StartLine: 14, EndLine: 21}}},
|
||||
{ID: "libc@0.2.140", Name: "libc", Version: "0.2.140", Locations: []types.Location{{StartLine: 23, EndLine: 27}}},
|
||||
{ID: "memchr@1.0.2", Name: "memchr", Version: "1.0.2", Locations: []types.Location{{StartLine: 29, EndLine: 36}}},
|
||||
{ID: "memchr@2.5.0", Name: "memchr", Version: "2.5.0", Locations: []types.Location{{StartLine: 38, EndLine: 42}}},
|
||||
{ID: "regex@1.7.3", Name: "regex", Version: "1.7.3", Locations: []types.Location{{StartLine: 44, EndLine: 53}}},
|
||||
{ID: "regex-syntax@0.5.6", Name: "regex-syntax", Version: "0.5.6", Locations: []types.Location{{StartLine: 55, EndLine: 62}}},
|
||||
{ID: "regex-syntax@0.6.29", Name: "regex-syntax", Version: "0.6.29", Locations: []types.Location{{StartLine: 64, EndLine: 68}}},
|
||||
{ID: "ucd-util@0.1.10", Name: "ucd-util", Version: "0.1.10", Locations: []types.Location{{StartLine: 70, EndLine: 74}}},
|
||||
}
|
||||
|
||||
cargoV3Deps = []types.Dependency{
|
||||
cargoV3Deps = []ftypes.Dependency{
|
||||
{
|
||||
ID: "aho-corasick@0.7.20",
|
||||
DependsOn: []string{"memchr@2.5.0"}},
|
||||
DependsOn: []string{"memchr@2.5.0"},
|
||||
},
|
||||
{
|
||||
ID: "app@0.1.0",
|
||||
DependsOn: []string{"memchr@1.0.2", "regex-syntax@0.5.6", "regex@1.7.3"},
|
||||
ID: "app@0.1.0",
|
||||
DependsOn: []string{
|
||||
"memchr@1.0.2",
|
||||
"regex-syntax@0.5.6",
|
||||
"regex@1.7.3",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "memchr@1.0.2",
|
||||
DependsOn: []string{"libc@0.2.140"},
|
||||
},
|
||||
{
|
||||
ID: "regex@1.7.3",
|
||||
DependsOn: []string{"aho-corasick@0.7.20", "memchr@2.5.0", "regex-syntax@0.6.29"},
|
||||
ID: "regex@1.7.3",
|
||||
DependsOn: []string{
|
||||
"aho-corasick@0.7.20",
|
||||
"memchr@2.5.0",
|
||||
"regex-syntax@0.6.29",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "regex-syntax@0.5.6",
|
||||
@@ -87,25 +349,25 @@ var (
|
||||
func TestParse(t *testing.T) {
|
||||
vectors := []struct {
|
||||
file string // Test input file
|
||||
wantLibs []types.Library
|
||||
wantDeps []types.Dependency
|
||||
wantPkgs []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
file: "testdata/cargo_normal.lock",
|
||||
wantLibs: cargoNormalLibs,
|
||||
wantPkgs: cargoNormalPkgs,
|
||||
wantDeps: cargoNormalDeps,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
file: "testdata/cargo_mixed.lock",
|
||||
wantLibs: cargoMixedLibs,
|
||||
wantPkgs: cargoMixedPkgs,
|
||||
wantDeps: cargoNormalDeps,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
file: "testdata/cargo_v3.lock",
|
||||
wantLibs: cargoV3Libs,
|
||||
wantPkgs: cargoV3Pkgs,
|
||||
wantDeps: cargoV3Deps,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
@@ -120,7 +382,7 @@ func TestParse(t *testing.T) {
|
||||
f, err := os.Open(v.file)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotLibs, gotDeps, err := NewParser().Parse(f)
|
||||
gotPkgs, gotDeps, err := NewParser().Parse(f)
|
||||
|
||||
if !v.wantErr(t, err, fmt.Sprintf("Parse(%v)", v.file)) {
|
||||
return
|
||||
@@ -130,23 +392,10 @@ func TestParse(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
sortLibs(v.wantLibs)
|
||||
sortDeps(v.wantDeps)
|
||||
|
||||
assert.Equalf(t, v.wantLibs, gotLibs, "Parse libraries(%v)", v.file)
|
||||
sort.Sort(ftypes.Packages(v.wantPkgs))
|
||||
sort.Sort(ftypes.Dependencies(v.wantDeps))
|
||||
assert.Equalf(t, v.wantPkgs, gotPkgs, "Parse libraries(%v)", v.file)
|
||||
assert.Equalf(t, v.wantDeps, gotDeps, "Parse dependencies(%v)", v.file)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func sortLibs(libs []types.Library) {
|
||||
sort.Slice(libs, func(i, j int) bool {
|
||||
return strings.Compare(libs[i].ID, libs[j].ID) < 0
|
||||
})
|
||||
}
|
||||
|
||||
func sortDeps(deps []types.Dependency) {
|
||||
sort.Slice(deps, func(i, j int) bool {
|
||||
return strings.Compare(deps[i].ID, deps[j].ID) < 0
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -20,7 +19,7 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("cocoapods"),
|
||||
}
|
||||
@@ -30,32 +29,32 @@ type lockFile struct {
|
||||
Pods []any `yaml:"PODS"` // pod can be string or map[string]interface{}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
lock := &lockFile{}
|
||||
decoder := yaml.NewDecoder(r)
|
||||
if err := decoder.Decode(&lock); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to decode cocoapods lock file: %s", err.Error())
|
||||
}
|
||||
|
||||
parsedDeps := make(map[string]types.Library) // dependency name => Library
|
||||
directDeps := make(map[string][]string) // dependency name => slice of child dependency names
|
||||
parsedDeps := make(map[string]ftypes.Package) // dependency name => Package
|
||||
directDeps := make(map[string][]string) // dependency name => slice of child dependency names
|
||||
for _, pod := range lock.Pods {
|
||||
switch dep := pod.(type) {
|
||||
case string: // dependency with version number
|
||||
lib, err := parseDep(dep)
|
||||
pkg, err := parseDep(dep)
|
||||
if err != nil {
|
||||
p.logger.Debug("Dependency parse error", log.Err(err))
|
||||
continue
|
||||
}
|
||||
parsedDeps[lib.Name] = lib
|
||||
parsedDeps[pkg.Name] = pkg
|
||||
case map[string]interface{}: // dependency with its child dependencies
|
||||
for dep, childDeps := range dep {
|
||||
lib, err := parseDep(dep)
|
||||
pkg, err := parseDep(dep)
|
||||
if err != nil {
|
||||
p.logger.Debug("Dependency parse error", log.Err(err))
|
||||
continue
|
||||
}
|
||||
parsedDeps[lib.Name] = lib
|
||||
parsedDeps[pkg.Name] = pkg
|
||||
|
||||
children, ok := childDeps.([]interface{})
|
||||
if !ok {
|
||||
@@ -67,30 +66,30 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
if !ok {
|
||||
return nil, nil, xerrors.Errorf("must be string: %q", childDep)
|
||||
}
|
||||
directDeps[lib.Name] = append(directDeps[lib.Name], strings.Fields(s)[0])
|
||||
directDeps[pkg.Name] = append(directDeps[pkg.Name], strings.Fields(s)[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var deps []types.Dependency
|
||||
var deps ftypes.Dependencies
|
||||
for dep, childDeps := range directDeps {
|
||||
var dependsOn []string
|
||||
// find versions for child dependencies
|
||||
for _, childDep := range childDeps {
|
||||
dependsOn = append(dependsOn, packageID(childDep, parsedDeps[childDep].Version))
|
||||
}
|
||||
deps = append(deps, types.Dependency{
|
||||
deps = append(deps, ftypes.Dependency{
|
||||
ID: parsedDeps[dep].ID,
|
||||
DependsOn: dependsOn,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Sort(types.Dependencies(deps))
|
||||
return utils.UniqueLibraries(maps.Values(parsedDeps)), deps, nil
|
||||
sort.Sort(deps)
|
||||
return utils.UniquePackages(maps.Values(parsedDeps)), deps, nil
|
||||
}
|
||||
|
||||
func parseDep(dep string) (types.Library, error) {
|
||||
func parseDep(dep string) (ftypes.Package, error) {
|
||||
// dep example:
|
||||
// 'AppCenter (4.2.0)'
|
||||
// direct dep examples:
|
||||
@@ -99,18 +98,18 @@ func parseDep(dep string) (types.Library, error) {
|
||||
// 'AppCenter/Analytics (-> 4.2.0)'
|
||||
ss := strings.Split(dep, " (")
|
||||
if len(ss) != 2 {
|
||||
return types.Library{}, xerrors.Errorf("Unable to determine cocoapods dependency: %q", dep)
|
||||
return ftypes.Package{}, xerrors.Errorf("Unable to determine cocoapods dependency: %q", dep)
|
||||
}
|
||||
|
||||
name := ss[0]
|
||||
version := strings.Trim(strings.TrimSpace(ss[1]), "()")
|
||||
lib := types.Library{
|
||||
pkg := ftypes.Package{
|
||||
ID: packageID(name, version),
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
|
||||
return lib, nil
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
func packageID(name, version string) string {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/swift/cocoapods"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -14,13 +14,18 @@ func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string // Test input file
|
||||
wantLibs []types.Library
|
||||
wantDeps []types.Dependency
|
||||
wantPkgs []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
inputFile: "testdata/happy.lock",
|
||||
wantLibs: []types.Library{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "AppCenter@4.2.0",
|
||||
Name: "AppCenter",
|
||||
Version: "4.2.0",
|
||||
},
|
||||
{
|
||||
ID: "AppCenter/Analytics@4.2.0",
|
||||
Name: "AppCenter/Analytics",
|
||||
@@ -36,18 +41,13 @@ func TestParse(t *testing.T) {
|
||||
Name: "AppCenter/Crashes",
|
||||
Version: "4.2.0",
|
||||
},
|
||||
{
|
||||
ID: "AppCenter@4.2.0",
|
||||
Name: "AppCenter",
|
||||
Version: "4.2.0",
|
||||
},
|
||||
{
|
||||
ID: "KeychainAccess@4.2.1",
|
||||
Name: "KeychainAccess",
|
||||
Version: "4.2.1",
|
||||
},
|
||||
},
|
||||
wantDeps: []types.Dependency{
|
||||
wantDeps: []ftypes.Dependency{
|
||||
{
|
||||
ID: "AppCenter/Analytics@4.2.0",
|
||||
DependsOn: []string{
|
||||
@@ -85,10 +85,10 @@ func TestParse(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
gotLibs, gotDeps, err := cocoapods.NewParser().Parse(f)
|
||||
gotPkgs, gotDeps, err := cocoapods.NewParser().Parse(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.wantLibs, gotLibs)
|
||||
assert.Equal(t, tt.wantPkgs, gotPkgs)
|
||||
assert.Equal(t, tt.wantDeps, gotDeps)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -21,13 +20,13 @@ type Parser struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewParser() types.Parser {
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("swift"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
|
||||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
||||
var lockFile LockFile
|
||||
input, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
@@ -37,13 +36,13 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
return nil, nil, xerrors.Errorf("decode error: %w", err)
|
||||
}
|
||||
|
||||
var libs types.Libraries
|
||||
var pkgs ftypes.Packages
|
||||
pins := lockFile.Object.Pins
|
||||
if lockFile.Version > 1 {
|
||||
pins = lockFile.Pins
|
||||
}
|
||||
for _, pin := range pins {
|
||||
name := libraryName(pin, lockFile.Version)
|
||||
name := pkgName(pin, lockFile.Version)
|
||||
|
||||
// Skip packages for which we cannot resolve the version
|
||||
if pin.State.Version == "" && pin.State.Branch == "" {
|
||||
@@ -55,11 +54,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
// e.g. https://github.com/element-hq/element-ios/blob/6a9bcc88ea37147efba8f0a7bcf3ec187f4a4011/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved#L84-L92
|
||||
version := lo.Ternary(pin.State.Version != "", pin.State.Version, pin.State.Branch)
|
||||
|
||||
libs = append(libs, types.Library{
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
ID: dependency.ID(ftypes.Swift, name, version),
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: pin.StartLine,
|
||||
EndLine: pin.EndLine,
|
||||
@@ -67,11 +66,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
|
||||
},
|
||||
})
|
||||
}
|
||||
sort.Sort(libs)
|
||||
return libs, nil, nil
|
||||
sort.Sort(pkgs)
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func libraryName(pin Pin, lockVersion int) string {
|
||||
func pkgName(pin Pin, lockVersion int) string {
|
||||
// Package.resolved v1 uses `RepositoryURL`
|
||||
// v2 uses `Location`
|
||||
name := pin.RepositoryURL
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package swift
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -11,7 +11,7 @@ func TestParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []types.Library
|
||||
want []ftypes.Package
|
||||
}{
|
||||
// docker run -it --rm swift@sha256:3c62ac97506ecf19ca15e4db57d7930e6a71559b23b19aa57e13d380133a54db
|
||||
// mkdir app && cd app
|
||||
@@ -22,60 +22,60 @@ func TestParser_Parse(t *testing.T) {
|
||||
{
|
||||
name: "happy path v1",
|
||||
inputFile: "testdata/happy-v1-Package.resolved",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/Quick/Nimble@9.2.1",
|
||||
Name: "github.com/Quick/Nimble",
|
||||
Version: "9.2.1",
|
||||
Locations: []types.Location{{StartLine: 4, EndLine: 12}},
|
||||
Locations: []ftypes.Location{{StartLine: 4, EndLine: 12}},
|
||||
},
|
||||
{
|
||||
ID: "github.com/ReactiveCocoa/ReactiveSwift@7.1.1",
|
||||
Name: "github.com/ReactiveCocoa/ReactiveSwift",
|
||||
Version: "7.1.1",
|
||||
Locations: []types.Location{{StartLine: 13, EndLine: 21}},
|
||||
Locations: []ftypes.Location{{StartLine: 13, EndLine: 21}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path v2",
|
||||
inputFile: "testdata/happy-v2-Package.resolved",
|
||||
want: []types.Library{
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/Quick/Nimble@9.2.1",
|
||||
Name: "github.com/Quick/Nimble",
|
||||
Version: "9.2.1",
|
||||
Locations: []types.Location{{StartLine: 21, EndLine: 29}},
|
||||
Locations: []ftypes.Location{{StartLine: 21, EndLine: 29}},
|
||||
},
|
||||
{
|
||||
ID: "github.com/Quick/Quick@7.2.0",
|
||||
Name: "github.com/Quick/Quick",
|
||||
Version: "7.2.0",
|
||||
Locations: []types.Location{{StartLine: 30, EndLine: 38}},
|
||||
Locations: []ftypes.Location{{StartLine: 30, EndLine: 38}},
|
||||
},
|
||||
{
|
||||
ID: "github.com/ReactiveCocoa/ReactiveSwift@7.1.1",
|
||||
Name: "github.com/ReactiveCocoa/ReactiveSwift",
|
||||
Version: "7.1.1",
|
||||
Locations: []types.Location{{StartLine: 39, EndLine: 47}},
|
||||
Locations: []ftypes.Location{{StartLine: 39, EndLine: 47}},
|
||||
},
|
||||
{
|
||||
ID: "github.com/element-hq/swift-ogg@0.0.1",
|
||||
Name: "github.com/element-hq/swift-ogg",
|
||||
Version: "0.0.1",
|
||||
Locations: []types.Location{{StartLine: 48, EndLine: 56}},
|
||||
Locations: []ftypes.Location{{StartLine: 48, EndLine: 56}},
|
||||
},
|
||||
{
|
||||
ID: "github.com/mattgallagher/CwlCatchException@2.1.2",
|
||||
Name: "github.com/mattgallagher/CwlCatchException",
|
||||
Version: "2.1.2",
|
||||
Locations: []types.Location{{StartLine: 3, EndLine: 11}},
|
||||
Locations: []ftypes.Location{{StartLine: 3, EndLine: 11}},
|
||||
},
|
||||
{
|
||||
ID: "github.com/mattgallagher/CwlPreconditionTesting@2.1.2",
|
||||
Name: "github.com/mattgallagher/CwlPreconditionTesting",
|
||||
Version: "2.1.2",
|
||||
Locations: []types.Location{{StartLine: 12, EndLine: 20}},
|
||||
Locations: []ftypes.Location{{StartLine: 12, EndLine: 20}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -92,9 +92,9 @@ func TestParser_Parse(t *testing.T) {
|
||||
f, err := os.Open(tt.inputFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
libs, _, err := parser.Parse(f)
|
||||
gotPkgs, _, err := parser.Parse(f)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, libs)
|
||||
assert.Equal(t, tt.want, gotPkgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func UniqueStrings(ss []string) []string {
|
||||
@@ -22,36 +22,36 @@ func UniqueStrings(ss []string) []string {
|
||||
return results
|
||||
}
|
||||
|
||||
func UniqueLibraries(libs []types.Library) []types.Library {
|
||||
if len(libs) == 0 {
|
||||
func UniquePackages(pkgs []ftypes.Package) []ftypes.Package {
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
unique := make(map[string]types.Library)
|
||||
for _, lib := range libs {
|
||||
identifier := fmt.Sprintf("%s@%s", lib.Name, lib.Version)
|
||||
unique := make(map[string]ftypes.Package)
|
||||
for _, pkg := range pkgs {
|
||||
identifier := fmt.Sprintf("%s@%s", pkg.Name, pkg.Version)
|
||||
if l, ok := unique[identifier]; !ok {
|
||||
unique[identifier] = lib
|
||||
unique[identifier] = pkg
|
||||
} else {
|
||||
// There are times when we get 2 same libraries as root and dev dependencies.
|
||||
// There are times when we get 2 same packages as root and dev dependencies.
|
||||
// https://github.com/aquasecurity/trivy/issues/5532
|
||||
// In these cases, we need to mark the dependency as a root dependency.
|
||||
if !lib.Dev {
|
||||
l.Dev = lib.Dev
|
||||
if !pkg.Dev {
|
||||
l.Dev = pkg.Dev
|
||||
unique[identifier] = l
|
||||
}
|
||||
|
||||
if len(lib.Locations) > 0 {
|
||||
if len(pkg.Locations) > 0 {
|
||||
// merge locations
|
||||
l.Locations = append(l.Locations, lib.Locations...)
|
||||
l.Locations = append(l.Locations, pkg.Locations...)
|
||||
sort.Sort(l.Locations)
|
||||
unique[identifier] = l
|
||||
}
|
||||
}
|
||||
}
|
||||
libSlice := maps.Values(unique)
|
||||
sort.Sort(types.Libraries(libSlice))
|
||||
pkgSlice := maps.Values(unique)
|
||||
sort.Sort(ftypes.Packages(pkgSlice))
|
||||
|
||||
return libSlice
|
||||
return pkgSlice
|
||||
}
|
||||
|
||||
func MergeMaps(parent, child map[string]string) map[string]string {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
@@ -9,17 +9,17 @@ import (
|
||||
func TestUniqueLibraries(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
libs []types.Library
|
||||
wantLibs []types.Library
|
||||
pkgs []ftypes.Package
|
||||
wantPkgs []ftypes.Package
|
||||
}{
|
||||
{
|
||||
name: "happy path merge locations",
|
||||
libs: []types.Library{
|
||||
pkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
Version: "0.2.6",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 10,
|
||||
EndLine: 14,
|
||||
@@ -30,7 +30,7 @@ func TestUniqueLibraries(t *testing.T) {
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
Version: "0.2.6",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 24,
|
||||
EndLine: 30,
|
||||
@@ -38,12 +38,12 @@ func TestUniqueLibraries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantLibs: []types.Library{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
Version: "0.2.6",
|
||||
Locations: []types.Location{
|
||||
Locations: []ftypes.Location{
|
||||
{
|
||||
StartLine: 10,
|
||||
EndLine: 14,
|
||||
@@ -58,7 +58,7 @@ func TestUniqueLibraries(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy path Dev and Root deps",
|
||||
libs: []types.Library{
|
||||
pkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
@@ -72,7 +72,7 @@ func TestUniqueLibraries(t *testing.T) {
|
||||
Dev: false,
|
||||
},
|
||||
},
|
||||
wantLibs: []types.Library{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
@@ -83,7 +83,7 @@ func TestUniqueLibraries(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy path Root and Dev deps",
|
||||
libs: []types.Library{
|
||||
pkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
@@ -97,7 +97,7 @@ func TestUniqueLibraries(t *testing.T) {
|
||||
Dev: true,
|
||||
},
|
||||
},
|
||||
wantLibs: []types.Library{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "asn1@0.2.6",
|
||||
Name: "asn1",
|
||||
@@ -110,8 +110,8 @@ func TestUniqueLibraries(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotLibs := UniqueLibraries(tt.libs)
|
||||
require.Equal(t, tt.wantLibs, gotLibs)
|
||||
gotPkgs := UniquePackages(tt.pkgs)
|
||||
require.Equal(t, tt.wantPkgs, gotPkgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type Library struct {
|
||||
ID string `json:",omitempty"`
|
||||
Name string
|
||||
Version string
|
||||
Dev bool `json:",omitempty"`
|
||||
Relationship Relationship `json:",omitempty"`
|
||||
License string `json:",omitempty"`
|
||||
ExternalReferences []ExternalRef `json:",omitempty"`
|
||||
Locations Locations `json:",omitempty"`
|
||||
FilePath string `json:",omitempty"` // Required to show nested jars
|
||||
}
|
||||
|
||||
type Libraries []Library
|
||||
|
||||
func (libs Libraries) Len() int { return len(libs) }
|
||||
func (libs Libraries) Less(i, j int) bool {
|
||||
switch {
|
||||
case libs[i].Relationship != libs[j].Relationship:
|
||||
if libs[i].Relationship == RelationshipUnknown {
|
||||
return false
|
||||
} else if libs[j].Relationship == RelationshipUnknown {
|
||||
return true
|
||||
}
|
||||
return libs[i].Relationship < libs[j].Relationship
|
||||
case libs[i].ID != libs[j].ID: // ID could be empty
|
||||
return libs[i].ID < libs[j].ID
|
||||
case libs[i].Name != libs[j].Name: // Name could be the same
|
||||
return libs[i].Name < libs[j].Name
|
||||
}
|
||||
return libs[i].Version < libs[j].Version
|
||||
}
|
||||
func (libs Libraries) Swap(i, j int) { libs[i], libs[j] = libs[j], libs[i] }
|
||||
|
||||
// Location in lock file
|
||||
type Location struct {
|
||||
StartLine int `json:",omitempty"`
|
||||
EndLine int `json:",omitempty"`
|
||||
}
|
||||
|
||||
type Locations []Location
|
||||
|
||||
func (locs Locations) Len() int { return len(locs) }
|
||||
func (locs Locations) Less(i, j int) bool {
|
||||
return locs[i].StartLine < locs[j].StartLine
|
||||
}
|
||||
func (locs Locations) Swap(i, j int) { locs[i], locs[j] = locs[j], locs[i] }
|
||||
|
||||
type ExternalRef struct {
|
||||
Type RefType
|
||||
URL string
|
||||
}
|
||||
|
||||
type Dependency struct {
|
||||
ID string
|
||||
DependsOn []string
|
||||
}
|
||||
|
||||
type Dependencies []Dependency
|
||||
|
||||
func (deps Dependencies) Len() int { return len(deps) }
|
||||
func (deps Dependencies) Less(i, j int) bool {
|
||||
return deps[i].ID < deps[j].ID
|
||||
}
|
||||
func (deps Dependencies) Swap(i, j int) { deps[i], deps[j] = deps[j], deps[i] }
|
||||
|
||||
type Parser interface {
|
||||
// Parse parses the dependency file
|
||||
Parse(r xio.ReadSeekerAt) ([]Library, []Dependency, error)
|
||||
}
|
||||
|
||||
type RefType string
|
||||
|
||||
const (
|
||||
RefVCS RefType = "vcs"
|
||||
RefOther RefType = "other"
|
||||
)
|
||||
|
||||
type Relationship int
|
||||
|
||||
const (
|
||||
RelationshipUnknown Relationship = iota
|
||||
RelationshipRoot
|
||||
RelationshipDirect
|
||||
RelationshipIndirect
|
||||
)
|
||||
|
||||
var relationshipNames = [...]string{
|
||||
"unknown",
|
||||
"root",
|
||||
"direct",
|
||||
"indirect",
|
||||
}
|
||||
|
||||
func (r Relationship) String() string {
|
||||
if r <= RelationshipUnknown || int(r) >= len(relationshipNames) {
|
||||
return "unknown"
|
||||
}
|
||||
return relationshipNames[r]
|
||||
}
|
||||
|
||||
func (r Relationship) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(r.String())
|
||||
}
|
||||
|
||||
func (r *Relationship) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
for i, name := range relationshipNames {
|
||||
if s == name {
|
||||
*r = Relationship(i)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return xerrors.Errorf("invalid relationship (%s)", s)
|
||||
}
|
||||
@@ -202,7 +202,7 @@ func (r *AnalysisResult) Sort() {
|
||||
})
|
||||
|
||||
for _, app := range r.Applications {
|
||||
sort.Sort(app.Libraries)
|
||||
sort.Sort(app.Packages)
|
||||
}
|
||||
|
||||
// Custom resources
|
||||
@@ -475,12 +475,12 @@ func (ag AnalyzerGroup) PostAnalyze(ctx context.Context, compositeFS *CompositeF
|
||||
skippedFiles := result.SystemInstalledFiles
|
||||
for _, app := range result.Applications {
|
||||
skippedFiles = append(skippedFiles, app.FilePath)
|
||||
for _, lib := range app.Libraries {
|
||||
for _, pkg := range app.Packages {
|
||||
// The analysis result could contain packages listed in SBOM.
|
||||
// The files of those packages don't have to be analyzed.
|
||||
// This is especially helpful for expensive post-analyzers such as the JAR analyzer.
|
||||
if lib.FilePath != "" {
|
||||
skippedFiles = append(skippedFiles, lib.FilePath)
|
||||
if pkg.FilePath != "" {
|
||||
skippedFiles = append(skippedFiles, pkg.FilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func TestAnalysisResult_Merge(t *testing.T) {
|
||||
{
|
||||
Type: "bundler",
|
||||
FilePath: "app/Gemfile.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
Name: "rails",
|
||||
Version: "5.0.0",
|
||||
@@ -95,7 +95,7 @@ func TestAnalysisResult_Merge(t *testing.T) {
|
||||
{
|
||||
Type: "bundler",
|
||||
FilePath: "app2/Gemfile.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
Name: "nokogiri",
|
||||
Version: "1.0.0",
|
||||
@@ -134,7 +134,7 @@ func TestAnalysisResult_Merge(t *testing.T) {
|
||||
{
|
||||
Type: "bundler",
|
||||
FilePath: "app/Gemfile.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
Name: "rails",
|
||||
Version: "5.0.0",
|
||||
@@ -144,7 +144,7 @@ func TestAnalysisResult_Merge(t *testing.T) {
|
||||
{
|
||||
Type: "bundler",
|
||||
FilePath: "app2/Gemfile.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
Name: "nokogiri",
|
||||
Version: "1.0.0",
|
||||
@@ -378,7 +378,7 @@ func TestAnalyzerGroup_AnalyzeFile(t *testing.T) {
|
||||
{
|
||||
Type: "bundler",
|
||||
FilePath: "/app/Gemfile.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
ID: "actioncable@5.2.3",
|
||||
Name: "actioncable",
|
||||
@@ -441,7 +441,7 @@ func TestAnalyzerGroup_AnalyzeFile(t *testing.T) {
|
||||
{
|
||||
Type: "bundler",
|
||||
FilePath: "/app/Gemfile-dev.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
ID: "actioncable@5.2.3",
|
||||
Name: "actioncable",
|
||||
@@ -576,7 +576,7 @@ func TestAnalyzerGroup_PostAnalyze(t *testing.T) {
|
||||
{
|
||||
Type: types.Jar,
|
||||
FilePath: "testdata/post-apps/jar/jackson-annotations-2.15.0-rc2.jar",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
Name: "com.fasterxml.jackson.core:jackson-annotations",
|
||||
Version: "2.15.0-rc2",
|
||||
@@ -596,7 +596,7 @@ func TestAnalyzerGroup_PostAnalyze(t *testing.T) {
|
||||
{
|
||||
Type: types.Poetry,
|
||||
FilePath: "testdata/post-apps/poetry/happy/poetry.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
ID: "certifi@2022.12.7",
|
||||
Name: "certifi",
|
||||
|
||||
@@ -2,11 +2,9 @@ package language
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
"github.com/aquasecurity/trivy/pkg/digest"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
@@ -15,8 +13,13 @@ import (
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
)
|
||||
|
||||
type Parser interface {
|
||||
// Parse parses the dependency file
|
||||
Parse(r xio.ReadSeekerAt) ([]types.Package, []types.Dependency, error)
|
||||
}
|
||||
|
||||
// Analyze returns an analysis result of the lock file
|
||||
func Analyze(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser godeptypes.Parser) (*analyzer.AnalysisResult, error) {
|
||||
func Analyze(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser Parser) (*analyzer.AnalysisResult, error) {
|
||||
app, err := Parse(fileType, filePath, r, parser)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
|
||||
@@ -30,7 +33,7 @@ func Analyze(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parse
|
||||
}
|
||||
|
||||
// AnalyzePackage returns an analysis result of the package file other than lock files
|
||||
func AnalyzePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser godeptypes.Parser, checksum bool) (*analyzer.AnalysisResult, error) {
|
||||
func AnalyzePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser Parser, checksum bool) (*analyzer.AnalysisResult, error) {
|
||||
app, err := ParsePackage(fileType, filePath, r, parser, checksum)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
|
||||
@@ -44,24 +47,24 @@ func AnalyzePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt
|
||||
}
|
||||
|
||||
// Parse returns a parsed result of the lock file
|
||||
func Parse(fileType types.LangType, filePath string, r io.Reader, parser godeptypes.Parser) (*types.Application, error) {
|
||||
func Parse(fileType types.LangType, filePath string, r io.Reader, parser Parser) (*types.Application, error) {
|
||||
rr, err := xio.NewReadSeekerAt(r)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("reader error: %w", err)
|
||||
}
|
||||
parsedLibs, parsedDependencies, err := parser.Parse(rr)
|
||||
parsedPkgs, parsedDependencies, err := parser.Parse(rr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
|
||||
}
|
||||
|
||||
// The file path of each library should be empty in case of dependency list such as lock file
|
||||
// since they all will be the same path.
|
||||
return toApplication(fileType, filePath, "", nil, parsedLibs, parsedDependencies), nil
|
||||
return toApplication(fileType, filePath, "", nil, parsedPkgs, parsedDependencies), nil
|
||||
}
|
||||
|
||||
// ParsePackage returns a parsed result of the package file
|
||||
func ParsePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser godeptypes.Parser, checksum bool) (*types.Application, error) {
|
||||
parsedLibs, parsedDependencies, err := parser.Parse(r)
|
||||
func ParsePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser Parser, checksum bool) (*types.Application, error) {
|
||||
parsedPkgs, parsedDependencies, err := parser.Parse(r)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
|
||||
}
|
||||
@@ -73,11 +76,11 @@ func ParsePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt,
|
||||
|
||||
// The file path of each library should be empty in case of dependency list such as lock file
|
||||
// since they all will be the same path.
|
||||
return toApplication(fileType, filePath, filePath, r, parsedLibs, parsedDependencies), nil
|
||||
return toApplication(fileType, filePath, filePath, r, parsedPkgs, parsedDependencies), nil
|
||||
}
|
||||
|
||||
func toApplication(fileType types.LangType, filePath, libFilePath string, r xio.ReadSeekerAt, libs []godeptypes.Library, depGraph []godeptypes.Dependency) *types.Application {
|
||||
if len(libs) == 0 {
|
||||
func toApplication(fileType types.LangType, filePath, libFilePath string, r xio.ReadSeekerAt, pkgs []types.Package, depGraph []types.Dependency) *types.Application {
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -92,50 +95,24 @@ func toApplication(fileType types.LangType, filePath, libFilePath string, r xio.
|
||||
deps[dep.ID] = dep.DependsOn
|
||||
}
|
||||
|
||||
var pkgs []types.Package
|
||||
for _, lib := range libs {
|
||||
var licenses []string
|
||||
if lib.License != "" {
|
||||
licenses = licensing.SplitLicenses(lib.License)
|
||||
for i, license := range licenses {
|
||||
licenses[i] = licensing.Normalize(strings.TrimSpace(license))
|
||||
}
|
||||
}
|
||||
var locs []types.Location
|
||||
for _, loc := range lib.Locations {
|
||||
l := types.Location{
|
||||
StartLine: loc.StartLine,
|
||||
EndLine: loc.EndLine,
|
||||
}
|
||||
locs = append(locs, l)
|
||||
}
|
||||
|
||||
for i, pkg := range pkgs {
|
||||
// This file path is populated for virtual file paths within archives, such as nested JAR files.
|
||||
libPath := libFilePath
|
||||
if lib.FilePath != "" {
|
||||
libPath = lib.FilePath
|
||||
if pkg.FilePath == "" {
|
||||
pkgs[i].FilePath = libFilePath
|
||||
}
|
||||
pkgs[i].DependsOn = deps[pkg.ID]
|
||||
pkgs[i].Digest = d
|
||||
pkgs[i].Indirect = isIndirect(pkg.Relationship) // For backward compatibility
|
||||
|
||||
newPkg := types.Package{
|
||||
ID: lib.ID,
|
||||
Name: lib.Name,
|
||||
Version: lib.Version,
|
||||
Dev: lib.Dev,
|
||||
FilePath: libPath,
|
||||
Indirect: isIndirect(lib.Relationship), // For backward compatibility
|
||||
Relationship: lib.Relationship,
|
||||
Licenses: licenses,
|
||||
DependsOn: deps[lib.ID],
|
||||
Locations: locs,
|
||||
Digest: d,
|
||||
for j, license := range pkg.Licenses {
|
||||
pkgs[i].Licenses[j] = licensing.Normalize(license)
|
||||
}
|
||||
pkgs = append(pkgs, newPkg)
|
||||
}
|
||||
|
||||
return &types.Application{
|
||||
Type: fileType,
|
||||
FilePath: filePath,
|
||||
Libraries: pkgs,
|
||||
Type: fileType,
|
||||
FilePath: filePath,
|
||||
Packages: pkgs,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
@@ -20,13 +19,13 @@ type mockParser struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (p *mockParser) Parse(r xio.ReadSeekerAt) ([]godeptypes.Library, []godeptypes.Dependency, error) {
|
||||
func (p *mockParser) Parse(r xio.ReadSeekerAt) ([]types.Package, []types.Dependency, error) {
|
||||
b, err := io.ReadAll(r)
|
||||
require.NoError(p.t, err)
|
||||
|
||||
switch string(b) {
|
||||
case "happy":
|
||||
return []godeptypes.Library{
|
||||
return []types.Package{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.2.3",
|
||||
@@ -63,7 +62,7 @@ func TestAnalyze(t *testing.T) {
|
||||
{
|
||||
Type: types.GoBinary,
|
||||
FilePath: "app/myweb",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.2.3",
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency/parser/c/conan"
|
||||
godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
@@ -33,7 +32,7 @@ const (
|
||||
// conanLockAnalyzer analyzes conan.lock
|
||||
type conanLockAnalyzer struct {
|
||||
logger *log.Logger
|
||||
parser godeptypes.Parser
|
||||
parser language.Parser
|
||||
}
|
||||
|
||||
func newConanLockAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
|
||||
@@ -65,15 +64,15 @@ func (a conanLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAna
|
||||
}
|
||||
|
||||
// Fill licenses
|
||||
for i, lib := range app.Libraries {
|
||||
for i, lib := range app.Packages {
|
||||
if license, ok := licenses[lib.Name]; ok {
|
||||
app.Libraries[i].Licenses = []string{
|
||||
app.Packages[i].Licenses = []string{
|
||||
license,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(app.Libraries)
|
||||
sort.Sort(app.Packages)
|
||||
apps = append(apps, *app)
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
||||
@@ -27,7 +27,7 @@ func Test_conanLockAnalyzer_Analyze(t *testing.T) {
|
||||
{
|
||||
Type: types.Conan,
|
||||
FilePath: "conan.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
ID: "openssl/3.0.5",
|
||||
Name: "openssl",
|
||||
@@ -70,7 +70,7 @@ func Test_conanLockAnalyzer_Analyze(t *testing.T) {
|
||||
{
|
||||
Type: types.Conan,
|
||||
FilePath: "conan.lock",
|
||||
Libraries: types.Packages{
|
||||
Packages: types.Packages{
|
||||
{
|
||||
ID: "openssl/3.0.5",
|
||||
Name: "openssl",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user