mirror of
https://github.com/anchore/syft.git
synced 2026-02-12 02:26:42 +01:00
Merge remote-tracking branch 'origin/main' into add-go-symbol-extract
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
commit
89824f0ae7
12
.binny.yaml
12
.binny.yaml
@ -2,7 +2,7 @@ tools:
|
|||||||
# we want to use a pinned version of binny to manage the toolchain (so binny manages itself!)
|
# we want to use a pinned version of binny to manage the toolchain (so binny manages itself!)
|
||||||
- name: binny
|
- name: binny
|
||||||
version:
|
version:
|
||||||
want: v0.10.0
|
want: v0.11.0
|
||||||
method: github-release
|
method: github-release
|
||||||
with:
|
with:
|
||||||
repo: anchore/binny
|
repo: anchore/binny
|
||||||
@ -26,7 +26,7 @@ tools:
|
|||||||
# used for linting
|
# used for linting
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
version:
|
version:
|
||||||
want: v2.6.2
|
want: v2.7.2
|
||||||
method: github-release
|
method: github-release
|
||||||
with:
|
with:
|
||||||
repo: golangci/golangci-lint
|
repo: golangci/golangci-lint
|
||||||
@ -42,7 +42,7 @@ tools:
|
|||||||
# used for signing the checksums file at release
|
# used for signing the checksums file at release
|
||||||
- name: cosign
|
- name: cosign
|
||||||
version:
|
version:
|
||||||
want: v3.0.2
|
want: v3.0.3
|
||||||
method: github-release
|
method: github-release
|
||||||
with:
|
with:
|
||||||
repo: sigstore/cosign
|
repo: sigstore/cosign
|
||||||
@ -58,7 +58,7 @@ tools:
|
|||||||
# used to release all artifacts
|
# used to release all artifacts
|
||||||
- name: goreleaser
|
- name: goreleaser
|
||||||
version:
|
version:
|
||||||
want: v2.13.0
|
want: v2.13.1
|
||||||
method: github-release
|
method: github-release
|
||||||
with:
|
with:
|
||||||
repo: goreleaser/goreleaser
|
repo: goreleaser/goreleaser
|
||||||
@ -98,7 +98,7 @@ tools:
|
|||||||
# used for triggering a release
|
# used for triggering a release
|
||||||
- name: gh
|
- name: gh
|
||||||
version:
|
version:
|
||||||
want: v2.83.1
|
want: v2.83.2
|
||||||
method: github-release
|
method: github-release
|
||||||
with:
|
with:
|
||||||
repo: cli/cli
|
repo: cli/cli
|
||||||
@ -114,7 +114,7 @@ tools:
|
|||||||
# used to upload test fixture cache
|
# used to upload test fixture cache
|
||||||
- name: yq
|
- name: yq
|
||||||
version:
|
version:
|
||||||
want: v4.49.2
|
want: v4.50.1
|
||||||
method: github-release
|
method: github-release
|
||||||
with:
|
with:
|
||||||
repo: mikefarah/yq
|
repo: mikefarah/yq
|
||||||
|
|||||||
4
.github/actions/bootstrap/action.yaml
vendored
4
.github/actions/bootstrap/action.yaml
vendored
@ -37,7 +37,7 @@ runs:
|
|||||||
- name: Restore tool cache
|
- name: Restore tool cache
|
||||||
if: inputs.tools == 'true'
|
if: inputs.tools == 'true'
|
||||||
id: tool-cache
|
id: tool-cache
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/.tool
|
path: ${{ github.workspace }}/.tool
|
||||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
|
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
|
||||||
@ -63,7 +63,7 @@ runs:
|
|||||||
|
|
||||||
- name: Restore ORAS cache from github actions
|
- name: Restore ORAS cache from github actions
|
||||||
if: inputs.download-test-fixture-cache == 'true'
|
if: inputs.download-test-fixture-cache == 'true'
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/.tmp/oras-cache
|
path: ${{ github.workspace }}/.tmp/oras-cache
|
||||||
key: ${{ inputs.cache-key-prefix }}-oras-cache
|
key: ${{ inputs.cache-key-prefix }}-oras-cache
|
||||||
|
|||||||
15
.github/pull_request_template.md
vendored
15
.github/pull_request_template.md
vendored
@ -1,11 +1,10 @@
|
|||||||
# Description
|
## Description
|
||||||
|
|
||||||
Please include a summary of the changes along with any relevant motivation and context,
|
<!-- Please include a summary of the changes along with any relevant motivation and context -->
|
||||||
or link to an issue where this is explained.
|
|
||||||
|
|
||||||
<!-- If this completes an issue, please include: -->
|
<!-- If CLI output changed, show an example (before/after if helpful) -->
|
||||||
|
|
||||||
- Fixes
|
<!-- If this changes application or API configuration, describe new/changed/removed options -->
|
||||||
|
|
||||||
## Type of change
|
## Type of change
|
||||||
|
|
||||||
@ -18,8 +17,12 @@ or link to an issue where this is explained.
|
|||||||
- [ ] Chore (improve the developer experience, fix a test flake, etc, without changing the visible behavior of Syft)
|
- [ ] Chore (improve the developer experience, fix a test flake, etc, without changing the visible behavior of Syft)
|
||||||
- [ ] Performance (make Syft run faster or use less memory, without changing visible behavior much)
|
- [ ] Performance (make Syft run faster or use less memory, without changing visible behavior much)
|
||||||
|
|
||||||
# Checklist:
|
## Checklist
|
||||||
|
|
||||||
- [ ] I have added unit tests that cover changed behavior
|
- [ ] I have added unit tests that cover changed behavior
|
||||||
- [ ] I have tested my code in common scenarios and confirmed there are no regressions
|
- [ ] I have tested my code in common scenarios and confirmed there are no regressions
|
||||||
- [ ] I have added comments to my code, particularly in hard-to-understand sections
|
- [ ] I have added comments to my code, particularly in hard-to-understand sections
|
||||||
|
|
||||||
|
## Issue references
|
||||||
|
|
||||||
|
<!-- If this fixes an issue, include "Fixes #<issue-number>" or otherwise list the issue references -->
|
||||||
|
|||||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@ -47,7 +47,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@fe4161a26a8629af62121b670040955b330f9af2 #v3.29.5
|
uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e #v3.29.5
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@ -58,7 +58,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@fe4161a26a8629af62121b670040955b330f9af2 #v3.29.5
|
uses: github/codeql-action/autobuild@1b168cd39490f61582a9beae412bb7057a6b2c4e #v3.29.5
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@ -72,4 +72,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@fe4161a26a8629af62121b670040955b330f9af2 #v3.29.5
|
uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e #v3.29.5
|
||||||
|
|||||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@ -181,7 +181,7 @@ jobs:
|
|||||||
# for updating brew formula in anchore/homebrew-syft
|
# for updating brew formula in anchore/homebrew-syft
|
||||||
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
|
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
|
||||||
|
|
||||||
- uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d #v0.20.10
|
- uses: anchore/sbom-action@43a17d6e7add2b5535efe4dcae9952337c479a93 #v0.20.11
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
file: go.mod
|
file: go.mod
|
||||||
|
|||||||
@ -31,13 +31,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repos: ${{ github.event.inputs.repos }}
|
repos: ${{ github.event.inputs.repos }}
|
||||||
|
|
||||||
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 #v2.1.4
|
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf #v2.2.1
|
||||||
id: generate-token
|
id: generate-token
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.TOKEN_APP_ID }}
|
app-id: ${{ secrets.TOKEN_APP_ID }}
|
||||||
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
|
- uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 #v8.0.0
|
||||||
with:
|
with:
|
||||||
signoff: true
|
signoff: true
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
|
|||||||
4
.github/workflows/update-bootstrap-tools.yml
vendored
4
.github/workflows/update-bootstrap-tools.yml
vendored
@ -45,13 +45,13 @@ jobs:
|
|||||||
echo "\`\`\`"
|
echo "\`\`\`"
|
||||||
} >> $GITHUB_STEP_SUMMARY
|
} >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 #v2.1.4
|
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf #v2.2.1
|
||||||
id: generate-token
|
id: generate-token
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.TOKEN_APP_ID }}
|
app-id: ${{ secrets.TOKEN_APP_ID }}
|
||||||
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
|
- uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 #v8.0.0
|
||||||
with:
|
with:
|
||||||
signoff: true
|
signoff: true
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
|
|||||||
@ -46,13 +46,13 @@ jobs:
|
|||||||
- name: Push updated CPE cache to registry
|
- name: Push updated CPE cache to registry
|
||||||
run: make generate:cpe-index:cache:push
|
run: make generate:cpe-index:cache:push
|
||||||
|
|
||||||
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 #v2.1.4
|
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf #v2.2.1
|
||||||
id: generate-token
|
id: generate-token
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.TOKEN_APP_ID }}
|
app-id: ${{ secrets.TOKEN_APP_ID }}
|
||||||
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
|
- uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 #v8.0.0
|
||||||
with:
|
with:
|
||||||
signoff: true
|
signoff: true
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
|
|||||||
@ -32,7 +32,7 @@ jobs:
|
|||||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
|
- uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 #v8.0.0
|
||||||
with:
|
with:
|
||||||
signoff: true
|
signoff: true
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
|
|||||||
14
.github/workflows/validations.yaml
vendored
14
.github/workflows/validations.yaml
vendored
@ -1,5 +1,9 @@
|
|||||||
name: "Validations"
|
name: "Validations"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -90,7 +94,7 @@ jobs:
|
|||||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||||
- name: Upload snapshot artifacts
|
- name: Upload snapshot artifacts
|
||||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb #v5.0.1
|
||||||
with:
|
with:
|
||||||
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
||||||
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
||||||
@ -118,7 +122,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
id: snapshot-cache
|
id: snapshot-cache
|
||||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb #v5.0.1
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
snapshot
|
snapshot
|
||||||
@ -175,7 +179,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
id: snapshot-cache
|
id: snapshot-cache
|
||||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb #v5.0.1
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
snapshot
|
snapshot
|
||||||
@ -225,7 +229,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
id: snapshot-cache
|
id: snapshot-cache
|
||||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb #v5.0.1
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
snapshot
|
snapshot
|
||||||
@ -262,7 +266,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
id: snapshot-cache
|
id: snapshot-cache
|
||||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb #v5.0.1
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
snapshot
|
snapshot
|
||||||
|
|||||||
@ -49,6 +49,12 @@ linters:
|
|||||||
- common-false-positives
|
- common-false-positives
|
||||||
- legacy
|
- legacy
|
||||||
- std-error-handling
|
- std-error-handling
|
||||||
|
rules:
|
||||||
|
# internal/os contains OS feature detection logic; the name reflects its purpose
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
path: internal/os/
|
||||||
|
text: "var-naming: avoid package names that conflict"
|
||||||
paths:
|
paths:
|
||||||
- third_party$
|
- third_party$
|
||||||
- builtin$
|
- builtin$
|
||||||
|
|||||||
@ -189,7 +189,8 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
|||||||
WithFromContents(cfg.Golang.MainModuleVersion.FromContents).
|
WithFromContents(cfg.Golang.MainModuleVersion.FromContents).
|
||||||
WithFromBuildSettings(cfg.Golang.MainModuleVersion.FromBuildSettings).
|
WithFromBuildSettings(cfg.Golang.MainModuleVersion.FromBuildSettings).
|
||||||
WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags),
|
WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags),
|
||||||
),
|
).
|
||||||
|
WithUsePackagesLib(*multiLevelOption(true, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.UsePackagesLib)),
|
||||||
JavaScript: javascript.DefaultCatalogerConfig().
|
JavaScript: javascript.DefaultCatalogerConfig().
|
||||||
WithIncludeDevDependencies(*multiLevelOption(false, cfg.JavaScript.IncludeDevDependencies)).
|
WithIncludeDevDependencies(*multiLevelOption(false, cfg.JavaScript.IncludeDevDependencies)).
|
||||||
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.JavaScript, task.Node, task.NPM), cfg.JavaScript.SearchRemoteLicenses)).
|
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.JavaScript, task.Node, task.NPM), cfg.JavaScript.SearchRemoteLicenses)).
|
||||||
|
|||||||
@ -16,6 +16,7 @@ type golangConfig struct {
|
|||||||
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
|
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
|
||||||
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
|
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
|
||||||
MainModuleVersion golangMainModuleVersionConfig `json:"main-module-version" yaml:"main-module-version" mapstructure:"main-module-version"`
|
MainModuleVersion golangMainModuleVersionConfig `json:"main-module-version" yaml:"main-module-version" mapstructure:"main-module-version"`
|
||||||
|
UsePackagesLib *bool `json:"use-packages-lib" yaml:"use-packages-lib" mapstructure:"use-packages-lib"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
@ -37,6 +38,7 @@ if unset this defaults to $GONOPROXY`)
|
|||||||
descriptions.Add(&o.MainModuleVersion, `the go main module version discovered from binaries built with the go compiler will
|
descriptions.Add(&o.MainModuleVersion, `the go main module version discovered from binaries built with the go compiler will
|
||||||
always show (devel) as the version. Use these options to control heuristics to guess
|
always show (devel) as the version. Use these options to control heuristics to guess
|
||||||
a more accurate version from the binary.`)
|
a more accurate version from the binary.`)
|
||||||
|
descriptions.Add(&o.UsePackagesLib, `use the golang.org/x/tools/go/packages library, which executes golang tooling found on the path in addition to potential network access to get the most accurate results`)
|
||||||
descriptions.Add(&o.MainModuleVersion.FromLDFlags, `look for LD flags that appear to be setting a version (e.g. -X main.version=1.0.0)`)
|
descriptions.Add(&o.MainModuleVersion.FromLDFlags, `look for LD flags that appear to be setting a version (e.g. -X main.version=1.0.0)`)
|
||||||
descriptions.Add(&o.MainModuleVersion.FromBuildSettings, `use the build settings (e.g. vcs.version & vcs.time) to craft a v0 pseudo version
|
descriptions.Add(&o.MainModuleVersion.FromBuildSettings, `use the build settings (e.g. vcs.version & vcs.time) to craft a v0 pseudo version
|
||||||
(e.g. v0.0.0-20220308212642-53e6d0aaf6fb) when a more accurate version cannot be found otherwise`)
|
(e.g. v0.0.0-20220308212642-53e6d0aaf6fb) when a more accurate version cannot be found otherwise`)
|
||||||
@ -64,5 +66,6 @@ func defaultGolangConfig() golangConfig {
|
|||||||
FromContents: def.MainModuleVersion.FromContents,
|
FromContents: def.MainModuleVersion.FromContents,
|
||||||
FromBuildSettings: def.MainModuleVersion.FromBuildSettings,
|
FromBuildSettings: def.MainModuleVersion.FromBuildSettings,
|
||||||
},
|
},
|
||||||
|
UsePackagesLib: nil, // this defaults to true, which is the API default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
go.mod
28
go.mod
@ -23,7 +23,7 @@ require (
|
|||||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
|
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
|
||||||
github.com/anchore/stereoscope v0.1.13
|
github.com/anchore/stereoscope v0.1.16
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
||||||
@ -39,9 +39,9 @@ require (
|
|||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/elliotchance/phpserialize v1.4.0
|
github.com/elliotchance/phpserialize v1.4.0
|
||||||
github.com/facebookincubator/nvdtools v0.1.5
|
github.com/facebookincubator/nvdtools v0.1.5
|
||||||
github.com/github/go-spdx/v2 v2.3.4
|
github.com/github/go-spdx/v2 v2.3.5
|
||||||
github.com/gkampitakis/go-snaps v0.5.18
|
github.com/gkampitakis/go-snaps v0.5.18
|
||||||
github.com/go-git/go-billy/v5 v5.6.2
|
github.com/go-git/go-billy/v5 v5.7.0
|
||||||
github.com/go-git/go-git/v5 v5.16.3
|
github.com/go-git/go-git/v5 v5.16.3
|
||||||
github.com/go-test/deep v1.1.1
|
github.com/go-test/deep v1.1.1
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
github.com/go-viper/mapstructure/v2 v2.4.0
|
||||||
@ -57,7 +57,7 @@ require (
|
|||||||
github.com/hashicorp/hcl/v2 v2.24.0
|
github.com/hashicorp/hcl/v2 v2.24.0
|
||||||
github.com/iancoleman/strcase v0.3.0
|
github.com/iancoleman/strcase v0.3.0
|
||||||
github.com/invopop/jsonschema v0.7.0
|
github.com/invopop/jsonschema v0.7.0
|
||||||
github.com/jedib0t/go-pretty/v6 v6.7.5
|
github.com/jedib0t/go-pretty/v6 v6.7.7
|
||||||
github.com/jinzhu/copier v0.4.0
|
github.com/jinzhu/copier v0.4.0
|
||||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||||
github.com/magiconair/properties v1.8.10
|
github.com/magiconair/properties v1.8.10
|
||||||
@ -78,7 +78,7 @@ require (
|
|||||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb
|
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb
|
||||||
github.com/spdx/tools-golang v0.5.5
|
github.com/spdx/tools-golang v0.5.5
|
||||||
github.com/spf13/afero v1.15.0
|
github.com/spf13/afero v1.15.0
|
||||||
github.com/spf13/cobra v1.10.1
|
github.com/spf13/cobra v1.10.2
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/vbatts/go-mtree v0.6.0
|
github.com/vbatts/go-mtree v0.6.0
|
||||||
github.com/vifraa/gopom v1.0.0
|
github.com/vifraa/gopom v1.0.0
|
||||||
@ -89,8 +89,8 @@ require (
|
|||||||
go.uber.org/goleak v1.3.0
|
go.uber.org/goleak v1.3.0
|
||||||
go.yaml.in/yaml/v3 v3.0.4
|
go.yaml.in/yaml/v3 v3.0.4
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||||
golang.org/x/mod v0.30.0
|
golang.org/x/mod v0.31.0
|
||||||
golang.org/x/net v0.47.0
|
golang.org/x/net v0.48.0
|
||||||
modernc.org/sqlite v1.40.1
|
modernc.org/sqlite v1.40.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ require (
|
|||||||
github.com/containerd/typeurl/v2 v2.2.0 // indirect
|
github.com/containerd/typeurl/v2 v2.2.0 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
|
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/docker/cli v29.0.3+incompatible // indirect
|
github.com/docker/cli v29.1.2+incompatible // indirect
|
||||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||||
github.com/docker/docker v28.5.2+incompatible // indirect
|
github.com/docker/docker v28.5.2+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.9.4 // indirect
|
github.com/docker/docker-credential-helpers v0.9.4 // indirect
|
||||||
@ -257,14 +257,14 @@ require (
|
|||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/oauth2 v0.33.0 // indirect
|
golang.org/x/oauth2 v0.33.0 // indirect
|
||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/term v0.37.0 // indirect
|
golang.org/x/term v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
golang.org/x/time v0.14.0
|
golang.org/x/time v0.14.0
|
||||||
golang.org/x/tools v0.39.0
|
golang.org/x/tools v0.40.0
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||||
google.golang.org/api v0.203.0 // indirect
|
google.golang.org/api v0.203.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||||
|
|||||||
56
go.sum
56
go.sum
@ -138,8 +138,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
|||||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
|
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
|
||||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
|
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
|
||||||
github.com/anchore/stereoscope v0.1.13 h1:32GKF4+t8j0w+l6aOuEaofkPBLjlVCbsBCiVv3/+8u0=
|
github.com/anchore/stereoscope v0.1.16 h1:kNcEH/B16DTPMaUIzJAFFaDX3N4mj06xnLFZcUC1gaE=
|
||||||
github.com/anchore/stereoscope v0.1.13/go.mod h1:S4FMIyKp6dh2Ez8U44m/+krKuZKb+bVY9SNK345sAKs=
|
github.com/anchore/stereoscope v0.1.16/go.mod h1:bXzc5wCnaJzzTTX5wGyUj2bWfwJbphCYLJfFEqXjbd8=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
@ -327,8 +327,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
|||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||||
github.com/docker/cli v29.0.3+incompatible h1:8J+PZIcF2xLd6h5sHPsp5pvvJA+Sr2wGQxHkRl53a1E=
|
github.com/docker/cli v29.1.2+incompatible h1:s4QI7drXpIo78OM+CwuthPsO5kCf8cpNsck5PsLVTH8=
|
||||||
github.com/docker/cli v29.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v29.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||||
@ -390,8 +390,8 @@ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/github/go-spdx/v2 v2.3.4 h1:6VNAsYWvQge+SOeoubTlH81MY21d5uekXNIRGfXMNXo=
|
github.com/github/go-spdx/v2 v2.3.5 h1:rtRQmzDSq2sU/F2oTIvNQQ+6oInq7yxex5npgY//bJQ=
|
||||||
github.com/github/go-spdx/v2 v2.3.4/go.mod h1:7LYNCshU2Gj17qZ0heJ5CQUKWWmpd98K7o93K8fJSMk=
|
github.com/github/go-spdx/v2 v2.3.5/go.mod h1:VziiWwQ/hoGS++2ifYyr/za0Ng9rlgMS+c4U7zckrDs=
|
||||||
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
|
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
|
||||||
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||||
github.com/gkampitakis/go-snaps v0.5.18 h1:oZaQoonWI4KX3c9LNSWsxby8SM6EL+mex4KgLjzfIWg=
|
github.com/gkampitakis/go-snaps v0.5.18 h1:oZaQoonWI4KX3c9LNSWsxby8SM6EL+mex4KgLjzfIWg=
|
||||||
@ -402,8 +402,8 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
|||||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM=
|
||||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||||
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
|
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
|
||||||
@ -614,8 +614,8 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77
|
|||||||
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
|
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jedib0t/go-pretty/v6 v6.7.5 h1:9dJSWTJnsXJVVAbvxIFxeHf/JxoJd7GUl5o3UzhtuiM=
|
github.com/jedib0t/go-pretty/v6 v6.7.7 h1:Y1Id3lJ3k4UB8uwWWy3l8EVFnUlx5chR5+VbsofPNX0=
|
||||||
github.com/jedib0t/go-pretty/v6 v6.7.5/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
github.com/jedib0t/go-pretty/v6 v6.7.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
@ -877,8 +877,8 @@ github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
|||||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
||||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||||
@ -1029,8 +1029,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -1070,8 +1070,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -1116,8 +1116,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -1149,8 +1149,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -1226,13 +1226,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -1243,8 +1243,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -1305,8 +1305,8 @@ 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.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.5/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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
@ -3,10 +3,11 @@ package internal
|
|||||||
const (
|
const (
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||||
JSONSchemaVersion = "16.1.1"
|
JSONSchemaVersion = "16.1.2"
|
||||||
|
|
||||||
// Changelog
|
// Changelog
|
||||||
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
|
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
|
||||||
// 16.1.1 - add file executable toolchain and symbol information
|
// 16.1.1 - correct elf package osCpe field according to the document of systemd (also add appCpe field)
|
||||||
|
// 16.1.2 - add file executable toolchain and symbol information
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,46 +0,0 @@
|
|||||||
package file
|
|
||||||
|
|
||||||
// GlobMatch evaluates the given glob pattern against the given "name" string, indicating if there is a match or not.
|
|
||||||
// Source: https://research.swtch.com/glob.go
|
|
||||||
func GlobMatch(pattern, name string) bool {
|
|
||||||
px := 0
|
|
||||||
nx := 0
|
|
||||||
nextPx := 0
|
|
||||||
nextNx := 0
|
|
||||||
for px < len(pattern) || nx < len(name) {
|
|
||||||
if px < len(pattern) {
|
|
||||||
c := pattern[px]
|
|
||||||
switch c {
|
|
||||||
default: // ordinary character
|
|
||||||
if nx < len(name) && name[nx] == c {
|
|
||||||
px++
|
|
||||||
nx++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case '?': // single-character wildcard
|
|
||||||
if nx < len(name) {
|
|
||||||
px++
|
|
||||||
nx++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case '*': // zero-or-more-character wildcard
|
|
||||||
// Try to match at nx.
|
|
||||||
// If that doesn't work out,
|
|
||||||
// restart at nx+1 next.
|
|
||||||
nextPx = px
|
|
||||||
nextNx = nx + 1
|
|
||||||
px++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Mismatch. Maybe restart.
|
|
||||||
if 0 < nextNx && nextNx <= len(name) {
|
|
||||||
px = nextPx
|
|
||||||
nx = nextNx
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Matched all of pattern to all of name. Success.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGlobMatch(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
pattern string
|
|
||||||
data string
|
|
||||||
ok bool
|
|
||||||
}{
|
|
||||||
{"", "", true},
|
|
||||||
{"x", "", false},
|
|
||||||
{"", "x", false},
|
|
||||||
{"abc", "abc", true},
|
|
||||||
{"*", "abc", true},
|
|
||||||
{"*c", "abc", true},
|
|
||||||
{"*b", "abc", false},
|
|
||||||
{"a*", "abc", true},
|
|
||||||
{"b*", "abc", false},
|
|
||||||
{"a*", "a", true},
|
|
||||||
{"*a", "a", true},
|
|
||||||
{"a*b*c*d*e*", "axbxcxdxe", true},
|
|
||||||
{"a*b*c*d*e*", "axbxcxdxexxx", true},
|
|
||||||
{"a*b?c*x", "abxbbxdbxebxczzx", true},
|
|
||||||
{"a*b?c*x", "abxbbxdbxebxczzy", false},
|
|
||||||
{"a*a*a*a*b", strings.Repeat("a", 100), false},
|
|
||||||
{"*x", "xxx", true},
|
|
||||||
{"/home/place/**", "/home/place/a/thing", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
if GlobMatch(test.pattern, test.data) != test.ok {
|
|
||||||
t.Errorf("failed glob='%s' data='%s'", test.pattern, test.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
this file is in a subdirectory
|
||||||
@ -7,14 +7,14 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var expectedZipArchiveEntries = []string{
|
var expectedZipArchiveEntries = []string{
|
||||||
"some-dir" + string(os.PathSeparator),
|
"some-dir/",
|
||||||
filepath.Join("some-dir", "a-file.txt"),
|
"some-dir/a-file.txt",
|
||||||
"b-file.txt",
|
"b-file.txt",
|
||||||
|
"b-file/",
|
||||||
|
"b-file/in-subdir.txt",
|
||||||
"nested.zip",
|
"nested.zip",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,12 +59,6 @@ func createZipArchive(t testing.TB, sourceDirPath, destinationArchivePath string
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertNoError(t testing.TB, fn func() error) func() {
|
|
||||||
return func() {
|
|
||||||
assert.NoError(t, fn())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupZipFileTest encapsulates common test setup work for zip file tests. It returns a cleanup function,
|
// setupZipFileTest encapsulates common test setup work for zip file tests. It returns a cleanup function,
|
||||||
// which should be called (typically deferred) by the caller, the path of the created zip archive, and an error,
|
// which should be called (typically deferred) by the caller, the path of the created zip archive, and an error,
|
||||||
// which should trigger a fatal test failure in the consuming test. The returned cleanup function will never be nil
|
// which should trigger a fatal test failure in the consuming test. The returned cleanup function will never be nil
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
"github.com/mholt/archives"
|
"github.com/mholt/archives"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
|
|
||||||
@ -44,12 +45,17 @@ func (z ZipFileManifest) Add(entry string, info os.FileInfo) {
|
|||||||
z[entry] = info
|
z[entry] = info
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobMatch returns the path keys that match the given value(s).
|
// GlobMatch returns the path keys to files (not directories) that match the given value(s).
|
||||||
func (z ZipFileManifest) GlobMatch(caseInsensitive bool, patterns ...string) []string {
|
func (z ZipFileManifest) GlobMatch(caseInsensitive bool, patterns ...string) []string {
|
||||||
uniqueMatches := strset.New()
|
uniqueMatches := strset.New()
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
for entry := range z {
|
for entry := range z {
|
||||||
|
fileInfo := z[entry]
|
||||||
|
if fileInfo != nil && fileInfo.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// We want to match globs as if entries begin with a leading slash (akin to an absolute path)
|
// We want to match globs as if entries begin with a leading slash (akin to an absolute path)
|
||||||
// so that glob logic is consistent inside and outside of ZIP archives
|
// so that glob logic is consistent inside and outside of ZIP archives
|
||||||
normalizedEntry := normalizeZipEntryName(caseInsensitive, entry)
|
normalizedEntry := normalizeZipEntryName(caseInsensitive, entry)
|
||||||
@ -57,7 +63,13 @@ func (z ZipFileManifest) GlobMatch(caseInsensitive bool, patterns ...string) []s
|
|||||||
if caseInsensitive {
|
if caseInsensitive {
|
||||||
pattern = strings.ToLower(pattern)
|
pattern = strings.ToLower(pattern)
|
||||||
}
|
}
|
||||||
if GlobMatch(pattern, normalizedEntry) {
|
|
||||||
|
matches, err := doublestar.Match(pattern, normalizedEntry)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("error with match pattern '%s', including by default: %v", pattern, err)
|
||||||
|
matches = true
|
||||||
|
}
|
||||||
|
if matches {
|
||||||
uniqueMatches.Add(entry)
|
uniqueMatches.Add(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewZipFileManifest(t *testing.T) {
|
func TestNewZipFileManifest(t *testing.T) {
|
||||||
@ -107,23 +109,27 @@ func TestZipFileManifest_GlobMatch(t *testing.T) {
|
|||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
glob string
|
glob string
|
||||||
expected string
|
expected []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"/b*",
|
"/b*",
|
||||||
"b-file.txt",
|
[]string{"b-file.txt"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"*/a-file.txt",
|
"/b*/**",
|
||||||
"some-dir/a-file.txt",
|
[]string{"b-file.txt", "b-file/in-subdir.txt"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"*/A-file.txt",
|
"**/a-file.txt",
|
||||||
"some-dir/a-file.txt",
|
[]string{"some-dir/a-file.txt"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"**/A-file.txt",
|
||||||
|
[]string{"some-dir/a-file.txt"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"**/*.zip",
|
"**/*.zip",
|
||||||
"nested.zip",
|
[]string{"nested.zip"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,11 +139,7 @@ func TestZipFileManifest_GlobMatch(t *testing.T) {
|
|||||||
|
|
||||||
results := z.GlobMatch(true, glob)
|
results := z.GlobMatch(true, glob)
|
||||||
|
|
||||||
if len(results) == 1 && results[0] == tc.expected {
|
require.ElementsMatch(t, tc.expected, results)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Errorf("unexpected results for glob '%s': %+v", glob, results)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,10 @@ func (i *Index) Remove(id artifact.ID) {
|
|||||||
|
|
||||||
func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
|
func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
|
||||||
for _, mapped := range fromMappedByID(i.fromID, ogID) {
|
for _, mapped := range fromMappedByID(i.fromID, ogID) {
|
||||||
|
// the stale relationship(i.e. if there's an elder ID in either side) should be discarded
|
||||||
|
if len(fromMappedByID(i.toID, mapped.relationship.To.ID())) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
i.Add(artifact.Relationship{
|
i.Add(artifact.Relationship{
|
||||||
From: replacement,
|
From: replacement,
|
||||||
To: mapped.relationship.To,
|
To: mapped.relationship.To,
|
||||||
@ -87,6 +91,10 @@ func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, mapped := range fromMappedByID(i.toID, ogID) {
|
for _, mapped := range fromMappedByID(i.toID, ogID) {
|
||||||
|
// same as the above
|
||||||
|
if len(fromMappedByID(i.fromID, mapped.relationship.To.ID())) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
i.Add(artifact.Relationship{
|
i.Add(artifact.Relationship{
|
||||||
From: mapped.relationship.From,
|
From: mapped.relationship.From,
|
||||||
To: replacement,
|
To: replacement,
|
||||||
|
|||||||
@ -1169,7 +1169,11 @@
|
|||||||
},
|
},
|
||||||
"osCPE": {
|
"osCPE": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)"
|
"description": "OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)\n\nDeprecated: in Syft 2.0 the struct tag will be corrected to `osCpe` to match the systemd spec casing."
|
||||||
|
},
|
||||||
|
"appCpe": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "AppCpe is a CPE name for the upstream Application, as found in NVD CPE search (e.g. cpe:2.3:a:gnu:coreutils:5.0)"
|
||||||
},
|
},
|
||||||
"os": {
|
"os": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -1279,20 +1283,6 @@
|
|||||||
"elfSecurityFeatures": {
|
"elfSecurityFeatures": {
|
||||||
"$ref": "#/$defs/ELFSecurityFeatures",
|
"$ref": "#/$defs/ELFSecurityFeatures",
|
||||||
"description": "ELFSecurityFeatures contains ELF-specific security hardening information when Format is ELF."
|
"description": "ELFSecurityFeatures contains ELF-specific security hardening information when Format is ELF."
|
||||||
},
|
|
||||||
"symbolNames": {
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": "array",
|
|
||||||
"description": "Symbols captures the selection from the symbol table found in the binary."
|
|
||||||
},
|
|
||||||
"toolchains": {
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/$defs/Toolchain"
|
|
||||||
},
|
|
||||||
"type": "array",
|
|
||||||
"description": "Toolchains captures information about the compiler, linker, runtime, or other toolchains used to build (or otherwise exist within) the executable."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -4235,27 +4225,6 @@
|
|||||||
],
|
],
|
||||||
"description": "TerraformLockProviderEntry represents a single provider entry in a Terraform dependency lock file (.terraform.lock.hcl)."
|
"description": "TerraformLockProviderEntry represents a single provider entry in a Terraform dependency lock file (.terraform.lock.hcl)."
|
||||||
},
|
},
|
||||||
"Toolchain": {
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Name is the name of the toolchain (e.g., \"gcc\", \"clang\", \"ld\", etc.)."
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Version is the version of the toolchain."
|
|
||||||
},
|
|
||||||
"kind": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Kind indicates the type of toolchain (e.g., compiler, linker, runtime)."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"name",
|
|
||||||
"kind"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"WordpressPluginEntry": {
|
"WordpressPluginEntry": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"pluginInstallDirectory": {
|
"pluginInstallDirectory": {
|
||||||
|
|||||||
4297
schema/json/schema-16.1.2.json
Normal file
4297
schema/json/schema-16.1.2.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "anchore.io/schema/syft/json/16.1.1/document",
|
"$id": "anchore.io/schema/syft/json/16.1.2/document",
|
||||||
"$ref": "#/$defs/Document",
|
"$ref": "#/$defs/Document",
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"AlpmDbEntry": {
|
"AlpmDbEntry": {
|
||||||
@ -1169,7 +1169,11 @@
|
|||||||
},
|
},
|
||||||
"osCPE": {
|
"osCPE": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)"
|
"description": "OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)\n\nDeprecated: in Syft 2.0 the struct tag will be corrected to `osCpe` to match the systemd spec casing."
|
||||||
|
},
|
||||||
|
"appCpe": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "AppCpe is a CPE name for the upstream Application, as found in NVD CPE search (e.g. cpe:2.3:a:gnu:coreutils:5.0)"
|
||||||
},
|
},
|
||||||
"os": {
|
"os": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -153,11 +153,12 @@ func NewLocationFromImage(accessPath string, ref file.Reference, img *image.Imag
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewLocationFromDirectory creates a new Location representing the given path (extracted from the Reference) relative to the given directory.
|
// NewLocationFromDirectory creates a new Location representing the given path (extracted from the Reference) relative to the given directory.
|
||||||
func NewLocationFromDirectory(responsePath string, ref file.Reference) Location {
|
func NewLocationFromDirectory(responsePath string, fd string, ref file.Reference) Location {
|
||||||
return Location{
|
return Location{
|
||||||
LocationData: LocationData{
|
LocationData: LocationData{
|
||||||
Coordinates: Coordinates{
|
Coordinates: Coordinates{
|
||||||
RealPath: responsePath,
|
RealPath: responsePath,
|
||||||
|
FileSystemID: fd,
|
||||||
},
|
},
|
||||||
AccessPath: responsePath,
|
AccessPath: responsePath,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
urilib "github.com/spdx/gordf/uri"
|
urilib "github.com/spdx/gordf/uri"
|
||||||
@ -49,9 +50,21 @@ func isURIValid(uri string) bool {
|
|||||||
func URIValue(uri string) string {
|
func URIValue(uri string) string {
|
||||||
if strings.ToLower(uri) != "none" {
|
if strings.ToLower(uri) != "none" {
|
||||||
if isURIValid(uri) {
|
if isURIValid(uri) {
|
||||||
return uri
|
return updateForGithub(url.Parse(uri))
|
||||||
}
|
}
|
||||||
return NOASSERTION
|
return NOASSERTION
|
||||||
}
|
}
|
||||||
return NONE
|
return NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Github repository is a valid NPM location but not a valid SPDX DownloadURL
|
||||||
|
func updateForGithub(uri *url.URL, err error) string {
|
||||||
|
if err != nil {
|
||||||
|
return NOASSERTION
|
||||||
|
}
|
||||||
|
updatedLocation := uri.String()
|
||||||
|
if uri.Scheme == "github" {
|
||||||
|
updatedLocation = "https://github.com/" + uri.Opaque
|
||||||
|
}
|
||||||
|
return updatedLocation
|
||||||
|
}
|
||||||
|
|||||||
@ -640,6 +640,16 @@ func Test_DownloadLocation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: "bzr+https://bzr.myproject.org/MyProject/trunk@2019#src/somefile.c",
|
expected: "bzr+https://bzr.myproject.org/MyProject/trunk@2019#src/somefile.c",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "Github Repository",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackage{
|
||||||
|
URL: "github:anchore/syft",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "https://github.com/anchore/syft",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
|||||||
@ -14,10 +14,11 @@ import (
|
|||||||
// the user given root, the base path (if any) to consider as the root, and the current working directory.
|
// the user given root, the base path (if any) to consider as the root, and the current working directory.
|
||||||
// Note: this only works on a real filesystem, not on a virtual filesystem (such as a stereoscope filetree).
|
// Note: this only works on a real filesystem, not on a virtual filesystem (such as a stereoscope filetree).
|
||||||
type ChrootContext struct {
|
type ChrootContext struct {
|
||||||
root string
|
root string
|
||||||
base string
|
rootRelativeToBase string
|
||||||
cwd string
|
base string
|
||||||
cwdRelativeToRoot string
|
cwd string
|
||||||
|
cwdRelativeToRoot string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChrootContextFromCWD(root, base string) (*ChrootContext, error) {
|
func NewChrootContextFromCWD(root, base string) (*ChrootContext, error) {
|
||||||
@ -40,10 +41,26 @@ func NewChrootContext(root, base, cwd string) (*ChrootContext, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we need to track the relative path from root to base (if set) so that request paths can un-apply the base path
|
||||||
|
// changes from any incoming requests.
|
||||||
|
var rootRelativeToBase string
|
||||||
|
if cleanBase != cleanRoot && cleanBase != "" {
|
||||||
|
absRoot := cleanRoot
|
||||||
|
if !filepath.IsAbs(cleanRoot) {
|
||||||
|
absRoot = filepath.Join(cwd, cleanRoot)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootRelativeToBase, err = filepath.Rel(absRoot, cleanBase) // validate that base is within root
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("base path %q is not within root path %q: %w", cleanBase, cleanRoot, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
chroot := &ChrootContext{
|
chroot := &ChrootContext{
|
||||||
root: cleanRoot,
|
root: cleanRoot,
|
||||||
base: cleanBase,
|
rootRelativeToBase: rootRelativeToBase,
|
||||||
cwd: cwd,
|
base: cleanBase,
|
||||||
|
cwd: cwd,
|
||||||
}
|
}
|
||||||
|
|
||||||
return chroot, chroot.ChangeDirectory(cwd)
|
return chroot, chroot.ChangeDirectory(cwd)
|
||||||
@ -125,8 +142,8 @@ func (r ChrootContext) ToNativePath(chrootPath string) (string, error) {
|
|||||||
responsePath := chrootPath
|
responsePath := chrootPath
|
||||||
|
|
||||||
if filepath.IsAbs(responsePath) {
|
if filepath.IsAbs(responsePath) {
|
||||||
// don't allow input to potentially hop above root path
|
// don't allow input to potentially hop above root path (and still un-apply any base paths)
|
||||||
responsePath = path.Join(r.root, responsePath)
|
responsePath = path.Join(r.root, r.rootRelativeToBase, responsePath)
|
||||||
} else {
|
} else {
|
||||||
// ensure we take into account any relative difference between the root path and the CWD for relative requests
|
// ensure we take into account any relative difference between the root path and the CWD for relative requests
|
||||||
responsePath = path.Join(r.cwdRelativeToRoot, responsePath)
|
responsePath = path.Join(r.cwdRelativeToRoot, responsePath)
|
||||||
|
|||||||
@ -452,6 +452,25 @@ func Test_ChrootContext_RequestResponse(t *testing.T) {
|
|||||||
expectedNativePath: absRelOutsidePath,
|
expectedNativePath: absRelOutsidePath,
|
||||||
expectedChrootPath: "to/the/rel-outside.txt",
|
expectedChrootPath: "to/the/rel-outside.txt",
|
||||||
},
|
},
|
||||||
|
// base path within root cases...
|
||||||
|
// note: for absolute input paths, rootRelativeToBase is used to resolve the native path
|
||||||
|
// note: for relative input paths, cwdRelativeToRoot is used (base does not affect relative path resolution)
|
||||||
|
{
|
||||||
|
name: "relative root, abs request, with base",
|
||||||
|
root: relative,
|
||||||
|
base: filepath.Join(relative, "path", "to"),
|
||||||
|
input: "/the/file.txt",
|
||||||
|
expectedNativePath: absPathToTheFile,
|
||||||
|
expectedChrootPath: "/the/file.txt", // ToChrootPath trims base prefix without adding separator
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "abs root, abs request, with base",
|
||||||
|
root: absolute,
|
||||||
|
base: filepath.Join(absolute, "path", "to"),
|
||||||
|
input: "/the/file.txt",
|
||||||
|
expectedNativePath: absPathToTheFile,
|
||||||
|
expectedChrootPath: "/the/file.txt", // ToChrootPath trims base prefix without adding separator
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
@ -480,6 +499,69 @@ func Test_ChrootContext_RequestResponse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewChrootContext_BaseValidation(t *testing.T) {
|
||||||
|
testDir, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
relative := filepath.Join("test-fixtures", "req-resp")
|
||||||
|
absolute := filepath.Join(testDir, relative)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
root string
|
||||||
|
base string
|
||||||
|
cwd string
|
||||||
|
expectedRootRelativeToBase string
|
||||||
|
wantErr require.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "base within root",
|
||||||
|
root: absolute,
|
||||||
|
base: filepath.Join(absolute, "path", "to"),
|
||||||
|
cwd: testDir,
|
||||||
|
expectedRootRelativeToBase: filepath.Join("path", "to"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "base equals root",
|
||||||
|
root: absolute,
|
||||||
|
base: absolute,
|
||||||
|
cwd: testDir,
|
||||||
|
expectedRootRelativeToBase: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty base",
|
||||||
|
root: absolute,
|
||||||
|
base: "",
|
||||||
|
cwd: testDir,
|
||||||
|
expectedRootRelativeToBase: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative root with base",
|
||||||
|
root: relative,
|
||||||
|
base: filepath.Join(absolute, "path", "to"),
|
||||||
|
cwd: testDir,
|
||||||
|
expectedRootRelativeToBase: filepath.Join("path", "to"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.wantErr == nil {
|
||||||
|
tt.wantErr = require.NoError
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err := NewChrootContext(tt.root, tt.base, tt.cwd)
|
||||||
|
tt.wantErr(t, err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRootRelativeToBase, ctx.rootRelativeToBase)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestToNativeGlob(t *testing.T) {
|
func TestToNativeGlob(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@ -190,7 +190,7 @@ func (r *FiletreeResolver) AllLocations(ctx context.Context) <-chan file.Locatio
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case results <- file.NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref):
|
case results <- file.NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), "", ref):
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -984,12 +984,12 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "use file reference for content requests",
|
name: "use file reference for content requests",
|
||||||
location: file.NewLocationFromDirectory("some/place", *existingPath.Reference),
|
location: file.NewLocationFromDirectory("some/place", "", *existingPath.Reference),
|
||||||
expects: "this file has contents",
|
expects: "this file has contents",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error on empty file reference",
|
name: "error on empty file reference",
|
||||||
location: file.NewLocationFromDirectory("doesn't matter", stereoscopeFile.Reference{}),
|
location: file.NewLocationFromDirectory("doesn't matter", "", stereoscopeFile.Reference{}),
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1525,12 +1525,12 @@ func Test_fileResolver_FileContentsByLocation(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "use file reference for content requests",
|
name: "use file reference for content requests",
|
||||||
location: file.NewLocationFromDirectory("some/place", *existingPath.Reference),
|
location: file.NewLocationFromDirectory("some/place", "", *existingPath.Reference),
|
||||||
expects: "this file has contents",
|
expects: "this file has contents",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error on empty file reference",
|
name: "error on empty file reference",
|
||||||
location: file.NewLocationFromDirectory("doesn't matter", stereoscopeFile.Reference{}),
|
location: file.NewLocationFromDirectory("doesn't matter", "", stereoscopeFile.Reference{}),
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -950,7 +950,7 @@ func Test_UnindexedDirectoryResolver_FileContentsByLocation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error on empty file reference",
|
name: "error on empty file reference",
|
||||||
location: file.NewLocationFromDirectory("doesn't matter", stereoscopeFile.Reference{}),
|
location: file.NewLocationFromDirectory("doesn't matter", "", stereoscopeFile.Reference{}),
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,8 +24,13 @@ type ELFBinaryPackageNoteJSONPayload struct {
|
|||||||
Architecture string `json:"architecture,omitempty"`
|
Architecture string `json:"architecture,omitempty"`
|
||||||
|
|
||||||
// OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)
|
// OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)
|
||||||
|
//
|
||||||
|
// Deprecated: in Syft 2.0 the struct tag will be corrected to `osCpe` to match the systemd spec casing.
|
||||||
OSCPE string `json:"osCPE,omitempty"`
|
OSCPE string `json:"osCPE,omitempty"`
|
||||||
|
|
||||||
|
// AppCpe is a CPE name for the upstream Application, as found in NVD CPE search (e.g. cpe:2.3:a:gnu:coreutils:5.0)
|
||||||
|
AppCpe string `json:"appCpe,omitempty"`
|
||||||
|
|
||||||
// OS is the OS name, typically corresponding to ID in os-release (e.g. "fedora")
|
// OS is the OS name, typically corresponding to ID in os-release (e.g. "fedora")
|
||||||
OS string `json:"os,omitempty"`
|
OS string `json:"os,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@ -68,11 +68,15 @@ func osNameAndVersionFromMetadata(metadata elfBinaryPackageNotes) (string, strin
|
|||||||
return os, osVersion
|
return os, osVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata.OSCPE == "" {
|
if metadata.OSCPE == "" { //nolint:staticcheck
|
||||||
|
// best-effort to get the os info
|
||||||
|
if os != "" {
|
||||||
|
return os, ""
|
||||||
|
}
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs, err := cpe.NewAttributes(metadata.OSCPE)
|
attrs, err := cpe.NewAttributes(metadata.OSCPE) //nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields("error", err).Trace("unable to parse cpe attributes for elf binary package")
|
log.WithFields("error", err).Trace("unable to parse cpe attributes for elf binary package")
|
||||||
return "", ""
|
return "", ""
|
||||||
|
|||||||
@ -34,7 +34,10 @@ type elfBinaryPackageNotes struct {
|
|||||||
CPE string `json:"cpe"`
|
CPE string `json:"cpe"`
|
||||||
License string `json:"license"`
|
License string `json:"license"`
|
||||||
pkg.ELFBinaryPackageNoteJSONPayload `json:",inline"`
|
pkg.ELFBinaryPackageNoteJSONPayload `json:",inline"`
|
||||||
Location file.Location `json:"-"`
|
// CorrectOSCPE has the corrected casing for the osCPE field relative to the systemd ELF package metadata "spec" https://systemd.io/ELF_PACKAGE_METADATA/ .
|
||||||
|
// Ideally in syft 2.0 this field should be replaced with the pkg.ELFBinaryPackageNoteJSONPayload.OSCPE field directly (with the struct tag corrected).
|
||||||
|
CorrectOSCPE string `json:"osCpe,omitempty"`
|
||||||
|
Location file.Location `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type elfPackageKey struct {
|
type elfPackageKey struct {
|
||||||
@ -164,9 +167,9 @@ func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var metadata elfBinaryPackageNotes
|
var metadata *elfBinaryPackageNotes
|
||||||
if err := json.Unmarshal(notes, &metadata); err == nil {
|
if metadata, err = unmarshalELFPackageNotesPayload(notes); err == nil {
|
||||||
return &metadata, nil
|
return metadata, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,10 +177,10 @@ func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
|||||||
var header elf64SectionHeader
|
var header elf64SectionHeader
|
||||||
headerSize := binary.Size(header) / 4
|
headerSize := binary.Size(header) / 4
|
||||||
if len(notes) > headerSize {
|
if len(notes) > headerSize {
|
||||||
var metadata elfBinaryPackageNotes
|
var metadata *elfBinaryPackageNotes
|
||||||
newPayload := bytes.TrimRight(notes[headerSize:], "\x00")
|
newPayload := bytes.TrimRight(notes[headerSize:], "\x00")
|
||||||
if err = json.Unmarshal(newPayload, &metadata); err == nil {
|
if metadata, err = unmarshalELFPackageNotesPayload(newPayload); err == nil {
|
||||||
return &metadata, nil
|
return metadata, nil
|
||||||
}
|
}
|
||||||
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to unmarshal ELF package notes as JSON")
|
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to unmarshal ELF package notes as JSON")
|
||||||
}
|
}
|
||||||
@ -186,6 +189,21 @@ func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unmarshalELFPackageNotesPayload(data []byte) (*elfBinaryPackageNotes, error) {
|
||||||
|
var metadata elfBinaryPackageNotes
|
||||||
|
if err := json.Unmarshal(data, &metadata); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to unmarshal ELF package notes payload: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize the os CPE field
|
||||||
|
if metadata.OSCPE == "" { //nolint:staticcheck
|
||||||
|
// ensure the public field is populated for backwards compatibility
|
||||||
|
metadata.OSCPE = metadata.CorrectOSCPE //nolint:staticcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
return &metadata, nil
|
||||||
|
}
|
||||||
|
|
||||||
type elf64SectionHeader struct {
|
type elf64SectionHeader struct {
|
||||||
ShName uint32
|
ShName uint32
|
||||||
ShType uint32
|
ShType uint32
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
extFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||||
@ -112,6 +113,29 @@ func Test_ELFPackageCataloger(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Debian 64 bit binaries w/o os version",
|
||||||
|
fixture: "image-wolfi-64bit-without-version",
|
||||||
|
expected: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "glibc",
|
||||||
|
Version: "2.42-r4",
|
||||||
|
PURL: "pkg:apk/wolfi/glibc@2.42-r4?distro=wolfi",
|
||||||
|
Locations: file.NewLocationSet(
|
||||||
|
file.NewLocationFromDirectory("/lib/libBrokenLocale.so.1",
|
||||||
|
"sha256:559eaef4e501b8e7a150661a94ee8b9ebc63bfca3256953a703f9f82053346f2",
|
||||||
|
*extFile.NewFileReference("/lib/libBrokenLocale.so.1")).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||||
|
),
|
||||||
|
Licenses: pkg.NewLicenseSet(),
|
||||||
|
Type: pkg.ApkPkg,
|
||||||
|
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||||
|
Type: "apk",
|
||||||
|
Architecture: "x86_64",
|
||||||
|
OS: "wolfi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range cases {
|
for _, v := range cases {
|
||||||
@ -126,3 +150,61 @@ func Test_ELFPackageCataloger(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_unmarshalELFPackageNotesPayload(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
payload string
|
||||||
|
wantOSCPE string
|
||||||
|
wantCorrect string
|
||||||
|
wantErr require.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "only osCPE (incorrect) provided",
|
||||||
|
payload: `{"name":"test","version":"1.0","osCPE":"cpe:/o:fedoraproject:fedora:40"}`,
|
||||||
|
wantOSCPE: "cpe:/o:fedoraproject:fedora:40",
|
||||||
|
wantCorrect: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only osCpe (correct) provided",
|
||||||
|
payload: `{"name":"test","version":"1.0","osCpe":"cpe:/o:fedoraproject:fedora:40"}`,
|
||||||
|
wantOSCPE: "cpe:/o:fedoraproject:fedora:40",
|
||||||
|
wantCorrect: "cpe:/o:fedoraproject:fedora:40",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both osCPE and osCpe provided uses osCPE",
|
||||||
|
payload: `{"name":"test","version":"1.0","osCPE":"cpe:/o:fedoraproject:fedora:40","osCpe":"cpe:/o:redhat:rhel:9"}`,
|
||||||
|
wantOSCPE: "cpe:/o:fedoraproject:fedora:40",
|
||||||
|
wantCorrect: "cpe:/o:redhat:rhel:9",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "neither osCPE nor osCpe provided",
|
||||||
|
payload: `{"name":"test","version":"1.0"}`,
|
||||||
|
wantOSCPE: "",
|
||||||
|
wantCorrect: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid JSON",
|
||||||
|
payload: `{invalid}`,
|
||||||
|
wantErr: require.Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.wantErr == nil {
|
||||||
|
tt.wantErr = require.NoError
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := unmarshalELFPackageNotesPayload([]byte(tt.payload))
|
||||||
|
tt.wantErr(t, err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, tt.wantOSCPE, got.OSCPE)
|
||||||
|
require.Equal(t, tt.wantCorrect, got.CorrectOSCPE)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
FROM --platform=linux/amd64 cgr.dev/chainguard/wolfi-base AS build
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=build /lib/libBrokenLocale.so.1 /lib/libBrokenLocale.so.1
|
||||||
|
|
||||||
|
CMD ["/bin/sh"]
|
||||||
|
|
||||||
@ -489,6 +489,40 @@ func TestCataloger(t *testing.T) {
|
|||||||
"netstandard @ 8.0.1425.11118 (/app/netstandard.dll)",
|
"netstandard @ 8.0.1425.11118 (/app/netstandard.dll)",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assertAllDepEntriesInEmbeddedExecutable := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||||
|
t.Helper()
|
||||||
|
for _, p := range pkgs {
|
||||||
|
// assert that all packages DO NOT have an executable associated with it
|
||||||
|
m, ok := p.Metadata.(pkg.DotnetDepsEntry)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected metadata to be of type DotnetDepsEntry")
|
||||||
|
}
|
||||||
|
if len(m.Executables) != 0 {
|
||||||
|
t.Errorf("expected no executables for package %s, found %d", p.Name, len(m.Executables))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := extractMatchingPackage(t, "Newtonsoft.Json", pkgs)
|
||||||
|
expected := pkg.Package{
|
||||||
|
Name: "Newtonsoft.Json",
|
||||||
|
Version: "13.0.3",
|
||||||
|
Locations: file.NewLocationSet(file.NewLocation("/app/dotnetapp.exe")), // important! not this is an exe
|
||||||
|
Language: pkg.Dotnet,
|
||||||
|
Type: pkg.DotnetPkg,
|
||||||
|
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||||
|
Metadata: pkg.DotnetDepsEntry{
|
||||||
|
Name: "Newtonsoft.Json",
|
||||||
|
Version: "13.0.3",
|
||||||
|
Path: "newtonsoft.json/13.0.3",
|
||||||
|
Sha512: "sha512-HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==",
|
||||||
|
HashPath: "newtonsoft.json.13.0.3.nupkg.sha512",
|
||||||
|
Executables: nil, // important!
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgtest.AssertPackagesEqualIgnoreLayers(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
assertAllDepEntriesWithoutExecutables := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
assertAllDepEntriesWithoutExecutables := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
for _, p := range pkgs {
|
for _, p := range pkgs {
|
||||||
@ -895,12 +929,163 @@ func TestCataloger(t *testing.T) {
|
|||||||
name: "combined cataloger (single file)",
|
name: "combined cataloger (single file)",
|
||||||
fixture: "image-net8-app-single-file",
|
fixture: "image-net8-app-single-file",
|
||||||
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
|
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
|
||||||
|
expectedPkgs: []string{"Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
// important: no relationships should be found
|
// extracted libraries from the embedded deps.json
|
||||||
expectedPkgs: []string{
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
"dotnetapp @ 1.0.0.0 (/app/dotnetapp.exe)",
|
"Humanizer.Core.af @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ar @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.az @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.bg @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.bn-BD @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.cs @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.da @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.de @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.el @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.es @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fa @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fi-FI @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fr-BE @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.he @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.hr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.hu @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.hy @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.id @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.is @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.it @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ja @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ko-KR @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ku @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.lv @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ms-MY @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.mt @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.nb @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.nb-NO @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.nl @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.pl @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.pt @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ro @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ru @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sk @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sl @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sr-Latn @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sv @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.th-TH @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.tr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.uk @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.uz-Cyrl-UZ @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.uz-Latn-UZ @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.vi @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.zh-CN @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.zh-Hans @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.zh-Hant @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Newtonsoft.Json @ 13.0.3 (/app/dotnetapp.exe)",
|
||||||
|
"dotnetapp @ 1.0.0 (/app/dotnetapp.exe)",
|
||||||
|
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.exe)",
|
||||||
},
|
},
|
||||||
assertion: assertSingleFileDeployment,
|
expectedRels: []string{
|
||||||
|
"Humanizer @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.af @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.ar @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.az @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.bg @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.bn-BD @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.cs @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.da @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.de @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.el @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.es @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.fa @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.fi-FI @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.fr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.fr-BE @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.he @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.hr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.hu @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.hy @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.id @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.is @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.it @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.ja @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.ko-KR @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.ku @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.lv @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.ms-MY @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.mt @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.nb @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.nb-NO @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.nl @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.pl @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.pt @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.ro @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.ru @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.sk @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.sl @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.sr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.sr-Latn @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.sv @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.th-TH @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.tr @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.uk @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.uz-Cyrl-UZ @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.uz-Latn-UZ @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.vi @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.zh-CN @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.zh-Hans @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer.Core.zh-Hant @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.af @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ar @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.az @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.bg @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.bn-BD @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.cs @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.da @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.de @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.el @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.es @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fa @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fi-FI @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fr @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.fr-BE @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.he @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.hr @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.hu @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.hy @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.id @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.is @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.it @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ja @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ko-KR @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ku @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.lv @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ms-MY @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.mt @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.nb @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.nb-NO @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.nl @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.pl @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.pt @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ro @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.ru @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sk @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sl @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sr @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sr-Latn @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.sv @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.th-TH @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.tr @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.uk @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.uz-Cyrl-UZ @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.uz-Latn-UZ @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.vi @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.zh-CN @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.zh-Hans @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Humanizer.Core.zh-Hant @ 2.14.1 (/app/dotnetapp.exe) [dependency-of] Humanizer @ 2.14.1 (/app/dotnetapp.exe)",
|
||||||
|
"Newtonsoft.Json @ 13.0.3 (/app/dotnetapp.exe) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.exe)",
|
||||||
|
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.exe) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.exe)",
|
||||||
|
},
|
||||||
|
assertion: assertAllDepEntriesInEmbeddedExecutable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pe cataloger (single file)",
|
name: "pe cataloger (single file)",
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package dotnet
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
@ -147,6 +148,18 @@ func isRuntimePackageLocation(loc file.Location) (string, bool) {
|
|||||||
|
|
||||||
// partitionPEs pairs PE files with the deps.json based on directory containment.
|
// partitionPEs pairs PE files with the deps.json based on directory containment.
|
||||||
func partitionPEs(depJsons []logicalDepsJSON, peFiles []logicalPE) ([]logicalDepsJSON, []logicalPE, []logicalDepsJSON) {
|
func partitionPEs(depJsons []logicalDepsJSON, peFiles []logicalPE) ([]logicalDepsJSON, []logicalPE, []logicalDepsJSON) {
|
||||||
|
// if there are any embedded deps.json files in PE files, extract them and add them to the list of deps.json files to process.
|
||||||
|
consideredPEs := file.NewCoordinateSet()
|
||||||
|
for _, pe := range peFiles {
|
||||||
|
if pe.EmbeddedDepsJSON != "" {
|
||||||
|
dep := extractEmbeddedDeps(pe)
|
||||||
|
if dep != nil {
|
||||||
|
depJsons = append(depJsons, *dep)
|
||||||
|
consideredPEs.Add(pe.Location.Coordinates) // mark this PE as already considered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sort deps.json paths from longest to shortest. This is so we are processing the most specific match first.
|
// sort deps.json paths from longest to shortest. This is so we are processing the most specific match first.
|
||||||
sort.Slice(depJsons, func(i, j int) bool {
|
sort.Slice(depJsons, func(i, j int) bool {
|
||||||
return depJsons[i].Location.RealPath > depJsons[j].Location.RealPath
|
return depJsons[i].Location.RealPath > depJsons[j].Location.RealPath
|
||||||
@ -170,7 +183,9 @@ func partitionPEs(depJsons []logicalDepsJSON, peFiles []logicalPE) ([]logicalDep
|
|||||||
// across multiple deps.json files.
|
// across multiple deps.json files.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
// if we did not find a deps.json to associate this PE with, keep track of it for later processing.
|
||||||
|
// also, if we have already considered this PE because it had an embedded deps.json, skip it.
|
||||||
|
if !found && !consideredPEs.Contains(pe.Location.Coordinates) {
|
||||||
remainingPeFiles = append(remainingPeFiles, pe)
|
remainingPeFiles = append(remainingPeFiles, pe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,3 +504,14 @@ func readPEFile(resolver file.Resolver, loc file.Location) (*logicalPE, error) {
|
|||||||
|
|
||||||
return ldpe, nil
|
return ldpe, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractEmbeddedDeps(pe logicalPE) *logicalDepsJSON {
|
||||||
|
doc, err := newDepsJSON(file.NewLocationReadCloser(pe.Location, io.NopCloser(strings.NewReader(pe.EmbeddedDepsJSON))))
|
||||||
|
if err != nil || doc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.Location = pe.Location
|
||||||
|
lDoc := getLogicalDepsJSON(*doc, nil)
|
||||||
|
return &lDoc
|
||||||
|
}
|
||||||
|
|||||||
@ -48,6 +48,9 @@ type CatalogerConfig struct {
|
|||||||
NoProxy []string `yaml:"no-proxy,omitempty" json:"no-proxy,omitempty" mapstructure:"no-proxy"`
|
NoProxy []string `yaml:"no-proxy,omitempty" json:"no-proxy,omitempty" mapstructure:"no-proxy"`
|
||||||
|
|
||||||
MainModuleVersion MainModuleVersionConfig `yaml:"main-module-version" json:"main-module-version" mapstructure:"main-module-version"`
|
MainModuleVersion MainModuleVersionConfig `yaml:"main-module-version" json:"main-module-version" mapstructure:"main-module-version"`
|
||||||
|
|
||||||
|
// Whether to use the golang.org/x/tools/go/packages, which executes golang tooling found on the path in addition to potential network access
|
||||||
|
UsePackagesLib bool `json:"use-packages-lib" yaml:"use-packages-lib" mapstructure:"use-packages-lib"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MainModuleVersionConfig struct {
|
type MainModuleVersionConfig struct {
|
||||||
@ -70,6 +73,7 @@ type MainModuleVersionConfig struct {
|
|||||||
// - setting the default local module cache dir if none is provided
|
// - setting the default local module cache dir if none is provided
|
||||||
func DefaultCatalogerConfig() CatalogerConfig {
|
func DefaultCatalogerConfig() CatalogerConfig {
|
||||||
g := CatalogerConfig{
|
g := CatalogerConfig{
|
||||||
|
UsePackagesLib: true,
|
||||||
MainModuleVersion: DefaultMainModuleVersionConfig(),
|
MainModuleVersion: DefaultMainModuleVersionConfig(),
|
||||||
LocalModCacheDir: defaultGoModDir(),
|
LocalModCacheDir: defaultGoModDir(),
|
||||||
}
|
}
|
||||||
@ -180,6 +184,11 @@ func (g CatalogerConfig) WithMainModuleVersion(input MainModuleVersionConfig) Ca
|
|||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g CatalogerConfig) WithUsePackagesLib(useLib bool) CatalogerConfig {
|
||||||
|
g.UsePackagesLib = useLib
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
func (g MainModuleVersionConfig) WithFromLDFlags(input bool) MainModuleVersionConfig {
|
func (g MainModuleVersionConfig) WithFromLDFlags(input bool) MainModuleVersionConfig {
|
||||||
g.FromLDFlags = input
|
g.FromLDFlags = input
|
||||||
return g
|
return g
|
||||||
|
|||||||
@ -57,6 +57,7 @@ func Test_Config(t *testing.T) {
|
|||||||
Proxies: []string{"https://my.proxy"},
|
Proxies: []string{"https://my.proxy"},
|
||||||
NoProxy: []string{"my.private", "no.proxy"},
|
NoProxy: []string{"my.private", "no.proxy"},
|
||||||
MainModuleVersion: DefaultMainModuleVersionConfig(),
|
MainModuleVersion: DefaultMainModuleVersionConfig(),
|
||||||
|
UsePackagesLib: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -84,6 +85,7 @@ func Test_Config(t *testing.T) {
|
|||||||
Proxies: []string{"https://alt.proxy", "direct"},
|
Proxies: []string{"https://alt.proxy", "direct"},
|
||||||
NoProxy: []string{"alt.no.proxy"},
|
NoProxy: []string{"alt.no.proxy"},
|
||||||
MainModuleVersion: DefaultMainModuleVersionConfig(),
|
MainModuleVersion: DefaultMainModuleVersionConfig(),
|
||||||
|
UsePackagesLib: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,27 +64,23 @@ func newBinaryMetadata(dep *debug.Module, mainModule, goVersion, architecture st
|
|||||||
func packageURL(moduleName, moduleVersion string) string {
|
func packageURL(moduleName, moduleVersion string) string {
|
||||||
// source: https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang
|
// source: https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang
|
||||||
// note: "The version is often empty when a commit is not specified and should be the commit in most cases when available."
|
// note: "The version is often empty when a commit is not specified and should be the commit in most cases when available."
|
||||||
|
if moduleName == "" {
|
||||||
fields := strings.Split(moduleName, "/")
|
|
||||||
if len(fields) == 0 {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace := ""
|
namespace := ""
|
||||||
name := ""
|
name := moduleName
|
||||||
// The subpath is used to point to a subpath inside a package (e.g. pkg:golang/google.golang.org/genproto#googleapis/api/annotations)
|
|
||||||
subpath := ""
|
|
||||||
|
|
||||||
switch len(fields) {
|
// golang PURLs from _modules_ are constructed as pkg:golang/<module>@version, where
|
||||||
case 1:
|
// the full module name often includes multiple segments including `/v#`-type versions, for example:
|
||||||
name = fields[0]
|
// pkg:golang/github.com/cli/cli/v2@2.63.0
|
||||||
case 2:
|
// see: https://github.com/package-url/purl-spec/issues/63
|
||||||
name = fields[1]
|
// and: https://github.com/package-url/purl-spec/blob/main/types-doc/golang-definition.md#subpath-definition
|
||||||
namespace = fields[0]
|
// by setting the namespace this way, it does not escape the slashes:
|
||||||
default:
|
lastSlash := strings.LastIndex(moduleName, "/")
|
||||||
name = fields[2]
|
if lastSlash > 0 && lastSlash < len(moduleName)-1 {
|
||||||
namespace = strings.Join(fields[0:2], "/")
|
name = moduleName[lastSlash+1:]
|
||||||
subpath = strings.Join(fields[3:], "/")
|
namespace = moduleName[0:lastSlash]
|
||||||
}
|
}
|
||||||
|
|
||||||
return packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
@ -93,6 +89,6 @@ func packageURL(moduleName, moduleVersion string) string {
|
|||||||
name,
|
name,
|
||||||
moduleVersion,
|
moduleVersion,
|
||||||
nil,
|
nil,
|
||||||
subpath,
|
"", // subpath is used to reference a specific _package_ within the module
|
||||||
).ToString()
|
).ToString()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,14 +38,14 @@ func Test_packageURL(t *testing.T) {
|
|||||||
Name: "github.com/coreos/go-systemd/v22",
|
Name: "github.com/coreos/go-systemd/v22",
|
||||||
Version: "v22.1.0",
|
Version: "v22.1.0",
|
||||||
},
|
},
|
||||||
expected: "pkg:golang/github.com/coreos/go-systemd@v22.1.0#v22",
|
expected: "pkg:golang/github.com/coreos/go-systemd/v22@v22.1.0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "golang with subpath deep",
|
name: "golang with subpath deep",
|
||||||
pkg: pkg.Package{
|
pkg: pkg.Package{
|
||||||
Name: "google.golang.org/genproto/googleapis/api/annotations",
|
Name: "google.golang.org/genproto/googleapis/api/annotations",
|
||||||
},
|
},
|
||||||
expected: "pkg:golang/google.golang.org/genproto/googleapis#api/annotations",
|
expected: "pkg:golang/google.golang.org/genproto/googleapis/api/annotations",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -98,6 +98,24 @@ func createModuleRelationships(main pkg.Package, deps []pkg.Package) []artifact.
|
|||||||
return relationships
|
return relationships
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// moduleEqual is used to deduplicate go modules especially the sub module may be identical to the main one
|
||||||
|
func moduleEqual(lhs, rhs *debug.Module) bool {
|
||||||
|
if lhs == rhs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if lhs == nil || rhs == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs.Path != rhs.Path ||
|
||||||
|
lhs.Version != rhs.Version ||
|
||||||
|
lhs.Sum != rhs.Sum {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return moduleEqual(lhs.Replace, rhs.Replace)
|
||||||
|
}
|
||||||
|
|
||||||
var emptyModule debug.Module
|
var emptyModule debug.Module
|
||||||
var moduleFromPartialPackageBuild = debug.Module{Path: "command-line-arguments"}
|
var moduleFromPartialPackageBuild = debug.Module{Path: "command-line-arguments"}
|
||||||
|
|
||||||
@ -115,7 +133,9 @@ func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, resolver file.Re
|
|||||||
if dep == nil {
|
if dep == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if moduleEqual(dep, &mod.Main) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
lics := c.licenseResolver.getLicenses(ctx, resolver, dep.Path, dep.Version)
|
lics := c.licenseResolver.getLicenses(ctx, resolver, dep.Path, dep.Version)
|
||||||
gover, experiments := getExperimentsFromVersion(mod.GoVersion)
|
gover, experiments := getExperimentsFromVersion(mod.GoVersion)
|
||||||
|
|
||||||
|
|||||||
@ -278,7 +278,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "github.com/a/b/c",
|
Name: "github.com/a/b/c",
|
||||||
Version: "", // this was (devel) but we cleared it explicitly
|
Version: "", // this was (devel) but we cleared it explicitly
|
||||||
PURL: "pkg:golang/github.com/a/b#c",
|
PURL: "pkg:golang/github.com/a/b/c",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: file.NewLocationSet(
|
Locations: file.NewLocationSet(
|
||||||
@ -847,6 +847,86 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
unmodifiedMain,
|
unmodifiedMain,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "parse a populated mod string and returns packages when a replace directive and synthetic main module 'command line arguments' exists",
|
||||||
|
mod: &extendedBuildInfo{
|
||||||
|
BuildInfo: &debug.BuildInfo{
|
||||||
|
GoVersion: goCompiledVersion,
|
||||||
|
Main: debug.Module{Path: "command-line-arguments", Version: devel},
|
||||||
|
Settings: []debug.BuildSetting{
|
||||||
|
{Key: "GOARCH", Value: archDetails},
|
||||||
|
{Key: "GOOS", Value: "linux"},
|
||||||
|
{Key: "GOAMD64", Value: "v1"},
|
||||||
|
},
|
||||||
|
Path: "command-line-arguments",
|
||||||
|
Deps: []*debug.Module{
|
||||||
|
{
|
||||||
|
Path: "example.com/mylib",
|
||||||
|
Version: "v0.0.0",
|
||||||
|
Replace: &debug.Module{
|
||||||
|
Path: "./mylib",
|
||||||
|
Version: devel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "command-line-arguments",
|
||||||
|
Version: devel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cryptoSettings: nil,
|
||||||
|
arch: archDetails,
|
||||||
|
},
|
||||||
|
expected: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "example.com/mylib",
|
||||||
|
Version: "",
|
||||||
|
PURL: "pkg:golang/example.com/mylib",
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
|
Locations: file.NewLocationSet(
|
||||||
|
file.NewLocationFromCoordinates(
|
||||||
|
file.Coordinates{
|
||||||
|
RealPath: "/a-path",
|
||||||
|
FileSystemID: "layer-id",
|
||||||
|
},
|
||||||
|
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||||
|
),
|
||||||
|
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||||
|
GoCompiledVersion: goCompiledVersion,
|
||||||
|
Architecture: archDetails,
|
||||||
|
H1Digest: "",
|
||||||
|
MainModule: "command-line-arguments",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "command-line-arguments",
|
||||||
|
Version: "",
|
||||||
|
PURL: "pkg:golang/command-line-arguments",
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
|
Locations: file.NewLocationSet(
|
||||||
|
file.NewLocationFromCoordinates(
|
||||||
|
file.Coordinates{
|
||||||
|
RealPath: "/a-path",
|
||||||
|
FileSystemID: "layer-id",
|
||||||
|
},
|
||||||
|
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||||
|
),
|
||||||
|
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||||
|
BuildSettings: pkg.KeyValues{
|
||||||
|
{Key: "GOARCH", Value: "amd64"},
|
||||||
|
{Key: "GOOS", Value: "linux"},
|
||||||
|
{Key: "GOAMD64", Value: "v1"},
|
||||||
|
},
|
||||||
|
GoCompiledVersion: goCompiledVersion,
|
||||||
|
Architecture: archDetails,
|
||||||
|
H1Digest: "",
|
||||||
|
MainModule: "command-line-arguments",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "parse main mod and replace devel with pattern from binary contents",
|
name: "parse main mod and replace devel with pattern from binary contents",
|
||||||
cfg: func() *CatalogerConfig {
|
cfg: func() *CatalogerConfig {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
@ -26,17 +25,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type goModCataloger struct {
|
type goModCataloger struct {
|
||||||
|
usePackagesLib bool
|
||||||
licenseResolver goLicenseResolver
|
licenseResolver goLicenseResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGoModCataloger(opts CatalogerConfig) *goModCataloger {
|
func newGoModCataloger(opts CatalogerConfig) *goModCataloger {
|
||||||
return &goModCataloger{
|
return &goModCataloger{
|
||||||
|
usePackagesLib: opts.UsePackagesLib,
|
||||||
licenseResolver: newGoLicenseResolver(modFileCatalogerName, opts),
|
licenseResolver: newGoLicenseResolver(modFileCatalogerName, opts),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseGoModFile takes a go.mod and tries to resolve and lists all packages discovered.
|
// parseGoModFile takes a go.mod and tries to resolve and lists all packages discovered.
|
||||||
func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) (pkgs []pkg.Package, relationships []artifact.Relationship, err error) {
|
||||||
modDir := filepath.Dir(string(reader.Location.Reference().RealPath))
|
modDir := filepath.Dir(string(reader.Location.Reference().RealPath))
|
||||||
digests, err := parseGoSumFile(resolver, reader)
|
digests, err := parseGoSumFile(resolver, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -48,24 +49,34 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
|
|||||||
scanRoot = dir.Chroot.Base()
|
scanRoot = dir.Chroot.Base()
|
||||||
}
|
}
|
||||||
|
|
||||||
// source analysis using go toolchain if available
|
|
||||||
syftSourcePackages, sourceModules, sourceDependencies, unknownErr := c.loadPackages(modDir, reader.Location)
|
|
||||||
catalogedModules, sourceModuleToPkg := c.catalogModules(ctx, scanRoot, syftSourcePackages, sourceModules, reader, digests)
|
|
||||||
relationships := buildModuleRelationships(catalogedModules, sourceDependencies, sourceModuleToPkg)
|
|
||||||
|
|
||||||
// base case go.mod file parsing
|
// base case go.mod file parsing
|
||||||
modFile, err := c.parseModFileContents(reader)
|
modFile, err := c.parseModFileContents(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// only use mod packages NOT found in source analysis
|
// source analysis using go toolchain if available
|
||||||
|
var sourceModules map[string]*packages.Module
|
||||||
|
var catalogedModules []pkg.Package
|
||||||
|
|
||||||
|
if c.usePackagesLib {
|
||||||
|
var sourcePackages map[string][]pkgInfo
|
||||||
|
var sourceDependencies map[string][]string
|
||||||
|
var sourceModuleToPkg map[string]artifact.Identifiable
|
||||||
|
|
||||||
|
sourcePackages, sourceModules, sourceDependencies, err = c.loadPackages(modDir, reader.Location)
|
||||||
|
catalogedModules, sourceModuleToPkg = c.catalogModules(ctx, scanRoot, sourcePackages, sourceModules, reader, digests)
|
||||||
|
relationships = buildModuleRelationships(catalogedModules, sourceDependencies, sourceModuleToPkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// only use go.mod packages NOT found in source analysis
|
||||||
goModPackages := c.createGoModPackages(ctx, resolver, modFile, sourceModules, reader, digests)
|
goModPackages := c.createGoModPackages(ctx, resolver, modFile, sourceModules, reader, digests)
|
||||||
c.applyReplaceDirectives(ctx, resolver, modFile, goModPackages, reader, digests)
|
c.applyReplaceDirectives(ctx, resolver, modFile, goModPackages, reader, digests)
|
||||||
c.applyExcludeDirectives(modFile, goModPackages)
|
c.applyExcludeDirectives(modFile, goModPackages)
|
||||||
|
|
||||||
finalPkgs := c.assembleResults(catalogedModules, goModPackages)
|
pkgs = c.assembleResults(catalogedModules, goModPackages)
|
||||||
return finalPkgs, relationships, unknownErr
|
|
||||||
|
return pkgs, relationships, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadPackages uses golang.org/x/tools/go/packages to get dependency information.
|
// loadPackages uses golang.org/x/tools/go/packages to get dependency information.
|
||||||
@ -327,7 +338,7 @@ func (c *goModCataloger) createGoModPackages(ctx context.Context, resolver file.
|
|||||||
goModPackages := make(map[string]pkg.Package)
|
goModPackages := make(map[string]pkg.Package)
|
||||||
|
|
||||||
for _, m := range modFile.Require {
|
for _, m := range modFile.Require {
|
||||||
if _, exists := sourceModules[m.Mod.Path]; !exists {
|
if sourceModules == nil || sourceModules[m.Mod.Path] == nil {
|
||||||
lics := c.licenseResolver.getLicenses(ctx, resolver, m.Mod.Path, m.Mod.Version)
|
lics := c.licenseResolver.getLicenses(ctx, resolver, m.Mod.Path, m.Mod.Version)
|
||||||
goModPkg := pkg.Package{
|
goModPkg := pkg.Package{
|
||||||
Name: m.Mod.Path,
|
Name: m.Mod.Path,
|
||||||
@ -392,9 +403,7 @@ func (c *goModCataloger) assembleResults(catalogedPkgs []pkg.Package, goModPacka
|
|||||||
pkgsSlice = append(pkgsSlice, p)
|
pkgsSlice = append(pkgsSlice, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.SliceStable(pkgsSlice, func(i, j int) bool {
|
pkg.Sort(pkgsSlice)
|
||||||
return pkgsSlice[i].Name < pkgsSlice[j].Name
|
|
||||||
})
|
|
||||||
|
|
||||||
return pkgsSlice
|
return pkgsSlice
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,7 +54,7 @@ func TestParseGoMod(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "github.com/anchore/archiver/v3",
|
Name: "github.com/anchore/archiver/v3",
|
||||||
Version: "v3.5.2",
|
Version: "v3.5.2",
|
||||||
PURL: "pkg:golang/github.com/anchore/archiver@v3.5.2#v3",
|
PURL: "pkg:golang/github.com/anchore/archiver/v3@v3.5.2",
|
||||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
@ -111,7 +111,7 @@ func TestParseGoMod(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.fixture, func(t *testing.T) {
|
t.Run(test.fixture, func(t *testing.T) {
|
||||||
c := newGoModCataloger(DefaultCatalogerConfig())
|
c := newGoModCataloger(DefaultCatalogerConfig().WithUsePackagesLib(false))
|
||||||
pkgtest.NewCatalogTester().
|
pkgtest.NewCatalogTester().
|
||||||
FromFile(t, test.fixture).
|
FromFile(t, test.fixture).
|
||||||
Expects(test.expected, nil).
|
Expects(test.expected, nil).
|
||||||
@ -172,7 +172,9 @@ func Test_GoSumHashes(t *testing.T) {
|
|||||||
pkgtest.NewCatalogTester().
|
pkgtest.NewCatalogTester().
|
||||||
FromDirectory(t, test.fixture).
|
FromDirectory(t, test.fixture).
|
||||||
Expects(test.expected, nil).
|
Expects(test.expected, nil).
|
||||||
TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
|
TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{
|
||||||
|
UsePackagesLib: false,
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +191,6 @@ func Test_parseGoSource_packageResolution(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fixturePath string
|
fixturePath string
|
||||||
config CatalogerConfig
|
|
||||||
expectedPkgs []string
|
expectedPkgs []string
|
||||||
expectedRels []string
|
expectedRels []string
|
||||||
expectedLicenses map[string][]string
|
expectedLicenses map[string][]string
|
||||||
@ -333,7 +334,7 @@ func Test_parseGoSource_packageResolution(t *testing.T) {
|
|||||||
t.Errorf("mismatch in licenses (-want +got):\n%s", diff)
|
t.Errorf("mismatch in licenses (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
|
TestCataloger(t, NewGoModuleFileCataloger(DefaultCatalogerConfig().WithUsePackagesLib(true)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -196,6 +196,11 @@ var defaultCandidateAdditions = buildCandidateLookup(
|
|||||||
candidateAddition{AdditionalVendors: []string{"handlebarsjs"}},
|
candidateAddition{AdditionalVendors: []string{"handlebarsjs"}},
|
||||||
},
|
},
|
||||||
// NPM packages
|
// NPM packages
|
||||||
|
{
|
||||||
|
pkg.NpmPkg,
|
||||||
|
candidateKey{PkgName: "next"},
|
||||||
|
candidateAddition{AdditionalProducts: []string{"next.js"}, AdditionalVendors: []string{"vercel"}},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pkg.NpmPkg,
|
pkg.NpmPkg,
|
||||||
candidateKey{PkgName: "hapi"},
|
candidateKey{PkgName: "hapi"},
|
||||||
|
|||||||
259
syft/pkg/cataloger/internal/pe/bundle.go
Normal file
259
syft/pkg/cataloger/internal/pe/bundle.go
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
package pe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"debug/pe"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dotNetBundleSignature is the SHA-256 hash of ".net core bundle" used to identify single-file bundles.
|
||||||
|
var dotNetBundleSignature = []byte{
|
||||||
|
0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38,
|
||||||
|
0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32,
|
||||||
|
0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18,
|
||||||
|
0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae,
|
||||||
|
}
|
||||||
|
|
||||||
|
// dotNetBundleHeader represents the fixed portion of the bundle header (version 1+)
|
||||||
|
type dotNetBundleHeader struct {
|
||||||
|
MajorVersion uint32
|
||||||
|
MinorVersion uint32
|
||||||
|
NumEmbeddedFiles int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// dotNetBundleHeaderV2 represents additional fields in V2+ bundles (.NET 5+)
|
||||||
|
type dotNetBundleHeaderV2 struct {
|
||||||
|
DepsJSONOffset int64
|
||||||
|
DepsJSONSize int64
|
||||||
|
RuntimeConfigJSONOffset int64
|
||||||
|
RuntimeConfigJSONSize int64
|
||||||
|
Flags uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// dotNetFileType represents the type of bundled file in the manifest
|
||||||
|
type dotNetFileType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
dotNetFileTypeUnknown dotNetFileType = iota
|
||||||
|
dotNetFileTypeAssembly
|
||||||
|
dotNetFileTypeNativeBinary
|
||||||
|
dotNetFileTypeDepsJSON
|
||||||
|
dotNetFileTypeRuntimeConfigJSON
|
||||||
|
dotNetFileTypeSymbols
|
||||||
|
)
|
||||||
|
|
||||||
|
// extractDepsJSONFromBundle searches for an embedded deps.json file in a .NET single-file bundle.
|
||||||
|
// When built with PublishSingleFile=true, .NET embeds the application and all dependencies into
|
||||||
|
// the AppHost executable. The bundle marker (8-byte header offset + 32-byte signature) is placed
|
||||||
|
// in a placeholder location within the PE structure, pointing to the bundle header which contains
|
||||||
|
// file entry metadata. For V2+ bundles (.NET 5+), the header includes direct offsets to deps.json;
|
||||||
|
// for V1 bundles (.NET Core 3.x), we parse the manifest to locate it.
|
||||||
|
//
|
||||||
|
// ┌──────────────────────────────────┐
|
||||||
|
// │ PE AppHost Binary │ Standard PE structure
|
||||||
|
// │ ... │
|
||||||
|
// │ [8B offset][32B signature] │ Bundle marker (in placeholder within PE)
|
||||||
|
// │ ... │
|
||||||
|
// ├──────────────────────────────────┤
|
||||||
|
// │ Bundled Files │ Raw file contents (assemblies, deps.json, etc.)
|
||||||
|
// ├──────────────────────────────────┤
|
||||||
|
// │ Bundle Header │ Version info, file count, deps.json offset (V2+)
|
||||||
|
// │ File Manifest │ Per-file: offset, size, type, path
|
||||||
|
// └──────────────────────────────────┘
|
||||||
|
//
|
||||||
|
// Parsing strategy:
|
||||||
|
// 1. Search only the PE portion (using section headers) for the bundle signature
|
||||||
|
// 2. Read 8 bytes before signature to get header offset
|
||||||
|
// 3. Parse header to get deps.json location (V2+) or scan manifest entries (V1)
|
||||||
|
//
|
||||||
|
// See related documentation for more information:
|
||||||
|
// - https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/design.md
|
||||||
|
// - https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/bundler.md
|
||||||
|
// - https://github.com/dotnet/runtime/blob/main/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs
|
||||||
|
// - https://github.com/dotnet/runtime/blob/main/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs
|
||||||
|
// - https://github.com/dotnet/runtime/blob/main/src/native/corehost/bundle/header.h
|
||||||
|
// - https://github.com/dotnet/runtime/blob/main/src/native/corehost/bundle/file_entry.h
|
||||||
|
// - https://github.com/dotnet/runtime/blob/main/src/native/corehost/bundle/file_type.h
|
||||||
|
func extractDepsJSONFromBundle(r io.ReadSeeker, sections []pe.SectionHeader32) (string, error) {
|
||||||
|
headerOffset, err := findBundleHeaderOffset(r, sections)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if headerOffset == 0 {
|
||||||
|
return "", nil // not a .NET single-file bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
return readDepsJSONFromBundleHeader(r, headerOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// findBundleHeaderOffset locates the bundle marker within the PE structure and returns the header offset.
|
||||||
|
// Returns 0 if no bundle marker is found (not a single-file bundle).
|
||||||
|
func findBundleHeaderOffset(r io.ReadSeeker, sections []pe.SectionHeader32) (int64, error) {
|
||||||
|
peEndOffset := calculatePEEndOffset(sections)
|
||||||
|
|
||||||
|
if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
peData := make([]byte, peEndOffset)
|
||||||
|
n, err := io.ReadFull(r, peData)
|
||||||
|
if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
peData = peData[:n]
|
||||||
|
|
||||||
|
idx := bytes.Index(peData, dotNetBundleSignature)
|
||||||
|
if idx == -1 || idx < 8 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// the header offset is stored in the 8 bytes immediately before the signature
|
||||||
|
headerOffset := int64(binary.LittleEndian.Uint64(peData[idx-8 : idx]))
|
||||||
|
return headerOffset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculatePEEndOffset determines where the PE structure ends based on section headers,
|
||||||
|
// adding padding for alignment. This bounds our search for the bundle marker.
|
||||||
|
func calculatePEEndOffset(sections []pe.SectionHeader32) int64 {
|
||||||
|
var peEndOffset int64
|
||||||
|
for _, sec := range sections {
|
||||||
|
endOfSection := int64(sec.PointerToRawData) + int64(sec.SizeOfRawData)
|
||||||
|
if endOfSection > peEndOffset {
|
||||||
|
peEndOffset = endOfSection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add buffer for alignment padding after sections
|
||||||
|
return peEndOffset + 4096
|
||||||
|
}
|
||||||
|
|
||||||
|
// readDepsJSONFromBundleHeader parses the bundle header at the given offset and extracts deps.json content.
|
||||||
|
func readDepsJSONFromBundleHeader(r io.ReadSeeker, headerOffset int64) (string, error) {
|
||||||
|
if _, err := r.Seek(headerOffset, io.SeekStart); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var header dotNetBundleHeader
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip bundle ID (7-bit length-prefixed string)
|
||||||
|
if err := skipDotNetString(r); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// for V2+ bundles (.NET 5+), read deps.json location directly from header
|
||||||
|
if header.MajorVersion >= 2 {
|
||||||
|
var headerV2 dotNetBundleHeaderV2
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &headerV2); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if headerV2.DepsJSONSize > 0 && headerV2.DepsJSONOffset > 0 {
|
||||||
|
return readDepsJSONAtOffset(r, headerV2.DepsJSONOffset, headerV2.DepsJSONSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for V1 bundles (.NET Core 3.x) or if V2 header doesn't have deps.json, parse manifest
|
||||||
|
return findDepsJSONInManifest(r, header.NumEmbeddedFiles, header.MajorVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// skipDotNetString skips a 7-bit length-prefixed string (.NET BinaryWriter format)
|
||||||
|
func skipDotNetString(r io.ReadSeeker) error {
|
||||||
|
length, err := read7BitEncodedInt(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = r.Seek(int64(length), io.SeekCurrent)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// read7BitEncodedInt reads a .NET 7-bit encoded integer (variable-length encoding used by BinaryWriter)
|
||||||
|
func read7BitEncodedInt(r io.Reader) (int, error) {
|
||||||
|
result := 0
|
||||||
|
shift := 0
|
||||||
|
for {
|
||||||
|
var b [1]byte
|
||||||
|
if _, err := r.Read(b[:]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
result |= int(b[0]&0x7F) << shift
|
||||||
|
if b[0]&0x80 == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
shift += 7
|
||||||
|
if shift >= 35 { // prevent overflow
|
||||||
|
return 0, errors.New("invalid 7-bit encoded int")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readDepsJSONAtOffset reads deps.json content at a specific offset using seeks (avoiding loading entire file)
|
||||||
|
func readDepsJSONAtOffset(r io.ReadSeeker, offset, size int64) (string, error) {
|
||||||
|
if _, err := r.Seek(offset, io.SeekStart); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to seek to deps.json at offset %d: %w", offset, err)
|
||||||
|
}
|
||||||
|
data := make([]byte, size)
|
||||||
|
if _, err := io.ReadFull(r, data); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read deps.json (%d bytes): %w", size, err)
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findDepsJSONInManifest parses manifest entries to find deps.json (for V1 bundles or fallback)
|
||||||
|
func findDepsJSONInManifest(r io.ReadSeeker, numFiles int32, majorVersion uint32) (string, error) {
|
||||||
|
for i := int32(0); i < numFiles; i++ {
|
||||||
|
var offset, size int64
|
||||||
|
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &offset); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &size); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// V6+ bundles (.NET 6+) have compressed size field
|
||||||
|
if majorVersion >= 6 {
|
||||||
|
var compressedSize int64
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &compressedSize); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileType dotNetFileType
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &fileType); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip relativePath string
|
||||||
|
if err := skipDotNetString(r); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileType == dotNetFileTypeDepsJSON && size > 0 {
|
||||||
|
// save current position to resume manifest parsing if needed
|
||||||
|
currentPos, err := r.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// read deps.json content
|
||||||
|
content, err := readDepsJSONAtOffset(r, offset, size)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore position (in case caller needs to continue)
|
||||||
|
if _, err := r.Seek(currentPos, io.SeekStart); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
58
syft/pkg/cataloger/internal/pe/bundle_test.go
Normal file
58
syft/pkg/cataloger/internal/pe/bundle_test.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package pe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_extractDepsJSONFromBundle_Versions(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fixture string
|
||||||
|
path string
|
||||||
|
wantDepsJSON bool // true if deps.json should be found
|
||||||
|
wantJSONContain string // string that should be in the JSON (varies by .NET version)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "V1 bundle (.NET Core 3.1)",
|
||||||
|
fixture: "image-dotnet31-single-file",
|
||||||
|
path: "/app/hello.exe",
|
||||||
|
wantDepsJSON: true,
|
||||||
|
wantJSONContain: "runtimeOptions", // .NET Core 3.1 uses runtimeOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "V2 bundle (.NET 5)",
|
||||||
|
fixture: "image-dotnet5-single-file",
|
||||||
|
path: "/app/hello.exe",
|
||||||
|
wantDepsJSON: true,
|
||||||
|
wantJSONContain: "runtimeTarget", // .NET 5+ uses runtimeTarget
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "V6 bundle (.NET 6)",
|
||||||
|
fixture: "image-dotnet6-single-file",
|
||||||
|
path: "/app/hello.exe",
|
||||||
|
wantDepsJSON: true,
|
||||||
|
wantJSONContain: "runtimeTarget", // .NET 6+ uses runtimeTarget
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
reader := fixtureFile(t, tt.fixture, tt.path)
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
got, err := Read(reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if tt.wantDepsJSON {
|
||||||
|
assert.NotEmpty(t, got.EmbeddedDepsJSON, "expected deps.json to be extracted from bundle")
|
||||||
|
// verify it looks like valid JSON for this .NET version
|
||||||
|
assert.Contains(t, got.EmbeddedDepsJSON, tt.wantJSONContain, "deps.json should contain expected field")
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, got.EmbeddedDepsJSON, "expected no deps.json in non-bundle file")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,6 +34,10 @@ type File struct {
|
|||||||
// understand if this executable is even a .NET application.
|
// understand if this executable is even a .NET application.
|
||||||
CLR *CLREvidence
|
CLR *CLREvidence
|
||||||
|
|
||||||
|
// EmbeddedDepsJSON is the contents of an embedded deps.json file found within the PE file, if any.
|
||||||
|
// This is typical when using the PublishSingleFile build option.
|
||||||
|
EmbeddedDepsJSON string
|
||||||
|
|
||||||
// VersionResources is a map of version resource keys to their values found in the VERSIONINFO resource directory.
|
// VersionResources is a map of version resource keys to their values found in the VERSIONINFO resource directory.
|
||||||
VersionResources map[string]string
|
VersionResources map[string]string
|
||||||
}
|
}
|
||||||
@ -153,7 +157,7 @@ func Read(f file.LocationReadCloser) (*File, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sections, _, err := parsePEFile(r)
|
sections, sectionHeaders, err := parsePEFile(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to parse PE sections: %w", err)
|
return nil, fmt.Errorf("unable to parse PE sections: %w", err)
|
||||||
}
|
}
|
||||||
@ -171,9 +175,15 @@ func Read(f file.LocationReadCloser) (*File, error) {
|
|||||||
return nil, fmt.Errorf("unable to parse PE CLR directory: %w", err)
|
return nil, fmt.Errorf("unable to parse PE CLR directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
embeddedDepsJSON, err := extractDepsJSONFromBundle(r, sectionHeaders)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to extract embedded deps.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &File{
|
return &File{
|
||||||
Location: f.Location,
|
Location: f.Location,
|
||||||
CLR: c,
|
CLR: c,
|
||||||
|
EmbeddedDepsJSON: embeddedDepsJSON,
|
||||||
VersionResources: versionResources,
|
VersionResources: versionResources,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package pe
|
package pe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
@ -14,13 +16,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_Read_DotNetDetection(t *testing.T) {
|
func Test_Read_DotNetDetection(t *testing.T) {
|
||||||
|
singleFileDepsJSON, err := os.ReadFile("test-fixtures/net8-app-single-file.deps.json")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fixture string
|
fixture string
|
||||||
path string
|
path string
|
||||||
wantVR map[string]string
|
wantVR map[string]string
|
||||||
wantCLR bool
|
wantCLR bool
|
||||||
wantErr require.ErrorAssertionFunc
|
wantDepsJSON string
|
||||||
|
wantErr require.ErrorAssertionFunc
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "newtonsoft",
|
name: "newtonsoft",
|
||||||
@ -114,7 +120,8 @@ func Test_Read_DotNetDetection(t *testing.T) {
|
|||||||
"ProductVersion": "1.0.0",
|
"ProductVersion": "1.0.0",
|
||||||
"Assembly Version": "1.0.0.0",
|
"Assembly Version": "1.0.0.0",
|
||||||
},
|
},
|
||||||
wantErr: require.NoError,
|
wantDepsJSON: string(singleFileDepsJSON),
|
||||||
|
wantErr: require.NoError,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +144,11 @@ func Test_Read_DotNetDetection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, tt.wantCLR, got.CLR.HasEvidenceOfCLR())
|
assert.Equal(t, tt.wantCLR, got.CLR.HasEvidenceOfCLR())
|
||||||
|
|
||||||
|
if d := cmp.Diff(tt.wantDepsJSON, got.EmbeddedDepsJSON); d != "" {
|
||||||
|
fmt.Printf("got embedded deps.json: %s\n", got.EmbeddedDepsJSON)
|
||||||
|
t.Errorf("unexpected deps.json location (-want +got): %s", d)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
syft/pkg/cataloger/internal/pe/test-fixtures/Makefile
Normal file
19
syft/pkg/cataloger/internal/pe/test-fixtures/Makefile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FINGERPRINT_FILE=cache.fingerprint
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := fixtures
|
||||||
|
|
||||||
|
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||||
|
fixtures:
|
||||||
|
@echo "nothing to do"
|
||||||
|
|
||||||
|
# requirement 2: 'fingerprint' goal to determine if cache should be busted
|
||||||
|
fingerprint: $(FINGERPRINT_FILE)
|
||||||
|
|
||||||
|
# requirement 3: always recalculate fingerprint based on source
|
||||||
|
.PHONY: $(FINGERPRINT_FILE)
|
||||||
|
$(FINGERPRINT_FILE):
|
||||||
|
@find Makefile **/Dockerfile **/src/** -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||||
|
|
||||||
|
# requirement 4: 'clean' goal to remove generated test fixtures
|
||||||
|
clean:
|
||||||
|
rm -f $(FINGERPRINT_FILE)
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY src/ .
|
||||||
|
RUN dotnet publish -c Release -r win-x64 \
|
||||||
|
-p:PublishSingleFile=true \
|
||||||
|
-p:SelfContained=true \
|
||||||
|
-o /app
|
||||||
|
|
||||||
|
FROM busybox
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/hello.exe .
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Hello
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY src/ .
|
||||||
|
RUN dotnet publish -c Release -r win-x64 \
|
||||||
|
-p:PublishSingleFile=true \
|
||||||
|
-p:SelfContained=true \
|
||||||
|
-o /app
|
||||||
|
|
||||||
|
FROM busybox
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/hello.exe .
|
||||||
@ -0,0 +1 @@
|
|||||||
|
System.Console.WriteLine("Hello");
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY src/ .
|
||||||
|
RUN dotnet publish -c Release -r win-x64 \
|
||||||
|
-p:PublishSingleFile=true \
|
||||||
|
-p:SelfContained=true \
|
||||||
|
-o /app
|
||||||
|
|
||||||
|
FROM busybox
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/hello.exe .
|
||||||
@ -0,0 +1 @@
|
|||||||
|
System.Console.WriteLine("Hello");
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
File diff suppressed because it is too large
Load Diff
@ -184,7 +184,7 @@ func (j *archiveParser) parse(ctx context.Context, parentPkg *pkg.Package) ([]pk
|
|||||||
relationships = append(relationships, nestedRelationships...)
|
relationships = append(relationships, nestedRelationships...)
|
||||||
} else {
|
} else {
|
||||||
// .jar and .war files are present in archives, are others? or generally just consider them top-level?
|
// .jar and .war files are present in archives, are others? or generally just consider them top-level?
|
||||||
nestedArchives := j.fileManifest.GlobMatch(true, "*.jar", "*.war")
|
nestedArchives := j.fileManifest.GlobMatch(true, "**/*.jar", "**/*.war")
|
||||||
if len(nestedArchives) > 0 {
|
if len(nestedArchives) > 0 {
|
||||||
slices.Sort(nestedArchives)
|
slices.Sort(nestedArchives)
|
||||||
errs = unknown.Appendf(errs, j.location, "nested archives not cataloged: %v", strings.Join(nestedArchives, ", "))
|
errs = unknown.Appendf(errs, j.location, "nested archives not cataloged: %v", strings.Join(nestedArchives, ", "))
|
||||||
@ -252,10 +252,7 @@ func (j *archiveParser) discoverMainPackage(ctx context.Context) (*pkg.Package,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
name, version, lics, parsedPom, err := j.discoverNameVersionLicense(ctx, manifest)
|
name, version, lics, parsedPom := j.discoverNameVersionLicense(ctx, manifest)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var pkgPomProject *pkg.JavaPomProject
|
var pkgPomProject *pkg.JavaPomProject
|
||||||
if parsedPom != nil {
|
if parsedPom != nil {
|
||||||
pkgPomProject = newPomProject(ctx, j.maven, parsedPom.path, parsedPom.project)
|
pkgPomProject = newPomProject(ctx, j.maven, parsedPom.path, parsedPom.project)
|
||||||
@ -280,7 +277,7 @@ func (j *archiveParser) discoverMainPackage(ctx context.Context) (*pkg.Package,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest *pkg.JavaManifest) (string, string, []pkg.License, *parsedPomProject, error) {
|
func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest *pkg.JavaManifest) (string, string, []pkg.License, *parsedPomProject) {
|
||||||
// we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest
|
// we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest
|
||||||
// TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar
|
// TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar
|
||||||
lics := pkg.NewLicensesFromLocationWithContext(ctx, j.location, selectLicenses(manifest)...)
|
lics := pkg.NewLicensesFromLocationWithContext(ctx, j.location, selectLicenses(manifest)...)
|
||||||
@ -300,10 +297,7 @@ func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(lics) == 0 {
|
if len(lics) == 0 {
|
||||||
fileLicenses, err := j.getLicenseFromFileInArchive(ctx)
|
fileLicenses := j.getLicenseFromFileInArchive(ctx)
|
||||||
if err != nil {
|
|
||||||
return "", "", nil, parsedPom, err
|
|
||||||
}
|
|
||||||
if fileLicenses != nil {
|
if fileLicenses != nil {
|
||||||
lics = append(lics, fileLicenses...)
|
lics = append(lics, fileLicenses...)
|
||||||
}
|
}
|
||||||
@ -317,7 +311,7 @@ func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest
|
|||||||
lics = j.findLicenseFromJavaMetadata(ctx, groupID, artifactID, version, parsedPom, manifest)
|
lics = j.findLicenseFromJavaMetadata(ctx, groupID, artifactID, version, parsedPom, manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
return artifactID, version, lics, parsedPom, nil
|
return artifactID, version, lics, parsedPom
|
||||||
}
|
}
|
||||||
|
|
||||||
// findLicenseFromJavaMetadata attempts to find license information from all available maven metadata properties and pom info
|
// findLicenseFromJavaMetadata attempts to find license information from all available maven metadata properties and pom info
|
||||||
@ -562,7 +556,7 @@ func getDigestsFromArchive(ctx context.Context, archivePath string) ([]file.Dige
|
|||||||
return digests, nil
|
return digests, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *archiveParser) getLicenseFromFileInArchive(ctx context.Context) ([]pkg.License, error) {
|
func (j *archiveParser) getLicenseFromFileInArchive(ctx context.Context) []pkg.License {
|
||||||
// prefer identified licenses, fall back to unknown
|
// prefer identified licenses, fall back to unknown
|
||||||
var identified []pkg.License
|
var identified []pkg.License
|
||||||
var unidentified []pkg.License
|
var unidentified []pkg.License
|
||||||
@ -578,7 +572,8 @@ func (j *archiveParser) getLicenseFromFileInArchive(ctx context.Context) ([]pkg.
|
|||||||
if len(licenseMatches) > 0 {
|
if len(licenseMatches) > 0 {
|
||||||
contents, err := intFile.ContentsFromZip(ctx, j.archivePath, licenseMatches...)
|
contents, err := intFile.ContentsFromZip(ctx, j.archivePath, licenseMatches...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to extract java license (%s): %w", j.location, err)
|
log.Debugf("unable to extract java license (%s): %w", j.location, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, licenseMatch := range licenseMatches {
|
for _, licenseMatch := range licenseMatches {
|
||||||
@ -602,10 +597,10 @@ func (j *archiveParser) getLicenseFromFileInArchive(ctx context.Context) ([]pkg.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(identified) == 0 {
|
if len(identified) == 0 {
|
||||||
return unidentified, nil
|
return unidentified
|
||||||
}
|
}
|
||||||
|
|
||||||
return identified, nil
|
return identified
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *archiveParser) discoverPkgsFromNestedArchives(ctx context.Context, parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
|
func (j *archiveParser) discoverPkgsFromNestedArchives(ctx context.Context, parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pomPropertiesGlob = "*pom.properties"
|
const pomPropertiesGlob = "**/*pom.properties"
|
||||||
|
|
||||||
func parsePomProperties(path string, reader io.Reader) (*pkg.JavaPomProperties, error) {
|
func parsePomProperties(path string, reader io.Reader) (*pkg.JavaPomProperties, error) {
|
||||||
var props pkg.JavaPomProperties
|
var props pkg.JavaPomProperties
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pomXMLGlob = "*pom.xml"
|
pomXMLGlob = "**/*pom.xml"
|
||||||
pomCatalogerName = "java-pom-cataloger"
|
pomCatalogerName = "java-pom-cataloger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -27,8 +27,6 @@ func packageLockDependencySpecifier(p pkg.Package) dependency.Specification {
|
|||||||
// if the package url is valid, include the name from the package url since this is likely an alias
|
// if the package url is valid, include the name from the package url since this is likely an alias
|
||||||
var fullName = fmt.Sprintf("%s/%s", purl.Namespace, purl.Name)
|
var fullName = fmt.Sprintf("%s/%s", purl.Namespace, purl.Name)
|
||||||
requires = append(requires, fullName)
|
requires = append(requires, fullName)
|
||||||
} else {
|
|
||||||
fmt.Println("error", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requires = append(requires, name)
|
requires = append(requires, name)
|
||||||
|
|||||||
@ -6,9 +6,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
"github.com/go-viper/mapstructure/v2"
|
"github.com/go-viper/mapstructure/v2"
|
||||||
|
|
||||||
intFile "github.com/anchore/syft/internal/file"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
@ -141,7 +141,7 @@ func getFieldType(key, in string) any {
|
|||||||
// of egg metadata (as opposed to a directory that contains more metadata
|
// of egg metadata (as opposed to a directory that contains more metadata
|
||||||
// files).
|
// files).
|
||||||
func isEggRegularFile(path string) bool {
|
func isEggRegularFile(path string) bool {
|
||||||
return intFile.GlobMatch(eggInfoGlob, path)
|
return doublestar.MatchUnvalidated(eggInfoGlob, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// determineSitePackagesRootPath returns the path of the site packages root,
|
// determineSitePackagesRootPath returns the path of the site packages root,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
// NewGemFileLockCataloger returns a new Bundler cataloger object tailored for parsing index-oriented files (e.g. Gemfile.lock).
|
// NewGemFileLockCataloger returns a new Bundler cataloger object tailored for parsing index-oriented files (e.g. Gemfile.lock).
|
||||||
func NewGemFileLockCataloger() pkg.Cataloger {
|
func NewGemFileLockCataloger() pkg.Cataloger {
|
||||||
return generic.NewCataloger("ruby-gemfile-cataloger").
|
return generic.NewCataloger("ruby-gemfile-cataloger").
|
||||||
WithParserByGlobs(parseGemFileLockEntries, "**/Gemfile.lock")
|
WithParserByGlobs(parseGemFileLockEntries, "**/Gemfile.lock", "**/Gemfile.next.lock")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInstalledGemSpecCataloger returns a new Bundler cataloger object tailored for detecting installations of gems (e.g. Gemspec).
|
// NewInstalledGemSpecCataloger returns a new Bundler cataloger object tailored for detecting installations of gems (e.g. Gemspec).
|
||||||
|
|||||||
@ -17,6 +17,7 @@ func Test_GemFileLock_Globs(t *testing.T) {
|
|||||||
fixture: "test-fixtures/glob-paths",
|
fixture: "test-fixtures/glob-paths",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"src/Gemfile.lock",
|
"src/Gemfile.lock",
|
||||||
|
"src/Gemfile.next.lock",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
bogus
|
||||||
Loading…
x
Reference in New Issue
Block a user