Compare commits

..

18 Commits

Author SHA1 Message Date
DmitriyLewen
9aa9e173bf ci: use tmp dir inside Trivy repo dir for GoReleaser (#6533) 2024-04-22 17:24:10 +04:00
dependabot[bot]
058f4839db chore(deps): bump golang.org/x/net from 0.21.0 to 0.23.0 (#6526)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: knqyf263 <knqyf263@gmail.com>
2024-04-22 12:54:46 +04:00
l-qing
9e3d2c5f95 chore(deps): bump github.com/hashicorp/go-getter from 1.7.3 to 1.7.4 (#6523) 2024-04-22 12:52:20 +04:00
DmitriyLewen
2ad8e332e8 fix(java): update logic to detect pom.xml file snapshot artifacts from remote repositories (#6412) 2024-04-22 12:51:14 +04:00
DmitriyLewen
5f69937cc6 fix(sbom): fix error when parent of SPDX Relationships is not a package. (#6399) 2024-03-27 07:07:12 +00:00
DmitriyLewen
258d153461 fix(nodejs): merge Indirect, Dev, ExternalReferences fields for same deps from package-lock.json files v2 or later (#6356)
Signed-off-by: knqyf263 <knqyf263@gmail.com>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2024-03-27 06:08:58 +00:00
DmitriyLewen
ade033a837 docs: add info about support for package license detection in fs/repo modes (#6381) 2024-03-27 05:51:09 +00:00
DmitriyLewen
f85c9fac6f fix(nodejs): add support for parsing workspaces from package.json as an object (#6231)
Co-authored-by: Teppei Fukuda <knqyf263@gmail.com>
2024-03-27 05:46:25 +00:00
DmitriyLewen
9d7f5c948e fix: use 0600 perms for tmp files for post analyzers (#6386) 2024-03-27 05:32:22 +00:00
Nikita Pivkin
f148eb10f2 fix(helm): scan the subcharts once (#6382) 2024-03-26 17:10:16 +00:00
Nikita Pivkin
97f95c4ddf docs(terraform): add file patterns for Terraform Plan (#6393) 2024-03-26 17:04:40 +00:00
Nikita Pivkin
abd62ae74e fix(terraform): сhecking SSE encryption algorithm validity (#6341) 2024-03-26 03:31:28 +00:00
DmitriyLewen
7c409fd270 fix(java): parse modules from pom.xml files once (#6312) 2024-03-24 09:57:32 +00:00
dependabot[bot]
1b68327b65 chore(deps): bump github.com/docker/docker from 25.0.3+incompatible to 25.0.5+incompatible (#6364)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-24 09:55:08 +00:00
DmitriyLewen
a2482c14e1 fix(server): add Locations for Packages in client/server mode (#6366) 2024-03-24 09:46:56 +00:00
DmitriyLewen
e866bd5b5d fix(sbom): add check for CreationInfo to nil when detecting SPDX created using Trivy (#6346) 2024-03-24 09:45:45 +00:00
DmitriyLewen
1870f28461 fix(report): don't include empty strings in .vulnerabilities[].identifiers[].url when gitlab.tpl is used (#6348) 2024-03-24 09:44:40 +00:00
Stefan Mayr
6c81e5505e chore(ubuntu): Add Ubuntu 22.04 EOL date (#6371) 2024-03-24 07:26:49 +00:00
43 changed files with 1420 additions and 591 deletions

View File

@@ -90,6 +90,11 @@ jobs:
run: |
echo "$GPG_KEY" > gpg.key
# Create tmp dir for GoReleaser
- name: "create tmp dir"
run: |
mkdir tmp
- name: GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
@@ -99,6 +104,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.ORG_REPO_TOKEN }}
NFPM_DEFAULT_RPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
GPG_FILE: "gpg.key"
TMPDIR: "tmp"
- name: "remove gpg key"
run: |

View File

@@ -73,8 +73,11 @@
{{- /* TODO: Type not extractable - https://github.com/aquasecurity/trivy-db/pull/24 */}}
"type": "cve",
"name": "{{ .VulnerabilityID }}",
"value": "{{ .VulnerabilityID }}",
"value": "{{ .VulnerabilityID }}"
{{- /* cf. https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/e3d280d7f0862ca66a1555ea8b24016a004bb914/dist/container-scanning-report-format.json#L157-179 */}}
{{- if .PrimaryURL | regexMatch "^(https?|ftp)://.+" -}},
"url": "{{ .PrimaryURL }}"
{{- end }}
}
],
"links": [
@@ -85,9 +88,13 @@
{{- else -}}
,
{{- end -}}
{{- if . | regexMatch "^(https?|ftp)://.+" -}}
{
"url": "{{ regexFind "[^ ]+" . }}"
"url": "{{ . }}"
}
{{- else -}}
{{- $l_first = true }}
{{- end -}}
{{- end }}
]
}

View File

@@ -8,14 +8,15 @@ Trivy scans Infrastructure as Code (IaC) files for
## Supported configurations
| Config type | File patterns |
|-------------------------------------|-------------------------------|
| [Kubernetes](kubernetes.md) | *.yml, *.yaml, *.json |
| [Docker](docker.md) | Dockerfile, Containerfile |
| [Terraform](terraform.md) | *.tf, *.tf.json, *.tfvars, |
| [CloudFormation](cloudformation.md) | *.yml, *.yaml, *.json |
| [Azure ARM Template](azure-arm.md) | *.json |
| [Helm](helm.md) | *.yaml, *.tpl, *.tar.gz, etc. |
| Config type | File patterns |
|-------------------------------------|-----------------------------------------------|
| [Kubernetes](kubernetes.md) | \*.yml, \*.yaml, \*.json |
| [Docker](docker.md) | Dockerfile, Containerfile |
| [Terraform](terraform.md) | \*.tf, \*.tf.json, \*.tfvars |
| [Terraform Plan](terraform.md) | tfplan, \*.tfplan, \*.tfplan.json, \*.tf.json |
| [CloudFormation](cloudformation.md) | \*.yml, \*.yaml, \*.json |
| [Azure ARM Template](azure-arm.md) | \*.json |
| [Helm](helm.md) | \*.yaml, \*.tpl, \*.tar.gz, etc. |
[misconf]: ../../scanner/misconfiguration/index.md
[secret]: ../../scanner/secret.md

View File

@@ -42,7 +42,19 @@ Trivy parses your `pom.xml` file and tries to find files with dependencies from
- relativePath field[^5]
- local repository directory[^6].
If your machine doesn't have the necessary files - Trivy tries to find the information about these dependencies in the [maven repository](https://repo.maven.apache.org/maven2/).
### remote repositories
If your machine doesn't have the necessary files - Trivy tries to find the information about these dependencies in the remote repositories:
- [repositories from pom files][maven-pom-repos]
- [maven central repository][maven-central]
Trivy reproduces Maven's repository selection and priority:
- for snapshot artifacts:
- check only snapshot repositories from pom files (if exists)
- for other artifacts:
- check release repositories from pom files (if exists)
- check [maven central][maven-central]
!!! Note
Trivy only takes information about packages. We don't take a list of vulnerabilities for packages from the `maven repository`.
@@ -92,4 +104,6 @@ Make sure that you have cache[^8] directory to find licenses from `*.pom` depend
[^8]: The supported directories are `$GRADLE_USER_HOME/caches` and `$HOME/.gradle/caches` (`%HOMEPATH%\.gradle\caches` for Windows).
[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
[maven-invoker-plugin]: https://maven.apache.org/plugins/maven-invoker-plugin/usage.html
[maven-invoker-plugin]: https://maven.apache.org/plugins/maven-invoker-plugin/usage.html
[maven-central]: https://repo.maven.apache.org/maven2/
[maven-pom-repos]: https://maven.apache.org/settings.html#repositories

View File

@@ -22,17 +22,15 @@ Check out [the coverage document][coverage] for details.
To enable extended license scanning, you can use `--license-full`.
In addition to package licenses, Trivy scans source code files, Markdown documents, text files and `LICENSE` documents to identify license usage within the image or filesystem.
By default, Trivy only classifies licenses that are matched with a confidence level of 0.9 or more by the classifer.
By default, Trivy only classifies licenses that are matched with a confidence level of 0.9 or more by the classifier.
To configure the confidence level, you can use `--license-confidence-level`. This enables us to classify licenses that might be matched with a lower confidence level by the classifer.
!!! note
The full license scanning is expensive. It takes a while.
Currently, the standard license scanning doesn't support filesystem and repository scanning.
| License scanning | Image | Rootfs | Filesystem | Repository | SBOM |
|:---------------------:|:-----:|:------:|:----------:|:----------:|:----:|
| Standard | ✅ | ✅ | - | - | ✅ |
| Standard | ✅ | ✅ | ✅[^1][^2] | ✅[^1][^2] | ✅ |
| Full (--license-full) | ✅ | ✅ | ✅ | ✅ | - |
License checking classifies the identified licenses and map the classification to severity.
@@ -344,6 +342,8 @@ license:
permissive: []
```
[^1]: See the list of supported language files [here](../coverage/language/index.md).
[^2]: Some lock files require additional files (e.g. files from the cache directory) to detect licenses. Check [coverage][coverage] for more information.
[coverage]: ../coverage/index.md
[google-license-classification]: https://opensource.google/documentation/reference/thirdparty/licenses

16
go.mod
View File

@@ -40,7 +40,7 @@ require (
github.com/cheggaaa/pb/v3 v3.1.4
github.com/containerd/containerd v1.7.13
github.com/csaf-poc/csaf_distribution/v3 v3.0.0
github.com/docker/docker v25.0.3+incompatible
github.com/docker/docker v25.0.5+incompatible
github.com/docker/go-connections v0.5.0
github.com/fatih/color v1.16.0
github.com/go-git/go-git/v5 v5.11.0
@@ -52,8 +52,8 @@ require (
github.com/google/go-containerregistry v0.19.0
github.com/google/licenseclassifier/v2 v2.0.0
github.com/google/uuid v1.6.0
github.com/google/wire v0.5.0
github.com/hashicorp/go-getter v1.7.3
github.com/google/wire v0.6.0
github.com/hashicorp/go-getter v1.7.4
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.7.5
github.com/hashicorp/golang-lru/v2 v2.0.6
@@ -107,9 +107,9 @@ require (
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/mod v0.15.0
golang.org/x/net v0.21.0
golang.org/x/net v0.23.0
golang.org/x/sync v0.6.0
golang.org/x/term v0.17.0
golang.org/x/term v0.18.0
golang.org/x/text v0.14.0
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
google.golang.org/protobuf v1.33.0
@@ -136,7 +136,7 @@ require (
github.com/owenrumney/squealer v1.2.2
github.com/zclconf/go-cty v1.14.1
github.com/zclconf/go-cty-yaml v1.0.3
golang.org/x/crypto v0.19.0
golang.org/x/crypto v0.21.0
helm.sh/helm/v3 v3.14.2
)
@@ -388,9 +388,9 @@ require (
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.1 // indirect
golang.org/x/tools v0.17.0 // indirect
google.golang.org/api v0.155.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect

55
go.sum
View File

@@ -717,8 +717,8 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v23.0.0-rc.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE=
github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
@@ -1016,7 +1016,7 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -1024,8 +1024,8 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
@@ -1081,8 +1081,8 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.7.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E=
github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-getter v1.7.4 h1:3yQjWuxICvSpYwqSayAdKRFcvBl1y/vogCxczWSmix0=
github.com/hashicorp/go-getter v1.7.4/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
@@ -1774,8 +1774,10 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1814,6 +1816,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1882,8 +1887,11 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1926,6 +1934,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2046,8 +2056,11 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -2055,8 +2068,11 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2070,6 +2086,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2088,7 +2106,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -2140,8 +2157,10 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -242,6 +242,16 @@ func TestClientServer(t *testing.T) {
},
golden: "testdata/pom.json.golden",
},
{
name: "scan package-lock.json with repo command in client/server mode",
args: csArgs{
Command: "repo",
RemoteAddrOption: "--server",
Target: "testdata/fixtures/repo/npm/",
ListAllPackages: true,
},
golden: "testdata/npm.json.golden",
},
{
name: "scan sample.pem with repo command in client/server mode",
args: csArgs{
@@ -588,6 +598,10 @@ func setupClient(t *testing.T, c csArgs, addr string, cacheDir string, golden st
osArgs = append(osArgs, "--format", "json")
}
if c.ListAllPackages {
osArgs = append(osArgs, "--list-all-pkgs")
}
if c.IgnoreUnfixed {
osArgs = append(osArgs, "--ignore-unfixed")
}

View File

@@ -30,3 +30,4 @@
19.04,Disco Dingo,disco,2018-10-18,2019-04-18,2020-01-18
19.10,Eoan Ermine,eoan,2019-04-18,2019-10-17,2020-07-17
20.04 LTS,Focal Fossa,focal,2020-04-23,2025-04-23,2030-04-23
22.04 LTS,Jammy Jellyfish,jammy,2022-04-21,2027-04-21,2032-04-21
1 4.10,Warty Warthog,warty,2004-03-05,2004-10-20,2006-04-30
30 19.04,Disco Dingo,disco,2018-10-18,2019-04-18,2020-01-18
31 19.10,Eoan Ermine,eoan,2019-04-18,2019-10-17,2020-07-17
32 20.04 LTS,Focal Fossa,focal,2020-04-23,2025-04-23,2030-04-23
33 22.04 LTS,Jammy Jellyfish,jammy,2022-04-21,2027-04-21,2032-04-21

View File

@@ -50,6 +50,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=

View File

@@ -31,8 +31,9 @@ const (
)
type options struct {
offline bool
remoteRepos []string
offline bool
releaseRemoteRepos []string
snapshotRemoteRepos []string
}
type option func(*options)
@@ -43,25 +44,26 @@ func WithOffline(offline bool) option {
}
}
func WithRemoteRepos(repos []string) option {
func WithReleaseRemoteRepos(repos []string) option {
return func(opts *options) {
opts.remoteRepos = repos
opts.releaseRemoteRepos = repos
}
}
type parser struct {
rootPath string
cache pomCache
localRepository string
remoteRepositories []string
offline bool
servers []Server
rootPath string
cache pomCache
localRepository string
releaseRemoteRepos []string
snapshotRemoteRepos []string
offline bool
servers []Server
}
func NewParser(filePath string, opts ...option) types.Parser {
o := &options{
offline: false,
remoteRepos: []string{centralURL},
offline: false,
releaseRemoteRepos: []string{centralURL}, // Maven doesn't use central repository for snapshot dependencies
}
for _, opt := range opts {
@@ -76,12 +78,13 @@ func NewParser(filePath string, opts ...option) types.Parser {
}
return &parser{
rootPath: filepath.Clean(filePath),
cache: newPOMCache(),
localRepository: localRepository,
remoteRepositories: o.remoteRepos,
offline: o.offline,
servers: s.Servers,
rootPath: filepath.Clean(filePath),
cache: newPOMCache(),
localRepository: localRepository,
releaseRemoteRepos: o.releaseRemoteRepos,
snapshotRemoteRepos: o.snapshotRemoteRepos,
offline: o.offline,
servers: s.Servers,
}
}
@@ -105,10 +108,10 @@ func (p *parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
// Cache root POM
p.cache.put(result.artifact, result)
return p.parseRoot(root.artifact())
return p.parseRoot(root.artifact(), make(map[string]struct{}))
}
func (p *parser) parseRoot(root artifact) ([]types.Library, []types.Dependency, error) {
func (p *parser) parseRoot(root artifact, uniqModules map[string]struct{}) ([]types.Library, []types.Dependency, error) {
// Prepare a queue for dependencies
queue := newArtifactQueue()
@@ -132,7 +135,12 @@ func (p *parser) parseRoot(root artifact) ([]types.Library, []types.Dependency,
// Modules should be handled separately so that they can have independent dependencies.
// It means multi-module allows for duplicate dependencies.
if art.Module {
moduleLibs, moduleDeps, err := p.parseRoot(art)
if _, ok := uniqModules[art.String()]; ok {
continue
}
uniqModules[art.String()] = struct{}{}
moduleLibs, moduleDeps, err := p.parseRoot(art, uniqModules)
if err != nil {
return nil, nil, err
}
@@ -316,7 +324,9 @@ func (p *parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error)
}
// Update remoteRepositories
p.remoteRepositories = utils.UniqueStrings(append(pom.repositories(p.servers), p.remoteRepositories...))
pomReleaseRemoteRepos, pomSnapshotRemoteRepos := pom.repositories(p.servers)
p.releaseRemoteRepos = lo.Uniq(append(pomReleaseRemoteRepos, p.releaseRemoteRepos...))
p.snapshotRemoteRepos = lo.Uniq(append(pomSnapshotRemoteRepos, p.snapshotRemoteRepos...))
// Parent
parent, err := p.parseParent(pom.filePath, pom.content.Parent)
@@ -607,7 +617,7 @@ func (p *parser) tryRepository(groupID, artifactID, version string) (*pom, error
}
// Search remote remoteRepositories
loaded, err = p.fetchPOMFromRemoteRepositories(paths)
loaded, err = p.fetchPOMFromRemoteRepositories(paths, isSnapshot(version))
if err == nil {
return loaded, nil
}
@@ -622,15 +632,21 @@ func (p *parser) loadPOMFromLocalRepository(paths []string) (*pom, error) {
return p.openPom(localPath)
}
func (p *parser) fetchPOMFromRemoteRepositories(paths []string) (*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 {
log.Logger.Debug("Fetching the remote pom.xml is skipped")
return nil, xerrors.New("offline mode")
}
remoteRepos := p.releaseRemoteRepos
// Maven uses only snapshot repos for snapshot artifacts
if snapshot {
remoteRepos = p.snapshotRemoteRepos
}
// try all remoteRepositories
for _, repo := range p.remoteRepositories {
for _, repo := range remoteRepos {
fetched, err := fetchPOMFromRemoteRepository(repo, paths)
if err != nil {
return nil, xerrors.Errorf("fetch repository error: %w", err)
@@ -694,3 +710,8 @@ func parsePom(r io.Reader) (*pomXML, error) {
func packageID(name, version string) string {
return dependency.ID(ftypes.Pom, name, version)
}
// cf. https://github.com/apache/maven/blob/259404701402230299fe05ee889ecdf1c9dae816/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java#L482-L486
func isSnapshot(ver string) bool {
return strings.HasSuffix(ver, "SNAPSHOT") || ver == "LATEST"
}

View File

@@ -70,7 +70,7 @@ func TestPom_Parse(t *testing.T) {
},
},
{
name: "remote repository",
name: "remote release repository",
inputFile: filepath.Join("testdata", "happy", "pom.xml"),
local: false,
want: []types.Library{
@@ -114,6 +114,37 @@ func TestPom_Parse(t *testing.T) {
},
},
},
{
name: "snapshot dependency",
inputFile: filepath.Join("testdata", "snapshot", "pom.xml"),
local: false,
want: []types.Library{
{
ID: "com.example:happy:1.0.0",
Name: "com.example:happy",
Version: "1.0.0",
},
{
ID: "org.example:example-dependency:1.2.3-SNAPSHOT",
Name: "org.example:example-dependency",
Version: "1.2.3-SNAPSHOT",
Locations: types.Locations{
{
StartLine: 14,
EndLine: 18,
},
},
},
},
wantDeps: []types.Dependency{
{
ID: "com.example:happy:1.0.0",
DependsOn: []string{
"org.example:example-dependency:1.2.3-SNAPSHOT",
},
},
},
},
{
name: "offline mode",
inputFile: filepath.Join("testdata", "offline", "pom.xml"),
@@ -959,6 +990,43 @@ func TestPom_Parse(t *testing.T) {
},
},
},
{
name: "Infinity loop for modules",
inputFile: filepath.Join("testdata", "modules-infinity-loop", "pom.xml"),
local: true,
want: []types.Library{
// as module
{
ID: "org.example:module-1:2.0.0",
Name: "org.example:module-1",
Version: "2.0.0",
},
// as dependency
{
ID: "org.example:module-1:2.0.0",
Name: "org.example:module-1",
Version: "2.0.0",
},
{
ID: "org.example:module-2:3.0.0",
Name: "org.example:module-2",
Version: "3.0.0",
},
{
ID: "org.example:root:1.0.0",
Name: "org.example:root",
Version: "1.0.0",
},
},
wantDeps: []types.Dependency{
{
ID: "org.example:module-2:3.0.0",
DependsOn: []string{
"org.example:module-1:2.0.0",
},
},
},
},
{
name: "multi module soft requirement",
inputFile: filepath.Join("testdata", "multi-module-soft-requirement", "pom.xml"),
@@ -1258,7 +1326,7 @@ func TestPom_Parse(t *testing.T) {
remoteRepos = []string{ts.URL}
}
p := pom.NewParser(tt.inputFile, pom.WithRemoteRepos(remoteRepos), pom.WithOffline(tt.offline))
p := pom.NewParser(tt.inputFile, pom.WithReleaseRemoteRepos(remoteRepos), pom.WithOffline(tt.offline))
gotLibs, gotDeps, err := p.Parse(f)
if tt.wantErr != "" {

View File

@@ -115,11 +115,13 @@ func (p pom) licenses() []string {
})
}
func (p pom) repositories(servers []Server) []string {
var urls []string
func (p pom) repositories(servers []Server) ([]string, []string) {
var releaseRepos, snapshotRepos []string
for _, rep := range p.content.Repositories.Repository {
snapshot := rep.Snapshots.Enabled == "true"
release := rep.Releases.Enabled == "true"
// Add only enabled repositories
if rep.Releases.Enabled == "false" && rep.Snapshots.Enabled == "false" {
if !release && !snapshot {
continue
}
@@ -139,9 +141,15 @@ func (p pom) repositories(servers []Server) []string {
}
log.Logger.Debugf("Adding repository %s: %s", rep.ID, rep.URL)
urls = append(urls, repoURL.String())
if snapshot {
snapshotRepos = append(snapshotRepos, repoURL.String())
}
if release {
releaseRepos = append(releaseRepos, repoURL.String())
}
}
return urls
return releaseRepos, snapshotRepos
}
type pomXML struct {

View File

@@ -0,0 +1,16 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>module-2</artifactId>
<groupId>org.example</groupId>
<version>3.0.0</version>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>module-1</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>module-1</artifactId>
<groupId>org.example</groupId>
<version>2.0.0</version>
<modules>
<module>module-2</module>
</modules>
</project>

View File

@@ -0,0 +1,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>root</artifactId>
<groupId>org.example</groupId>
<version>1.0.0</version>
<modules>
<module>module-1</module>
<module>module-2</module>
</modules>
</project>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>example-dependency</artifactId>
<version>1.2.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Example API Dependency</name>
<description>The example API</description>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>example-api</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>happy</artifactId>
<version>1.0.0</version>
<name>happy</name>
<description>Example</description>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>example-dependency</artifactId>
<version>1.2.3-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"path"
"slices"
"sort"
"strings"
@@ -115,28 +116,42 @@ func (p *Parser) parseV2(packages map[string]Package) ([]types.Library, []types.
EndLine: pkg.EndLine,
}
var ref types.ExternalRef
if pkg.Resolved != "" {
ref = types.ExternalRef{
Type: types.RefOther,
URL: pkg.Resolved,
}
}
pkgIndirect := isIndirectLib(pkgPath, directDeps)
// There are cases when similar libraries use same dependencies
// we need to add location for each these dependencies
if savedLib, ok := libs[pkgID]; ok {
savedLib.Dev = savedLib.Dev && pkg.Dev
savedLib.Indirect = savedLib.Indirect && pkgIndirect
if ref.URL != "" && !slices.Contains(savedLib.ExternalReferences, ref) {
savedLib.ExternalReferences = append(savedLib.ExternalReferences, ref)
sortExternalReferences(savedLib.ExternalReferences)
}
savedLib.Locations = append(savedLib.Locations, location)
sort.Sort(savedLib.Locations)
libs[pkgID] = savedLib
continue
}
lib := types.Library{
ID: pkgID,
Name: pkgName,
Version: pkg.Version,
Indirect: isIndirectLib(pkgPath, directDeps),
Dev: pkg.Dev,
ExternalReferences: []types.ExternalRef{
{
Type: types.RefOther,
URL: pkg.Resolved,
},
},
Locations: []types.Location{location},
ID: pkgID,
Name: pkgName,
Version: pkg.Version,
Indirect: pkgIndirect,
Dev: pkg.Dev,
ExternalReferences: lo.Ternary(ref.URL != "", []types.ExternalRef{ref}, nil),
Locations: []types.Location{location},
}
libs[pkgID] = lib
@@ -385,3 +400,12 @@ func (t *Package) UnmarshalJSONWithMetadata(node jfather.Node) error {
func packageID(name, version string) string {
return dependency.ID(ftypes.Npm, name, version)
}
func sortExternalReferences(refs []types.ExternalRef) {
sort.Slice(refs, func(i, j int) bool {
if refs[i].Type != refs[j].Type {
return refs[i].Type < refs[j].Type
}
return refs[i].URL < refs[j].URL
})
}

View File

@@ -41,6 +41,12 @@ func TestParse(t *testing.T) {
want: npmV3WithWorkspaceLibs,
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,
wantDeps: npmV3WithSameDevAndNonDevDeps,
},
{
name: "lock version v3 with workspace and without direct deps field",
file: "testdata/package-lock_v3_without_root_deps_field.json",

View File

@@ -1516,4 +1516,89 @@ var (
DependsOn: []string{"debug@2.6.9"},
},
}
npmV3WithSameDevAndNonDevLibs = []types.Library{
{
ID: "fsevents@1.2.9",
Name: "fsevents",
Version: "1.2.9",
Dev: true,
ExternalReferences: []types.ExternalRef{
{
Type: types.RefOther,
URL: "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
},
},
Locations: []types.Location{
{
StartLine: 18,
EndLine: 37,
},
},
},
{
ID: "minimist@0.0.8",
Name: "minimist",
Version: "0.0.8",
Indirect: false,
Dev: false,
ExternalReferences: []types.ExternalRef{
{
Type: types.RefOther,
URL: "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
},
},
Locations: []types.Location{
{
StartLine: 38,
EndLine: 43,
},
{
StartLine: 68,
EndLine: 72,
},
},
},
{
ID: "mkdirp@0.5.1",
Name: "mkdirp",
Version: "0.5.1",
Indirect: true,
Dev: true,
Locations: []types.Location{
{
StartLine: 44,
EndLine: 55,
},
},
},
{
ID: "node-pre-gyp@0.12.0",
Name: "node-pre-gyp",
Version: "0.12.0",
Indirect: true,
Dev: true,
Locations: []types.Location{
{
StartLine: 56,
EndLine: 67,
},
},
},
}
npmV3WithSameDevAndNonDevDeps = []types.Dependency{
{
ID: "fsevents@1.2.9",
DependsOn: []string{"node-pre-gyp@0.12.0"},
},
{
ID: "mkdirp@0.5.1",
DependsOn: []string{"minimist@0.0.8"},
},
{
ID: "node-pre-gyp@0.12.0",
DependsOn: []string{"mkdirp@0.5.1"},
},
}
)

View File

@@ -0,0 +1,74 @@
{
"name": "5139",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "5139",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"minimist": "^0.0.8"
},
"devDependencies": {
"fsevents": "^1.2.9"
}
},
"node_modules/fsevents": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
"integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
"bundleDependencies": [
"node-pre-gyp"
],
"deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2",
"dev": true,
"hasInstallScript": true,
"os": [
"darwin"
],
"dependencies": {
"node-pre-gyp": "^0.12.0"
},
"engines": {
"node": ">=4.0"
}
},
"node_modules/fsevents/node_modules/minimist": {
"version": "0.0.8",
"dev": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/fsevents/node_modules/mkdirp": {
"version": "0.5.1",
"dev": true,
"inBundle": true,
"license": "MIT",
"dependencies": {
"minimist": "0.0.8"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/fsevents/node_modules/node-pre-gyp": {
"version": "0.12.0",
"dev": true,
"inBundle": true,
"license": "BSD-3-Clause",
"dependencies": {
"mkdirp": "^0.5.1"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q=="
}
}
}

View File

@@ -5,6 +5,7 @@ import (
"io"
"regexp"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/dependency"
@@ -21,7 +22,7 @@ type packageJSON struct {
Dependencies map[string]string `json:"dependencies"`
OptionalDependencies map[string]string `json:"optionalDependencies"`
DevDependencies map[string]string `json:"devDependencies"`
Workspaces []string `json:"workspaces"`
Workspaces any `json:"workspaces"`
}
type Package struct {
@@ -65,7 +66,7 @@ func (p *Parser) Parse(r io.Reader) (Package, error) {
Dependencies: pkgJSON.Dependencies,
OptionalDependencies: pkgJSON.OptionalDependencies,
DevDependencies: pkgJSON.DevDependencies,
Workspaces: pkgJSON.Workspaces,
Workspaces: parseWorkspaces(pkgJSON.Workspaces),
}, nil
}
@@ -82,6 +83,29 @@ func parseLicense(val interface{}) string {
return ""
}
// parseWorkspaces returns slice of workspaces
func parseWorkspaces(val any) []string {
// Workspaces support 2 types - https://github.com/SchemaStore/schemastore/blob/d9516961f8a5b0e65a457808070147b5a866f60b/src/schemas/json/package.json#L777
switch ws := val.(type) {
// Workspace as object (map[string][]string)
// e.g. "workspaces": {"packages": ["packages/*", "plugins/*"]},
case map[string]interface{}:
// Take only workspaces for `packages` - https://classic.yarnpkg.com/blog/2018/02/15/nohoist/
if pkgsWorkspaces, ok := ws["packages"]; ok {
return lo.Map(pkgsWorkspaces.([]interface{}), func(workspace interface{}, _ int) string {
return workspace.(string)
})
}
// Workspace as string array
// e.g. "workspaces": ["packages/*", "backend"],
case []interface{}:
return lo.Map(ws, func(workspace interface{}, _ int) string {
return workspace.(string)
})
}
return nil
}
func IsValidName(name string) bool {
// Name is optional field
// https://docs.npmjs.com/cli/v9/configuring-npm/package-json#name

View File

@@ -76,6 +76,20 @@ func TestParse(t *testing.T) {
},
},
},
{
name: "happy path - workspace as struct",
inputFile: "testdata/workspace_as_map_package.json",
want: packagejson.Package{
Library: types.Library{
ID: "example@1.0.0",
Name: "example",
Version: "1.0.0",
},
Workspaces: []string{
"packages/*",
},
},
},
{
name: "invalid package name",
inputFile: "testdata/invalid_name.json",

View File

@@ -0,0 +1,8 @@
{
"name": "example",
"version": "1.0.0",
"workspaces": {
"packages": ["packages/*"],
"nohoist": ["**/react-native", "**/react-native/**"]
}
}

View File

@@ -55,7 +55,8 @@ func (c *CompositeFS) CopyFileToTemp(opener Opener, info os.FileInfo) (string, e
return "", xerrors.Errorf("copy error: %w", err)
}
if err = os.Chmod(f.Name(), info.Mode()); err != nil {
// Use 0600 instead of file permissions to avoid errors when a file uses incorrect permissions (e.g. 0044).
if err = os.Chmod(f.Name(), 0600); err != nil {
return "", xerrors.Errorf("chmod error: %w", err)
}

View File

@@ -36,7 +36,7 @@ resource "aws_s3_bucket_public_access_block" "example_access_block"{
hasPublicAccess: true,
},
{
desc: "public access block is found when using the bucket name as the lookup",
desc: "public access block is found when using the bucket id as the lookup",
source: `
resource "aws_s3_bucket" "example" {
bucket = "bucketname"
@@ -254,6 +254,33 @@ func Test_Adapt(t *testing.T) {
},
},
},
{
name: "non-valid SSE algorithm",
terraform: `
resource "aws_s3_bucket" "this" {
bucket = "test"
}
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = ""
}
}
}`,
expected: s3.S3{
Buckets: []s3.Bucket{
{
Name: iacTypes.String("test", iacTypes.NewTestMetadata()),
Encryption: s3.Encryption{
Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
},
ACL: iacTypes.String("private", iacTypes.NewTestMetadata()),
},
},
},
},
}
for _, test := range tests {

View File

@@ -1,6 +1,10 @@
package s3
import (
"slices"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3"
"github.com/aquasecurity/trivy/pkg/iac/terraform"
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
@@ -194,11 +198,13 @@ func isEncrypted(sseConfgihuration *terraform.Block) iacTypes.BoolValue {
sseConfgihuration,
"rule.apply_server_side_encryption_by_default.sse_algorithm",
func(attr *terraform.Attribute, parent *terraform.Block) iacTypes.BoolValue {
if attr.IsNil() {
if attr.IsNil() || !attr.IsString() {
return iacTypes.BoolDefault(false, parent.GetMetadata())
}
algoVal := attr.Value().AsString()
isValidAlgo := slices.Contains(s3types.ServerSideEncryption("").Values(), s3types.ServerSideEncryption(algoVal))
return iacTypes.Bool(
true,
isValidAlgo,
attr.GetMetadata(),
)
},

View File

@@ -7,6 +7,7 @@ import (
"io/fs"
"path/filepath"
"strings"
"sync"
"github.com/liamg/memoryfs"
@@ -38,6 +39,8 @@ type Scanner struct {
skipRequired bool
frameworks []framework.Framework
spec string
regoScanner *rego.Scanner
mu sync.Mutex
}
func (s *Scanner) SetSpec(spec string) {
@@ -120,6 +123,10 @@ func (s *Scanner) SetRegoErrorLimit(_ int) {}
func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, path string) (scan.Results, error) {
if err := s.initRegoScanner(target); err != nil {
return nil, fmt.Errorf("failed to init rego scanner: %w", err)
}
var results []scan.Result
if err := fs.WalkDir(target, path, func(path string, d fs.DirEntry, err error) error {
select {
@@ -150,6 +157,7 @@ func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, path string) (scan.R
} else {
results = append(results, scanResults...)
}
return fs.SkipDir
}
return nil
@@ -174,14 +182,6 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS)
return nil, nil
}
regoScanner := rego.NewScanner(types.SourceKubernetes, s.options...)
policyFS := target
if s.policyFS != nil {
policyFS = s.policyFS
}
if err := regoScanner.LoadPolicies(s.loadEmbeddedLibraries, s.loadEmbeddedPolicies, policyFS, s.policyDirs, s.policyReaders); err != nil {
return nil, fmt.Errorf("policies load: %w", err)
}
for _, file := range chartFiles {
file := file
s.debug.Log("Processing rendered chart file: %s", file.TemplateFilePath)
@@ -191,7 +191,7 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS)
return nil, fmt.Errorf("unmarshal yaml: %w", err)
}
for _, manifest := range manifests {
fileResults, err := regoScanner.ScanInput(ctx, rego.Input{
fileResults, err := s.regoScanner.ScanInput(ctx, rego.Input{
Path: file.TemplateFilePath,
Contents: manifest,
FS: target,
@@ -219,3 +219,18 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS)
}
return results, nil
}
func (s *Scanner) initRegoScanner(srcFS fs.FS) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.regoScanner != nil {
return nil
}
regoScanner := rego.NewScanner(types.SourceKubernetes, s.options...)
regoScanner.SetParentDebugLogger(s.debug)
if err := regoScanner.LoadPolicies(s.loadEmbeddedLibraries, s.loadEmbeddedPolicies, srcFS, s.policyDirs, s.policyReaders); err != nil {
return err
}
s.regoScanner = regoScanner
return nil
}

View File

@@ -318,3 +318,44 @@ deny[res] {
require.NoError(t, err)
assert.NotNil(t, code)
}
func TestScanSubchartOnce(t *testing.T) {
check := `# METADATA
# title: "Test rego"
# description: "Test rego"
# scope: package
# schemas:
# - input: schema["kubernetes"]
# custom:
# id: ID001
# avd_id: AVD-USR-ID001
# severity: LOW
# input:
# selector:
# - type: kubernetes
# subtypes:
# - kind: pod
package user.kubernetes.ID001
import data.lib.kubernetes
deny[res] {
container := kubernetes.containers[_]
container.securityContext.readOnlyRootFilesystem == false
res := result.new("set 'securityContext.readOnlyRootFilesystem' to true", container)
}
`
scanner := helm.New(
options.ScannerWithEmbeddedPolicies(false),
options.ScannerWithEmbeddedLibraries(true),
options.ScannerWithPolicyNamespaces("user"),
options.ScannerWithPolicyReader(strings.NewReader(check)),
)
results, err := scanner.ScanFS(context.TODO(), os.DirFS("testdata/with-subchart"), ".")
require.NoError(t, err)
require.Len(t, results, 1)
assert.Len(t, results.GetFailed(), 0)
}

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: test
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "1.16.0"

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: nginx
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "1.16.0"

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 8080
securityContext:
readOnlyRootFilesystem: {{ .Values.readOnlyFs }}

View File

@@ -0,0 +1 @@
readOnlyFs: false

View File

@@ -0,0 +1,2 @@
nginx:
readOnlyFs: true

View File

@@ -65,6 +65,7 @@ func ConvertToRPCPkgs(pkgs []ftypes.Package) []*common.Package {
SrcRelease: pkg.SrcRelease,
SrcEpoch: int32(pkg.SrcEpoch),
Licenses: pkg.Licenses,
Locations: ConvertToRPCLocations(pkg.Locations),
Layer: ConvertToRPCLayer(pkg.Layer),
FilePath: pkg.FilePath,
DependsOn: pkg.DependsOn,
@@ -90,6 +91,17 @@ func ConvertToRPCPkgIdentifier(pkg ftypes.PkgIdentifier) *common.PkgIdentifier {
}
}
func ConvertToRPCLocations(pkgLocs []ftypes.Location) []*common.Location {
var locations []*common.Location
for _, pkgLoc := range pkgLocs {
locations = append(locations, &common.Location{
StartLine: int32(pkgLoc.StartLine),
EndLine: int32(pkgLoc.EndLine),
})
}
return locations
}
func ConvertToRPCCustomResources(resources []ftypes.CustomResource) []*common.CustomResource {
var rpcResources []*common.CustomResource
for _, r := range resources {
@@ -207,6 +219,7 @@ func ConvertFromRPCPkgs(rpcPkgs []*common.Package) []ftypes.Package {
SrcRelease: pkg.SrcRelease,
SrcEpoch: int(pkg.SrcEpoch),
Licenses: pkg.Licenses,
Locations: ConvertFromRPCLocation(pkg.Locations),
Layer: ConvertFromRPCLayer(pkg.Layer),
FilePath: pkg.FilePath,
DependsOn: pkg.DependsOn,
@@ -237,6 +250,17 @@ func ConvertFromRPCPkgIdentifier(pkg *common.PkgIdentifier) ftypes.PkgIdentifier
return pkgID
}
func ConvertFromRPCLocation(locs []*common.Location) []ftypes.Location {
var pkgLocs []ftypes.Location
for _, loc := range locs {
pkgLocs = append(pkgLocs, ftypes.Location{
StartLine: int(loc.StartLine),
EndLine: int(loc.EndLine),
})
}
return pkgLocs
}
// ConvertToRPCVulns returns common.Vulnerability
func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerability {
var rpcVulns []*common.Vulnerability

View File

@@ -39,6 +39,16 @@ func TestConvertToRpcPkgs(t *testing.T) {
SrcRelease: "1",
SrcEpoch: 2,
Licenses: []string{"MIT"},
Locations: []ftypes.Location{
{
StartLine: 10,
EndLine: 20,
},
{
StartLine: 22,
EndLine: 32,
},
},
Layer: ftypes.Layer{
Digest: "sha256:6a428f9f83b0a29f1fdd2ccccca19a9bab805a925b8eddf432a5a3d3da04afbc",
DiffID: "sha256:39982b2a789afc156fff00c707d0ff1c6ab4af8f1666a8df4787714059ce24e7",
@@ -60,6 +70,16 @@ func TestConvertToRpcPkgs(t *testing.T) {
SrcRelease: "1",
SrcEpoch: 2,
Licenses: []string{"MIT"},
Locations: []*common.Location{
{
StartLine: 10,
EndLine: 20,
},
{
StartLine: 22,
EndLine: 32,
},
},
Layer: &common.Layer{
Digest: "sha256:6a428f9f83b0a29f1fdd2ccccca19a9bab805a925b8eddf432a5a3d3da04afbc",
DiffId: "sha256:39982b2a789afc156fff00c707d0ff1c6ab4af8f1666a8df4787714059ce24e7",
@@ -101,6 +121,16 @@ func TestConvertFromRpcPkgs(t *testing.T) {
SrcRelease: "1",
SrcEpoch: 2,
Licenses: []string{"MIT"},
Locations: []*common.Location{
{
StartLine: 10,
EndLine: 20,
},
{
StartLine: 22,
EndLine: 32,
},
},
Layer: &common.Layer{
Digest: "sha256:6a428f9f83b0a29f1fdd2ccccca19a9bab805a925b8eddf432a5a3d3da04afbc",
DiffId: "sha256:39982b2a789afc156fff00c707d0ff1c6ab4af8f1666a8df4787714059ce24e7",
@@ -122,6 +152,16 @@ func TestConvertFromRpcPkgs(t *testing.T) {
SrcRelease: "1",
SrcEpoch: 2,
Licenses: []string{"MIT"},
Locations: []ftypes.Location{
{
StartLine: 10,
EndLine: 20,
},
{
StartLine: 22,
EndLine: 32,
},
},
Layer: ftypes.Layer{
Digest: "sha256:6a428f9f83b0a29f1fdd2ccccca19a9bab805a925b8eddf432a5a3d3da04afbc",
DiffID: "sha256:39982b2a789afc156fff00c707d0ff1c6ab4af8f1666a8df4787714059ce24e7",

View File

@@ -238,12 +238,20 @@ func (b *BOM) AddComponent(c *Component) {
}
func (b *BOM) AddRelationship(parent, child *Component, relationshipType RelationshipType) {
// Check the wrong parent to avoid `panic`
if parent == nil {
return
}
if parent.id == uuid.Nil {
b.AddComponent(parent)
}
if child == nil {
b.relationships[parent.id] = nil // Meaning no dependencies
// It is possible that `relationships` already contains this parent.
// Check this to avoid overwriting.
if _, ok := b.relationships[parent.id]; !ok {
b.relationships[parent.id] = nil // Meaning no dependencies
}
return
}

View File

@@ -0,0 +1,54 @@
{
"files": [
{
"fileName": "./Modules/Microsoft.PowerShell.PSResourceGet/_manifest/spdx_2.2/manifest.spdx.json",
"SPDXID": "SPDXRef-File--Modules-Microsoft.PowerShell.PSResourceGet--manifest-spdx-2.2-manifest.spdx.json-2B9FB98F5CA97DC84FD382A8F8E68F663C003362",
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "4201b0989938842ef8c11a006184e0b1466bd7f9bb2af61d89a4c8318d43466e"
},
{
"algorithm": "SHA1",
"checksumValue": "2b9fb98f5ca97dc84fd382a8f8e68f663c003362"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "NOASSERTION",
"fileTypes": [
"SPDX"
]
}
],
"externalDocumentRefs": [],
"relationships": [
{
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-RootPackage",
"spdxElementId": "SPDXRef-DOCUMENT"
},
{
"relationshipType": "DESCRIBED_BY",
"relatedSpdxElement": "SPDXRef-DOCUMENT",
"spdxElementId": "SPDXRef-File--Modules-Microsoft.PowerShell.PSResourceGet--manifest-spdx-2.2-manifest.spdx.json-2B9FB98F5CA97DC84FD382A8F8E68F663C003362"
}
],
"spdxVersion": "SPDX-2.2",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "PowerShell Linux Arm32 7.5.0-preview.2",
"documentNamespace": "https://sbom.microsoft/1:2QSF7qZlbE-F7QrUJlEo7g:pHp_nUFvDUijZ4LrJ4RhoQ/696:458654/PowerShell%20Linux%20Arm32:7.5.0-preview.2:pDkyTHXmgUOdzSXIq9CiqA",
"creationInfo": {
"created": "2024-02-22T00:43:53Z",
"creators": [
"Organization: Microsoft",
"Tool: Microsoft.SBOMTool-2.2.3"
]
},
"documentDescribes": [
"SPDXRef-RootPackage"
]
}

View File

@@ -87,8 +87,16 @@ func (s *SPDX) unmarshal(spdxDocument *spdx.Document) error {
continue
}
compA := components[rel.RefA.ElementRefID]
compB := components[rel.RefB.ElementRefID]
compA, ok := components[rel.RefA.ElementRefID]
if !ok { // Skip if parent is not Package
continue
}
compB, ok := components[rel.RefB.ElementRefID]
if !ok { // Skip if child is not Package
continue
}
s.BOM.AddRelationship(compA, compB, s.parseRelationshipType(rel.Relationship))
}
@@ -255,6 +263,10 @@ func (s *SPDX) parseExternalReferences(refs []*spdx.PackageExternalReference) (*
}
func (s *SPDX) isTrivySBOM(spdxDocument *spdx.Document) bool {
if spdxDocument == nil || spdxDocument.CreationInfo == nil || spdxDocument.CreationInfo.Creators == nil {
return false
}
for _, c := range spdxDocument.CreationInfo.Creators {
if c.CreatorType == "Tool" && strings.HasPrefix(c.Creator, "trivy") {
return true

View File

@@ -314,6 +314,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
},
},
},
{
name: "happy path with file as parent of relationship",
inputFile: "testdata/happy/with-file-as-relationship-parent.json",
want: types.SBOM{},
},
{
name: "happy path only os component",
inputFile: "testdata/happy/os-only-bom.json",

File diff suppressed because it is too large Load Diff

View File

@@ -42,17 +42,18 @@ message Package {
string arch = 5;
// src package containing some binary packages
// e.g. bind
string src_name = 6;
string src_version = 7;
string src_release = 8;
int32 src_epoch = 9;
repeated string licenses = 15;
Layer layer = 11;
string file_path = 12;
repeated string depends_on = 14;
string digest = 16;
bool dev = 17;
bool indirect = 18;
string src_name = 6;
string src_version = 7;
string src_release = 8;
int32 src_epoch = 9;
repeated string licenses = 15;
repeated Location locations = 20;
Layer layer = 11;
string file_path = 12;
repeated string depends_on = 14;
string digest = 16;
bool dev = 17;
bool indirect = 18;
}
message PkgIdentifier {
@@ -60,6 +61,11 @@ message PkgIdentifier {
string bom_ref = 2;
}
message Location {
int32 start_line = 1;
int32 end_line = 2;
}
message Misconfiguration {
string file_type = 1;
string file_path = 2;