diff --git a/.binny.yaml b/.binny.yaml index 006b6229e97..84d3368bb66 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -1,11 +1,21 @@ tools: + # we want to use a pinned version of binny to manage the toolchain (so binny manages itself!) - name: binny version: - want: v0.6.2 + want: v0.6.3 method: github-release with: repo: anchore/binny + # used to produce SBOMs during release + - name: syft + version: + want: latest + method: github-release + with: + repo: anchore/syft + + # used to sign mac binaries at release - name: quill version: want: v0.4.1 @@ -13,13 +23,15 @@ tools: with: repo: anchore/quill + # used for linting - name: golangci-lint version: - want: v1.55.2 + want: v1.56.1 method: github-release with: repo: golangci/golangci-lint + # used for showing the changelog at release - name: glow version: want: v1.5.1 @@ -27,13 +39,15 @@ tools: with: repo: charmbracelet/glow + # used for signing the checksums file at release - name: cosign version: - want: v2.2.1 + want: v2.2.3 method: github-release with: repo: sigstore/cosign + # used in integration tests to verify JSON schemas - name: yajsv version: want: v1.4.1 @@ -41,13 +55,15 @@ tools: with: repo: neilpa/yajsv + # used to release all artifacts - name: goreleaser version: - want: v1.22.1 + want: v1.24.0 method: github-release with: repo: goreleaser/goreleaser + # used for organizing imports during static analysis - name: gosimports version: want: v0.3.8 @@ -55,6 +71,7 @@ tools: with: repo: rinchsan/gosimports + # used at release to generate the changelog - name: chronicle version: want: v0.8.0 @@ -62,6 +79,7 @@ tools: with: repo: anchore/chronicle + # used during static analysis for license compliance - name: bouncer version: want: v0.4.0 @@ -69,6 +87,7 @@ tools: with: repo: wagoodman/go-bouncer + # used for showing benchmark testing - name: benchstat version: want: latest @@ -81,16 +100,18 @@ tools: entrypoint: cmd/benchstat module: golang.org/x/perf + # used for running all local and CI tasks - name: task version: - want: v3.31.0 + want: v3.34.1 method: github-release with: repo: go-task/task + # used for triggering a release - name: gh version: - want: v2.39.1 + want: v2.43.1 method: github-release with: repo: cli/cli diff --git a/.chronicle.yaml b/.chronicle.yaml index b74726c4143..58ba668f691 100644 --- a/.chronicle.yaml +++ b/.chronicle.yaml @@ -1,2 +1,78 @@ enforce-v0: true # don't make breaking-change label bump major version before 1.0. -title: "" \ No newline at end of file +title: "" + +github: + host: github.com + include-issue-pr-authors: true + include-issue-prs: true + include-issues-not-planned: false + include-prs: true + include-issues: true + include-unlabeled-issues: true + include-unlabeled-prs: true + issues-require-linked-prs: false + consider-pr-merge-commits: true + + exclude-labels: + - duplicate + - question + - invalid + - wontfix + - wont-fix + - release-ignore + - changelog-ignore + - ignore + + changes: + + - name: security-fixes + title: Security Fixes + semver-field: patch + labels: + - security + - vulnerability + + - name: added-feature + title: Added Features + semver-field: minor + labels: + - enhancement + - feature + - minor + + - name: bug-fix + title: Bug Fixes + semver-field: patch + labels: + - bug + - fix + - bug-fix + - patch + + - name: breaking-feature + title: Breaking Changes + semver-field: major + labels: + - breaking + - backwards-incompatible + - breaking-change + - breaking-feature + - major + - detected-breaking-change + + - name: removed-feature + title: Removed Features + semver-field: major + labels: + - removed + + - name: deprecated-feature + title: Deprecated Features + semver-field: minor + labels: + - deprecated + + - name: unknown + title: Additional Changes + semver-field: "" + labels: [] diff --git a/.github/scripts/check_binary_fixture_size.sh b/.github/scripts/check_binary_fixture_size.sh new file mode 100755 index 00000000000..764824820cc --- /dev/null +++ b/.github/scripts/check_binary_fixture_size.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# current limit for fixture size +size=1000 + +if [ $# -eq 0 ]; then + echo "Usage: $0 " + exit 1 +fi + +directory="$1" + +# Remove trailing slash using parameter expansion +directory="${directory%/}" + +if [ ! -d "$directory" ]; then + echo "Directory not found: $directory" + exit 1 +fi + +found_large_files=0 +while IFS= read -r -d '' file; do + if [ $(wc -c < "$file") -gt $size ]; then + echo "File $file is greater than ${size} bytes." + found_large_files=1 + fi +done < <(find "$directory" -type f -print0) + +if [ "$found_large_files" -eq 1 ]; then + echo "Script failed: Some files are greater than ${size} bytes." + exit 1 +else + echo "All files in $directory and its subdirectories are ${size} bytes or smaller. Check passed." + exit 0 +fi + diff --git a/.github/scripts/labeler.py b/.github/scripts/labeler.py index 7ddeee7b895..8b608293f6b 100644 --- a/.github/scripts/labeler.py +++ b/.github/scripts/labeler.py @@ -8,6 +8,12 @@ DRY_RUN = False +JSON_SCHEMA_LABEL = "json-schema" + +# note: we can't use "breaking-change" as the label since that might be applied manually by a user. This is a +# distinct label that we can use to indicate that the label was applied (or removed) by automation. +BREAKING_CHANGE_LABEL = "detected-breaking-change" + def main(changed_files: str | None = None, merge_base_schema_files: str | None = None): global DRY_RUN @@ -67,17 +73,18 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None = # if there is a new or modified schema, we should add the "json-schema" label to the PR... if new_schema_files or removed_or_modified_schema_files: print("\nAdding json-schema label...") - add_label(pr_number, "json-schema") + add_label(pr_number, JSON_SCHEMA_LABEL) + else: - remove_label(pr_number, "json-schema") + remove_label(pr_number, JSON_SCHEMA_LABEL) # new schema files should be scrutinized, comparing the latest and added versions to see if it's a breaking # change (major version bump). Warn about it on the PR via adding a breaking-change label... if is_breaking_change(new_schema_files, og_json_schema_files[-1]): print("\nBreaking change detected...") - add_label(pr_number, "breaking-change") + add_label(pr_number, BREAKING_CHANGE_LABEL) else: - remove_label(pr_number, "breaking-change") + remove_label(pr_number, BREAKING_CHANGE_LABEL) # modifying an existing schema could be a breaking change, we should warn about it on the PR via a comment... # removing schema files should never be allowed, we should warn about it on the PR via a comment... @@ -169,7 +176,8 @@ def filter_to_schema_files(list_of_files: list[str]) -> list[str]: def list_json_schema_files() -> list[str]: # list files in "schema/json" directory matching the pattern of "schema-*.json" - return sort_json_schema_files(list(glob.glob("schema/json/schema-*.json"))) + # special case: always ignore the "latest" schema file + return sort_json_schema_files([f for f in glob.glob("schema/json/schema-*.json") if "latest" not in f]) def run(command: str, **kwargs) -> subprocess.CompletedProcess: @@ -190,7 +198,7 @@ def sort_json_schema_files(files: list[str]) -> list[str]: # so that "schema/json/schema-1.2.1.json" comes before "schema/json/schema-1.12.1.json". versions = [get_semver(file) for file in files if file] - versions = sorted(versions, key=lambda s: [int(u) for u in s.split('.')]) + versions = sorted(versions, key=lambda s: [int(u) for u in s.split('.') if "." in s]) return [f"schema/json/schema-{version}.json" for version in versions] diff --git a/.github/scripts/labeler_test.py b/.github/scripts/labeler_test.py index 33e583f82de..36eebd18c9f 100644 --- a/.github/scripts/labeler_test.py +++ b/.github/scripts/labeler_test.py @@ -60,6 +60,11 @@ def test_sort_json_schema_files(self): expected_sorted_files = ["schema/json/schema-1.2.1.json", "schema/json/schema-1.12.1.json"] self.assertEqual(labeler.sort_json_schema_files(files), expected_sorted_files) + # ensure that "latest" doesn't cause a problem and is ultimately ignored + files = ["schema/json/schema-1.12.1.json", "schema/json/schema-_bogus.json"] + expected_sorted_files = ["schema/json/schema-_bogus.json", "schema/json/schema-1.12.1.json"] + self.assertEqual(labeler.sort_json_schema_files(files), expected_sorted_files) + if __name__ == "__main__": unittest.main() diff --git a/.github/workflows/benchmark-testing.yaml b/.github/workflows/benchmark-testing.yaml index a87fa1f9d18..71ec8014ae1 100644 --- a/.github/workflows/benchmark-testing.yaml +++ b/.github/workflows/benchmark-testing.yaml @@ -23,7 +23,7 @@ jobs: uses: ./.github/actions/bootstrap - name: Restore base benchmark result - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: test/results/benchmark-main.txt # use base sha for PR or new commit hash for main push in benchmark result key @@ -39,13 +39,13 @@ jobs: OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters echo "result=$OUTPUT" >> $GITHUB_OUTPUT - - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: name: benchmark-test-results path: test/results/**/* - name: Update PR benchmark results comment - uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0 + uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 #v2.9.0 continue-on-error: true with: header: benchmark diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 75cd2f5bace..46cf933b73f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,7 +20,7 @@ permissions: jobs: analyze: name: Analyze - runs-on: ubuntu-latest + runs-on: ubuntu-22.04-4core-16gb permissions: security-events: write @@ -39,13 +39,13 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 - name: Install Go - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0 with: go-version-file: go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 #v2.22.8 + uses: github/codeql-action/init@379614612a29c9e28f31f39a59013eb8012a51f0 #v3.24.3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@407ffafae6a767df3e0230c3df91b6443ae8df75 #v2.22.8 + uses: github/codeql-action/autobuild@379614612a29c9e28f31f39a59013eb8012a51f0 #v3.24.3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 #v2.22.8 + uses: github/codeql-action/analyze@379614612a29c9e28f31f39a59013eb8012a51f0 #v3.24.3 diff --git a/.github/workflows/dependabot-automation.yaml b/.github/workflows/dependabot-automation.yaml new file mode 100644 index 00000000000..1c3ded297c7 --- /dev/null +++ b/.github/workflows/dependabot-automation.yaml @@ -0,0 +1,10 @@ +name: Dependabot Automation +on: + pull_request: + +permissions: + pull-requests: write + +jobs: + run: + uses: anchore/workflows/.github/workflows/dependabot-automation.yaml@main diff --git a/.github/workflows/labeler.yaml b/.github/workflows/detect-schema-changes.yaml similarity index 89% rename from .github/workflows/labeler.yaml rename to .github/workflows/detect-schema-changes.yaml index 1b17a271d58..edc794dabdd 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/detect-schema-changes.yaml @@ -40,7 +40,7 @@ jobs: - name: Delete existing comment if: ${{ hashFiles( env.CI_COMMENT_FILE ) == '' }} - uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0 + uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 #v2.9.0 with: header: ${{ env.COMMENT_HEADER }} hide: true @@ -48,7 +48,7 @@ jobs: - name: Add comment if: ${{ hashFiles( env.CI_COMMENT_FILE ) != '' }} - uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0 + uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 #v2.9.0 with: header: ${{ env.COMMENT_HEADER }} path: ${{ env.CI_COMMENT_FILE }} diff --git a/.github/workflows/release-homebrew.yaml b/.github/workflows/release-homebrew.yaml index 0be645a635e..2395831fc10 100644 --- a/.github/workflows/release-homebrew.yaml +++ b/.github/workflows/release-homebrew.yaml @@ -30,7 +30,7 @@ jobs: echo -n "commit=$(git rev-parse HEAD)" | tee -a $GITHUB_OUTPUT - name: Update Homebrew formula - uses: dawidd6/action-homebrew-bump-formula@d3667e5ae14df19579e4414897498e3e88f2f458 # v3.10.0 + uses: dawidd6/action-homebrew-bump-formula@baf2b60c51fc1f8453c884b0c61052668a71bd1d # v3.11.0 with: token: ${{ secrets.HOMEBREW_TOKEN }} org: anchore @@ -38,7 +38,7 @@ jobs: tag: ${{ steps.release_info.outputs.tag_name }} revision: ${{ steps.git_info.outputs.commit }} - - uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1 + - uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486 #v3.16.2 if: ${{ failure() }} with: status: ${{ job.status }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c2cc18e1c0b..72e2431308c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -94,6 +94,8 @@ jobs: permissions: contents: write packages: write + # required for goreleaser signs section with cosign + id-token: write steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 with: @@ -141,12 +143,12 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }} - - uses: anchore/sbom-action@fd74a6fb98a204a1ad35bbfae0122c1a302ff88b #v0.15.0 + - uses: anchore/sbom-action@b6a39da80722a2cb0ef5d197531764a89b5d48c3 #v0.15.8 continue-on-error: true with: artifact-name: sbom.spdx.json - - uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1 + - uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486 #v3.16.2 continue-on-error: true with: status: ${{ job.status }} diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index 8613b9d33be..d20189f56e3 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -50,7 +50,7 @@ jobs: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2 + - uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 #v6.0.0 with: signoff: true delete-branch: true diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index 34abd163c8f..9f6d12ffcd7 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -33,7 +33,7 @@ jobs: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2 + - uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 #v6.0.0 with: signoff: true delete-branch: true @@ -45,7 +45,7 @@ jobs: Update CPE dictionary index based on the latest available CPE dictionary token: ${{ steps.generate-token.outputs.token }} - - uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1 + - uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486 #v3.16.2 with: status: ${{ job.status }} fields: workflow,eventName,job diff --git a/.github/workflows/update-stereoscope-release.yml b/.github/workflows/update-stereoscope-release.yml index ed5d00a631c..158bb9b03c2 100644 --- a/.github/workflows/update-stereoscope-release.yml +++ b/.github/workflows/update-stereoscope-release.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 - - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 + - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0 with: go-version: ${{ env.GO_VERSION }} stable: ${{ env.GO_STABLE_VERSION }} @@ -44,7 +44,7 @@ jobs: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2 + - uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 #v6.0.0 with: signoff: true delete-branch: true diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 862244d9bea..5bd3507a121 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -36,32 +36,38 @@ jobs: - name: Bootstrap environment uses: ./.github/actions/bootstrap + - name: Restore file executable test-fixture cache + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 + with: + path: syft/file/cataloger/executable/test-fixtures/bin + key: ${{ runner.os }}-unit-file-executable-cache-${{ hashFiles( 'syft/file/cataloger/executable/test-fixtures/cache.fingerprint' ) }} + - name: Restore Java test-fixture cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }} - name: Restore RPM test-fixture cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: syft/pkg/cataloger/redhat/test-fixtures/rpms key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/redhat/test-fixtures/rpms.fingerprint' ) }} - name: Restore go binary test-fixture cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }} - name: Restore binary cataloger test-fixture cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: - path: syft/pkg/cataloger/binary/test-fixtures/classifiers/dynamic + path: syft/pkg/cataloger/binary/test-fixtures/classifiers/bin key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }} - name: Restore Kernel test-fixture cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: syft/pkg/cataloger/kernel/test-fixtures/cache key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }} @@ -84,10 +90,10 @@ jobs: run: make validate-cyclonedx-schema - name: Restore integration test cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: - path: ${{ github.workspace }}/test/integration/test-fixtures/cache - key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }} + path: ${{ github.workspace }}/cmd/syft/internal/test/integration/test-fixtures/cache + key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('/cmd/syft/internal/test/integration/test-fixtures/cache.fingerprint') }} - name: Run integration tests run: make integration @@ -110,7 +116,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). # see https://github.com/actions/upload-artifact/issues/199 for more info - name: Upload snapshot artifacts - uses: actions/cache/save@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache/save@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: # 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 @@ -134,7 +140,7 @@ jobs: - name: Download snapshot build id: snapshot-cache - uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: | snapshot @@ -152,7 +158,7 @@ jobs: - name: Restore install.sh test image cache id: install-test-image-cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: ${{ github.workspace }}/test/install/cache key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }} @@ -187,7 +193,7 @@ jobs: - name: Download snapshot build id: snapshot-cache - uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: | snapshot @@ -202,7 +208,7 @@ jobs: - name: Restore docker image cache for compare testing id: mac-compare-testing-cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: image.tar key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }} @@ -226,14 +232,14 @@ jobs: uses: ./.github/actions/bootstrap - name: Restore CLI test-fixture cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: ${{ github.workspace }}/test/cli/test-fixtures/cache key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }} - name: Download snapshot build id: snapshot-cache - uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0 with: path: | snapshot diff --git a/.gitignore b/.gitignore index d80e8f4ffb7..3ba60dc502b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ VERSION /test/results coverage.txt *.log -test/integration/test-fixtures/**/go.sum # probable archives .images diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 1a12342d05d..762d87d3efa 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -247,3 +247,28 @@ docker_manifests: - ghcr.io/anchore/syft:{{.Tag}}-arm64v8 - ghcr.io/anchore/syft:{{.Tag}}-ppc64le - ghcr.io/anchore/syft:{{.Tag}}-s390x + +sboms: + - artifacts: archive + # this is relative to the snapshot/dist directory, not the root of the repo + cmd: ../.tool/syft + documents: + - "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}.sbom" + args: + - "scan" + - "$artifact" + - "--output" + - "json=$document" + +signs: + - cmd: .tool/cosign + signature: "${artifact}.sig" + certificate: "${artifact}.pem" + args: + - "sign-blob" + - "--oidc-issuer=https://token.actions.githubusercontent.com" + - "--output-certificate=${certificate}" + - "--output-signature=${signature}" + - "${artifact}" + - "--yes" + artifacts: checksum diff --git a/DEVELOPING.md b/DEVELOPING.md index 7d5db769a44..2308c51b123 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -234,7 +234,7 @@ rough outline how that works: the `/test` directory. These tests should focus on correctness of functionality in depth. % test coverage metrics only considers unit tests and no other forms of testing. -- `integration`: located within `test/integration`, these tests focus on the behavior surfaced by the common library +- `integration`: located within `cmd/syft/internal/test/integration`, these tests focus on the behavior surfaced by the common library entrypoints from the `syft` package and make light assertions about the results surfaced. Additionally, these tests tend to make diversity assertions for enum-like objects, ensuring that as enum values are added to a definition that integration tests will automatically fail if no test attempts to use that enum value. For more details see diff --git a/README.md b/README.md index bcc8876d64a..96ad78ac916 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ For commercial support options with Syft or Grype, please [contact Anchore](http - Ruby (gem) - Rust (cargo.lock) - Swift (cocoapods, swift-package-manager) +- Wordpress plugins ## Installation @@ -109,21 +110,21 @@ nix-shell -p syft To generate an SBOM for a container image: -``` +```bash syft ``` The above output includes only software that is visible in the container (i.e., the squashed representation of the image). To include software from all image layers in the SBOM, regardless of its presence in the final image, provide `--scope all-layers`: -``` +```bash syft --scope all-layers ``` ### Supported sources -Syft can generate a SBOM from a variety of sources: +Syft can generate an SBOM from a variety of sources: -``` +```bash # catalog a container image archive (from the result of `docker image save ...`, `podman save ...`, or `skopeo copy` commands) syft path/to/image.tar @@ -155,69 +156,114 @@ If docker is not present, then the Podman daemon is attempted next, followed by This default behavior can be overridden with the `default-image-pull-source` configuration option (See [Configuration](https://github.com/anchore/syft#configuration) for more details). -### Default Cataloger Configuration by scan type - -Syft uses different default sets of catalogers depending on what it is scanning: a container image or a directory on disk. The default catalogers for an image scan assumes that package installation steps have already been completed. For example, Syft will identify Python packages that have egg or wheel metadata files under a site-packages directory, since this indicates software actually installed on an image. - -However, if you are scanning a directory, Syft doesn't assume that all relevant software is installed, and will use catalogers that can identify declared dependencies that may not yet be installed on the final system: for example, dependencies listed in a Python requirements.txt. - -You can override the list of enabled/disabled catalogers by using the "catalogers" keyword in the [Syft configuration file](https://github.com/anchore/syft#configuration). - -##### Image Scanning: -- alpmdb -- apkdb -- binary -- dotnet-deps -- dotnet-portable-executable -- dpkgdb -- go-module-binary -- graalvm-native-image -- java -- javascript-package -- linux-kernel -- nix-store -- php-composer-installed -- portage -- python-package -- rpm-db -- ruby-gemspec -- sbom - -##### Directory Scanning: -- alpmdb -- apkdb -- binary -- cocoapods -- conan -- dartlang-lock -- dotnet-deps -- dotnet-portable-executable -- dpkgdb -- elixir-mix-lock -- erlang-rebar-lock -- go-mod-file -- go-module-binary -- graalvm-native-image -- haskell -- java -- java-gradle-lockfile -- java-pom -- javascript-lock -- linux-kernel -- nix-store -- php-composer-lock -- portage -- python-index -- python-package -- rpm-db -- rpm-file -- ruby-gemfile -- rust-cargo-lock -- sbom -- swift-package-manager - -##### Non Default: -- cargo-auditable-binary + +### File selection + +By default, Syft will catalog file details and digests for files that are owned by discovered packages. You can change this behavior by using the `SYFT_FILE_METADATA_SELECTION` environment variable or the `file.metadata.selection` configuration option. The options are: + +- `all`: capture all files from the search space +- `owned-by-package`: capture only files owned by packages (default) +- `none`: disable capturing any file information + + +### Package cataloger selection + +#### Concepts + +> [!IMPORTANT] +> Syft uses a different set of catalogers by default when scanning files directly than it does when scanning images + +The catalogers for an image scan assumes that package installation steps have already been completed. For example, Syft will identify Python packages that have egg or wheel metadata files under a `site-packages` directory, since this is how the canonical tooling `pip` installs python packages. + +The catalogers for a directory scan will look for installed software as well as declared dependencies that are not necessarily installed. For example, dependencies listed in a Python `requirements.txt`. + +This default set of catalogers being dynamic is critical as this allows Syft to be used in a variety of contexts while still generating accurate SBOMs. +Overriding the set of default catalogers is not recommended for most purposes, however, is possible if needed. + +Catalogers can be referenced in two different ways: +- *by name*: the exact cataloger name (e.g. `java-pom-cataloger` or `java-archive-cataloger`) +- *by tag*: a tag that is associated with a cataloger (e.g. `java`) + +Syft can take lists of references on the CLI or in the application configuration to define which catalogers to use. + +You can **set** the list of catalogers explicitly to use with the `--override-default-catalogers` CLI flag, accepting a comma-separated list of cataloger names or tags. + +You can also **add** to, **remove** from, or **sub-select** catalogers to use within the default set of catalogers by using the `--select-catalogers` CLI flag. + - To **sub-select** catalogers simply provide a tag (e.g. `--select-catalogers TAG`). Catalogers will always be selected from the default set of catalogers (e.g. `--select-catalogers java,go` will select all the `java` catalogers in the default set and all the `go` catalogers in the default set). + - To **add** a cataloger prefix the cataloger name with `+` (e.g. `--select-catalogers +NAME`). Added catalogers will _always be added_ regardless of removals, filtering, or other defaults. + - To **remove** a cataloger prefix the cataloger name or tag with `-` (e.g. `--select-catalogers -NAME_OR_TAG`). Catalogers are removed from the set of default catalogers after processing any sub-selections. + +These rules and the dynamic default cataloger sets approximates to the following logic: + +``` +image_catalogers = all_catalogers AND catalogers_tagged("image") + +directory_catalogers = all_catalogers AND catalogers_tagged("directory") + +default_catalogers = image_catalogers OR directory_catalogers + +sub_selected_catalogers = default_catalogers INTERSECT catalogers_tagged(TAG) [ UNION sub_selected_catalogers ... ] + +base_catalogers = default_catalogers OR sub_selected_catalogers + +final_set = (base_catalogers SUBTRACT removed_catalogers) UNION added_catalogers +``` + + +#### Examples + +Only scan for python related packages with catalogers appropriate for the source type (image or directory): +```bash +syft --select-catalogers "python" +# results in the following catalogers being used: +# - python-installed-package-cataloger +``` + +Same command, but the set of catalogers changes based on what is being analyzed (in this case a directory): +```bash +syft --select-catalogers "python" +# results in the following catalogers being used: +# - python-installed-package-cataloger +# - python-package-cataloger +``` + +Use the default set of catalogers and add a cataloger to the set: +```bash +syft ... --catalogers "+sbom-cataloger" +``` + +Use the default set of catalogers but remove any catalogers that deal with RPMs: +```bash +syft ... --catalogers "-rpm" +``` + +Only scan with catalogers that: +- are tagged with "go" +- always use the sbom-cataloger +- are appropriate for the source type (image or directory) + +```bash +syft --select-catalogers "go,+sbom-cataloger" +# results in the following catalogers being used: +# - go-module-binary-cataloger +# - sbom-cataloger +``` + +Scan with all catalogers that deal with binary analysis, regardless of the source type: +```bash +syft ... --override-default-catalogers "binary" +# results in the following catalogers being used: +# - binary-cataloger +# - cargo-auditable-binary-cataloger +# - dotnet-portable-executable-cataloger +# - go-module-binary-cataloger +``` + +Only scan with the specific `go-module-binary-cataloger` and `go-module-file-cataloger` catalogers: +```bash +syft ... --override-default-catalogers "go-module-binary-cataloger,go-module-file-cataloger" +``` + ### Excluding file paths @@ -260,7 +306,7 @@ Where the `formats` available are: ## Using templates -Syft lets you define custom output formats, using [Go templates](https://pkg.go.dev/text/template). Here's how it works: +Syft lets you define custom output formats, using [Go templates](https://pkg.go.dev/text/template) relative to the Syft JSON output. Here's how it works: - Define your format as a Go template, and save this template as a file. @@ -274,9 +320,9 @@ Syft lets you define custom output formats, using [Go templates](https://pkg.go. Here's what the `csv.tmpl` file might look like: ```gotemplate -"Package","Version Installed","Found by" -{{- range .Artifacts}} -"{{.Name}}","{{.Version}}","{{.FoundBy}}" +"Package","Version Installed", "Found by" +{{- range .artifacts}} +"{{.name}}","{{.version}}","{{.foundBy}}" {{- end}} ``` @@ -293,6 +339,9 @@ Syft also includes a vast array of utility templating functions from [sprig](htt Lastly, Syft has custom templating functions defined in `./syft/format/template/encoder.go` to help parse the passed-in JSON structs. +> [!NOTE] +> If you have templates being used before Syft v0.102.0 that are no longer working. This is because templating keys were relative to the internal go structs before this version whereas now the keys are relative to the Syft JSON output. To get the legacy behavior back you can set the `format.template.legacy` option to `true` in your configuration. + ## Multiple outputs Syft can also output _multiple_ files in differing formats by appending @@ -457,87 +506,60 @@ Configuration options (example values are the default): ```yaml # the output format(s) of the SBOM report (options: syft-table, syft-text, syft-json, spdx-json, ...) -# same as -o, --output, and SYFT_OUTPUT env var # to specify multiple output files in differing formats, use a list: # output: # - "syft-json=" # - "spdx-json=" -output: "syft-table" +# SYFT_OUTPUT env var / -o, --output flags +output: + - "syft-table" # suppress all output (except for the SBOM report) -# same as -q ; SYFT_QUIET env var +# SYFT_QUIET env var / -q flag quiet: false -# same as --file; write output report to a file (default is to write to stdout) -file: "" - # enable/disable checking for application updates on startup -# same as SYFT_CHECK_FOR_APP_UPDATE env var +# SYFT_CHECK_FOR_APP_UPDATE env var check-for-app-update: true -# allows users to specify which image source should be used to generate the sbom -# valid values are: registry, docker, podman -default-image-pull-source: "" +# maximum number of workers used to process the list of package catalogers in parallel +parallelism: 1 -# a list of globs to exclude from scanning. same as --exclude ; for example: +# a list of globs to exclude from scanning, for example: # exclude: # - "/etc/**" # - "./out/**/*.json" +# SYFT_EXCLUDE env var / --exclude flag exclude: [] -# allows users to exclude synthetic binary packages from the sbom -# these packages are removed if an overlap with a non-synthetic package is found -exclude-binary-overlap-by-ownership: true - # os and/or architecture to use when referencing container images (e.g. "windows/armv6" or "arm64") -# same as --platform; SYFT_PLATFORM env var +# SYFT_PLATFORM env var / --platform flag platform: "" +# the search space to look for file and package data (options: all-layers, squashed) +# SYFT_SCOPE env var +scope: "squashed" + # set the list of package catalogers to use when generating the SBOM # default = empty (cataloger set determined automatically by the source type [image or file/directory]) -# catalogers: -# - alpm-db-cataloger -# - apkdb-cataloger -# - binary-cataloger -# - cargo-auditable-binary-cataloger -# - cocoapods-cataloger -# - conan-cataloger -# - dartlang-lock-cataloger -# - dotnet-deps-cataloger -# - dotnet-portable-executable-cataloger -# - dpkg-db-cataloger -# - elixir-mix-lock-cataloger -# - erlang-rebar-lock-cataloger -# - go-module-file-cataloger -# - go-module-binary-cataloger -# - graalvm-native-image-cataloger -# - haskell-cataloger -# - java-cataloger -# - java-gradle-lockfile-cataloger -# - java-pom-cataloger -# - javascript-lock-cataloger -# - javascript-package-cataloger -# - linux-kernel-cataloger -# - nix-store-cataloger -# - php-composer-installed-cataloger -# - php-composer-lock-cataloger -# - portage-cataloger -# - python-package-cataloger -# - python-installed-package-cataloger -# - rpm-db-cataloger -# - rpm-archive-cataloger -# - ruby-gemfile-cataloger -# - ruby-installed-gemspec-cataloger -# - rust-cargo-lock-cataloger -# - sbom-cataloger -# - spm-cataloger +# Use `syft cataloger list` for a list of catalogers you can specify +# DEPRECATED: please use default-catalogers and select-catalogers configuration options instead +# SYFT_CATALOGERS env var / --catalogers flag catalogers: +# set the base set of catalogers to use (defaults to 'image' or 'directory' depending on the scan source) +# SYFT_DEFAULT_CATALOGERS env var / --override-default-catalogers flag +default-catalogers: [] + +# add, remove, and filter the catalogers to be used +# SYFT_SELECT_CATALOGERS env var / --select-catalogers flag; +select-catalogers: [] # all format configuration format: # default value for all formats that support the "pretty" option (default is unset) + # SYFT_FORMAT_PRETTY env var pretty: # all syft-json format options @@ -545,6 +567,7 @@ format: # include space indention and newlines (inherits default value from 'format.pretty' or 'false' if parent is unset) # note: inherits default value from 'format.pretty' or 'false' if parent is unset + # SYFT_FORMAT_JSON_PRETTY env var pretty: false # transform any syft-json output to conform to an approximation of the v11.0.1 schema. This includes: @@ -554,21 +577,30 @@ format: # that output might not strictly be json schema v11 compliant, however, for consumers that require time to port # over to the final syft 1.0 json output this option can be used to ease the transition. # - # Note: long term support for this option is not guaranteed. + # Note: long term support for this option is not guaranteed (it may change or break at any time). + # SYFT_FORMAT_JSON_LEGACY env var legacy: false # all template format options template: # path to the template file to use when rendering the output with the `template` output format. # Note that all template paths are based on the current syft-json schema. - # same as -t ; SYFT_TEMPLATE_PATH env var + # SYFT_FORMAT_TEMPLATE_PATH env var / -t flag path: "" + + # if true, uses the go structs for the syft-json format for templating. + # if false, uses the syft-json output for templating (which follows the syft JSON schema exactly). + # + # Note: long term support for this option is not guaranteed (it may change or break at any time). + # SYFT_FORMAT_TEMPLATE_LEGACY env var + legacy: false # all spdx-json format options spdx-json: # include space indention and newlines # note: inherits default value from 'format.pretty' or 'false' if parent is unset + # SYFT_FORMAT_SPDX_JSON_PRETTY env var pretty: false # all cyclonedx-json format options @@ -576,6 +608,7 @@ format: # include space indention and newlines # note: inherits default value from 'format.pretty' or 'false' if parent is unset + # SYFT_FORMAT_CYCLONEDX_JSON_PRETTY env var pretty: false # all cyclonedx-xml format options @@ -583,12 +616,41 @@ format: # include space indention # note: inherits default value from 'format.pretty' or 'false' if parent is unset + # SYFT_FORMAT_CYCLONEDX_XML_PRETTY env var pretty: false +file: + + metadata: + # select which files should be captured by the file-metadata cataloger and included in the SBOM. + # Options include: + # - "all": capture all files from the search space + # - "owned-by-package": capture only files owned by packages + # - "none", "": do not capture any files + # SYFT_FILE_METADATA_SELECTION env var + selection: "owned-by-package" + + # the file digest algorithms to use when cataloging files (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512") + # SYFT_FILE_METADATA_DIGESTS env var + digests: + - "sha256" + - "sha1" + + # capture the contents of select files in the SBOM + content: + # skip searching a file entirely if it is above the given size (default = 1MB; unit = bytes) + # SYFT_FILE_CONTENT_SKIP_FILES_ABOVE_SIZE env var + skip-files-above-size: 1048576 + + # file globs for the cataloger to match on + # SYFT_FILE_CONTENT_GLOBS env var + globs: [] + + # cataloging packages is exposed through the packages and power-user subcommands package: - + # search within archives that do contain a file index to search against (zip) # note: for now this only applies to the java package cataloger # SYFT_PACKAGE_SEARCH_INDEXED_ARCHIVES env var @@ -600,14 +662,11 @@ package: # SYFT_PACKAGE_SEARCH_UNINDEXED_ARCHIVES env var search-unindexed-archives: false - cataloger: - # enable/disable cataloging of packages - # SYFT_PACKAGE_CATALOGER_ENABLED env var - enabled: true + # allows users to exclude synthetic binary packages from the sbom + # these packages are removed if an overlap with a non-synthetic package is found + # SYFT_PACKAGE_EXCLUDE_BINARY_OVERLAP_BY_OWNERSHIP env var + exclude-binary-overlap-by-ownership: true - # the search space to look for packages (options: all-layers, squashed) - # same as -s ; SYFT_PACKAGE_CATALOGER_SCOPE env var - scope: "squashed" golang: # search for go package licences in the GOPATH of the system running Syft, note that this is outside the @@ -633,6 +692,24 @@ golang: # SYFT_GOLANG_NOPROXY env var no-proxy: "" + # 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 + # a more accurate version from the binary. + main-module-version: + + # look for LD flags that appear to be setting a version (e.g. -X main.version=1.0.0) + # SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_LD_FLAGS env var + from-ld-flags: true + + # 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. + # SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_BUILD_SETTINGS env var + from-build-settings: true + + # search for semver-like strings in the binary contents. + # SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_CONTENTS env var + from-contents: true + java: maven-url: "https://repo1.maven.org/maven2" max-parent-recursive-depth: 5 @@ -656,55 +733,39 @@ python: # when given an arbitrary constraint will be used (even if that version may not be available/published). guess-unpinned-requirements: false -file-contents: - cataloger: - # enable/disable cataloging of file contents - # SYFT_FILE_CONTENTS_CATALOGER_ENABLED env var - enabled: true - - # the search space to look for file contents (options: all-layers, squashed) - # SYFT_FILE_CONTENTS_CATALOGER_SCOPE env var - scope: "squashed" - - # skip searching a file entirely if it is above the given size (default = 1MB; unit = bytes) - # SYFT_FILE_CONTENTS_SKIP_FILES_ABOVE_SIZE env var - skip-files-above-size: 1048576 - - # file globs for the cataloger to match on - # SYFT_FILE_CONTENTS_GLOBS env var - globs: [] +javascript: + search-remote-licenses: false + npm-base-url: "https://registry.npmjs.org" -file-metadata: - cataloger: - # enable/disable cataloging of file metadata - # SYFT_FILE_METADATA_CATALOGER_ENABLED env var - enabled: true - # the search space to look for file metadata (options: all-layers, squashed) - # SYFT_FILE_METADATA_CATALOGER_SCOPE env var - scope: "squashed" - - # the file digest algorithms to use when cataloging files (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512") - # SYFT_FILE_METADATA_DIGESTS env var - digests: ["sha256"] - -# maximum number of workers used to process the list of package catalogers in parallel -parallelism: 1 - -# options that apply to all scan sources +# configuration for the source that the SBOM is generated from (e.g. a file, directory, or container image) source: # alias name for the source - # SYFT_SOURCE_NAME env var; --source-name flag + # SYFT_SOURCE_NAME env var / --source-name flag name: "" # alias version for the source - # SYFT_SOURCE_VERSION env var; --source-version flag + # SYFT_SOURCE_VERSION env var / --source-version flag version: "" - - # options affecting the file source type + + # base directory for scanning, no links will be followed above this directory, and all paths will be + # reported relative to this directory + # SYFT_SOURCE_BASE_PATH env var + base-path: '' + + # options affecting the file source type file: # the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512") - digests: ["sha256"] + digests: + - "sha256" + + image: + + # allows users to specify which image source should be used to generate the sbom + # valid values are: registry, docker, podman + # SYFT_SOURCE_IMAGE_DEFAULT_PULL_SOURCE env var + default-pull-source: "" + # options when pulling directly from a registry via the "registry:" or "containerd:" scheme registry: @@ -758,15 +819,15 @@ attest: log: # use structured logging - # same as SYFT_LOG_STRUCTURED env var + # SYFT_LOG_STRUCTURED env var structured: false # the log level; note: detailed logging suppress the ETUI - # same as SYFT_LOG_LEVEL env var + # SYFT_LOG_LEVEL env var level: "error" # location to write the log file (default is not to have a log file) - # same as SYFT_LOG_FILE env var + # SYFT_LOG_FILE env var file: "" ``` diff --git a/Taskfile.yaml b/Taskfile.yaml index 1df8b423861..363b10350f5 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -54,6 +54,7 @@ tasks: - task: check-licenses - task: lint - task: check-json-schema-drift + - task: check-binary-fixture-size test: desc: Run all levels of test @@ -169,6 +170,11 @@ tasks: cmds: - .github/scripts/json-schema-drift-check.sh + check-binary-fixture-size: + desc: Ensure that the binary test fixtures are not too large + cmds: + - .github/scripts/check_binary_fixture_size.sh syft/pkg/cataloger/binary/test-fixtures/classifiers/snippets + ## Testing tasks ################################# @@ -179,7 +185,7 @@ tasks: - fixtures vars: TEST_PKGS: - sh: "go list ./... | grep -v {{ .OWNER }}/{{ .PROJECT }}/test | tr '\n' ' '" + sh: "go list ./... | grep -v {{ .OWNER }}/{{ .PROJECT }}/test | grep -v {{ .OWNER }}/{{ .PROJECT }}/cmd/syft/internal/test | tr '\n' ' '" # unit test coverage threshold (in % coverage) COVERAGE_THRESHOLD: 62 @@ -191,7 +197,7 @@ tasks: integration: desc: Run integration tests cmds: - - "go test -v ./test/integration" + - "go test -v ./cmd/syft/internal/test/integration" # exercise most of the CLI with the data race detector - "go run -race cmd/syft/main.go alpine:latest" @@ -259,7 +265,8 @@ tasks: fingerprints: desc: Generate test fixture fingerprints generates: - - test/integration/test-fixtures/cache.fingerprint + - cmd/syft/internal/test/integration/test-fixtures/cache.fingerprint + - syft/file/cataloger/executable/test-fixtures/cache.fingerprint - syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint - syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint - syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint @@ -268,19 +275,21 @@ tasks: - test/install/cache.fingerprint - test/cli/test-fixtures/cache.fingerprint cmds: + # for EXECUTABLE unit test fixtures + - "cd syft/file/cataloger/executable/test-fixtures && make cache.fingerprint" # for IMAGE integration test fixtures - - "cd test/integration/test-fixtures && make cache.fingerprint" - # for BINARY test fixtures + - "cd cmd/syft/internal/test/integration/test-fixtures && make cache.fingerprint" + # for BINARY unit test fixtures - "cd syft/pkg/cataloger/binary/test-fixtures && make cache.fingerprint" - # for JAVA BUILD test fixtures + # for JAVA BUILD unit test fixtures - "cd syft/pkg/cataloger/java/test-fixtures/java-builds && make cache.fingerprint" - # for GO BINARY test fixtures + # for GO BINARY unit test fixtures - "cd syft/pkg/cataloger/golang/test-fixtures/archs && make binaries.fingerprint" - # for RPM test fixtures + # for RPM unit test fixtures - "cd syft/pkg/cataloger/redhat/test-fixtures && make rpms.fingerprint" - # for Kernel test fixtures + # for Kernel unit test fixtures - "cd syft/pkg/cataloger/kernel/test-fixtures && make cache.fingerprint" - # for INSTALL integration test fixtures + # for INSTALL test fixtures - "cd test/install && make cache.fingerprint" # for CLI test fixtures - "cd test/cli/test-fixtures && make cache.fingerprint" @@ -288,6 +297,7 @@ tasks: fixtures: desc: Generate test fixtures cmds: + - "cd syft/file/cataloger/executable/test-fixtures && make" - "cd syft/pkg/cataloger/java/test-fixtures/java-builds && make" - "cd syft/pkg/cataloger/redhat/test-fixtures && make" - "cd syft/pkg/cataloger/binary/test-fixtures && make" diff --git a/cmd/syft/cli/cli.go b/cmd/syft/cli/cli.go index 2c7f44dc3a5..1d1b3cf3a51 100644 --- a/cmd/syft/cli/cli.go +++ b/cmd/syft/cli/cli.go @@ -8,13 +8,8 @@ import ( "github.com/spf13/cobra" "github.com/anchore/clio" - "github.com/anchore/stereoscope" - "github.com/anchore/syft/cmd/syft/cli/commands" - handler "github.com/anchore/syft/cmd/syft/cli/ui" - "github.com/anchore/syft/cmd/syft/internal/ui" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/internal/redact" + "github.com/anchore/syft/cmd/syft/internal" + "github.com/anchore/syft/cmd/syft/internal/commands" ) // Application constructs the `syft packages` command and aliases the root command to `syft packages`. @@ -34,62 +29,32 @@ func Command(id clio.Identification) *cobra.Command { } func create(id clio.Identification, out io.Writer) (clio.Application, *cobra.Command) { - clioCfg := clio.NewSetupConfig(id). - WithGlobalConfigFlag(). // add persistent -c for reading an application config from - WithGlobalLoggingFlags(). // add persistent -v and -q flags tied to the logging config - WithConfigInRootHelp(). // --help on the root command renders the full application config in the help text - WithUIConstructor( - // select a UI based on the logging configuration and state of stdin (if stdin is a tty) - func(cfg clio.Config) ([]clio.UI, error) { - noUI := ui.None(out, cfg.Log.Quiet) - if !cfg.Log.AllowUI(os.Stdin) || cfg.Log.Quiet { - return []clio.UI{noUI}, nil - } - - return []clio.UI{ - ui.New(out, cfg.Log.Quiet, - handler.New(handler.DefaultHandlerConfig()), - ), - noUI, - }, nil - }, - ). - WithInitializers( - func(state *clio.State) error { - // clio is setting up and providing the bus, redact store, and logger to the application. Once loaded, - // we can hoist them into the internal packages for global use. - stereoscope.SetBus(state.Bus) - bus.Set(state.Bus) - - redact.Set(state.RedactStore) - - log.Set(state.Logger) - stereoscope.SetLogger(state.Logger) - - return nil - }, - ). - WithPostRuns(func(state *clio.State, err error) { - stereoscope.Cleanup() - }) + clioCfg := internal.AppClioSetupConfig(id, out) app := clio.New(*clioCfg) // since root is aliased as the packages cmd we need to construct this command first // we also need the command to have information about the `root` options because of this alias - packagesCmd := commands.Packages(app) + scanCmd := commands.Scan(app) - // rootCmd is currently an alias for the packages command - rootCmd := commands.Root(app, packagesCmd) + // root is currently an alias for the scan command + rootCmd := commands.Root(app, scanCmd) // add sub-commands rootCmd.AddCommand( - packagesCmd, + scanCmd, + commands.Packages(app, scanCmd), // this is currently an alias for the scan command + commands.Cataloger(app), commands.Attest(app), commands.Convert(app), clio.VersionCommand(id), cranecmd.NewCmdAuthLogin(id.Name), // syft login uses the same command as crane ) + // note: we would direct cobra to use our writer explicitly with rootCmd.SetOut(out) , however this causes + // deprecation warnings to be shown to stdout via the writer instead of stderr. This is unfortunate since this + // does not appear to be the correct behavior on cobra's part https://github.com/spf13/cobra/issues/1708 . + // In the future this functionality should be restored. + return app, rootCmd } diff --git a/cmd/syft/cli/commands/lib_scribe.go b/cmd/syft/cli/commands/lib_scribe.go deleted file mode 100644 index 4fbe6cdba67..00000000000 --- a/cmd/syft/cli/commands/lib_scribe.go +++ /dev/null @@ -1,61 +0,0 @@ -package commands - -import ( - "fmt" - - "github.com/anchore/clio" - "github.com/anchore/go-logger" - "github.com/anchore/stereoscope" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/sbom" - "github.com/anchore/syft/syft/source" -) - -func LibInitLoggingConfig(logWrapper logger.Logger) { - syft.SetLogger(logWrapper) - stereoscope.SetLogger(logWrapper) -} - -func DefaultPackagesOptions() *packagesOptions { - return defaultPackagesOptions() -} - -type PackagesOptions packagesOptions - -func GetSource(opts *options.Catalog, userInput string, filters ...func(*source.Detection) error) (source.Source, error) { - return getSource(opts, userInput, filters...) -} - -func LibPackagesExec(id clio.Identification, opts *PackagesOptions, userInput string, l logger.Logger, enable_log bool) (*sbom.SBOM, error) { - if enable_log { - LibInitLoggingConfig(l) - } - - src, err := getSource(&opts.Catalog, userInput) - - if err != nil { - return nil, err - } - - defer func() { - if src != nil { - if err := src.Close(); err != nil { - log.Tracef("unable to close source: %+v", err) - } - } - }() - - s, err := generateSBOM(id, src, &opts.Catalog) - if err != nil { - return nil, err - } - - if s == nil { - return nil, fmt.Errorf("no SBOM produced for %q", userInput) - } - - return s, nil - -} diff --git a/cmd/syft/cli/commands/packages.go b/cmd/syft/cli/commands/packages.go deleted file mode 100644 index 290ab570b3a..00000000000 --- a/cmd/syft/cli/commands/packages.go +++ /dev/null @@ -1,255 +0,0 @@ -package commands - -import ( - "fmt" - - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - - "github.com/anchore/clio" - "github.com/anchore/stereoscope/pkg/image" - "github.com/anchore/syft/cmd/syft/cli/eventloop" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/cmd/syft/internal/ui" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/internal/file" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/sbom" - "github.com/anchore/syft/syft/source" -) - -const ( - packagesExample = ` {{.appName}} {{.command}} alpine:latest a summary of discovered packages - {{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details - {{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM - {{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM - {{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.3 Tag-Value formatted SBOM - {{.appName}} {{.command}} alpine:latest -o spdx@2.2 show a SPDX 2.2 Tag-Value formatted SBOM - {{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.3 JSON formatted SBOM - {{.appName}} {{.command}} alpine:latest -o spdx-json@2.2 show a SPDX 2.2 JSON formatted SBOM - {{.appName}} {{.command}} alpine:latest -vv show verbose debug information - {{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file - - Supports the following image sources: - {{.appName}} {{.command}} yourrepo/yourimage:tag defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry. - {{.appName}} {{.command}} path/to/a/file/or/dir a Docker tar, OCI tar, OCI directory, SIF container, or generic filesystem directory -` - - schemeHelpHeader = "You can also explicitly specify the scheme to use:" - imageSchemeHelp = ` {{.appName}} {{.command}} docker:yourrepo/yourimage:tag explicitly use the Docker daemon - {{.appName}} {{.command}} podman:yourrepo/yourimage:tag explicitly use the Podman daemon - {{.appName}} {{.command}} registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required) - {{.appName}} {{.command}} docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save" - {{.appName}} {{.command}} oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise) - {{.appName}} {{.command}} oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise) - {{.appName}} {{.command}} singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk -` - nonImageSchemeHelp = ` {{.appName}} {{.command}} dir:path/to/yourproject read directly from a path on disk (any directory) - {{.appName}} {{.command}} file:path/to/yourproject/file read directly from a path on disk (any single file) -` - packagesSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp + nonImageSchemeHelp - - packagesHelp = packagesExample + packagesSchemeHelp -) - -type packagesOptions struct { - options.Config `yaml:",inline" mapstructure:",squash"` - options.Output `yaml:",inline" mapstructure:",squash"` - options.UpdateCheck `yaml:",inline" mapstructure:",squash"` - options.Catalog `yaml:",inline" mapstructure:",squash"` -} - -func defaultPackagesOptions() *packagesOptions { - return &packagesOptions{ - Output: options.DefaultOutput(), - UpdateCheck: options.DefaultUpdateCheck(), - Catalog: options.DefaultCatalog(), - } -} - -//nolint:dupl -func Packages(app clio.Application) *cobra.Command { - id := app.ID() - - opts := defaultPackagesOptions() - - return app.SetupCommand(&cobra.Command{ - Use: "packages [SOURCE]", - Short: "Generate a package SBOM", - Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems", - Example: internal.Tprintf(packagesHelp, map[string]interface{}{ - "appName": id.Name, - "command": "packages", - }), - Args: validatePackagesArgs, - PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), - RunE: func(cmd *cobra.Command, args []string) error { - restoreStdout := ui.CaptureStdoutToTraceLog() - defer restoreStdout() - - return runPackages(id, opts, args[0]) - }, - }, opts) -} - -func validatePackagesArgs(cmd *cobra.Command, args []string) error { - return validateArgs(cmd, args, "an image/directory argument is required") -} - -func validateArgs(cmd *cobra.Command, args []string, error string) error { - if len(args) == 0 { - // in the case that no arguments are given we want to show the help text and return with a non-0 return code. - if err := cmd.Help(); err != nil { - return fmt.Errorf("unable to display help: %w", err) - } - return fmt.Errorf(error) - } - - return cobra.MaximumNArgs(1)(cmd, args) -} - -// nolint:funlen -func runPackages(id clio.Identification, opts *packagesOptions, userInput string) error { - writer, err := opts.SBOMWriter() - if err != nil { - return err - } - - src, err := getSource(&opts.Catalog, userInput) - - if err != nil { - return err - } - - defer func() { - if src != nil { - if err := src.Close(); err != nil { - log.Tracef("unable to close source: %+v", err) - } - } - }() - - s, err := generateSBOM(id, src, &opts.Catalog) - if err != nil { - return err - } - - if s == nil { - return fmt.Errorf("no SBOM produced for %q", userInput) - } - - if err := writer.Write(*s); err != nil { - return fmt.Errorf("failed to write SBOM: %w", err) - } - - return nil -} - -func getSource(opts *options.Catalog, userInput string, filters ...func(*source.Detection) error) (source.Source, error) { - detection, err := source.Detect( - userInput, - source.DetectConfig{ - DefaultImageSource: opts.DefaultImagePullSource, - }, - ) - if err != nil { - return nil, fmt.Errorf("could not deteremine source: %w", err) - } - - for _, filter := range filters { - if err := filter(detection); err != nil { - return nil, err - } - } - - var platform *image.Platform - - if opts.Platform != "" { - platform, err = image.NewPlatform(opts.Platform) - if err != nil { - return nil, fmt.Errorf("invalid platform: %w", err) - } - } - - hashers, err := file.Hashers(opts.Source.File.Digests...) - if err != nil { - return nil, fmt.Errorf("invalid hash: %w", err) - } - - src, err := detection.NewSource( - source.DetectionSourceConfig{ - Alias: source.Alias{ - Name: opts.Source.Name, - Version: opts.Source.Version, - }, - RegistryOptions: opts.Registry.ToOptions(), - Platform: platform, - Exclude: source.ExcludeConfig{ - Paths: opts.Exclusions, - }, - DigestAlgorithms: hashers, - BasePath: opts.BasePath, - }, - ) - - if err != nil { - if userInput == "power-user" { - bus.Notify("Note: the 'power-user' command has been removed.") - } - return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) - } - - return src, nil -} - -func generateSBOM(id clio.Identification, src source.Source, opts *options.Catalog) (*sbom.SBOM, error) { - tasks, err := eventloop.Tasks(opts) - if err != nil { - return nil, err - } - - s := sbom.SBOM{ - Source: src.Describe(), - Descriptor: sbom.Descriptor{ - Name: id.Name, - Version: id.Version, - Configuration: opts, - }, - } - - err = buildRelationships(&s, src, tasks) - - return &s, err -} - -func buildRelationships(s *sbom.SBOM, src source.Source, tasks []eventloop.Task) error { - var errs error - - var relationships []<-chan artifact.Relationship - for _, task := range tasks { - c := make(chan artifact.Relationship) - relationships = append(relationships, c) - go func(task eventloop.Task) { - err := eventloop.RunTask(task, &s.Artifacts, src, c) - if err != nil { - errs = multierror.Append(errs, err) - } - }(task) - } - - s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...) - - return errs -} - -func mergeRelationships(cs ...<-chan artifact.Relationship) (relationships []artifact.Relationship) { - for _, c := range cs { - for n := range c { - relationships = append(relationships, n) - } - } - - return relationships -} diff --git a/cmd/syft/cli/eventloop/tasks.go b/cmd/syft/cli/eventloop/tasks.go deleted file mode 100644 index ffa808330fa..00000000000 --- a/cmd/syft/cli/eventloop/tasks.go +++ /dev/null @@ -1,151 +0,0 @@ -package eventloop - -import ( - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/internal/file" - "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/file/cataloger/filecontent" - "github.com/anchore/syft/syft/file/cataloger/filedigest" - "github.com/anchore/syft/syft/file/cataloger/filemetadata" - "github.com/anchore/syft/syft/sbom" - "github.com/anchore/syft/syft/source" -) - -type Task func(*sbom.Artifacts, source.Source) ([]artifact.Relationship, error) - -func Tasks(opts *options.Catalog) ([]Task, error) { - var tasks []Task - - generators := []func(opts *options.Catalog) (Task, error){ - generateCatalogPackagesTask, - generateCatalogFileMetadataTask, - generateCatalogFileDigestsTask, - generateCatalogContentsTask, - } - - for _, generator := range generators { - task, err := generator(opts) - if err != nil { - return nil, err - } - - if task != nil { - tasks = append(tasks, task) - } - } - - return tasks, nil -} - -func generateCatalogPackagesTask(opts *options.Catalog) (Task, error) { - if !opts.Package.Cataloger.Enabled { - return nil, nil - } - - task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - packageCatalog, relationships, theDistro, err := syft.CatalogPackagesScribe(src, opts.ToCatalogerConfig()) - - results.Packages = packageCatalog - results.LinuxDistribution = theDistro - - return relationships, err - } - - return task, nil -} - -func generateCatalogFileMetadataTask(opts *options.Catalog) (Task, error) { - if !opts.FileMetadata.Cataloger.Enabled { - return nil, nil - } - - metadataCataloger := filemetadata.NewCataloger() - - task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - resolver, err := src.FileResolver(opts.FileMetadata.Cataloger.GetScope()) - if err != nil { - return nil, err - } - - result, err := metadataCataloger.Catalog(resolver) - if err != nil { - return nil, err - } - results.FileMetadata = result - return nil, nil - } - - return task, nil -} - -func generateCatalogFileDigestsTask(opts *options.Catalog) (Task, error) { - if !opts.FileMetadata.Cataloger.Enabled { - return nil, nil - } - - hashes, err := file.Hashers(opts.FileMetadata.Digests...) - if err != nil { - return nil, err - } - - digestsCataloger := filedigest.NewCataloger(hashes) - - task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - resolver, err := src.FileResolver(opts.FileMetadata.Cataloger.GetScope()) - if err != nil { - return nil, err - } - - result, err := digestsCataloger.Catalog(resolver) - if err != nil { - return nil, err - } - results.FileDigests = result - return nil, nil - } - - return task, nil -} - -func generateCatalogContentsTask(opts *options.Catalog) (Task, error) { - if !opts.FileContents.Cataloger.Enabled { - return nil, nil - } - - contentsCataloger, err := filecontent.NewCataloger(opts.FileContents.Globs, opts.FileContents.SkipFilesAboveSize) //nolint:staticcheck - if err != nil { - return nil, err - } - - task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - resolver, err := src.FileResolver(opts.FileContents.Cataloger.GetScope()) - if err != nil { - return nil, err - } - - result, err := contentsCataloger.Catalog(resolver) - if err != nil { - return nil, err - } - results.FileContents = result - return nil, nil - } - - return task, nil -} - -func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship) error { - defer close(c) - - relationships, err := t(a, src) - if err != nil { - return err - } - - for _, relationship := range relationships { - c <- relationship - } - - return nil -} diff --git a/cmd/syft/cli/lib_scribe.go b/cmd/syft/cli/lib_scribe.go new file mode 100644 index 00000000000..6b2f74f0547 --- /dev/null +++ b/cmd/syft/cli/lib_scribe.go @@ -0,0 +1,93 @@ +package cli + +import ( + "bytes" + "context" + "fmt" + + "github.com/anchore/clio" + "github.com/anchore/go-logger" + "github.com/anchore/stereoscope" + "github.com/anchore/syft/cmd/syft/internal/commands" + "github.com/anchore/syft/cmd/syft/internal/options" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +// type PackagesOptions = commands.PackagesOptions +type OptionsCatalog = commands.OptionsCatalog +type ScanOptions = commands.ScanOptions + +func LibInitLoggingConfig(logWrapper logger.Logger) { + syft.SetLogger(logWrapper) + stereoscope.SetLogger(logWrapper) +} + +func DefaultScanOptions() *commands.ScanOptions { + return commands.DefaultScanOptions() +} + +func GetSource(opts *options.Catalog, userInput string, filters ...func(*source.Detection) error) (source.Source, error) { + return commands.GetSource(opts, userInput, filters...) +} + +func GenerateSBOM(ctx context.Context, id clio.Identification, src source.Source, opts *options.Catalog) (*sbom.SBOM, error) { + return commands.GenerateSBOM(ctx, id, src, opts) +} + +func LibPackagesExec(ctx context.Context, id clio.Identification, opts *ScanOptions, userInput string, l logger.Logger, enableLog bool) (*sbom.SBOM, error) { + if enableLog { + LibInitLoggingConfig(l) + } + + src, err := commands.GetSource(&opts.Catalog, userInput) + + if err != nil { + return nil, err + } + + defer func() { + if src != nil { + if err := src.Close(); err != nil { + log.Tracef("unable to close source: %+v", err) + } + } + }() + + s, err := commands.GenerateSBOM(ctx, id, src, &opts.Catalog) + if err != nil { + return nil, err + } + + if s == nil { + return nil, fmt.Errorf("no SBOM produced for %q", userInput) + } + + return s, nil + +} + +type SbomBuffer struct { + Format sbom.FormatEncoder + buf *bytes.Buffer +} + +func (w *SbomBuffer) Read() []byte { + if w.buf != nil { + return w.buf.Bytes() + } + + return []byte{} +} + +func (w *SbomBuffer) Write(s sbom.SBOM) error { + if w.buf == nil { + w.buf = &bytes.Buffer{} + } + if err := w.Format.Encode(w.buf, s); err != nil { + return fmt.Errorf("unable to encode SBOM: %w", err) + } + return nil +} diff --git a/cmd/syft/cli/options/catalog.go b/cmd/syft/cli/options/catalog.go deleted file mode 100644 index 9da104eb3dc..00000000000 --- a/cmd/syft/cli/options/catalog.go +++ /dev/null @@ -1,171 +0,0 @@ -package options - -import ( - "fmt" - "sort" - "strings" - - "github.com/iancoleman/strcase" - "github.com/mitchellh/go-homedir" - "github.com/scylladb/go-set/strset" - - "github.com/anchore/clio" - "github.com/anchore/fangs" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft/pkg/cataloger" - golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang" - javaCataloger "github.com/anchore/syft/syft/pkg/cataloger/java" - "github.com/anchore/syft/syft/pkg/cataloger/kernel" - pythonCataloger "github.com/anchore/syft/syft/pkg/cataloger/python" - "github.com/anchore/syft/syft/source" -) - -type Catalog struct { - Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"` - Package pkg `yaml:"package" json:"package" mapstructure:"package"` - Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"` - Java java `yaml:"java" json:"java" mapstructure:"java"` - LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"` - Python python `yaml:"python" json:"python" mapstructure:"python"` - FileMetadata fileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"` - FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"` - Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"` - Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"` - Platform string `yaml:"platform" json:"platform" mapstructure:"platform"` - Name string `yaml:"name" json:"name" mapstructure:"name"` - Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"` - Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel - DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source - BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths - ExcludeBinaryOverlapByOwnership bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` // exclude synthetic binary packages owned by os package files -} - -var _ interface { - clio.FlagAdder - clio.PostLoader -} = (*Catalog)(nil) - -func DefaultCatalog() Catalog { - return Catalog{ - Package: defaultPkg(), - LinuxKernel: defaultLinuxKernel(), - FileMetadata: defaultFileMetadata(), - FileContents: defaultFileContents(), - Source: defaultSourceCfg(), - Parallelism: 1, - ExcludeBinaryOverlapByOwnership: true, - } -} - -func (cfg *Catalog) AddFlags(flags clio.FlagSet) { - var validScopeValues []string - for _, scope := range source.AllScopes { - validScopeValues = append(validScopeValues, strcase.ToDelimited(string(scope), '-')) - } - flags.StringVarP(&cfg.Package.Cataloger.Scope, "scope", "s", - fmt.Sprintf("selection of layers to catalog, options=%v", validScopeValues)) - - flags.StringVarP(&cfg.Platform, "platform", "", - "an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')") - - flags.StringArrayVarP(&cfg.Exclusions, "exclude", "", - "exclude paths from being scanned using a glob expression") - - flags.StringArrayVarP(&cfg.Catalogers, "catalogers", "", - "enable one or more package catalogers") - - flags.StringVarP(&cfg.Source.Name, "name", "", - "set the name of the target being analyzed") - - if pfp, ok := flags.(fangs.PFlagSetProvider); ok { - flagSet := pfp.PFlagSet() - flagSet.Lookup("name").Deprecated = "use: source-name" - } - - flags.StringVarP(&cfg.Source.Name, "source-name", "", - "set the name of the target being analyzed") - - flags.StringVarP(&cfg.Source.Version, "source-version", "", - "set the version of the target being analyzed") - - flags.StringVarP(&cfg.BasePath, "base-path", "", - "base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory") -} - -func (cfg *Catalog) PostLoad() error { - // parse options on this struct - var catalogers []string - for _, c := range cfg.Catalogers { - for _, f := range strings.Split(c, ",") { - catalogers = append(catalogers, strings.TrimSpace(f)) - } - } - sort.Strings(catalogers) - cfg.Catalogers = catalogers - - if err := checkDefaultSourceValues(cfg.DefaultImagePullSource); err != nil { - return err - } - - if cfg.Name != "" { - log.Warnf("name parameter is deprecated. please use: source-name. name will be removed in a future version") - if cfg.Source.Name == "" { - cfg.Source.Name = cfg.Name - } - } - - return nil -} - -func (cfg Catalog) ToCatalogerConfig() cataloger.Config { - return cataloger.Config{ - Search: cataloger.SearchConfig{ - IncludeIndexedArchives: cfg.Package.SearchIndexedArchives, - IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives, - Scope: cfg.Package.Cataloger.GetScope(), - }, - Catalogers: cfg.Catalogers, - Parallelism: cfg.Parallelism, - Golang: golangCataloger.NewGoCatalogerOpts(). - WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses). - WithLocalModCacheDir(cfg.Golang.LocalModCacheDir). - WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses). - WithProxy(cfg.Golang.Proxy). - WithNoProxy(cfg.Golang.NoProxy), - LinuxKernel: kernel.LinuxCatalogerConfig{ - CatalogModules: cfg.LinuxKernel.CatalogModules, - }, - Java: javaCataloger.DefaultCatalogerOpts(). - WithUseNetwork(cfg.Java.UseNetwork). - WithMavenURL(cfg.Java.MavenURL). - WithMaxParentRecursiveDepth(cfg.Java.MaxParentRecursiveDepth), - Python: pythonCataloger.CatalogerConfig{ - GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements, - }, - ExcludeBinaryOverlapByOwnership: cfg.ExcludeBinaryOverlapByOwnership, - CatalogerGroup: cfg.Package.CatalogerGroup, - } -} - -var validDefaultSourceValues = []string{"registry", "docker", "podman", ""} - -func checkDefaultSourceValues(source string) error { - validValues := strset.New(validDefaultSourceValues...) - if !validValues.Has(source) { - validValuesString := strings.Join(validDefaultSourceValues, ", ") - return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString) - } - - return nil -} - -func expandFilePath(file string) (string, error) { - if file != "" { - expandedPath, err := homedir.Expand(file) - if err != nil { - return "", fmt.Errorf("unable to expand file path=%q: %w", file, err) - } - file = expandedPath - } - return file, nil -} diff --git a/cmd/syft/cli/options/config.go b/cmd/syft/cli/options/config.go deleted file mode 100644 index 85aeb69ddd3..00000000000 --- a/cmd/syft/cli/options/config.go +++ /dev/null @@ -1,6 +0,0 @@ -package options - -// Config holds a reference to the specific config file that was used to load application configuration -type Config struct { - ConfigFile string `yaml:"config" json:"config" mapstructure:"config"` -} diff --git a/cmd/syft/cli/options/file_contents.go b/cmd/syft/cli/options/file_contents.go deleted file mode 100644 index 6dba465f5da..00000000000 --- a/cmd/syft/cli/options/file_contents.go +++ /dev/null @@ -1,21 +0,0 @@ -package options - -import ( - "github.com/anchore/syft/internal/file" - "github.com/anchore/syft/syft/source" -) - -type fileContents struct { - Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` - SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"` - Globs []string `yaml:"globs" json:"globs" mapstructure:"globs"` -} - -func defaultFileContents() fileContents { - return fileContents{ - Cataloger: scope{ - Scope: source.SquashedScope.String(), - }, - SkipFilesAboveSize: 1 * file.MB, - } -} diff --git a/cmd/syft/cli/options/file_metadata.go b/cmd/syft/cli/options/file_metadata.go deleted file mode 100644 index eb2335a24da..00000000000 --- a/cmd/syft/cli/options/file_metadata.go +++ /dev/null @@ -1,19 +0,0 @@ -package options - -import ( - "github.com/anchore/syft/syft/source" -) - -type fileMetadata struct { - Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` - Digests []string `yaml:"digests" json:"digests" mapstructure:"digests"` -} - -func defaultFileMetadata() fileMetadata { - return fileMetadata{ - Cataloger: scope{ - Scope: source.SquashedScope.String(), - }, - Digests: []string{"sha256"}, - } -} diff --git a/cmd/syft/cli/options/format.go b/cmd/syft/cli/options/format.go deleted file mode 100644 index 03d9bf42346..00000000000 --- a/cmd/syft/cli/options/format.go +++ /dev/null @@ -1,129 +0,0 @@ -package options - -import ( - "fmt" - - "github.com/hashicorp/go-multierror" - - "github.com/anchore/clio" - "github.com/anchore/syft/syft/format/cyclonedxjson" - "github.com/anchore/syft/syft/format/cyclonedxxml" - "github.com/anchore/syft/syft/format/github" - "github.com/anchore/syft/syft/format/spdxjson" - "github.com/anchore/syft/syft/format/spdxtagvalue" - "github.com/anchore/syft/syft/format/syftjson" - "github.com/anchore/syft/syft/format/table" - "github.com/anchore/syft/syft/format/template" - "github.com/anchore/syft/syft/format/text" - "github.com/anchore/syft/syft/sbom" -) - -var _ clio.PostLoader = (*Format)(nil) - -// Format contains all user configuration for output formatting. -type Format struct { - Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` - Template FormatTemplate `yaml:"template" json:"template" mapstructure:"template"` - SyftJSON FormatSyftJSON `yaml:"json" json:"json" mapstructure:"json"` - SPDXJSON FormatSPDXJSON `yaml:"spdx-json" json:"spdx-json" mapstructure:"spdx-json"` - CyclonedxJSON FormatCyclonedxJSON `yaml:"cyclonedx-json" json:"cyclonedx-json" mapstructure:"cyclonedx-json"` - CyclonedxXML FormatCyclonedxXML `yaml:"cyclonedx-xml" json:"cyclonedx-xml" mapstructure:"cyclonedx-xml"` -} - -func (o *Format) PostLoad() error { - o.SyftJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SyftJSON.Pretty) - o.SPDXJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SPDXJSON.Pretty) - o.CyclonedxJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxJSON.Pretty) - o.CyclonedxXML.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxXML.Pretty) - - return nil -} - -func DefaultFormat() Format { - return Format{ - Template: DefaultFormatTemplate(), - SyftJSON: DefaultFormatJSON(), - SPDXJSON: DefaultFormatSPDXJSON(), - CyclonedxJSON: DefaultFormatCyclonedxJSON(), - CyclonedxXML: DefaultFormatCyclonedxXML(), - } -} - -func (o *Format) Encoders() ([]sbom.FormatEncoder, error) { - // setup all encoders based on the configuration - var list encoderList - - // in the future there will be application configuration options that can be used to set the default output format - list.addWithErr(template.ID)(o.Template.formatEncoders()) - list.addWithErr(syftjson.ID)(o.SyftJSON.formatEncoders()) - list.add(table.ID)(table.NewFormatEncoder()) - list.add(text.ID)(text.NewFormatEncoder()) - list.add(github.ID)(github.NewFormatEncoder()) - list.addWithErr(cyclonedxxml.ID)(o.CyclonedxXML.formatEncoders()) - list.addWithErr(cyclonedxjson.ID)(o.CyclonedxJSON.formatEncoders()) - list.addWithErr(spdxjson.ID)(o.SPDXJSON.formatEncoders()) - list.addWithErr(spdxtagvalue.ID)(spdxTagValueEncoders()) - - return list.encoders, list.err -} - -// TODO: when application configuration is made for this format then this should be ported to the options object -// that is created for that configuration (as done with the template output option) -func spdxTagValueEncoders() ([]sbom.FormatEncoder, error) { - var ( - encs []sbom.FormatEncoder - errs error - ) - for _, v := range spdxtagvalue.SupportedVersions() { - enc, err := spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.EncoderConfig{Version: v}) - if err != nil { - errs = multierror.Append(errs, err) - } else { - encs = append(encs, enc) - } - } - return encs, errs -} - -type encoderList struct { - encoders []sbom.FormatEncoder - err error -} - -func (l *encoderList) addWithErr(name sbom.FormatID) func([]sbom.FormatEncoder, error) { - return func(encs []sbom.FormatEncoder, err error) { - if err != nil { - l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: %w", name, err)) - return - } - for _, enc := range encs { - if enc == nil { - l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: nil encoder returned", name)) - continue - } - l.encoders = append(l.encoders, enc) - } - } -} - -func (l *encoderList) add(name sbom.FormatID) func(...sbom.FormatEncoder) { - return func(encs ...sbom.FormatEncoder) { - for _, enc := range encs { - if enc == nil { - l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: nil encoder returned", name)) - continue - } - l.encoders = append(l.encoders, enc) - } - } -} - -func multiLevelOption[T any](defaultValue T, option ...*T) *T { - result := defaultValue - for _, opt := range option { - if opt != nil { - result = *opt - } - } - return &result -} diff --git a/cmd/syft/cli/options/format_cyclonedx_json.go b/cmd/syft/cli/options/format_cyclonedx_json.go deleted file mode 100644 index 8d2fecbba17..00000000000 --- a/cmd/syft/cli/options/format_cyclonedx_json.go +++ /dev/null @@ -1,43 +0,0 @@ -package options - -import ( - "github.com/hashicorp/go-multierror" - - "github.com/anchore/syft/syft/format/cyclonedxjson" - "github.com/anchore/syft/syft/sbom" -) - -type FormatCyclonedxJSON struct { - Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` -} - -func DefaultFormatCyclonedxJSON() FormatCyclonedxJSON { - return FormatCyclonedxJSON{} -} - -func (o FormatCyclonedxJSON) formatEncoders() ([]sbom.FormatEncoder, error) { - var ( - encs []sbom.FormatEncoder - errs error - ) - for _, v := range cyclonedxjson.SupportedVersions() { - enc, err := cyclonedxjson.NewFormatEncoderWithConfig(o.buildConfig(v)) - if err != nil { - errs = multierror.Append(errs, err) - } else { - encs = append(encs, enc) - } - } - return encs, errs -} - -func (o FormatCyclonedxJSON) buildConfig(version string) cyclonedxjson.EncoderConfig { - var pretty bool - if o.Pretty != nil { - pretty = *o.Pretty - } - return cyclonedxjson.EncoderConfig{ - Version: version, - Pretty: pretty, - } -} diff --git a/cmd/syft/cli/options/format_cyclonedx_xml.go b/cmd/syft/cli/options/format_cyclonedx_xml.go deleted file mode 100644 index 359806db380..00000000000 --- a/cmd/syft/cli/options/format_cyclonedx_xml.go +++ /dev/null @@ -1,43 +0,0 @@ -package options - -import ( - "github.com/hashicorp/go-multierror" - - "github.com/anchore/syft/syft/format/cyclonedxxml" - "github.com/anchore/syft/syft/sbom" -) - -type FormatCyclonedxXML struct { - Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` -} - -func DefaultFormatCyclonedxXML() FormatCyclonedxXML { - return FormatCyclonedxXML{} -} - -func (o FormatCyclonedxXML) formatEncoders() ([]sbom.FormatEncoder, error) { - var ( - encs []sbom.FormatEncoder - errs error - ) - for _, v := range cyclonedxxml.SupportedVersions() { - enc, err := cyclonedxxml.NewFormatEncoderWithConfig(o.buildConfig(v)) - if err != nil { - errs = multierror.Append(errs, err) - } else { - encs = append(encs, enc) - } - } - return encs, errs -} - -func (o FormatCyclonedxXML) buildConfig(version string) cyclonedxxml.EncoderConfig { - var pretty bool - if o.Pretty != nil { - pretty = *o.Pretty - } - return cyclonedxxml.EncoderConfig{ - Version: version, - Pretty: pretty, - } -} diff --git a/cmd/syft/cli/options/format_spdx_json.go b/cmd/syft/cli/options/format_spdx_json.go deleted file mode 100644 index 002e996a321..00000000000 --- a/cmd/syft/cli/options/format_spdx_json.go +++ /dev/null @@ -1,43 +0,0 @@ -package options - -import ( - "github.com/hashicorp/go-multierror" - - "github.com/anchore/syft/syft/format/spdxjson" - "github.com/anchore/syft/syft/sbom" -) - -type FormatSPDXJSON struct { - Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` -} - -func DefaultFormatSPDXJSON() FormatSPDXJSON { - return FormatSPDXJSON{} -} - -func (o FormatSPDXJSON) formatEncoders() ([]sbom.FormatEncoder, error) { - var ( - encs []sbom.FormatEncoder - errs error - ) - for _, v := range spdxjson.SupportedVersions() { - enc, err := spdxjson.NewFormatEncoderWithConfig(o.buildConfig(v)) - if err != nil { - errs = multierror.Append(errs, err) - } else { - encs = append(encs, enc) - } - } - return encs, errs -} - -func (o FormatSPDXJSON) buildConfig(v string) spdxjson.EncoderConfig { - var pretty bool - if o.Pretty != nil { - pretty = *o.Pretty - } - return spdxjson.EncoderConfig{ - Version: v, - Pretty: pretty, - } -} diff --git a/cmd/syft/cli/options/golang.go b/cmd/syft/cli/options/golang.go deleted file mode 100644 index ff99f414fd3..00000000000 --- a/cmd/syft/cli/options/golang.go +++ /dev/null @@ -1,9 +0,0 @@ -package options - -type golang struct { - SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"` - LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"` - SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"` - Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"` - NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"` -} diff --git a/cmd/syft/cli/options/pkg.go b/cmd/syft/cli/options/pkg.go deleted file mode 100644 index cf8c190d902..00000000000 --- a/cmd/syft/cli/options/pkg.go +++ /dev/null @@ -1,24 +0,0 @@ -package options - -import ( - "github.com/anchore/syft/syft/pkg/cataloger" -) - -type pkg struct { - Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` - SearchUnindexedArchives bool `yaml:"search-unindexed-archives" json:"search-unindexed-archives" mapstructure:"search-unindexed-archives"` - SearchIndexedArchives bool `yaml:"search-indexed-archives" json:"search-indexed-archives" mapstructure:"search-indexed-archives"` - CatalogerGroup cataloger.Group `yaml:"cataloger-group" json:"cataloger-group" mapstructure:"cataloger-group"` -} - -func defaultPkg() pkg { - c := cataloger.DefaultSearchConfig() - return pkg{ - SearchIndexedArchives: c.IncludeIndexedArchives, - SearchUnindexedArchives: c.IncludeUnindexedArchives, - Cataloger: scope{ - Enabled: true, - Scope: c.Scope.String(), - }, - } -} diff --git a/cmd/syft/cli/options/scope.go b/cmd/syft/cli/options/scope.go deleted file mode 100644 index ae6efcffea7..00000000000 --- a/cmd/syft/cli/options/scope.go +++ /dev/null @@ -1,27 +0,0 @@ -package options - -import ( - "fmt" - - "github.com/anchore/clio" - "github.com/anchore/syft/syft/source" -) - -type scope struct { - Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"` - Scope string `yaml:"scope" json:"scope" mapstructure:"scope"` -} - -var _ clio.PostLoader = (*scope)(nil) - -func (opt *scope) PostLoad() error { - s := opt.GetScope() - if s == source.UnknownScope { - return fmt.Errorf("bad scope value %v", opt.Scope) - } - return nil -} - -func (opt scope) GetScope() source.Scope { - return source.ParseScope(opt.Scope) -} diff --git a/cmd/syft/cli/options/source.go b/cmd/syft/cli/options/source.go deleted file mode 100644 index 41e30199645..00000000000 --- a/cmd/syft/cli/options/source.go +++ /dev/null @@ -1,19 +0,0 @@ -package options - -type sourceCfg struct { - Name string `json:"name" yaml:"name" mapstructure:"name"` - Version string `json:"version" yaml:"version" mapstructure:"version"` - File fileSource `json:"file" yaml:"file" mapstructure:"file"` -} - -type fileSource struct { - Digests []string `json:"digests" yaml:"digests" mapstructure:"digests"` -} - -func defaultSourceCfg() sourceCfg { - return sourceCfg{ - File: fileSource{ - Digests: []string{"sha256"}, - }, - } -} diff --git a/cmd/syft/cli/ui/__snapshots__/handle_cataloger_task_test.snap b/cmd/syft/cli/ui/__snapshots__/handle_cataloger_task_test.snap index aff1f474a75..fc204c05aa7 100755 --- a/cmd/syft/cli/ui/__snapshots__/handle_cataloger_task_test.snap +++ b/cmd/syft/cli/ui/__snapshots__/handle_cataloger_task_test.snap @@ -1,16 +1,24 @@ [TestHandler_handleCatalogerTaskStarted/cataloging_task_in_progress - 1] - some task title [some value] + ⠙ Cataloging contents + ⠙ some task title ━━━━━━━━━━━━━━━━━━━━ [some stage] --- [TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_in_progress - 1] - └── some task title [some value] + ⠙ Cataloging contents + └── ⠙ some task title ━━━━━━━━━━━━━━━━━━━━ [some stage] --- [TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete - 1] - ✔ └── some task done [some value] + ⠙ Cataloging contents + └── ✔ some task done [some stage] --- -[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete_with_removal - 1] +[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete_--_hide_stage - 1] + ⠙ Cataloging contents + └── ✔ some task done +--- +[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete_with_removal - 1] + ⠙ Cataloging contents --- diff --git a/cmd/syft/cli/ui/__snapshots__/handle_file_digests_cataloger_test.snap b/cmd/syft/cli/ui/__snapshots__/handle_file_digests_cataloger_test.snap deleted file mode 100755 index b4572c26541..00000000000 --- a/cmd/syft/cli/ui/__snapshots__/handle_file_digests_cataloger_test.snap +++ /dev/null @@ -1,8 +0,0 @@ - -[TestHandler_handleFileDigestsCatalogerStarted/cataloging_in_progress - 1] - ⠋ Cataloging file digests ━━━━━━━━━━━━━━━━━━━━ [current] ---- - -[TestHandler_handleFileDigestsCatalogerStarted/cataloging_complete - 1] - ✔ Cataloged file digests ---- diff --git a/cmd/syft/cli/ui/__snapshots__/handle_file_metadata_cataloger_test.snap b/cmd/syft/cli/ui/__snapshots__/handle_file_metadata_cataloger_test.snap deleted file mode 100755 index 200a0dfb4ad..00000000000 --- a/cmd/syft/cli/ui/__snapshots__/handle_file_metadata_cataloger_test.snap +++ /dev/null @@ -1,8 +0,0 @@ - -[TestHandler_handleFileMetadataCatalogerStarted/cataloging_in_progress - 1] - ⠋ Cataloging file metadata ━━━━━━━━━━━━━━━━━━━━ [current] ---- - -[TestHandler_handleFileMetadataCatalogerStarted/cataloging_complete - 1] - ✔ Cataloged file metadata ---- diff --git a/cmd/syft/cli/ui/__snapshots__/handle_package_cataloger_test.snap b/cmd/syft/cli/ui/__snapshots__/handle_package_cataloger_test.snap deleted file mode 100755 index 5d5c165e03d..00000000000 --- a/cmd/syft/cli/ui/__snapshots__/handle_package_cataloger_test.snap +++ /dev/null @@ -1,16 +0,0 @@ - -[TestHandler_handlePackageCatalogerStarted/cataloging_in_progress - 1] - ⠋ Cataloging packages [50 packages] ---- - -[TestHandler_handlePackageCatalogerStarted/cataloging_only_files_complete - 1] - ⠋ Cataloging packages [50 packages] ---- - -[TestHandler_handlePackageCatalogerStarted/cataloging_only_packages_complete - 1] - ⠋ Cataloging packages [100 packages] ---- - -[TestHandler_handlePackageCatalogerStarted/cataloging_complete - 1] - ✔ Cataloged packages [100 packages] ---- diff --git a/cmd/syft/cli/ui/__snapshots__/handle_secrets_cataloger_test.snap b/cmd/syft/cli/ui/__snapshots__/handle_secrets_cataloger_test.snap deleted file mode 100755 index 00a123ef7f0..00000000000 --- a/cmd/syft/cli/ui/__snapshots__/handle_secrets_cataloger_test.snap +++ /dev/null @@ -1,8 +0,0 @@ - -[TestHandler_handleSecretsCatalogerStarted/cataloging_in_progress - 1] - ⠋ Cataloging secrets ━━━━━━━━━━━━━━━━━━━━ [64 secrets] ---- - -[TestHandler_handleSecretsCatalogerStarted/cataloging_complete - 1] - ✔ Cataloged secrets [64 secrets] ---- diff --git a/cmd/syft/cli/ui/handle_attestation_test.go b/cmd/syft/cli/ui/handle_attestation_test.go index c79d01d2c2e..76857ed65e7 100644 --- a/cmd/syft/cli/ui/handle_attestation_test.go +++ b/cmd/syft/cli/ui/handle_attestation_test.go @@ -100,18 +100,21 @@ func TestHandler_handleAttestationStarted(t *testing.T) { Height: 80, } - models := handler.Handle(event) + models, _ := handler.Handle(event) require.Len(t, models, 2) t.Run("task line", func(t *testing.T) { tsk, ok := models[0].(taskprogress.Model) require.True(t, ok) - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ + gotModel := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ Time: time.Now(), Sequence: tsk.Sequence(), ID: tsk.ID(), }) + + got := gotModel.View() + t.Log(got) snaps.MatchSnapshot(t, got) }) @@ -119,11 +122,15 @@ func TestHandler_handleAttestationStarted(t *testing.T) { t.Run("log", func(t *testing.T) { log, ok := models[1].(attestLogFrame) require.True(t, ok) - got := runModel(t, log, tt.iterations, attestLogFrameTickMsg{ + + gotModel := runModel(t, log, tt.iterations, attestLogFrameTickMsg{ Time: time.Now(), Sequence: log.sequence, ID: log.id, }, log.reader.running) + + got := gotModel.View() + t.Log(got) snaps.MatchSnapshot(t, got) }) diff --git a/cmd/syft/cli/ui/handle_cataloger_task.go b/cmd/syft/cli/ui/handle_cataloger_task.go index 393bd6e7ad6..0d4539ed945 100644 --- a/cmd/syft/cli/ui/handle_cataloger_task.go +++ b/cmd/syft/cli/ui/handle_cataloger_task.go @@ -3,70 +3,121 @@ package ui import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/google/uuid" "github.com/wagoodman/go-partybus" "github.com/wagoodman/go-progress" "github.com/anchore/bubbly/bubbles/taskprogress" + "github.com/anchore/bubbly/bubbles/tree" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/event/monitor" syftEventParsers "github.com/anchore/syft/syft/event/parsers" ) -var _ progress.Stager = (*catalogerTaskStageAdapter)(nil) +// we standardize how rows are instantiated to ensure consistency in the appearance across the UI +type taskModelFactory func(title taskprogress.Title, opts ...taskprogress.Option) taskprogress.Model -type catalogerTaskStageAdapter struct { - mon *monitor.CatalogerTask +var _ tea.Model = (*catalogerTaskModel)(nil) + +type catalogerTaskModel struct { + model tree.Model + modelFactory taskModelFactory } -func newCatalogerTaskStageAdapter(mon *monitor.CatalogerTask) *catalogerTaskStageAdapter { - return &catalogerTaskStageAdapter{ - mon: mon, +func newCatalogerTaskTreeModel(f taskModelFactory) *catalogerTaskModel { + t := tree.NewModel() + t.Padding = " " + t.RootsWithoutPrefix = true + return &catalogerTaskModel{ + modelFactory: f, + model: t, } } -func (c catalogerTaskStageAdapter) Stage() string { - return c.mon.GetValue() +type newCatalogerTaskRowEvent struct { + info monitor.GenericTask + prog progress.StagedProgressable } -func (m *Handler) handleCatalogerTaskStarted(e partybus.Event) []tea.Model { - mon, err := syftEventParsers.ParseCatalogerTaskStarted(e) - if err != nil { - log.WithFields("error", err).Warn("unable to parse event") - return nil - } +func (cts catalogerTaskModel) Init() tea.Cmd { + return cts.model.Init() +} + +func (cts catalogerTaskModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + event, ok := msg.(newCatalogerTaskRowEvent) + if !ok { + model, cmd := cts.model.Update(msg) + cts.model = model.(tree.Model) - var prefix string - if mon.SubStatus { - // TODO: support list of sub-statuses, not just a single leaf - prefix = "└── " + return cts, cmd } - tsk := m.newTaskProgress( + info, prog := event.info, event.prog + + tsk := cts.modelFactory( taskprogress.Title{ - // TODO: prefix should not be part of the title, but instead a separate field that is aware of the tree structure - Default: prefix + mon.Title, - Running: prefix + mon.Title, - Success: prefix + mon.TitleOnCompletion, + Default: info.Title.Default, + Running: info.Title.WhileRunning, + Success: info.Title.OnSuccess, }, - taskprogress.WithStagedProgressable( - struct { - progress.Stager - progress.Progressable - }{ - Progressable: mon.GetMonitor(), - Stager: newCatalogerTaskStageAdapter(mon), - }, - ), + taskprogress.WithStagedProgressable(prog), ) - // TODO: this isn't ideal since the model stays around after it is no longer needed, but it works for now - tsk.HideOnSuccess = mon.RemoveOnCompletion - tsk.HideStageOnSuccess = false - tsk.HideProgressOnSuccess = false + if info.Context != "" { + tsk.Context = []string{info.Context} + } + + tsk.HideOnSuccess = info.HideOnSuccess + tsk.HideStageOnSuccess = info.HideStageOnSuccess + tsk.HideProgressOnSuccess = true + + if info.ParentID != "" { + tsk.TitleStyle = lipgloss.NewStyle() + } - tsk.TitleStyle = lipgloss.NewStyle() - // TODO: this is a hack to get the spinner to not show up, but ideally the component would support making the spinner optional - tsk.Spinner.Spinner.Frames = []string{" "} + if err := cts.model.Add(info.ParentID, info.ID, tsk); err != nil { + log.WithFields("error", err).Error("unable to add cataloger task to tree model") + } + + return cts, tsk.Init() +} + +func (cts catalogerTaskModel) View() string { + return cts.model.View() +} + +func (m *Handler) handleCatalogerTaskStarted(e partybus.Event) ([]tea.Model, tea.Cmd) { + mon, info, err := syftEventParsers.ParseCatalogerTaskStarted(e) + if err != nil { + log.WithFields("error", err).Warn("unable to parse event") + return nil, nil + } + + var models []tea.Model + + // only create the new cataloger task tree once to manage all cataloger task events + m.onNewCatalogerTask.Do(func() { + models = append(models, newCatalogerTaskTreeModel(m.newTaskProgress)) + }) + + // we need to update the cataloger task model with a new row. We should never update the model outside of the + // bubbletea update-render event loop. Instead, we return a command that will be executed by the bubbletea runtime, + // producing a message that is passed to the cataloger task model. This is the prescribed way to update models + // in bubbletea. + + if info.ID == "" { + // ID is optional from the consumer perspective, but required internally + info.ID = uuid.Must(uuid.NewRandom()).String() + } + + cmd := func() tea.Msg { + // this message will cause the cataloger task model to add a new row to the output based on the given task + // information and progress data. + return newCatalogerTaskRowEvent{ + info: *info, + prog: mon, + } + } - return []tea.Model{tsk} + return models, cmd } diff --git a/cmd/syft/cli/ui/handle_cataloger_task_test.go b/cmd/syft/cli/ui/handle_cataloger_task_test.go index 055694588fa..3efb0fdac21 100644 --- a/cmd/syft/cli/ui/handle_cataloger_task_test.go +++ b/cmd/syft/cli/ui/handle_cataloger_task_test.go @@ -2,19 +2,22 @@ package ui import ( "testing" - "time" tea "github.com/charmbracelet/bubbletea" "github.com/gkampitakis/go-snaps/snaps" "github.com/stretchr/testify/require" "github.com/wagoodman/go-partybus" + "github.com/wagoodman/go-progress" - "github.com/anchore/bubbly/bubbles/taskprogress" syftEvent "github.com/anchore/syft/syft/event" "github.com/anchore/syft/syft/event/monitor" ) func TestHandler_handleCatalogerTaskStarted(t *testing.T) { + title := monitor.Title{ + Default: "some task title", + OnSuccess: "some task done", + } tests := []struct { name string eventFn func(*testing.T) partybus.Event @@ -23,99 +26,171 @@ func TestHandler_handleCatalogerTaskStarted(t *testing.T) { { name: "cataloging task in progress", eventFn: func(t *testing.T) partybus.Event { - src := &monitor.CatalogerTask{ - SubStatus: false, - RemoveOnCompletion: false, - Title: "some task title", - TitleOnCompletion: "some task done", + value := &monitor.CatalogerTaskProgress{ + AtomicStage: progress.NewAtomicStage("some stage"), + Manual: progress.NewManual(100), } - src.SetValue("some value") + value.Manual.Add(50) return partybus.Event{ - Type: syftEvent.CatalogerTaskStarted, - Source: src, + Type: syftEvent.CatalogerTaskStarted, + Source: monitor.GenericTask{ + Title: title, + HideOnSuccess: false, + HideStageOnSuccess: false, + ID: "my-id", + }, + Value: value, } }, }, { name: "cataloging sub task in progress", eventFn: func(t *testing.T) partybus.Event { - src := &monitor.CatalogerTask{ - SubStatus: true, - RemoveOnCompletion: false, - Title: "some task title", - TitleOnCompletion: "some task done", + value := &monitor.CatalogerTaskProgress{ + AtomicStage: progress.NewAtomicStage("some stage"), + Manual: progress.NewManual(100), } - src.SetValue("some value") + value.Manual.Add(50) return partybus.Event{ - Type: syftEvent.CatalogerTaskStarted, - Source: src, + Type: syftEvent.CatalogerTaskStarted, + Source: monitor.GenericTask{ + Title: title, + HideOnSuccess: false, + HideStageOnSuccess: false, + ID: "my-id", + ParentID: "top-level-task", + }, + Value: value, } }, }, { name: "cataloging sub task complete", eventFn: func(t *testing.T) partybus.Event { - src := &monitor.CatalogerTask{ - SubStatus: true, - RemoveOnCompletion: false, - Title: "some task title", - TitleOnCompletion: "some task done", + value := &monitor.CatalogerTaskProgress{ + AtomicStage: progress.NewAtomicStage("some stage"), + Manual: progress.NewManual(100), } - src.SetValue("some value") - src.SetCompleted() + value.SetCompleted() return partybus.Event{ - Type: syftEvent.CatalogerTaskStarted, - Source: src, + Type: syftEvent.CatalogerTaskStarted, + Source: monitor.GenericTask{ + Title: title, + HideOnSuccess: false, + HideStageOnSuccess: false, + ID: "my-id", + ParentID: "top-level-task", + }, + Value: value, + } + }, + }, + { + name: "cataloging sub task complete -- hide stage", + eventFn: func(t *testing.T) partybus.Event { + value := &monitor.CatalogerTaskProgress{ + AtomicStage: progress.NewAtomicStage("some stage"), + Manual: progress.NewManual(100), + } + + value.SetCompleted() + + return partybus.Event{ + Type: syftEvent.CatalogerTaskStarted, + Source: monitor.GenericTask{ + Title: title, + HideOnSuccess: false, + HideStageOnSuccess: true, + ID: "my-id", + ParentID: "top-level-task", + }, + Value: value, } }, }, { name: "cataloging sub task complete with removal", eventFn: func(t *testing.T) partybus.Event { - src := &monitor.CatalogerTask{ - SubStatus: true, - RemoveOnCompletion: true, - Title: "some task title", - TitleOnCompletion: "some task done", + value := &monitor.CatalogerTaskProgress{ + AtomicStage: progress.NewAtomicStage("some stage"), + Manual: progress.NewManual(100), } - src.SetValue("some value") - src.SetCompleted() + value.SetCompleted() return partybus.Event{ - Type: syftEvent.CatalogerTaskStarted, - Source: src, + Type: syftEvent.CatalogerTaskStarted, + Source: monitor.GenericTask{ + Title: title, + HideOnSuccess: true, + HideStageOnSuccess: false, + ID: "my-id", + ParentID: "top-level-task", + }, + Value: value, } }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - event := tt.eventFn(t) + // need to be able to get the initial newCatalogerTaskRowEvent + initialize the nested taskprogress model + if tt.iterations == 0 { + tt.iterations = 2 + } + + e := tt.eventFn(t) handler := New(DefaultHandlerConfig()) handler.WindowSize = tea.WindowSizeMsg{ Width: 100, Height: 80, } - models := handler.Handle(event) + info := monitor.GenericTask{ + Title: monitor.Title{ + Default: "Catalog contents", + WhileRunning: "Cataloging contents", + OnSuccess: "Cataloged contents", + }, + ID: "top-level-task", + } + + // note: this line / event is not under test, only needed to show a sub status + kickoffEvent := &monitor.CatalogerTaskProgress{ + AtomicStage: progress.NewAtomicStage(""), + Manual: progress.NewManual(-1), + } + + models, cmd := handler.Handle( + partybus.Event{ + Type: syftEvent.CatalogerTaskStarted, + Source: info, + Value: progress.StagedProgressable(kickoffEvent), + }, + ) require.Len(t, models, 1) + require.NotNil(t, cmd) model := models[0] - tsk, ok := model.(taskprogress.Model) + tr, ok := model.(*catalogerTaskModel) require.True(t, ok) - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ - Time: time.Now(), - Sequence: tsk.Sequence(), - ID: tsk.ID(), - }) + gotModel := runModel(t, tr, tt.iterations, cmd()) + + models, cmd = handler.Handle(e) + require.Len(t, models, 0) + require.NotNil(t, cmd) + + gotModel = runModel(t, gotModel, tt.iterations, cmd()) + + got := gotModel.View() + t.Log(got) snaps.MatchSnapshot(t, got) }) diff --git a/cmd/syft/cli/ui/handle_fetch_image_test.go b/cmd/syft/cli/ui/handle_fetch_image_test.go index c514b986542..a2df4701c72 100644 --- a/cmd/syft/cli/ui/handle_fetch_image_test.go +++ b/cmd/syft/cli/ui/handle_fetch_image_test.go @@ -80,18 +80,21 @@ func TestHandler_handleFetchImage(t *testing.T) { Height: 80, } - models := handler.Handle(event) + models, _ := handler.Handle(event) require.Len(t, models, 1) model := models[0] tsk, ok := model.(taskprogress.Model) require.True(t, ok) - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ + gotModel := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ Time: time.Now(), Sequence: tsk.Sequence(), ID: tsk.ID(), }) + + got := gotModel.View() + t.Log(got) snaps.MatchSnapshot(t, got) }) diff --git a/cmd/syft/cli/ui/handle_file_digests_cataloger.go b/cmd/syft/cli/ui/handle_file_digests_cataloger.go deleted file mode 100644 index 79550e9cf4a..00000000000 --- a/cmd/syft/cli/ui/handle_file_digests_cataloger.go +++ /dev/null @@ -1,28 +0,0 @@ -package ui - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/wagoodman/go-partybus" - - "github.com/anchore/bubbly/bubbles/taskprogress" - "github.com/anchore/syft/internal/log" - syftEventParsers "github.com/anchore/syft/syft/event/parsers" -) - -func (m *Handler) handleFileDigestsCatalogerStarted(e partybus.Event) []tea.Model { - prog, err := syftEventParsers.ParseFileDigestsCatalogingStarted(e) - if err != nil { - log.WithFields("error", err).Warn("unable to parse event") - return nil - } - - tsk := m.newTaskProgress( - taskprogress.Title{ - Default: "Catalog file digests", - Running: "Cataloging file digests", - Success: "Cataloged file digests", - }, taskprogress.WithStagedProgressable(prog), - ) - - return []tea.Model{tsk} -} diff --git a/cmd/syft/cli/ui/handle_file_digests_cataloger_test.go b/cmd/syft/cli/ui/handle_file_digests_cataloger_test.go deleted file mode 100644 index 2e74009a4cd..00000000000 --- a/cmd/syft/cli/ui/handle_file_digests_cataloger_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package ui - -import ( - "testing" - "time" - - tea "github.com/charmbracelet/bubbletea" - "github.com/gkampitakis/go-snaps/snaps" - "github.com/stretchr/testify/require" - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - - "github.com/anchore/bubbly/bubbles/taskprogress" - syftEvent "github.com/anchore/syft/syft/event" -) - -func TestHandler_handleFileDigestsCatalogerStarted(t *testing.T) { - - tests := []struct { - name string - eventFn func(*testing.T) partybus.Event - iterations int - }{ - { - name: "cataloging in progress", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(50) - - mon := struct { - progress.Progressable - progress.Stager - }{ - Progressable: prog, - Stager: &progress.Stage{ - Current: "current", - }, - } - - return partybus.Event{ - Type: syftEvent.FileDigestsCatalogerStarted, - Value: mon, - } - }, - }, - { - name: "cataloging complete", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(100) - prog.SetCompleted() - - mon := struct { - progress.Progressable - progress.Stager - }{ - Progressable: prog, - Stager: &progress.Stage{ - Current: "current", - }, - } - - return partybus.Event{ - Type: syftEvent.FileDigestsCatalogerStarted, - Value: mon, - } - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - event := tt.eventFn(t) - handler := New(DefaultHandlerConfig()) - handler.WindowSize = tea.WindowSizeMsg{ - Width: 100, - Height: 80, - } - - models := handler.Handle(event) - require.Len(t, models, 1) - model := models[0] - - tsk, ok := model.(taskprogress.Model) - require.True(t, ok) - - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ - Time: time.Now(), - Sequence: tsk.Sequence(), - ID: tsk.ID(), - }) - t.Log(got) - snaps.MatchSnapshot(t, got) - }) - } -} diff --git a/cmd/syft/cli/ui/handle_file_indexing_test.go b/cmd/syft/cli/ui/handle_file_indexing_test.go index 86473c411a4..65615119dfd 100644 --- a/cmd/syft/cli/ui/handle_file_indexing_test.go +++ b/cmd/syft/cli/ui/handle_file_indexing_test.go @@ -80,18 +80,21 @@ func TestHandler_handleFileIndexingStarted(t *testing.T) { Height: 80, } - models := handler.Handle(event) + models, _ := handler.Handle(event) require.Len(t, models, 1) model := models[0] tsk, ok := model.(taskprogress.Model) require.True(t, ok) - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ + gotModel := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ Time: time.Now(), Sequence: tsk.Sequence(), ID: tsk.ID(), }) + + got := gotModel.View() + t.Log(got) snaps.MatchSnapshot(t, got) }) diff --git a/cmd/syft/cli/ui/handle_file_metadata_cataloger.go b/cmd/syft/cli/ui/handle_file_metadata_cataloger.go deleted file mode 100644 index 58535abc198..00000000000 --- a/cmd/syft/cli/ui/handle_file_metadata_cataloger.go +++ /dev/null @@ -1,29 +0,0 @@ -package ui - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/wagoodman/go-partybus" - - "github.com/anchore/bubbly/bubbles/taskprogress" - "github.com/anchore/syft/internal/log" - syftEventParsers "github.com/anchore/syft/syft/event/parsers" -) - -func (m *Handler) handleFileMetadataCatalogerStarted(e partybus.Event) []tea.Model { - prog, err := syftEventParsers.ParseFileMetadataCatalogingStarted(e) - if err != nil { - log.WithFields("error", err).Warn("unable to parse event") - return nil - } - - tsk := m.newTaskProgress( - taskprogress.Title{ - Default: "Catalog file metadata", - Running: "Cataloging file metadata", - Success: "Cataloged file metadata", - }, - taskprogress.WithStagedProgressable(prog), - ) - - return []tea.Model{tsk} -} diff --git a/cmd/syft/cli/ui/handle_file_metadata_cataloger_test.go b/cmd/syft/cli/ui/handle_file_metadata_cataloger_test.go deleted file mode 100644 index d247001c8fd..00000000000 --- a/cmd/syft/cli/ui/handle_file_metadata_cataloger_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package ui - -import ( - "testing" - "time" - - tea "github.com/charmbracelet/bubbletea" - "github.com/gkampitakis/go-snaps/snaps" - "github.com/stretchr/testify/require" - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - - "github.com/anchore/bubbly/bubbles/taskprogress" - syftEvent "github.com/anchore/syft/syft/event" -) - -func TestHandler_handleFileMetadataCatalogerStarted(t *testing.T) { - - tests := []struct { - name string - eventFn func(*testing.T) partybus.Event - iterations int - }{ - { - name: "cataloging in progress", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(50) - - mon := struct { - progress.Progressable - progress.Stager - }{ - Progressable: prog, - Stager: &progress.Stage{ - Current: "current", - }, - } - - return partybus.Event{ - Type: syftEvent.FileMetadataCatalogerStarted, - Value: mon, - } - }, - }, - { - name: "cataloging complete", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(100) - prog.SetCompleted() - - mon := struct { - progress.Progressable - progress.Stager - }{ - Progressable: prog, - Stager: &progress.Stage{ - Current: "current", - }, - } - - return partybus.Event{ - Type: syftEvent.FileMetadataCatalogerStarted, - Value: mon, - } - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - event := tt.eventFn(t) - handler := New(DefaultHandlerConfig()) - handler.WindowSize = tea.WindowSizeMsg{ - Width: 100, - Height: 80, - } - - models := handler.Handle(event) - require.Len(t, models, 1) - model := models[0] - - tsk, ok := model.(taskprogress.Model) - require.True(t, ok) - - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ - Time: time.Now(), - Sequence: tsk.Sequence(), - ID: tsk.ID(), - }) - t.Log(got) - snaps.MatchSnapshot(t, got) - }) - } -} diff --git a/cmd/syft/cli/ui/handle_package_cataloger.go b/cmd/syft/cli/ui/handle_package_cataloger.go deleted file mode 100644 index 3aa2f9330e5..00000000000 --- a/cmd/syft/cli/ui/handle_package_cataloger.go +++ /dev/null @@ -1,87 +0,0 @@ -package ui - -import ( - "fmt" - - tea "github.com/charmbracelet/bubbletea" - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - - "github.com/anchore/bubbly/bubbles/taskprogress" - "github.com/anchore/syft/internal/log" - syftEventParsers "github.com/anchore/syft/syft/event/parsers" - "github.com/anchore/syft/syft/pkg/cataloger" -) - -var _ progress.StagedProgressable = (*packageCatalogerProgressAdapter)(nil) - -type packageCatalogerProgressAdapter struct { - monitor *cataloger.Monitor - monitors []progress.Monitorable -} - -func newPackageCatalogerProgressAdapter(monitor *cataloger.Monitor) packageCatalogerProgressAdapter { - return packageCatalogerProgressAdapter{ - monitor: monitor, - monitors: []progress.Monitorable{ - monitor.FilesProcessed, - monitor.PackagesDiscovered, - }, - } -} - -func (p packageCatalogerProgressAdapter) Stage() string { - return fmt.Sprintf("%d packages", p.monitor.PackagesDiscovered.Current()) -} - -func (p packageCatalogerProgressAdapter) Current() int64 { - return p.monitor.PackagesDiscovered.Current() -} - -func (p packageCatalogerProgressAdapter) Error() error { - completedMonitors := 0 - for _, monitor := range p.monitors { - err := monitor.Error() - if err == nil { - continue - } - if progress.IsErrCompleted(err) { - completedMonitors++ - continue - } - // something went wrong - return err - } - if completedMonitors == len(p.monitors) && len(p.monitors) > 0 { - return p.monitors[0].Error() - } - return nil -} - -func (p packageCatalogerProgressAdapter) Size() int64 { - // this is an inherently unknown value (indeterminate total number of packages to discover) - return -1 -} - -func (m *Handler) handlePackageCatalogerStarted(e partybus.Event) []tea.Model { - monitor, err := syftEventParsers.ParsePackageCatalogerStarted(e) - if err != nil { - log.WithFields("error", err).Warn("unable to parse event") - return nil - } - - tsk := m.newTaskProgress( - taskprogress.Title{ - Default: "Catalog packages", - Running: "Cataloging packages", - Success: "Cataloged packages", - }, - taskprogress.WithStagedProgressable( - newPackageCatalogerProgressAdapter(monitor), - ), - ) - - tsk.HideStageOnSuccess = false - - return []tea.Model{tsk} -} diff --git a/cmd/syft/cli/ui/handle_package_cataloger_test.go b/cmd/syft/cli/ui/handle_package_cataloger_test.go deleted file mode 100644 index a5a72d59d36..00000000000 --- a/cmd/syft/cli/ui/handle_package_cataloger_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package ui - -import ( - "testing" - "time" - - tea "github.com/charmbracelet/bubbletea" - "github.com/gkampitakis/go-snaps/snaps" - "github.com/stretchr/testify/require" - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - - "github.com/anchore/bubbly/bubbles/taskprogress" - syftEvent "github.com/anchore/syft/syft/event" - "github.com/anchore/syft/syft/pkg/cataloger" -) - -func TestHandler_handlePackageCatalogerStarted(t *testing.T) { - - tests := []struct { - name string - eventFn func(*testing.T) partybus.Event - iterations int - }{ - { - name: "cataloging in progress", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(50) - - mon := cataloger.Monitor{ - FilesProcessed: progress.NewManual(-1), - PackagesDiscovered: prog, - } - - return partybus.Event{ - Type: syftEvent.PackageCatalogerStarted, - Value: mon, - } - }, - }, - { - name: "cataloging only files complete", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(50) - - files := progress.NewManual(-1) - files.SetCompleted() - - mon := cataloger.Monitor{ - FilesProcessed: files, - PackagesDiscovered: prog, - } - - return partybus.Event{ - Type: syftEvent.PackageCatalogerStarted, - Value: mon, - } - }, - }, - { - name: "cataloging only packages complete", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(100) - prog.SetCompleted() - - files := progress.NewManual(-1) - - mon := cataloger.Monitor{ - FilesProcessed: files, - PackagesDiscovered: prog, - } - - return partybus.Event{ - Type: syftEvent.PackageCatalogerStarted, - Value: mon, - } - }, - }, - { - name: "cataloging complete", - eventFn: func(t *testing.T) partybus.Event { - prog := &progress.Manual{} - prog.SetTotal(100) - prog.Set(100) - prog.SetCompleted() - - files := progress.NewManual(-1) - files.SetCompleted() - - mon := cataloger.Monitor{ - FilesProcessed: files, - PackagesDiscovered: prog, - } - - return partybus.Event{ - Type: syftEvent.PackageCatalogerStarted, - Value: mon, - } - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - event := tt.eventFn(t) - handler := New(DefaultHandlerConfig()) - handler.WindowSize = tea.WindowSizeMsg{ - Width: 100, - Height: 80, - } - - models := handler.Handle(event) - require.Len(t, models, 1) - model := models[0] - - tsk, ok := model.(taskprogress.Model) - require.True(t, ok) - - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ - Time: time.Now(), - Sequence: tsk.Sequence(), - ID: tsk.ID(), - }) - t.Log(got) - snaps.MatchSnapshot(t, got) - }) - } -} diff --git a/cmd/syft/cli/ui/handle_read_image_test.go b/cmd/syft/cli/ui/handle_read_image_test.go index 864d1e782f4..d3354c04e79 100644 --- a/cmd/syft/cli/ui/handle_read_image_test.go +++ b/cmd/syft/cli/ui/handle_read_image_test.go @@ -98,18 +98,21 @@ func TestHandler_handleReadImage(t *testing.T) { Height: 80, } - models := handler.Handle(event) + models, _ := handler.Handle(event) require.Len(t, models, 1) model := models[0] tsk, ok := model.(taskprogress.Model) require.True(t, ok) - got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ + gotModel := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{ Time: time.Now(), Sequence: tsk.Sequence(), ID: tsk.ID(), }) + + got := gotModel.View() + t.Log(got) snaps.MatchSnapshot(t, got) }) diff --git a/cmd/syft/cli/ui/handler.go b/cmd/syft/cli/ui/handler.go index 1a0e64d10fc..aefc471f3a1 100644 --- a/cmd/syft/cli/ui/handler.go +++ b/cmd/syft/cli/ui/handler.go @@ -29,6 +29,8 @@ type Handler struct { Config HandlerConfig bubbly.EventHandler + + onNewCatalogerTask *sync.Once } func DefaultHandlerConfig() HandlerConfig { @@ -41,28 +43,32 @@ func New(cfg HandlerConfig) *Handler { d := bubbly.NewEventDispatcher() h := &Handler{ - EventHandler: d, - Running: &sync.WaitGroup{}, - Config: cfg, + EventHandler: d, + Running: &sync.WaitGroup{}, + Config: cfg, + onNewCatalogerTask: &sync.Once{}, } // register all supported event types with the respective handler functions d.AddHandlers(map[partybus.EventType]bubbly.EventHandlerFn{ - stereoscopeEvent.PullDockerImage: h.handlePullDockerImage, - stereoscopeEvent.PullContainerdImage: h.handlePullContainerdImage, - stereoscopeEvent.ReadImage: h.handleReadImage, - stereoscopeEvent.FetchImage: h.handleFetchImage, - syftEvent.PackageCatalogerStarted: h.handlePackageCatalogerStarted, - syftEvent.FileDigestsCatalogerStarted: h.handleFileDigestsCatalogerStarted, - syftEvent.FileMetadataCatalogerStarted: h.handleFileMetadataCatalogerStarted, - syftEvent.FileIndexingStarted: h.handleFileIndexingStarted, - syftEvent.AttestationStarted: h.handleAttestationStarted, - syftEvent.CatalogerTaskStarted: h.handleCatalogerTaskStarted, + stereoscopeEvent.PullDockerImage: simpleHandler(h.handlePullDockerImage), + stereoscopeEvent.PullContainerdImage: simpleHandler(h.handlePullContainerdImage), + stereoscopeEvent.ReadImage: simpleHandler(h.handleReadImage), + stereoscopeEvent.FetchImage: simpleHandler(h.handleFetchImage), + syftEvent.FileIndexingStarted: simpleHandler(h.handleFileIndexingStarted), + syftEvent.AttestationStarted: simpleHandler(h.handleAttestationStarted), + syftEvent.CatalogerTaskStarted: h.handleCatalogerTaskStarted, }) return h } +func simpleHandler(fn func(partybus.Event) []tea.Model) bubbly.EventHandlerFn { + return func(e partybus.Event) ([]tea.Model, tea.Cmd) { + return fn(e), nil + } +} + func (m *Handler) OnMessage(msg tea.Msg) { if msg, ok := msg.(tea.WindowSizeMsg); ok { m.WindowSize = msg diff --git a/cmd/syft/cli/ui/util_test.go b/cmd/syft/cli/ui/util_test.go index 71cc8809a86..c13aadae050 100644 --- a/cmd/syft/cli/ui/util_test.go +++ b/cmd/syft/cli/ui/util_test.go @@ -4,12 +4,11 @@ import ( "reflect" "sync" "testing" - "unsafe" tea "github.com/charmbracelet/bubbletea" ) -func runModel(t testing.TB, m tea.Model, iterations int, message tea.Msg, h ...*sync.WaitGroup) string { +func runModel(t testing.TB, m tea.Model, iterations int, message tea.Msg, h ...*sync.WaitGroup) tea.Model { t.Helper() if iterations == 0 { iterations = 1 @@ -37,34 +36,21 @@ func runModel(t testing.TB, m tea.Model, iterations int, message tea.Msg, h ...* cmd = tea.Batch(nextCmds...) } - return m.View() + return m } -func flatten(p tea.Msg) (msgs []tea.Msg) { - if reflect.TypeOf(p).Name() == "batchMsg" { - partials := extractBatchMessages(p) - for _, m := range partials { - msgs = append(msgs, flatten(m)...) +func flatten(ps ...tea.Msg) (msgs []tea.Msg) { + for _, p := range ps { + if bm, ok := p.(tea.BatchMsg); ok { + for _, m := range bm { + if m == nil { + continue + } + msgs = append(msgs, flatten(m())...) + } + } else { + msgs = []tea.Msg{p} } - } else { - msgs = []tea.Msg{p} } return msgs } - -func extractBatchMessages(m tea.Msg) (ret []tea.Msg) { - sliceMsgType := reflect.SliceOf(reflect.TypeOf(tea.Cmd(nil))) - value := reflect.ValueOf(m) // note: this is technically unaddressable - - // make our own instance that is addressable - valueCopy := reflect.New(value.Type()).Elem() - valueCopy.Set(value) - - cmds := reflect.NewAt(sliceMsgType, unsafe.Pointer(valueCopy.UnsafeAddr())).Elem() - for i := 0; i < cmds.Len(); i++ { - item := cmds.Index(i) - r := item.Call(nil) - ret = append(ret, r[0].Interface().(tea.Msg)) - } - return ret -} diff --git a/cmd/syft/internal/clio_setup_config.go b/cmd/syft/internal/clio_setup_config.go new file mode 100644 index 00000000000..0127fda0aa9 --- /dev/null +++ b/cmd/syft/internal/clio_setup_config.go @@ -0,0 +1,55 @@ +package internal + +import ( + "io" + "os" + + "github.com/anchore/clio" + "github.com/anchore/stereoscope" + ui2 "github.com/anchore/syft/cmd/syft/cli/ui" + "github.com/anchore/syft/cmd/syft/internal/ui" + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/internal/redact" +) + +func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig { + clioCfg := clio.NewSetupConfig(id). + WithGlobalConfigFlag(). // add persistent -c for reading an application config from + WithGlobalLoggingFlags(). // add persistent -v and -q flags tied to the logging config + WithConfigInRootHelp(). // --help on the root command renders the full application config in the help text + WithUIConstructor( + // select a UI based on the logging configuration and state of stdin (if stdin is a tty) + func(cfg clio.Config) ([]clio.UI, error) { + noUI := ui.None(out, cfg.Log.Quiet) + if !cfg.Log.AllowUI(os.Stdin) || cfg.Log.Quiet { + return []clio.UI{noUI}, nil + } + + return []clio.UI{ + ui.New(out, cfg.Log.Quiet, + ui2.New(ui2.DefaultHandlerConfig()), + ), + noUI, + }, nil + }, + ). + WithInitializers( + func(state *clio.State) error { + // clio is setting up and providing the bus, redact store, and logger to the application. Once loaded, + // we can hoist them into the internal packages for global use. + stereoscope.SetBus(state.Bus) + bus.Set(state.Bus) + + redact.Set(state.RedactStore) + + log.Set(state.Logger) + stereoscope.SetLogger(state.Logger) + return nil + }, + ). + WithPostRuns(func(_ *clio.State, _ error) { + stereoscope.Cleanup() + }) + return clioCfg +} diff --git a/cmd/syft/cli/commands/attest.go b/cmd/syft/internal/commands/attest.go similarity index 90% rename from cmd/syft/cli/commands/attest.go rename to cmd/syft/internal/commands/attest.go index c3067933ef6..e349a1cffb5 100644 --- a/cmd/syft/cli/commands/attest.go +++ b/cmd/syft/internal/commands/attest.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "io" "os" @@ -12,7 +13,7 @@ import ( "github.com/wagoodman/go-progress" "github.com/anchore/clio" - "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/cmd/syft/internal/options" "github.com/anchore/syft/cmd/syft/internal/ui" "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/bus" @@ -29,7 +30,7 @@ import ( ) const ( - attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry + attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry ` attestSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp attestHelp = attestExample + attestSchemeHelp @@ -41,7 +42,7 @@ type attestOptions struct { options.Output `yaml:",inline" mapstructure:",squash"` options.UpdateCheck `yaml:",inline" mapstructure:",squash"` options.Catalog `yaml:",inline" mapstructure:",squash"` - options.Attest `yaml:",inline" mapstructure:",squash"` + Attest options.Attest `yaml:"attest" mapstructure:"attest"` } func Attest(app clio.Application) *cobra.Command { @@ -60,13 +61,13 @@ func Attest(app clio.Application) *cobra.Command { "appName": id.Name, "command": "attest", }), - Args: validatePackagesArgs, + Args: validateScanArgs, PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), RunE: func(cmd *cobra.Command, args []string) error { restoreStdout := ui.CaptureStdoutToTraceLog() defer restoreStdout() - return runAttest(id, &opts, args[0]) + return runAttest(cmd.Context(), id, &opts, args[0]) }, }, &opts) } @@ -98,7 +99,7 @@ func defaultAttestOutputOptions() options.Output { } //nolint:funlen -func runAttest(id clio.Identification, opts *attestOptions, userInput string) error { +func runAttest(ctx context.Context, id clio.Identification, opts *attestOptions, userInput string) error { // TODO: what other validation here besides binary name? if !commandExists(cosignBinName) { return fmt.Errorf("'syft attest' requires cosign to be installed, however it does not appear to be on PATH") @@ -111,7 +112,7 @@ func runAttest(id clio.Identification, opts *attestOptions, userInput string) er } defer os.Remove(f.Name()) - s, err := generateSBOMForAttestation(id, &opts.Catalog, userInput) + s, err := generateSBOMForAttestation(ctx, id, &opts.Catalog, userInput) if err != nil { return fmt.Errorf("unable to build SBOM: %w", err) } @@ -231,7 +232,7 @@ func attestCommand(sbomFilepath string, opts *attestOptions, userInput string) ( } func predicateType(outputName string) string { - // Select Cosign predicate type based on defined output type + // select the Cosign predicate type based on defined output type // As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go switch strings.ToLower(outputName) { case "cyclonedx-json": @@ -245,7 +246,7 @@ func predicateType(outputName string) string { } } -func generateSBOMForAttestation(id clio.Identification, opts *options.Catalog, userInput string) (*sbom.SBOM, error) { +func generateSBOMForAttestation(ctx context.Context, id clio.Identification, opts *options.Catalog, userInput string) (*sbom.SBOM, error) { src, err := getSource(opts, userInput, onlyContainerImages) if err != nil { @@ -260,7 +261,7 @@ func generateSBOMForAttestation(id clio.Identification, opts *options.Catalog, u } }() - s, err := generateSBOM(id, src, opts) + s, err := generateSBOM(ctx, id, src, opts) if err != nil { return nil, err } diff --git a/cmd/syft/cli/commands/attest_test.go b/cmd/syft/internal/commands/attest_test.go similarity index 74% rename from cmd/syft/cli/commands/attest_test.go rename to cmd/syft/internal/commands/attest_test.go index 31ab90e8952..41a26822664 100644 --- a/cmd/syft/cli/commands/attest_test.go +++ b/cmd/syft/internal/commands/attest_test.go @@ -2,18 +2,24 @@ package commands import ( "bytes" + "context" "fmt" + "io" "os/exec" "regexp" "strings" "testing" + "github.com/google/go-cmp/cmp" "github.com/scylladb/go-set/strset" + "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anchore/clio" - "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/clio/cliotestutils" + "github.com/anchore/syft/cmd/syft/internal" + "github.com/anchore/syft/cmd/syft/internal/options" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) @@ -258,7 +264,7 @@ func Test_buildSBOMForAttestation(t *testing.T) { if tt.wantErr == nil { tt.wantErr = require.NoError } - _, err := generateSBOMForAttestation(tt.args.id, tt.args.opts, tt.args.userInput) + _, err := generateSBOMForAttestation(context.Background(), tt.args.id, tt.args.opts, tt.args.userInput) tt.wantErr(t, err) if err != nil { return @@ -266,3 +272,60 @@ func Test_buildSBOMForAttestation(t *testing.T) { }) } } + +func Test_attestCLIWiring(t *testing.T) { + id := clio.Identification{ + Name: "syft", + Version: "testing", + } + cfg := internal.AppClioSetupConfig(id, io.Discard) + tests := []struct { + name string + assertionFunc func(*testing.T, *cobra.Command, []string, ...any) + wantOpts attestOptions + args []string + env map[string]string + }{ + { + name: "key flag is accepted", + args: []string{"some-image:some-tag", "--key", "some-cosign-key.key"}, + assertionFunc: hasAttestOpts(options.Attest{Key: "some-cosign-key.key"}), + }, + { + name: "key password is read from env", + args: []string{"some-image:some-tag", "--key", "cosign.key"}, + env: map[string]string{ + "SYFT_ATTEST_PASSWORD": "some-password", + }, + assertionFunc: hasAttestOpts(options.Attest{ + Key: "cosign.key", + Password: "some-password", + }), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.env != nil { + for k, v := range tt.env { + t.Setenv(k, v) + } + } + app := cliotestutils.NewApplication(t, cfg, tt.assertionFunc) + cmd := Attest(app) + cmd.SetArgs(tt.args) + err := cmd.Execute() + assert.NoError(t, err) + }) + } +} + +func hasAttestOpts(wantOpts options.Attest) cliotestutils.AssertionFunc { + return func(t *testing.T, _ *cobra.Command, _ []string, cfgs ...any) { + assert.Equal(t, len(cfgs), 1) + attestOpts, ok := cfgs[0].(*attestOptions) + require.True(t, ok) + if d := cmp.Diff(wantOpts, attestOpts.Attest); d != "" { + t.Errorf("mismatched attest options (-want +got):\n%s", d) + } + } +} diff --git a/cmd/syft/internal/commands/cataloger.go b/cmd/syft/internal/commands/cataloger.go new file mode 100644 index 00000000000..0baf5b5231a --- /dev/null +++ b/cmd/syft/internal/commands/cataloger.go @@ -0,0 +1,20 @@ +package commands + +import ( + "github.com/spf13/cobra" + + "github.com/anchore/clio" +) + +func Cataloger(app clio.Application) *cobra.Command { + cmd := &cobra.Command{ + Use: "cataloger", + Short: "Show available catalogers and configuration", + } + + cmd.AddCommand( + CatalogerList(app), + ) + + return cmd +} diff --git a/cmd/syft/internal/commands/cataloger_list.go b/cmd/syft/internal/commands/cataloger_list.go new file mode 100644 index 00000000000..2cdf6e8b82f --- /dev/null +++ b/cmd/syft/internal/commands/cataloger_list.go @@ -0,0 +1,267 @@ +package commands + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/charmbracelet/lipgloss" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/scylladb/go-set/strset" + "github.com/spf13/cobra" + + "github.com/anchore/clio" + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" +) + +type catalogerListOptions struct { + Output string `yaml:"output" json:"output" mapstructure:"output"` + DefaultCatalogers []string `yaml:"default-catalogers" json:"default-catalogers" mapstructure:"default-catalogers"` + SelectCatalogers []string `yaml:"select-catalogers" json:"select-catalogers" mapstructure:"select-catalogers"` + ShowHidden bool `yaml:"show-hidden" json:"show-hidden" mapstructure:"show-hidden"` +} + +func (o *catalogerListOptions) AddFlags(flags clio.FlagSet) { + flags.StringVarP(&o.Output, "output", "o", "format to output the cataloger list (available: table, json)") + + flags.StringArrayVarP(&o.DefaultCatalogers, "override-default-catalogers", "", "override the default catalogers with an expression") + + flags.StringArrayVarP(&o.SelectCatalogers, "select-catalogers", "", "select catalogers with an expression") + + flags.BoolVarP(&o.ShowHidden, "show-hidden", "s", "show catalogers that have been de-selected") +} + +func defaultCatalogerListOptions() *catalogerListOptions { + return &catalogerListOptions{ + DefaultCatalogers: []string{"all"}, + } +} + +func CatalogerList(app clio.Application) *cobra.Command { + opts := defaultCatalogerListOptions() + + return app.SetupCommand(&cobra.Command{ + Use: "list [OPTIONS]", + Short: "List available catalogers", + RunE: func(_ *cobra.Command, _ []string) error { + return runCatalogerList(opts) + }, + }, opts) +} + +func runCatalogerList(opts *catalogerListOptions) error { + factories := task.DefaultPackageTaskFactories() + allTasks, err := factories.Tasks(task.DefaultCatalogingFactoryConfig()) + if err != nil { + return fmt.Errorf("unable to create cataloger tasks: %w", err) + } + + report, err := catalogerListReport(opts, allTasks) + if err != nil { + return fmt.Errorf("unable to generate cataloger list report: %w", err) + } + + bus.Report(report) + + return nil +} + +func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (string, error) { + selectedTasks, selectionEvidence, err := task.Select(allTasks, + pkgcataloging.NewSelectionRequest(). + WithDefaults(opts.DefaultCatalogers...). + WithExpression(opts.SelectCatalogers...), + ) + if err != nil { + return "", fmt.Errorf("unable to select catalogers: %w", err) + } + var report string + + switch opts.Output { + case "json": + report, err = renderCatalogerListJSON(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers) + case "table", "": + if opts.ShowHidden { + report = renderCatalogerListTable(allTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers) + } else { + report = renderCatalogerListTable(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers) + } + } + + if err != nil { + return "", fmt.Errorf("unable to render cataloger list: %w", err) + } + + return report, nil +} + +func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) (string, error) { + type node struct { + Name string `json:"name"` + Tags []string `json:"tags"` + } + + names, tagsByName := extractTaskInfo(tasks) + + nodesByName := make(map[string]node) + + for name := range tagsByName { + tagsSelected := selection.TokensByTask[name].SelectedOn.List() + + if len(tagsSelected) == 1 && tagsSelected[0] == "all" { + tagsSelected = tagsByName[name] + } + + sort.Strings(tagsSelected) + + if tagsSelected == nil { + // ensure collections are not null + tagsSelected = []string{} + } + + nodesByName[name] = node{ + Name: name, + Tags: tagsSelected, + } + } + + type document struct { + DefaultSelection []string `json:"default"` + Selection []string `json:"selection"` + Catalogers []node `json:"catalogers"` + } + + if selections == nil { + // ensure collections are not null + selections = []string{} + } + + doc := document{ + DefaultSelection: defaultSelections, + Selection: selections, + } + + for _, name := range names { + doc.Catalogers = append(doc.Catalogers, nodesByName[name]) + } + + by, err := json.Marshal(doc) + + return string(by), err +} + +func renderCatalogerListTable(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) string { + t := table.NewWriter() + t.SetStyle(table.StyleLight) + t.AppendHeader(table.Row{"Cataloger", "Tags"}) + + names, tagsByName := extractTaskInfo(tasks) + + rowsByName := make(map[string]table.Row) + + for name, tags := range tagsByName { + rowsByName[name] = formatRow(name, tags, selection) + } + + for _, name := range names { + t.AppendRow(rowsByName[name]) + } + + report := t.Render() + + if len(selections) > 0 { + header := "Selected by expressions:\n" + for _, expr := range selections { + header += fmt.Sprintf(" - %q\n", expr) + } + report = header + report + } + + if len(defaultSelections) > 0 { + header := "Default selections:\n" + for _, expr := range defaultSelections { + header += fmt.Sprintf(" - %q\n", expr) + } + report = header + report + } + + return report +} + +func formatRow(name string, tags []string, selection task.Selection) table.Row { + isIncluded := selection.Result.Has(name) + var selections *task.TokenSelection + if s, exists := selection.TokensByTask[name]; exists { + selections = &s + } + + var formattedTags []string + for _, tag := range tags { + formattedTags = append(formattedTags, formatToken(tag, selections, isIncluded)) + } + + var tagStr string + if isIncluded { + tagStr = strings.Join(formattedTags, ", ") + } else { + tagStr = strings.Join(formattedTags, grey.Render(", ")) + } + + // TODO: selection should keep warnings (non-selections) in struct + + return table.Row{ + formatToken(name, selections, isIncluded), + tagStr, + } +} + +var ( + green = lipgloss.NewStyle().Foreground(lipgloss.Color("10")) // hi green + grey = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) // dark grey + red = lipgloss.NewStyle().Foreground(lipgloss.Color("9")) // high red +) + +func formatToken(token string, selection *task.TokenSelection, included bool) string { + if included && selection != nil { + // format all tokens in selection in green + if selection.SelectedOn.Has(token) { + return green.Render(token) + } + + return token + } + + // format all tokens in selection in red, all others in grey + if selection != nil && selection.DeselectedOn.Has(token) { + return red.Render(token) + } + + return grey.Render(token) +} + +func extractTaskInfo(tasks []task.Task) ([]string, map[string][]string) { + tagsByName := make(map[string][]string) + var names []string + + for _, tsk := range tasks { + var tags []string + name := tsk.Name() + + if s, ok := tsk.(task.Selector); ok { + set := strset.New(s.Selectors()...) + set.Remove(name) + tags = set.List() + sort.Strings(tags) + } + + tagsByName[name] = tags + names = append(names, name) + } + + sort.Strings(names) + + return names, tagsByName +} diff --git a/cmd/syft/internal/commands/cataloger_list_test.go b/cmd/syft/internal/commands/cataloger_list_test.go new file mode 100644 index 00000000000..24d8ab1f20c --- /dev/null +++ b/cmd/syft/internal/commands/cataloger_list_test.go @@ -0,0 +1,201 @@ +package commands + +import ( + "context" + "strings" + "testing" + + "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/syft/file" +) + +var _ interface { + task.Task + task.Selector +} = (*dummyTask)(nil) + +type dummyTask struct { + name string + selectors []string +} + +func (d dummyTask) HasAllSelectors(s ...string) bool { + return strset.New(d.selectors...).Has(s...) +} + +func (d dummyTask) Selectors() []string { + return d.selectors +} + +func (d dummyTask) Name() string { + return d.name +} + +func (d dummyTask) Execute(_ context.Context, _ file.Resolver, _ sbomsync.Builder) error { + panic("implement me") +} + +func testTasks() []task.Task { + return []task.Task{ + dummyTask{ + name: "task1", + selectors: []string{"image", "a", "b", "1"}, + }, + dummyTask{ + name: "task2", + selectors: []string{"image", "b", "c", "2"}, + }, + dummyTask{ + name: "task3", + selectors: []string{"directory", "c", "d", "3"}, + }, + dummyTask{ + name: "task4", + selectors: []string{"directory", "d", "e", "4"}, + }, + } +} + +func Test_catalogerListReport(t *testing.T) { + tests := []struct { + name string + options *catalogerListOptions + want string + wantErr require.ErrorAssertionFunc + }{ + { + name: "no expressions, table", + options: func() *catalogerListOptions { + c := defaultCatalogerListOptions() + c.Output = "table" + return c + }(), + want: ` +Default selections: + - "all" +┌───────────â”Ŧ────────────────────┐ +│ CATALOGER │ TAGS │ +├───────────â”ŧ────────────────────┤ +│ task1 │ 1, a, b, image │ +│ task2 │ 2, b, c, image │ +│ task3 │ 3, c, d, directory │ +│ task4 │ 4, d, directory, e │ +└───────────┴────────────────────┘ +`, + }, + { + name: "no expressions, json", + options: func() *catalogerListOptions { + c := defaultCatalogerListOptions() + c.Output = "json" + return c + }(), + want: ` +{"default":["all"],"selection":[],"catalogers":[{"name":"task1","tags":["1","a","b","image"]},{"name":"task2","tags":["2","b","c","image"]},{"name":"task3","tags":["3","c","d","directory"]},{"name":"task4","tags":["4","d","directory","e"]}]} +`, + }, + { + name: "no expressions, default selection, table", + options: func() *catalogerListOptions { + c := defaultCatalogerListOptions() + c.Output = "table" + c.DefaultCatalogers = []string{ + "image", + } + return c + }(), + want: ` +Default selections: + - "image" +┌───────────â”Ŧ────────────────┐ +│ CATALOGER │ TAGS │ +├───────────â”ŧ────────────────┤ +│ task1 │ 1, a, b, image │ +│ task2 │ 2, b, c, image │ +└───────────┴────────────────┘ +`, + }, + { + name: "no expressions, default selection, json", + options: func() *catalogerListOptions { + c := defaultCatalogerListOptions() + c.Output = "json" + c.DefaultCatalogers = []string{ + "image", + } + return c + }(), + want: ` +{"default":["image"],"selection":[],"catalogers":[{"name":"task1","tags":["image"]},{"name":"task2","tags":["image"]}]} +`, + }, + { + name: "with expressions, default selection, table", + options: func() *catalogerListOptions { + c := defaultCatalogerListOptions() + c.Output = "table" + c.DefaultCatalogers = []string{ + "image", + } + c.SelectCatalogers = []string{ + "-directory", + "+task3", + "-c", + "b", + } + return c + }(), + want: ` +Default selections: + - "image" +Selected by expressions: + - "-directory" + - "+task3" + - "-c" + - "b" +┌───────────â”Ŧ────────────────────┐ +│ CATALOGER │ TAGS │ +├───────────â”ŧ────────────────────┤ +│ task1 │ 1, a, b, image │ +│ task3 │ 3, c, d, directory │ +└───────────┴────────────────────┘ +`, + }, + { + name: "with expressions, default selection, json", + options: func() *catalogerListOptions { + c := defaultCatalogerListOptions() + c.Output = "json" + c.DefaultCatalogers = []string{ + "image", + } + c.SelectCatalogers = []string{ + "-directory", + "+task3", + "-c", + "b", + } + return c + }(), + want: ` +{"default":["image"],"selection":["-directory","+task3","-c","b"],"catalogers":[{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]} +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = require.NoError + } + + got, err := catalogerListReport(tt.options, testTasks()) + tt.wantErr(t, err) + assert.Equal(t, strings.TrimSpace(tt.want), strings.TrimSpace(got)) + }) + } +} diff --git a/cmd/syft/internal/commands/commands_test.go b/cmd/syft/internal/commands/commands_test.go new file mode 100644 index 00000000000..4ffc937d875 --- /dev/null +++ b/cmd/syft/internal/commands/commands_test.go @@ -0,0 +1,20 @@ +package commands + +import ( + "os" + "testing" + + gologgerredact "github.com/anchore/go-logger/adapter/redact" + "github.com/anchore/syft/internal/redact" +) + +func TestMain(m *testing.M) { + // Initialize global state needed to test clio/cobra commands directly + // Should be kept minimal. + + // Initialize redact store once for all tests in the commands package + // Redact store must be wired up here because syft will panic unless + // a redact store is wired up exactly once + redact.Set(gologgerredact.NewStore()) + os.Exit(m.Run()) +} diff --git a/cmd/syft/cli/commands/convert.go b/cmd/syft/internal/commands/convert.go similarity index 96% rename from cmd/syft/cli/commands/convert.go rename to cmd/syft/internal/commands/convert.go index 3a3687811fa..2a1e6725643 100644 --- a/cmd/syft/cli/commands/convert.go +++ b/cmd/syft/internal/commands/convert.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" "github.com/anchore/clio" - "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/cmd/syft/internal/options" "github.com/anchore/syft/cmd/syft/internal/ui" "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" @@ -47,7 +47,7 @@ func Convert(app clio.Application) *cobra.Command { }), Args: validateConvertArgs, PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { restoreStdout := ui.CaptureStdoutToTraceLog() defer restoreStdout() diff --git a/cmd/syft/internal/commands/lib_scribe.go b/cmd/syft/internal/commands/lib_scribe.go new file mode 100644 index 00000000000..3d4154842c8 --- /dev/null +++ b/cmd/syft/internal/commands/lib_scribe.go @@ -0,0 +1,25 @@ +package commands + +import ( + "context" + + "github.com/anchore/clio" + "github.com/anchore/syft/cmd/syft/internal/options" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +type OptionsCatalog = options.Catalog +type ScanOptions = scanOptions + +func GetSource(opts *options.Catalog, userInput string, filters ...func(*source.Detection) error) (source.Source, error) { + return getSource(opts, userInput, filters...) +} + +func GenerateSBOM(ctx context.Context, id clio.Identification, src source.Source, opts *options.Catalog) (*sbom.SBOM, error) { + return generateSBOM(ctx, id, src, opts) +} + +func DefaultScanOptions() *scanOptions { + return defaultScanOptions() +} diff --git a/cmd/syft/internal/commands/packages.go b/cmd/syft/internal/commands/packages.go new file mode 100644 index 00000000000..92f225d1d57 --- /dev/null +++ b/cmd/syft/internal/commands/packages.go @@ -0,0 +1,37 @@ +package commands + +import ( + "github.com/spf13/cobra" + + "github.com/anchore/clio" + "github.com/anchore/syft/cmd/syft/internal/ui" + "github.com/anchore/syft/internal" +) + +func Packages(app clio.Application, scanCmd *cobra.Command) *cobra.Command { + id := app.ID() + + opts := defaultScanOptions() + + cmd := app.SetupCommand(&cobra.Command{ + Use: "packages [SOURCE]", + Short: scanCmd.Short, + Long: scanCmd.Long, + Args: scanCmd.Args, + Example: internal.Tprintf(scanHelp, map[string]interface{}{ + "appName": id.Name, + "command": "packages", + }), + PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), + RunE: func(cmd *cobra.Command, args []string) error { + restoreStdout := ui.CaptureStdoutToTraceLog() + defer restoreStdout() + + return runScan(cmd.Context(), id, opts, args[0]) + }, + }, opts) + + cmd.Deprecated = "use `syft scan` instead" + + return cmd +} diff --git a/cmd/syft/internal/commands/packages_test.go b/cmd/syft/internal/commands/packages_test.go new file mode 100644 index 00000000000..35d5241a217 --- /dev/null +++ b/cmd/syft/internal/commands/packages_test.go @@ -0,0 +1,300 @@ +package commands + +import ( + "errors" + "fmt" + "testing" + + "github.com/hashicorp/go-multierror" + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/internal/task" +) + +func Test_filterExpressionErrors_expressionErrorsHelp(t *testing.T) { + tests := []struct { + name string + err error + wantExpErrs []task.ErrInvalidExpression + wantErr assert.ErrorAssertionFunc + wantHelp string + }{ + { + name: "no errors", + err: nil, + wantExpErrs: nil, + wantErr: assert.NoError, + wantHelp: "", + }, + { + name: "single non-expression error is retained", + err: errors.New("foo"), + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.Equal(t, "foo", err.Error()) + }, + wantHelp: "", + }, + { + name: "multiple non-expression sibling errors are retained", + err: func() error { + var err error + err = multierror.Append(err, errors.New("foo")) + err = multierror.Append(err, errors.New("bar")) + return err + }(), + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + // note: this is the default formatting from the hashicorp multierror object + expected := `2 errors occurred: + * foo + * bar + +` + return assert.Equal(t, expected, err.Error()) + }, + wantHelp: "", + }, + { + name: "has multiple expression errors (with sibling errors)", + err: func() error { + var err error + err = multierror.Append(err, errors.New("foo")) + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}) + err = multierror.Append(err, errors.New("bar")) + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed}) + err = multierror.Append(err, errors.New("last")) + return err + }(), + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + expected := `5 errors occurred: + * foo + * invalid expression: "foo": tags are not allowed with this operation (must use exact names) + * bar + * invalid expression: "bar": names are not allowed with this operation (must use tags) + * last + +` + return assert.Equal(t, expected, err.Error()) + }, + wantExpErrs: []task.ErrInvalidExpression{ + {Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}, + {Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed}, + }, + wantHelp: `Suggestions: + + ❖ Given expression "--select-catalogers foo" + However, tags are not allowed with this operation (must use exact names). + Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs). + If you are certain this is what you want to do, use "--override-default-catalogers foo" instead. + + ❖ Given expression "--select-catalogers bar" + However, names are not allowed with this operation (must use tags). + It seems like you are intending to add a cataloger in addition to the default set. + ... Did you mean "--select-catalogers +bar" instead? +`, + }, + { + name: "has multiple expression errors (with error chains and sibling errors)", + err: func() error { + var err error + err = multierror.Append(err, fmt.Errorf("foo: %w", fmt.Errorf("bar: %w", errors.New("last")))) + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}) + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed}) + err = multierror.Append(err, errors.New("bottom")) + + return fmt.Errorf("top: %w", fmt.Errorf("middle: %w", err)) + }(), + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + expected := `top: middle: 4 errors occurred: + * foo: bar: last + * invalid expression: "foo": tags are not allowed with this operation (must use exact names) + * invalid expression: "bar": names are not allowed with this operation (must use tags) + * bottom + +` + return assert.Equal(t, expected, err.Error()) + }, + wantExpErrs: []task.ErrInvalidExpression{ + {Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}, + {Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed}, + }, + wantHelp: `Suggestions: + + ❖ Given expression "--select-catalogers foo" + However, tags are not allowed with this operation (must use exact names). + Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs). + If you are certain this is what you want to do, use "--override-default-catalogers foo" instead. + + ❖ Given expression "--select-catalogers bar" + However, names are not allowed with this operation (must use tags). + It seems like you are intending to add a cataloger in addition to the default set. + ... Did you mean "--select-catalogers +bar" instead? +`, + }, + { + name: "has multiple expression errors (with error chains and sibling errors)", + err: func() error { + var err error + err = multierror.Append(err, fmt.Errorf("foo: %w", fmt.Errorf("bar: %w", errors.New("last")))) + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}) + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed}) + err = multierror.Append(err, errors.New("bottom")) + + // note we wrap the top error in a chain + return fmt.Errorf("top: %w", fmt.Errorf("middle: %w", err)) + }(), + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + expected := `top: middle: 4 errors occurred: + * foo: bar: last + * invalid expression: "foo": tags are not allowed with this operation (must use exact names) + * invalid expression: "bar": names are not allowed with this operation (must use tags) + * bottom + +` + return assert.Equal(t, expected, err.Error()) + }, + wantExpErrs: []task.ErrInvalidExpression{ + {Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}, + {Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed}, + }, + wantHelp: `Suggestions: + + ❖ Given expression "--select-catalogers foo" + However, tags are not allowed with this operation (must use exact names). + Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs). + If you are certain this is what you want to do, use "--override-default-catalogers foo" instead. + + ❖ Given expression "--select-catalogers bar" + However, names are not allowed with this operation (must use tags). + It seems like you are intending to add a cataloger in addition to the default set. + ... Did you mean "--select-catalogers +bar" instead? +`, + }, + { + name: "preserve for any errors within ErrInvalidExpression types", + err: func() error { + var err error + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}) + err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: errors.New("explanation")}) // this is what makes this test different... + + return err + }(), + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + // note: the errors are removed and the help text shows the enriched error help + expected := `2 errors occurred: + * invalid expression: "foo": tags are not allowed with this operation (must use exact names) + * invalid expression: "bar": explanation + +` + return assert.Equal(t, expected, err.Error()) + }, + wantExpErrs: []task.ErrInvalidExpression{ + {Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed}, + {Expression: "bar", Operation: task.SubSelectOperation, Err: errors.New("explanation")}, + }, + wantHelp: `Suggestions: + + ❖ Given expression "--select-catalogers foo" + However, tags are not allowed with this operation (must use exact names). + Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs). + If you are certain this is what you want to do, use "--override-default-catalogers foo" instead. + +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotExpErrs := filterExpressionErrors(tt.err) + tt.wantErr(t, tt.err) // ensure the error still remains + assert.Equal(t, tt.wantExpErrs, gotExpErrs) + + gotHelp := expressionErrorsHelp(gotExpErrs) + assert.Equal(t, tt.wantHelp, gotHelp) + }) + } +} + +func Test_expressionSuggestions(t *testing.T) { + tests := []struct { + name string + expErr task.ErrInvalidExpression + want string + }{ + { + name: "no embedded error", + expErr: task.ErrInvalidExpression{ + Expression: "example", + }, + want: ``, + }, + { + name: "general error", + expErr: task.ErrInvalidExpression{ + Err: errors.New("general error message"), + Expression: "example", + }, + want: ``, + }, + { + name: "ErrUnknownNameOrTag with add operation", + expErr: task.ErrInvalidExpression{ + Err: task.ErrUnknownNameOrTag, + Operation: task.AddOperation, + Expression: "+example", + }, + want: ``, + }, + { + name: "ErrUnknownNameOrTag with subselect operation", + expErr: task.ErrInvalidExpression{ + Err: task.ErrUnknownNameOrTag, + Operation: task.SubSelectOperation, + Expression: "example", + }, + want: ``, + }, + { + name: "ErrNamesNotAllowed with subselect operator", + expErr: task.ErrInvalidExpression{ + Err: task.ErrNamesNotAllowed, + Operation: task.SubSelectOperation, + Expression: "example", + }, + want: ` ❖ Given expression "--select-catalogers example" + However, names are not allowed with this operation (must use tags). + It seems like you are intending to add a cataloger in addition to the default set. + ... Did you mean "--select-catalogers +example" instead? +`, + }, + { + name: "ErrTagsNotAllowed with add operation", + expErr: task.ErrInvalidExpression{ + Err: task.ErrTagsNotAllowed, + Operation: task.AddOperation, + Expression: "+example", + }, + want: ` ❖ Given expression "--select-catalogers +example" + However, tags are not allowed with this operation (must use exact names). + Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs). + If you are certain this is what you want to do, use "--override-default-catalogers example" instead. +`, + }, + { + name: "ErrAllNotAllowed with subselect operation", + expErr: task.ErrInvalidExpression{ + Err: task.ErrAllNotAllowed, + Operation: task.SubSelectOperation, + Expression: "example", + }, + want: ` ❖ Given expression "--select-catalogers example" + However, you cannot use the 'all' operand in this context. + It seems like you are intending to use all catalogers (which is not recommended). + ... Did you mean "--override-default-catalogers example" instead? +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, expressionSuggetions(tt.expErr)) + }) + } +} diff --git a/cmd/syft/cli/commands/root.go b/cmd/syft/internal/commands/root.go similarity index 72% rename from cmd/syft/cli/commands/root.go rename to cmd/syft/internal/commands/root.go index fb3234b50b9..bfc508bf33e 100644 --- a/cmd/syft/cli/commands/root.go +++ b/cmd/syft/internal/commands/root.go @@ -6,12 +6,13 @@ import ( "github.com/spf13/cobra" "github.com/anchore/clio" + "github.com/anchore/syft/cmd/syft/internal/ui" ) func Root(app clio.Application, packagesCmd *cobra.Command) *cobra.Command { id := app.ID() - opts := defaultPackagesOptions() + opts := defaultScanOptions() return app.SetupRootCommand(&cobra.Command{ Use: fmt.Sprintf("%s [SOURCE]", app.ID().Name), @@ -21,7 +22,10 @@ func Root(app clio.Application, packagesCmd *cobra.Command) *cobra.Command { Example: packagesCmd.Example, PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), RunE: func(cmd *cobra.Command, args []string) error { - return runPackages(id, opts, args[0]) + restoreStdout := ui.CaptureStdoutToTraceLog() + defer restoreStdout() + + return runScan(cmd.Context(), id, opts, args[0]) }, }, opts) } diff --git a/cmd/syft/internal/commands/scan.go b/cmd/syft/internal/commands/scan.go new file mode 100644 index 00000000000..c70d84e994d --- /dev/null +++ b/cmd/syft/internal/commands/scan.go @@ -0,0 +1,447 @@ +package commands + +import ( + "context" + "errors" + "fmt" + "os" + "reflect" + "strings" + + "github.com/hashicorp/go-multierror" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + + "github.com/anchore/clio" + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/syft/cmd/syft/internal/options" + "github.com/anchore/syft/cmd/syft/internal/ui" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +const ( + scanExample = ` {{.appName}} {{.command}} alpine:latest a summary of discovered packages + {{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details + {{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM + {{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM + {{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.3 Tag-Value formatted SBOM + {{.appName}} {{.command}} alpine:latest -o spdx@2.2 show a SPDX 2.2 Tag-Value formatted SBOM + {{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.3 JSON formatted SBOM + {{.appName}} {{.command}} alpine:latest -o spdx-json@2.2 show a SPDX 2.2 JSON formatted SBOM + {{.appName}} {{.command}} alpine:latest -vv show verbose debug information + {{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file + + Supports the following image sources: + {{.appName}} {{.command}} yourrepo/yourimage:tag defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry. + {{.appName}} {{.command}} path/to/a/file/or/dir a Docker tar, OCI tar, OCI directory, SIF container, or generic filesystem directory +` + + schemeHelpHeader = "You can also explicitly specify the scheme to use:" + imageSchemeHelp = ` {{.appName}} {{.command}} docker:yourrepo/yourimage:tag explicitly use the Docker daemon + {{.appName}} {{.command}} podman:yourrepo/yourimage:tag explicitly use the Podman daemon + {{.appName}} {{.command}} registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required) + {{.appName}} {{.command}} docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save" + {{.appName}} {{.command}} oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise) + {{.appName}} {{.command}} oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise) + {{.appName}} {{.command}} singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk +` + nonImageSchemeHelp = ` {{.appName}} {{.command}} dir:path/to/yourproject read directly from a path on disk (any directory) + {{.appName}} {{.command}} file:path/to/yourproject/file read directly from a path on disk (any single file) +` + scanSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp + nonImageSchemeHelp + + scanHelp = scanExample + scanSchemeHelp +) + +type scanOptions struct { + options.Config `yaml:",inline" mapstructure:",squash"` + options.Output `yaml:",inline" mapstructure:",squash"` + options.UpdateCheck `yaml:",inline" mapstructure:",squash"` + options.Catalog `yaml:",inline" mapstructure:",squash"` +} + +func defaultScanOptions() *scanOptions { + return &scanOptions{ + Output: options.DefaultOutput(), + UpdateCheck: options.DefaultUpdateCheck(), + Catalog: options.DefaultCatalog(), + } +} + +//nolint:dupl +func Scan(app clio.Application) *cobra.Command { + id := app.ID() + + opts := defaultScanOptions() + + return app.SetupCommand(&cobra.Command{ + Use: "scan [SOURCE]", + Short: "Generate an SBOM", + Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems", + Example: internal.Tprintf(scanHelp, map[string]interface{}{ + "appName": id.Name, + "command": "scan", + }), + Args: validateScanArgs, + PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), + RunE: func(cmd *cobra.Command, args []string) error { + restoreStdout := ui.CaptureStdoutToTraceLog() + defer restoreStdout() + + return runScan(cmd.Context(), id, opts, args[0]) + }, + }, opts) +} + +func (o *scanOptions) PostLoad() error { + return o.validateLegacyOptionsNotUsed() +} + +func (o *scanOptions) validateLegacyOptionsNotUsed() error { + if o.Config.ConfigFile == "" { + return nil + } + + // check for legacy config file shapes that are no longer valid + type legacyConfig struct { + BasePath *string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` + DefaultImagePullSource *string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` + ExcludeBinaryOverlapByOwnership *bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` + File any `yaml:"file" json:"file" mapstructure:"file"` + } + + by, err := os.ReadFile(o.Config.ConfigFile) + if err != nil { + return fmt.Errorf("unable to read config file during validations %q: %w", o.Config.ConfigFile, err) + } + + var legacy legacyConfig + if err := yaml.Unmarshal(by, &legacy); err != nil { + return fmt.Errorf("unable to parse config file during validations %q: %w", o.Config.ConfigFile, err) + } + + if legacy.DefaultImagePullSource != nil { + return fmt.Errorf("the config file option 'default-image-pull-source' has been removed, please use 'source.image.default-pull-source' instead") + } + + if legacy.ExcludeBinaryOverlapByOwnership != nil { + return fmt.Errorf("the config file option 'exclude-binary-overlap-by-ownership' has been removed, please use 'package.exclude-binary-overlap-by-ownership' instead") + } + + if legacy.BasePath != nil { + return fmt.Errorf("the config file option 'base-path' has been removed, please use 'source.base-path' instead") + } + + if legacy.File != nil && reflect.TypeOf(legacy.File).Kind() == reflect.String { + return fmt.Errorf("the config file option 'file' has been removed, please use 'outputs' instead") + } + + return nil +} + +func validateScanArgs(cmd *cobra.Command, args []string) error { + return validateArgs(cmd, args, "an image/directory argument is required") +} + +func validateArgs(cmd *cobra.Command, args []string, error string) error { + if len(args) == 0 { + // in the case that no arguments are given we want to show the help text and return with a non-0 return code. + if err := cmd.Help(); err != nil { + return fmt.Errorf("unable to display help: %w", err) + } + return fmt.Errorf(error) + } + + return cobra.MaximumNArgs(1)(cmd, args) +} + +// nolint:funlen +func runScan(ctx context.Context, id clio.Identification, opts *scanOptions, userInput string) error { + writer, err := opts.SBOMWriter() + if err != nil { + return err + } + + src, err := getSource(&opts.Catalog, userInput) + + if err != nil { + return err + } + + defer func() { + if src != nil { + if err := src.Close(); err != nil { + log.Tracef("unable to close source: %+v", err) + } + } + }() + + s, err := generateSBOM(ctx, id, src, &opts.Catalog) + if err != nil { + return err + } + + if s == nil { + return fmt.Errorf("no SBOM produced for %q", userInput) + } + + if err := writer.Write(*s); err != nil { + return fmt.Errorf("failed to write SBOM: %w", err) + } + + return nil +} + +func getSource(opts *options.Catalog, userInput string, filters ...func(*source.Detection) error) (source.Source, error) { + detection, err := source.Detect( + userInput, + source.DetectConfig{ + DefaultImageSource: opts.Source.Image.DefaultPullSource, + }, + ) + if err != nil { + return nil, fmt.Errorf("could not deteremine source: %w", err) + } + + for _, filter := range filters { + if err := filter(detection); err != nil { + return nil, err + } + } + + var platform *image.Platform + + if opts.Platform != "" { + platform, err = image.NewPlatform(opts.Platform) + if err != nil { + return nil, fmt.Errorf("invalid platform: %w", err) + } + } + + hashers, err := file.Hashers(opts.Source.File.Digests...) + if err != nil { + return nil, fmt.Errorf("invalid hash algorithm: %w", err) + } + + src, err := detection.NewSource( + source.DetectionSourceConfig{ + Alias: source.Alias{ + Name: opts.Source.Name, + Version: opts.Source.Version, + }, + RegistryOptions: opts.Registry.ToOptions(), + Platform: platform, + Exclude: source.ExcludeConfig{ + Paths: opts.Exclusions, + }, + DigestAlgorithms: hashers, + BasePath: opts.Source.BasePath, + }, + ) + + if err != nil { + if userInput == "power-user" { + bus.Notify("Note: the 'power-user' command has been removed.") + } + return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) + } + + return src, nil +} + +func generateSBOM(ctx context.Context, id clio.Identification, src source.Source, opts *options.Catalog) (*sbom.SBOM, error) { + s, err := syft.CreateSBOM(ctx, src, opts.ToSBOMConfig(id)) + if err != nil { + expErrs := filterExpressionErrors(err) + notifyExpressionErrors(expErrs) + return nil, err + } + return s, nil +} + +func filterExpressionErrors(err error) []task.ErrInvalidExpression { + if err == nil { + return nil + } + + expErrs := processErrors(err) + + return expErrs +} + +// processErrors traverses error chains and multierror lists and returns all ErrInvalidExpression errors found +func processErrors(err error) []task.ErrInvalidExpression { + var result []task.ErrInvalidExpression + + var processError func(...error) + processError = func(errs ...error) { + for _, e := range errs { + // note: using errors.As will result in surprising behavior (since that will traverse the error chain, + // potentially skipping over nodes in a list of errors) + if cerr, ok := e.(task.ErrInvalidExpression); ok { + result = append(result, cerr) + continue + } + var multiErr *multierror.Error + if errors.As(e, &multiErr) { + processError(multiErr.Errors...) + } + } + } + + processError(err) + + return result +} + +func notifyExpressionErrors(expErrs []task.ErrInvalidExpression) { + helpText := expressionErrorsHelp(expErrs) + if helpText == "" { + return + } + + bus.Notify(helpText) +} + +func expressionErrorsHelp(expErrs []task.ErrInvalidExpression) string { + // enrich all errors found with CLI hints + if len(expErrs) == 0 { + return "" + } + + sb := strings.Builder{} + + sb.WriteString("Suggestions:\n\n") + + found := false + for i, expErr := range expErrs { + help := expressionSuggetions(expErr) + if help == "" { + continue + } + found = true + sb.WriteString(help) + if i != len(expErrs)-1 { + sb.WriteString("\n") + } + } + + if !found { + return "" + } + + return sb.String() +} + +const expressionHelpTemplate = " ❖ Given expression %q\n%s%s" + +func expressionSuggetions(expErr task.ErrInvalidExpression) string { + if expErr.Err == nil { + return "" + } + + hint := getHintPhrase(expErr) + if hint == "" { + return "" + } + + return fmt.Sprintf(expressionHelpTemplate, + getExpression(expErr), + indentMsg(getExplanation(expErr)), + indentMsg(hint), + ) +} + +func indentMsg(msg string) string { + if msg == "" { + return "" + } + + lines := strings.Split(msg, "\n") + for i, line := range lines { + lines[i] = " " + line + } + + return strings.Join(lines, "\n") + "\n" +} + +func getExpression(expErr task.ErrInvalidExpression) string { + flag := "--select-catalogers" + if expErr.Operation == task.SetOperation { + flag = "--override-default-catalogers" + } + return fmt.Sprintf("%s %s", flag, expErr.Expression) +} + +func getExplanation(expErr task.ErrInvalidExpression) string { + err := expErr.Err + if errors.Is(err, task.ErrUnknownNameOrTag) { + noun := "" + switch expErr.Operation { + case task.AddOperation: + noun = "name" + case task.SubSelectOperation: + noun = "tag" + default: + noun = "name or tag" + } + + return fmt.Sprintf("However, %q is not a recognized cataloger %s.", trimOperation(expErr.Expression), noun) + } + + if errors.Is(err, task.ErrNamesNotAllowed) { + if expErr.Operation == task.SubSelectOperation { + return "However, " + err.Error() + ".\nIt seems like you are intending to add a cataloger in addition to the default set." // nolint:goconst + } + return "However, " + err.Error() + "." // nolint:goconst + } + + if errors.Is(err, task.ErrTagsNotAllowed) { + return "However, " + err.Error() + ".\nAdding groups of catalogers may result in surprising behavior (create inaccurate SBOMs)." // nolint:goconst + } + + if errors.Is(err, task.ErrAllNotAllowed) { + return "However, you " + err.Error() + ".\nIt seems like you are intending to use all catalogers (which is not recommended)." + } + + if err != nil { + return "However, this is not valid: " + err.Error() + } + + return "" +} + +func getHintPhrase(expErr task.ErrInvalidExpression) string { + if errors.Is(expErr.Err, task.ErrUnknownNameOrTag) { + return "" + } + + switch expErr.Operation { + case task.AddOperation: + if errors.Is(expErr.Err, task.ErrTagsNotAllowed) { + return fmt.Sprintf("If you are certain this is what you want to do, use %q instead.", "--override-default-catalogers "+trimOperation(expErr.Expression)) + } + + case task.SubSelectOperation: + didYouMean := "... Did you mean %q instead?" + if errors.Is(expErr.Err, task.ErrNamesNotAllowed) { + return fmt.Sprintf(didYouMean, "--select-catalogers +"+expErr.Expression) + } + + if errors.Is(expErr.Err, task.ErrAllNotAllowed) { + return fmt.Sprintf(didYouMean, "--override-default-catalogers "+expErr.Expression) + } + } + return "" +} + +func trimOperation(x string) string { + return strings.TrimLeft(x, "+-") +} diff --git a/cmd/syft/internal/commands/scan_test.go b/cmd/syft/internal/commands/scan_test.go new file mode 100644 index 00000000000..63f22543fc8 --- /dev/null +++ b/cmd/syft/internal/commands/scan_test.go @@ -0,0 +1,66 @@ +package commands + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/cmd/syft/internal/options" +) + +func Test_scanOptions_validateLegacyOptionsNotUsed(t *testing.T) { + tests := []struct { + name string + cfg string + wantErr assert.ErrorAssertionFunc + }{ + { + name: "no config file", + }, + { + name: "config file with no legacy options", + cfg: "test-fixtures/scan-configs/no-legacy-options.yaml", + }, + { + name: "config file with default image pull source legacy option", + cfg: "test-fixtures/scan-configs/with-default-pull-source.yaml", + wantErr: assertErrorContains("source.image.default-pull-source"), + }, + { + name: "config file with exclude-binary-overlap-by-ownership legacy option", + cfg: "test-fixtures/scan-configs/with-exclude-binary-overlap-by-ownership.yaml", + wantErr: assertErrorContains("package.exclude-binary-overlap-by-ownership"), + }, + { + name: "config file with file string legacy option", + cfg: "test-fixtures/scan-configs/with-file-string.yaml", + wantErr: assertErrorContains("outputs"), + }, + { + name: "config file with file section", + cfg: "test-fixtures/scan-configs/with-file-section.yaml", + }, + { + name: "config file with base-path legacy option", + cfg: "test-fixtures/scan-configs/with-base-path.yaml", + wantErr: assertErrorContains("source.base-path"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = assert.NoError + } + o := &scanOptions{ + Config: options.Config{ConfigFile: tt.cfg}, + } + tt.wantErr(t, o.validateLegacyOptionsNotUsed()) + }) + } +} + +func assertErrorContains(contains string) assert.ErrorAssertionFunc { + return func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.ErrorContains(t, err, contains, i...) + } +} diff --git a/cmd/syft/internal/commands/test-fixtures/scan-configs/no-legacy-options.yaml b/cmd/syft/internal/commands/test-fixtures/scan-configs/no-legacy-options.yaml new file mode 100644 index 00000000000..51af565a245 --- /dev/null +++ b/cmd/syft/internal/commands/test-fixtures/scan-configs/no-legacy-options.yaml @@ -0,0 +1,2 @@ +# simple, valid syft config with no legacy fields used +parallelism: 5 diff --git a/cmd/syft/internal/commands/test-fixtures/scan-configs/with-base-path.yaml b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-base-path.yaml new file mode 100644 index 00000000000..5dc5db6bcaf --- /dev/null +++ b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-base-path.yaml @@ -0,0 +1 @@ +base-path: "something" \ No newline at end of file diff --git a/cmd/syft/internal/commands/test-fixtures/scan-configs/with-default-pull-source.yaml b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-default-pull-source.yaml new file mode 100644 index 00000000000..8a9e836342e --- /dev/null +++ b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-default-pull-source.yaml @@ -0,0 +1 @@ +default-image-pull-source: "something" \ No newline at end of file diff --git a/cmd/syft/internal/commands/test-fixtures/scan-configs/with-exclude-binary-overlap-by-ownership.yaml b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-exclude-binary-overlap-by-ownership.yaml new file mode 100644 index 00000000000..e0864a8785b --- /dev/null +++ b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-exclude-binary-overlap-by-ownership.yaml @@ -0,0 +1 @@ +exclude-binary-overlap-by-ownership: true \ No newline at end of file diff --git a/cmd/syft/internal/commands/test-fixtures/scan-configs/with-file-section.yaml b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-file-section.yaml new file mode 100644 index 00000000000..603e3aeca27 --- /dev/null +++ b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-file-section.yaml @@ -0,0 +1,2 @@ +file: + selection: all \ No newline at end of file diff --git a/cmd/syft/internal/commands/test-fixtures/scan-configs/with-file-string.yaml b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-file-string.yaml new file mode 100644 index 00000000000..804eed613da --- /dev/null +++ b/cmd/syft/internal/commands/test-fixtures/scan-configs/with-file-string.yaml @@ -0,0 +1 @@ +file: path \ No newline at end of file diff --git a/cmd/syft/cli/commands/update.go b/cmd/syft/internal/commands/update.go similarity index 96% rename from cmd/syft/cli/commands/update.go rename to cmd/syft/internal/commands/update.go index 88f15970035..27629ff499d 100644 --- a/cmd/syft/cli/commands/update.go +++ b/cmd/syft/internal/commands/update.go @@ -11,8 +11,8 @@ import ( "github.com/anchore/clio" hashiVersion "github.com/anchore/go-version" - "github.com/anchore/syft/cmd/syft/cli/options" "github.com/anchore/syft/cmd/syft/internal" + "github.com/anchore/syft/cmd/syft/internal/options" "github.com/anchore/syft/internal/bus" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/event" @@ -28,7 +28,7 @@ var latestAppVersionURL = struct { } func applicationUpdateCheck(id clio.Identification, check *options.UpdateCheck) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { + return func(_ *cobra.Command, _ []string) error { if check.CheckForAppUpdate { checkForApplicationUpdate(id) } diff --git a/cmd/syft/cli/commands/update_test.go b/cmd/syft/internal/commands/update_test.go similarity index 100% rename from cmd/syft/cli/commands/update_test.go rename to cmd/syft/internal/commands/update_test.go diff --git a/cmd/syft/cli/options/attest.go b/cmd/syft/internal/options/attest.go similarity index 83% rename from cmd/syft/cli/options/attest.go rename to cmd/syft/internal/options/attest.go index 8c0420386c7..7507ae36e27 100644 --- a/cmd/syft/cli/options/attest.go +++ b/cmd/syft/internal/options/attest.go @@ -4,6 +4,8 @@ import ( "github.com/anchore/clio" ) +var _ clio.FlagAdder = (*Attest)(nil) + type Attest struct { // IMPORTANT: do not show the attestation key/password in any YAML/JSON output (sensitive information) Key secret `yaml:"key" json:"key" mapstructure:"key"` @@ -12,6 +14,6 @@ type Attest struct { var _ clio.FlagAdder = (*Attest)(nil) -func (o Attest) AddFlags(flags clio.FlagSet) { +func (o *Attest) AddFlags(flags clio.FlagSet) { flags.StringVarP((*string)(&o.Key), "key", "k", "the key to use for the attestation") } diff --git a/cmd/syft/internal/options/catalog.go b/cmd/syft/internal/options/catalog.go new file mode 100644 index 00000000000..f12ea41cc5d --- /dev/null +++ b/cmd/syft/internal/options/catalog.go @@ -0,0 +1,236 @@ +package options + +import ( + "fmt" + "sort" + "strings" + + "github.com/iancoleman/strcase" + + "github.com/anchore/clio" + "github.com/anchore/fangs" + intFile "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/cataloging/filecataloging" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/file/cataloger/executable" + "github.com/anchore/syft/syft/file/cataloger/filecontent" + "github.com/anchore/syft/syft/pkg/cataloger/binary" + "github.com/anchore/syft/syft/pkg/cataloger/golang" + "github.com/anchore/syft/syft/pkg/cataloger/java" + "github.com/anchore/syft/syft/pkg/cataloger/javascript" + "github.com/anchore/syft/syft/pkg/cataloger/kernel" + "github.com/anchore/syft/syft/pkg/cataloger/python" + "github.com/anchore/syft/syft/source" +) + +type Catalog struct { + // high-level cataloger configuration + Catalogers []string `yaml:"-" json:"catalogers" mapstructure:"catalogers"` // deprecated and not shown in yaml output + DefaultCatalogers []string `yaml:"default-catalogers" json:"default-catalogers" mapstructure:"default-catalogers"` + SelectCatalogers []string `yaml:"select-catalogers" json:"select-catalogers" mapstructure:"select-catalogers"` + Package packageConfig `yaml:"package" json:"package" mapstructure:"package"` + File fileConfig `yaml:"file" json:"file" mapstructure:"file"` + Scope string `yaml:"scope" json:"scope" mapstructure:"scope"` + Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel + Relationships relationshipsConfig `yaml:"relationships" json:"relationships" mapstructure:"relationships"` + + // ecosystem-specific cataloger configuration + Golang golangConfig `yaml:"golang" json:"golang" mapstructure:"golang"` + Java javaConfig `yaml:"java" json:"java" mapstructure:"java"` + JavaScript javaScriptConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"` + LinuxKernel linuxKernelConfig `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"` + Python pythonConfig `yaml:"python" json:"python" mapstructure:"python"` + + // configuration for the source (the subject being analyzed) + Registry registryConfig `yaml:"registry" json:"registry" mapstructure:"registry"` + Platform string `yaml:"platform" json:"platform" mapstructure:"platform"` + Source sourceConfig `yaml:"source" json:"source" mapstructure:"source"` + Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"` +} + +var _ interface { + clio.FlagAdder + clio.PostLoader + fangs.FieldDescriber +} = (*Catalog)(nil) + +func DefaultCatalog() Catalog { + return Catalog{ + Scope: source.SquashedScope.String(), + Package: defaultPackageConfig(), + LinuxKernel: defaultLinuxKernelConfig(), + Golang: defaultGolangConfig(), + File: defaultFileConfig(), + Relationships: defaultRelationshipsConfig(), + Source: defaultSourceConfig(), + Parallelism: 1, + } +} + +func (cfg Catalog) ToSBOMConfig(id clio.Identification) *syft.CreateSBOMConfig { + return syft.DefaultCreateSBOMConfig(). + WithTool(id.Name, id.Version). + WithParallelism(cfg.Parallelism). + WithRelationshipsConfig(cfg.ToRelationshipsConfig()). + WithSearchConfig(cfg.ToSearchConfig()). + WithPackagesConfig(cfg.ToPackagesConfig()). + WithFilesConfig(cfg.ToFilesConfig()). + WithCatalogerSelection( + pkgcataloging.NewSelectionRequest(). + WithDefaults(cfg.DefaultCatalogers...). + WithExpression(cfg.SelectCatalogers...), + ) +} + +func (cfg Catalog) ToSearchConfig() cataloging.SearchConfig { + return cataloging.SearchConfig{ + Scope: source.ParseScope(cfg.Scope), + } +} + +func (cfg Catalog) ToRelationshipsConfig() cataloging.RelationshipsConfig { + return cataloging.RelationshipsConfig{ + PackageFileOwnership: cfg.Relationships.PackageFileOwnership, + PackageFileOwnershipOverlap: cfg.Relationships.PackageFileOwnershipOverlap, + // note: this option was surfaced in the syft application configuration before this relationships section was added + ExcludeBinaryPackagesWithFileOwnershipOverlap: cfg.Package.ExcludeBinaryOverlapByOwnership, + } +} + +func (cfg Catalog) ToFilesConfig() filecataloging.Config { + hashers, err := intFile.Hashers(cfg.File.Metadata.Digests...) + if err != nil { + log.WithFields("error", err).Warn("unable to configure file hashers") + } + + return filecataloging.Config{ + Selection: cfg.File.Metadata.Selection, + Hashers: hashers, + Content: filecontent.Config{ + Globs: cfg.File.Content.Globs, + SkipFilesAboveSize: cfg.File.Content.SkipFilesAboveSize, + }, + Executable: executable.Config{ + MIMETypes: executable.DefaultConfig().MIMETypes, + Globs: cfg.File.Executable.Globs, + }, + } +} + +func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config { + archiveSearch := cataloging.ArchiveSearchConfig{ + IncludeIndexedArchives: cfg.Package.SearchIndexedArchives, + IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives, + } + return pkgcataloging.Config{ + Binary: binary.DefaultClassifierCatalogerConfig(), + Golang: golang.DefaultCatalogerConfig(). + WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses). + WithLocalModCacheDir(cfg.Golang.LocalModCacheDir). + WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses). + WithProxy(cfg.Golang.Proxy). + WithNoProxy(cfg.Golang.NoProxy). + WithMainModuleVersion( + golang.DefaultMainModuleVersionConfig(). + WithFromContents(cfg.Golang.MainModuleVersion.FromContents). + WithFromBuildSettings(cfg.Golang.MainModuleVersion.FromBuildSettings). + WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags), + ), + JavaScript: javascript.DefaultCatalogerConfig(). + WithSearchRemoteLicenses(cfg.JavaScript.SearchRemoteLicenses). + WithNpmBaseURL(cfg.JavaScript.NpmBaseURL), + LinuxKernel: kernel.LinuxKernelCatalogerConfig{ + CatalogModules: cfg.LinuxKernel.CatalogModules, + }, + Python: python.CatalogerConfig{ + GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements, + }, + JavaArchive: java.DefaultArchiveCatalogerConfig(). + WithUseNetwork(cfg.Java.UseNetwork). + WithMavenBaseURL(cfg.Java.MavenURL). + WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth), + } +} + +func (cfg *Catalog) AddFlags(flags clio.FlagSet) { + var validScopeValues []string + for _, scope := range source.AllScopes { + validScopeValues = append(validScopeValues, strcase.ToDelimited(string(scope), '-')) + } + flags.StringVarP(&cfg.Scope, "scope", "s", + fmt.Sprintf("selection of layers to catalog, options=%v", validScopeValues)) + + flags.StringVarP(&cfg.Platform, "platform", "", + "an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')") + + flags.StringArrayVarP(&cfg.Exclusions, "exclude", "", + "exclude paths from being scanned using a glob expression") + + flags.StringArrayVarP(&cfg.Catalogers, "catalogers", "", + "enable one or more package catalogers") + + if pfp, ok := flags.(fangs.PFlagSetProvider); ok { + if err := pfp.PFlagSet().MarkDeprecated("catalogers", "use: override-default-catalogers and select-catalogers"); err != nil { + panic(err) + } + } else { + panic("unable to mark flags as deprecated") + } + + flags.StringArrayVarP(&cfg.DefaultCatalogers, "override-default-catalogers", "", + "set the base set of catalogers to use (defaults to 'image' or 'directory' depending on the scan source)") + + flags.StringArrayVarP(&cfg.SelectCatalogers, "select-catalogers", "", + "add, remove, and filter the catalogers to be used") + + flags.StringVarP(&cfg.Source.Name, "source-name", "", + "set the name of the target being analyzed") + + flags.StringVarP(&cfg.Source.Version, "source-version", "", + "set the version of the target being analyzed") + + flags.StringVarP(&cfg.Source.BasePath, "base-path", "", + "base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory") +} + +func (cfg *Catalog) DescribeFields(descriptions fangs.FieldDescriptionSet) { + descriptions.Add(&cfg.Parallelism, "number of cataloger workers to run in parallel") +} + +func (cfg *Catalog) PostLoad() error { + usingLegacyCatalogers := len(cfg.Catalogers) > 0 + usingNewCatalogers := len(cfg.DefaultCatalogers) > 0 || len(cfg.SelectCatalogers) > 0 + + if usingLegacyCatalogers && usingNewCatalogers { + return fmt.Errorf("cannot use both 'catalogers' and 'select-catalogers'/'default-catalogers' flags") + } + + flatten := func(l []string) []string { + var out []string + for _, v := range l { + for _, s := range strings.Split(v, ",") { + out = append(out, strings.TrimSpace(s)) + } + } + sort.Strings(out) + + return out + } + + cfg.Catalogers = flatten(cfg.Catalogers) + cfg.DefaultCatalogers = flatten(cfg.DefaultCatalogers) + cfg.SelectCatalogers = flatten(cfg.SelectCatalogers) + + // for backwards compatibility + cfg.DefaultCatalogers = append(cfg.DefaultCatalogers, cfg.Catalogers...) + + s := source.ParseScope(cfg.Scope) + if s == source.UnknownScope { + return fmt.Errorf("bad scope value %q", cfg.Scope) + } + + return nil +} diff --git a/cmd/syft/internal/options/catalog_test.go b/cmd/syft/internal/options/catalog_test.go new file mode 100644 index 00000000000..f9a54249ae3 --- /dev/null +++ b/cmd/syft/internal/options/catalog_test.go @@ -0,0 +1,72 @@ +package options + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCatalog_PostLoad(t *testing.T) { + + tests := []struct { + name string + options Catalog + assert func(t *testing.T, options Catalog) + wantErr assert.ErrorAssertionFunc + }{ + { + name: "mutually exclusive cataloger flags (cat / def-cat)", + options: Catalog{ + Catalogers: []string{"foo,bar", "42"}, + DefaultCatalogers: []string{"some,thing"}, + Scope: "squashed", + }, + wantErr: assert.Error, + }, + { + name: "mutually exclusive cataloger flags (cat / sel-cat)", + options: Catalog{ + Catalogers: []string{"foo,bar", "42"}, + SelectCatalogers: []string{"some,thing"}, + Scope: "squashed", + }, + wantErr: assert.Error, + }, + { + name: "allow old cataloger flags", + options: Catalog{ + Catalogers: []string{"foo,bar"}, + Scope: "squashed", + }, + assert: func(t *testing.T, options Catalog) { + assert.Equal(t, []string{"bar", "foo"}, options.DefaultCatalogers) // note: sorted order + assert.Equal(t, []string{"bar", "foo"}, options.Catalogers) // note: sorted order + }, + }, + { + name: "allow new cataloger flags", + options: Catalog{ + SelectCatalogers: []string{"foo,bar", "42"}, + DefaultCatalogers: []string{"some,thing"}, + Scope: "squashed", + }, + assert: func(t *testing.T, options Catalog) { + assert.Equal(t, []string{"42", "bar", "foo"}, options.SelectCatalogers) // note: sorted order + assert.Equal(t, []string{"some", "thing"}, options.DefaultCatalogers) // note: sorted order + assert.Empty(t, options.Catalogers) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = assert.NoError + } + tt.wantErr(t, tt.options.PostLoad(), fmt.Sprintf("PostLoad()")) + if tt.assert != nil { + tt.assert(t, tt.options) + } + }) + } +} diff --git a/cmd/syft/internal/options/config.go b/cmd/syft/internal/options/config.go new file mode 100644 index 00000000000..25158ea75b2 --- /dev/null +++ b/cmd/syft/internal/options/config.go @@ -0,0 +1,12 @@ +package options + +import "github.com/anchore/fangs" + +// Config holds a reference to the specific config file that was used to load application configuration +type Config struct { + ConfigFile string `yaml:"config" json:"config" mapstructure:"config"` +} + +func (cfg *Config) DescribeFields(descriptions fangs.FieldDescriptionSet) { + descriptions.Add(&cfg.ConfigFile, "the configuration file that was used to load application configuration") +} diff --git a/cmd/syft/internal/options/file.go b/cmd/syft/internal/options/file.go new file mode 100644 index 00000000000..ae628f07801 --- /dev/null +++ b/cmd/syft/internal/options/file.go @@ -0,0 +1,58 @@ +package options + +import ( + "fmt" + "sort" + + "github.com/scylladb/go-set/strset" + + intFile "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/syft/file" +) + +type fileConfig struct { + Metadata fileMetadata `yaml:"metadata" json:"metadata" mapstructure:"metadata"` + Content fileContent `yaml:"content" json:"content" mapstructure:"content"` + Executable fileExecutable `yaml:"executable" json:"executable" mapstructure:"executable"` +} + +type fileMetadata struct { + Selection file.Selection `yaml:"selection" json:"selection" mapstructure:"selection"` + Digests []string `yaml:"digests" json:"digests" mapstructure:"digests"` +} + +type fileContent struct { + SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"` + Globs []string `yaml:"globs" json:"globs" mapstructure:"globs"` +} + +type fileExecutable struct { + Globs []string `yaml:"globs" json:"globs" mapstructure:"globs"` +} + +func defaultFileConfig() fileConfig { + return fileConfig{ + Metadata: fileMetadata{ + Selection: file.FilesOwnedByPackageSelection, + Digests: []string{"sha1", "sha256"}, + }, + Content: fileContent{ + SkipFilesAboveSize: 250 * intFile.KB, + }, + Executable: fileExecutable{ + Globs: nil, + }, + } +} + +func (c *fileConfig) PostLoad() error { + digests := strset.New(c.Metadata.Digests...).List() + sort.Strings(digests) + c.Metadata.Digests = digests + + switch c.Metadata.Selection { + case file.NoFilesSelection, file.FilesOwnedByPackageSelection, file.AllFilesSelection: + return nil + } + return fmt.Errorf("invalid file metadata selection: %q", c.Metadata.Selection) +} diff --git a/cmd/syft/internal/options/file_test.go b/cmd/syft/internal/options/file_test.go new file mode 100644 index 00000000000..2e61a59e87b --- /dev/null +++ b/cmd/syft/internal/options/file_test.go @@ -0,0 +1,56 @@ +package options + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/file" +) + +func Test_fileConfig_PostLoad(t *testing.T) { + tests := []struct { + name string + cfg fileConfig + assert func(t *testing.T, cfg fileConfig) + wantErr assert.ErrorAssertionFunc + }{ + { + name: "deduplicate digests", + cfg: fileConfig{ + Metadata: fileMetadata{ + Selection: file.NoFilesSelection, + Digests: []string{"sha1", "sha1"}, + }, + }, + assert: func(t *testing.T, cfg fileConfig) { + assert.Equal(t, []string{"sha1"}, cfg.Metadata.Digests) + }, + }, + { + name: "error on invalid selection", + cfg: fileConfig{ + Metadata: fileMetadata{ + Selection: file.Selection("invalid"), + }, + }, + wantErr: assert.Error, + }, + { + name: "error on empty selection", + cfg: fileConfig{}, + wantErr: assert.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = assert.NoError + } + tt.wantErr(t, tt.cfg.PostLoad()) + if tt.assert != nil { + tt.assert(t, tt.cfg) + } + }) + } +} diff --git a/cmd/syft/internal/options/format.go b/cmd/syft/internal/options/format.go new file mode 100644 index 00000000000..e5ab65bb43c --- /dev/null +++ b/cmd/syft/internal/options/format.go @@ -0,0 +1,58 @@ +package options + +import ( + "github.com/anchore/clio" + "github.com/anchore/syft/syft/format" + "github.com/anchore/syft/syft/sbom" +) + +var _ clio.PostLoader = (*Format)(nil) + +// Format contains all user configuration for output formatting. +type Format struct { + Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` + Template FormatTemplate `yaml:"template" json:"template" mapstructure:"template"` + SyftJSON FormatSyftJSON `yaml:"json" json:"json" mapstructure:"json"` + SPDXJSON FormatSPDXJSON `yaml:"spdx-json" json:"spdx-json" mapstructure:"spdx-json"` + CyclonedxJSON FormatCyclonedxJSON `yaml:"cyclonedx-json" json:"cyclonedx-json" mapstructure:"cyclonedx-json"` + CyclonedxXML FormatCyclonedxXML `yaml:"cyclonedx-xml" json:"cyclonedx-xml" mapstructure:"cyclonedx-xml"` +} + +func (o *Format) PostLoad() error { + o.SyftJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SyftJSON.Pretty) + o.SPDXJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SPDXJSON.Pretty) + o.CyclonedxJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxJSON.Pretty) + o.CyclonedxXML.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxXML.Pretty) + + return nil +} + +func DefaultFormat() Format { + return Format{ + Template: DefaultFormatTemplate(), + SyftJSON: DefaultFormatJSON(), + SPDXJSON: DefaultFormatSPDXJSON(), + CyclonedxJSON: DefaultFormatCyclonedxJSON(), + CyclonedxXML: DefaultFormatCyclonedxXML(), + } +} + +func (o Format) Encoders() ([]sbom.FormatEncoder, error) { + return format.EncodersConfig{ + Template: o.Template.config(), + SyftJSON: o.SyftJSON.config(), + SPDXJSON: o.SPDXJSON.config(format.AllVersions), // we support multiple versions, not just a single version + CyclonedxJSON: o.CyclonedxJSON.config(format.AllVersions), // we support multiple versions, not just a single version + CyclonedxXML: o.CyclonedxXML.config(format.AllVersions), // we support multiple versions, not just a single version + }.Encoders() +} + +func multiLevelOption[T any](defaultValue T, option ...*T) *T { + result := defaultValue + for _, opt := range option { + if opt != nil { + result = *opt + } + } + return &result +} diff --git a/cmd/syft/internal/options/format_cyclonedx_json.go b/cmd/syft/internal/options/format_cyclonedx_json.go new file mode 100644 index 00000000000..eb65264ca9b --- /dev/null +++ b/cmd/syft/internal/options/format_cyclonedx_json.go @@ -0,0 +1,24 @@ +package options + +import ( + "github.com/anchore/syft/syft/format/cyclonedxjson" +) + +type FormatCyclonedxJSON struct { + Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` +} + +func DefaultFormatCyclonedxJSON() FormatCyclonedxJSON { + return FormatCyclonedxJSON{} +} + +func (o FormatCyclonedxJSON) config(version string) cyclonedxjson.EncoderConfig { + var pretty bool + if o.Pretty != nil { + pretty = *o.Pretty + } + return cyclonedxjson.EncoderConfig{ + Version: version, + Pretty: pretty, + } +} diff --git a/cmd/syft/cli/options/format_cyclonedx_json_test.go b/cmd/syft/internal/options/format_cyclonedx_json_test.go similarity index 97% rename from cmd/syft/cli/options/format_cyclonedx_json_test.go rename to cmd/syft/internal/options/format_cyclonedx_json_test.go index 53b7a6adb4b..af43574fb97 100644 --- a/cmd/syft/cli/options/format_cyclonedx_json_test.go +++ b/cmd/syft/internal/options/format_cyclonedx_json_test.go @@ -13,7 +13,7 @@ func TestFormatCyclonedxJSON_buildConfig(t *testing.T) { ft := &FormatCyclonedxJSON{} ft = setAllToNonZero(t, ft).(*FormatCyclonedxJSON) - subject := ft.buildConfig("Version") + subject := ft.config("Version") assertExpectedValue(t, subject) } diff --git a/cmd/syft/internal/options/format_cyclonedx_xml.go b/cmd/syft/internal/options/format_cyclonedx_xml.go new file mode 100644 index 00000000000..3b757775802 --- /dev/null +++ b/cmd/syft/internal/options/format_cyclonedx_xml.go @@ -0,0 +1,24 @@ +package options + +import ( + "github.com/anchore/syft/syft/format/cyclonedxxml" +) + +type FormatCyclonedxXML struct { + Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` +} + +func DefaultFormatCyclonedxXML() FormatCyclonedxXML { + return FormatCyclonedxXML{} +} + +func (o FormatCyclonedxXML) config(version string) cyclonedxxml.EncoderConfig { + var pretty bool + if o.Pretty != nil { + pretty = *o.Pretty + } + return cyclonedxxml.EncoderConfig{ + Version: version, + Pretty: pretty, + } +} diff --git a/cmd/syft/cli/options/format_cyclonedx_xml_test.go b/cmd/syft/internal/options/format_cyclonedx_xml_test.go similarity index 88% rename from cmd/syft/cli/options/format_cyclonedx_xml_test.go rename to cmd/syft/internal/options/format_cyclonedx_xml_test.go index f1b4c656f8c..31ddcabbbe8 100644 --- a/cmd/syft/cli/options/format_cyclonedx_xml_test.go +++ b/cmd/syft/internal/options/format_cyclonedx_xml_test.go @@ -10,6 +10,6 @@ func TestFormatCyclonedxXML_buildConfig(t *testing.T) { ft := FormatCyclonedxXML{} ftp := setAllToNonZero(t, &ft).(*FormatCyclonedxXML) - subject := ftp.buildConfig("Version") + subject := ftp.config("Version") assertExpectedValue(t, subject) } diff --git a/cmd/syft/internal/options/format_spdx_json.go b/cmd/syft/internal/options/format_spdx_json.go new file mode 100644 index 00000000000..efdccb230af --- /dev/null +++ b/cmd/syft/internal/options/format_spdx_json.go @@ -0,0 +1,24 @@ +package options + +import ( + "github.com/anchore/syft/syft/format/spdxjson" +) + +type FormatSPDXJSON struct { + Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` +} + +func DefaultFormatSPDXJSON() FormatSPDXJSON { + return FormatSPDXJSON{} +} + +func (o FormatSPDXJSON) config(v string) spdxjson.EncoderConfig { + var pretty bool + if o.Pretty != nil { + pretty = *o.Pretty + } + return spdxjson.EncoderConfig{ + Version: v, + Pretty: pretty, + } +} diff --git a/cmd/syft/cli/options/format_spdx_json_test.go b/cmd/syft/internal/options/format_spdx_json_test.go similarity index 87% rename from cmd/syft/cli/options/format_spdx_json_test.go rename to cmd/syft/internal/options/format_spdx_json_test.go index 8cc390d05e9..b81ef04c7db 100644 --- a/cmd/syft/cli/options/format_spdx_json_test.go +++ b/cmd/syft/internal/options/format_spdx_json_test.go @@ -10,6 +10,6 @@ func TestFormatSPDXJSON_buildConfig(t *testing.T) { ft := &FormatSPDXJSON{} ft = setAllToNonZero(t, ft).(*FormatSPDXJSON) - subject := ft.buildConfig("Version") + subject := ft.config("Version") assertExpectedValue(t, subject) } diff --git a/cmd/syft/cli/options/format_syft_json.go b/cmd/syft/internal/options/format_syft_json.go similarity index 62% rename from cmd/syft/cli/options/format_syft_json.go rename to cmd/syft/internal/options/format_syft_json.go index a2765ba8783..85cb193ddaa 100644 --- a/cmd/syft/cli/options/format_syft_json.go +++ b/cmd/syft/internal/options/format_syft_json.go @@ -2,7 +2,6 @@ package options import ( "github.com/anchore/syft/syft/format/syftjson" - "github.com/anchore/syft/syft/sbom" ) type FormatSyftJSON struct { @@ -16,12 +15,7 @@ func DefaultFormatJSON() FormatSyftJSON { } } -func (o FormatSyftJSON) formatEncoders() ([]sbom.FormatEncoder, error) { - enc, err := syftjson.NewFormatEncoderWithConfig(o.buildConfig()) - return []sbom.FormatEncoder{enc}, err -} - -func (o FormatSyftJSON) buildConfig() syftjson.EncoderConfig { +func (o FormatSyftJSON) config() syftjson.EncoderConfig { var pretty bool if o.Pretty != nil { pretty = *o.Pretty diff --git a/cmd/syft/cli/options/format_syft_json_test.go b/cmd/syft/internal/options/format_syft_json_test.go similarity index 90% rename from cmd/syft/cli/options/format_syft_json_test.go rename to cmd/syft/internal/options/format_syft_json_test.go index 94fc1d07d19..2df7d43b269 100644 --- a/cmd/syft/cli/options/format_syft_json_test.go +++ b/cmd/syft/internal/options/format_syft_json_test.go @@ -10,6 +10,6 @@ func TestFormatSyftJSON_buildConfig(t *testing.T) { ft := &FormatSyftJSON{} ft = setAllToNonZero(t, ft).(*FormatSyftJSON) - subject := ft.buildConfig() + subject := ft.config() assertExpectedValue(t, subject) } diff --git a/cmd/syft/cli/options/format_template.go b/cmd/syft/internal/options/format_template.go similarity index 71% rename from cmd/syft/cli/options/format_template.go rename to cmd/syft/internal/options/format_template.go index 724e72891b6..9987090893c 100644 --- a/cmd/syft/cli/options/format_template.go +++ b/cmd/syft/internal/options/format_template.go @@ -3,7 +3,6 @@ package options import ( "github.com/anchore/clio" "github.com/anchore/syft/syft/format/template" - "github.com/anchore/syft/syft/sbom" ) var _ clio.FlagAdder = (*FormatTemplate)(nil) @@ -11,6 +10,7 @@ var _ clio.FlagAdder = (*FormatTemplate)(nil) type FormatTemplate struct { Enabled bool `yaml:"-" json:"-" mapstructure:"-"` Path string `yaml:"path" json:"path" mapstructure:"path"` // -t template file to use for output + Legacy bool `yaml:"legacy" json:"legacy" mapstructure:"legacy"` } func DefaultFormatTemplate() FormatTemplate { @@ -26,12 +26,9 @@ func (o *FormatTemplate) AddFlags(flags clio.FlagSet) { } } -func (o FormatTemplate) formatEncoders() ([]sbom.FormatEncoder, error) { - if !o.Enabled { - return nil, nil - } - enc, err := template.NewFormatEncoder(template.EncoderConfig{ +func (o FormatTemplate) config() template.EncoderConfig { + return template.EncoderConfig{ TemplatePath: o.Path, - }) - return []sbom.FormatEncoder{enc}, err + Legacy: o.Legacy, + } } diff --git a/cmd/syft/internal/options/golang.go b/cmd/syft/internal/options/golang.go new file mode 100644 index 00000000000..a4121eda08a --- /dev/null +++ b/cmd/syft/internal/options/golang.go @@ -0,0 +1,38 @@ +package options + +import ( + "strings" + + "github.com/anchore/syft/syft/pkg/cataloger/golang" +) + +type golangConfig struct { + SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"` + LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"` + SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"` + Proxy string `json:"proxy" yaml:"proxy" mapstructure:"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"` +} + +type golangMainModuleVersionConfig struct { + FromLDFlags bool `json:"from-ld-flags" yaml:"from-ld-flags" mapstructure:"from-ld-flags"` + FromContents bool `json:"from-contents" yaml:"from-contents" mapstructure:"from-contents"` + FromBuildSettings bool `json:"from-build-settings" yaml:"from-build-settings" mapstructure:"from-build-settings"` +} + +func defaultGolangConfig() golangConfig { + def := golang.DefaultCatalogerConfig() + return golangConfig{ + SearchLocalModCacheLicenses: def.SearchLocalModCacheLicenses, + LocalModCacheDir: def.LocalModCacheDir, + SearchRemoteLicenses: def.SearchRemoteLicenses, + Proxy: strings.Join(def.Proxies, ","), + NoProxy: strings.Join(def.NoProxy, ","), + MainModuleVersion: golangMainModuleVersionConfig{ + FromLDFlags: def.MainModuleVersion.FromLDFlags, + FromContents: def.MainModuleVersion.FromContents, + FromBuildSettings: def.MainModuleVersion.FromBuildSettings, + }, + } +} diff --git a/cmd/syft/cli/options/java.go b/cmd/syft/internal/options/java.go similarity index 93% rename from cmd/syft/cli/options/java.go rename to cmd/syft/internal/options/java.go index 7b7b9fd2506..342954068ee 100644 --- a/cmd/syft/cli/options/java.go +++ b/cmd/syft/internal/options/java.go @@ -1,6 +1,6 @@ package options -type java struct { +type javaConfig struct { UseNetwork bool `yaml:"use-network" json:"use-network" mapstructure:"use-network"` MavenURL string `yaml:"maven-url" json:"maven-url" mapstructure:"maven-url"` MaxParentRecursiveDepth int `yaml:"max-parent-recursive-depth" json:"max-parent-recursive-depth" mapstructure:"max-parent-recursive-depth"` diff --git a/cmd/syft/internal/options/javascript.go b/cmd/syft/internal/options/javascript.go new file mode 100644 index 00000000000..353b190036c --- /dev/null +++ b/cmd/syft/internal/options/javascript.go @@ -0,0 +1,6 @@ +package options + +type javaScriptConfig struct { + SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"` + NpmBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"` +} diff --git a/cmd/syft/cli/options/lib_scribe.go b/cmd/syft/internal/options/lib_scribe.go similarity index 100% rename from cmd/syft/cli/options/lib_scribe.go rename to cmd/syft/internal/options/lib_scribe.go diff --git a/cmd/syft/cli/options/linux_kernel.go b/cmd/syft/internal/options/linux_kernel.go similarity index 57% rename from cmd/syft/cli/options/linux_kernel.go rename to cmd/syft/internal/options/linux_kernel.go index c56466abf81..ac6adff2adc 100644 --- a/cmd/syft/cli/options/linux_kernel.go +++ b/cmd/syft/internal/options/linux_kernel.go @@ -1,11 +1,11 @@ package options -type linuxKernel struct { +type linuxKernelConfig struct { CatalogModules bool `json:"catalog-modules" yaml:"catalog-modules" mapstructure:"catalog-modules"` } -func defaultLinuxKernel() linuxKernel { - return linuxKernel{ +func defaultLinuxKernelConfig() linuxKernelConfig { + return linuxKernelConfig{ CatalogModules: true, } } diff --git a/cmd/syft/cli/options/output.go b/cmd/syft/internal/options/output.go similarity index 98% rename from cmd/syft/cli/options/output.go rename to cmd/syft/internal/options/output.go index 82431c6a632..961698d1cc8 100644 --- a/cmd/syft/cli/options/output.go +++ b/cmd/syft/internal/options/output.go @@ -96,7 +96,7 @@ func (o Output) SBOMWriter() (sbom.Writer, error) { } } - return makeSBOMWriter(o.Outputs, o.File, encoders) + return makeSBOMWriter(o.Outputs, o.LegacyFile, encoders) } func (o Output) OutputNameSet() *strset.Set { diff --git a/cmd/syft/cli/options/output_file.go b/cmd/syft/internal/options/output_file.go similarity index 62% rename from cmd/syft/cli/options/output_file.go rename to cmd/syft/internal/options/output_file.go index b23fb91e13a..cf8e5632b96 100644 --- a/cmd/syft/cli/options/output_file.go +++ b/cmd/syft/internal/options/output_file.go @@ -1,6 +1,10 @@ package options import ( + "fmt" + + "github.com/mitchellh/go-homedir" + "github.com/anchore/clio" "github.com/anchore/fangs" "github.com/anchore/syft/syft/sbom" @@ -13,13 +17,13 @@ var _ interface { // Deprecated: OutputFile supports the --file to write the SBOM output to type OutputFile struct { - Enabled bool `yaml:"-" json:"-" mapstructure:"-"` - File string `yaml:"file" json:"file" mapstructure:"file"` + Enabled bool `yaml:"-" json:"-" mapstructure:"-"` + LegacyFile string `yaml:"-" json:"-" mapstructure:"legacyFile"` } func (o *OutputFile) AddFlags(flags clio.FlagSet) { if o.Enabled { - flags.StringVarP(&o.File, "file", "", + flags.StringVarP(&o.LegacyFile, "file", "", "file to write the default report output to (default is STDOUT)") if pfp, ok := flags.(fangs.PFlagSetProvider); ok { @@ -33,12 +37,12 @@ func (o *OutputFile) PostLoad() error { if !o.Enabled { return nil } - if o.File != "" { - file, err := expandFilePath(o.File) + if o.LegacyFile != "" { + file, err := expandFilePath(o.LegacyFile) if err != nil { return err } - o.File = file + o.LegacyFile = file } return nil } @@ -47,10 +51,21 @@ func (o *OutputFile) SBOMWriter(f sbom.FormatEncoder) (sbom.Writer, error) { if !o.Enabled { return nil, nil } - writer, err := newSBOMMultiWriter(newSBOMWriterDescription(f, o.File)) + writer, err := newSBOMMultiWriter(newSBOMWriterDescription(f, o.LegacyFile)) if err != nil { return nil, err } return writer, nil } + +func expandFilePath(file string) (string, error) { + if file != "" { + expandedPath, err := homedir.Expand(file) + if err != nil { + return "", fmt.Errorf("unable to expand file path=%q: %w", file, err) + } + file = expandedPath + } + return file, nil +} diff --git a/cmd/syft/cli/options/output_test.go b/cmd/syft/internal/options/output_test.go similarity index 100% rename from cmd/syft/cli/options/output_test.go rename to cmd/syft/internal/options/output_test.go diff --git a/cmd/syft/internal/options/pkg.go b/cmd/syft/internal/options/pkg.go new file mode 100644 index 00000000000..e384e568079 --- /dev/null +++ b/cmd/syft/internal/options/pkg.go @@ -0,0 +1,18 @@ +package options + +import "github.com/anchore/syft/syft/cataloging" + +type packageConfig struct { + SearchUnindexedArchives bool `yaml:"search-unindexed-archives" json:"search-unindexed-archives" mapstructure:"search-unindexed-archives"` + SearchIndexedArchives bool `yaml:"search-indexed-archives" json:"search-indexed-archives" mapstructure:"search-indexed-archives"` + ExcludeBinaryOverlapByOwnership bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` // exclude synthetic binary packages owned by os package files +} + +func defaultPackageConfig() packageConfig { + c := cataloging.DefaultArchiveSearchConfig() + return packageConfig{ + SearchIndexedArchives: c.IncludeIndexedArchives, + SearchUnindexedArchives: c.IncludeUnindexedArchives, + ExcludeBinaryOverlapByOwnership: true, + } +} diff --git a/cmd/syft/cli/options/python.go b/cmd/syft/internal/options/python.go similarity index 86% rename from cmd/syft/cli/options/python.go rename to cmd/syft/internal/options/python.go index 0efab8713a2..26b36c99ac2 100644 --- a/cmd/syft/cli/options/python.go +++ b/cmd/syft/internal/options/python.go @@ -1,5 +1,5 @@ package options -type python struct { +type pythonConfig struct { GuessUnpinnedRequirements bool `json:"guess-unpinned-requirements" yaml:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"` } diff --git a/cmd/syft/cli/options/registry.go b/cmd/syft/internal/options/registry.go similarity index 94% rename from cmd/syft/cli/options/registry.go rename to cmd/syft/internal/options/registry.go index 455ba93d51f..2ea23653c1b 100644 --- a/cmd/syft/cli/options/registry.go +++ b/cmd/syft/internal/options/registry.go @@ -18,16 +18,16 @@ type RegistryCredentials struct { TLSKey string `yaml:"tls-key,omitempty" json:"tls-key,omitempty" mapstructure:"tls-key"` } -type registry struct { +type registryConfig struct { InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify" json:"insecure-skip-tls-verify" mapstructure:"insecure-skip-tls-verify"` InsecureUseHTTP bool `yaml:"insecure-use-http" json:"insecure-use-http" mapstructure:"insecure-use-http"` Auth []RegistryCredentials `yaml:"auth" json:"auth" mapstructure:"auth"` CACert string `yaml:"ca-cert" json:"ca-cert" mapstructure:"ca-cert"` } -var _ clio.PostLoader = (*registry)(nil) +var _ clio.PostLoader = (*registryConfig)(nil) -func (cfg *registry) PostLoad() error { +func (cfg *registryConfig) PostLoad() error { // there may be additional credentials provided by env var that should be appended to the set of credentials authority, username, password, token, tlsCert, tlsKey := os.Getenv("SYFT_REGISTRY_AUTH_AUTHORITY"), @@ -62,7 +62,7 @@ func hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey string) b return hasUserPass || hasToken || hasTLSMaterial } -func (cfg *registry) ToOptions() *image.RegistryOptions { +func (cfg *registryConfig) ToOptions() *image.RegistryOptions { var auth = make([]image.RegistryCredentials, len(cfg.Auth)) for i, a := range cfg.Auth { auth[i] = image.RegistryCredentials{ diff --git a/cmd/syft/cli/options/registry_test.go b/cmd/syft/internal/options/registry_test.go similarity index 94% rename from cmd/syft/cli/options/registry_test.go rename to cmd/syft/internal/options/registry_test.go index 4979fcbc281..150f2007805 100644 --- a/cmd/syft/cli/options/registry_test.go +++ b/cmd/syft/internal/options/registry_test.go @@ -72,19 +72,19 @@ func TestHasNonEmptyCredentials(t *testing.T) { func Test_registry_ToOptions(t *testing.T) { tests := []struct { name string - input registry + input registryConfig expected image.RegistryOptions }{ { name: "no registry options", - input: registry{}, + input: registryConfig{}, expected: image.RegistryOptions{ Credentials: []image.RegistryCredentials{}, }, }, { name: "set InsecureSkipTLSVerify", - input: registry{ + input: registryConfig{ InsecureSkipTLSVerify: true, }, expected: image.RegistryOptions{ @@ -94,7 +94,7 @@ func Test_registry_ToOptions(t *testing.T) { }, { name: "set InsecureUseHTTP", - input: registry{ + input: registryConfig{ InsecureUseHTTP: true, }, expected: image.RegistryOptions{ @@ -104,7 +104,7 @@ func Test_registry_ToOptions(t *testing.T) { }, { name: "set all bool options", - input: registry{ + input: registryConfig{ InsecureSkipTLSVerify: true, InsecureUseHTTP: true, }, @@ -116,7 +116,7 @@ func Test_registry_ToOptions(t *testing.T) { }, { name: "provide all tls configuration", - input: registry{ + input: registryConfig{ CACert: "ca.crt", InsecureSkipTLSVerify: true, Auth: []RegistryCredentials{ diff --git a/cmd/syft/internal/options/relationships.go b/cmd/syft/internal/options/relationships.go new file mode 100644 index 00000000000..5bc491b85e0 --- /dev/null +++ b/cmd/syft/internal/options/relationships.go @@ -0,0 +1,22 @@ +package options + +import "github.com/anchore/fangs" + +var _ fangs.FieldDescriber = (*relationshipsConfig)(nil) + +type relationshipsConfig struct { + PackageFileOwnership bool `mapstructure:"package-file-ownership" json:"package-file-ownership" yaml:"package-file-ownership"` + PackageFileOwnershipOverlap bool `mapstructure:"package-file-ownership-overlap" json:"package-file-ownership-overlap" yaml:"package-file-ownership-overlap"` +} + +func defaultRelationshipsConfig() relationshipsConfig { + return relationshipsConfig{ + PackageFileOwnership: true, + PackageFileOwnershipOverlap: true, + } +} + +func (r *relationshipsConfig) DescribeFields(descriptions fangs.FieldDescriptionSet) { + descriptions.Add(&r.PackageFileOwnership, "include package-to-file relationships that indicate which files are owned by which packages.") + descriptions.Add(&r.PackageFileOwnershipOverlap, "include package-to-package relationships that indicate one package is owned by another due to files claimed to be owned by one package are also evidence of another package's existence.") +} diff --git a/cmd/syft/cli/options/secret.go b/cmd/syft/internal/options/secret.go similarity index 100% rename from cmd/syft/cli/options/secret.go rename to cmd/syft/internal/options/secret.go diff --git a/cmd/syft/internal/options/source.go b/cmd/syft/internal/options/source.go new file mode 100644 index 00000000000..f582043e33f --- /dev/null +++ b/cmd/syft/internal/options/source.go @@ -0,0 +1,59 @@ +package options + +import ( + "fmt" + "sort" + "strings" + + "github.com/scylladb/go-set/strset" +) + +type sourceConfig struct { + Name string `json:"name" yaml:"name" mapstructure:"name"` + Version string `json:"version" yaml:"version" mapstructure:"version"` + BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths + File fileSource `json:"file" yaml:"file" mapstructure:"file"` + Image imageSource `json:"image" yaml:"image" mapstructure:"image"` +} + +type fileSource struct { + Digests []string `json:"digests" yaml:"digests" mapstructure:"digests"` +} + +type imageSource struct { + DefaultPullSource string `json:"default-pull-source" yaml:"default-pull-source" mapstructure:"default-pull-source"` +} + +func defaultSourceConfig() sourceConfig { + return sourceConfig{ + File: fileSource{ + Digests: []string{"sha256"}, + }, + Image: imageSource{ + DefaultPullSource: "", + }, + } +} + +func (c *fileSource) PostLoad() error { + digests := strset.New(c.Digests...).List() + sort.Strings(digests) + c.Digests = digests + return nil +} + +func (c imageSource) PostLoad() error { + return checkDefaultSourceValues(c.DefaultPullSource) +} + +var validDefaultSourceValues = []string{"registry", "docker", "podman", ""} + +func checkDefaultSourceValues(source string) error { + validValues := strset.New(validDefaultSourceValues...) + if !validValues.Has(source) { + validValuesString := strings.Join(validDefaultSourceValues, ", ") + return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString) + } + + return nil +} diff --git a/cmd/syft/internal/options/source_test.go b/cmd/syft/internal/options/source_test.go new file mode 100644 index 00000000000..7adf6893ed2 --- /dev/null +++ b/cmd/syft/internal/options/source_test.go @@ -0,0 +1,37 @@ +package options + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_fileSource_PostLoad(t *testing.T) { + tests := []struct { + name string + cfg fileSource + assert func(t *testing.T, cfg fileSource) + wantErr assert.ErrorAssertionFunc + }{ + { + name: "deduplicate digests", + cfg: fileSource{ + Digests: []string{"sha1", "sha1"}, + }, + assert: func(t *testing.T, cfg fileSource) { + assert.Equal(t, []string{"sha1"}, cfg.Digests) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = assert.NoError + } + tt.wantErr(t, tt.cfg.PostLoad()) + if tt.assert != nil { + tt.assert(t, tt.cfg) + } + }) + } +} diff --git a/cmd/syft/cli/options/update_check.go b/cmd/syft/internal/options/update_check.go similarity index 58% rename from cmd/syft/cli/options/update_check.go rename to cmd/syft/internal/options/update_check.go index a05b5242061..0f396a5d9fc 100644 --- a/cmd/syft/cli/options/update_check.go +++ b/cmd/syft/internal/options/update_check.go @@ -1,5 +1,7 @@ package options +import "github.com/anchore/fangs" + type UpdateCheck struct { CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not } @@ -9,3 +11,7 @@ func DefaultUpdateCheck() UpdateCheck { CheckForAppUpdate: true, } } + +func (cfg *UpdateCheck) DescribeFields(descriptions fangs.FieldDescriptionSet) { + descriptions.Add(&cfg.CheckForAppUpdate, "whether to check for an application update on start up or not") +} diff --git a/cmd/syft/cli/options/writer.go b/cmd/syft/internal/options/writer.go similarity index 100% rename from cmd/syft/cli/options/writer.go rename to cmd/syft/internal/options/writer.go diff --git a/cmd/syft/cli/options/writer_test.go b/cmd/syft/internal/options/writer_test.go similarity index 100% rename from cmd/syft/cli/options/writer_test.go rename to cmd/syft/internal/options/writer_test.go diff --git a/test/integration/all_layers_squashed_comparison_test.go b/cmd/syft/internal/test/integration/all_layers_squashed_comparison_test.go similarity index 91% rename from test/integration/all_layers_squashed_comparison_test.go rename to cmd/syft/internal/test/integration/all_layers_squashed_comparison_test.go index 39973cbfaa3..4dedd9ef44e 100644 --- a/test/integration/all_layers_squashed_comparison_test.go +++ b/cmd/syft/internal/test/integration/all_layers_squashed_comparison_test.go @@ -8,8 +8,8 @@ import ( func Test_AllLayersIncludesSquashed(t *testing.T) { // This is a verification test for issue #894 (https://github.com/anchore/syft/issues/894) - allLayers, _ := catalogFixtureImage(t, "image-suse-all-layers", source.AllLayersScope, nil) - squashed, _ := catalogFixtureImage(t, "image-suse-all-layers", source.SquashedScope, nil) + allLayers, _ := catalogFixtureImage(t, "image-suse-all-layers", source.AllLayersScope) + squashed, _ := catalogFixtureImage(t, "image-suse-all-layers", source.SquashedScope) lenAllLayers := len(allLayers.Artifacts.Packages.Sorted()) lenSquashed := len(squashed.Artifacts.Packages.Sorted()) diff --git a/test/integration/catalog_packages_cases_test.go b/cmd/syft/internal/test/integration/catalog_packages_cases_test.go similarity index 97% rename from test/integration/catalog_packages_cases_test.go rename to cmd/syft/internal/test/integration/catalog_packages_cases_test.go index 3df13864a00..869019a0f93 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_cases_test.go @@ -368,6 +368,14 @@ var dirOnlyTestCases = []testCase{ "unicode_util_compat": "0.7.0", }, }, + { + name: "find ErLang OTP applications", + pkgType: pkg.ErlangOTPPkg, + pkgLanguage: pkg.Erlang, + pkgInfo: map[string]string{ + "accept": "0.3.5", + }, + }, { name: "find swift package manager packages", pkgType: pkg.SwiftPkg, @@ -444,4 +452,12 @@ var commonTestCases = []testCase{ "glibc": "2.34-210", }, }, + { + name: "find wordpress plugins", + pkgType: pkg.WordpressPluginPkg, + pkgLanguage: pkg.PHP, + pkgInfo: map[string]string{ + "Akismet Anti-spam: Spam Protection": "5.3", + }, + }, } diff --git a/test/integration/catalog_packages_test.go b/cmd/syft/internal/test/integration/catalog_packages_test.go similarity index 82% rename from test/integration/catalog_packages_test.go rename to cmd/syft/internal/test/integration/catalog_packages_test.go index 1e8fc1c4637..f67adf7b78a 100644 --- a/test/integration/catalog_packages_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_test.go @@ -1,6 +1,7 @@ package integration import ( + "context" "strings" "testing" @@ -9,53 +10,39 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/imagetest" - "github.com/anchore/syft/syft/linux" + "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/pkg/cataloger" "github.com/anchore/syft/syft/source" ) func BenchmarkImagePackageCatalogers(b *testing.B) { + // get the fixture image tar file fixtureImageName := "image-pkg-coverage" imagetest.GetFixtureImage(b, "docker-archive", fixtureImageName) tarPath := imagetest.GetFixtureImageTarPath(b, fixtureImageName) - var pc *pkg.Collection - for _, c := range cataloger.ImageCatalogers(cataloger.DefaultConfig()) { - // in case of future alteration where state is persisted, assume no dependency is safe to reuse - userInput := "docker-archive:" + tarPath - detection, err := source.Detect(userInput, source.DefaultDetectConfig()) - require.NoError(b, err) - theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig()) - if err != nil { - b.Fatalf("unable to get source: %+v", err) - } - b.Cleanup(func() { - theSource.Close() - }) + // get the source object for the image + userInput := "docker-archive:" + tarPath + detection, err := source.Detect(userInput, source.DefaultDetectConfig()) + require.NoError(b, err) - resolver, err := theSource.FileResolver(source.SquashedScope) - if err != nil { - b.Fatalf("unable to get resolver: %+v", err) - } + theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig()) + require.NoError(b, err) - theDistro := linux.IdentifyRelease(resolver) + b.Cleanup(func() { + theSource.Close() + }) - b.Run(c.Name(), func(b *testing.B) { - for i := 0; i < b.N; i++ { - pc, _, err = cataloger.Catalog(resolver, theDistro, 1, c) - if err != nil { - b.Fatalf("failure during benchmark: %+v", err) - } - } - }) + // build the SBOM + s, err := syft.CreateSBOM(context.Background(), theSource, syft.DefaultCreateSBOMConfig()) - b.Logf("catalog for %q number of packages: %d", c.Name(), pc.PackageCount()) - } + // did it work? + require.NoError(b, err) + require.NotNil(b, s) } func TestPkgCoverageImage(t *testing.T) { - sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope) observedLanguages := strset.New() definedLanguages := strset.New() @@ -70,8 +57,8 @@ func TestPkgCoverageImage(t *testing.T) { definedLanguages.Remove(pkg.Swift.String()) definedLanguages.Remove(pkg.CPP.String()) definedLanguages.Remove(pkg.Haskell.String()) - definedLanguages.Remove(pkg.Erlang.String()) definedLanguages.Remove(pkg.Elixir.String()) + definedLanguages.Remove(pkg.Erlang.String()) observedPkgs := strset.New() definedPkgs := strset.New() @@ -84,6 +71,7 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.GoModulePkg)) definedPkgs.Remove(string(pkg.RustPkg)) definedPkgs.Remove(string(pkg.DartPubPkg)) + definedPkgs.Remove(string(pkg.ErlangOTPPkg)) definedPkgs.Remove(string(pkg.CocoapodsPkg)) definedPkgs.Remove(string(pkg.ConanPkg)) definedPkgs.Remove(string(pkg.HackagePkg)) @@ -135,7 +123,6 @@ func TestPkgCoverageImage(t *testing.T) { } t.Fatalf("unexpected package count: %d!=%d", pkgCount, len(c.pkgInfo)) } - }) } @@ -223,7 +210,6 @@ func TestPkgCoverageDirectory(t *testing.T) { } t.Fatalf("unexpected package count: %d!=%d", actualPkgCount, len(test.pkgInfo)) } - }) } @@ -250,29 +236,8 @@ func TestPkgCoverageDirectory(t *testing.T) { } } -func TestPkgCoverageCatalogerConfiguration(t *testing.T) { - // Check that cataloger configuration can be used to run a cataloger on a source - // for which that cataloger isn't enabled by defauly - sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, []string{"rust"}) - - observedLanguages := strset.New() - definedLanguages := strset.New() - definedLanguages.Add("rust") - - for actualPkg := range sbom.Artifacts.Packages.Enumerate() { - observedLanguages.Add(actualPkg.Language.String()) - } - - assert.Equal(t, definedLanguages, observedLanguages) - - // Verify that rust isn't actually an image cataloger - c := cataloger.DefaultConfig() - c.Catalogers = []string{"rust"} - assert.Len(t, cataloger.ImageCatalogers(c), 0) -} - func TestPkgCoverageImage_HasEvidence(t *testing.T) { - sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope) var cases []testCase cases = append(cases, commonTestCases...) @@ -282,7 +247,6 @@ func TestPkgCoverageImage_HasEvidence(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { - for a := range sbom.Artifacts.Packages.Enumerate(c.pkgType) { assert.NotEmpty(t, a.Locations.ToSlice(), "package %q has no locations (type=%q)", a.Name, a.Type) for _, l := range a.Locations.ToSlice() { @@ -292,7 +256,6 @@ func TestPkgCoverageImage_HasEvidence(t *testing.T) { } } } - }) } @@ -312,7 +275,6 @@ func TestPkgCoverageDirectory_HasEvidence(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { - for a := range sbom.Artifacts.Packages.Enumerate(c.pkgType) { assert.NotEmpty(t, a.Locations.ToSlice(), "package %q has no locations (type=%q)", a.Name, a.Type) for _, l := range a.Locations.ToSlice() { @@ -322,7 +284,6 @@ func TestPkgCoverageDirectory_HasEvidence(t *testing.T) { } } } - }) } diff --git a/test/integration/distro_test.go b/cmd/syft/internal/test/integration/distro_test.go similarity index 96% rename from test/integration/distro_test.go rename to cmd/syft/internal/test/integration/distro_test.go index d2159660ff8..6c8f904534e 100644 --- a/test/integration/distro_test.go +++ b/cmd/syft/internal/test/integration/distro_test.go @@ -10,7 +10,7 @@ import ( ) func TestDistroImage(t *testing.T) { - sbom, _ := catalogFixtureImage(t, "image-distro-id", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-distro-id", source.SquashedScope) expected := &linux.Release{ PrettyName: "BusyBox v1.31.1", diff --git a/test/integration/encode_decode_cycle_test.go b/cmd/syft/internal/test/integration/encode_decode_cycle_test.go similarity index 89% rename from test/integration/encode_decode_cycle_test.go rename to cmd/syft/internal/test/integration/encode_decode_cycle_test.go index 9b32081d6dd..56bd7b77260 100644 --- a/test/integration/encode_decode_cycle_test.go +++ b/cmd/syft/internal/test/integration/encode_decode_cycle_test.go @@ -10,12 +10,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/cmd/syft/internal/options" "github.com/anchore/syft/syft/format" "github.com/anchore/syft/syft/format/cyclonedxjson" "github.com/anchore/syft/syft/format/cyclonedxxml" "github.com/anchore/syft/syft/format/syftjson" - "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) @@ -32,12 +31,12 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { "image-owning-package", } tests := []struct { - formatOption sbom.FormatID - redactor func(in []byte) []byte - json bool + name string + redactor func(in []byte) []byte + json bool }{ { - formatOption: syftjson.ID, + name: syftjson.ID.String(), redactor: func(in []byte) []byte { // no redactions necessary return in @@ -45,7 +44,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { json: true, }, { - formatOption: cyclonedxjson.ID, + name: cyclonedxjson.ID.String(), redactor: func(in []byte) []byte { // unstable values in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref|ref)":\s*"(\n|[^"])+"`).ReplaceAll(in, []byte(`"$1": "redacted"`)) @@ -55,7 +54,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { json: true, }, { - formatOption: cyclonedxxml.ID, + name: cyclonedxxml.ID.String(), redactor: func(in []byte) []byte { // unstable values in = regexp.MustCompile(`(serialNumber|bom-ref|ref)="[^"]+"`).ReplaceAll(in, []byte{}) @@ -75,11 +74,11 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { decoders := format.NewDecoderCollection(format.Decoders()...) for _, test := range tests { - t.Run(string(test.formatOption), func(t *testing.T) { + t.Run(test.name, func(t *testing.T) { for _, image := range images { - originalSBOM, _ := catalogFixtureImage(t, image, source.SquashedScope, nil) + originalSBOM, _ := catalogFixtureImage(t, image, source.SquashedScope) - f := encoders.GetByString(string(test.formatOption)) + f := encoders.GetByString(test.name) require.NotNil(t, f) var buff1 bytes.Buffer diff --git a/cmd/syft/internal/test/integration/files_test.go b/cmd/syft/internal/test/integration/files_test.go new file mode 100644 index 00000000000..6794eed08eb --- /dev/null +++ b/cmd/syft/internal/test/integration/files_test.go @@ -0,0 +1,154 @@ +package integration + +import ( + "crypto" + "os" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/clio" + stereoscopeFile "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/cmd/syft/internal/options" + "github.com/anchore/syft/syft/cataloging/filecataloging" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/file/cataloger/filecontent" + "github.com/anchore/syft/syft/sbom" +) + +func TestFileCataloging_Default(t *testing.T) { + cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{}) + cfg = cfg.WithFilesConfig(filecataloging.DefaultConfig()) + sbom, _ := catalogDirectoryWithConfig(t, "test-fixtures/files", cfg) + + var metadata map[file.Coordinates]file.Metadata + + var digests map[file.Coordinates][]file.Digest + + var contents map[file.Coordinates]string + + assertFileData(t, metadata, digests, contents, sbom) +} + +func TestFileCataloging_AllFiles(t *testing.T) { + cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{}) + cfg = cfg.WithFilesConfig(filecataloging.Config{ + Selection: file.AllFilesSelection, + Hashers: []crypto.Hash{ + crypto.SHA256, + }, + Content: filecontent.Config{ + // this is enough to potentially capture a/file, a-small-file, a-symlink-to-a-small-file, and a-symlink-to-file + // but the size of a/file will cause it to be filtered, and the symlinks will not be included since + // they are not regular files + Globs: []string{"**/*file"}, + SkipFilesAboveSize: 30, + }, + }) + sbom, _ := catalogDirectoryWithConfig(t, "test-fixtures/files", cfg) + + pwd, err := os.Getwd() + require.NoError(t, err) + + testPath := func(path string) string { + return filepath.Join(pwd, "test-fixtures/files", path) + } + + metadata := map[file.Coordinates]file.Metadata{ + {RealPath: ""}: { + Path: testPath(""), + Type: stereoscopeFile.TypeDirectory, + }, + {RealPath: "/somewhere"}: { + Path: testPath("/somewhere"), + Type: stereoscopeFile.TypeDirectory, + }, + {RealPath: "/somewhere/there"}: { + Path: testPath("/somewhere/there"), + Type: stereoscopeFile.TypeDirectory, + }, + {RealPath: "/somewhere/there/is"}: { + Path: testPath("/somewhere/there/is"), + Type: stereoscopeFile.TypeDirectory, + }, + {RealPath: "/somewhere/there/is/a"}: { + Path: testPath("/somewhere/there/is/a"), + Type: stereoscopeFile.TypeDirectory, + }, + {RealPath: "/somewhere/there/is/a-small-file"}: { + Path: testPath("/somewhere/there/is/a-small-file"), + Type: stereoscopeFile.TypeRegular, + MIMEType: "text/plain", + }, + {RealPath: "/somewhere/there/is/a-symlink-to-a-small-file"}: { + Path: testPath("/somewhere/there/is/a-symlink-to-a-small-file"), + LinkDestination: testPath("/somewhere/there/is/a-small-file"), + Type: stereoscopeFile.TypeSymLink, + }, + {RealPath: "/somewhere/there/is/a-symlink-to-file"}: { + Path: testPath("/somewhere/there/is/a-symlink-to-file"), + LinkDestination: testPath("/somewhere/there/is/a/file"), + Type: stereoscopeFile.TypeSymLink, + }, + {RealPath: "/somewhere/there/is/a/file"}: { + Path: testPath("/somewhere/there/is/a/file"), + Type: stereoscopeFile.TypeRegular, + MIMEType: "text/plain", + }, + } + + digests := map[file.Coordinates][]file.Digest{ + {RealPath: "/somewhere/there/is/a-small-file"}: { + file.Digest{Algorithm: "sha256", Value: "672c23470e4ce99cf270bb63ae66ad2b8a80aa19090c40e59fbb1229a4ab661a"}, + }, + {RealPath: "/somewhere/there/is/a/file"}: { + file.Digest{Algorithm: "sha256", Value: "00dac26d6d94353ac0d92bb9640cba76f82f5ca8707bb845ecdc574bd002348e"}, + }, + } + + contents := map[file.Coordinates]string{ + {RealPath: "/somewhere/there/is/a-small-file"}: "c29tZSBjb250ZW50cyE=", + } + + assertFileData(t, metadata, digests, contents, sbom) + +} + +func assertFileData(t testing.TB, metadata map[file.Coordinates]file.Metadata, digests map[file.Coordinates][]file.Digest, contents map[file.Coordinates]string, sbom sbom.SBOM) { + metadataCompareOpts := cmp.Options{ + cmp.Comparer(func(x, y file.Metadata) bool { + if x.Path != y.Path { + t.Logf("path mismatch: %s != %s", x.Path, y.Path) + return false + } + + if x.Type != y.Type { + t.Logf("type mismatch: %s != %s", x.Type, y.Type) + return false + } + + if x.LinkDestination != y.LinkDestination { + t.Logf("link destination mismatch: %s != %s", x.LinkDestination, y.LinkDestination) + return false + } + + if x.MIMEType != y.MIMEType { + t.Logf("mime type mismatch: %s != %s", x.MIMEType, y.MIMEType) + return false + } + + return true + }), + } + + if d := cmp.Diff(metadata, sbom.Artifacts.FileMetadata, metadataCompareOpts...); d != "" { + t.Errorf("unexpected metadata (-want +got):\n%s", d) + } + + assert.Equal(t, digests, sbom.Artifacts.FileDigests, "different digests detected") + assert.Equal(t, contents, sbom.Artifacts.FileContents, "different contents detected") + +} diff --git a/test/integration/go_compiler_detection_test.go b/cmd/syft/internal/test/integration/go_compiler_detection_test.go similarity index 97% rename from test/integration/go_compiler_detection_test.go rename to cmd/syft/internal/test/integration/go_compiler_detection_test.go index 26994482379..8c440a0309e 100644 --- a/test/integration/go_compiler_detection_test.go +++ b/cmd/syft/internal/test/integration/go_compiler_detection_test.go @@ -19,13 +19,13 @@ func TestGolangCompilerDetection(t *testing.T) { name: "syft can detect a single golang compiler given the golang base image", image: "image-golang-compiler", expectedCompilers: []string{"go1.18.10"}, - expectedCPE: []cpe.CPE{cpe.Must("cpe:2.3:a:golang:go:1.18.10:-:*:*:*:*:*:*")}, + expectedCPE: []cpe.CPE{cpe.Must("cpe:2.3:a:golang:go:1.18.10:-:*:*:*:*:*:*", cpe.GeneratedSource)}, expectedPURL: []string{"pkg:golang/stdlib@1.18.10"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - sbom, _ := catalogFixtureImage(t, tt.image, source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, tt.image, source.SquashedScope) packages := sbom.Artifacts.Packages.PackagesByName("stdlib") foundCompilerVersions := make(map[string]struct{}) diff --git a/cmd/syft/internal/test/integration/java_purl_test.go b/cmd/syft/internal/test/integration/java_purl_test.go new file mode 100644 index 00000000000..a7adbc396c2 --- /dev/null +++ b/cmd/syft/internal/test/integration/java_purl_test.go @@ -0,0 +1,638 @@ +package integration + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func TestJavaPURLs(t *testing.T) { + sbom, _ := catalogFixtureImage(t, "image-test-java-purls", source.SquashedScope) + found := make(map[string]string) + for _, p := range sbom.Artifacts.Packages.Sorted() { + if p.Type != pkg.JavaPkg && p.Type != pkg.JenkinsPluginPkg { + continue + } + + if metadata, ok := p.Metadata.(pkg.JavaArchive); ok { + if _, ok := noAssertion[metadata.VirtualPath]; ok { + continue + } + + found[metadata.VirtualPath] = p.PURL + } + } + for key, expectedPURL := range expectedPURLs { + purl := found[key] + assert.Equal(t, expectedPURL, purl, fmt.Sprintf("found wrong or missing PURL for %s want %s, got %s", key, expectedPURL, purl)) + } + for key, foundPURL := range found { + expectedPURL := expectedPURLs[key] + assert.Equal(t, expectedPURL, foundPURL, fmt.Sprintf("found extra purl for %s want %s, got %s", key, expectedPURL, foundPURL)) + } +} + +// This is for artifacts that either don't have a known groupid/artifactid +// since they aren't available on a known maven repository, or where syft is currently +// generating the incorrect PURL +var noAssertion = map[string]string{ + // These are not known to exist on any public maven repo, so no real PURL should be asserted + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/jopenssl.jar": "pkg:maven/jopenssl/jopenssl", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/json/ext/generator.jar": "pkg:maven/generator/generator", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/json/ext/parser.jar": "pkg:maven/parser/parser", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/psych.jar": "pkg:maven/psych/psych", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/racc/cparse-jruby.jar": "pkg:maven/cparse-jruby/cparse-jruby", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/readline.jar": "pkg:maven/readline/readline", + "/packages/org.eclipse.ant.core-3.7.0.jar:lib/antsupportlib.jar": "pkg:maven/antsupportlib/antsupportlib", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/sdk-3.0.jar": "pkg:maven/sdk/sdk@3.0", + + // syft generates incorrect purls + "/packages/akka-actor_2.13-2.6.6.jar": "pkg:maven/com.typesafe.akka/akka-actor_2.13@2.6.6", + "/packages/akka-management-cluster-bootstrap_2.13-1.2.0.jar": "pkg:maven/com.lightbend.akka.management/akka-management-cluster-bootstrap_2.13@1.2.0", + "/packages/hudson.war:WEB-INF/lib/asm-2.2.3.jar": "pkg:maven/asm/asm@2.2.3", + "/packages/hudson.war:WEB-INF/lib/asm-commons-2.2.3.jar": "pkg:maven/asm/asm-commons@2.2.3", + "/packages/hudson.war:WEB-INF/lib/asm-tree-2.2.3.jar": "pkg:maven/asm/asm-tree@2.2.3", + "/packages/hudson.war:WEB-INF/slave.jar": "pkg:maven/org.jvnet.hudson.main/remoting@1.390", + "/packages/hudson.war:WEB-INF/lib/xpp3_min-1.1.4c.jar": "pkg:maven/xpp3_min/xpp3_min@1.1.4c", + "/packages/hudson.war:WEB-INF/lib/xpp3-1.1.4c.jar": "pkg:maven/xpp3/xpp3@1.1.4c", + "/packages/hudson.war:WEB-INF/hudson-cli.jar": "pkg:maven/org.jvnet.hudson.main/hudson-cli@1.390", + "/packages/hudson.war:WEB-INF/lib/dom4j-1.6.1-hudson-3.jar": "pkg:maven/org.jvnet.hudson.dom4j/dom4j@1.6.1-hudson-3", + "/packages/xpp3_min-1.1.4c.jar": "pkg:maven/xpp3/xpp3_min@1.1.4c", + + // syft generates an unstable purl + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-auth": "pkg:maven/org.apache.dubbo/dubbo-auth@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-cluster": "pkg:maven/org.apache.dubbo/dubbo-cluster@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-common": "pkg:maven/org.apache.dubbo/dubbo-common@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-compatible": "pkg:maven/org.apache.dubbo/dubbo-compatible@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-config-api": "pkg:maven/org.apache.dubbo/dubbo-config-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-config-spring": "pkg:maven/org.apache.dubbo/dubbo-config-spring@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-configcenter-apollo": "pkg:maven/org.apache.dubbo/dubbo-configcenter-apollo@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-configcenter-nacos": "pkg:maven/org.apache.dubbo/dubbo-configcenter-nacos@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-configcenter-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-configcenter-zookeeper@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-container-api": "pkg:maven/org.apache.dubbo/dubbo-container-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-container-spring": "pkg:maven/org.apache.dubbo/dubbo-container-spring@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-filter-cache": "pkg:maven/org.apache.dubbo/dubbo-filter-cache@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-filter-validation": "pkg:maven/org.apache.dubbo/dubbo-filter-validation@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-kubernetes": "pkg:maven/org.apache.dubbo/dubbo-kubernetes@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-api": "pkg:maven/org.apache.dubbo/dubbo-metadata-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-definition-protobuf": "pkg:maven/org.apache.dubbo/dubbo-metadata-definition-protobuf@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-report-nacos": "pkg:maven/org.apache.dubbo/dubbo-metadata-report-nacos@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-report-redis": "pkg:maven/org.apache.dubbo/dubbo-metadata-report-redis@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-report-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-metadata-report-zookeeper@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metrics-api": "pkg:maven/org.apache.dubbo/dubbo-metrics-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metrics-prometheus": "pkg:maven/org.apache.dubbo/dubbo-metrics-prometheus@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-monitor-api": "pkg:maven/org.apache.dubbo/dubbo-monitor-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-monitor-default": "pkg:maven/org.apache.dubbo/dubbo-monitor-default@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-qos": "pkg:maven/org.apache.dubbo/dubbo-qos@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-reactive": "pkg:maven/org.apache.dubbo/dubbo-reactive@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-api": "pkg:maven/org.apache.dubbo/dubbo-registry-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-multicast": "pkg:maven/org.apache.dubbo/dubbo-registry-multicast@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-multiple": "pkg:maven/org.apache.dubbo/dubbo-registry-multiple@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-nacos": "pkg:maven/org.apache.dubbo/dubbo-registry-nacos@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-registry-zookeeper@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-api": "pkg:maven/org.apache.dubbo/dubbo-remoting-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-http": "pkg:maven/org.apache.dubbo/dubbo-remoting-http@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-netty": "pkg:maven/org.apache.dubbo/dubbo-remoting-netty@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-netty4": "pkg:maven/org.apache.dubbo/dubbo-remoting-netty4@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-remoting-zookeeper@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-zookeeper-curator5": "pkg:maven/org.apache.dubbo/dubbo-remoting-zookeeper-curator5@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-api": "pkg:maven/org.apache.dubbo/dubbo-rpc-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-dubbo": "pkg:maven/org.apache.dubbo/dubbo-rpc-dubbo@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-grpc": "pkg:maven/org.apache.dubbo/dubbo-rpc-grpc@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-injvm": "pkg:maven/org.apache.dubbo/dubbo-rpc-injvm@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-rest": "pkg:maven/org.apache.dubbo/dubbo-rpc-rest@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-triple": "pkg:maven/org.apache.dubbo/dubbo-rpc-triple@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-api": "pkg:maven/org.apache.dubbo/dubbo-serialization-api@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-fastjson2": "pkg:maven/org.apache.dubbo/dubbo-serialization-fastjson2@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-hessian2": "pkg:maven/org.apache.dubbo/dubbo-serialization-hessian2@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-jdk": "pkg:maven/org.apache.dubbo/dubbo-serialization-jdk@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-xds": "pkg:maven/org.apache.dubbo/dubbo-xds@3.1.4", + "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo": "pkg:maven/org.apache.dubbo/dubbo@3.1.4", + "/packages/dubbo-3.1.4.jar": "pkg:maven/org.apache.dubbo/dubbo@3.1.4", +} + +// Constructed by: +// syft anchore/test_images:java-1abc58f -o json | jq -r '.artifacts.[] | [.metadata.virtualPath, .purl, ""] | @csv' | grep 'pkg:maven' | sort | uniq >> /tmp/java_artifacts_mapping.txt +// The map was then hand-edited for correctness by comparing to Maven Central. +var expectedPURLs = map[string]string{ + "/packages/activemq-client-5.18.2.jar": "pkg:maven/org.apache.activemq/activemq-client@5.18.2", + "/packages/activemq-protobuf-1.1.jar": "pkg:maven/org.apache.activemq.protobuf/activemq-protobuf@1.1", + // "/packages/akka-actor_2.13-2.6.6.jar": "pkg:maven/com.typesafe.akka/akka-actor_2.13@2.6.6", + // "/packages/akka-management-cluster-bootstrap_2.13-1.2.0.jar": "pkg:maven/com.lightbend.akka.management/akka-management-cluster-bootstrap_2.13@1.2.0", + "/packages/ant-1.10.3.jar": "pkg:maven/org.apache.ant/ant@1.10.3", + "/packages/apache-chainsaw-2.1.0.jar": "pkg:maven/log4j/apache-chainsaw@2.1.0", + "/packages/apache-log4j-extras-1.1.jar": "pkg:maven/log4j/apache-log4j-extras@1.1", + "/packages/apoc-4.4.0.11.jar": "pkg:maven/org.neo4j.procedure/apoc@4.4.0.11", + "/packages/bc-fips-1.0.2.3.jar": "pkg:maven/org.bouncycastle/bc-fips@1.0.2.3", + "/packages/camel-core-3.1.0.jar": "pkg:maven/org.apache.camel/camel-core@3.1.0", + "/packages/cassandra-all-4.1.1.jar": "pkg:maven/org.apache.cassandra/cassandra-all@4.1.1", + "/packages/commons-logging-1.1.1.jar": "pkg:maven/commons-logging/commons-logging@1.1.1", + "/packages/commons-vfs-1.0.jar": "pkg:maven/commons-vfs/commons-vfs@1.0", + "/packages/cxf-rt-transports-http-2.7.3.jar": "pkg:maven/org.apache.cxf/cxf-rt-transports-http@2.7.3", + "/packages/dubbo-3.1.4.jar:com.alibaba:hessian-lite": "pkg:maven/com.alibaba/hessian-lite@3.2.13", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-auth": "pkg:maven/org.apache.dubbo/dubbo-auth@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-cluster": "pkg:maven/org.apache.dubbo/dubbo-cluster@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-common": "pkg:maven/org.apache.dubbo/dubbo-common@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-compatible": "pkg:maven/org.apache.dubbo/dubbo-compatible@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-config-api": "pkg:maven/org.apache.dubbo/dubbo-config-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-config-spring": "pkg:maven/org.apache.dubbo/dubbo-config-spring@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-configcenter-apollo": "pkg:maven/org.apache.dubbo/dubbo-configcenter-apollo@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-configcenter-nacos": "pkg:maven/org.apache.dubbo/dubbo-configcenter-nacos@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-configcenter-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-configcenter-zookeeper@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-container-api": "pkg:maven/org.apache.dubbo/dubbo-container-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-container-spring": "pkg:maven/org.apache.dubbo/dubbo-container-spring@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-filter-cache": "pkg:maven/org.apache.dubbo/dubbo-filter-cache@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-filter-validation": "pkg:maven/org.apache.dubbo/dubbo-filter-validation@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-kubernetes": "pkg:maven/org.apache.dubbo/dubbo-kubernetes@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-api": "pkg:maven/org.apache.dubbo/dubbo-metadata-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-definition-protobuf": "pkg:maven/org.apache.dubbo/dubbo-metadata-definition-protobuf@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-report-nacos": "pkg:maven/org.apache.dubbo/dubbo-metadata-report-nacos@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-report-redis": "pkg:maven/org.apache.dubbo/dubbo-metadata-report-redis@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metadata-report-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-metadata-report-zookeeper@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metrics-api": "pkg:maven/org.apache.dubbo/dubbo-metrics-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-metrics-prometheus": "pkg:maven/org.apache.dubbo/dubbo-metrics-prometheus@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-monitor-api": "pkg:maven/org.apache.dubbo/dubbo-monitor-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-monitor-default": "pkg:maven/org.apache.dubbo/dubbo-monitor-default@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-qos": "pkg:maven/org.apache.dubbo/dubbo-qos@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-reactive": "pkg:maven/org.apache.dubbo/dubbo-reactive@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-api": "pkg:maven/org.apache.dubbo/dubbo-registry-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-multicast": "pkg:maven/org.apache.dubbo/dubbo-registry-multicast@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-multiple": "pkg:maven/org.apache.dubbo/dubbo-registry-multiple@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-nacos": "pkg:maven/org.apache.dubbo/dubbo-registry-nacos@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-registry-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-registry-zookeeper@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-api": "pkg:maven/org.apache.dubbo/dubbo-remoting-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-http": "pkg:maven/org.apache.dubbo/dubbo-remoting-http@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-netty": "pkg:maven/org.apache.dubbo/dubbo-remoting-netty@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-netty4": "pkg:maven/org.apache.dubbo/dubbo-remoting-netty4@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-zookeeper": "pkg:maven/org.apache.dubbo/dubbo-remoting-zookeeper@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-remoting-zookeeper-curator5": "pkg:maven/org.apache.dubbo/dubbo-remoting-zookeeper-curator5@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-api": "pkg:maven/org.apache.dubbo/dubbo-rpc-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-dubbo": "pkg:maven/org.apache.dubbo/dubbo-rpc-dubbo@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-grpc": "pkg:maven/org.apache.dubbo/dubbo-rpc-grpc@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-injvm": "pkg:maven/org.apache.dubbo/dubbo-rpc-injvm@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-rest": "pkg:maven/org.apache.dubbo/dubbo-rpc-rest@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-rpc-triple": "pkg:maven/org.apache.dubbo/dubbo-rpc-triple@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-api": "pkg:maven/org.apache.dubbo/dubbo-serialization-api@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-fastjson2": "pkg:maven/org.apache.dubbo/dubbo-serialization-fastjson2@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-hessian2": "pkg:maven/org.apache.dubbo/dubbo-serialization-hessian2@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-serialization-jdk": "pkg:maven/org.apache.dubbo/dubbo-serialization-jdk@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo-xds": "pkg:maven/org.apache.dubbo/dubbo-xds@3.1.4", + // "/packages/dubbo-3.1.4.jar:org.apache.dubbo:dubbo": "pkg:maven/org.apache.dubbo/dubbo@3.1.4", + // "/packages/dubbo-3.1.4.jar": "pkg:maven/org.apache.dubbo/dubbo@3.1.4", + "/packages/elasticsearch-8.10.2.jar": "pkg:maven/org.elasticsearch/elasticsearch@8.10.2", + "/packages/elasticsearch-rest-client-sniffer-7.17.11.jar": "pkg:maven/org.elasticsearch.client/elasticsearch-rest-client-sniffer@7.17.11", + "/packages/example-java-app-gradle-0.1.0.ear": "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0", + "/packages/geode-core-1.14.3.jar": "pkg:maven/org.apache.geode/geode-core@1.14.3", + "/packages/github-api-1.118.jar": "pkg:maven/org.kohsuke/github-api@1.118", + "/packages/google-oauth-client-1.25.0.jar": "pkg:maven/com.google.oauth-client/google-oauth-client@1.25.0", + "/packages/graphql-java-20.0.jar:com.google.guava:guava": "pkg:maven/com.google.guava/guava@31.0.1-jre", + "/packages/graphql-java-20.0.jar:org.antlr:antlr4-runtime": "pkg:maven/org.antlr/antlr4-runtime@4.9.3", + "/packages/graphql-java-20.0.jar": "pkg:maven/com.graphql-java/graphql-java@20.0", + "/packages/guava-29.0-jre.jar": "pkg:maven/com.google.guava/guava@29.0-jre", + "/packages/hadoop-common-3.3.2.jar": "pkg:maven/org.apache.hadoop/hadoop-common@3.3.2", + "/packages/hazelcast-5.2.4.jar:com.fasterxml.jackson.core:jackson-core": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.15.2", + "/packages/hazelcast-5.2.4.jar:com.fasterxml.jackson.jr:jackson-jr-annotation-support": "pkg:maven/com.fasterxml.jackson.jr/jackson-jr-annotation-support@2.15.2", + "/packages/hazelcast-5.2.4.jar:com.fasterxml.jackson.jr:jackson-jr-objects": "pkg:maven/com.fasterxml.jackson.jr/jackson-jr-objects@2.15.2", + "/packages/hazelcast-5.2.4.jar:com.github.erosb:everit-json-schema": "pkg:maven/com.github.erosb/everit-json-schema@1.14.2", + "/packages/hazelcast-5.2.4.jar:com.zaxxer:HikariCP": "pkg:maven/com.zaxxer/HikariCP@4.0.3", + "/packages/hazelcast-5.2.4.jar:io.github.classgraph:classgraph": "pkg:maven/io.github.classgraph/classgraph@4.8.149", + "/packages/hazelcast-5.2.4.jar:org.json:json": "pkg:maven/org.json/json@20230227", + "/packages/hazelcast-5.2.4.jar:org.snakeyaml:snakeyaml-engine": "pkg:maven/org.snakeyaml/snakeyaml-engine@2.3", + "/packages/hazelcast-5.2.4.jar": "pkg:maven/com.hazelcast/hazelcast@5.2.4", + "/packages/hibernate-validator-6.1.3.Final.jar": "pkg:maven/org.hibernate.validator/hibernate-validator@6.1.3.Final", + "/packages/http2-common-11.0.16.jar": "pkg:maven/org.eclipse.jetty.http2/http2-common@11.0.16", + "/packages/hudson.war:WEB-INF/hudson-cli.jar:args4j:args4j": "pkg:maven/args4j/args4j@2.0.16", + "/packages/hudson.war:WEB-INF/hudson-cli.jar:org.jvnet:animal-sniffer-annotation": "pkg:maven/org.jvnet/animal-sniffer-annotation@1.0", + "/packages/hudson.war:WEB-INF/hudson-cli.jar:org.jvnet.hudson.main:remoting": "pkg:maven/org.jvnet.hudson.main/remoting@1.390", + "/packages/hudson.war:WEB-INF/hudson-cli.jar:org.jvnet.localizer:localizer": "pkg:maven/org.jvnet.localizer/localizer@1.10", + // "/packages/hudson.war:WEB-INF/hudson-cli.jar": "pkg:maven/org.jvnet.hudson.main/hudson-cli@1.390", + "/packages/hudson.war:WEB-INF/lib/access-modifier-annotation-1.0.jar": "pkg:maven/org.kohsuke/access-modifier-annotation@1.0", + "/packages/hudson.war:WEB-INF/lib/acegi-security-1.0.5.jar": "pkg:maven/org.acegisecurity/acegi-security@1.0.5", + "/packages/hudson.war:WEB-INF/lib/activation-1.1.1-hudson-1.jar": "pkg:maven/org.jvnet.hudson/activation@1.1.1-hudson-1", + "/packages/hudson.war:WEB-INF/lib/akuma-1.2.jar": "pkg:maven/com.sun.akuma/akuma@1.2", + "/packages/hudson.war:WEB-INF/lib/annotation-indexer-1.2.jar": "pkg:maven/org.jvnet.hudson/annotation-indexer@1.2", + "/packages/hudson.war:WEB-INF/lib/ant-1.8.0.jar": "pkg:maven/org.apache.ant/ant@1.8.0", + "/packages/hudson.war:WEB-INF/lib/ant-launcher-1.8.0.jar": "pkg:maven/org.apache.ant/ant-launcher@1.8.0", + "/packages/hudson.war:WEB-INF/lib/antlr-2.7.6.jar": "pkg:maven/antlr/antlr@2.7.6", + "/packages/hudson.war:WEB-INF/lib/aopalliance-1.0.jar": "pkg:maven/aopalliance/aopalliance@1.0", + "/packages/hudson.war:WEB-INF/lib/args4j-2.0.16.jar": "pkg:maven/args4j/args4j@2.0.16", + // "/packages/hudson.war:WEB-INF/lib/asm-2.2.3.jar": "pkg:maven/asm/asm@2.2.3", + // "/packages/hudson.war:WEB-INF/lib/asm-commons-2.2.3.jar": "pkg:maven/asm/asm-commons@2.2.3", + // "/packages/hudson.war:WEB-INF/lib/asm-tree-2.2.3.jar": "pkg:maven/asm/asm-tree@2.2.3", + "/packages/hudson.war:WEB-INF/lib/avalon-framework-4.1.3.jar": "pkg:maven/avalon-framework/avalon-framework@4.1.3", + "/packages/hudson.war:WEB-INF/lib/bridge-method-annotation-1.2.jar": "pkg:maven/com.infradna.tool/bridge-method-annotation@1.2", + "/packages/hudson.war:WEB-INF/lib/cli-1.390.jar": "pkg:maven/org.jvnet.hudson.main/cli@1.390", + "/packages/hudson.war:WEB-INF/lib/commons-beanutils-1.8.0.jar": "pkg:maven/commons-beanutils/commons-beanutils@1.8.0", + "/packages/hudson.war:WEB-INF/lib/commons-codec-1.4.jar": "pkg:maven/commons-codec/commons-codec@1.4", + "/packages/hudson.war:WEB-INF/lib/commons-collections-3.2.jar": "pkg:maven/commons-collections/commons-collections@3.2", + "/packages/hudson.war:WEB-INF/lib/commons-digester-1.7.jar": "pkg:maven/commons-digester/commons-digester@1.7", + "/packages/hudson.war:WEB-INF/lib/commons-discovery-0.4.jar": "pkg:maven/commons-discovery/commons-discovery@0.4", + "/packages/hudson.war:WEB-INF/lib/commons-fileupload-1.2.1.jar": "pkg:maven/commons-fileupload/commons-fileupload@1.2.1", + "/packages/hudson.war:WEB-INF/lib/commons-io-1.4.jar": "pkg:maven/commons-io/commons-io@1.4", + "/packages/hudson.war:WEB-INF/lib/commons-jelly-1.1-hudson-20100305.jar": "pkg:maven/org.jvnet.hudson/commons-jelly@1.1-hudson-20100305", + "/packages/hudson.war:WEB-INF/lib/commons-jelly-tags-define-1.0.1-hudson-20071021.jar": "pkg:maven/org.jvnet.hudson/commons-jelly-tags-define@1.0.1-hudson-20071021", + "/packages/hudson.war:WEB-INF/lib/commons-jelly-tags-fmt-1.0.jar": "pkg:maven/commons-jelly/commons-jelly-tags-fmt@1.0", + "/packages/hudson.war:WEB-INF/lib/commons-jelly-tags-xml-1.1.jar": "pkg:maven/commons-jelly/commons-jelly-tags-xml@1.1", + "/packages/hudson.war:WEB-INF/lib/commons-jexl-1.1-hudson-20090508.jar": "pkg:maven/org.jvnet.hudson/commons-jexl@1.1-hudson-20090508", + "/packages/hudson.war:WEB-INF/lib/commons-lang-2.4.jar": "pkg:maven/commons-lang/commons-lang@2.4", + "/packages/hudson.war:WEB-INF/lib/commons-logging-1.1.jar": "pkg:maven/commons-logging/commons-logging@1.1", + "/packages/hudson.war:WEB-INF/lib/commons-pool-1.3.jar": "pkg:maven/commons-pool/commons-pool@1.3", + "/packages/hudson.war:WEB-INF/lib/crypto-util-1.0.jar": "pkg:maven/org.jvnet.hudson/crypto-util@1.0", + // "/packages/hudson.war:WEB-INF/lib/dom4j-1.6.1-hudson-3.jar": "pkg:maven/org.jvnet.hudson.dom4j/dom4j@1.6.1-hudson-3", + "/packages/hudson.war:WEB-INF/lib/embedded_su4j-1.1.jar": "pkg:maven/com.sun.solaris/embedded_su4j@1.1", + "/packages/hudson.war:WEB-INF/lib/ezmorph-1.0.3.jar": "pkg:maven/net.sf.ezmorph/ezmorph@1.0.3", + "/packages/hudson.war:WEB-INF/lib/graph-layouter-1.0.jar": "pkg:maven/org.kohsuke/graph-layouter@1.0", + "/packages/hudson.war:WEB-INF/lib/groovy-all-1.6.0.jar": "pkg:maven/org.codehaus.groovy/groovy-all@1.6.0", + "/packages/hudson.war:WEB-INF/lib/guava-r06.jar": "pkg:maven/com.google.guava/guava@r06", + "/packages/hudson.war:WEB-INF/lib/hudson-core-1.390.jar": "pkg:maven/org.jvnet.hudson.main/hudson-core@1.390", + "/packages/hudson.war:WEB-INF/lib/j-interop-2.0.5.jar": "pkg:maven/j-interop/j-interop@2.0.5", + "/packages/hudson.war:WEB-INF/lib/j-interopdeps-2.0.5.jar": "pkg:maven/j-interopdeps/j-interopdeps@2.0.5", + "/packages/hudson.war:WEB-INF/lib/jaxen-1.1-beta-11.jar": "pkg:maven/org.jaxen/jaxen@1.1-beta-11", + "/packages/hudson.war:WEB-INF/lib/jcaptcha-all-1.0-RC6.jar": "pkg:maven/jcaptcha-all/jcaptcha-all@1.0-RC6", + "/packages/hudson.war:WEB-INF/lib/jcifs-1.3.14-kohsuke-1.jar": "pkg:maven/org.samba.jcifs/jcifs@1.3.14-kohsuke-1", + "/packages/hudson.war:WEB-INF/lib/jcommon-1.0.12.jar": "pkg:maven/jfree/jcommon@1.0.12", + "/packages/hudson.war:WEB-INF/lib/jfreechart-1.0.9.jar": "pkg:maven/jfreechart/jfreechart@1.0.9", + "/packages/hudson.war:WEB-INF/lib/jinterop-proxy-1.1.jar": "pkg:maven/org.kohsuke.jinterop/jinterop-proxy@1.1", + "/packages/hudson.war:WEB-INF/lib/jinterop-wmi-1.0.jar": "pkg:maven/org.jvnet.hudson/jinterop-wmi@1.0", + "/packages/hudson.war:WEB-INF/lib/jline-0.9.94.jar": "pkg:maven/jline/jline@0.9.94", + "/packages/hudson.war:WEB-INF/lib/jmdns-3.1.6-hudson-2.jar": "pkg:maven/com.strangeberry.jmdns.tools.Main/jmdns@3.1.6-hudson-2", + "/packages/hudson.war:WEB-INF/lib/jna-3.2.4.jar": "pkg:maven/com.sun.jna/jna@3.2.4", + "/packages/hudson.war:WEB-INF/lib/jna-posix-1.0.3.jar": "pkg:maven/org.jruby.ext.posix/jna-posix@1.0.3", + "/packages/hudson.war:WEB-INF/lib/json-lib-2.1-rev6.jar": "pkg:maven/json-lib/json-lib@2.1-rev6", + "/packages/hudson.war:WEB-INF/lib/jstl-1.1.0.jar": "pkg:maven/com.sun/jstl@1.1.0", + "/packages/hudson.war:WEB-INF/lib/jtidy-4aug2000r7-dev-hudson-1.jar": "pkg:maven/jtidy/jtidy@4aug2000r7-dev-hudson-1", + "/packages/hudson.war:WEB-INF/lib/libpam4j-1.2.jar": "pkg:maven/org.jvnet.libpam4j/libpam4j@1.2", + "/packages/hudson.war:WEB-INF/lib/libzfs-0.5.jar": "pkg:maven/org.jvnet.libzfs/libzfs@0.5", + "/packages/hudson.war:WEB-INF/lib/localizer-1.10.jar": "pkg:maven/org.jvnet.localizer/localizer@1.10", + "/packages/hudson.war:WEB-INF/lib/log4j-1.2.9.jar": "pkg:maven/log4j/log4j@1.2.9", + "/packages/hudson.war:WEB-INF/lib/logkit-1.0.1.jar": "pkg:maven/logkit/logkit@1.0.1", + "/packages/hudson.war:WEB-INF/lib/mail-1.4.jar": "pkg:maven/com.sun/mail@1.4", + "/packages/hudson.war:WEB-INF/lib/memory-monitor-1.3.jar": "pkg:maven/org.jvnet.hudson/memory-monitor@1.3", + "/packages/hudson.war:WEB-INF/lib/oro-2.0.8.jar": "pkg:maven/org.apache.oro/oro@2.0.8", + "/packages/hudson.war:WEB-INF/lib/remoting-1.390.jar:args4j:args4j": "pkg:maven/args4j/args4j@2.0.16", + "/packages/hudson.war:WEB-INF/lib/remoting-1.390.jar": "pkg:maven/org.jvnet.hudson.main/remoting@1.390", + "/packages/hudson.war:WEB-INF/lib/robust-http-client-1.1.jar": "pkg:maven/org.jvnet.robust-http-client/robust-http-client@1.1", + "/packages/hudson.war:WEB-INF/lib/sezpoz-1.7.jar": "pkg:maven/net.java.sezpoz/sezpoz@1.7", + "/packages/hudson.war:WEB-INF/lib/spring-aop-2.5.jar": "pkg:maven/org.springframework/spring-aop@2.5", + "/packages/hudson.war:WEB-INF/lib/spring-beans-2.5.jar": "pkg:maven/org.springframework/spring-beans@2.5", + "/packages/hudson.war:WEB-INF/lib/spring-context-2.5.jar": "pkg:maven/org.springframework/spring-context@2.5", + "/packages/hudson.war:WEB-INF/lib/spring-core-2.5.jar": "pkg:maven/org.springframework/spring-core@2.5", + "/packages/hudson.war:WEB-INF/lib/spring-dao-1.2.9.jar": "pkg:maven/org.springframework/spring-dao@1.2.9", + "/packages/hudson.war:WEB-INF/lib/spring-jdbc-1.2.9.jar": "pkg:maven/org.springframework/spring-jdbc@1.2.9", + "/packages/hudson.war:WEB-INF/lib/spring-web-2.5.jar": "pkg:maven/org.springframework/spring-web@2.5", + "/packages/hudson.war:WEB-INF/lib/stapler-1.155.jar": "pkg:maven/org.kohsuke.stapler/stapler@1.155", + "/packages/hudson.war:WEB-INF/lib/stapler-adjunct-timeline-1.2.jar": "pkg:maven/org.kohsuke.stapler/stapler-adjunct-timeline@1.2", + "/packages/hudson.war:WEB-INF/lib/stapler-jelly-1.155.jar": "pkg:maven/org.kohsuke.stapler/stapler-jelly@1.155", + "/packages/hudson.war:WEB-INF/lib/stax-api-1.0.1.jar": "pkg:maven/stax-api/stax-api@1.0.1", + "/packages/hudson.war:WEB-INF/lib/task-reactor-1.2.jar": "pkg:maven/org.jvnet.hudson/task-reactor@1.2", + "/packages/hudson.war:WEB-INF/lib/tiger-types-1.3.jar": "pkg:maven/org.jvnet/tiger-types@1.3", + "/packages/hudson.war:WEB-INF/lib/trilead-putty-extension-1.0.jar": "pkg:maven/org.kohsuke/trilead-putty-extension@1.0", + "/packages/hudson.war:WEB-INF/lib/trilead-ssh2-build212-hudson-5.jar": "pkg:maven/org.jvnet.hudson/trilead-ssh2@build212-hudson-5", + "/packages/hudson.war:WEB-INF/lib/txw2-20070624.jar": "pkg:maven/txw2/txw2@20070624", + "/packages/hudson.war:WEB-INF/lib/windows-remote-command-1.0.jar": "pkg:maven/org.jvnet.hudson/windows-remote-command@1.0", + "/packages/hudson.war:WEB-INF/lib/winp-1.14.jar": "pkg:maven/org.jvnet.winp/winp@1.14", + "/packages/hudson.war:WEB-INF/lib/wstx-asl-3.2.7.jar": "pkg:maven/wstx-asl/wstx-asl@3.2.7", + // "/packages/hudson.war:WEB-INF/lib/xpp3_min-1.1.4c.jar": "pkg:maven/xpp3_min/xpp3_min@1.1.4c", + // "/packages/hudson.war:WEB-INF/lib/xpp3-1.1.4c.jar": "pkg:maven/xpp3/xpp3@1.1.4c", + "/packages/hudson.war:WEB-INF/lib/xstream-1.3.1-hudson-8.jar": "pkg:maven/org.jvnet.hudson/xstream@1.3.1-hudson-8", + "/packages/hudson.war:WEB-INF/plugins/cvs.hpi": "pkg:maven/org.jvnet.hudson.plugins/cvs@1.2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/avalon-framework-4.1.3.jar": "pkg:maven/avalon-framework/avalon-framework@4.1.3", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/classworlds-1.1.jar": "pkg:maven/org.codehaus.classworlds/classworlds@1.1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/commons-codec-1.4.jar": "pkg:maven/commons-codec/commons-codec@1.4", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/commons-httpclient-3.1-rc1.jar": "pkg:maven/commons-httpclient/commons-httpclient@3.1-rc1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/commons-logging-1.1.jar": "pkg:maven/commons-logging/commons-logging@1.1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/doxia-sink-api-1.0-alpha-10.jar": "pkg:maven/org.apache.maven.doxia/doxia-sink-api@1.0-alpha-10", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/jsch-0.1.27.jar": "pkg:maven/jsch/jsch@0.1.27", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/log4j-1.2.9.jar": "pkg:maven/log4j/log4j@1.2.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/logkit-1.0.1.jar": "pkg:maven/logkit/logkit@1.0.1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-agent-1.390.jar": "pkg:maven/org.jvnet.hudson.main/maven-agent@1.390", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-artifact-2.0.9.jar": "pkg:maven/org.apache.maven/maven-artifact@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-artifact-manager-2.0.9.jar": "pkg:maven/org.apache.maven/maven-artifact-manager@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-core-2.0.9.jar": "pkg:maven/org.apache.maven/maven-core@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-embedder-2.0.4-hudson-1.jar": "pkg:maven/org.jvnet.hudson/maven-embedder@2.0.4-hudson-1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-embedder-2.0.4.jar": "pkg:maven/org.apache.maven/maven-embedder@2.0.4", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-error-diagnostics-2.0.9.jar": "pkg:maven/org.apache.maven/maven-error-diagnostics@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-interceptor-1.390.jar": "pkg:maven/org.jvnet.hudson.main/maven-interceptor@1.390", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-model-2.0.9.jar": "pkg:maven/org.apache.maven/maven-model@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-monitor-2.0.9.jar": "pkg:maven/org.apache.maven/maven-monitor@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-plugin-api-2.0.9.jar": "pkg:maven/org.apache.maven/maven-plugin-api@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-plugin-descriptor-2.0.9.jar": "pkg:maven/org.apache.maven/maven-plugin-descriptor@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-plugin-parameter-documenter-2.0.9.jar": "pkg:maven/org.apache.maven/maven-plugin-parameter-documenter@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-plugin-registry-2.0.9.jar": "pkg:maven/org.apache.maven/maven-plugin-registry@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-profile-2.0.9.jar": "pkg:maven/org.apache.maven/maven-profile@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-project-2.0.9.jar": "pkg:maven/org.apache.maven/maven-project@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-reporting-api-2.0.9.jar": "pkg:maven/org.apache.maven.reporting/maven-reporting-api@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-repository-metadata-2.0.9.jar": "pkg:maven/org.apache.maven/maven-repository-metadata@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven-settings-2.0.9.jar": "pkg:maven/org.apache.maven/maven-settings@2.0.9", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/maven2.1-interceptor-1.2.jar": "pkg:maven/org.jvnet.hudson/maven2.1-interceptor@1.2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/plexus-container-default-1.0-alpha-9-stable-1.jar": "pkg:maven/org.codehaus.plexus/plexus-container-default@1.0-alpha-9-stable-1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/plexus-interactivity-api-1.0-alpha-4.jar": "pkg:maven/org.codehaus.plexus/plexus-interactivity-api@1.0-alpha-4", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/plexus-utils-1.5.1.jar": "pkg:maven/org.codehaus.plexus/plexus-utils@1.5.1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/slide-webdavlib-2.1.jar": "pkg:maven/slide-webdavlib/slide-webdavlib@2.1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-file-1.0-beta-2.jar": "pkg:maven/org.apache.maven.wagon/wagon-file@1.0-beta-2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-http-lightweight-1.0-beta-2.jar": "pkg:maven/org.apache.maven.wagon/wagon-http-lightweight@1.0-beta-2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-http-shared-1.0-beta-2.jar": "pkg:maven/org.apache.maven.wagon/wagon-http-shared@1.0-beta-2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-provider-api-1.0-beta-2.jar": "pkg:maven/org.apache.maven.wagon/wagon-provider-api@1.0-beta-2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-ssh-1.0-beta-2.jar": "pkg:maven/org.apache.maven.wagon/wagon-ssh@1.0-beta-2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-ssh-common-1.0-beta-2.jar": "pkg:maven/org.apache.maven.wagon/wagon-ssh-common@1.0-beta-2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-ssh-external-1.0-beta-2.jar": "pkg:maven/org.apache.maven.wagon/wagon-ssh-external@1.0-beta-2", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/wagon-webdav-1.0-beta-2-hudson-1.jar": "pkg:maven/org.jvnet.hudson/wagon-webdav@1.0-beta-2-hudson-1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi:WEB-INF/lib/xml-im-exporter-1.1.jar": "pkg:maven/xml-im-exporter/xml-im-exporter@1.1", + "/packages/hudson.war:WEB-INF/plugins/maven-plugin.hpi": "pkg:maven/org.jvnet.hudson.main/maven-plugin@1.390", + "/packages/hudson.war:WEB-INF/plugins/ssh-slaves.hpi": "pkg:maven/org.jvnet.hudson.plugins/ssh-slaves@0.14", + "/packages/hudson.war:WEB-INF/plugins/subversion.hpi:WEB-INF/lib/svnkit-1.3.4-hudson-2.jar": "pkg:maven/svnkit/svnkit@1.3.4-hudson-2", + "/packages/hudson.war:WEB-INF/plugins/subversion.hpi": "pkg:maven/org.jvnet.hudson.plugins/subversion@1.20", + "/packages/hudson.war:WEB-INF/remoting.jar:args4j:args4j": "pkg:maven/args4j/args4j@2.0.16", + "/packages/hudson.war:WEB-INF/remoting.jar": "pkg:maven/org.jvnet.hudson.main/remoting@1.390", + "/packages/hudson.war:WEB-INF/slave.jar:args4j:args4j": "pkg:maven/args4j/args4j@2.0.16", + "/packages/hudson.war:WEB-INF/slave.jar:org.jvnet.hudson.main:remoting": "pkg:maven/org.jvnet.hudson.main/remoting@1.390", + // "/packages/hudson.war:WEB-INF/slave.jar": "pkg:maven/org.jvnet.hudson.main/remoting@1.390", + "/packages/hudson.war:winstone.jar": "pkg:maven/org.jvnet.hudson.winstone/winstone@0.9.10-hudson-24", + "/packages/hudson.war": "pkg:maven/org.jvnet.hudson.main/hudson-war@1.390", + "/packages/infinispan-core-11.0.4.Final.jar": "pkg:maven/org.infinispan/infinispan-core@11.0.4.Final", + "/packages/jeecg-boot-common-3.6.0.jar": "pkg:maven/org.jeecgframework.boot/jeecg-boot-common@3.6.0", + "/packages/jetty-client-9.4.31.v20200723.jar": "pkg:maven/org.eclipse.jetty/jetty-client@9.4.31.v20200723", + "/packages/jetty-server-12.0.0.alpha3.jar": "pkg:maven/org.eclipse.jetty/jetty-server@12.0.0.alpha3", + "/packages/jetty-setuid-java-1.0.4.jar": "pkg:maven/org.eclipse.jetty.toolchain.setuid/jetty-setuid-java@1.0.4", + "/packages/jmdns-3.4.1.jar": "pkg:maven/javax.jmdns/jmdns@3.4.1", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/jopenssl.jar:rubygems:jruby-openssl": "pkg:maven/rubygems/jruby-openssl@0.9.21", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/org/bouncycastle/bcpkix-jdk15on/1.56/bcpkix-jdk15on-1.56.jar": "pkg:maven/org.bouncycastle/bcpkix-jdk15on@1.56", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/org/bouncycastle/bcprov-jdk15on/1.56/bcprov-jdk15on-1.56.jar": "pkg:maven/org.bouncycastle/bcprov-jdk15on@1.56", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/org/yaml/snakeyaml/1.18/snakeyaml-1.18.jar": "pkg:maven/org.yaml/snakeyaml@1.18", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/readline.jar:jline:jline": "pkg:maven/jline/jline@2.11", + "/packages/jruby-stdlib-9.1.15.0.jar:META-INF/jruby.home/lib/ruby/stdlib/readline.jar:rubygems:jruby-readline": "pkg:maven/rubygems/jruby-readline@1.2.0", + "/packages/jruby-stdlib-9.1.15.0.jar": "pkg:maven/org.jruby/jruby-stdlib@9.1.15.0", + "/packages/jsch-0.1.55.jar": "pkg:maven/com.jcraft/jsch@0.1.55", + "/packages/junit-4.13.1.jar": "pkg:maven/junit/junit@4.13.1", + "/packages/kafka_2.13-3.2.2.jar": "pkg:maven/org.apache.kafka/kafka_2.13@3.2.2", + "/packages/keycloak-core-22.0.2.jar": "pkg:maven/org.keycloak/keycloak-core@22.0.2", + "/packages/log4j-1.2.16.jar": "pkg:maven/log4j/log4j@1.2.16", + "/packages/log4j-core-2.17.0.jar": "pkg:maven/org.apache.logging.log4j/log4j-core@2.17.0", + "/packages/log4j-over-slf4j-1.7.36.jar": "pkg:maven/org.slf4j/log4j-over-slf4j@1.7.36", + "/packages/manager-pojo-1.8.0.jar": "pkg:maven/org.apache.inlong/manager-pojo@1.8.0", + "/packages/maven-shared-utils-3.2.1.jar": "pkg:maven/org.apache.maven.shared/maven-shared-utils@3.2.1", + "/packages/mesos-1.7.1.jar": "pkg:maven/org.apache.mesos/mesos@1.7.1", + "/packages/minio-8.3.8.jar": "pkg:maven/io.minio/minio@8.3.8", + "/packages/ms-mcms-5.3.1.jar": "pkg:maven/net.mingsoft/ms-mcms@5.3.1", + "/packages/my-app-1.jar:com.fasterxml:classmate": "pkg:maven/com.fasterxml/classmate@1.5.1", + "/packages/my-app-1.jar:com.fasterxml.jackson.core:jackson-annotations": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.13.2", + "/packages/my-app-1.jar:com.fasterxml.jackson.core:jackson-core": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.13.2", + "/packages/my-app-1.jar:com.fasterxml.jackson.core:jackson-databind": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.0", + "/packages/my-app-1.jar:com.google.errorprone:error_prone_annotations": "pkg:maven/com.google.errorprone/error_prone_annotations@2.0.18", + "/packages/my-app-1.jar:com.google.guava:guava": "pkg:maven/com.google.guava/guava@23.0", + "/packages/my-app-1.jar:com.google.j2objc:j2objc-annotations": "pkg:maven/com.google.j2objc/j2objc-annotations@1.1", + "/packages/my-app-1.jar:com.hazelcast:hazelcast-kubernetes": "pkg:maven/com.hazelcast/hazelcast-kubernetes@1.5.4", + "/packages/my-app-1.jar:com.hierynomus:asn-one": "pkg:maven/com.hierynomus/asn-one@0.5.0", + "/packages/my-app-1.jar:com.typesafe.netty:netty-reactive-streams": "pkg:maven/com.typesafe.netty/netty-reactive-streams@2.0.5", + "/packages/my-app-1.jar:commons-beanutils:commons-beanutils": "pkg:maven/commons-beanutils/commons-beanutils@1.9.2", + "/packages/my-app-1.jar:commons-collections:commons-collections": "pkg:maven/commons-collections/commons-collections@3.2.2", + "/packages/my-app-1.jar:commons-digester:commons-digester": "pkg:maven/commons-digester/commons-digester@1.8.1", + "/packages/my-app-1.jar:commons-io:commons-io": "pkg:maven/commons-io/commons-io@2.8.0", + "/packages/my-app-1.jar:commons-logging:commons-logging": "pkg:maven/commons-logging/commons-logging@1.2", + "/packages/my-app-1.jar:commons-validator:commons-validator": "pkg:maven/commons-validator/commons-validator@1.5.1", + "/packages/my-app-1.jar:io.netty:netty-buffer": "pkg:maven/io.netty/netty-buffer@4.1.52.Final", + "/packages/my-app-1.jar:io.netty:netty-codec-dns": "pkg:maven/io.netty/netty-codec-dns@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-codec-http": "pkg:maven/io.netty/netty-codec-http@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-codec-socks": "pkg:maven/io.netty/netty-codec-socks@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-codec": "pkg:maven/io.netty/netty-codec@4.1.52.Final", + "/packages/my-app-1.jar:io.netty:netty-common": "pkg:maven/io.netty/netty-common@4.1.52.Final", + "/packages/my-app-1.jar:io.netty:netty-handler-proxy": "pkg:maven/io.netty/netty-handler-proxy@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-handler": "pkg:maven/io.netty/netty-handler@4.1.52.Final", + "/packages/my-app-1.jar:io.netty:netty-resolver-dns-native-macos": "pkg:maven/io.netty/netty-resolver-dns-native-macos@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-resolver-dns": "pkg:maven/io.netty/netty-resolver-dns@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-resolver": "pkg:maven/io.netty/netty-resolver@4.1.52.Final", + "/packages/my-app-1.jar:io.netty:netty-transport-native-epoll": "pkg:maven/io.netty/netty-transport-native-epoll@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-transport-native-unix-common": "pkg:maven/io.netty/netty-transport-native-unix-common@4.1.63.Final", + "/packages/my-app-1.jar:io.netty:netty-transport": "pkg:maven/io.netty/netty-transport@4.1.52.Final", + "/packages/my-app-1.jar:io.prometheus:simpleclient_common": "pkg:maven/io.prometheus/simpleclient_common@0.9.0", + "/packages/my-app-1.jar:io.prometheus:simpleclient": "pkg:maven/io.prometheus/simpleclient@0.9.0", + "/packages/my-app-1.jar:io.vavr:vavr-match": "pkg:maven/io.vavr/vavr-match@0.10.2", + "/packages/my-app-1.jar:io.vavr:vavr": "pkg:maven/io.vavr/vavr@0.10.2", + "/packages/my-app-1.jar:jakarta.validation:jakarta.validation-api": "pkg:maven/jakarta.validation/jakarta.validation-api@2.0.2", + "/packages/my-app-1.jar:javax.servlet:javax.servlet-api": "pkg:maven/javax.servlet/javax.servlet-api@3.1.0", + "/packages/my-app-1.jar:org.apache.activemq.protobuf:activemq-protobuf": "pkg:maven/org.apache.activemq.protobuf/activemq-protobuf@1.1", + "/packages/my-app-1.jar:org.apache.commons:commons-compress": "pkg:maven/org.apache.commons/commons-compress@1.21", + "/packages/my-app-1.jar:org.apache.commons:commons-lang3": "pkg:maven/org.apache.commons/commons-lang3@3.10", + "/packages/my-app-1.jar:org.apache.maven.shared:maven-shared-utils": "pkg:maven/org.apache.maven.shared/maven-shared-utils@3.3.4", + "/packages/my-app-1.jar:org.codehaus.mojo:animal-sniffer-annotations": "pkg:maven/org.codehaus.mojo/animal-sniffer-annotations@1.14", + "/packages/my-app-1.jar:org.eclipse.jetty:jetty-client": "pkg:maven/org.eclipse.jetty/jetty-client@9.4.43.v20210629", + "/packages/my-app-1.jar:org.eclipse.jetty:jetty-http": "pkg:maven/org.eclipse.jetty/jetty-http@9.4.43.v20210629", + "/packages/my-app-1.jar:org.eclipse.jetty:jetty-io": "pkg:maven/org.eclipse.jetty/jetty-io@9.4.43.v20210629", + "/packages/my-app-1.jar:org.eclipse.jetty:jetty-server": "pkg:maven/org.eclipse.jetty/jetty-server@9.1.0.M0", + "/packages/my-app-1.jar:org.eclipse.jetty:jetty-util": "pkg:maven/org.eclipse.jetty/jetty-util@9.4.43.v20210629", + "/packages/my-app-1.jar:org.eclipse.jetty.http2:http2-common": "pkg:maven/org.eclipse.jetty.http2/http2-common@9.4.43.v20210629", + "/packages/my-app-1.jar:org.eclipse.jetty.http2:http2-hpack": "pkg:maven/org.eclipse.jetty.http2/http2-hpack@9.4.43.v20210629", + "/packages/my-app-1.jar:org.eclipse.jetty.toolchain.setuid:jetty-setuid-java": "pkg:maven/org.eclipse.jetty.toolchain.setuid/jetty-setuid-java@1.0.4", + "/packages/my-app-1.jar:org.everit.json:org.everit.json.schema": "pkg:maven/org.everit.json/org.everit.json.schema@1.5.1", + "/packages/my-app-1.jar:org.hibernate.validator:hibernate-validator": "pkg:maven/org.hibernate.validator/hibernate-validator@6.2.4.Final", + "/packages/my-app-1.jar:org.jboss.logging:jboss-logging": "pkg:maven/org.jboss.logging/jboss-logging@3.4.1.Final", + "/packages/my-app-1.jar:org.jctools:jctools-core": "pkg:maven/org.jctools/jctools-core@3.1.0", + "/packages/my-app-1.jar:org.jetbrains:annotations": "pkg:maven/org.jetbrains/annotations@13.0", + "/packages/my-app-1.jar:org.json:json": "pkg:maven/org.json/json@20160810", + "/packages/my-app-1.jar:org.kohsuke:github-api": "pkg:maven/org.kohsuke/github-api@1.301", + "/packages/my-app-1.jar:org.slf4j:log4j-over-slf4j": "pkg:maven/org.slf4j/log4j-over-slf4j@1.7.33", + "/packages/my-app-1.jar:org.slf4j:slf4j-api": "pkg:maven/org.slf4j/slf4j-api@1.7.33", + "/packages/my-app-1.jar:org.sonarsource.analyzer-commons:sonar-analyzer-test-commons": "pkg:maven/org.sonarsource.analyzer-commons/sonar-analyzer-test-commons@1.14.1.690", + "/packages/my-app-1.jar:org.sonarsource.php:php-checks": "pkg:maven/org.sonarsource.php/php-checks@3.17.0.7439", + "/packages/my-app-1.jar:org.sonarsource.php:php-frontend": "pkg:maven/org.sonarsource.php/php-frontend@3.17.0.7439", + "/packages/my-app-1.jar:org.sonarsource.python:python-checks": "pkg:maven/org.sonarsource.python/python-checks@3.4.1.8066", + "/packages/my-app-1.jar:org.sonarsource.python:python-frontend": "pkg:maven/org.sonarsource.python/python-frontend@3.4.1.8066", + "/packages/my-app-1.jar:org.sonarsource.sslr:sslr-core": "pkg:maven/org.sonarsource.sslr/sslr-core@1.24.0.633", + "/packages/my-app-1.jar": "pkg:maven/com.mycompany.app/my-app@1", + "/packages/netty-reactive-streams-2.0.6.jar": "pkg:maven/com.typesafe.netty/netty-reactive-streams@2.0.6", + "/packages/nifi-utils-1.12.0.jar": "pkg:maven/org.apache.nifi/nifi-utils@1.12.0", + "/packages/nomad.hpi:WEB-INF/lib/annotations-13.0.jar": "pkg:maven/org.jetbrains/annotations@13.0", + "/packages/nomad.hpi:WEB-INF/lib/gson-2.8.6.jar": "pkg:maven/com.google.code.gson/gson@2.8.6", + "/packages/nomad.hpi:WEB-INF/lib/json-20200518.jar": "pkg:maven/org.json/json@20200518", + "/packages/nomad.hpi:WEB-INF/lib/kotlin-stdlib-1.3.70.jar": "pkg:maven/kotlin-stdlib/kotlin-stdlib@1.3.70", + "/packages/nomad.hpi:WEB-INF/lib/kotlin-stdlib-common-1.3.70.jar": "pkg:maven/kotlin-stdlib-common/kotlin-stdlib-common@1.3.70", + "/packages/nomad.hpi:WEB-INF/lib/nomad.jar": "pkg:maven/org.jenkins-ci.plugins/nomad@0.7.4", + "/packages/nomad.hpi:WEB-INF/lib/okhttp-4.5.0.jar": "pkg:maven/com.squareup.okhttp3/okhttp@4.5.0", + "/packages/nomad.hpi:WEB-INF/lib/okio-2.5.0.jar": "pkg:maven/com.squareup.okio/okio@2.5.0", + "/packages/nomad.hpi": "pkg:maven/org.jenkins-ci.plugins/nomad@0.7.4", + "/packages/openmeetings-util-4.0.9.jar": "pkg:maven/org.apache.openmeetings/openmeetings-util@4.0.9", + "/packages/org.eclipse.ant.core-3.7.0.jar": "pkg:maven/org.eclipse.platform/org.eclipse.ant.core@3.7.0", + "/packages/org.eclipse.osgi-3.18.0.jar": "pkg:maven/org.eclipse.platform/org.eclipse.osgi@3.18.0", + "/packages/org.everit.json.schema-1.5.1.jar": "pkg:maven/org.everit.json/org.everit.json.schema@1.5.1", + "/packages/original-my-app-1.jar:com.fasterxml:classmate": "pkg:maven/com.fasterxml/classmate@1.5.1", + "/packages/original-my-app-1.jar:com.fasterxml.jackson.core:jackson-annotations": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.13.2", + "/packages/original-my-app-1.jar:com.fasterxml.jackson.core:jackson-core": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.13.2", + "/packages/original-my-app-1.jar:com.fasterxml.jackson.core:jackson-databind": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.0", + "/packages/original-my-app-1.jar:com.google.errorprone:error_prone_annotations": "pkg:maven/com.google.errorprone/error_prone_annotations@2.0.18", + "/packages/original-my-app-1.jar:com.google.guava:guava": "pkg:maven/com.google.guava/guava@23.0", + "/packages/original-my-app-1.jar:com.google.j2objc:j2objc-annotations": "pkg:maven/com.google.j2objc/j2objc-annotations@1.1", + "/packages/original-my-app-1.jar:com.hazelcast:hazelcast-kubernetes": "pkg:maven/com.hazelcast/hazelcast-kubernetes@1.5.4", + "/packages/original-my-app-1.jar:com.hierynomus:asn-one": "pkg:maven/com.hierynomus/asn-one@0.5.0", + "/packages/original-my-app-1.jar:com.typesafe.netty:netty-reactive-streams": "pkg:maven/com.typesafe.netty/netty-reactive-streams@2.0.5", + "/packages/original-my-app-1.jar:commons-beanutils:commons-beanutils": "pkg:maven/commons-beanutils/commons-beanutils@1.9.2", + "/packages/original-my-app-1.jar:commons-collections:commons-collections": "pkg:maven/commons-collections/commons-collections@3.2.2", + "/packages/original-my-app-1.jar:commons-digester:commons-digester": "pkg:maven/commons-digester/commons-digester@1.8.1", + "/packages/original-my-app-1.jar:commons-io:commons-io": "pkg:maven/commons-io/commons-io@2.8.0", + "/packages/original-my-app-1.jar:commons-logging:commons-logging": "pkg:maven/commons-logging/commons-logging@1.2", + "/packages/original-my-app-1.jar:commons-validator:commons-validator": "pkg:maven/commons-validator/commons-validator@1.5.1", + "/packages/original-my-app-1.jar:io.netty:netty-buffer": "pkg:maven/io.netty/netty-buffer@4.1.52.Final", + "/packages/original-my-app-1.jar:io.netty:netty-codec-dns": "pkg:maven/io.netty/netty-codec-dns@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-codec-http": "pkg:maven/io.netty/netty-codec-http@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-codec-socks": "pkg:maven/io.netty/netty-codec-socks@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-codec": "pkg:maven/io.netty/netty-codec@4.1.52.Final", + "/packages/original-my-app-1.jar:io.netty:netty-common": "pkg:maven/io.netty/netty-common@4.1.52.Final", + "/packages/original-my-app-1.jar:io.netty:netty-handler-proxy": "pkg:maven/io.netty/netty-handler-proxy@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-handler": "pkg:maven/io.netty/netty-handler@4.1.52.Final", + "/packages/original-my-app-1.jar:io.netty:netty-resolver-dns-native-macos": "pkg:maven/io.netty/netty-resolver-dns-native-macos@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-resolver-dns": "pkg:maven/io.netty/netty-resolver-dns@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-resolver": "pkg:maven/io.netty/netty-resolver@4.1.52.Final", + "/packages/original-my-app-1.jar:io.netty:netty-transport-native-epoll": "pkg:maven/io.netty/netty-transport-native-epoll@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-transport-native-unix-common": "pkg:maven/io.netty/netty-transport-native-unix-common@4.1.63.Final", + "/packages/original-my-app-1.jar:io.netty:netty-transport": "pkg:maven/io.netty/netty-transport@4.1.52.Final", + "/packages/original-my-app-1.jar:io.prometheus:simpleclient_common": "pkg:maven/io.prometheus/simpleclient_common@0.9.0", + "/packages/original-my-app-1.jar:io.prometheus:simpleclient": "pkg:maven/io.prometheus/simpleclient@0.9.0", + "/packages/original-my-app-1.jar:io.vavr:vavr-match": "pkg:maven/io.vavr/vavr-match@0.10.2", + "/packages/original-my-app-1.jar:io.vavr:vavr": "pkg:maven/io.vavr/vavr@0.10.2", + "/packages/original-my-app-1.jar:jakarta.validation:jakarta.validation-api": "pkg:maven/jakarta.validation/jakarta.validation-api@2.0.2", + "/packages/original-my-app-1.jar:javax.servlet:javax.servlet-api": "pkg:maven/javax.servlet/javax.servlet-api@3.1.0", + "/packages/original-my-app-1.jar:org.apache.activemq.protobuf:activemq-protobuf": "pkg:maven/org.apache.activemq.protobuf/activemq-protobuf@1.1", + "/packages/original-my-app-1.jar:org.apache.commons:commons-compress": "pkg:maven/org.apache.commons/commons-compress@1.21", + "/packages/original-my-app-1.jar:org.apache.commons:commons-lang3": "pkg:maven/org.apache.commons/commons-lang3@3.10", + "/packages/original-my-app-1.jar:org.apache.maven.shared:maven-shared-utils": "pkg:maven/org.apache.maven.shared/maven-shared-utils@3.3.4", + "/packages/original-my-app-1.jar:org.codehaus.mojo:animal-sniffer-annotations": "pkg:maven/org.codehaus.mojo/animal-sniffer-annotations@1.14", + "/packages/original-my-app-1.jar:org.eclipse.jetty:jetty-client": "pkg:maven/org.eclipse.jetty/jetty-client@9.4.43.v20210629", + "/packages/original-my-app-1.jar:org.eclipse.jetty:jetty-http": "pkg:maven/org.eclipse.jetty/jetty-http@9.4.43.v20210629", + "/packages/original-my-app-1.jar:org.eclipse.jetty:jetty-io": "pkg:maven/org.eclipse.jetty/jetty-io@9.4.43.v20210629", + "/packages/original-my-app-1.jar:org.eclipse.jetty:jetty-server": "pkg:maven/org.eclipse.jetty/jetty-server@9.1.0.M0", + "/packages/original-my-app-1.jar:org.eclipse.jetty:jetty-util": "pkg:maven/org.eclipse.jetty/jetty-util@9.4.43.v20210629", + "/packages/original-my-app-1.jar:org.eclipse.jetty.http2:http2-common": "pkg:maven/org.eclipse.jetty.http2/http2-common@9.4.43.v20210629", + "/packages/original-my-app-1.jar:org.eclipse.jetty.http2:http2-hpack": "pkg:maven/org.eclipse.jetty.http2/http2-hpack@9.4.43.v20210629", + "/packages/original-my-app-1.jar:org.eclipse.jetty.toolchain.setuid:jetty-setuid-java": "pkg:maven/org.eclipse.jetty.toolchain.setuid/jetty-setuid-java@1.0.4", + "/packages/original-my-app-1.jar:org.everit.json:org.everit.json.schema": "pkg:maven/org.everit.json/org.everit.json.schema@1.5.1", + "/packages/original-my-app-1.jar:org.hibernate.validator:hibernate-validator": "pkg:maven/org.hibernate.validator/hibernate-validator@6.2.4.Final", + "/packages/original-my-app-1.jar:org.jboss.logging:jboss-logging": "pkg:maven/org.jboss.logging/jboss-logging@3.4.1.Final", + "/packages/original-my-app-1.jar:org.jctools:jctools-core": "pkg:maven/org.jctools/jctools-core@3.1.0", + "/packages/original-my-app-1.jar:org.jetbrains:annotations": "pkg:maven/org.jetbrains/annotations@13.0", + "/packages/original-my-app-1.jar:org.json:json": "pkg:maven/org.json/json@20160810", + "/packages/original-my-app-1.jar:org.kohsuke:github-api": "pkg:maven/org.kohsuke/github-api@1.301", + "/packages/original-my-app-1.jar:org.slf4j:log4j-over-slf4j": "pkg:maven/org.slf4j/log4j-over-slf4j@1.7.33", + "/packages/original-my-app-1.jar:org.slf4j:slf4j-api": "pkg:maven/org.slf4j/slf4j-api@1.7.33", + "/packages/original-my-app-1.jar:org.sonarsource.analyzer-commons:sonar-analyzer-test-commons": "pkg:maven/org.sonarsource.analyzer-commons/sonar-analyzer-test-commons@1.14.1.690", + "/packages/original-my-app-1.jar:org.sonarsource.php:php-checks": "pkg:maven/org.sonarsource.php/php-checks@3.17.0.7439", + "/packages/original-my-app-1.jar:org.sonarsource.php:php-frontend": "pkg:maven/org.sonarsource.php/php-frontend@3.17.0.7439", + "/packages/original-my-app-1.jar:org.sonarsource.python:python-checks": "pkg:maven/org.sonarsource.python/python-checks@3.4.1.8066", + "/packages/original-my-app-1.jar:org.sonarsource.python:python-frontend": "pkg:maven/org.sonarsource.python/python-frontend@3.4.1.8066", + "/packages/original-my-app-1.jar:org.sonarsource.sslr:sslr-core": "pkg:maven/org.sonarsource.sslr/sslr-core@1.24.0.633", + "/packages/original-my-app-1.jar": "pkg:maven/com.mycompany.app/my-app@1", + "/packages/php-frontend-3.9.0.6331.jar": "pkg:maven/org.sonarsource.php/php-frontend@3.9.0.6331", + "/packages/postgresql-42.5.0.jar": "pkg:maven/org.postgresql/postgresql@42.5.0", + "/packages/protobuf-java-3.21.6.jar": "pkg:maven/com.google.protobuf/protobuf-java@3.21.6", + "/packages/python-frontend-3.24.0.10784.jar": "pkg:maven/org.sonarsource.python/python-frontend@3.24.0.10784", + "/packages/ratpack-core-1.8.2.jar": "pkg:maven/io.ratpack/ratpack-core@1.8.2", + "/packages/reactor-netty-core-1.0.35.jar": "pkg:maven/io.projectreactor.netty/reactor-netty-core@1.0.35", + "/packages/reactor-netty-http-1.1.9.jar": "pkg:maven/io.projectreactor.netty/reactor-netty-http@1.1.9", + "/packages/reactor-netty-incubator-quic-0.1.3.jar": "pkg:maven/io.projectreactor.netty.incubator/reactor-netty-incubator-quic@0.1.3", + "/packages/resilience4j-prometheus-0.17.0.jar": "pkg:maven/io.github.resilience4j.prometheus/resilience4j-prometheus@0.17.0", + "/packages/shiro-core-1.9.1.jar": "pkg:maven/org.apache.shiro/shiro-core@1.9.1", + "/packages/solr-core-8.8.1.jar": "pkg:maven/org.apache.solr/solr-core@8.8.1", + "/packages/spring-amqp-2.4.17.jar": "pkg:maven/org.springframework.amqp/spring-amqp@2.4.17", + "/packages/spring-analytics-2.0.0.RELEASE.jar": "pkg:maven/org.springframework.analytics/spring-analytics@2.0.0.RELEASE", + "/packages/spring-asm-3.0.7.RELEASE.jar": "pkg:maven/org.springframework/spring-asm@3.0.7.RELEASE", + "/packages/spring-batch-core-4.2.1.RELEASE.jar": "pkg:maven/org.springframework.batch/spring-batch-core@4.2.1.RELEASE", + "/packages/spring-boot-3.1.2.jar": "pkg:maven/org.springframework.boot/spring-boot@3.1.2", + "/packages/spring-cloud-app-broker-core-1.6.1.jar": "pkg:maven/org.springframework.cloud/spring-cloud-app-broker-core@1.6.1", + "/packages/spring-cql-1.5.11.RELEASE.jar": "pkg:maven/org.springframework.data/spring-cql@1.5.11.RELEASE", + "/packages/spring-credhub-core-1.0.1.RELEASE.jar": "pkg:maven/org.springframework.credhub/spring-credhub-core@1.0.1.RELEASE", + "/packages/spring-flex-1.0.3.RELEASE.jar": "pkg:maven/org.springframework.flex/spring-flex@1.0.3.RELEASE", + "/packages/spring-graphql-1.2.2.jar": "pkg:maven/org.springframework.graphql/spring-graphql@1.2.2", + "/packages/spring-hateoas-2.1.0.jar": "pkg:maven/org.springframework.hateoas/spring-hateoas@2.1.0", + "/packages/spring-integration-amqp-6.0.5.jar": "pkg:maven/org.springframework.integration/spring-integration-amqp@6.0.5", + "/packages/spring-kafka-3.0.9.jar": "pkg:maven/org.springframework.kafka/spring-kafka@3.0.9", + "/packages/spring-security-core-6.0.2.jar": "pkg:maven/org.springframework.security/spring-security-core@6.0.2", + "/packages/spring-security-kerberos-core-1.0.1.RELEASE.jar": "pkg:maven/org.springframework.security.kerberos/spring-security-kerberos-core@1.0.1.RELEASE", + "/packages/spring-security-oauth-2.5.1.RELEASE.jar": "pkg:maven/org.springframework.security.oauth/spring-security-oauth@2.5.1.RELEASE", + "/packages/spring-security-oauth2-autoconfigure-2.5.9.jar": "pkg:maven/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure@2.5.9", + "/packages/spring-security-saml2-core-1.0.10.RELEASE.jar": "pkg:maven/org.springframework.security.extensions/spring-security-saml2-core@1.0.10.RELEASE", + "/packages/spring-session-core-3.0.0.jar": "pkg:maven/org.springframework.session/spring-session-core@3.0.0", + "/packages/spring-social-core-1.1.2.RELEASE.jar": "pkg:maven/org.springframework.social/spring-social-core@1.1.2.RELEASE", + "/packages/spring-vault-core-3.0.1.jar": "pkg:maven/org.springframework.vault/spring-vault-core@3.0.1", + "/packages/spring-webmvc-3.1.4.RELEASE.jar": "pkg:maven/org.springframework/spring-webmvc@3.1.4.RELEASE", + "/packages/spring-xml-3.0.3.RELEASE.jar": "pkg:maven/org.springframework.ws/spring-xml@3.0.3.RELEASE", + "/packages/storm-core-1.2.2.jar:cheshire:cheshire": "pkg:maven/cheshire/cheshire@5.3.1", + "/packages/storm-core-1.2.2.jar:clj-time:clj-time": "pkg:maven/clj-time/clj-time@0.8.0", + "/packages/storm-core-1.2.2.jar:clout:clout": "pkg:maven/clout/clout@1.2.0", + "/packages/storm-core-1.2.2.jar:com.fasterxml.jackson.core:jackson-core": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.9.4", + "/packages/storm-core-1.2.2.jar:com.fasterxml.jackson.dataformat:jackson-dataformat-smile": "pkg:maven/com.fasterxml.jackson.dataformat/jackson-dataformat-smile@2.9.4", + "/packages/storm-core-1.2.2.jar:com.google.guava:guava": "pkg:maven/com.google.guava/guava@16.0.1", + "/packages/storm-core-1.2.2.jar:com.twitter:carbonite": "pkg:maven/com.twitter/carbonite@1.5.0", + "/packages/storm-core-1.2.2.jar:commons-codec:commons-codec": "pkg:maven/commons-codec/commons-codec@1.6", + "/packages/storm-core-1.2.2.jar:commons-collections:commons-collections": "pkg:maven/commons-collections/commons-collections@3.2.2", + "/packages/storm-core-1.2.2.jar:commons-fileupload:commons-fileupload": "pkg:maven/commons-fileupload/commons-fileupload@1.3.2", + "/packages/storm-core-1.2.2.jar:commons-io:commons-io": "pkg:maven/commons-io/commons-io@2.5", + "/packages/storm-core-1.2.2.jar:commons-lang:commons-lang": "pkg:maven/commons-lang/commons-lang@2.5", + "/packages/storm-core-1.2.2.jar:compojure:compojure": "pkg:maven/compojure/compojure@1.1.9", + "/packages/storm-core-1.2.2.jar:hiccup:hiccup": "pkg:maven/hiccup/hiccup@0.3.6", + "/packages/storm-core-1.2.2.jar:io.netty:netty": "pkg:maven/io.netty/netty@3.9.9.Final", + "/packages/storm-core-1.2.2.jar:joda-time:joda-time": "pkg:maven/joda-time/joda-time@2.3", + "/packages/storm-core-1.2.2.jar:metrics-clojure:metrics-clojure": "pkg:maven/metrics-clojure/metrics-clojure@2.5.1", + "/packages/storm-core-1.2.2.jar:ns-tracker:ns-tracker": "pkg:maven/ns-tracker/ns-tracker@0.2.2", + "/packages/storm-core-1.2.2.jar:org.apache.commons:commons-compress": "pkg:maven/org.apache.commons/commons-compress@1.4.1", + "/packages/storm-core-1.2.2.jar:org.apache.commons:commons-exec": "pkg:maven/org.apache.commons/commons-exec@1.1", + "/packages/storm-core-1.2.2.jar:org.apache.curator:curator-client": "pkg:maven/org.apache.curator/curator-client@4.0.1", + "/packages/storm-core-1.2.2.jar:org.apache.curator:curator-framework": "pkg:maven/org.apache.curator/curator-framework@4.0.1", + "/packages/storm-core-1.2.2.jar:org.apache.curator:curator-recipes": "pkg:maven/org.apache.curator/curator-recipes@4.0.1", + "/packages/storm-core-1.2.2.jar:org.apache.hadoop:hadoop-auth": "pkg:maven/org.apache.hadoop/hadoop-auth@2.6.1", + "/packages/storm-core-1.2.2.jar:org.apache.httpcomponents:httpclient": "pkg:maven/org.apache.httpcomponents/httpclient@4.3.3", + "/packages/storm-core-1.2.2.jar:org.apache.httpcomponents:httpcore": "pkg:maven/org.apache.httpcomponents/httpcore@4.4.1", + "/packages/storm-core-1.2.2.jar:org.clojure:java.jmx": "pkg:maven/org.clojure/java.jmx@0.3.1", + "/packages/storm-core-1.2.2.jar:org.clojure:math.numeric-tower": "pkg:maven/org.clojure/math.numeric-tower@0.0.1", + "/packages/storm-core-1.2.2.jar:org.clojure:tools.cli": "pkg:maven/org.clojure/tools.cli@0.2.4", + "/packages/storm-core-1.2.2.jar:org.clojure:tools.logging": "pkg:maven/org.clojure/tools.logging@0.2.3", + "/packages/storm-core-1.2.2.jar:org.clojure:tools.macro": "pkg:maven/org.clojure/tools.macro@0.1.0", + "/packages/storm-core-1.2.2.jar:org.clojure:tools.namespace": "pkg:maven/org.clojure/tools.namespace@0.2.4", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-client": "pkg:maven/org.eclipse.jetty/jetty-client@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-continuation": "pkg:maven/org.eclipse.jetty/jetty-continuation@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-http": "pkg:maven/org.eclipse.jetty/jetty-http@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-io": "pkg:maven/org.eclipse.jetty/jetty-io@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-security": "pkg:maven/org.eclipse.jetty/jetty-security@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-server": "pkg:maven/org.eclipse.jetty/jetty-server@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-servlet": "pkg:maven/org.eclipse.jetty/jetty-servlet@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-servlets": "pkg:maven/org.eclipse.jetty/jetty-servlets@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.eclipse.jetty:jetty-util": "pkg:maven/org.eclipse.jetty/jetty-util@7.6.13.v20130916", + "/packages/storm-core-1.2.2.jar:org.jgrapht:jgrapht-core": "pkg:maven/org.jgrapht/jgrapht-core@0.9.0", + "/packages/storm-core-1.2.2.jar:org.yaml:snakeyaml": "pkg:maven/org.yaml/snakeyaml@1.11", + "/packages/storm-core-1.2.2.jar:ring:ring-codec": "pkg:maven/ring/ring-codec@1.0.0", + "/packages/storm-core-1.2.2.jar:ring:ring-core": "pkg:maven/ring/ring-core@1.3.1", + "/packages/storm-core-1.2.2.jar:ring:ring-devel": "pkg:maven/ring/ring-devel@1.3.1", + "/packages/storm-core-1.2.2.jar:ring:ring-jetty-adapter": "pkg:maven/ring/ring-jetty-adapter@1.3.1", + "/packages/storm-core-1.2.2.jar:ring:ring-json": "pkg:maven/ring/ring-json@0.3.1", + "/packages/storm-core-1.2.2.jar:ring:ring-servlet": "pkg:maven/ring/ring-servlet@1.3.1", + "/packages/storm-core-1.2.2.jar": "pkg:maven/org.apache.storm/storm-core@1.2.2", + "/packages/tapestry-core-5.8.1.jar": "pkg:maven/org.apache.tapestry/tapestry-core@5.8.1", + "/packages/tika-core-1.26.jar": "pkg:maven/org.apache.tika/tika-core@1.26", + "/packages/tomcat-catalina-11.0.0-M1.jar": "pkg:maven/org.apache.tomcat/tomcat-catalina@11.0.0-M1", + "/packages/tomcat-embed-core-11.0.0-M7.jar": "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@11.0.0-M7", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/commons-codec-1.2.jar": "pkg:maven/commons-codec/commons-codec@1.2", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/commons-httpclient-3.1.jar": "pkg:maven/org.apache/commons-httpclient@3.1", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/commons-logging-1.0.4.jar": "pkg:maven/commons-logging/commons-logging@1.0.4", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/log4j-1.2.9.jar": "pkg:maven/log4j/log4j@1.2.9", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/sdk-3.0.jar:commons-codec:commons-codec": "pkg:maven/commons-codec/commons-codec@1.4", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/sdk-3.0.jar:commons-lang:commons-lang": "pkg:maven/commons-lang/commons-lang@2.5", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/sdk-3.0.jar:commons-logging:commons-logging": "pkg:maven/commons-logging/commons-logging@1.1.1", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/sdk-3.0.jar:org.apache.httpcomponents:httpclient": "pkg:maven/org.apache.httpcomponents/httpclient@4.1.1", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/sdk-3.0.jar:org.apache.httpcomponents:httpcore": "pkg:maven/org.apache.httpcomponents/httpcore@4.1", + "/packages/TwilioNotifier.hpi:WEB-INF/lib/sdk-3.0.jar:org.easymock:easymock": "pkg:maven/org.easymock/easymock@2.4", + "/packages/TwilioNotifier.hpi": "pkg:maven/com.twilio.jenkins/TwilioNotifier@0.2.1", + "/packages/undertow-core-2.3.4.Final.jar": "pkg:maven/io.undertow/undertow-core@2.3.4.Final", + "/packages/vaadin-client-8.11.3.jar": "pkg:maven/com.vaadin/vaadin-client@8.11.3", + "/packages/velocity-1.7.jar": "pkg:maven/org.apache.velocity/velocity@1.7", + "/packages/vertx-web-4.3.7.jar": "pkg:maven/io.vertx/vertx-web@4.3.7", + "/packages/wicket-core-9.2.0.jar": "pkg:maven/org.apache.wicket/wicket-core@9.2.0", + "/packages/xalan-2.7.2.jar": "pkg:maven/xalan/xalan@2.7.2", + "/packages/xmlpull-1.1.3.1.jar": "pkg:maven/xmlpull/xmlpull@1.1.3.1", + // "/packages/xpp3_min-1.1.4c.jar": "pkg:maven/xpp3/xpp3_min@1.1.4c", + "/packages/xstream-1.4.11.1.jar": "pkg:maven/com.thoughtworks.xstream/xstream@1.4.11.1", + "/packages/xstream-1.4.19.jar": "pkg:maven/com.thoughtworks.xstream/xstream@1.4.19", + "/packages/xwiki-commons-xml-15.1.jar": "pkg:maven/org.xwiki.commons/xwiki-commons-xml@15.1", + "/packages/xwiki-platform-oldcore-15.3.jar": "pkg:maven/org.xwiki.platform/xwiki-platform-oldcore@15.3", + "/packages/zookeeper-3.7.1.jar": "pkg:maven/org.apache.zookeeper/zookeeper@3.7.1", +} diff --git a/test/integration/license_list_test.go b/cmd/syft/internal/test/integration/license_list_test.go similarity index 100% rename from test/integration/license_list_test.go rename to cmd/syft/internal/test/integration/license_list_test.go diff --git a/test/integration/mariner_distroless_test.go b/cmd/syft/internal/test/integration/mariner_distroless_test.go similarity index 94% rename from test/integration/mariner_distroless_test.go rename to cmd/syft/internal/test/integration/mariner_distroless_test.go index 95c457cea84..5231f325427 100644 --- a/test/integration/mariner_distroless_test.go +++ b/cmd/syft/internal/test/integration/mariner_distroless_test.go @@ -8,7 +8,7 @@ import ( ) func TestMarinerDistroless(t *testing.T) { - sbom, _ := catalogFixtureImage(t, "image-mariner-distroless", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-mariner-distroless", source.SquashedScope) expectedPkgs := 12 actualPkgs := 0 diff --git a/test/integration/node_packages_test.go b/cmd/syft/internal/test/integration/node_packages_test.go similarity index 100% rename from test/integration/node_packages_test.go rename to cmd/syft/internal/test/integration/node_packages_test.go diff --git a/cmd/syft/internal/test/integration/package_cataloger_convention_test.go b/cmd/syft/internal/test/integration/package_cataloger_convention_test.go new file mode 100644 index 00000000000..4040dbf6833 --- /dev/null +++ b/cmd/syft/internal/test/integration/package_cataloger_convention_test.go @@ -0,0 +1,352 @@ +package integration + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "go/types" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/bmatcuk/doublestar/v4" + "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_packageCatalogerExports(t *testing.T) { + // sanity check that we are actually finding exports + + exports := packageCatalogerExports(t) + require.NotEmpty(t, exports) + + expectAtLeast := map[string]*strset.Set{ + "golang": strset.New("NewGoModuleFileCataloger", "NewGoModuleBinaryCataloger", "CatalogerConfig", "DefaultCatalogerConfig"), + } + + for pkg, expected := range expectAtLeast { + actual, ok := exports[pkg] + require.True(t, ok, pkg) + if !assert.True(t, actual.Names().IsSubset(expected), pkg) { + t.Logf("missing: %s", strset.SymmetricDifference(expected, actual.Names())) + } + } + +} + +func Test_validatePackageCatalogerExport(t *testing.T) { + cases := []struct { + name string + export exportToken + wantErr assert.ErrorAssertionFunc + }{ + // valid... + { + name: "valid constructor", + export: exportToken{ + Name: "NewFooCataloger", + Type: "*ast.FuncType", + SignatureSize: 1, + ReturnTypeNames: []string{ + "pkg.Cataloger", + }, + }, + }, + { + name: "valid default config", + export: exportToken{ + Name: "DefaultFooConfig", + Type: "*ast.FuncType", + SignatureSize: 0, + }, + }, + { + name: "valid config", + export: exportToken{ + Name: "FooConfig", + Type: "*ast.StructType", + }, + }, + // invalid... + { + name: "constructor that returns a concrete type", + export: exportToken{ + Name: "NewFooCataloger", + Type: "*ast.FuncType", + SignatureSize: 1, + ReturnTypeNames: []string{ + "*generic.Cataloger", + }, + }, + wantErr: assert.Error, + }, + { + name: "struct with constructor name", + export: exportToken{ + Name: "NewFooCataloger", + Type: "*ast.StructType", + }, + wantErr: assert.Error, + }, + { + name: "struct with default config fn name", + export: exportToken{ + Name: "DefaultFooConfig", + Type: "*ast.StructType", + }, + wantErr: assert.Error, + }, + { + name: "fn with struct name", + export: exportToken{ + Name: "FooConfig", + Type: "*ast.FuncType", + }, + wantErr: assert.Error, + }, + { + name: "default config with parameters", + export: exportToken{ + Name: "DefaultFooConfig", + Type: "*ast.FuncType", + SignatureSize: 1, + }, + wantErr: assert.Error, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + if c.wantErr == nil { + c.wantErr = assert.NoError + } + err := validatePackageCatalogerExport(t, "test", c.export) + c.wantErr(t, err) + }) + } +} + +func Test_PackageCatalogerConventions(t *testing.T) { + // look at each package in syft/pkg/cataloger... + // we want to make certain that only the following things are exported from the package: + // - function matching New*Cataloger (e.g. NewAptCataloger) + // - function matching Default*Config + // - struct matching *Config + // + // anything else that is exported should result in the test failing. + // note: this is meant to apply to things in static space, not methods on structs or within interfaces. + // + // this additionally ensures that: + // - any config struct has a Default*Config function to pair with it. + // - all cataloger constructors return pkg.Cataloger interface instead of a concrete type + + exportsPerPackage := packageCatalogerExports(t) + + //for debugging purposes... + //for pkg, exports := range exportsPerPackage { + // t.Log(pkg) + // for _, export := range exports { + // t.Logf(" %#v", export) + // } + //} + + for pkg, exports := range exportsPerPackage { + for _, export := range exports.List() { + // assert the export name is valid... + assert.NoError(t, validatePackageCatalogerExport(t, pkg, export)) + + // assert that config structs have a Default*Config functions to pair with them... + if strings.Contains(export.Name, "Config") && !strings.Contains(export.Name, "Default") { + // this is a config struct, make certain there is a pairing with a Default*Config function + assert.True(t, exports.Has("Default"+export.Name), "cataloger config struct %q in pkg %q must have a 'Default%s' function", export.Name, pkg, export.Name) + } + } + } +} + +func validatePackageCatalogerExport(t *testing.T, pkg string, export exportToken) error { + + constructorMatches, err := doublestar.Match("New*Cataloger", export.Name) + require.NoError(t, err) + + defaultConfigMatches, err := doublestar.Match("Default*Config", export.Name) + require.NoError(t, err) + + configMatches, err := doublestar.Match("*Config", export.Name) + require.NoError(t, err) + + switch { + case constructorMatches: + if !export.isFunction() { + return fmt.Errorf("constructor convention used for non-function in pkg=%q: %#v", pkg, export) + } + + returnTypes := strset.New(export.ReturnTypeNames...) + if !returnTypes.Has("pkg.Cataloger") { + return fmt.Errorf("constructor convention is to return pkg.Cataloger and not concrete types. pkg=%q constructor=%q types=%+v", pkg, export.Name, strings.Join(export.ReturnTypeNames, ",")) + } + + case defaultConfigMatches: + if !export.isFunction() { + return fmt.Errorf("default config convention used for non-function in pkg=%q: %#v", pkg, export) + } + if export.SignatureSize != 0 { + return fmt.Errorf("default config convention used for non-zero signature size in pkg=%q: %#v", pkg, export) + } + case configMatches: + if !export.isStruct() { + return fmt.Errorf("config convention used for non-struct in pkg=%q: %#v", pkg, export) + } + default: + return fmt.Errorf("unexpected export in pkg=%q: %#v", pkg, export) + } + return nil +} + +type exportToken struct { + Name string + Type string + SignatureSize int + ReturnTypeNames []string +} + +func (e exportToken) isFunction() bool { + return strings.Contains(e.Type, "ast.FuncType") +} + +func (e exportToken) isStruct() bool { + return strings.Contains(e.Type, "ast.StructType") +} + +type exportTokenSet map[string]exportToken + +func (s exportTokenSet) Names() *strset.Set { + set := strset.New() + for k := range s { + set.Add(k) + } + return set +} + +func (s exportTokenSet) Has(name string) bool { + _, ok := s[name] + return ok +} + +func (s exportTokenSet) Add(tokens ...exportToken) { + for _, t := range tokens { + if _, ok := s[t.Name]; ok { + panic("duplicate token name: " + t.Name) + } + s[t.Name] = t + } +} + +func (s exportTokenSet) Remove(names ...string) { + for _, name := range names { + delete(s, name) + } +} + +func (s exportTokenSet) List() []exportToken { + var tokens []exportToken + for _, t := range s { + tokens = append(tokens, t) + } + return tokens +} + +func packageCatalogerExports(t *testing.T) map[string]exportTokenSet { + t.Helper() + + catalogerPath := filepath.Join(repoRoot(t), "syft", "pkg", "cataloger") + + ignorePaths := []string{ + filepath.Join(catalogerPath, "common"), + filepath.Join(catalogerPath, "generic"), + } + + exportsPerPackage := make(map[string]exportTokenSet) + + err := filepath.Walk(catalogerPath, func(path string, info os.FileInfo, err error) error { + require.NoError(t, err) + + if info.IsDir() || + !strings.HasSuffix(info.Name(), ".go") || + strings.HasSuffix(info.Name(), "_test.go") || + strings.Contains(path, "test-fixtures") || + strings.Contains(path, "internal") { + return nil + } + + for _, ignorePath := range ignorePaths { + if strings.Contains(path, ignorePath) { + return nil + } + } + + fset := token.NewFileSet() + node, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + require.NoError(t, err) + + pkg := node.Name.Name + for _, f := range node.Decls { + switch decl := f.(type) { + case *ast.GenDecl: + for _, spec := range decl.Specs { + switch spec := spec.(type) { + case *ast.TypeSpec: + if spec.Name.IsExported() { + if _, ok := exportsPerPackage[pkg]; !ok { + exportsPerPackage[pkg] = make(exportTokenSet) + } + exportsPerPackage[pkg].Add(exportToken{ + Name: spec.Name.Name, + Type: reflect.TypeOf(spec.Type).String(), + }) + } + } + } + case *ast.FuncDecl: + if decl.Recv == nil && decl.Name.IsExported() { + var returnTypes []string + if decl.Type.Results != nil { + for _, field := range decl.Type.Results.List { + // TODO: there is probably a better way to extract the specific type name + //ty := strings.Join(strings.Split(fmt.Sprint(field.Type), " "), ".") + ty := types.ExprString(field.Type) + + returnTypes = append(returnTypes, ty) + } + } + + if _, ok := exportsPerPackage[pkg]; !ok { + exportsPerPackage[pkg] = make(exportTokenSet) + } + exportsPerPackage[pkg].Add(exportToken{ + Name: decl.Name.Name, + Type: reflect.TypeOf(decl.Type).String(), + SignatureSize: len(decl.Type.Params.List), + ReturnTypeNames: returnTypes, + }) + } + } + } + + return nil + }) + + require.NoError(t, err) + + // remove exceptions + // these are known violations to the common convention that are allowed. + if vs, ok := exportsPerPackage["binary"]; ok { + vs.Remove("Classifier", "EvidenceMatcher", "FileContentsVersionMatcher", "DefaultClassifiers") + } + + return exportsPerPackage +} diff --git a/cmd/syft/internal/test/integration/package_catalogers_represented_test.go b/cmd/syft/internal/test/integration/package_catalogers_represented_test.go new file mode 100644 index 00000000000..2375c429093 --- /dev/null +++ b/cmd/syft/internal/test/integration/package_catalogers_represented_test.go @@ -0,0 +1,204 @@ +package integration + +import ( + "bytes" + "go/ast" + "go/parser" + "go/token" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" +) + +func TestAllPackageCatalogersReachableInTasks(t *testing.T) { + // we want to see if we can get a task for all package catalogers. This is a bit tricky since we + // don't have a nice way to find all cataloger names in the codebase. Instead, we'll look at the + // count of unique task names from the package task factory set and compare that with the known constructors + // from a source analysis... they should match. + + // additionally, at this time they should either have a "directory" or "image" tag as well. If there is no tag + // on a cataloger task then the test should fail. + + taskFactories := task.DefaultPackageTaskFactories() + taskTagsByName := make(map[string][]string) + for _, factory := range taskFactories { + tsk := factory(task.DefaultCatalogingFactoryConfig()) + if taskTagsByName[tsk.Name()] != nil { + t.Fatalf("duplicate task name: %q", tsk.Name()) + } + + require.NotNil(t, tsk) + if sel, ok := tsk.(task.Selector); ok { + taskTagsByName[tsk.Name()] = sel.Selectors() + } else { + taskTagsByName[tsk.Name()] = []string{} + } + } + + var constructorCount int + constructorsPerPackage := getCatalogerConstructors(t) + for _, constructors := range constructorsPerPackage { + constructorCount += constructors.Size() + } + + assert.Equal(t, len(taskTagsByName), constructorCount, "mismatch in number of cataloger constructors and task names") + + for taskName, tags := range taskTagsByName { + if taskName == "sbom-cataloger" { + continue // this is a special case + } + if !strset.New(tags...).HasAny(pkgcataloging.ImageTag, pkgcataloging.DirectoryTag) { + t.Errorf("task %q is missing 'directory' or 'image' a tag", taskName) + } + } + +} + +func TestAllPackageCatalogersRepresentedInSource(t *testing.T) { + // find all functions in syft/pkg/cataloger/** that either: + // - match the name glob "New*Cataloger" + // - are in cataloger.go and match the name glob "New*" + // + // Then: + // - keep track of all packages with cataloger constructors + // - keep track of all constructors + constructorsPerPackage := getCatalogerConstructors(t) + + // look at the source file in internal/task/package_tasks.go: + // - ensure all go packages that have constructors are imported + // - ensure there is a reference to all package constructors + assertAllPackageCatalogersRepresented(t, constructorsPerPackage) +} + +func getCatalogerConstructors(t *testing.T) map[string]*strset.Set { + t.Helper() + root := repoRoot(t) + catalogerPath := filepath.Join(root, "syft", "pkg", "cataloger") + + constructorsPerPackage := make(map[string]*strset.Set) + + err := filepath.Walk(catalogerPath, func(path string, info os.FileInfo, err error) error { + require.NoError(t, err) + + // ignore directories and test files... + if info.IsDir() || strings.HasSuffix(info.Name(), "_test.go") { + return nil + } + + partialResults := getConstructorsFromExpectedFile(t, path, info) + + constructorsPerPackage = mergeConstructors(constructorsPerPackage, partialResults) + + partialResults = getCatalogerConstructorsFromPackage(t, path, info) + + constructorsPerPackage = mergeConstructors(constructorsPerPackage, partialResults) + + return nil + }) + + require.NoError(t, err) + + // remove some exceptions + delete(constructorsPerPackage, "generic") // this is not an actual cataloger + + return constructorsPerPackage +} + +func getConstructorsFromExpectedFile(t *testing.T, path string, info os.FileInfo) map[string][]string { + constructorsPerPackage := make(map[string][]string) + + if !strings.HasSuffix(info.Name(), "cataloger.go") && !strings.HasSuffix(info.Name(), "catalogers.go") { + return nil + } + + fset := token.NewFileSet() + node, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + require.NoError(t, err) + + for _, f := range node.Decls { + fn, ok := f.(*ast.FuncDecl) + if !ok || fn.Recv != nil || !strings.HasPrefix(fn.Name.Name, "New") { + continue + } + + pkg := node.Name.Name + constructorsPerPackage[pkg] = append(constructorsPerPackage[pkg], fn.Name.Name) + } + + return constructorsPerPackage +} + +func getCatalogerConstructorsFromPackage(t *testing.T, path string, info os.FileInfo) map[string][]string { + constructorsPerPackage := make(map[string][]string) + + if info.IsDir() || !strings.HasSuffix(info.Name(), ".go") { + return nil + } + + fset := token.NewFileSet() + node, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + require.NoError(t, err) + + for _, f := range node.Decls { + fn, ok := f.(*ast.FuncDecl) + if !ok || fn.Recv != nil || !strings.HasPrefix(fn.Name.Name, "New") || !strings.HasSuffix(fn.Name.Name, "Cataloger") { + continue + } + + pkg := node.Name.Name + constructorsPerPackage[pkg] = append(constructorsPerPackage[pkg], fn.Name.Name) + } + + return constructorsPerPackage +} + +func assertAllPackageCatalogersRepresented(t *testing.T, constructorsPerPackage map[string]*strset.Set) { + t.Helper() + + contents, err := os.ReadFile(filepath.Join(repoRoot(t), "internal", "task", "package_tasks.go")) + require.NoError(t, err) + + // ensure all packages (keys) are represented in the package_tasks.go file + for pkg, constructors := range constructorsPerPackage { + if !assert.True(t, bytes.Contains(contents, []byte(pkg)), "missing package %q", pkg) { + continue + } + for _, constructor := range constructors.List() { + assert.True(t, bytes.Contains(contents, []byte(constructor)), "missing constructor %q for package %q", constructor, pkg) + } + } + +} + +func repoRoot(t testing.TB) string { + t.Helper() + root, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() + if err != nil { + t.Fatalf("unable to find repo root dir: %+v", err) + } + absRepoRoot, err := filepath.Abs(strings.TrimSpace(string(root))) + if err != nil { + t.Fatal("unable to get abs path to repo root:", err) + } + return absRepoRoot +} + +func mergeConstructors(constructorsPerPackage map[string]*strset.Set, partialResults map[string][]string) map[string]*strset.Set { + for pkg, constructors := range partialResults { + if _, ok := constructorsPerPackage[pkg]; !ok { + constructorsPerPackage[pkg] = strset.New() + } + constructorsPerPackage[pkg].Add(constructors...) + } + + return constructorsPerPackage +} diff --git a/test/integration/package_deduplication_test.go b/cmd/syft/internal/test/integration/package_deduplication_test.go similarity index 99% rename from test/integration/package_deduplication_test.go rename to cmd/syft/internal/test/integration/package_deduplication_test.go index 75c86234e44..12fa9dcf207 100644 --- a/test/integration/package_deduplication_test.go +++ b/cmd/syft/internal/test/integration/package_deduplication_test.go @@ -63,7 +63,7 @@ func TestPackageDeduplication(t *testing.T) { for _, tt := range tests { t.Run(string(tt.scope), func(t *testing.T) { - sbom, _ := catalogFixtureImage(t, "image-vertical-package-dups", tt.scope, nil) + sbom, _ := catalogFixtureImage(t, "image-vertical-package-dups", tt.scope) for _, p := range sbom.Artifacts.Packages.Sorted() { if p.Type == pkg.BinaryPkg { assert.NotEmpty(t, p.Name) diff --git a/test/integration/package_ownership_relationship_test.go b/cmd/syft/internal/test/integration/package_ownership_relationship_test.go similarity index 98% rename from test/integration/package_ownership_relationship_test.go rename to cmd/syft/internal/test/integration/package_ownership_relationship_test.go index d3ce0745404..dc323ce1060 100644 --- a/test/integration/package_ownership_relationship_test.go +++ b/cmd/syft/internal/test/integration/package_ownership_relationship_test.go @@ -25,7 +25,7 @@ func TestPackageOwnershipRelationships(t *testing.T) { for _, test := range tests { t.Run(test.fixture, func(t *testing.T) { - sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope) output := bytes.NewBufferString("") err := syftjson.NewFormatEncoder().Encode(output, sbom) @@ -60,7 +60,7 @@ func TestPackageOwnershipExclusions(t *testing.T) { for _, test := range tests { t.Run(test.fixture, func(t *testing.T) { - sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope) binaryPackages := make([]pkg.Package, 0) apkPackages := make([]pkg.Package, 0) for p := range sbom.Artifacts.Packages.Enumerate() { diff --git a/test/integration/regression_apk_scanner_buffer_size_test.go b/cmd/syft/internal/test/integration/regression_apk_scanner_buffer_size_test.go similarity index 96% rename from test/integration/regression_apk_scanner_buffer_size_test.go rename to cmd/syft/internal/test/integration/regression_apk_scanner_buffer_size_test.go index 3549d52ee14..b0f61232ea7 100644 --- a/test/integration/regression_apk_scanner_buffer_size_test.go +++ b/cmd/syft/internal/test/integration/regression_apk_scanner_buffer_size_test.go @@ -10,7 +10,7 @@ import ( func TestRegression212ApkBufferSize(t *testing.T) { // This is a regression test for issue #212 (https://github.com/anchore/syft/issues/212) in which the apk db could // not be processed due to a scanner buffer that was too small - sbom, _ := catalogFixtureImage(t, "image-large-apk-data", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-large-apk-data", source.SquashedScope) expectedPkgs := 58 actualPkgs := 0 diff --git a/test/integration/regression_go_bin_scanner_arch_test.go b/cmd/syft/internal/test/integration/regression_go_bin_scanner_arch_test.go similarity index 97% rename from test/integration/regression_go_bin_scanner_arch_test.go rename to cmd/syft/internal/test/integration/regression_go_bin_scanner_arch_test.go index d88ee6c7c6c..8a5d231884e 100644 --- a/test/integration/regression_go_bin_scanner_arch_test.go +++ b/cmd/syft/internal/test/integration/regression_go_bin_scanner_arch_test.go @@ -16,7 +16,7 @@ func TestRegressionGoArchDiscovery(t *testing.T) { ) // This is a regression test to make sure the way we detect go binary packages // stays consistent and reproducible as the tool chain evolves - sbom, _ := catalogFixtureImage(t, "image-go-bin-arch-coverage", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-go-bin-arch-coverage", source.SquashedScope) var actualELF, actualWIN, actualMACOS int diff --git a/test/integration/regression_java_no_main_package_test.go b/cmd/syft/internal/test/integration/regression_java_no_main_package_test.go similarity index 93% rename from test/integration/regression_java_no_main_package_test.go rename to cmd/syft/internal/test/integration/regression_java_no_main_package_test.go index 102271ec8c9..31a755efa36 100644 --- a/test/integration/regression_java_no_main_package_test.go +++ b/cmd/syft/internal/test/integration/regression_java_no_main_package_test.go @@ -7,5 +7,5 @@ import ( ) func TestRegressionJavaNoMainPackage(t *testing.T) { // Regression: https://github.com/anchore/syft/issues/252 - catalogFixtureImage(t, "image-java-no-main-package", source.SquashedScope, nil) + catalogFixtureImage(t, "image-java-no-main-package", source.SquashedScope) } diff --git a/test/integration/regression_java_virtualpath_test.go b/cmd/syft/internal/test/integration/regression_java_virtualpath_test.go similarity index 97% rename from test/integration/regression_java_virtualpath_test.go rename to cmd/syft/internal/test/integration/regression_java_virtualpath_test.go index d9d100fa65e..a4685602e0e 100644 --- a/test/integration/regression_java_virtualpath_test.go +++ b/cmd/syft/internal/test/integration/regression_java_virtualpath_test.go @@ -12,7 +12,7 @@ import ( func TestWarCatalogedCorrectlyIfRenamed(t *testing.T) { // install hudson-war@2.2.1 and renames the file to `/hudson.war` - sbom, _ := catalogFixtureImage(t, "image-java-virtualpath-regression", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-java-virtualpath-regression", source.SquashedScope) badPURL := "pkg:maven/hudson/hudson@2.2.1" goodPURL := "pkg:maven/org.jvnet.hudson.main/hudson-war@2.2.1" diff --git a/test/integration/regression_photon_package_test.go b/cmd/syft/internal/test/integration/regression_photon_package_test.go similarity index 95% rename from test/integration/regression_photon_package_test.go rename to cmd/syft/internal/test/integration/regression_photon_package_test.go index dc8360a8efd..6d9aaf322b2 100644 --- a/test/integration/regression_photon_package_test.go +++ b/cmd/syft/internal/test/integration/regression_photon_package_test.go @@ -8,7 +8,7 @@ import ( ) func TestPhotonPackageRegression(t *testing.T) { // Regression: https://github.com/anchore/syft/pull/1997 - sbom, _ := catalogFixtureImage(t, "image-photon-all-layers", source.AllLayersScope, nil) + sbom, _ := catalogFixtureImage(t, "image-photon-all-layers", source.AllLayersScope) var packages []pkg.Package for p := range sbom.Artifacts.Packages.Enumerate() { packages = append(packages, p) diff --git a/cmd/syft/internal/test/integration/regression_sbom_duplicate_relationships_test.go b/cmd/syft/internal/test/integration/regression_sbom_duplicate_relationships_test.go new file mode 100644 index 00000000000..1a093cea71d --- /dev/null +++ b/cmd/syft/internal/test/integration/regression_sbom_duplicate_relationships_test.go @@ -0,0 +1,26 @@ +package integration + +import ( + "fmt" + "testing" + + "github.com/scylladb/go-set/strset" + + "github.com/anchore/syft/syft/source" +) + +func TestRelationshipsUnique(t *testing.T) { + // This test is to ensure that the relationships are deduplicated in the final SBOM. + // It is not a test of the relationships themselves. + // This test is a regression test for #syft/2509 + sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope) + observedRelationships := strset.New() + + for _, rel := range sbom.Relationships { + unique := fmt.Sprintf("%s:%s:%s", rel.From.ID(), rel.To.ID(), rel.Type) + if observedRelationships.Has(unique) { + t.Errorf("duplicate relationship found: %s", unique) + } + observedRelationships.Add(unique) + } +} diff --git a/test/integration/rust_audit_binary_test.go b/cmd/syft/internal/test/integration/rust_audit_binary_test.go similarity index 93% rename from test/integration/rust_audit_binary_test.go rename to cmd/syft/internal/test/integration/rust_audit_binary_test.go index 57baf46af36..5a61055e5e9 100644 --- a/test/integration/rust_audit_binary_test.go +++ b/cmd/syft/internal/test/integration/rust_audit_binary_test.go @@ -8,7 +8,7 @@ import ( ) func TestRustAudit(t *testing.T) { - sbom, _ := catalogFixtureImage(t, "image-rust-auditable", source.SquashedScope, []string{"all"}) + sbom, _ := catalogFixtureImage(t, "image-rust-auditable", source.SquashedScope) expectedPkgs := 2 actualPkgs := 0 diff --git a/cmd/syft/internal/test/integration/sbom_cataloger_test.go b/cmd/syft/internal/test/integration/sbom_cataloger_test.go new file mode 100644 index 00000000000..ef538ac6bc0 --- /dev/null +++ b/cmd/syft/internal/test/integration/sbom_cataloger_test.go @@ -0,0 +1,50 @@ +package integration + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +func TestSbomCataloger(t *testing.T) { + assertCount := func(t *testing.T, sbom sbom.SBOM, expectedGoModCatalogerPkgs int, expectedSbomCatalogerPkgs int) { + actualSbomPkgs := 0 + actualGoModPkgs := 0 + + for p := range sbom.Artifacts.Packages.Enumerate(pkg.GoModulePkg) { + if p.FoundBy == "go-module-file-cataloger" { + actualGoModPkgs += 1 + } else if p.FoundBy == "sbom-cataloger" { + actualSbomPkgs += 1 + } + } + + if actualGoModPkgs != expectedGoModCatalogerPkgs { + t.Errorf("unexpected number of packages from go mod cataloger: %d != %d", expectedGoModCatalogerPkgs, actualGoModPkgs) + } + if actualSbomPkgs != expectedSbomCatalogerPkgs { + t.Errorf("unexpected number of packages from sbom cataloger: %d != %d", expectedSbomCatalogerPkgs, actualSbomPkgs) + } + } + + t.Run("default catalogers", func(t *testing.T) { + sbom, _ := catalogFixtureImage(t, "image-sbom-cataloger", source.SquashedScope, "+go-module-file-cataloger") + + expectedSbomCatalogerPkgs := 0 + expectedGoModCatalogerPkgs := 2 + assertCount(t, sbom, expectedGoModCatalogerPkgs, expectedSbomCatalogerPkgs) + }) + + // The image contains a go.mod file with 2 dependencies and an spdx json sbom. + // The go.mod file contains 2 dependencies, and the sbom includes a go dependency + // that overlaps with the go.mod + t.Run("with sbom cataloger", func(t *testing.T) { + sbom, _ := catalogFixtureImage(t, "image-sbom-cataloger", source.SquashedScope, "+go-module-file-cataloger", "+sbom-cataloger") + + expectedSbomCatalogerPkgs := 1 + expectedGoModCatalogerPkgs := 2 + assertCount(t, sbom, expectedGoModCatalogerPkgs, expectedSbomCatalogerPkgs) + }) +} diff --git a/test/integration/sqlite_rpmdb_test.go b/cmd/syft/internal/test/integration/sqlite_rpmdb_test.go similarity index 97% rename from test/integration/sqlite_rpmdb_test.go rename to cmd/syft/internal/test/integration/sqlite_rpmdb_test.go index fd3dfa98a01..37f21cbba46 100644 --- a/test/integration/sqlite_rpmdb_test.go +++ b/cmd/syft/internal/test/integration/sqlite_rpmdb_test.go @@ -12,7 +12,7 @@ import ( func TestSqliteRpm(t *testing.T) { // This is a regression test for issue #469 (https://github.com/anchore/syft/issues/469). Recent RPM // based distribution store package data in an sqlite database - sbom, _ := catalogFixtureImage(t, "image-sqlite-rpmdb", source.SquashedScope, nil) + sbom, _ := catalogFixtureImage(t, "image-sqlite-rpmdb", source.SquashedScope) expectedPkgs := 139 actualPkgs := 0 diff --git a/test/integration/test-fixtures/.gitignore b/cmd/syft/internal/test/integration/test-fixtures/.gitignore similarity index 93% rename from test/integration/test-fixtures/.gitignore rename to cmd/syft/internal/test/integration/test-fixtures/.gitignore index 68785602119..ea642e713f9 100644 --- a/test/integration/test-fixtures/.gitignore +++ b/cmd/syft/internal/test/integration/test-fixtures/.gitignore @@ -5,3 +5,6 @@ # functionality), committing it seems like an acceptable exception. !image-pkg-coverage/pkgs/java/*.jar !image-pkg-coverage/pkgs/java/*.hpi + +**/go.sum +!image-go-bin-arch-coverage/go.sum \ No newline at end of file diff --git a/test/integration/test-fixtures/Makefile b/cmd/syft/internal/test/integration/test-fixtures/Makefile similarity index 100% rename from test/integration/test-fixtures/Makefile rename to cmd/syft/internal/test/integration/test-fixtures/Makefile diff --git a/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-small-file b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-small-file new file mode 100644 index 00000000000..50c7fcb078b --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-small-file @@ -0,0 +1 @@ +some contents! \ No newline at end of file diff --git a/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-symlink-to-a-small-file b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-symlink-to-a-small-file new file mode 120000 index 00000000000..2255208c262 --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-symlink-to-a-small-file @@ -0,0 +1 @@ +./a-small-file \ No newline at end of file diff --git a/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-symlink-to-file b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-symlink-to-file new file mode 120000 index 00000000000..064ba9f0443 --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a-symlink-to-file @@ -0,0 +1 @@ +./a/file \ No newline at end of file diff --git a/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a/file b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a/file new file mode 100644 index 00000000000..2ebc965e0d7 --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/files/somewhere/there/is/a/file @@ -0,0 +1,10 @@ +with a lot of contents! +with a lot of contents! +with a lot of contents! +with a lot of contents! +with a lot of contents! +with a lot of contents! +with a lot of contents! +with a lot of contents! +with a lot of contents! +with a lot of contents! \ No newline at end of file diff --git a/test/integration/test-fixtures/image-distro-id/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-distro-id/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-distro-id/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-distro-id/Dockerfile diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/app.go b/cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/app.go similarity index 100% rename from test/integration/test-fixtures/image-go-bin-arch-coverage/app.go rename to cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/app.go diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/go.mod b/cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/go.mod similarity index 100% rename from test/integration/test-fixtures/image-go-bin-arch-coverage/go.mod rename to cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/go.mod diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/go.sum b/cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/go.sum similarity index 100% rename from test/integration/test-fixtures/image-go-bin-arch-coverage/go.sum rename to cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/go.sum diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/test b/cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/test similarity index 100% rename from test/integration/test-fixtures/image-go-bin-arch-coverage/test rename to cmd/syft/internal/test/integration/test-fixtures/image-go-bin-arch-coverage/test diff --git a/test/integration/test-fixtures/image-golang-compiler/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-golang-compiler/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-golang-compiler/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-golang-compiler/Dockerfile diff --git a/test/integration/test-fixtures/image-java-no-main-package/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-java-no-main-package/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-java-no-main-package/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-java-no-main-package/Dockerfile diff --git a/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile diff --git a/test/integration/test-fixtures/image-large-apk-data/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-large-apk-data/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-large-apk-data/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-large-apk-data/Dockerfile diff --git a/test/integration/test-fixtures/image-mariner-distroless/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-mariner-distroless/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-mariner-distroless/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-mariner-distroless/Dockerfile diff --git a/test/integration/test-fixtures/image-os-binary-overlap/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-os-binary-overlap/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-os-binary-overlap/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-os-binary-overlap/Dockerfile diff --git a/test/integration/test-fixtures/image-owning-package/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-owning-package/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-owning-package/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-owning-package/Dockerfile diff --git a/test/integration/test-fixtures/image-photon-all-layers/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-photon-all-layers/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-photon-all-layers/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-photon-all-layers/Dockerfile diff --git a/test/integration/test-fixtures/image-pkg-coverage/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/Dockerfile diff --git a/test/integration/test-fixtures/image-pkg-coverage/cocoapods/Podfile.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/cocoapods/Podfile.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/cocoapods/Podfile.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/cocoapods/Podfile.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/composer/composer.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/composer/composer.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/composer/composer.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/composer/composer.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/conan/conanfile.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/conan/conanfile.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/conan/conanfile.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/conan/conanfile.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/etc/os-release b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/etc/os-release similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/etc/os-release rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/etc/os-release diff --git a/test/integration/test-fixtures/image-pkg-coverage/hackage/cabal.project.freeze b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/hackage/cabal.project.freeze similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/hackage/cabal.project.freeze rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/hackage/cabal.project.freeze diff --git a/test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml diff --git a/test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/hackage/stack.yaml.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/lib/apk/db/installed b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/lib/apk/db/installed similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/lib/apk/db/installed rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/lib/apk/db/installed diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/dart/pubspec.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/dart/pubspec.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/dart/pubspec.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/dart/pubspec.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/elixir/mix.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/elixir/mix.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/elixir/mix.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/elixir/mix.lock diff --git a/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/accept.app b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/accept.app new file mode 100644 index 00000000000..a2b960a7684 --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/accept.app @@ -0,0 +1,10 @@ +{application,accept, + [{description,"Accept header(s) for Erlang/Elixir"}, + {vsn,"0.3.5"}, + {registered,[]}, + {applications,[kernel,stdlib]}, + {env,[]}, + {modules, ['accept_encoding_header','accept_header','accept_neg','accept_parser']}, + {maintainers,["Ilya Khaprov"]}, + {licenses,["MIT"]}, + {links,[{"Github","https://github.com/deadtrickster/accept"}]}]}. diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/rebar.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/rebar.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/rebar.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/rebar.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/go/go.mod b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/go/go.mod similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/go/go.mod rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/go/go.mod diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-java-app-maven-0.1.0.jar b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-java-app-maven-0.1.0.jar similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-java-app-maven-0.1.0.jar rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-java-app-maven-0.1.0.jar diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-jenkins-plugin.hpi b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-jenkins-plugin.hpi similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-jenkins-plugin.hpi rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/example-jenkins-plugin.hpi diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/generate-fixtures.md b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/generate-fixtures.md similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/java/generate-fixtures.md rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/generate-fixtures.md diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/pom.xml b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/pom.xml similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/java/pom.xml rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/java/pom.xml diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-json/package.json b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-json/package.json similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-json/package.json rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-json/package.json diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-lock/package-lock.json b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-lock/package-lock.json similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-lock/package-lock.json rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/package-lock/package-lock.json diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/yarn/yarn.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/yarn/yarn.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/yarn/yarn.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/javascript/yarn/yarn.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/lib/apk/db/installed b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/lib/apk/db/installed similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/lib/apk/db/installed rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/lib/apk/db/installed diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man/glibc.1 b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man/glibc.1 similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man/glibc.1 rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man/glibc.1 diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/php/vendor/composer/installed.json b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/php/vendor/composer/installed.json similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/php/vendor/composer/installed.json rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/php/vendor/composer/installed.json diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/METADATA b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/METADATA similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/METADATA rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/METADATA diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/RECORD b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/RECORD similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/RECORD rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/RECORD diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/top_level.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/top_level.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/top_level.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/dist-info/top_level.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/PKG-INFO b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/PKG-INFO similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/PKG-INFO rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/PKG-INFO diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/top_level.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/top_level.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/top_level.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/egg-info/top_level.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements-dev.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements-dev.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements-dev.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements-dev.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/requirements.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/test-requirements.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/test-requirements.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/test-requirements.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/requires/test-requirements.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/setup/setup.py b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/setup/setup.py similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/setup/setup.py rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/setup/setup.py diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/PKG-INFO b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/PKG-INFO similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/PKG-INFO rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/PKG-INFO diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/top_level.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/top_level.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/top_level.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/someotherpkg-3.19.0-py3.8.egg-info/top_level.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/METADATA b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/METADATA similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/METADATA rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/METADATA diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/top_level.txt b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/top_level.txt similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/top_level.txt rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/python/somerequests-3.22.0.dist-info/top_level.txt diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/Gemfile.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/Gemfile.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/Gemfile.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/Gemfile.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/bundler.gemspec b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/bundler.gemspec similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/bundler.gemspec rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/bundler.gemspec diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/default/unbundler.gemspec b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/default/unbundler.gemspec similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/default/unbundler.gemspec rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/ruby/specifications/default/unbundler.gemspec diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/rust/Cargo.lock b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/rust/Cargo.lock similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/rust/Cargo.lock rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/rust/Cargo.lock diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/CONTENTS b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/CONTENTS similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/CONTENTS rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/CONTENTS diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/LICENSE similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/LICENSE diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/SIZE b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/SIZE similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/SIZE rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/db/pkg/app-containers/skopeo-1.5.1/SIZE diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/dash b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/dash similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/dash rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/dash diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/netbase b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/netbase similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/netbase rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/dpkg/status.d/netbase diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/ALPM_DB_VERSION b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/ALPM_DB_VERSION similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/ALPM_DB_VERSION rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/ALPM_DB_VERSION diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/desc b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/desc similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/desc rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/desc diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/files b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/files similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/files rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/files diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/mtree b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/mtree similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/mtree rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/pacman/local/pacman-6.0.1-5/mtree diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/Packages b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/Packages similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/Packages rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/Packages diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/generate-fixture.sh b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/generate-fixture.sh similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/generate-fixture.sh rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/var/lib/rpm/generate-fixture.sh diff --git a/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/wordpress/wp-content/plugins/akismet/akismet.php b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/wordpress/wp-content/plugins/akismet/akismet.php new file mode 100644 index 00000000000..f70330b46d2 --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/wordpress/wp-content/plugins/akismet/akismet.php @@ -0,0 +1,17 @@ +protect your blog from spam. Akismet Anti-spam keeps your site protected even while you sleep. To get started: activate the Akismet plugin and then go to your Akismet Settings page to set up your API key. +Version: 5.3 +Requires at least: 5.8 +Requires PHP: 5.6.20 +Author: Automattic - Anti-spam Team +Author URI: https://automattic.com/wordpress-plugins/ +License: GPLv2 or later +Text Domain: akismet +*/ +// rest of plugin's code ... \ No newline at end of file diff --git a/test/integration/test-fixtures/image-pkg-coverage/swift/Package.resolved b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/swift/Package.resolved similarity index 100% rename from test/integration/test-fixtures/image-pkg-coverage/swift/Package.resolved rename to cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/swift/Package.resolved diff --git a/test/integration/test-fixtures/image-rust-auditable/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-rust-auditable/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-rust-auditable/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-rust-auditable/Dockerfile diff --git a/test/integration/test-fixtures/image-sbom-cataloger/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-sbom-cataloger/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-sbom-cataloger/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-sbom-cataloger/Dockerfile diff --git a/test/integration/test-fixtures/image-sbom-cataloger/go.mod b/cmd/syft/internal/test/integration/test-fixtures/image-sbom-cataloger/go.mod similarity index 100% rename from test/integration/test-fixtures/image-sbom-cataloger/go.mod rename to cmd/syft/internal/test/integration/test-fixtures/image-sbom-cataloger/go.mod diff --git a/test/integration/test-fixtures/image-sbom-cataloger/test.spdx.json b/cmd/syft/internal/test/integration/test-fixtures/image-sbom-cataloger/test.spdx.json similarity index 100% rename from test/integration/test-fixtures/image-sbom-cataloger/test.spdx.json rename to cmd/syft/internal/test/integration/test-fixtures/image-sbom-cataloger/test.spdx.json diff --git a/test/integration/test-fixtures/image-sqlite-rpmdb/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-sqlite-rpmdb/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-sqlite-rpmdb/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-sqlite-rpmdb/Dockerfile diff --git a/test/integration/test-fixtures/image-suse-all-layers/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-suse-all-layers/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-suse-all-layers/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-suse-all-layers/Dockerfile diff --git a/cmd/syft/internal/test/integration/test-fixtures/image-test-java-purls/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-test-java-purls/Dockerfile new file mode 100644 index 00000000000..9d9b9304164 --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/image-test-java-purls/Dockerfile @@ -0,0 +1 @@ +FROM anchore/test_images:java-1abc58f@sha256:3add9f90e9ed35739cc99b7830767e09eec921052e2412adf4491648c741f066 diff --git a/test/integration/test-fixtures/image-vertical-package-dups/Dockerfile b/cmd/syft/internal/test/integration/test-fixtures/image-vertical-package-dups/Dockerfile similarity index 100% rename from test/integration/test-fixtures/image-vertical-package-dups/Dockerfile rename to cmd/syft/internal/test/integration/test-fixtures/image-vertical-package-dups/Dockerfile diff --git a/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.d.ts b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.d.ts similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.d.ts rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.d.ts diff --git a/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/index.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/license b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/license similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/license rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/license diff --git a/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package-lock.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package-lock.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package-lock.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package-lock.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/package.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/readme.md b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/readme.md similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/readme.md rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/collapse-white-space/readme.md diff --git a/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/LICENSE similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/LICENSE diff --git a/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/README.md b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/README.md similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/README.md rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/README.md diff --git a/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/index.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/index.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/index.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/index.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/package.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/package.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/package.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/end-of-stream/package.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/insert-css/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/LICENSE similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/insert-css/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/LICENSE diff --git a/test/integration/test-fixtures/npm-lock/node_modules/insert-css/example.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/example.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/insert-css/example.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/example.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/insert-css/index.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/index.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/insert-css/index.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/index.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/insert-css/package.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/package.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/insert-css/package.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/package.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/insert-css/readme.markdown b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/readme.markdown similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/insert-css/readme.markdown rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/readme.markdown diff --git a/test/integration/test-fixtures/npm-lock/node_modules/insert-css/test.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/test.js similarity index 98% rename from test/integration/test-fixtures/npm-lock/node_modules/insert-css/test.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/test.js index 8611e2d0cd9..d97aee9576d 100644 --- a/test/integration/test-fixtures/npm-lock/node_modules/insert-css/test.js +++ b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/insert-css/test.js @@ -1,7 +1,7 @@ var test = require('tape'); test(function (t) { - var insertCss = require('./'); + var insertCss = require('.'); var initialNbStyleTags = nbStyleTags(); // basic usage diff --git a/test/integration/test-fixtures/npm-lock/node_modules/once/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/LICENSE similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/once/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/LICENSE diff --git a/test/integration/test-fixtures/npm-lock/node_modules/once/README.md b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/README.md similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/once/README.md rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/README.md diff --git a/test/integration/test-fixtures/npm-lock/node_modules/once/once.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/once.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/once/once.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/once.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/once/package.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/package.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/once/package.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/once/package.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/pump/.travis.yml b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/.travis.yml similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/pump/.travis.yml rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/.travis.yml diff --git a/test/integration/test-fixtures/npm-lock/node_modules/pump/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/LICENSE similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/pump/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/LICENSE diff --git a/test/integration/test-fixtures/npm-lock/node_modules/pump/README.md b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/README.md similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/pump/README.md rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/README.md diff --git a/test/integration/test-fixtures/npm-lock/node_modules/pump/index.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/index.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/pump/index.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/index.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/pump/package.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/package.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/pump/package.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/package.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/pump/test-browser.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/test-browser.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/pump/test-browser.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/test-browser.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/pump/test-node.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/test-node.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/pump/test-node.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/pump/test-node.js diff --git a/test/integration/test-fixtures/npm-lock/node_modules/wrappy/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/LICENSE similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/wrappy/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/LICENSE diff --git a/test/integration/test-fixtures/npm-lock/node_modules/wrappy/README.md b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/README.md similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/wrappy/README.md rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/README.md diff --git a/test/integration/test-fixtures/npm-lock/node_modules/wrappy/package-lock.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/package-lock.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/wrappy/package-lock.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/package-lock.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/wrappy/package.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/package.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/wrappy/package.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/package.json diff --git a/test/integration/test-fixtures/npm-lock/node_modules/wrappy/wrappy.js b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/wrappy.js similarity index 100% rename from test/integration/test-fixtures/npm-lock/node_modules/wrappy/wrappy.js rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/node_modules/wrappy/wrappy.js diff --git a/test/integration/test-fixtures/npm-lock/package-lock.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/package-lock.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/package-lock.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/package-lock.json diff --git a/test/integration/test-fixtures/npm-lock/package.json b/cmd/syft/internal/test/integration/test-fixtures/npm-lock/package.json similarity index 100% rename from test/integration/test-fixtures/npm-lock/package.json rename to cmd/syft/internal/test/integration/test-fixtures/npm-lock/package.json diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/async/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/async/LICENSE similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/async/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/async/LICENSE diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/async/bower.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/async/bower.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/async/bower.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/async/bower.json diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/async/package.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/async/package.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/async/package.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/async/package.json diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/LICENSE.md b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/LICENSE.md similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/LICENSE.md rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/LICENSE.md diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/package.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/package.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/package.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/merge-objects/package.json diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/LICENSE similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/LICENSE diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/package.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/package.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/package.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/resize-observer-polyfill/package.json diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/should-type/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/should-type/LICENSE similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/should-type/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/should-type/LICENSE diff --git a/test/integration/test-fixtures/yarn-lock/node_modules/should-type/package.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/should-type/package.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/node_modules/should-type/package.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/node_modules/should-type/package.json diff --git a/test/integration/test-fixtures/yarn-lock/package.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/package.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/package.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/package.json diff --git a/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/LICENSE b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/LICENSE similarity index 100% rename from test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/LICENSE rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/LICENSE diff --git a/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/bower.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/bower.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/bower.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/bower.json diff --git a/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/component.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/component.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/component.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/component.json diff --git a/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/package.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/package.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/package.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/node_modules/async/package.json diff --git a/test/integration/test-fixtures/yarn-lock/packages/nested-package/package.json b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/package.json similarity index 100% rename from test/integration/test-fixtures/yarn-lock/packages/nested-package/package.json rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/packages/nested-package/package.json diff --git a/test/integration/test-fixtures/yarn-lock/yarn.lock b/cmd/syft/internal/test/integration/test-fixtures/yarn-lock/yarn.lock similarity index 100% rename from test/integration/test-fixtures/yarn-lock/yarn.lock rename to cmd/syft/internal/test/integration/test-fixtures/yarn-lock/yarn.lock diff --git a/cmd/syft/internal/test/integration/utils_test.go b/cmd/syft/internal/test/integration/utils_test.go new file mode 100644 index 00000000000..cfca478342d --- /dev/null +++ b/cmd/syft/internal/test/integration/utils_test.go @@ -0,0 +1,91 @@ +package integration + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/anchore/clio" + "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/cmd/syft/internal/options" + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Scope, catalogerSelection ...string) (sbom.SBOM, source.Source) { + cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{ + Name: "syft-tester", + Version: "v0.99.0", + }).WithCatalogerSelection( + pkgcataloging.NewSelectionRequest(). + WithExpression(catalogerSelection...), + ) + cfg.Search.Scope = scope + + return catalogFixtureImageWithConfig(t, fixtureImageName, cfg) +} + +func catalogFixtureImageWithConfig(t *testing.T, fixtureImageName string, cfg *syft.CreateSBOMConfig) (sbom.SBOM, source.Source) { + cfg.CatalogerSelection = cfg.CatalogerSelection.WithDefaults(pkgcataloging.ImageTag) + + // get the fixture image tar file + imagetest.GetFixtureImage(t, "docker-archive", fixtureImageName) + tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName) + userInput := "docker-archive:" + tarPath + + // get the source to build an SBOM against + detection, err := source.Detect(userInput, source.DefaultDetectConfig()) + require.NoError(t, err) + + theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig()) + require.NoError(t, err) + + t.Cleanup(func() { + theSource.Close() + }) + + s, err := syft.CreateSBOM(context.Background(), theSource, cfg) + + require.NoError(t, err) + require.NotNil(t, s) + + return *s, theSource +} + +func catalogDirectory(t *testing.T, dir string, catalogerSelection ...string) (sbom.SBOM, source.Source) { + cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{ + Name: "syft-tester", + Version: "v0.99.0", + }).WithCatalogerSelection( + pkgcataloging.NewSelectionRequest(). + WithExpression(catalogerSelection...), + ) + + return catalogDirectoryWithConfig(t, dir, cfg) +} + +func catalogDirectoryWithConfig(t *testing.T, dir string, cfg *syft.CreateSBOMConfig) (sbom.SBOM, source.Source) { + cfg.CatalogerSelection = cfg.CatalogerSelection.WithDefaults(pkgcataloging.DirectoryTag) + + // get the source to build an sbom against + userInput := "dir:" + dir + detection, err := source.Detect(userInput, source.DefaultDetectConfig()) + require.NoError(t, err) + + theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig()) + require.NoError(t, err) + t.Cleanup(func() { + theSource.Close() + }) + + // build the SBOM + s, err := syft.CreateSBOM(context.Background(), theSource, cfg) + + require.NoError(t, err) + require.NotNil(t, s) + + return *s, theSource +} diff --git a/cmd/syft/internal/ui/ui.go b/cmd/syft/internal/ui/ui.go index 6161294a53c..85fb137e95a 100644 --- a/cmd/syft/internal/ui/ui.go +++ b/cmd/syft/internal/ui/ui.go @@ -155,7 +155,11 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } - for _, newModel := range m.handler.Handle(msg) { + models, cmd := m.handler.Handle(msg) + if cmd != nil { + cmds = append(cmds, cmd) + } + for _, newModel := range models { if newModel == nil { continue } diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000000..8b6f1d45cf6 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,15 @@ +# Syft API Examples + +This directory contains examples of how to use the Syft API. + +- `create_simple_sbom`: Create a simple SBOM from scratch +- `create_custom_sbom`: Create an SBOM using as much custom configuration as possible, including a custom cataloger implementation +- `decode_sbom`: Take an existing SBOM file (of arbitrary format) and decode it into a Syft SBOM object +- `source_detection`: Shows how to detect what to catalog automatically from a user string (e.g. container image vs directory) +- `source_from_image`: Construct a source from a only a container image + +You can run any of these examples from this directory with: + +```bash +go run ./DIRECTORY_NAME +``` diff --git a/examples/create_custom_sbom/alpine_configuration_cataloger.go b/examples/create_custom_sbom/alpine_configuration_cataloger.go new file mode 100644 index 00000000000..94f826e68f4 --- /dev/null +++ b/examples/create_custom_sbom/alpine_configuration_cataloger.go @@ -0,0 +1,127 @@ +package main + +import ( + "context" + "fmt" + "io" + "path" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" +) + +/* + This is a contrived cataloger that attempts to capture useful APK files from the image as if it were a package. + This isn't a real cataloger, but it is a good example of how to use API elements to create a custom cataloger. +*/ + +var _ pkg.Cataloger = (*alpineConfigurationCataloger)(nil) + +type alpineConfigurationCataloger struct { +} + +func newAlpineConfigurationCataloger() pkg.Cataloger { + return alpineConfigurationCataloger{} +} + +func (m alpineConfigurationCataloger) Name() string { + return "apk-configuration-cataloger" +} + +func (m alpineConfigurationCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { + version, versionLocations, err := getVersion(resolver) + if err != nil { + return nil, nil, fmt.Errorf("unable to get alpine version: %w", err) + } + if len(versionLocations) == 0 { + // this doesn't mean we should stop cataloging, just that we don't have a version to use, thus no package to raise up + return nil, nil, nil + } + + metadata, metadataLocations, err := newAlpineConfiguration(resolver) + if err != nil { + return nil, nil, err + } + + var locations []file.Location + locations = append(locations, versionLocations...) + locations = append(locations, metadataLocations...) + + p := newPackage(version, *metadata, locations...) + + return []pkg.Package{p}, nil, nil +} + +func newPackage(version string, metadata AlpineConfiguration, locations ...file.Location) pkg.Package { + return pkg.Package{ + Name: "alpine-configuration", + Version: version, + Locations: file.NewLocationSet(locations...), + Type: pkg.Type("system-configuration"), // you can make up your own package type here or use an existing one + Metadata: metadata, + } +} + +func newAlpineConfiguration(resolver file.Resolver) (*AlpineConfiguration, []file.Location, error) { + var locations []file.Location + + keys, keyLocations, err := getAPKKeys(resolver) + if err != nil { + return nil, nil, err + } + + locations = append(locations, keyLocations...) + + return &AlpineConfiguration{ + APKKeys: keys, + }, locations, nil + +} + +func getVersion(resolver file.Resolver) (string, []file.Location, error) { + locations, err := resolver.FilesByPath("/etc/alpine-release") + if err != nil { + return "", nil, fmt.Errorf("unable to get alpine version: %w", err) + } + if len(locations) == 0 { + return "", nil, nil + } + + reader, err := resolver.FileContentsByLocation(locations[0]) + if err != nil { + return "", nil, fmt.Errorf("unable to read alpine version: %w", err) + } + + version, err := io.ReadAll(reader) + if err != nil { + return "", nil, fmt.Errorf("unable to read alpine version: %w", err) + } + + return string(version), locations, nil +} + +func getAPKKeys(resolver file.Resolver) (map[string]string, []file.Location, error) { + // name-to-content values + keyContent := make(map[string]string) + + locations, err := resolver.FilesByGlob("/etc/apk/keys/*.rsa.pub") + if err != nil { + return nil, nil, fmt.Errorf("unable to get apk keys: %w", err) + } + for _, location := range locations { + basename := path.Base(location.RealPath) + reader, err := resolver.FileContentsByLocation(location) + content, err := io.ReadAll(reader) + if err != nil { + return nil, nil, fmt.Errorf("unable to read apk key content at %s: %w", location.RealPath, err) + } + keyContent[basename] = string(content) + } + return keyContent, locations, nil +} + +type AlpineConfiguration struct { + APKKeys map[string]string `json:"apkKeys" yaml:"apkKeys"` + // Add more data you want to capture as part of the package metadata here... +} diff --git a/examples/create_custom_sbom/main.go b/examples/create_custom_sbom/main.go new file mode 100644 index 00000000000..d55821f7efb --- /dev/null +++ b/examples/create_custom_sbom/main.go @@ -0,0 +1,138 @@ +package main + +import ( + "context" + "crypto" + "fmt" + "os" + + "gopkg.in/yaml.v3" + + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/cataloging/filecataloging" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +const defaultImage = "alpine:3.19" + +func main() { + // automagically get a source.Source for arbitrary string input + src := getSource(imageReference()) + + // will catalog the given source and return a SBOM keeping in mind several configurable options + sbom := getSBOM(src) + + // show a simple package summary + summarize(sbom) + + // show the alpine-configuration cataloger results + showAlpineConfiguration(sbom) +} + +func imageReference() string { + // read an image string reference from the command line or use a default + if len(os.Args) > 1 { + return os.Args[1] + } + return defaultImage +} + +func getSource(input string) source.Source { + fmt.Println("detecting source type for input:", input, "...") + + detection, err := source.Detect(input, + source.DetectConfig{ + DefaultImageSource: "docker", + }, + ) + + if err != nil { + panic(err) + } + + src, err := detection.NewSource(source.DefaultDetectionSourceConfig()) + + if err != nil { + panic(err) + } + + return src +} + +func getSBOM(src source.Source) sbom.SBOM { + fmt.Println("creating SBOM...") + + cfg := syft.DefaultCreateSBOMConfig(). + // run the catalogers in parallel (5 at a time concurrently max) + WithParallelism(5). + // bake a specific tool name and version into the SBOM + WithTool("my-tool", "v1.0"). + // catalog all files with 3 digests + WithFilesConfig( + filecataloging.DefaultConfig(). + WithSelection(file.AllFilesSelection). + WithHashers( + crypto.MD5, + crypto.SHA1, + crypto.SHA256, + ), + ). + // only use OS related catalogers that would have been used with the kind of + // source type (container image or directory), but also add a specific python cataloger + WithCatalogerSelection( + pkgcataloging.NewSelectionRequest(). + WithSubSelections("os"). + WithAdditions("python-package-cataloger"), + ). + // which relationships to include + WithRelationshipsConfig( + cataloging.RelationshipsConfig{ + PackageFileOwnership: true, + PackageFileOwnershipOverlap: true, + ExcludeBinaryPackagesWithFileOwnershipOverlap: true, + }, + ). + // add your own cataloger to the mix + WithCatalogers( + pkgcataloging.NewAlwaysEnabledCatalogerReference( + newAlpineConfigurationCataloger(), + ), + ) + + s, err := syft.CreateSBOM(context.Background(), src, cfg) + if err != nil { + panic(err) + } + + return *s +} + +func summarize(s sbom.SBOM) { + fmt.Printf("Cataloged %d packages:\n", s.Artifacts.Packages.PackageCount()) + for _, p := range s.Artifacts.Packages.Sorted() { + fmt.Printf(" - %s@%s (%s)\n", p.Name, p.Version, p.Type) + } + fmt.Println() +} + +func showAlpineConfiguration(s sbom.SBOM) { + pkgs := s.Artifacts.Packages.PackagesByName("alpine-configuration") + if len(pkgs) == 0 { + fmt.Println("no alpine-configuration package found") + return + } + + p := pkgs[0] + + fmt.Printf("All 'alpine-configuration' packages: %s\n", p.Version) + meta, err := yaml.Marshal(p.Metadata) + if err != nil { + panic(err) + } + fmt.Println(string(meta)) + +} diff --git a/examples/create_simple_sbom/main.go b/examples/create_simple_sbom/main.go new file mode 100644 index 00000000000..36a8b85974b --- /dev/null +++ b/examples/create_simple_sbom/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/format" + "github.com/anchore/syft/syft/format/syftjson" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +const defaultImage = "alpine:3.19" + +func main() { + // automagically get a source.Source for arbitrary string input + src := getSource(imageReference()) + + // catalog the given source and return a SBOM + sbom := getSBOM(src) + + // take the SBOM object and encode it into the syft-json representation + bytes := formatSBOM(sbom) + + // show the SBOM! + fmt.Println(string(bytes)) +} + +func imageReference() string { + // read an image string reference from the command line or use a default + if len(os.Args) > 1 { + return os.Args[1] + } + return defaultImage +} + +func getSource(input string) source.Source { + detection, err := source.Detect(input, + source.DetectConfig{ + DefaultImageSource: "docker", + }, + ) + + if err != nil { + panic(err) + } + + src, err := detection.NewSource(source.DefaultDetectionSourceConfig()) + + if err != nil { + panic(err) + } + + return src +} + +func getSBOM(src source.Source) sbom.SBOM { + s, err := syft.CreateSBOM(context.Background(), src, nil) + if err != nil { + panic(err) + } + + return *s +} + +func formatSBOM(s sbom.SBOM) []byte { + bytes, err := format.Encode(s, syftjson.NewFormatEncoder()) + if err != nil { + panic(err) + } + return bytes +} diff --git a/examples/decode_sbom/alpine.syft.json b/examples/decode_sbom/alpine.syft.json new file mode 100644 index 00000000000..57327b7b21e --- /dev/null +++ b/examples/decode_sbom/alpine.syft.json @@ -0,0 +1,4964 @@ +{ + "artifacts": [ + { + "id": "70a21b16f1a2cc6a", + "name": "alpine-baselayout", + "version": "3.2.0-r7", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "GPL-2.0-only", + "spdxExpression": "GPL-2.0-only", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r7:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.2.0-r7:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine_baselayout:alpine-baselayout:3.2.0-r7:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine_baselayout:alpine_baselayout:3.2.0-r7:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine:alpine-baselayout:3.2.0-r7:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine:alpine_baselayout:3.2.0-r7:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/alpine-baselayout@3.2.0-r7?arch=aarch64&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "alpine-baselayout", + "originPackage": "alpine-baselayout", + "maintainer": "Natanael Copa ", + "version": "3.2.0-r7", + "architecture": "aarch64", + "url": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", + "description": "Alpine base dir structure and init scripts", + "size": 19906, + "installedSize": 401408, + "pullDependencies": [ + "/bin/sh", + "so:libc.musl-aarch64.so.1" + ], + "provides": [ + "cmd:mkmntdirs" + ], + "pullChecksum": "Q11bDnATFCrlCl73j/624EDFiVusQ=", + "gitCommitOfApkPort": "c3ce4065bd8a69d20ad392d4cf006ed652c3f1a7", + "files": [ + { + "path": "/dev" + }, + { + "path": "/dev/pts" + }, + { + "path": "/dev/shm" + }, + { + "path": "/etc" + }, + { + "path": "/etc/fstab", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q11Q7hNe8QpDS531guqCdrXBzoA/o=" + } + }, + { + "path": "/etc/group", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=" + } + }, + { + "path": "/etc/hostname", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q16nVwYVXP/tChvUPdukVD2ifXOmc=" + } + }, + { + "path": "/etc/hosts", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=" + } + }, + { + "path": "/etc/inittab", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=" + } + }, + { + "path": "/etc/modules", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1toogjUipHGcMgECgPJX64SwUT1M=" + } + }, + { + "path": "/etc/motd", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=" + } + }, + { + "path": "/etc/mtab", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1kiljhXXH1LlQroHsEJIkPZg2eiw=" + } + }, + { + "path": "/etc/passwd", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1TchuuLUfur0izvfZQZxgN/LJhB8=" + } + }, + { + "path": "/etc/profile", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1KpFb8kl5LvwXWlY3e58FNsjrI34=" + } + }, + { + "path": "/etc/protocols", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q13FqXUnvuOpMDrH/6rehxuYAEE34=" + } + }, + { + "path": "/etc/services", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=" + } + }, + { + "path": "/etc/shadow", + "ownerUid": "0", + "ownerGid": "42", + "permissions": "640", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=" + } + }, + { + "path": "/etc/shells", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=" + } + }, + { + "path": "/etc/sysctl.conf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q14upz3tfnNxZkIEsUhWn7Xoiw96g=" + } + }, + { + "path": "/etc/apk" + }, + { + "path": "/etc/conf.d" + }, + { + "path": "/etc/crontabs" + }, + { + "path": "/etc/crontabs/root", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "600", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1vfk1apUWI4yLJGhhNRd0kJixfvY=" + } + }, + { + "path": "/etc/init.d" + }, + { + "path": "/etc/modprobe.d" + }, + { + "path": "/etc/modprobe.d/aliases.conf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=" + } + }, + { + "path": "/etc/modprobe.d/blacklist.conf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=" + } + }, + { + "path": "/etc/modprobe.d/i386.conf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1pnay/njn6ol9cCssL7KiZZ8etlc=" + } + }, + { + "path": "/etc/modprobe.d/kms.conf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1ynbLn3GYDpvajba/ldp1niayeog=" + } + }, + { + "path": "/etc/modules-load.d" + }, + { + "path": "/etc/network" + }, + { + "path": "/etc/network/if-down.d" + }, + { + "path": "/etc/network/if-post-down.d" + }, + { + "path": "/etc/network/if-pre-up.d" + }, + { + "path": "/etc/network/if-up.d" + }, + { + "path": "/etc/opt" + }, + { + "path": "/etc/periodic" + }, + { + "path": "/etc/periodic/15min" + }, + { + "path": "/etc/periodic/daily" + }, + { + "path": "/etc/periodic/hourly" + }, + { + "path": "/etc/periodic/monthly" + }, + { + "path": "/etc/periodic/weekly" + }, + { + "path": "/etc/profile.d" + }, + { + "path": "/etc/profile.d/color_prompt", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q10wL23GuSCVfumMRgakabUI6EsSk=" + } + }, + { + "path": "/etc/profile.d/locale.sh", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1S8j+WW71mWxfVy8ythqU7HUVoBw=" + } + }, + { + "path": "/etc/sysctl.d" + }, + { + "path": "/home" + }, + { + "path": "/lib" + }, + { + "path": "/lib/firmware" + }, + { + "path": "/lib/mdev" + }, + { + "path": "/lib/modules-load.d" + }, + { + "path": "/lib/sysctl.d" + }, + { + "path": "/lib/sysctl.d/00-alpine.conf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1HpElzW1xEgmKfERtTy7oommnq6c=" + } + }, + { + "path": "/media" + }, + { + "path": "/media/cdrom" + }, + { + "path": "/media/floppy" + }, + { + "path": "/media/usb" + }, + { + "path": "/mnt" + }, + { + "path": "/opt" + }, + { + "path": "/proc" + }, + { + "path": "/root", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "700" + }, + { + "path": "/run" + }, + { + "path": "/sbin" + }, + { + "path": "/sbin/mkmntdirs", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1zmX3BDBctenbCV8dx/3QLz2bdRA=" + } + }, + { + "path": "/srv" + }, + { + "path": "/sys" + }, + { + "path": "/tmp", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "1777" + }, + { + "path": "/usr" + }, + { + "path": "/usr/lib" + }, + { + "path": "/usr/lib/modules-load.d" + }, + { + "path": "/usr/local" + }, + { + "path": "/usr/local/bin" + }, + { + "path": "/usr/local/lib" + }, + { + "path": "/usr/local/share" + }, + { + "path": "/usr/sbin" + }, + { + "path": "/usr/share" + }, + { + "path": "/usr/share/man" + }, + { + "path": "/usr/share/misc" + }, + { + "path": "/var" + }, + { + "path": "/var/run", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=" + } + }, + { + "path": "/var/cache" + }, + { + "path": "/var/cache/misc" + }, + { + "path": "/var/empty", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "555" + }, + { + "path": "/var/lib" + }, + { + "path": "/var/lib/misc" + }, + { + "path": "/var/local" + }, + { + "path": "/var/lock" + }, + { + "path": "/var/lock/subsys" + }, + { + "path": "/var/log" + }, + { + "path": "/var/mail" + }, + { + "path": "/var/opt" + }, + { + "path": "/var/spool" + }, + { + "path": "/var/spool/mail", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=" + } + }, + { + "path": "/var/spool/cron" + }, + { + "path": "/var/spool/cron/crontabs", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=" + } + }, + { + "path": "/var/tmp", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "1777" + } + ] + } + }, + { + "id": "300e781a409ed72e", + "name": "alpine-keys", + "version": "2.4-r0", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:alpine-keys:alpine-keys:2.4-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine-keys:alpine_keys:2.4-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine_keys:alpine-keys:2.4-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine_keys:alpine_keys:2.4-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine:alpine-keys:2.4-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:alpine:alpine_keys:2.4-r0:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/alpine-keys@2.4-r0?arch=aarch64&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "alpine-keys", + "originPackage": "alpine-keys", + "maintainer": "Natanael Copa ", + "version": "2.4-r0", + "architecture": "aarch64", + "url": "https://alpinelinux.org", + "description": "Public keys for Alpine Linux packages", + "size": 13692, + "installedSize": 159744, + "pullDependencies": [], + "provides": [], + "pullChecksum": "Q1NfxIKYE+5VU69ul2zrXwNHhvSQg=", + "gitCommitOfApkPort": "ad113d7b2c64187bba3c3e1f71c2774a3246800e", + "files": [ + { + "path": "/etc" + }, + { + "path": "/etc/apk" + }, + { + "path": "/etc/apk/keys" + }, + { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1BTqS+H/UUyhQuzHwiBl47+BTKuU=" + } + }, + { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Oaxdcsa6AYoPdLi0U4lO3J2we18=" + } + }, + { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-616a9724.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1I9Dy6hryacL2YWXg+KlE6WvwEd4=" + } + }, + { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-616adfeb.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q13hJBMHAUquPbp5jpAPFjQI2Y1vQ=" + } + }, + { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-616ae350.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1V/a5P9pKRJb6tihE3e8O6xaPgLU=" + } + }, + { + "path": "/usr" + }, + { + "path": "/usr/share" + }, + { + "path": "/usr/share/apk" + }, + { + "path": "/usr/share/apk/keys" + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1OvCFSO94z97c80mIDCxqGkh2Og4=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1v7YWZYzAWoclaLDI45jEguI7YN0=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1BTqS+H/UUyhQuzHwiBl47+BTKuU=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1NnGuDsdQOx4ZNYfB3N97eLyGPkI=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Oaxdcsa6AYoPdLi0U4lO3J2we18=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1yPq+su65ksNox3uXB+DR7P18+QU=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1MpZDNX0LeLHvSOwVUyXiXx11NN0=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5e69ca50.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1glCQ/eJbvA5xqcswdjFrWv5Fnk0=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1XUdDEoNTtjlvrS+iunk6ziFgIpU=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1lZlTESNrelWTNkL/oQzmAU8a99A=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-61666e3f.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1WNW6Sy87HpJ3IdemQy8pju33Kms=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616a9724.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1I9Dy6hryacL2YWXg+KlE6WvwEd4=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616abc23.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1NSnsgmcMbU4g7j5JaNs0tVHpHVA=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ac3bc.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1VaMBBk4Rxv6boPLKF+I085Q8y2E=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616adfeb.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q13hJBMHAUquPbp5jpAPFjQI2Y1vQ=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ae350.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1V/a5P9pKRJb6tihE3e8O6xaPgLU=" + } + }, + { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616db30d.rsa.pub", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q13wLJrcKQajql5a1p9Q45U+ZXENA=" + } + }, + { + "path": "/usr/share/apk/keys/aarch64" + }, + { + "path": "/usr/share/apk/keys/aarch64/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q17j9nWJkQ+wfIuVQzIFrmFZ7fSOc=" + } + }, + { + "path": "/usr/share/apk/keys/aarch64/alpine-devel@lists.alpinelinux.org-616ae350.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1snr+Q1UbfHyCr/cmmtVvMIS7SGs=" + } + }, + { + "path": "/usr/share/apk/keys/armhf" + }, + { + "path": "/usr/share/apk/keys/armhf/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1U9QtsdN+rYZ9Zh76EfXy00JZHMg=" + } + }, + { + "path": "/usr/share/apk/keys/armhf/alpine-devel@lists.alpinelinux.org-616a9724.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1bC+AdQ0qWBTmefXiI0PvmYOJoVQ=" + } + }, + { + "path": "/usr/share/apk/keys/armv7" + }, + { + "path": "/usr/share/apk/keys/armv7/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1U9QtsdN+rYZ9Zh76EfXy00JZHMg=" + } + }, + { + "path": "/usr/share/apk/keys/armv7/alpine-devel@lists.alpinelinux.org-616adfeb.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1xbIVu7ScwqGHxXGwI22aSe5OdUY=" + } + }, + { + "path": "/usr/share/apk/keys/mips64" + }, + { + "path": "/usr/share/apk/keys/mips64/alpine-devel@lists.alpinelinux.org-5e69ca50.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1hCZdFx+LvzbLtPs753je78gEEBQ=" + } + }, + { + "path": "/usr/share/apk/keys/ppc64le" + }, + { + "path": "/usr/share/apk/keys/ppc64le/alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1t21dhCLbTJmAHXSCeOMq/2vfSgo=" + } + }, + { + "path": "/usr/share/apk/keys/ppc64le/alpine-devel@lists.alpinelinux.org-616abc23.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1PS9zNIPJanC8qcsc5qarEWqhV5Q=" + } + }, + { + "path": "/usr/share/apk/keys/riscv64" + }, + { + "path": "/usr/share/apk/keys/riscv64/alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1NVPbZavaXpsItFwQYDWbpor7yYE=" + } + }, + { + "path": "/usr/share/apk/keys/riscv64/alpine-devel@lists.alpinelinux.org-616db30d.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1U6tfuKRy5J8C6iaKPMZaT/e8tbA=" + } + }, + { + "path": "/usr/share/apk/keys/s390x" + }, + { + "path": "/usr/share/apk/keys/s390x/alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1sjbV2r2w0Ih2vwdzC4Jq6UI7cMQ=" + } + }, + { + "path": "/usr/share/apk/keys/s390x/alpine-devel@lists.alpinelinux.org-616ac3bc.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1l09xa7RnbOIC1dI9FqbaCfS/GXY=" + } + }, + { + "path": "/usr/share/apk/keys/x86" + }, + { + "path": "/usr/share/apk/keys/x86/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Ii51i7Nrc4uft14HhqugaUqdH64=" + } + }, + { + "path": "/usr/share/apk/keys/x86/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Y49eVxhpvftbQ3yAdvlLfcrPLTU=" + } + }, + { + "path": "/usr/share/apk/keys/x86/alpine-devel@lists.alpinelinux.org-61666e3f.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1HjdvcVkpBZzr1aSe3p7oQfAtm/E=" + } + }, + { + "path": "/usr/share/apk/keys/x86_64" + }, + { + "path": "/usr/share/apk/keys/x86_64/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Ii51i7Nrc4uft14HhqugaUqdH64=" + } + }, + { + "path": "/usr/share/apk/keys/x86_64/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1AUFY+fwSBTcrYetjT7NHvafrSQc=" + } + }, + { + "path": "/usr/share/apk/keys/x86_64/alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1qKA23VzMUDle+Dqnrr5Kz+Xvty4=" + } + } + ] + } + }, + { + "id": "87076443f088ac71", + "name": "apk-tools", + "version": "2.10.8-r1", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "GPL-2.0-only", + "spdxExpression": "GPL-2.0-only", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:apk-tools:apk-tools:2.10.8-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:apk-tools:apk_tools:2.10.8-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:apk_tools:apk-tools:2.10.8-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:apk_tools:apk_tools:2.10.8-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:apk:apk-tools:2.10.8-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:apk:apk_tools:2.10.8-r1:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/apk-tools@2.10.8-r1?arch=aarch64&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "apk-tools", + "originPackage": "apk-tools", + "maintainer": "Natanael Copa ", + "version": "2.10.8-r1", + "architecture": "aarch64", + "url": "https://gitlab.alpinelinux.org/alpine/apk-tools", + "description": "Alpine Package Keeper - package manager for alpine", + "size": 106330, + "installedSize": 274432, + "pullDependencies": [ + "so:libc.musl-aarch64.so.1", + "so:libcrypto.so.1.1", + "so:libssl.so.1.1", + "so:libz.so.1" + ], + "provides": [ + "cmd:apk" + ], + "pullChecksum": "Q1NJwrNBaATecXdHtXo3mplM1oJow=", + "gitCommitOfApkPort": "6a528d0bdbd1dc3aac4c1ec1f6c841765bc6ae75", + "files": [ + { + "path": "/etc" + }, + { + "path": "/etc/apk" + }, + { + "path": "/etc/apk/keys" + }, + { + "path": "/etc/apk/protected_paths.d" + }, + { + "path": "/sbin" + }, + { + "path": "/sbin/apk", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1QIkQB4hy0Bq5y7oJ9pUYkoNhgDw=" + } + }, + { + "path": "/var" + }, + { + "path": "/var/cache" + }, + { + "path": "/var/cache/misc" + }, + { + "path": "/var/lib" + }, + { + "path": "/var/lib/apk" + } + ] + } + }, + { + "id": "db9331b6a3a9d6f7", + "name": "busybox", + "version": "1.31.1-r22", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "GPL-2.0-only", + "spdxExpression": "GPL-2.0-only", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:busybox:busybox:1.31.1-r22:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/busybox@1.31.1-r22?arch=aarch64&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "busybox", + "originPackage": "busybox", + "maintainer": "Natanael Copa ", + "version": "1.31.1-r22", + "architecture": "aarch64", + "url": "https://busybox.net/", + "description": "Size optimized toolbox of many common UNIX utilities", + "size": 517882, + "installedSize": 1007616, + "pullDependencies": [ + "so:libc.musl-aarch64.so.1" + ], + "provides": [ + "/bin/sh", + "cmd:busybox", + "cmd:sh" + ], + "pullChecksum": "Q1ElJNcYpfArndKCXUEFkc6EMKJx8=", + "gitCommitOfApkPort": "41c9e389fd1ebce7fc2e723a9074fa7f2a00e11a", + "files": [ + { + "path": "/bin" + }, + { + "path": "/bin/busybox", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1MLz73DQ9PwDwx4wY1G7S/AUtj4g=" + } + }, + { + "path": "/bin/sh", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1pcfTfDNEbNKQc2s1tia7da05M8Q=" + } + }, + { + "path": "/etc" + }, + { + "path": "/etc/securetty", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1RpeOldjaFVFDbjnK5fzpLGtxunM=" + } + }, + { + "path": "/etc/udhcpd.conf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1UAiPZcDIW1ClRzobfggcCQ77V28=" + } + }, + { + "path": "/etc/logrotate.d" + }, + { + "path": "/etc/logrotate.d/acpid", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1TylyCINVmnS+A/Tead4vZhE7Bks=" + } + }, + { + "path": "/etc/network" + }, + { + "path": "/etc/network/if-down.d" + }, + { + "path": "/etc/network/if-post-down.d" + }, + { + "path": "/etc/network/if-post-up.d" + }, + { + "path": "/etc/network/if-pre-down.d" + }, + { + "path": "/etc/network/if-pre-up.d" + }, + { + "path": "/etc/network/if-up.d" + }, + { + "path": "/etc/network/if-up.d/dad", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "775", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1hlxd3qExrihH8bYxDQ3i7TsM/44=" + } + }, + { + "path": "/sbin" + }, + { + "path": "/tmp", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "1777" + }, + { + "path": "/usr" + }, + { + "path": "/usr/sbin" + }, + { + "path": "/usr/share" + }, + { + "path": "/usr/share/udhcpc" + }, + { + "path": "/usr/share/udhcpc/default.script", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1N2KRDz/R6RqKhGqujQ4Y6EokMXY=" + } + }, + { + "path": "/var" + }, + { + "path": "/var/cache" + }, + { + "path": "/var/cache/misc" + }, + { + "path": "/var/lib" + }, + { + "path": "/var/lib/udhcpd" + } + ] + } + }, + { + "id": "49852edd7b8e27bc", + "name": "ca-certificates-bundle", + "version": "20211220-r0", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "GPL-2.0-or-later", + "spdxExpression": "GPL-2.0-or-later", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + }, + { + "value": "MPL-2.0", + "spdxExpression": "MPL-2.0", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca-certificates-bundle:ca_certificates_bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca_certificates_bundle:ca-certificates-bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca_certificates_bundle:ca_certificates_bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca-certificates:ca-certificates-bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca-certificates:ca_certificates_bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca_certificates:ca-certificates-bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca_certificates:ca_certificates_bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:mozilla:ca-certificates-bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:mozilla:ca_certificates_bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca:ca-certificates-bundle:20211220-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:ca:ca_certificates_bundle:20211220-r0:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/ca-certificates-bundle@20211220-r0?arch=aarch64&upstream=ca-certificates&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "ca-certificates-bundle", + "originPackage": "ca-certificates", + "maintainer": "Natanael Copa ", + "version": "20211220-r0", + "architecture": "aarch64", + "url": "https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/", + "description": "Pre generated bundle of Mozilla certificates", + "size": 119492, + "installedSize": 221184, + "pullDependencies": [], + "provides": [ + "ca-certificates-cacert=20211220-r0" + ], + "pullChecksum": "Q1gTd1q6+fUJ5dWxizEjOFrYOkJAs=", + "gitCommitOfApkPort": "ff8801d22feefbb59c634f4e5bd3763869025a9f", + "files": [ + { + "path": "/etc" + }, + { + "path": "/etc/ssl" + }, + { + "path": "/etc/ssl/cert.pem", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Nj6gTBdkZpTFW/obJGdpfvK0StA=" + } + }, + { + "path": "/etc/ssl/certs" + }, + { + "path": "/etc/ssl/certs/ca-certificates.crt", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1m2cJofoNZtCCK41yvjTgX4K7dvs=" + } + } + ] + } + }, + { + "id": "da2d83f64297fd63", + "name": "libc-utils", + "version": "0.7.2-r3", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "BSD-2-Clause AND BSD-3-Clause", + "spdxExpression": "BSD-2-Clause AND BSD-3-Clause", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:libc-utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", + "cpe:2.3:a:libc-utils:libc_utils:0.7.2-r3:*:*:*:*:*:*:*", + "cpe:2.3:a:libc_utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", + "cpe:2.3:a:libc_utils:libc_utils:0.7.2-r3:*:*:*:*:*:*:*", + "cpe:2.3:a:libc:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", + "cpe:2.3:a:libc:libc_utils:0.7.2-r3:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=aarch64&upstream=libc-dev&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "libc-utils", + "originPackage": "libc-dev", + "maintainer": "Natanael Copa ", + "version": "0.7.2-r3", + "architecture": "aarch64", + "url": "https://alpinelinux.org", + "description": "Meta package to pull in correct libc", + "size": 1225, + "installedSize": 4096, + "pullDependencies": [ + "musl-utils" + ], + "provides": [], + "pullChecksum": "Q17TxuYvbVV3Bx3QnFnmFDKT6RtBk=", + "gitCommitOfApkPort": "60424133be2e79bbfeff3d58147a22886f817ce2", + "files": [] + } + }, + { + "id": "3534729b6cc222b7", + "name": "libcrypto1.1", + "version": "1.1.1n-r0", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "OpenSSL", + "spdxExpression": "OpenSSL", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1n-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:libcrypto1.1:libcrypto:1.1.1n-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:libcrypto:libcrypto1.1:1.1.1n-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:libcrypto:libcrypto:1.1.1n-r0:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/libcrypto1.1@1.1.1n-r0?arch=aarch64&upstream=openssl&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "libcrypto1.1", + "originPackage": "openssl", + "maintainer": "Timo Teras ", + "version": "1.1.1n-r0", + "architecture": "aarch64", + "url": "https://www.openssl.org/", + "description": "Crypto library from openssl", + "size": 1100482, + "installedSize": 2469888, + "pullDependencies": [ + "so:libc.musl-aarch64.so.1" + ], + "provides": [ + "so:libcrypto.so.1.1=1.1" + ], + "pullChecksum": "Q1Y2uwiZ4pMJ+aOtwNwUkxMEAKmkA=", + "gitCommitOfApkPort": "b759455d39ebc9606b4b980416c55aa87303ba5f", + "files": [ + { + "path": "/etc" + }, + { + "path": "/etc/ssl" + }, + { + "path": "/etc/ssl/ct_log_list.cnf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1olh8TpdAi2QnTl4FK3TjdUiSwTo=" + } + }, + { + "path": "/etc/ssl/ct_log_list.cnf.dist", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1olh8TpdAi2QnTl4FK3TjdUiSwTo=" + } + }, + { + "path": "/etc/ssl/openssl.cnf", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1wGuxVEOK9iGLj1i8D3BSBnT7MJA=" + } + }, + { + "path": "/etc/ssl/openssl.cnf.dist", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1wGuxVEOK9iGLj1i8D3BSBnT7MJA=" + } + }, + { + "path": "/etc/ssl/certs" + }, + { + "path": "/etc/ssl/misc" + }, + { + "path": "/etc/ssl/misc/CA.pl", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1IACevKhK93GYBHp96Ie26jgZ17s=" + } + }, + { + "path": "/etc/ssl/misc/tsget", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q13NVgfr7dQUuGYxur0tNalH6EIjU=" + } + }, + { + "path": "/etc/ssl/misc/tsget.pl", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1dIIouz/IZaWh9ah1d+bWyFrIyFI=" + } + }, + { + "path": "/etc/ssl/private" + }, + { + "path": "/lib" + }, + { + "path": "/lib/libcrypto.so.1.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1jMWuykHLp49JqfXNb4ylcrjkbaU=" + } + }, + { + "path": "/usr" + }, + { + "path": "/usr/lib" + }, + { + "path": "/usr/lib/libcrypto.so.1.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1T2si+c7ts7sgDxQYve4B3i1Dgo0=" + } + }, + { + "path": "/usr/lib/engines-1.1" + }, + { + "path": "/usr/lib/engines-1.1/afalg.so", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1oIagSDEM9En4g3EVj+OP1oPl5lU=" + } + }, + { + "path": "/usr/lib/engines-1.1/capi.so", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1tzhXBEwKCXeBdk60P8vAgKPgPEE=" + } + }, + { + "path": "/usr/lib/engines-1.1/padlock.so", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Z7bW7J1miX5iqN6Ofy6zgmxvwxc=" + } + } + ] + } + }, + { + "id": "d0400558bea1fa0e", + "name": "libssl1.1", + "version": "1.1.1n-r0", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "OpenSSL", + "spdxExpression": "OpenSSL", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1n-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:libssl1.1:libssl:1.1.1n-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:libssl:libssl1.1:1.1.1n-r0:*:*:*:*:*:*:*", + "cpe:2.3:a:libssl:libssl:1.1.1n-r0:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/libssl1.1@1.1.1n-r0?arch=aarch64&upstream=openssl&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "libssl1.1", + "originPackage": "openssl", + "maintainer": "Timo Teras ", + "version": "1.1.1n-r0", + "architecture": "aarch64", + "url": "https://www.openssl.org/", + "description": "SSL shared libraries", + "size": 209147, + "installedSize": 536576, + "pullDependencies": [ + "so:libc.musl-aarch64.so.1", + "so:libcrypto.so.1.1" + ], + "provides": [ + "so:libssl.so.1.1=1.1" + ], + "pullChecksum": "Q10FSdADnxG9qZTsIGQ39aaq32sjA=", + "gitCommitOfApkPort": "b759455d39ebc9606b4b980416c55aa87303ba5f", + "files": [ + { + "path": "/lib" + }, + { + "path": "/lib/libssl.so.1.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q15R6WogvhTyQIh4ujf2m3Hi3igMw=" + } + }, + { + "path": "/usr" + }, + { + "path": "/usr/lib" + }, + { + "path": "/usr/lib/libssl.so.1.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q18j35pe3yp6HOgMih1wlGP1/mm2c=" + } + } + ] + } + }, + { + "id": "4dd349e42df17a96", + "name": "libtls-standalone", + "version": "2.9.1-r1", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "ISC", + "spdxExpression": "ISC", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:libtls-standalone:libtls-standalone:2.9.1-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:libtls-standalone:libtls_standalone:2.9.1-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:libtls_standalone:libtls-standalone:2.9.1-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:libtls_standalone:libtls_standalone:2.9.1-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:libtls:libtls-standalone:2.9.1-r1:*:*:*:*:*:*:*", + "cpe:2.3:a:libtls:libtls_standalone:2.9.1-r1:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/libtls-standalone@2.9.1-r1?arch=aarch64&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "libtls-standalone", + "originPackage": "libtls-standalone", + "maintainer": "", + "version": "2.9.1-r1", + "architecture": "aarch64", + "url": "https://www.libressl.org/", + "description": "libtls extricated from libressl sources", + "size": 31457, + "installedSize": 110592, + "pullDependencies": [ + "ca-certificates-bundle", + "so:libc.musl-aarch64.so.1", + "so:libcrypto.so.1.1", + "so:libssl.so.1.1" + ], + "provides": [ + "so:libtls-standalone.so.1=1.0.0" + ], + "pullChecksum": "Q1DUhwBWiB9vjN4qWbvbfL6gagsPo=", + "gitCommitOfApkPort": "7ebfa23b6119a24ca9b9375bdc55373259192da1", + "files": [ + { + "path": "/usr" + }, + { + "path": "/usr/lib" + }, + { + "path": "/usr/lib/libtls-standalone.so.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1MePJJ8Ajcuvc5x+6vlF3ygJoZYw=" + } + }, + { + "path": "/usr/lib/libtls-standalone.so.1.0.0", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1m2L9fYCclhgYL+t0i/aO/uZ5Hoc=" + } + } + ] + } + }, + { + "id": "761f5684fa0274c6", + "name": "musl", + "version": "1.1.24-r10", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:musl-libc:musl:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl_libc:musl:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl:musl:1.1.24-r10:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/musl@1.1.24-r10?arch=aarch64&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "musl", + "originPackage": "musl", + "maintainer": "Timo Teräs ", + "version": "1.1.24-r10", + "architecture": "aarch64", + "url": "https://musl.libc.org/", + "description": "the musl c library (libc) implementation", + "size": 383126, + "installedSize": 638976, + "pullDependencies": [], + "provides": [ + "so:libc.musl-aarch64.so.1=1" + ], + "pullChecksum": "Q12uzTrvht/xFcFxMtQUnMCMp/Q7U=", + "gitCommitOfApkPort": "908046ad5f96c4c1df5541ff4b0251767e110c85", + "files": [ + { + "path": "/lib" + }, + { + "path": "/lib/ld-musl-aarch64.so.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1OjfEikBQOAxRpOpB65rFxXrJSBY=" + } + }, + { + "path": "/lib/libc.musl-aarch64.so.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q14RpiCEfZIqcg1XDcVqp8QEpc9ks=" + } + } + ] + } + }, + { + "id": "618e6d5faa1432fe", + "name": "musl-utils", + "version": "1.1.24-r10", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "BSD", + "spdxExpression": "", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + }, + { + "value": "GPL2+", + "spdxExpression": "GPL-2.0-or-later", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + }, + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:musl-utils:musl-utils:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl-utils:musl_utils:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl_utils:musl-utils:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl_utils:musl_utils:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl-libc:musl-utils:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl-libc:musl_utils:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl:musl-utils:1.1.24-r10:*:*:*:*:*:*:*", + "cpe:2.3:a:musl:musl_utils:1.1.24-r10:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/musl-utils@1.1.24-r10?arch=aarch64&upstream=musl&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "musl-utils", + "originPackage": "musl", + "maintainer": "Timo Teräs ", + "version": "1.1.24-r10", + "architecture": "aarch64", + "url": "https://musl.libc.org/", + "description": "the musl c library (libc) implementation", + "size": 37550, + "installedSize": 139264, + "pullDependencies": [ + "scanelf", + "so:libc.musl-aarch64.so.1" + ], + "provides": [ + "cmd:getconf", + "cmd:getent", + "cmd:iconv", + "cmd:ldconfig", + "cmd:ldd" + ], + "pullChecksum": "Q1anVvYA5k/ok52UU/FaIWoK2sYf8=", + "gitCommitOfApkPort": "908046ad5f96c4c1df5541ff4b0251767e110c85", + "files": [ + { + "path": "/sbin" + }, + { + "path": "/sbin/ldconfig", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=" + } + }, + { + "path": "/usr" + }, + { + "path": "/usr/bin" + }, + { + "path": "/usr/bin/getconf", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1kdlRltC4b4df6xvZ4XtVw6LPnk4=" + } + }, + { + "path": "/usr/bin/getent", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1l8d/wLfUmI/BWCMfD6L0Vfczr+g=" + } + }, + { + "path": "/usr/bin/iconv", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1YmPm4OKk7cY+cKGyugmFI9cDUAo=" + } + }, + { + "path": "/usr/bin/ldd", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1r+KYty/HCLl4p4dvPt8kCb1mhB0=" + } + } + ] + } + }, + { + "id": "6c34a9b817d73d7b", + "name": "scanelf", + "version": "1.2.6-r0", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "GPL-2.0-only", + "spdxExpression": "GPL-2.0-only", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:scanelf:scanelf:1.2.6-r0:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/scanelf@1.2.6-r0?arch=aarch64&upstream=pax-utils&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "scanelf", + "originPackage": "pax-utils", + "maintainer": "Natanael Copa ", + "version": "1.2.6-r0", + "architecture": "aarch64", + "url": "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities", + "description": "Scan ELF binaries for stuff", + "size": 36381, + "installedSize": 94208, + "pullDependencies": [ + "so:libc.musl-aarch64.so.1" + ], + "provides": [ + "cmd:scanelf" + ], + "pullChecksum": "Q1rsUhHDqsLsp5CECQg8L6K2oe8PA=", + "gitCommitOfApkPort": "753fa8d765c7a4edb76f11b1995f84cdcb45bce5", + "files": [ + { + "path": "/usr" + }, + { + "path": "/usr/bin" + }, + { + "path": "/usr/bin/scanelf", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1PoUsJHtFOYHiAujB4RsURvdq3t8=" + } + } + ] + } + }, + { + "id": "ea144a516278f0ea", + "name": "ssl_client", + "version": "1.31.1-r22", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "GPL-2.0-only", + "spdxExpression": "GPL-2.0-only", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:ssl-client:ssl-client:1.31.1-r22:*:*:*:*:*:*:*", + "cpe:2.3:a:ssl-client:ssl_client:1.31.1-r22:*:*:*:*:*:*:*", + "cpe:2.3:a:ssl_client:ssl-client:1.31.1-r22:*:*:*:*:*:*:*", + "cpe:2.3:a:ssl_client:ssl_client:1.31.1-r22:*:*:*:*:*:*:*", + "cpe:2.3:a:ssl:ssl-client:1.31.1-r22:*:*:*:*:*:*:*", + "cpe:2.3:a:ssl:ssl_client:1.31.1-r22:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/ssl_client@1.31.1-r22?arch=aarch64&upstream=busybox&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "ssl_client", + "originPackage": "busybox", + "maintainer": "Natanael Copa ", + "version": "1.31.1-r22", + "architecture": "aarch64", + "url": "https://busybox.net/", + "description": "EXternal ssl_client for busybox wget", + "size": 4289, + "installedSize": 24576, + "pullDependencies": [ + "so:libc.musl-aarch64.so.1", + "so:libtls-standalone.so.1" + ], + "provides": [ + "cmd:ssl_client" + ], + "pullChecksum": "Q1ovyIuDBoKvoqqwFdTpvyxGB0Zso=", + "gitCommitOfApkPort": "41c9e389fd1ebce7fc2e723a9074fa7f2a00e11a", + "files": [ + { + "path": "/usr" + }, + { + "path": "/usr/bin" + }, + { + "path": "/usr/bin/ssl_client", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1pokN906jhF8cZfksfXQDDQAv3jg=" + } + } + ] + } + }, + { + "id": "f871830fe4975a68", + "name": "zlib", + "version": "1.2.12-r0", + "type": "apk", + "foundBy": "apk-db-cataloger", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ], + "licenses": [ + { + "value": "Zlib", + "spdxExpression": "Zlib", + "type": "declared", + "urls": [], + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "accessPath": "/lib/apk/db/installed", + "annotations": { + "evidence": "primary" + } + } + ] + } + ], + "language": "", + "cpes": [ + "cpe:2.3:a:zlib:zlib:1.2.12-r0:*:*:*:*:*:*:*" + ], + "purl": "pkg:apk/alpine/zlib@1.2.12-r0?arch=aarch64&distro=alpine-3.12.12", + "metadataType": "apk-db-entry", + "metadata": { + "package": "zlib", + "originPackage": "zlib", + "maintainer": "Natanael Copa ", + "version": "1.2.12-r0", + "architecture": "aarch64", + "url": "https://zlib.net/", + "description": "A compression/decompression Library", + "size": 52231, + "installedSize": 102400, + "pullDependencies": [ + "so:libc.musl-aarch64.so.1" + ], + "provides": [ + "so:libz.so.1=1.2.12" + ], + "pullChecksum": "Q1pmoYrJ0Z+qYAN3/bFhlKoWkmdN8=", + "gitCommitOfApkPort": "370acac0e4514c90d8c4e33a9740a7c92254853a", + "files": [ + { + "path": "/lib" + }, + { + "path": "/lib/libz.so.1", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "777", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1+aBjyJ7dmLatVkyqCNnAChlDZh8=" + } + }, + { + "path": "/lib/libz.so.1.2.12", + "ownerUid": "0", + "ownerGid": "0", + "permissions": "755", + "digest": { + "algorithm": "'Q1'+base64(sha1)", + "value": "Q1t8k1PRj167dkycAt7Ax8yOdws60=" + } + } + ] + } + } + ], + "artifactRelationships": [ + { + "parent": "300e781a409ed72e", + "child": "03873777fed9ff43", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "0acf160f41630757", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "13dd5ed25240c27e", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "22bbedf698c71e11", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "237ea5b321ae0600", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "2ff45fbb92b82580", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "300e781a409ed72e", + "child": "469c33e9d320fb4e", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "5a062656ece9d33f", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "5d8389605f14788c", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "5dc069665c5b6c2d", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "879b86f3d56992e3", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "8b8fbab211078ce3", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "8c1b0c306b55d52e", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "8c39a34f27d7b8b8", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "95e4dac118918344", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "a07bc89128850ea8", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "aca7829e5caaafec", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "c735ab9278164f87", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "cb4516b26ca8e5df", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "e3bbed8d2f3aa967", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "fcb3750e5159e213", + "type": "contains" + }, + { + "parent": "300e781a409ed72e", + "child": "fe1d8dac14773061", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "24adce31408e2b35", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "3534729b6cc222b7", + "child": "40f8c3687a866b44", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "4dd349e42df17a96", + "type": "dependency-of" + }, + { + "parent": "3534729b6cc222b7", + "child": "56bd948d4f18705e", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "582750a63290fb66", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "87076443f088ac71", + "type": "dependency-of" + }, + { + "parent": "3534729b6cc222b7", + "child": "ad399331b09e180e", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "b1f03a6b00cbd2fe", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "d0400558bea1fa0e", + "type": "dependency-of" + }, + { + "parent": "3534729b6cc222b7", + "child": "e557179542ac31a5", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "f2afd5237fe2c3a4", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "f51cbcf6ae499899", + "type": "contains" + }, + { + "parent": "3534729b6cc222b7", + "child": "faeab95f86dd9efc", + "type": "contains" + }, + { + "parent": "49852edd7b8e27bc", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "49852edd7b8e27bc", + "child": "4dd349e42df17a96", + "type": "dependency-of" + }, + { + "parent": "49852edd7b8e27bc", + "child": "b93f18b241f5e8fa", + "type": "contains" + }, + { + "parent": "4dd349e42df17a96", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "4dd349e42df17a96", + "child": "84404fd6464f61be", + "type": "contains" + }, + { + "parent": "4dd349e42df17a96", + "child": "ea144a516278f0ea", + "type": "dependency-of" + }, + { + "parent": "618e6d5faa1432fe", + "child": "0ea04a3bc4f3e371", + "type": "contains" + }, + { + "parent": "618e6d5faa1432fe", + "child": "18d1f9e5760a915a", + "type": "contains" + }, + { + "parent": "618e6d5faa1432fe", + "child": "1c96050d6f593ed6", + "type": "contains" + }, + { + "parent": "618e6d5faa1432fe", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "618e6d5faa1432fe", + "child": "4983f048a5f72416", + "type": "contains" + }, + { + "parent": "618e6d5faa1432fe", + "child": "da2d83f64297fd63", + "type": "dependency-of" + }, + { + "parent": "618e6d5faa1432fe", + "child": "f87d9fc71efb8382", + "type": "contains" + }, + { + "parent": "6c34a9b817d73d7b", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "6c34a9b817d73d7b", + "child": "618e6d5faa1432fe", + "type": "dependency-of" + }, + { + "parent": "6c34a9b817d73d7b", + "child": "99744ea397e4db46", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "003d67101ea3f703", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "0adde64b7419bc06", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "101f0230d16cd4cc", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "16b538190bd2d8e4", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "193e0466ebf75373", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "1ca4e605d79309bd", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "1f21b922ea357603", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "280df73804363fac", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "2e86066068a63825", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "3239a4705c81f17b", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "3ab6b534ff2a041d", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "3ff8019c27859ee5", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "421dffb84f68146d", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "4605d980af91d78c", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "57fdaca74461eada", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "65b95b833f4c85f9", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "8e5d70036925d90e", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "92846e0b9a31fa99", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "9511997217af8f59", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "a4878fc8e917a20e", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "c13d4ccfc8334055", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "d3f9378623d81cfb", + "type": "contains" + }, + { + "parent": "70a21b16f1a2cc6a", + "child": "d5707bd5de9a3be4", + "type": "contains" + }, + { + "parent": "761f5684fa0274c6", + "child": "3534729b6cc222b7", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "761f5684fa0274c6", + "child": "4dd349e42df17a96", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "618e6d5faa1432fe", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "6c34a9b817d73d7b", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "70a21b16f1a2cc6a", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "87076443f088ac71", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "c840950da1ceb044", + "type": "contains" + }, + { + "parent": "761f5684fa0274c6", + "child": "d0400558bea1fa0e", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "db9331b6a3a9d6f7", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "ea144a516278f0ea", + "type": "dependency-of" + }, + { + "parent": "761f5684fa0274c6", + "child": "f871830fe4975a68", + "type": "dependency-of" + }, + { + "parent": "87076443f088ac71", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "87076443f088ac71", + "child": "c555bffc18a4c941", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "300e781a409ed72e", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "3534729b6cc222b7", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "49852edd7b8e27bc", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "4dd349e42df17a96", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "618e6d5faa1432fe", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "6c34a9b817d73d7b", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "70a21b16f1a2cc6a", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "761f5684fa0274c6", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "87076443f088ac71", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "d0400558bea1fa0e", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "da2d83f64297fd63", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "db9331b6a3a9d6f7", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "ea144a516278f0ea", + "type": "contains" + }, + { + "parent": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "child": "f871830fe4975a68", + "type": "contains" + }, + { + "parent": "d0400558bea1fa0e", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "d0400558bea1fa0e", + "child": "4dd349e42df17a96", + "type": "dependency-of" + }, + { + "parent": "d0400558bea1fa0e", + "child": "83fe247fdca374e0", + "type": "contains" + }, + { + "parent": "d0400558bea1fa0e", + "child": "87076443f088ac71", + "type": "dependency-of" + }, + { + "parent": "da2d83f64297fd63", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "1a6b06b0bb5a0d17", + "type": "contains" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "59a8ba7df46c2920", + "type": "contains" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "63db6a190e31d94c", + "type": "contains" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "66cd3d11c9a64a91", + "type": "contains" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "70a21b16f1a2cc6a", + "type": "dependency-of" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "c2a2950ea79092e3", + "type": "contains" + }, + { + "parent": "db9331b6a3a9d6f7", + "child": "c81a1e5c8ec53a85", + "type": "contains" + }, + { + "parent": "ea144a516278f0ea", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "ea144a516278f0ea", + "child": "3da24b68eb0cb573", + "type": "contains" + }, + { + "parent": "f871830fe4975a68", + "child": "3859b6ea22aa5e17", + "type": "evident-by" + }, + { + "parent": "f871830fe4975a68", + "child": "783dcad80474487b", + "type": "contains" + }, + { + "parent": "f871830fe4975a68", + "child": "87076443f088ac71", + "type": "dependency-of" + } + ], + "files": [ + { + "id": "59a8ba7df46c2920", + "location": { + "path": "/bin/busybox", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 886368 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "30bcfbdc343d3f00f0c78c18d46ed2fc052d8f88" + }, + { + "algorithm": "sha256", + "value": "c624a8adfdcb3670022e56e1aab2b1a5668ef0bdedd9fc3c1f3f40bb1071d6b3" + } + ] + }, + { + "id": "469c33e9d320fb4e", + "location": { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "053a92f87fd4532850bb31f0881978efe0532ae5" + }, + { + "algorithm": "sha256", + "value": "1bb2a846c0ea4ca9d0e7862f970863857fc33c32f5506098c636a62a726a847b" + } + ] + }, + { + "id": "cb4516b26ca8e5df", + "location": { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "39ac5d72c6ba018a0f74b8b453894edc9db07b5f" + }, + { + "algorithm": "sha256", + "value": "73867d92083f2f8ab899a26ccda7ef63dfaa0032a938620eda605558958a8041" + } + ] + }, + { + "id": "e3bbed8d2f3aa967", + "location": { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-616a9724.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "23d0f2ea1af269c2f66165e0f8a944e96bf011de" + }, + { + "algorithm": "sha256", + "value": "10877cce0a935e46ad88cb79e174a2491680508eccda08e92bf04fb9bf37fbc1" + } + ] + }, + { + "id": "0acf160f41630757", + "location": { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-616adfeb.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "de1241307014aae3dba798e900f163408d98d6f4" + }, + { + "algorithm": "sha256", + "value": "ebe717d228555aa58133c202314a451f81e71f174781fd7ff8d8970d6cfa60da" + } + ] + }, + { + "id": "a07bc89128850ea8", + "location": { + "path": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-616ae350.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "57f6b93fda4a4496fab62844ddef0eeb168f80b5" + }, + { + "algorithm": "sha256", + "value": "d11f6b21c61b4274e182eb888883a8ba8acdbf820dcc7a6d82a7d9fc2fd2836d" + } + ] + }, + { + "id": "d3f9378623d81cfb", + "location": { + "path": "/etc/crontabs/root", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 600, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/tab-separated-values", + "size": 283 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "bdf9356a9516238c8b2468613517749098b17ef6" + }, + { + "algorithm": "sha256", + "value": "575d810a9fae5f2f0671c9b2c0ce973e46c7207fbe5cb8d1b0d1836a6a0470e3" + } + ] + }, + { + "id": "65b95b833f4c85f9", + "location": { + "path": "/etc/fstab", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/csv", + "size": 89 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "d50ee135ef10a434b9df582ea8276b5c1ce803fa" + }, + { + "algorithm": "sha256", + "value": "a3efca2e8d62785c87517283092b4c800d88612b6f3f06b80a4c2f39d8e68841" + } + ] + }, + { + "id": "101f0230d16cd4cc", + "location": { + "path": "/etc/group", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 682 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "a09d7ac56b9d80a3ab5c812ab840f3945d8bb26e" + }, + { + "algorithm": "sha256", + "value": "412af628e00706d3c90a5d465d59cc422ff68d79eeb8870c4f33ed6df04b2871" + } + ] + }, + { + "id": "9511997217af8f59", + "location": { + "path": "/etc/hostname", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 10 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "ea75706155cffed0a1bd43ddba4543da27d73a67" + }, + { + "algorithm": "sha256", + "value": "d906aecb61d076a967d9ffe8821c7b04b063f72df9d9e35b33ef36b1c0d98f16" + } + ] + }, + { + "id": "3ab6b534ff2a041d", + "location": { + "path": "/etc/hosts", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/tab-separated-values", + "size": 79 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "043eb324a653456caa1a73e2e2d49f77792bb0c5" + }, + { + "algorithm": "sha256", + "value": "e3998dbe02b51dada33de87ae43d18a93ab6915b9e34f5a751bf2b9b25a55492" + } + ] + }, + { + "id": "92846e0b9a31fa99", + "location": { + "path": "/etc/inittab", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 570 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "4ecb616e15bb4335917b513f34ac133ae0f8a477" + }, + { + "algorithm": "sha256", + "value": "54a5f36970125bf70cdf7b215c9e12a287d92ad76a693bd72aec4cbc5645df87" + } + ] + }, + { + "id": "63db6a190e31d94c", + "location": { + "path": "/etc/logrotate.d/acpid", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 140 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "4f29720883559a74be03f4de69de2f66113b064b" + }, + { + "algorithm": "sha256", + "value": "d608a3b7715886b5735def0cc50a6359fd364fac2e0e0a459c588c04be471031" + } + ] + }, + { + "id": "003d67101ea3f703", + "location": { + "path": "/etc/modprobe.d/aliases.conf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 1545 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "5946e1e930583552bb7b863eb94bcbb3feef8aa9" + }, + { + "algorithm": "sha256", + "value": "3ebaba946f213670170c7d69949f690a3854553bd0b1560f1d980cba4c83a942" + } + ] + }, + { + "id": "d5707bd5de9a3be4", + "location": { + "path": "/etc/modprobe.d/blacklist.conf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 1998 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "c7160653a4ba4cb42f6fb7abbcfad65b0016a8c8" + }, + { + "algorithm": "sha256", + "value": "2e2a6fd7a554924bbb5cbdd30f73dc05963fe0c458437b520cf4d503d4d73ff7" + } + ] + }, + { + "id": "193e0466ebf75373", + "location": { + "path": "/etc/modprobe.d/i386.conf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 122 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "a676b2fe78e7ea897d702b2c2fb2a2659f1eb657" + }, + { + "algorithm": "sha256", + "value": "6c46c4cbfb8b7594f19eb94801a350fa2221ae9ac5239a8819d15555caa76ae8" + } + ] + }, + { + "id": "4605d980af91d78c", + "location": { + "path": "/etc/modprobe.d/kms.conf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 91 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "ca76cb9f71980e9bda8db6bf95da759e26b27a88" + }, + { + "algorithm": "sha256", + "value": "50467fa732f809f3a2bb5738628765c5f895c3a237e1c1ad09f85d41fd9ca7c5" + } + ] + }, + { + "id": "421dffb84f68146d", + "location": { + "path": "/etc/modules", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 15 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "b68a208d48a91c670c8040a03c95fae12c144f53" + }, + { + "algorithm": "sha256", + "value": "2c881de75a5409c35d2433a24f180b8b02ba478ef2c1c60ea3434a35bcbc335d" + } + ] + }, + { + "id": "1f21b922ea357603", + "location": { + "path": "/etc/motd", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 283 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "5e676e555354447436ed3bd8a752ebe5332d15c0" + }, + { + "algorithm": "sha256", + "value": "4ada0c700c4460f85252987092650c6708f17b4ccebc9ae4fcf8732089a1485f" + } + ] + }, + { + "id": "c81a1e5c8ec53a85", + "location": { + "path": "/etc/network/if-up.d/dad", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 775, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 218 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "865c5ddea131ae2847f1b6310d0de2ed3b0cff8e" + }, + { + "algorithm": "sha256", + "value": "eadec0a3e18ef58316d8657c6e42b6f4d35d26de52d19cfeb3d3a256622c955b" + } + ] + }, + { + "id": "3ff8019c27859ee5", + "location": { + "path": "/etc/passwd", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 1172 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "4dc86eb8b51fbabd22cef7d9419c6037f2c9841f" + }, + { + "algorithm": "sha256", + "value": "2e0902cf0a7f64bf4e64ef2fad66ae977b4d01975dddf5352a84aea5c4e901f0" + } + ] + }, + { + "id": "3239a4705c81f17b", + "location": { + "path": "/etc/profile", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 238 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "2a915bf249792efc175a56377b9f0536c8eb237e" + }, + { + "algorithm": "sha256", + "value": "88dc4b847ee3ca91501b025dee3ff49590a85360a20e90a5e0f1a37bd610f598" + } + ] + }, + { + "id": "16b538190bd2d8e4", + "location": { + "path": "/etc/profile.d/color_prompt", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 295 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "d302f6dc6b920957ee98c4606a469b508e84b129" + }, + { + "algorithm": "sha256", + "value": "a00b56dbd437d3f2c32ced50974daa3cfc84a8dd1cbaf75cf307be20b398fc75" + } + ] + }, + { + "id": "280df73804363fac", + "location": { + "path": "/etc/profile.d/locale.sh", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 61 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "4bc8fe596ef5996c5f572f32b61a94ec7515a01c" + }, + { + "algorithm": "sha256", + "value": "84eb9034099d759ff08e6da5a731cacfc63a319547ad0f1dfc1c64853aca93f2" + } + ] + }, + { + "id": "0adde64b7419bc06", + "location": { + "path": "/etc/protocols", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 1865 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "dc5a97527bee3a9303ac7ffaade871b98004137e" + }, + { + "algorithm": "sha256", + "value": "a6695dbd53b87c7b41dfdafd40b1c8ba34fed2f0fa8eaaa296ad17c0b154603e" + } + ] + }, + { + "id": "1a6b06b0bb5a0d17", + "location": { + "path": "/etc/securetty", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 70 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "46978e95d8da1551436e39cae5fce92c6b71ba73" + }, + { + "algorithm": "sha256", + "value": "c8070bc7fc443ac96502b2d811afc3f7d62b24c0417451f9cc65e3eca2a2af13" + } + ] + }, + { + "id": "1ca4e605d79309bd", + "location": { + "path": "/etc/services", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 14464 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "0ba1c936042f2d6aade5563e9fb319275aec0ee6" + }, + { + "algorithm": "sha256", + "value": "a1c2d8af47c1a951f39f11cf53160703f282946d9821068eadf97b7d43208a34" + } + ] + }, + { + "id": "57fdaca74461eada", + "location": { + "path": "/etc/shadow", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 640, + "type": "RegularFile", + "userID": 0, + "groupID": 42, + "mimeType": "text/plain", + "size": 422 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "2877ffc63a5c635e09f404933b7871e210c3d013" + }, + { + "algorithm": "sha256", + "value": "2d6f677c66af468f483597fbaa53dbe7150eb925a111c2da25c96220915c6a1a" + } + ] + }, + { + "id": "2e86066068a63825", + "location": { + "path": "/etc/shells", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 38 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "a239b661da4227a07f6a9183699fd275bdb12640" + }, + { + "algorithm": "sha256", + "value": "24be6ceb236610df45684c83b06c918ae45635be55f69975e43676b7595bbc5f" + } + ] + }, + { + "id": "b93f18b241f5e8fa", + "location": { + "path": "/etc/ssl/certs/ca-certificates.crt", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 203223 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "9b6709a1fa0d66d0822b8d72be34e05f82bb76fb" + }, + { + "algorithm": "sha256", + "value": "07a23c1a1a5094e6c049c61f87ad09150abbda7590797bfbde8c34349f8dabd0" + } + ] + }, + { + "id": "f2afd5237fe2c3a4", + "location": { + "path": "/etc/ssl/ct_log_list.cnf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 412 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "a2587c4e97408b64274e5e052b74e3754892c13a" + }, + { + "algorithm": "sha256", + "value": "f1c1803d13d1d0b755b13b23c28bd4e20e07baf9f2b744c9337ba5866aa0ec3b" + } + ] + }, + { + "id": "ad399331b09e180e", + "location": { + "path": "/etc/ssl/ct_log_list.cnf.dist", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 412 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "a2587c4e97408b64274e5e052b74e3754892c13a" + }, + { + "algorithm": "sha256", + "value": "f1c1803d13d1d0b755b13b23c28bd4e20e07baf9f2b744c9337ba5866aa0ec3b" + } + ] + }, + { + "id": "24adce31408e2b35", + "location": { + "path": "/etc/ssl/misc/CA.pl", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/x-perl", + "size": 7598 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "20009ebca84af77198047a7de887b6ea3819d7bb" + }, + { + "algorithm": "sha256", + "value": "61ab95f7e96f2b0f2acdcafb8afde2f6c43e899416397230c2fae9c1e701e45b" + } + ] + }, + { + "id": "582750a63290fb66", + "location": { + "path": "/etc/ssl/misc/tsget.pl", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/x-perl", + "size": 6579 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "748228bb3fc865a5a1f5a87577e6d6c85ac8c852" + }, + { + "algorithm": "sha256", + "value": "212174afdc2b4b12cb2d28ec41bfc73bc500f5ad72c445843f9f7796a735496a" + } + ] + }, + { + "id": "faeab95f86dd9efc", + "location": { + "path": "/etc/ssl/openssl.cnf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 10909 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "c06bb154438af6218b8f58bc0f70520674fb3090" + }, + { + "algorithm": "sha256", + "value": "f10ba64917b4458fafc1e078c2eb9e6a7602e68fc98c2e9e6df5e1636ae27d6b" + } + ] + }, + { + "id": "b1f03a6b00cbd2fe", + "location": { + "path": "/etc/ssl/openssl.cnf.dist", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 10909 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "c06bb154438af6218b8f58bc0f70520674fb3090" + }, + { + "algorithm": "sha256", + "value": "f10ba64917b4458fafc1e078c2eb9e6a7602e68fc98c2e9e6df5e1636ae27d6b" + } + ] + }, + { + "id": "a4878fc8e917a20e", + "location": { + "path": "/etc/sysctl.conf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 53 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "e2ea73ded7e7371664204b148569fb5e88b0f7a8" + }, + { + "algorithm": "sha256", + "value": "8bba47da45bc8715c69ac904a60410eabffaa7bbbef640f9c1368ab9c48493d0" + } + ] + }, + { + "id": "66cd3d11c9a64a91", + "location": { + "path": "/etc/udhcpd.conf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 5306 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "50088f65c0c85b50a5473a1b7e081c090efb576f" + }, + { + "algorithm": "sha256", + "value": "edf929b3bf6da1fbde03687020739ee97a9a3edc825db6b768e3e2ce08ebbdd3" + } + ] + }, + { + "id": "3859b6ea22aa5e17", + "location": { + "path": "/lib/apk/db/installed", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + } + }, + { + "id": "c840950da1ceb044", + "location": { + "path": "/lib/ld-musl-aarch64.so.1", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 621016 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "3a37c48a4050380c51a4ea41eb9ac5c57ac94816" + }, + { + "algorithm": "sha256", + "value": "99c589a28a4639e8b82627ef39794fab8d3e25badc5ec9496c0fe3fd2e89aadc" + } + ] + }, + { + "id": "e557179542ac31a5", + "location": { + "path": "/lib/libcrypto.so.1.1", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 2342480 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "8cc5aeca41cba78f49a9f5cd6f8ca572b8e46da5" + }, + { + "algorithm": "sha256", + "value": "78ac2b6af32a71d895ebfa350c782c5e642231378432e28a3b936291ed5fe984" + } + ] + }, + { + "id": "83fe247fdca374e0", + "location": { + "path": "/lib/libssl.so.1.1", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 519496 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "e51e96a20be14f2408878ba37f69b71e2de280cc" + }, + { + "algorithm": "sha256", + "value": "e742f89e940a7e93c22928a374d215a7354a80bc2e26fa262e4d8ff205d2b049" + } + ] + }, + { + "id": "783dcad80474487b", + "location": { + "path": "/lib/libz.so.1.2.12", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 91888 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "b7c9353d18f5ebb764c9c02dec0c7cc8e770b3ad" + }, + { + "algorithm": "sha256", + "value": "da39c3b228641ea2bc568684cee24527e74d6219bcef9b922deca4e5d29e5506" + } + ] + }, + { + "id": "c13d4ccfc8334055", + "location": { + "path": "/lib/sysctl.d/00-alpine.conf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 1278 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "1e9125cd6d7112098a7c446d4f2ee8a269a7aba7" + }, + { + "algorithm": "sha256", + "value": "ee169bea2cb6859420b55ca7a9c23fb68b50adc1d26c951f904dec9e8f767380" + } + ] + }, + { + "id": "c555bffc18a4c941", + "location": { + "path": "/sbin/apk", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 223056 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "408910078872d01ab9cbba09f69518928361803c" + }, + { + "algorithm": "sha256", + "value": "8bec9ddd79c361763a76b59b051497aba6879c0c7ef9a81aacea39e02662afc4" + } + ] + }, + { + "id": "18d1f9e5760a915a", + "location": { + "path": "/sbin/ldconfig", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 393 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "2a36b6f8f3992b112450e66ac128c2ea499a103e" + }, + { + "algorithm": "sha256", + "value": "b4a2c06db38742e8c42c3c9838b285a7d8cdac6c091ff3df5ff9a15f1e41b9c7" + } + ] + }, + { + "id": "8e5d70036925d90e", + "location": { + "path": "/sbin/mkmntdirs", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 5712 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "ce65f704305cb5e9db095f1dc7fdd02f3d9b7510" + }, + { + "algorithm": "sha256", + "value": "c6597bce70930e51b48d00e4508bbf8831d951e3d49d796f95915ce4c7e2f027" + } + ] + }, + { + "id": "f87d9fc71efb8382", + "location": { + "path": "/usr/bin/getconf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 34648 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "91d95196d0b86f875feb1bd9e17b55c3a2cf9e4e" + }, + { + "algorithm": "sha256", + "value": "88d1b3ca6f4381f4ef6bec9c223f5f1aab705e12bf6bd62cd0dd9871b3c8e565" + } + ] + }, + { + "id": "0ea04a3bc4f3e371", + "location": { + "path": "/usr/bin/getent", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 49712 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "97c77fc0b7d4988fc158231f0fa2f455f733afe8" + }, + { + "algorithm": "sha256", + "value": "cdf20f3c528dcca61170fc2d39895c4783dc819efd5171e67aea8d0e62c23872" + } + ] + }, + { + "id": "1c96050d6f593ed6", + "location": { + "path": "/usr/bin/iconv", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 23216 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "6263e6e0e2a4edc63e70a1b2ba098523d703500a" + }, + { + "algorithm": "sha256", + "value": "6d3f770e026b5e73044a8462cbc821c672bb0fafde003c6e7b7cb6ce20f7f598" + } + ] + }, + { + "id": "4983f048a5f72416", + "location": { + "path": "/usr/bin/ldd", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 53 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "afe298b72fc708b978a7876f3edf2409bd66841d" + }, + { + "algorithm": "sha256", + "value": "5f115be8562262bcc50ec469e25c0af2fda3bad72a960c6aa3488acd7a7da8cf" + } + ] + }, + { + "id": "99744ea397e4db46", + "location": { + "path": "/usr/bin/scanelf", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 79560 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "3e852c247b453981e202e8c1e11b1446f76adedf" + }, + { + "algorithm": "sha256", + "value": "be87c7f3079fa22494861a5cdf0979ba74f34d028d1a62b99f2cc74eb0f1aa4d" + } + ] + }, + { + "id": "3da24b68eb0cb573", + "location": { + "path": "/usr/bin/ssl_client", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 9808 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "a6890df74ea3845f1c65f92c7d74030d002fde38" + }, + { + "algorithm": "sha256", + "value": "f6e7fd1c20782a4de5ff42a8723d960d513bc0c041f5357990111c9730796757" + } + ] + }, + { + "id": "40f8c3687a866b44", + "location": { + "path": "/usr/lib/engines-1.1/afalg.so", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 18568 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "a086a048310cf449f88371158fe38fd683e5e655" + }, + { + "algorithm": "sha256", + "value": "619fe939e7116910a2123378ae33703a3f3d2daa6ae5ea65f6da60c7081dbe2d" + } + ] + }, + { + "id": "f51cbcf6ae499899", + "location": { + "path": "/usr/lib/engines-1.1/capi.so", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 5672 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "b73857044c0a097781764eb43fcbc080a3e03c41" + }, + { + "algorithm": "sha256", + "value": "a657bc2985efc3ff5b71e2f168ede1baf9ceb6a450b04c582361aeae85cd860f" + } + ] + }, + { + "id": "56bd948d4f18705e", + "location": { + "path": "/usr/lib/engines-1.1/padlock.so", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 5672 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "67b6d6ec9d66897e62a8de8e7f2eb3826c6fc317" + }, + { + "algorithm": "sha256", + "value": "45abb6629c60e922e4af6390dee8c281e346658c55ad02521fd51d37416cd073" + } + ] + }, + { + "id": "84404fd6464f61be", + "location": { + "path": "/usr/lib/libtls-standalone.so.1.0.0", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "application/x-sharedlib", + "size": 96032 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "9b62fd7d809c9618182feb748bf68efee6791e87" + }, + { + "algorithm": "sha256", + "value": "3708a8f2fe798b424b8fa4d050dbdf7f4d3c28ac8f836753c3f26af35a34e4cf" + } + ] + }, + { + "id": "95e4dac118918344", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "3af08548ef78cfdedcf349880c2c6a1a48763a0e" + }, + { + "algorithm": "sha256", + "value": "9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4" + } + ] + }, + { + "id": "237ea5b321ae0600", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "bfb616658cc05a872568b0c8e398c482e23b60dd" + }, + { + "algorithm": "sha256", + "value": "ebf31683b56410ecc4c00acd9f6e2839e237a3b62b5ae7ef686705c7ba0396a9" + } + ] + }, + { + "id": "fcb3750e5159e213", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "053a92f87fd4532850bb31f0881978efe0532ae5" + }, + { + "algorithm": "sha256", + "value": "1bb2a846c0ea4ca9d0e7862f970863857fc33c32f5506098c636a62a726a847b" + } + ] + }, + { + "id": "8c1b0c306b55d52e", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "3671ae0ec7503b1e193587c1dcdf7b78bc863e42" + }, + { + "algorithm": "sha256", + "value": "12f899e55a7691225603d6fb3324940fc51cd7f133e7ead788663c2b7eecb00c" + } + ] + }, + { + "id": "8b8fbab211078ce3", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "39ac5d72c6ba018a0f74b8b453894edc9db07b5f" + }, + { + "algorithm": "sha256", + "value": "73867d92083f2f8ab899a26ccda7ef63dfaa0032a938620eda605558958a8041" + } + ] + }, + { + "id": "03873777fed9ff43", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "c8fabeb2eeb992c368c77b9707e0d1ecfd7cf905" + }, + { + "algorithm": "sha256", + "value": "9a4cd858d9710963848e6d5f555325dc199d1c952b01cf6e64da2c15deedbd97" + } + ] + }, + { + "id": "8c39a34f27d7b8b8", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "329643357d0b78b1ef48ec155325e25f1d7534dd" + }, + { + "algorithm": "sha256", + "value": "780b3ed41786772cbc7b68136546fa3f897f28a23b30c72dde6225319c44cfff" + } + ] + }, + { + "id": "22bbedf698c71e11", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5e69ca50.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "825090fde25bbc0e71a9cb3076316b5afe459e4d" + }, + { + "algorithm": "sha256", + "value": "59c01c57b446633249f67c04b115dd6787f4378f183dff2bbf65406df93f176d" + } + ] + }, + { + "id": "13dd5ed25240c27e", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 451 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "5d4743128353b6396fad2fa2ba793ace21602295" + }, + { + "algorithm": "sha256", + "value": "db0b49163f07ffba64a5ca198bcf1688610b0bd1f0d8d5afeaf78559d73f2278" + } + ] + }, + { + "id": "2ff45fbb92b82580", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "95995311236b7a55933642ffa10ce6014f1af7d0" + }, + { + "algorithm": "sha256", + "value": "207e4696d3c05f7cb05966aee557307151f1f00217af4143c1bcaf33b8df733f" + } + ] + }, + { + "id": "5dc069665c5b6c2d", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-61666e3f.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "58d5ba4b2f3b1e927721d7a6432f298eedf72a6b" + }, + { + "algorithm": "sha256", + "value": "128d34d4aec39b0daedea8163cd8dc24dff36fd3d848630ab97eeb1d3084bbb3" + } + ] + }, + { + "id": "5d8389605f14788c", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616a9724.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "23d0f2ea1af269c2f66165e0f8a944e96bf011de" + }, + { + "algorithm": "sha256", + "value": "10877cce0a935e46ad88cb79e174a2491680508eccda08e92bf04fb9bf37fbc1" + } + ] + }, + { + "id": "5a062656ece9d33f", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616abc23.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "3529ec82670c6d4e20ee3e4968db34b551e91d50" + }, + { + "algorithm": "sha256", + "value": "4a095a9daca86da496a3cd9adcd95ee2197fdbeb84638656d469f05a4d740751" + } + ] + }, + { + "id": "c735ab9278164f87", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ac3bc.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "55a301064e11c6fe9ba0f2ca17e234f3943ccb61" + }, + { + "algorithm": "sha256", + "value": "0caf5662fde45616d88cfd7021b7bda269a2fcaf311e51c48945a967a609ec0b" + } + ] + }, + { + "id": "fe1d8dac14773061", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616adfeb.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "de1241307014aae3dba798e900f163408d98d6f4" + }, + { + "algorithm": "sha256", + "value": "ebe717d228555aa58133c202314a451f81e71f174781fd7ff8d8970d6cfa60da" + } + ] + }, + { + "id": "879b86f3d56992e3", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ae350.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "57f6b93fda4a4496fab62844ddef0eeb168f80b5" + }, + { + "algorithm": "sha256", + "value": "d11f6b21c61b4274e182eb888883a8ba8acdbf820dcc7a6d82a7d9fc2fd2836d" + } + ] + }, + { + "id": "aca7829e5caaafec", + "location": { + "path": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616db30d.rsa.pub", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 644, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 800 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "df02c9adc2906a3aa5e5ad69f50e3953e65710d0" + }, + { + "algorithm": "sha256", + "value": "40a216cbd163f22e5f16a9e0929de7cde221b9cbae8e36aa368b1e128afe0a31" + } + ] + }, + { + "id": "c2a2950ea79092e3", + "location": { + "path": "/usr/share/udhcpc/default.script", + "layerID": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6" + }, + "metadata": { + "mode": 755, + "type": "RegularFile", + "userID": 0, + "groupID": 0, + "mimeType": "text/plain", + "size": 3121 + }, + "digests": [ + { + "algorithm": "sha1", + "value": "3762910f3fd1e91a8a846aae8d0e18e84a243176" + }, + { + "algorithm": "sha256", + "value": "18982a8d0bc50408b9893fa04c00bb390d43f6787655043915cbc21edf76a4a1" + } + ] + } + ], + "source": { + "id": "8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "name": "alpine", + "version": "3.12", + "type": "image", + "metadata": { + "userInput": "alpine:3.12", + "imageID": "sha256:cc604a625da1289c5dd57f947318133161ff7f40fb03dc2a649300473b97e743", + "manifestDigest": "sha256:8cb449c3c0a4611deee6858f633eaa9378ee2eb00c2c2ae643e76e82d06b850b", + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "tags": [ + "alpine:3.12" + ], + "imageSize": 5326796, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:d80e0208345a5c0e0e5575f11f35d99a179bcdfec9a075828e774145c0245eb6", + "size": 5326796 + } + ], + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoxNDg3LCJkaWdlc3QiOiJzaGEyNTY6Y2M2MDRhNjI1ZGExMjg5YzVkZDU3Zjk0NzMxODEzMzE2MWZmN2Y0MGZiMDNkYzJhNjQ5MzAwNDczYjk3ZTc0MyJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjo1NjE2MTI4LCJkaWdlc3QiOiJzaGEyNTY6ZDgwZTAyMDgzNDVhNWMwZTBlNTU3NWYxMWYzNWQ5OWExNzliY2RmZWM5YTA3NTgyOGU3NzQxNDVjMDI0NWViNiJ9XX0=", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhcm02NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giXSwiSW1hZ2UiOiJzaGEyNTY6MDZhOWI5YTE3NTgwYjU2YmUxYzVlMjhjMzBjMWEzYjQ2M2JjZjY3MTRhMjcxMzQ1ZTQwNzUxNTY2YWU1M2M1OSIsIlZvbHVtZXMiOm51bGwsIldvcmtpbmdEaXIiOiIiLCJFbnRyeXBvaW50IjpudWxsLCJPbkJ1aWxkIjpudWxsLCJMYWJlbHMiOm51bGx9LCJjb250YWluZXIiOiJiZWUwNDg4MDRjYzgwZDA2ZTQ2ZGJkYzk2YTZkZjY4OTJmNjJhYjRhNTNiMzBiMDA1NTcxZTkwYzRjYTVhMjgzIiwiY29udGFpbmVyX2NvbmZpZyI6eyJIb3N0bmFtZSI6ImJlZTA0ODgwNGNjOCIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giLCItYyIsIiMobm9wKSAiLCJDTUQgW1wiL2Jpbi9zaFwiXSJdLCJJbWFnZSI6InNoYTI1NjowNmE5YjlhMTc1ODBiNTZiZTFjNWUyOGMzMGMxYTNiNDYzYmNmNjcxNGEyNzEzNDVlNDA3NTE1NjZhZTUzYzU5IiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6e319LCJjcmVhdGVkIjoiMjAyMi0wNC0wNFQyMzozOTo1My41NTU0MjI1MzZaIiwiZG9ja2VyX3ZlcnNpb24iOiIyMC4xMC4xMiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA0LTA0VDIzOjM5OjUzLjQzNDc0NDIwNFoiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YTJhOTkyYjdmNmFmMWU2ZjhmNTY0OGYzMjlmNGE0MDU4ZDhjNDM3NzQxN2FjMjNhZTIxMTI5MGMwY2RjOGY0YiBpbiAvICJ9LHsiY3JlYXRlZCI6IjIwMjItMDQtMDRUMjM6Mzk6NTMuNTU1NDIyNTM2WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSAgQ01EIFtcIi9iaW4vc2hcIl0iLCJlbXB0eV9sYXllciI6dHJ1ZX1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZDgwZTAyMDgzNDVhNWMwZTBlNTU3NWYxMWYzNWQ5OWExNzliY2RmZWM5YTA3NTgyOGU3NzQxNDVjMDI0NWViNiJdfSwidmFyaWFudCI6InY4In0=", + "repoDigests": [ + "alpine@sha256:c75ac27b49326926b803b9ed43bf088bc220d22556de1bc5f72d742c91398f69" + ], + "architecture": "arm64", + "os": "linux" + } + }, + "distro": { + "prettyName": "Alpine Linux v3.12", + "name": "Alpine Linux", + "id": "alpine", + "versionID": "3.12.12", + "homeURL": "https://alpinelinux.org/", + "bugReportURL": "https://bugs.alpinelinux.org/" + }, + "descriptor": { + "name": "syft", + "version": "0.101.1", + "configuration": { + "catalogers": { + "requested": { + "default": [ + "image" + ] + }, + "used": [ + "alpm-db-cataloger", + "apk-db-cataloger", + "binary-cataloger", + "cargo-auditable-binary-cataloger", + "conan-info-cataloger", + "dotnet-portable-executable-cataloger", + "dpkg-db-cataloger", + "go-module-binary-cataloger", + "graalvm-native-image-cataloger", + "java-archive-cataloger", + "javascript-package-cataloger", + "nix-store-cataloger", + "php-composer-installed-cataloger", + "portage-cataloger", + "python-installed-package-cataloger", + "r-package-cataloger", + "rpm-db-cataloger", + "ruby-installed-gemspec-cataloger", + "sbom-cataloger" + ] + }, + "data-generation": { + "generate-cpes": true + }, + "files": { + "content": { + "globs": null, + "skip-files-above-size": 0 + }, + "hashers": [ + "sha-1", + "sha-256" + ], + "selection": "owned-by-package" + }, + "packages": { + "binary": [ + "python-binary", + "python-binary-lib", + "pypy-binary-lib", + "go-binary", + "julia-binary", + "helm", + "redis-binary", + "java-binary-openjdk", + "java-binary-ibm", + "java-binary-oracle", + "nodejs-binary", + "go-binary-hint", + "busybox-binary", + "haproxy-binary", + "perl-binary", + "php-cli-binary", + "php-fpm-binary", + "php-apache-binary", + "php-composer-binary", + "httpd-binary", + "memcached-binary", + "traefik-binary", + "postgresql-binary", + "mysql-binary", + "mysql-binary", + "mysql-binary", + "xtrabackup-binary", + "mariadb-binary", + "rust-standard-library-linux", + "rust-standard-library-macos", + "ruby-binary", + "erlang-binary", + "consul-binary", + "nginx-binary", + "bash-binary", + "openssl-binary", + "gcc-binary", + "wordpress-cli-binary" + ], + "golang": { + "local-mod-cache-dir": "/Users/wagoodman/go/pkg/mod", + "proxies": [ + "https://proxy.golang.org", + "direct" + ], + "search-local-mod-cache-licenses": false, + "search-remote-licenses": false + }, + "java-archive": { + "include-indexed-archives": true, + "include-unindexed-archives": false, + "maven-base-url": "https://repo1.maven.org/maven2", + "max-parent-recursive-depth": 5, + "use-network": false + }, + "javascript": { + "npm-base-url": "https://registry.npmjs.org", + "search-remote-licenses": false + }, + "linux-kernel": { + "catalog-modules": true + }, + "python": { + "guess-unpinned-requirements": false + } + }, + "relationships": { + "exclude-binary-packages-with-file-ownership-overlap": true, + "package-file-ownership": true, + "package-file-ownership-overlap": true + }, + "search": { + "scope": "squashed" + } + } + }, + "schema": { + "version": "13.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-13.0.0.json" + } +} diff --git a/examples/decode_sbom/main.go b/examples/decode_sbom/main.go new file mode 100644 index 00000000000..fbdcb766756 --- /dev/null +++ b/examples/decode_sbom/main.go @@ -0,0 +1,53 @@ +package main + +import ( + _ "embed" + "fmt" + "io" + "os" + "strings" + + "github.com/anchore/syft/syft/format" +) + +//go:embed alpine.syft.json +var sbomContents string + +func main() { + // decode the SBOM + fmt.Println("decoding SBOM...") + sbom, sbomFormat, formatVersion, err := format.Decode(sbomReader()) + if err != nil { + fmt.Printf("failed to decode sbom: %+v\n", err) + os.Exit(1) + } + + fmt.Printf("SBOM format: %s@%s\n", sbomFormat, formatVersion) + + // print packages found... + fmt.Println("\nPackages found:") + for _, pkg := range sbom.Artifacts.Packages.Sorted() { + fmt.Printf(" %s : %s@%s (%s)\n", pkg.ID(), pkg.Name, pkg.Version, pkg.Type) + } + + // print files found... + fmt.Println("\nFiles found:") + for c, f := range sbom.Artifacts.FileMetadata { + fmt.Printf(" %s : %s\n", c.ID(), f.Path) + } +} + +func sbomReader() io.Reader { + // read file from sys args (or use the default) + var reader io.Reader + if len(os.Args) < 2 { + reader = strings.NewReader(sbomContents) + } else { + var err error + reader, err = os.Open(os.Args[1]) + if err != nil { + panic(err) + } + } + return reader +} diff --git a/examples/source_detection/main.go b/examples/source_detection/main.go new file mode 100644 index 00000000000..70fabbd047e --- /dev/null +++ b/examples/source_detection/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "encoding/json" + "os" + + "github.com/anchore/syft/syft/source" +) + +/* + This example demonstrates how to create a source object from a generic string input. + + Example inputs: + alpine:3.19 pull an image from the docker daemon, podman, or the registry (based on what's available) + ./my.tar interpret a local archive as an OCI archive, docker save archive, or raw file from disk to catalog + docker:yourrepo/yourimage:tag explicitly use the Docker daemon + podman:yourrepo/yourimage:tag explicitly use the Podman daemon + registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required) + docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save" + oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise) + oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise) + singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk + dir:path/to/yourproject read directly from a path on disk (any directory) + file:path/to/yourproject/file read directly from a path on disk (any single file) + +*/ + +const defaultImage = "alpine:3.19" + +func main() { + detection, err := source.Detect( + imageReference(), + source.DetectConfig{ + DefaultImageSource: "docker", + }, + ) + + if err != nil { + panic(err) + } + + src, err := detection.NewSource(source.DefaultDetectionSourceConfig()) + + if err != nil { + panic(err) + } + + // Show a basic description of the source to the screen + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(src.Describe()); err != nil { + panic(err) + } +} + +func imageReference() string { + // read an image string reference from the command line or use a default + if len(os.Args) > 1 { + return os.Args[1] + } + return defaultImage +} diff --git a/examples/source_from_image/main.go b/examples/source_from_image/main.go new file mode 100644 index 00000000000..b5f8872feba --- /dev/null +++ b/examples/source_from_image/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "encoding/json" + "os" + + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/syft/syft/source" +) + +/* + This shows how to create a source from an image reference. This is useful when you are programmatically always + expecting to catalog a container image and always from the same source (e.g. docker daemon, podman, registry, etc). +*/ + +const defaultImage = "alpine:3.19" + +func main() { + platform, err := image.NewPlatform("linux/amd64") + if err != nil { + panic(err) + } + + src, err := source.NewFromStereoscopeImage( + source.StereoscopeImageConfig{ + Reference: imageReference(), + From: image.OciRegistrySource, // always use the registry, there are several other "Source" options here + Platform: platform, + }, + ) + + if err != nil { + panic(err) + } + + // Show a basic description of the source to the screen + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(src.Describe()); err != nil { + panic(err) + } +} + +func imageReference() string { + // read an image string reference from the command line or use a default + if len(os.Args) > 1 { + return os.Args[1] + } + return defaultImage +} diff --git a/go.mod b/go.mod index e9228eb9a0d..e3081cd4d91 100644 --- a/go.mod +++ b/go.mod @@ -3,45 +3,48 @@ module github.com/anchore/syft go 1.21.0 require ( - github.com/CycloneDX/cyclonedx-go v0.7.2 + github.com/CycloneDX/cyclonedx-go v0.8.0 github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig/v3 v3.2.3 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/acobaugh/osrelease v0.1.0 - github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 - github.com/anchore/clio v0.0.0-20231016125544-c98a83e1c7fc - github.com/anchore/fangs v0.0.0-20231103141714-84c94dc43a2e + github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 + github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65 + github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b - github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20231117203853-3610f4ef3e83 + github.com/anchore/packageurl-go v0.1.1-0.20240202171727-877e1747d426 + github.com/anchore/stereoscope v0.0.2-0.20240216182029-6171ee21e1d5 + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // we are hinting brotli to latest due to warning when installing archiver v3: // go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/bmatcuk/doublestar/v4 v4.6.1 - github.com/charmbracelet/bubbletea v0.24.2 + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/lipgloss v0.9.1 github.com/dave/jennifer v1.7.0 github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da github.com/distribution/reference v0.5.0 - github.com/docker/docker v24.0.7+incompatible + github.com/docker/docker v25.0.3+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/github/go-spdx/v2 v2.2.0 - github.com/gkampitakis/go-snaps v0.4.12 + github.com/gkampitakis/go-snaps v0.5.2 github.com/go-git/go-billy/v5 v5.5.0 - github.com/go-git/go-git/v5 v5.10.0 + github.com/go-git/go-git/v5 v5.11.0 github.com/go-test/deep v1.1.0 github.com/google/go-cmp v0.6.0 - github.com/google/go-containerregistry v0.16.1 + github.com/google/go-containerregistry v0.19.0 github.com/google/licensecheck v0.3.1 - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/hashicorp/go-multierror v1.1.1 github.com/iancoleman/strcase v0.3.0 github.com/invopop/jsonschema v0.7.0 + github.com/jedib0t/go-pretty/v6 v6.5.4 github.com/jinzhu/copier v0.4.0 github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b @@ -53,15 +56,15 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/opencontainers/go-digest v1.0.0 github.com/pelletier/go-toml v1.9.5 - github.com/saferwall/pe v1.4.7 + github.com/saferwall/pe v1.5.2 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/sanity-io/litter v1.5.5 - github.com/sassoftware/go-rpmutils v0.2.0 + github.com/sassoftware/go-rpmutils v0.3.0 // pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e github.com/sergi/go-diff v1.3.1 github.com/spdx/tools-golang v0.5.3 - github.com/spf13/afero v1.10.0 + github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 github.com/vbatts/go-mtree v0.5.3 @@ -70,36 +73,35 @@ require ( github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 - golang.org/x/mod v0.14.0 - golang.org/x/net v0.18.0 + go.uber.org/goleak v1.3.0 + golang.org/x/mod v0.15.0 + golang.org/x/net v0.21.0 gopkg.in/yaml.v3 v3.0.1 - modernc.org/sqlite v1.27.0 + modernc.org/sqlite v1.29.1 ) require ( dario.cat/mergo v1.0.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/DataDog/zstd v1.4.5 // indirect + github.com/DataDog/zstd v1.5.5 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.1 // indirect + github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect - github.com/acomagu/bufpipe v1.0.4 // indirect github.com/adrg/xdg v0.4.0 // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect + github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/becheran/wildmatch-go v1.0.0 // indirect - github.com/charmbracelet/bubbles v0.16.1 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect - github.com/containerd/containerd v1.7.8 // indirect + github.com/containerd/containerd v1.7.11 // indirect github.com/containerd/continuity v0.4.2 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect @@ -118,12 +120,13 @@ require ( github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/felixge/fgprof v0.9.3 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.0 // indirect github.com/gkampitakis/ciinfo v0.3.0 // indirect github.com/gkampitakis/go-diff v1.3.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -132,22 +135,22 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/maruel/natural v1.1.0 // indirect + github.com/maruel/natural v1.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect @@ -163,9 +166,10 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nwaples/rardecode v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect - github.com/opencontainers/runc v1.1.5 // indirect + github.com/opencontainers/runc v1.1.12 // indirect github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect github.com/opencontainers/selinux v1.11.0 // indirect github.com/pborman/indent v1.2.1 // indirect @@ -176,13 +180,14 @@ require ( github.com/pkg/profile v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rivo/uniseg v0.4.6 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/skeema/knownhosts v1.2.0 // indirect + github.com/skeema/knownhosts v1.2.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -195,7 +200,7 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/ulikunitz/xz v0.5.10 // indirect + github.com/ulikunitz/xz v0.5.11 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -204,36 +209,37 @@ require ( github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.14.0 // indirect - go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/term v0.14.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.17.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect - google.golang.org/grpc v1.58.3 // indirect + google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - lukechampine.com/uint128 v1.2.0 // indirect - modernc.org/cc/v3 v3.40.0 // indirect - modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.29.0 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.41.0 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.7.2 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.1 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect ) retract ( v0.53.2 v0.53.1 // Published accidentally with incorrect license in depdencies ) + +replace github.com/mholt/archiver/v3 v3.5.1 => github.com/anchore/archiver/v3 v3.5.2 diff --git a/go.sum b/go.sum index 4a6c6575a69..fad0f4b7c00 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -16,7 +15,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -47,7 +45,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -55,17 +52,17 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CycloneDX/cyclonedx-go v0.7.2 h1:kKQ0t1dPOlugSIYVOMiMtFqeXI2wp/f5DBIdfux8gnQ= -github.com/CycloneDX/cyclonedx-go v0.7.2/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= +github.com/CycloneDX/cyclonedx-go v0.8.0 h1:FyWVj6x6hoJrui5uRQdYZcSievw3Z32Z88uYzG/0D6M= +github.com/CycloneDX/cyclonedx-go v0.8.0/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -77,8 +74,8 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= -github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= @@ -86,20 +83,20 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY= -github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= -github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 h1:xGu4/uMWucwWV0YV3fpFIQZ6KVfS/Wfhmma8t0s0vRo= -github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk= -github.com/anchore/clio v0.0.0-20231016125544-c98a83e1c7fc h1:A1KFO+zZZmbNlz1+WKsCF0RKVx6XRoxsAG3lrqH9hUQ= -github.com/anchore/clio v0.0.0-20231016125544-c98a83e1c7fc/go.mod h1:QeWvNzxsrUNxcs6haQo3OtISfXUXW0qAuiG4EQiz0GU= -github.com/anchore/fangs v0.0.0-20231103141714-84c94dc43a2e h1:O8ZubApaSl7dRzKNvyfGq9cLIPLQ5v3Iz0Y3huHKCgg= -github.com/anchore/fangs v0.0.0-20231103141714-84c94dc43a2e/go.mod h1:yPsN3NUGhU5dcBtYBa1dMNzGu1yT5ZAfSjKq9DY4aV8= +github.com/anchore/archiver/v3 v3.5.2 h1:Bjemm2NzuRhmHy3m0lRe5tNoClB9A4zYyDV58PaB6aA= +github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= +github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU= +github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw= +github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65 h1:u9XrEabKlGPsrmRvAER+kUKkwXiJfLyqGhmOTFsXjX4= +github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65/go.mod h1:8Jr7CjmwFVcBPtkJdTpaAGHimoGJGfbExypjzOu87Og= +github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b h1:L/djgY7ZbZ/38+wUtdkk398W3PIBJLkt1N8nU/7e47A= +github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b/go.mod h1:TLcE0RE5+8oIx2/NPWem/dq1DeaMoC+fPEH7hoSzPLo= github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a h1:nJ2G8zWKASyVClGVgG7sfM5mwoZlZ2zYpIzN2OhjWkw= github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg= github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU= @@ -110,10 +107,10 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= -github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= -github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20231117203853-3610f4ef3e83 h1:mxGIOmj+asEm8LUkPTG3/v0hi27WIlDVjiEVsUB9eqY= -github.com/anchore/stereoscope v0.0.0-20231117203853-3610f4ef3e83/go.mod h1:GKAnytSVV1hoqB5r5Gd9M5Ph3Rzqq0zPdEJesewjC2w= +github.com/anchore/packageurl-go v0.1.1-0.20240202171727-877e1747d426 h1:agoiZchSf1Nnnos1azwIg5hk5Ao9TzZNBD9++AChGEg= +github.com/anchore/packageurl-go v0.1.1-0.20240202171727-877e1747d426/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= +github.com/anchore/stereoscope v0.0.2-0.20240216182029-6171ee21e1d5 h1:o//fhRcSpOYHC/xG/HiI6ddtSMiRgHlB96xJQXZawZM= +github.com/anchore/stereoscope v0.0.2-0.20240216182029-6171ee21e1d5/go.mod h1:o0TqYkefad6kIPtmbigFKss7P48z4bjd8Vp5Wklbf3Y= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= @@ -132,6 +129,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA= @@ -145,29 +144,30 @@ github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTS github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= -github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= -github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= -github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -180,11 +180,10 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/containerd/containerd v1.7.8 h1:RkwgOW3AVUT3H/dyT0W03Dc8AzlpMG65lX48KftOFSM= -github.com/containerd/containerd v1.7.8/go.mod h1:L/Hn9qylJtUFT7cPeM0Sr3fATj+WjHwRQ0lyrYk3OPY= +github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= +github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= @@ -204,7 +203,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= @@ -223,15 +221,14 @@ github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qe github.com/docker/cli v24.0.0+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/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= +github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= @@ -266,7 +263,8 @@ github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -281,8 +279,8 @@ github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvp github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= -github.com/gkampitakis/go-snaps v0.4.12 h1:YeMgKOm0XW3f/Pt2rYpUlpyF8nG6lYGe9oXFJw5LdME= -github.com/gkampitakis/go-snaps v0.4.12/go.mod h1:PpnF1KPXQAHBdb/DHoi/1VmlwE+ZkVHzl+QHmgzMSz8= +github.com/gkampitakis/go-snaps v0.5.2 h1:ay/6f7WHwRkOgpBec9DjMLRBAApziJommZ21NkOOCwY= +github.com/gkampitakis/go-snaps v0.5.2/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y= github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= @@ -293,8 +291,8 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+ github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= 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/v5 v5.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ= -github.com/go-git/go-git/v5 v5.10.0/go.mod h1:1FOZ/pQnqw24ghP2n7cunVl0ON55BsjPYvhWHvZGhoo= +github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= +github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -303,8 +301,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc= @@ -314,7 +312,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -373,8 +370,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ= -github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= +github.com/google/go-containerregistry v0.19.0 h1:uIsMRBV7m/HDkDxE/nXMnv1q+lOOSPlQ/ywc5JbB8Ic= +github.com/google/go-containerregistry v0.19.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs= github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY= @@ -391,7 +388,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -403,17 +399,19 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S3 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -440,6 +438,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -469,6 +469,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/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/jedib0t/go-pretty/v6 v6.5.4 h1:gOGo0613MoqUcf0xCj+h/V3sHDaZasfv152G6/5l91s= +github.com/jedib0t/go-pretty/v6 v6.5.4/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -480,17 +482,14 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 h1:WdAeg/imY2JFPc/9CST4bZ80nNJbiBFCAdSZCSgrS5Y= github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953/go.mod h1:6o+UrvuZWc4UTyBhQf0LGjW9Ld7qJxLz/OqvSOWWlEc= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -501,13 +500,14 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -516,10 +516,8 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8 github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ= -github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -547,8 +545,6 @@ github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= -github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 h1:tQRHcLQwnwrPq2j2Qra/NnyjyESBGwdeBeVdAE9kXYg= github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5/go.mod h1:vYT9HE7WCvL64iVeZylKmCsWKfE+JZ8105iuh2Trk8g= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -573,7 +569,6 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= @@ -589,7 +584,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -599,6 +593,8 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -609,12 +605,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w= github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -639,7 +633,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -664,35 +657,37 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg= +github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/saferwall/pe v1.4.7 h1:A+G3DxX49paJ5OsxBfHKskhyDtmTjShlDmBd81IsHlQ= -github.com/saferwall/pe v1.4.7/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= +github.com/saferwall/pe v1.5.2 h1:h5lLtLsyxGHQ9dN6cd8EfeLEBEo5gdqJpkuw4o4vTMY= +github.com/saferwall/pe v1.5.2/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= -github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE= -github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI= +github.com/sassoftware/go-rpmutils v0.3.0 h1:tE4TZ8KcOXay5iIP64P291s6Qxd9MQCYhI7DU+f3gFA= +github.com/sassoftware/go-rpmutils v0.3.0/go.mod h1:hM9wdxFsjUFR/tJ6SMsLrJuChcucCa0DsCzE9RMfwMo= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= @@ -707,8 +702,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= -github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -717,8 +712,8 @@ github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQr github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= @@ -757,7 +752,6 @@ github.com/sylabs/sif/v2 v2.11.5 h1:7ssPH3epSonsTrzbS1YxeJ9KuqAN7ISlSM61a7j/mQM= github.com/sylabs/sif/v2 v2.11.5/go.mod h1:GBoZs9LU3e4yJH1dcZ3Akf/jsqYgy5SeguJQC+zd75Y= github.com/sylabs/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ= github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= @@ -775,9 +769,8 @@ github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= -github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w= @@ -786,8 +779,6 @@ github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RV github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0= github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20= github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 h1:0KGbf+0SMg+UFy4e1A/CPVvXn21f1qtWdeJwxZFoQG8= @@ -827,17 +818,28 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= -go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= -go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= -go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= @@ -851,17 +853,14 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -872,8 +871,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -902,8 +901,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -938,7 +937,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -952,8 +950,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -984,8 +982,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ 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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -999,7 +997,6 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1007,7 +1004,6 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1033,7 +1029,6 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1041,7 +1036,6 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1051,12 +1045,10 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1071,16 +1063,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.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.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1098,8 +1089,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1116,7 +1107,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1149,7 +1139,6 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1158,8 +1147,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1240,9 +1229,7 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1270,10 +1257,12 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1301,8 +1290,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1352,34 +1341,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= -modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= +modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8= -modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= -modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= -modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= +modernc.org/sqlite v1.29.1 h1:19GY2qvWB4VPw0HppFlZCPAbmxFU41r+qjKZQdQ1ryA= +modernc.org/sqlite v1.29.1/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/bus/helpers.go b/internal/bus/helpers.go index b2c883ebdaa..363d9754d53 100644 --- a/internal/bus/helpers.go +++ b/internal/bus/helpers.go @@ -2,10 +2,12 @@ package bus import ( "github.com/wagoodman/go-partybus" + "github.com/wagoodman/go-progress" "github.com/anchore/clio" "github.com/anchore/syft/internal/redact" "github.com/anchore/syft/syft/event" + "github.com/anchore/syft/syft/event/monitor" ) func Exit() { @@ -33,3 +35,18 @@ func Notify(message string) { Value: message, }) } + +func StartCatalogerTask(info monitor.GenericTask, size int64, initialStage string) *monitor.CatalogerTaskProgress { + t := &monitor.CatalogerTaskProgress{ + AtomicStage: progress.NewAtomicStage(initialStage), + Manual: progress.NewManual(size), + } + + Publish(partybus.Event{ + Type: event.CatalogerTaskStarted, + Source: info, + Value: progress.StagedProgressable(t), + }) + + return t +} diff --git a/internal/constants.go b/internal/constants.go index 78981d38824..da5a77e4904 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -3,5 +3,5 @@ package internal const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "13.0.0" + JSONSchemaVersion = "16.0.4" ) diff --git a/internal/file/digest.go b/internal/file/digest.go index 4bc8c42325a..8c9932d9fe2 100644 --- a/internal/file/digest.go +++ b/internal/file/digest.go @@ -22,6 +22,7 @@ func supportedHashAlgorithms() []crypto.Hash { } func NewDigestsFromFile(closer io.ReadCloser, hashes []crypto.Hash) ([]file.Digest, error) { + hashes = NormalizeHashes(hashes) // create a set of hasher objects tied together with a single writer to feed content into hashers := make([]hash.Hash, len(hashes)) writers := make([]io.Writer, len(hashes)) @@ -67,7 +68,7 @@ func Hashers(names ...string) ([]crypto.Hash, error) { } hashers = append(hashers, hashObj) } - return hashers, nil + return NormalizeHashes(hashers), nil } func CleanDigestAlgorithmName(name string) string { diff --git a/internal/file/normalize_hashes.go b/internal/file/normalize_hashes.go new file mode 100644 index 00000000000..1b9ac71965f --- /dev/null +++ b/internal/file/normalize_hashes.go @@ -0,0 +1,24 @@ +package file + +import ( + "crypto" + "sort" + + "github.com/scylladb/go-set/uset" +) + +func NormalizeHashes(hashes []crypto.Hash) []crypto.Hash { + set := uset.New() + for _, h := range hashes { + set.Add(uint(h)) + } + list := set.List() + sort.Slice(list, func(i, j int) bool { + return list[i] < list[j] + }) + result := make([]crypto.Hash, len(list)) + for i, v := range list { + result[i] = crypto.Hash(v) + } + return result +} diff --git a/internal/file/normalize_hashes_test.go b/internal/file/normalize_hashes_test.go new file mode 100644 index 00000000000..8667ec6050c --- /dev/null +++ b/internal/file/normalize_hashes_test.go @@ -0,0 +1,44 @@ +package file + +import ( + "crypto" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNormalizeHashes(t *testing.T) { + + tests := []struct { + name string + input []crypto.Hash + want []crypto.Hash + }{ + { + name: "deduplicate hashes", + input: []crypto.Hash{ + crypto.SHA1, + crypto.SHA1, + }, + want: []crypto.Hash{ + crypto.SHA1, + }, + }, + { + name: "sort hashes", + input: []crypto.Hash{ + crypto.SHA512, + crypto.SHA1, + }, + want: []crypto.Hash{ + crypto.SHA1, + crypto.SHA512, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, NormalizeHashes(tt.input)) + }) + } +} diff --git a/internal/mimetype_helper.go b/internal/mimetype/mimetype_helper.go similarity index 99% rename from internal/mimetype_helper.go rename to internal/mimetype/mimetype_helper.go index 8ddf29fb3fa..3362a3d0f18 100644 --- a/internal/mimetype_helper.go +++ b/internal/mimetype/mimetype_helper.go @@ -1,4 +1,4 @@ -package internal +package mimetype import "github.com/scylladb/go-set/strset" diff --git a/internal/mimetype_helper_test.go b/internal/mimetype/mimetype_helper_test.go similarity index 98% rename from internal/mimetype_helper_test.go rename to internal/mimetype/mimetype_helper_test.go index 9a3f245e82f..8c6da33d83a 100644 --- a/internal/mimetype_helper_test.go +++ b/internal/mimetype/mimetype_helper_test.go @@ -1,4 +1,4 @@ -package internal +package mimetype import ( "testing" diff --git a/syft/pkg/relationships_by_file_ownership.go b/internal/relationship/by_file_ownership.go similarity index 70% rename from syft/pkg/relationships_by_file_ownership.go rename to internal/relationship/by_file_ownership.go index 8dc5c88e431..3c420217c0d 100644 --- a/syft/pkg/relationships_by_file_ownership.go +++ b/internal/relationship/by_file_ownership.go @@ -1,4 +1,4 @@ -package pkg +package relationship import ( "sort" @@ -7,20 +7,23 @@ import ( "github.com/scylladb/go-set/strset" "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/internal/sbomsync" "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" ) -// AltRpmDBGlob allows db matches against new locations introduced in fedora:{36,37} +// altRpmDBGlob allows db matches against new locations introduced in fedora:{36,37} // See https://github.com/anchore/syft/issues/1077 for larger context -const AltRpmDBGlob = "**/rpm/{Packages,Packages.db,rpmdb.sqlite}" +const altRpmDBGlob = "**/rpm/{Packages,Packages.db,rpmdb.sqlite}" var globsForbiddenFromBeingOwned = []string{ // any OS DBs should automatically be ignored to prevent cyclic issues (e.g. the "rpm" RPM owns the path to the // RPM DB, so if not ignored that package would own all other packages on the system). - ApkDBGlob, - DpkgDBGlob, - RpmDBGlob, - AltRpmDBGlob, + pkg.ApkDBGlob, + pkg.DpkgDBGlob, + pkg.RpmDBGlob, + altRpmDBGlob, // DEB packages share common copyright info between, this does not mean that sharing these paths implies ownership. "/usr/share/doc/**/copyright", } @@ -29,9 +32,21 @@ type ownershipByFilesMetadata struct { Files []string `json:"files"` } -// RelationshipsByFileOwnership creates a package-to-package relationship based on discovering which packages have +func byFileOwnershipOverlapWorker(accessor sbomsync.Accessor) { + var relationships []artifact.Relationship + + accessor.ReadFromSBOM(func(s *sbom.SBOM) { + relationships = byFileOwnershipOverlap(s.Artifacts.Packages) + }) + + accessor.WriteToSBOM(func(s *sbom.SBOM) { + s.Relationships = append(s.Relationships, relationships...) + }) +} + +// byFileOwnershipOverlap creates a package-to-package relationship based on discovering which packages have // evidence locations that overlap with ownership claim from another package's package manager metadata. -func RelationshipsByFileOwnership(catalog *Collection) []artifact.Relationship { +func byFileOwnershipOverlap(catalog *pkg.Collection) []artifact.Relationship { var relationships = findOwnershipByFilesRelationships(catalog) var edges []artifact.Relationship @@ -39,9 +54,13 @@ func RelationshipsByFileOwnership(catalog *Collection) []artifact.Relationship { for childID, files := range children { fs := files.List() sort.Strings(fs) + + parent := catalog.Package(parentID) // TODO: this is potentially expensive + child := catalog.Package(childID) // TODO: this is potentially expensive + edges = append(edges, artifact.Relationship{ - From: catalog.byID[parentID], - To: catalog.byID[childID], + From: parent, + To: child, Type: artifact.OwnershipByFileOverlapRelationship, Data: ownershipByFilesMetadata{ Files: fs, @@ -55,7 +74,7 @@ func RelationshipsByFileOwnership(catalog *Collection) []artifact.Relationship { // findOwnershipByFilesRelationships find overlaps in file ownership with a file that defines another package. Specifically, a .Location.Path of // a package is found to be owned by another (from the owner's .Metadata.Files[]). -func findOwnershipByFilesRelationships(catalog *Collection) map[artifact.ID]map[artifact.ID]*strset.Set { +func findOwnershipByFilesRelationships(catalog *pkg.Collection) map[artifact.ID]map[artifact.ID]*strset.Set { var relationships = make(map[artifact.ID]map[artifact.ID]*strset.Set) if catalog == nil { @@ -69,7 +88,7 @@ func findOwnershipByFilesRelationships(catalog *Collection) map[artifact.ID]map[ } // check to see if this is a file owner - pkgFileOwner, ok := candidateOwnerPkg.Metadata.(FileOwner) + pkgFileOwner, ok := candidateOwnerPkg.Metadata.(pkg.FileOwner) if !ok { continue } diff --git a/syft/pkg/relationships_by_file_ownership_test.go b/internal/relationship/by_file_ownership_test.go similarity index 71% rename from syft/pkg/relationships_by_file_ownership_test.go rename to internal/relationship/by_file_ownership_test.go index c155a8a4fa5..3961502406b 100644 --- a/syft/pkg/relationships_by_file_ownership_test.go +++ b/internal/relationship/by_file_ownership_test.go @@ -1,4 +1,4 @@ -package pkg +package relationship import ( "testing" @@ -7,25 +7,26 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" ) func TestOwnershipByFilesRelationship(t *testing.T) { tests := []struct { name string - setup func(t testing.TB) ([]Package, []artifact.Relationship) + setup func(t testing.TB) ([]pkg.Package, []artifact.Relationship) }{ { name: "owns-by-real-path", - setup: func(t testing.TB) ([]Package, []artifact.Relationship) { - parent := Package{ + setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) { + parent := pkg.Package{ Locations: file.NewLocationSet( file.NewVirtualLocation("/a/path", "/another/path"), file.NewVirtualLocation("/b/path", "/bee/path"), ), - Type: RpmPkg, - Metadata: RpmDBEntry{ - Files: []RpmFileRecord{ + Type: pkg.RpmPkg, + Metadata: pkg.RpmDBEntry{ + Files: []pkg.RpmFileRecord{ {Path: "/owning/path/1"}, {Path: "/owning/path/2"}, {Path: "/d/path"}, @@ -34,12 +35,12 @@ func TestOwnershipByFilesRelationship(t *testing.T) { } parent.SetID() - child := Package{ + child := pkg.Package{ Locations: file.NewLocationSet( file.NewVirtualLocation("/c/path", "/another/path"), file.NewVirtualLocation("/d/path", "/another/path"), ), - Type: NpmPkg, + Type: pkg.NpmPkg, } child.SetID() @@ -54,20 +55,20 @@ func TestOwnershipByFilesRelationship(t *testing.T) { }, } - return []Package{parent, child}, []artifact.Relationship{relationship} + return []pkg.Package{parent, child}, []artifact.Relationship{relationship} }, }, { name: "owns-by-virtual-path", - setup: func(t testing.TB) ([]Package, []artifact.Relationship) { - parent := Package{ + setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) { + parent := pkg.Package{ Locations: file.NewLocationSet( file.NewVirtualLocation("/a/path", "/some/other/path"), file.NewVirtualLocation("/b/path", "/bee/path"), ), - Type: RpmPkg, - Metadata: RpmDBEntry{ - Files: []RpmFileRecord{ + Type: pkg.RpmPkg, + Metadata: pkg.RpmDBEntry{ + Files: []pkg.RpmFileRecord{ {Path: "/owning/path/1"}, {Path: "/owning/path/2"}, {Path: "/another/path"}, @@ -76,12 +77,12 @@ func TestOwnershipByFilesRelationship(t *testing.T) { } parent.SetID() - child := Package{ + child := pkg.Package{ Locations: file.NewLocationSet( file.NewVirtualLocation("/c/path", "/another/path"), file.NewLocation("/d/path"), ), - Type: NpmPkg, + Type: pkg.NpmPkg, } child.SetID() @@ -95,20 +96,20 @@ func TestOwnershipByFilesRelationship(t *testing.T) { }, }, } - return []Package{parent, child}, []artifact.Relationship{relationship} + return []pkg.Package{parent, child}, []artifact.Relationship{relationship} }, }, { name: "ignore-empty-path", - setup: func(t testing.TB) ([]Package, []artifact.Relationship) { - parent := Package{ + setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) { + parent := pkg.Package{ Locations: file.NewLocationSet( file.NewVirtualLocation("/a/path", "/some/other/path"), file.NewVirtualLocation("/b/path", "/bee/path"), ), - Type: RpmPkg, - Metadata: RpmDBEntry{ - Files: []RpmFileRecord{ + Type: pkg.RpmPkg, + Metadata: pkg.RpmDBEntry{ + Files: []pkg.RpmFileRecord{ {Path: "/owning/path/1"}, {Path: "/owning/path/2"}, {Path: ""}, @@ -118,17 +119,17 @@ func TestOwnershipByFilesRelationship(t *testing.T) { parent.SetID() - child := Package{ + child := pkg.Package{ Locations: file.NewLocationSet( file.NewVirtualLocation("/c/path", "/another/path"), file.NewLocation("/d/path"), ), - Type: NpmPkg, + Type: pkg.NpmPkg, } child.SetID() - return []Package{parent, child}, nil + return []pkg.Package{parent, child}, nil }, }, } @@ -136,8 +137,8 @@ func TestOwnershipByFilesRelationship(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { pkgs, expectedRelations := test.setup(t) - c := NewCollection(pkgs...) - relationships := RelationshipsByFileOwnership(c) + c := pkg.NewCollection(pkgs...) + relationships := byFileOwnershipOverlap(c) assert.Len(t, relationships, len(expectedRelations)) for idx, expectedRelationship := range expectedRelations { diff --git a/syft/pkg/relationships_evident_by.go b/internal/relationship/evident_by.go similarity index 67% rename from syft/pkg/relationships_evident_by.go rename to internal/relationship/evident_by.go index 26512a91767..d241b512c5b 100644 --- a/syft/pkg/relationships_evident_by.go +++ b/internal/relationship/evident_by.go @@ -1,14 +1,15 @@ -package pkg +package relationship import ( "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" ) -func RelationshipsEvidentBy(catalog *Collection) []artifact.Relationship { +func evidentBy(catalog *pkg.Collection) []artifact.Relationship { var edges []artifact.Relationship for _, p := range catalog.Sorted() { for _, l := range p.Locations.ToSlice() { - if v, exists := l.Annotations[EvidenceAnnotationKey]; !exists || v != PrimaryEvidenceAnnotation { + if v, exists := l.Annotations[pkg.EvidenceAnnotationKey]; !exists || v != pkg.PrimaryEvidenceAnnotation { // skip non-primary evidence from being expressed as a relationship. // note: this may be configurable in the future. continue diff --git a/syft/pkg/relationships_evident_by_test.go b/internal/relationship/evident_by_test.go similarity index 74% rename from syft/pkg/relationships_evident_by_test.go rename to internal/relationship/evident_by_test.go index 21e7801bfd5..d65069053e3 100644 --- a/syft/pkg/relationships_evident_by_test.go +++ b/internal/relationship/evident_by_test.go @@ -1,4 +1,4 @@ -package pkg +package relationship import ( "testing" @@ -8,11 +8,12 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" ) func TestRelationshipsEvidentBy(t *testing.T) { - c := NewCollection() + c := pkg.NewCollection() coordA := file.Coordinates{ RealPath: "/somewhere/real", @@ -26,12 +27,12 @@ func TestRelationshipsEvidentBy(t *testing.T) { RealPath: "/somewhere/real", FileSystemID: "abc", } - pkgA := Package{ + pkgA := pkg.Package{ Locations: file.NewLocationSet( // added! - file.NewLocationFromCoordinates(coordA).WithAnnotation(EvidenceAnnotationKey, PrimaryEvidenceAnnotation), + file.NewLocationFromCoordinates(coordA).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), // ignored... - file.NewLocationFromCoordinates(coordC).WithAnnotation(EvidenceAnnotationKey, SupportingEvidenceAnnotation), + file.NewLocationFromCoordinates(coordC).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation), file.NewLocationFromCoordinates(coordD), ), } @@ -42,10 +43,10 @@ func TestRelationshipsEvidentBy(t *testing.T) { RealPath: "/somewhere-else/real", FileSystemID: "def", } - pkgB := Package{ + pkgB := pkg.Package{ Locations: file.NewLocationSet( // added! - file.NewLocationFromCoordinates(coordB).WithAnnotation(EvidenceAnnotationKey, PrimaryEvidenceAnnotation), + file.NewLocationFromCoordinates(coordB).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ), } pkgB.SetID() @@ -53,7 +54,7 @@ func TestRelationshipsEvidentBy(t *testing.T) { tests := []struct { name string - catalog *Collection + catalog *pkg.Collection want []artifact.Relationship }{ { @@ -75,7 +76,7 @@ func TestRelationshipsEvidentBy(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - actual := RelationshipsEvidentBy(tt.catalog) + actual := evidentBy(tt.catalog) require.Len(t, actual, len(tt.want)) for i := range actual { assert.Equal(t, tt.want[i].From.ID(), actual[i].From.ID(), "from mismatch at index %d", i) diff --git a/syft/pkg/cataloger/package_exclusions.go b/internal/relationship/exclude_binaries_by_file_ownership_overlap.go similarity index 64% rename from syft/pkg/cataloger/package_exclusions.go rename to internal/relationship/exclude_binaries_by_file_ownership_overlap.go index c9d67dd61c7..c85b12f7586 100644 --- a/syft/pkg/cataloger/package_exclusions.go +++ b/internal/relationship/exclude_binaries_by_file_ownership_overlap.go @@ -1,10 +1,12 @@ -package cataloger +package relationship import ( "slices" + "github.com/anchore/syft/internal/sbomsync" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" ) var ( @@ -21,13 +23,24 @@ var ( } ) -// ExcludeBinaryByFileOwnershipOverlap will remove packages from a collection given the following properties are true +func excludeBinariesByFileOwnershipOverlap(accessor sbomsync.Accessor) { + accessor.WriteToSBOM(func(s *sbom.SBOM) { + for _, r := range s.Relationships { + if excludeBinaryByFileOwnershipOverlap(r, s.Artifacts.Packages) { + s.Artifacts.Packages.Delete(r.To.ID()) + s.Relationships = RemoveRelationshipsByID(s.Relationships, r.To.ID()) + } + } + }) +} + +// excludeBinaryByFileOwnershipOverlap will remove packages from a collection given the following properties are true // 1) the relationship between packages is OwnershipByFileOverlap // 2) the parent is an "os" package // 3) the child is a synthetic package generated by the binary cataloger // 4) the package names are identical // This was implemented as a way to help resolve: https://github.com/anchore/syft/issues/931 -func ExcludeBinaryByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) bool { +func excludeBinaryByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) bool { if artifact.OwnershipByFileOverlapRelationship != r.Type { return false } diff --git a/syft/pkg/cataloger/package_exclusions_test.go b/internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go similarity index 95% rename from syft/pkg/cataloger/package_exclusions_test.go rename to internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go index b14ee29c6b4..6074e0c162a 100644 --- a/syft/pkg/cataloger/package_exclusions_test.go +++ b/internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go @@ -1,4 +1,4 @@ -package cataloger +package relationship import ( "testing" @@ -67,7 +67,7 @@ func TestExclude(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - if !ExcludeBinaryByFileOwnershipOverlap(test.relationship, test.packages) && test.shouldExclude { + if !excludeBinaryByFileOwnershipOverlap(test.relationship, test.packages) && test.shouldExclude { t.Errorf("expected to exclude relationship %+v", test.relationship) } }) diff --git a/internal/relationship/finalize.go b/internal/relationship/finalize.go new file mode 100644 index 00000000000..bfe6bd66fba --- /dev/null +++ b/internal/relationship/finalize.go @@ -0,0 +1,37 @@ +package relationship + +import ( + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/sbom" +) + +func Finalize(builder sbomsync.Builder, cfg cataloging.RelationshipsConfig, src artifact.Identifiable) { + accessor := builder.(sbomsync.Accessor) + + // add relationships showing packages that are evident by a file which is owned by another package (package-to-package) + if cfg.PackageFileOwnershipOverlap { + byFileOwnershipOverlapWorker(accessor) + } + + // conditionally remove binary packages based on file ownership overlap relationships found + // https://github.com/anchore/syft/issues/931 + if cfg.ExcludeBinaryPackagesWithFileOwnershipOverlap { + excludeBinariesByFileOwnershipOverlap(accessor) + } + + // add source "contains package" relationship (source-to-package) + var sourceRelationships []artifact.Relationship + accessor.ReadFromSBOM(func(s *sbom.SBOM) { + sourceRelationships = toSource(src, s.Artifacts.Packages) + }) + builder.AddRelationships(sourceRelationships...) + + // add evident-by relationships (package-to-file) + var evidentByRelationships []artifact.Relationship + accessor.ReadFromSBOM(func(s *sbom.SBOM) { + evidentByRelationships = evidentBy(s.Artifacts.Packages) + }) + builder.AddRelationships(evidentByRelationships...) +} diff --git a/internal/relationship/remove.go b/internal/relationship/remove.go new file mode 100644 index 00000000000..a1b969d4903 --- /dev/null +++ b/internal/relationship/remove.go @@ -0,0 +1,14 @@ +package relationship + +import "github.com/anchore/syft/syft/artifact" + +// TODO: put under test... +func RemoveRelationshipsByID(relationships []artifact.Relationship, id artifact.ID) []artifact.Relationship { + var filtered []artifact.Relationship + for _, r := range relationships { + if r.To.ID() != id && r.From.ID() != id { + filtered = append(filtered, r) + } + } + return filtered +} diff --git a/syft/pkg/relationships.go b/internal/relationship/sort.go similarity index 53% rename from syft/pkg/relationships.go rename to internal/relationship/sort.go index 204dab2d906..88582b4e922 100644 --- a/syft/pkg/relationships.go +++ b/internal/relationship/sort.go @@ -1,31 +1,26 @@ -package pkg +package relationship import ( "sort" "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" ) -func NewRelationships(catalog *Collection) []artifact.Relationship { - rels := RelationshipsByFileOwnership(catalog) - rels = append(rels, RelationshipsEvidentBy(catalog)...) - return rels -} - -// SortRelationships takes a set of package-to-package relationships and sorts them in a stable order by name and version. +// Sort takes a set of package-to-package relationships and sorts them in a stable order by name and version. // Note: this does not consider package-to-other, other-to-package, or other-to-other relationships. // TODO: ideally this should be replaced with a more type-agnostic sort function that resides in the artifact package. -func SortRelationships(rels []artifact.Relationship) { +func Sort(rels []artifact.Relationship) { sort.SliceStable(rels, func(i, j int) bool { - return relationshipLess(rels[i], rels[j]) + return less(rels[i], rels[j]) }) } -func relationshipLess(i, j artifact.Relationship) bool { - iFrom, ok1 := i.From.(Package) - iTo, ok2 := i.To.(Package) - jFrom, ok3 := j.From.(Package) - jTo, ok4 := j.To.(Package) +func less(i, j artifact.Relationship) bool { + iFrom, ok1 := i.From.(pkg.Package) + iTo, ok2 := i.To.(pkg.Package) + jFrom, ok3 := j.From.(pkg.Package) + jTo, ok4 := j.To.(pkg.Package) if !(ok1 && ok2 && ok3 && ok4) { return false diff --git a/internal/relationship/to_source.go b/internal/relationship/to_source.go new file mode 100644 index 00000000000..f11a8d5e119 --- /dev/null +++ b/internal/relationship/to_source.go @@ -0,0 +1,19 @@ +package relationship + +import ( + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" +) + +func toSource(src artifact.Identifiable, c *pkg.Collection) []artifact.Relationship { + relationships := make([]artifact.Relationship, 0) // Should we pre-allocate this by giving catalog a Len() method? + for p := range c.Enumerate() { + relationships = append(relationships, artifact.Relationship{ + From: src, + To: p, + Type: artifact.ContainsRelationship, + }) + } + + return relationships +} diff --git a/internal/sbomsync/builder.go b/internal/sbomsync/builder.go new file mode 100644 index 00000000000..f7404fcebc9 --- /dev/null +++ b/internal/sbomsync/builder.go @@ -0,0 +1,83 @@ +package sbomsync + +import ( + "sync" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/linux" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" +) + +var _ interface { + Accessor + Builder +} = (*sbomBuilder)(nil) // integrity check + +// Builder provides a simple facade for simple additions to the SBOM +type Builder interface { + // nodes + + AddPackages(...pkg.Package) + + // edges + + AddRelationships(...artifact.Relationship) + + // other + + SetLinuxDistribution(linux.Release) +} + +// Accessor allows for low-level access to the SBOM +type Accessor interface { + WriteToSBOM(func(*sbom.SBOM)) + ReadFromSBOM(func(*sbom.SBOM)) +} + +type sbomBuilder struct { + sbom *sbom.SBOM + lock *sync.RWMutex +} + +func NewBuilder(s *sbom.SBOM) Builder { + return &sbomBuilder{ + sbom: s, + lock: &sync.RWMutex{}, + } +} + +func (b sbomBuilder) WriteToSBOM(fn func(*sbom.SBOM)) { + b.lock.Lock() + defer b.lock.Unlock() + + fn(b.sbom) +} + +func (b sbomBuilder) ReadFromSBOM(fn func(*sbom.SBOM)) { + b.lock.RLock() + defer b.lock.RUnlock() + + fn(b.sbom) +} + +func (b sbomBuilder) AddPackages(p ...pkg.Package) { + b.lock.Lock() + defer b.lock.Unlock() + + b.sbom.Artifacts.Packages.Add(p...) +} + +func (b sbomBuilder) AddRelationships(relationship ...artifact.Relationship) { + b.lock.Lock() + defer b.lock.Unlock() + + b.sbom.Relationships = append(b.sbom.Relationships, relationship...) +} + +func (b sbomBuilder) SetLinuxDistribution(release linux.Release) { + b.lock.Lock() + defer b.lock.Unlock() + + b.sbom.Artifacts.LinuxDistribution = &release +} diff --git a/internal/spdxlicense/license_list.go b/internal/spdxlicense/license_list.go index d7fa6645e06..a07f5d904c5 100644 --- a/internal/spdxlicense/license_list.go +++ b/internal/spdxlicense/license_list.go @@ -1,9 +1,9 @@ // Code generated by go generate; DO NOT EDIT. -// This file was generated by robots at 2023-10-05 22:45:16.827263 -0400 EDT m=+0.138031521 +// This file was generated by robots at 2024-02-09 10:57:33.980847 -0500 EST m=+0.165923154 // using data from https://spdx.org/licenses/licenses.json package spdxlicense -const Version = "3.22" +const Version = "3.23" var licenseIDs = map[string]string{ "0bsd": "0BSD", @@ -13,6 +13,7 @@ var licenseIDs = map[string]string{ "adobe2006": "Adobe-2006", "adobe2006.0": "Adobe-2006", "adobe2006.0.0": "Adobe-2006", + "adobedisplaypostscript": "Adobe-Display-PostScript", "adobeglyph": "Adobe-Glyph", "adobeutopia": "Adobe-Utopia", "adsl": "ADSL", @@ -57,6 +58,7 @@ var licenseIDs = map[string]string{ "aladdin": "Aladdin", "amdplpa": "AMDPLPA", "aml": "AML", + "amlglslang": "AML-glslang", "ampas": "AMPAS", "antlrpd": "ANTLR-PD", "antlrpdfallback": "ANTLR-PD-fallback", @@ -108,6 +110,7 @@ var licenseIDs = map[string]string{ "baekmuk": "Baekmuk", "bahyph": "Bahyph", "barr": "Barr", + "bcryptsolardesigner": "bcrypt-Solar-Designer", "beerware": "Beerware", "bitstreamcharter": "Bitstream-Charter", "bitstreamvera": "Bitstream-Vera", @@ -122,6 +125,9 @@ var licenseIDs = map[string]string{ "blueoak1.0.0": "BlueOak-1.0.0", "boehmgc": "Boehm-GC", "borceux": "Borceux", + "briangladman2.0.0clause": "Brian-Gladman-2-Clause", + "briangladman2.0clause": "Brian-Gladman-2-Clause", + "briangladman2clause": "Brian-Gladman-2-Clause", "briangladman3.0.0clause": "Brian-Gladman-3-Clause", "briangladman3.0clause": "Brian-Gladman-3-Clause", "briangladman3clause": "Brian-Gladman-3-Clause", @@ -129,21 +135,25 @@ var licenseIDs = map[string]string{ "bsd1.0clause": "BSD-1-Clause", "bsd1clause": "BSD-1-Clause", "bsd2.0.0clause": "BSD-2-Clause", + "bsd2.0.0clausedarwin": "BSD-2-Clause-Darwin", "bsd2.0.0clausefreebsd": "BSD-2-Clause-Views", "bsd2.0.0clausenetbsd": "BSD-2-Clause", "bsd2.0.0clausepatent": "BSD-2-Clause-Patent", "bsd2.0.0clauseviews": "BSD-2-Clause-Views", "bsd2.0clause": "BSD-2-Clause", + "bsd2.0clausedarwin": "BSD-2-Clause-Darwin", "bsd2.0clausefreebsd": "BSD-2-Clause-Views", "bsd2.0clausenetbsd": "BSD-2-Clause", "bsd2.0clausepatent": "BSD-2-Clause-Patent", "bsd2.0clauseviews": "BSD-2-Clause-Views", "bsd2clause": "BSD-2-Clause", + "bsd2clausedarwin": "BSD-2-Clause-Darwin", "bsd2clausefreebsd": "BSD-2-Clause-Views", "bsd2clausenetbsd": "BSD-2-Clause", "bsd2clausepatent": "BSD-2-Clause-Patent", "bsd2clauseviews": "BSD-2-Clause-Views", "bsd3.0.0clause": "BSD-3-Clause", + "bsd3.0.0clauseacpica": "BSD-3-Clause-acpica", "bsd3.0.0clauseattribution": "BSD-3-Clause-Attribution", "bsd3.0.0clauseclear": "BSD-3-Clause-Clear", "bsd3.0.0clauseflex": "BSD-3-Clause-flex", @@ -157,6 +167,7 @@ var licenseIDs = map[string]string{ "bsd3.0.0clauseopenmpi": "BSD-3-Clause-Open-MPI", "bsd3.0.0clausesun": "BSD-3-Clause-Sun", "bsd3.0clause": "BSD-3-Clause", + "bsd3.0clauseacpica": "BSD-3-Clause-acpica", "bsd3.0clauseattribution": "BSD-3-Clause-Attribution", "bsd3.0clauseclear": "BSD-3-Clause-Clear", "bsd3.0clauseflex": "BSD-3-Clause-flex", @@ -170,6 +181,7 @@ var licenseIDs = map[string]string{ "bsd3.0clauseopenmpi": "BSD-3-Clause-Open-MPI", "bsd3.0clausesun": "BSD-3-Clause-Sun", "bsd3clause": "BSD-3-Clause", + "bsd3clauseacpica": "BSD-3-Clause-acpica", "bsd3clauseattribution": "BSD-3-Clause-Attribution", "bsd3clauseclear": "BSD-3-Clause-Clear", "bsd3clauseflex": "BSD-3-Clause-flex", @@ -201,8 +213,12 @@ var licenseIDs = map[string]string{ "bsdattributionhpnddisclaimer": "BSD-Attribution-HPND-disclaimer", "bsdinfernonettverk": "BSD-Inferno-Nettverk", "bsdprotection": "BSD-Protection", + "bsdsourcebeginningfile": "BSD-Source-beginning-file", "bsdsourcecode": "BSD-Source-Code", "bsdsystemics": "BSD-Systemics", + "bsdsystemicsw3.0.0works": "BSD-Systemics-W3Works", + "bsdsystemicsw3.0works": "BSD-Systemics-W3Works", + "bsdsystemicsw3works": "BSD-Systemics-W3Works", "bsl1": "BSL-1.0", "bsl1.0": "BSL-1.0", "bsl1.0.0": "BSL-1.0", @@ -220,6 +236,7 @@ var licenseIDs = map[string]string{ "cal1.0combinedworkexception": "CAL-1.0-Combined-Work-Exception", "cal1combinedworkexception": "CAL-1.0-Combined-Work-Exception", "caldera": "Caldera", + "calderanopreamble": "Caldera-no-preamble", "catosl1": "CATOSL-1.1", "catosl1.1": "CATOSL-1.1", "catosl1.1.0": "CATOSL-1.1", @@ -241,16 +258,19 @@ var licenseIDs = map[string]string{ "ccby3.0": "CC-BY-3.0", "ccby3.0.0": "CC-BY-3.0", "ccby3.0.0at": "CC-BY-3.0-AT", + "ccby3.0.0au": "CC-BY-3.0-AU", "ccby3.0.0de": "CC-BY-3.0-DE", "ccby3.0.0igo": "CC-BY-3.0-IGO", "ccby3.0.0nl": "CC-BY-3.0-NL", "ccby3.0.0us": "CC-BY-3.0-US", "ccby3.0at": "CC-BY-3.0-AT", + "ccby3.0au": "CC-BY-3.0-AU", "ccby3.0de": "CC-BY-3.0-DE", "ccby3.0igo": "CC-BY-3.0-IGO", "ccby3.0nl": "CC-BY-3.0-NL", "ccby3.0us": "CC-BY-3.0-US", "ccby3at": "CC-BY-3.0-AT", + "ccby3au": "CC-BY-3.0-AU", "ccby3de": "CC-BY-3.0-DE", "ccby3igo": "CC-BY-3.0-IGO", "ccby3nl": "CC-BY-3.0-NL", @@ -420,6 +440,7 @@ var licenseIDs = map[string]string{ "clartistic": "ClArtistic", "clips": "Clips", "cmumach": "CMU-Mach", + "cmumachnodoc": "CMU-Mach-nodoc", "cnrijython": "CNRI-Jython", "cnripython": "CNRI-Python", "cnripythongplcompatible": "CNRI-Python-GPL-Compatible", @@ -456,6 +477,9 @@ var licenseIDs = map[string]string{ "cuda1.0": "C-UDA-1.0", "cuda1.0.0": "C-UDA-1.0", "curl": "curl", + "dec3.0.0clause": "DEC-3-Clause", + "dec3.0clause": "DEC-3-Clause", + "dec3clause": "DEC-3-Clause", "dfsl1": "D-FSL-1.0", "dfsl1.0": "D-FSL-1.0", "dfsl1.0.0": "D-FSL-1.0", @@ -471,6 +495,8 @@ var licenseIDs = map[string]string{ "drl1": "DRL-1.0", "drl1.0": "DRL-1.0", "drl1.0.0": "DRL-1.0", + "drl1.1": "DRL-1.1", + "drl1.1.0": "DRL-1.1", "dsdp": "DSDP", "dtoa": "dtoa", "dvipdfm": "dvipdfm", @@ -526,12 +552,14 @@ var licenseIDs = map[string]string{ "freebsddoc": "FreeBSD-DOC", "freeimage": "FreeImage", "fsfap": "FSFAP", + "fsfapnowarrantydisclaimer": "FSFAP-no-warranty-disclaimer", "fsful": "FSFUL", "fsfullr": "FSFULLR", "fsfullrwd": "FSFULLRWD", "ftl": "FTL", "furuseth": "Furuseth", "fwlw": "fwlw", + "gcrdocs": "GCR-docs", "gd": "GD", "gfdl1": "GFDL-1.1-only", "gfdl1+": "GFDL-1.1-or-later", @@ -672,7 +700,9 @@ var licenseIDs = map[string]string{ "gsoap1.3.0b": "gSOAP-1.3b", "gsoap1.3b": "gSOAP-1.3b", "gsoap1b": "gSOAP-1.3b", + "gtkbook": "gtkbook", "haskellreport": "HaskellReport", + "hdparm": "hdparm", "hippocratic2": "Hippocratic-2.1", "hippocratic2.1": "Hippocratic-2.1", "hippocratic2.1.0": "Hippocratic-2.1", @@ -688,8 +718,13 @@ var licenseIDs = map[string]string{ "hpnddocsell": "HPND-doc-sell", "hpndexportus": "HPND-export-US", "hpndexportusmodify": "HPND-export-US-modify", + "hpndfenneberglivingston": "HPND-Fenneberg-Livingston", + "hpndinriaimag": "HPND-INRIA-IMAG", + "hpndkevlinhenney": "HPND-Kevlin-Henney", "hpndmarkuskuhn": "HPND-Markus-Kuhn", + "hpndmitdisclaimer": "HPND-MIT-disclaimer", "hpndpbmplus": "HPND-Pbmplus", + "hpndsellmitdisclaimerxserver": "HPND-sell-MIT-disclaimer-xserver", "hpndsellregexpr": "HPND-sell-regexpr", "hpndsellvariant": "HPND-sell-variant", "hpndsellvariantmitdisclaimer": "HPND-sell-variant-MIT-disclaimer", @@ -719,6 +754,7 @@ var licenseIDs = map[string]string{ "ipl1.0": "IPL-1.0", "ipl1.0.0": "IPL-1.0", "isc": "ISC", + "iscveillard": "ISC-Veillard", "jam": "Jam", "jasper2": "JasPer-2.0", "jasper2.0": "JasPer-2.0", @@ -802,6 +838,7 @@ var licenseIDs = map[string]string{ "linuxmanpagescopyleftvar": "Linux-man-pages-copyleft-var", "linuxopenib": "Linux-OpenIB", "loop": "LOOP", + "lpddocument": "LPD-document", "lpl1": "LPL-1.0", "lpl1.0": "LPL-1.0", "lpl1.0.0": "LPL-1.0", @@ -828,7 +865,14 @@ var licenseIDs = map[string]string{ "lzmasdk9.22": "LZMA-SDK-9.22", "lzmasdk9.22.0": "LZMA-SDK-9.22", "lzmasdk9to9.20": "LZMA-SDK-9.11-to-9.20", + "mackerras3.0.0clause": "Mackerras-3-Clause", + "mackerras3.0.0clauseacknowledgment": "Mackerras-3-Clause-acknowledgment", + "mackerras3.0clause": "Mackerras-3-Clause", + "mackerras3.0clauseacknowledgment": "Mackerras-3-Clause-acknowledgment", + "mackerras3clause": "Mackerras-3-Clause", + "mackerras3clauseacknowledgment": "Mackerras-3-Clause-acknowledgment", "magaz": "magaz", + "mailprio": "mailprio", "makeindex": "MakeIndex", "martinbirgmeier": "Martin-Birgmeier", "mcpheeslideshow": "McPhee-slideshow", @@ -1008,6 +1052,8 @@ var licenseIDs = map[string]string{ "openpbs2.3": "OpenPBS-2.3", "openpbs2.3.0": "OpenPBS-2.3", "openssl": "OpenSSL", + "opensslstandalone": "OpenSSL-standalone", + "openvision": "OpenVision", "opl1": "OPL-1.0", "opl1.0": "OPL-1.0", "opl1.0.0": "OPL-1.0", @@ -1051,6 +1097,7 @@ var licenseIDs = map[string]string{ "php3.0.0": "PHP-3.0", "php3.01": "PHP-3.01", "php3.01.0": "PHP-3.01", + "pixar": "Pixar", "plexus": "Plexus", "pnmstitch": "pnmstitch", "polyformnoncommercial1": "PolyForm-Noncommercial-1.0.0", @@ -1077,6 +1124,7 @@ var licenseIDs = map[string]string{ "qpl1.0.0inria2004": "QPL-1.0-INRIA-2004", "qpl1.0inria2004": "QPL-1.0-INRIA-2004", "qpl1inria2004": "QPL-1.0-INRIA-2004", + "radvd": "radvd", "rdisc": "Rdisc", "rhecos1": "RHeCos-1.1", "rhecos1.1": "RHeCos-1.1", @@ -1094,6 +1142,9 @@ var licenseIDs = map[string]string{ "ruby": "Ruby", "saxpath": "Saxpath", "saxpd": "SAX-PD", + "saxpd2": "SAX-PD-2.0", + "saxpd2.0": "SAX-PD-2.0", + "saxpd2.0.0": "SAX-PD-2.0", "scea": "SCEA", "schemereport": "SchemeReport", "sendmail": "Sendmail", @@ -1129,6 +1180,7 @@ var licenseIDs = map[string]string{ "smppl": "SMPPL", "snia": "SNIA", "snprintf": "snprintf", + "softsurfer": "softSurfer", "soundex": "Soundex", "spencer86": "Spencer-86", "spencer86.0": "Spencer-86", @@ -1145,6 +1197,7 @@ var licenseIDs = map[string]string{ "sshkeyscan": "ssh-keyscan", "sshopenssh": "SSH-OpenSSH", "sshshort": "SSH-short", + "ssleaystandalone": "SSLeay-standalone", "sspl1": "SSPL-1.0", "sspl1.0": "SSPL-1.0", "sspl1.0.0": "SSPL-1.0", @@ -1152,6 +1205,7 @@ var licenseIDs = map[string]string{ "sugarcrm1": "SugarCRM-1.1.3", "sugarcrm1.1": "SugarCRM-1.1.3", "sugarcrm1.1.3": "SugarCRM-1.1.3", + "sunppp": "Sun-PPP", "sunpro": "SunPro", "swl": "SWL", "swrule": "swrule", @@ -1162,6 +1216,9 @@ var licenseIDs = map[string]string{ "tcl": "TCL", "tcpwrappers": "TCP-wrappers", "termreadkey": "TermReadKey", + "tgppl1": "TGPPL-1.0", + "tgppl1.0": "TGPPL-1.0", + "tgppl1.0.0": "TGPPL-1.0", "tmate": "TMate", "torque1": "TORQUE-1.1", "torque1.1": "TORQUE-1.1", @@ -1184,6 +1241,10 @@ var licenseIDs = map[string]string{ "ucl1.0": "UCL-1.0", "ucl1.0.0": "UCL-1.0", "ulem": "ulem", + "umichmerit": "UMich-Merit", + "unicode3": "Unicode-3.0", + "unicode3.0": "Unicode-3.0", + "unicode3.0.0": "Unicode-3.0", "unicodedfs2015": "Unicode-DFS-2015", "unicodedfs2015.0": "Unicode-DFS-2015", "unicodedfs2015.0.0": "Unicode-DFS-2015", @@ -1236,6 +1297,7 @@ var licenseIDs = map[string]string{ "xfree861.1": "XFree86-1.1", "xfree861.1.0": "XFree86-1.1", "xinetd": "xinetd", + "xkeyboardconfigzinoviev": "xkeyboard-config-Zinoviev", "xlock": "xlock", "xnet": "Xnet", "xpp": "xpp", diff --git a/internal/task/environment_tasks.go b/internal/task/environment_tasks.go new file mode 100644 index 00000000000..2c988a6245d --- /dev/null +++ b/internal/task/environment_tasks.go @@ -0,0 +1,24 @@ +package task + +import ( + "context" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/linux" +) + +// TODO: add tui element here? + +func NewEnvironmentTask() Task { + fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error { + release := linux.IdentifyRelease(resolver) + if release != nil { + builder.SetLinuxDistribution(*release) + } + + return nil + } + + return NewTask("environment-cataloger", fn) +} diff --git a/internal/task/executor.go b/internal/task/executor.go new file mode 100644 index 00000000000..5ba48b58aa0 --- /dev/null +++ b/internal/task/executor.go @@ -0,0 +1,72 @@ +package task + +import ( + "context" + "fmt" + "runtime/debug" + "sync" + + "github.com/hashicorp/go-multierror" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/event/monitor" + "github.com/anchore/syft/syft/file" +) + +type Executor struct { + numWorkers int + tasks chan Task +} + +func NewTaskExecutor(tasks []Task, numWorkers int) *Executor { + p := &Executor{ + numWorkers: numWorkers, + tasks: make(chan Task, len(tasks)), + } + + for i := range tasks { + p.tasks <- tasks[i] + } + close(p.tasks) + + return p +} + +func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsync.Builder, prog *monitor.CatalogerTaskProgress) error { + var errs error + wg := &sync.WaitGroup{} + for i := 0; i < p.numWorkers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + for { + tsk, ok := <-p.tasks + if !ok { + return + } + + if err := runTaskSafely(ctx, tsk, resolver, s); err != nil { + errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", err)) + prog.SetError(err) + } + prog.Increment() + } + }() + } + + wg.Wait() + + return errs +} + +func runTaskSafely(ctx context.Context, t Task, resolver file.Resolver, s sbomsync.Builder) (err error) { + // handle individual cataloger panics + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("%v at:\n%s", e, string(debug.Stack())) + } + }() + + return t.Execute(ctx, resolver, s) +} diff --git a/internal/task/executor_test.go b/internal/task/executor_test.go new file mode 100644 index 00000000000..5da1a0f40cf --- /dev/null +++ b/internal/task/executor_test.go @@ -0,0 +1,27 @@ +package task + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "github.com/wagoodman/go-progress" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/event/monitor" + "github.com/anchore/syft/syft/file" +) + +func Test_TaskExecutor_PanicHandling(t *testing.T) { + tsk := NewTask("panicking-cataloger", func(_ context.Context, _ file.Resolver, _ sbomsync.Builder) error { + panic("something bad happened") + }) + ex := NewTaskExecutor([]Task{tsk}, 1) + + err := ex.Execute(context.Background(), nil, nil, &monitor.CatalogerTaskProgress{ + Manual: progress.NewManual(-1), + }) + + require.Error(t, err) + require.Contains(t, err.Error(), "executor_test.go") +} diff --git a/internal/task/expression.go b/internal/task/expression.go new file mode 100644 index 00000000000..e99551c6f3c --- /dev/null +++ b/internal/task/expression.go @@ -0,0 +1,238 @@ +package task + +import ( + "fmt" + "regexp" + "sort" + + "github.com/hashicorp/go-multierror" + "github.com/scylladb/go-set/strset" + + "github.com/anchore/syft/syft/cataloging/pkgcataloging" +) + +var expressionNodePattern = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9-+]*)+$`) + +const ( + SetOperation Operation = "set" + AddOperation Operation = "add" + SubSelectOperation Operation = "sub-select" + RemoveOperation Operation = "remove" +) + +var ( + ErrEmptyToken = fmt.Errorf("no value given") + ErrInvalidToken = fmt.Errorf("invalid token given: only alphanumeric characters and hyphens are allowed") + ErrInvalidOperator = fmt.Errorf("invalid operator given") + ErrUnknownNameOrTag = fmt.Errorf("unknown name or tag given") + ErrTagsNotAllowed = fmt.Errorf("tags are not allowed with this operation (must use exact names)") + ErrNamesNotAllowed = fmt.Errorf("names are not allowed with this operation (must use tags)") + ErrAllNotAllowed = fmt.Errorf("cannot use the 'all' operand in this context") +) + +// ErrInvalidExpression represents an expression that cannot be parsed or can be parsed but is logically invalid. +type ErrInvalidExpression struct { + Expression string + Operation Operation + Err error +} + +func (e ErrInvalidExpression) Error() string { + return fmt.Sprintf("invalid expression: %q: %s", e.Expression, e.Err.Error()) +} + +func newErrInvalidExpression(exp string, op Operation, err error) ErrInvalidExpression { + return ErrInvalidExpression{ + Expression: exp, + Operation: op, + Err: err, + } +} + +// Expression represents a single operation-operand pair with (all validation errors). +// E.g. "+foo", "-bar", or "something" are all expressions. Some validations are relevant to not only the +// syntax (operation and operator) but other are sensitive to the context of the operand (e.g. if a given operand +// is a tag or a name, validated against the operation). +type Expression struct { + Operation Operation + Operand string + Errors []error +} + +// Operation represents the type of operation to perform on the operand (set, add, remove, sub-select). +type Operation string + +// Expressions represents a list of expressions. +type Expressions []Expression + +// expressionContext represents all information needed to validate an expression (e.g. the set of all tasks and their tags). +type expressionContext struct { + Names *strset.Set + Tags *strset.Set +} + +func newExpressionContext(ts []Task) *expressionContext { + ec := &expressionContext{ + Names: strset.New(tasks(ts).Names()...), + Tags: strset.New(tasks(ts).Tags()...), + } + + ec.Tags.Add("all") + + return ec +} + +// newExpression creates a new validated Expression object relative to the task names and tags. +func (ec expressionContext) newExpression(exp string, operation Operation, token string) Expression { + if token == "" { + return Expression{ + Operation: operation, + Operand: token, + Errors: []error{newErrInvalidExpression(exp, operation, ErrEmptyToken)}, + } + } + + if !isValidNode(token) { + return Expression{ + Operation: operation, + Operand: token, + Errors: []error{newErrInvalidExpression(exp, operation, ErrInvalidToken)}, + } + } + + var err error + switch operation { + case SetOperation, RemoveOperation: + // names and tags allowed + if !ec.Tags.Has(token) && !ec.Names.Has(token) { + err = newErrInvalidExpression(exp, operation, ErrUnknownNameOrTag) + } + case AddOperation: + // only names are allowed + if !ec.Names.Has(token) { + if ec.Tags.Has(token) { + err = newErrInvalidExpression(exp, operation, ErrTagsNotAllowed) + } else { + err = newErrInvalidExpression(exp, operation, ErrUnknownNameOrTag) + } + } + case SubSelectOperation: + if token == "all" { + // special case: we cannot sub-select all (this is most likely a misconfiguration and the user intended to use the set operation) + err = newErrInvalidExpression(exp, operation, ErrAllNotAllowed) + } else if !ec.Tags.Has(token) { + // only tags are allowed... + if ec.Names.Has(token) { + err = newErrInvalidExpression(exp, operation, ErrNamesNotAllowed) + } else { + err = newErrInvalidExpression(exp, operation, ErrUnknownNameOrTag) + } + } + } + + var errs []error + if err != nil { + errs = append(errs, err) + } + + return Expression{ + Operation: operation, + Operand: token, + Errors: errs, + } +} + +func newExpressionsFromSelectionRequest(nc *expressionContext, selectionRequest pkgcataloging.SelectionRequest) Expressions { + var all Expressions + + for _, exp := range selectionRequest.DefaultNamesOrTags { + all = append(all, nc.newExpression(exp, SetOperation, exp)) + } + + for _, exp := range selectionRequest.SubSelectTags { + all = append(all, nc.newExpression(exp, SubSelectOperation, exp)) + } + + for _, exp := range selectionRequest.AddNames { + all = append(all, nc.newExpression(exp, AddOperation, exp)) + } + + for _, exp := range selectionRequest.RemoveNamesOrTags { + all = append(all, nc.newExpression(exp, RemoveOperation, exp)) + } + + sort.Sort(all) + return all +} + +func isValidNode(s string) bool { + return expressionNodePattern.Match([]byte(s)) +} + +func (e Expressions) Clone() Expressions { + clone := make(Expressions, len(e)) + copy(clone, e) + return clone +} + +func (e Expression) String() string { + var op string + switch e.Operation { + case AddOperation: + op = "+" + case RemoveOperation: + op = "-" + case SubSelectOperation: + op = "" + case SetOperation: + op = "" + default: + op = "?" + } + return op + e.Operand +} + +func (e Expressions) Len() int { + return len(e) +} + +func (e Expressions) Swap(i, j int) { + e[i], e[j] = e[j], e[i] +} + +// order of operations +var orderOfOps = map[Operation]int{ + SetOperation: 1, + SubSelectOperation: 2, + RemoveOperation: 3, + AddOperation: 4, +} + +func (e Expressions) Less(i, j int) bool { + ooi := orderOfOps[e[i].Operation] + ooj := orderOfOps[e[j].Operation] + + if ooi != ooj { + return ooi < ooj + } + + return i < j +} + +func (e Expressions) Errors() (errs []error) { + for _, n := range e { + if len(n.Errors) > 0 { + errs = append(errs, n.Errors...) + } + } + return errs +} + +func (e Expressions) Validate() error { + errs := e.Errors() + if len(errs) == 0 { + return nil + } + var err error + return multierror.Append(err, e.Errors()...) +} diff --git a/internal/task/expression_test.go b/internal/task/expression_test.go new file mode 100644 index 00000000000..e1a97ab2213 --- /dev/null +++ b/internal/task/expression_test.go @@ -0,0 +1,242 @@ +package task + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/cataloging/pkgcataloging" +) + +func Test_newExpressionsFromSelectionRequest(t *testing.T) { + ts := []Task{ + dummyTask("1", "t1"), + dummyTask("2", "t2"), + dummyTask("3", "t3"), + dummyTask("4", "t4"), + dummyTask("5"), + dummyTask("6"), + } + + nc := newExpressionContext(ts) + + var tests = []struct { + name string + basis []string + expressions []string + expected Expressions + expectedErrors []error + }{ + { + name: "empty input", + basis: []string{}, + expressions: []string{}, + expected: nil, + }, + { + name: "valid single set operation", + basis: []string{"1"}, + expressions: []string{}, + expected: []Expression{ + {Operation: SetOperation, Operand: "1"}, + }, + }, + { + name: "add operation", + basis: []string{}, + expressions: []string{"+4"}, + expected: []Expression{ + {Operation: AddOperation, Operand: "4"}, + }, + }, + { + name: "remove operation", + basis: []string{}, + expressions: []string{"-3"}, + expected: []Expression{ + {Operation: RemoveOperation, Operand: "3"}, + }, + }, + { + name: "select operation", + basis: []string{}, + expressions: []string{"t2"}, + expected: []Expression{ + {Operation: SubSelectOperation, Operand: "t2"}, + }, + }, + { + name: "mixed operations order", + basis: []string{"1"}, + expressions: []string{"+4", "-3", "t2"}, + expected: []Expression{ + // note they are sorted by operation + {Operation: SetOperation, Operand: "1"}, + {Operation: SubSelectOperation, Operand: "t2"}, + {Operation: RemoveOperation, Operand: "3"}, + {Operation: AddOperation, Operand: "4"}, + }, + }, + { + name: "invalid token", + basis: []string{"!1"}, + expressions: []string{}, + expected: nil, + expectedErrors: []error{ErrInvalidToken}, + }, + { + name: "use + operator in basis", + basis: []string{"+1"}, + expressions: []string{}, + expected: nil, + expectedErrors: []error{ErrInvalidToken}, + }, + { + name: "use - operator in basis", + basis: []string{"-1"}, + expressions: []string{}, + expected: nil, + expectedErrors: []error{ErrInvalidToken}, + }, + { + name: "invalid name", + basis: []string{}, + expressions: []string{"+t1"}, + expected: nil, + expectedErrors: []error{ErrTagsNotAllowed}, + }, + { + name: "invalid tag", + basis: []string{}, + expressions: []string{"1"}, + expected: nil, + expectedErrors: []error{ErrNamesNotAllowed}, + }, + { + name: "invalid use of all", + basis: []string{}, + expressions: []string{"all"}, + expected: nil, + expectedErrors: []error{ErrAllNotAllowed}, + }, + { + name: "allow all operand", + basis: []string{"all"}, + expressions: []string{}, + expected: []Expression{ + // note they are sorted by operation + {Operation: SetOperation, Operand: "all"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...) + + result := newExpressionsFromSelectionRequest(nc, req) + if tt.expectedErrors != nil { + errs := result.Errors() + require.Len(t, errs, len(tt.expectedErrors)) + for i, err := range tt.expectedErrors { + var target ErrInvalidExpression + require.ErrorAs(t, errs[i], &target) + assert.Equal(t, err, target.Err) + } + } else { + assert.Empty(t, result.Errors()) + assert.Equal(t, tt.expected, result) + } + }) + } +} + +func Test_expressionNodes_sort(t *testing.T) { + tests := []struct { + name string + subject Expressions + want Expressions + }{ + { + name: "sort operations but keep token order", + subject: []Expression{ + { + Operation: AddOperation, + Operand: "8", + }, + { + Operation: AddOperation, + Operand: "7", + }, + { + Operation: RemoveOperation, + Operand: "6", + }, + { + Operation: RemoveOperation, + Operand: "5", + }, + { + Operation: SetOperation, + Operand: "2", + }, + { + Operation: SetOperation, + Operand: "1", + }, + { + Operation: SubSelectOperation, + Operand: "4", + }, + { + Operation: SubSelectOperation, + Operand: "3", + }, + }, + want: []Expression{ + { + Operation: SetOperation, + Operand: "2", + }, + { + Operation: SetOperation, + Operand: "1", + }, + { + Operation: SubSelectOperation, + Operand: "4", + }, + { + Operation: SubSelectOperation, + Operand: "3", + }, + { + Operation: RemoveOperation, + Operand: "6", + }, + { + Operation: RemoveOperation, + Operand: "5", + }, + { + Operation: AddOperation, + Operand: "8", + }, + { + Operation: AddOperation, + Operand: "7", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := tt.subject.Clone() + sort.Sort(s) + assert.Equal(t, tt.want, s) + }) + } +} diff --git a/internal/task/file_tasks.go b/internal/task/file_tasks.go new file mode 100644 index 00000000000..857f6e4238e --- /dev/null +++ b/internal/task/file_tasks.go @@ -0,0 +1,161 @@ +package task + +import ( + "context" + "crypto" + "fmt" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/file/cataloger/executable" + "github.com/anchore/syft/syft/file/cataloger/filecontent" + "github.com/anchore/syft/syft/file/cataloger/filedigest" + "github.com/anchore/syft/syft/file/cataloger/filemetadata" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" +) + +func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash) Task { + if selection == file.NoFilesSelection || len(hashers) == 0 { + return nil + } + + digestsCataloger := filedigest.NewCataloger(hashers) + + fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error { + accessor := builder.(sbomsync.Accessor) + + coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor)) + if !ok { + return nil + } + + result, err := digestsCataloger.Catalog(ctx, resolver, coordinates...) + if err != nil { + return fmt.Errorf("unable to catalog file digests: %w", err) + } + + accessor.WriteToSBOM(func(sbom *sbom.SBOM) { + sbom.Artifacts.FileDigests = result + }) + + return nil + } + + return NewTask("file-digest-cataloger", fn) +} + +func NewFileMetadataCatalogerTask(selection file.Selection) Task { + if selection == file.NoFilesSelection { + return nil + } + + metadataCataloger := filemetadata.NewCataloger() + + fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error { + accessor := builder.(sbomsync.Accessor) + + coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor)) + if !ok { + return nil + } + + result, err := metadataCataloger.Catalog(ctx, resolver, coordinates...) + if err != nil { + return err + } + + accessor.WriteToSBOM(func(sbom *sbom.SBOM) { + sbom.Artifacts.FileMetadata = result + }) + + return nil + } + + return NewTask("file-metadata-cataloger", fn) +} + +func NewFileContentCatalogerTask(cfg filecontent.Config) Task { + if len(cfg.Globs) == 0 { + return nil + } + + cat := filecontent.NewCataloger(cfg) + + fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error { + accessor := builder.(sbomsync.Accessor) + + result, err := cat.Catalog(ctx, resolver) + if err != nil { + return err + } + + accessor.WriteToSBOM(func(sbom *sbom.SBOM) { + sbom.Artifacts.FileContents = result + }) + + return nil + } + + return NewTask("file-content-cataloger", fn) +} + +func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config) Task { + if selection == file.NoFilesSelection { + return nil + } + + cat := executable.NewCataloger(cfg) + + fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error { + accessor := builder.(sbomsync.Accessor) + + result, err := cat.Catalog(resolver) + if err != nil { + return err + } + + accessor.WriteToSBOM(func(sbom *sbom.SBOM) { + sbom.Artifacts.Executables = result + }) + + return nil + } + + return NewTask("file-executable-cataloger", fn) +} + +// TODO: this should be replaced with a fix that allows passing a coordinate or location iterator to the cataloger +// Today internal to both cataloger this functions differently: a slice of coordinates vs a channel of locations +func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accessor) ([]file.Coordinates, bool) { + if selection == file.AllFilesSelection { + return nil, true + } + + if selection == file.FilesOwnedByPackageSelection { + var coordinates []file.Coordinates + + accessor.ReadFromSBOM(func(sbom *sbom.SBOM) { + for _, r := range sbom.Relationships { + if r.Type != artifact.ContainsRelationship { + continue + } + if _, ok := r.From.(pkg.Package); !ok { + continue + } + if c, ok := r.To.(file.Coordinates); ok { + coordinates = append(coordinates, c) + } + } + }) + + if len(coordinates) == 0 { + return nil, false + } + + return coordinates, true + } + + return nil, false +} diff --git a/internal/task/file_tasks_test.go b/internal/task/file_tasks_test.go new file mode 100644 index 00000000000..b24e7e254a2 --- /dev/null +++ b/internal/task/file_tasks_test.go @@ -0,0 +1,104 @@ +package task + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" +) + +func Test_coordinatesForSelection(t *testing.T) { + + tests := []struct { + name string + selection file.Selection + sbom *sbom.SBOM + files []file.Coordinates + ok bool + }{ + { + name: "all files", + selection: file.AllFilesSelection, + files: nil, + ok: true, + }, + { + name: "no files", + selection: file.NoFilesSelection, + files: nil, + ok: false, + }, + { + name: "specific files with hits", + selection: file.FilesOwnedByPackageSelection, + sbom: &sbom.SBOM{ + Relationships: []artifact.Relationship{ + { + From: pkg.Package{}, + To: file.Coordinates{ + RealPath: "path", + FileSystemID: "fs", + }, + Type: artifact.ContainsRelationship, + }, + }, + }, + files: []file.Coordinates{ + { + RealPath: "path", + FileSystemID: "fs", + }, + }, + ok: true, + }, + { + name: "specific files no hits (by wrong type)", + selection: file.FilesOwnedByPackageSelection, + sbom: &sbom.SBOM{ + Relationships: []artifact.Relationship{ + { + From: pkg.Package{}, + To: file.Coordinates{ + RealPath: "path", + FileSystemID: "fs", + }, + // wrong type + Type: artifact.DependencyOfRelationship, + }, + }, + }, + files: nil, + ok: false, + }, + { + name: "specific files no hits (by wrong node types)", + selection: file.FilesOwnedByPackageSelection, + sbom: &sbom.SBOM{ + Relationships: []artifact.Relationship{ + { + From: file.Coordinates{}, // wrong type + To: file.Coordinates{ + RealPath: "path", + FileSystemID: "fs", + }, + Type: artifact.ContainsRelationship, + }, + }, + }, + files: nil, + ok: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + files, ok := coordinatesForSelection(tt.selection, sbomsync.NewBuilder(tt.sbom).(sbomsync.Accessor)) + assert.Equal(t, tt.files, files) + assert.Equal(t, tt.ok, ok) + }) + } +} diff --git a/internal/task/package_task_factory.go b/internal/task/package_task_factory.go new file mode 100644 index 00000000000..17b613d7789 --- /dev/null +++ b/internal/task/package_task_factory.go @@ -0,0 +1,207 @@ +package task + +import ( + "context" + "fmt" + "sort" + "strings" + "unicode" + + "github.com/scylladb/go-set/strset" + + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/event/monitor" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/common/cpe" +) + +type packageTaskFactory func(cfg CatalogingFactoryConfig) Task + +type PackageTaskFactories []packageTaskFactory + +type CatalogingFactoryConfig struct { + SearchConfig cataloging.SearchConfig + RelationshipsConfig cataloging.RelationshipsConfig + DataGenerationConfig cataloging.DataGenerationConfig + PackagesConfig pkgcataloging.Config +} + +func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig { + return CatalogingFactoryConfig{ + SearchConfig: cataloging.DefaultSearchConfig(), + RelationshipsConfig: cataloging.DefaultRelationshipsConfig(), + DataGenerationConfig: cataloging.DefaultDataGenerationConfig(), + PackagesConfig: pkgcataloging.DefaultConfig(), + } +} + +func newPackageTaskFactory(catalogerFactory func(CatalogingFactoryConfig) pkg.Cataloger, tags ...string) packageTaskFactory { + return func(cfg CatalogingFactoryConfig) Task { + return NewPackageTask(cfg, catalogerFactory(cfg), tags...) + } +} + +func newSimplePackageTaskFactory(catalogerFactory func() pkg.Cataloger, tags ...string) packageTaskFactory { + return func(cfg CatalogingFactoryConfig) Task { + return NewPackageTask(cfg, catalogerFactory(), tags...) + } +} + +func (f PackageTaskFactories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error) { + var allTasks []Task + taskNames := strset.New() + duplicateTaskNames := strset.New() + var err error + for _, factory := range f { + tsk := factory(cfg) + if tsk == nil { + continue + } + tskName := tsk.Name() + if taskNames.Has(tskName) { + duplicateTaskNames.Add(tskName) + } + + allTasks = append(allTasks, tsk) + taskNames.Add(tskName) + } + if duplicateTaskNames.Size() > 0 { + names := duplicateTaskNames.List() + sort.Strings(names) + err = fmt.Errorf("duplicate cataloger task names: %v", strings.Join(names, ", ")) + } + + return allTasks, err +} + +// NewPackageTask creates a Task function for a generic pkg.Cataloger, honoring the common configuration options. +// +//nolint:funlen +func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string) Task { + fn := func(ctx context.Context, resolver file.Resolver, sbom sbomsync.Builder) error { + catalogerName := c.Name() + log.WithFields("name", catalogerName).Trace("starting package cataloger") + + info := monitor.GenericTask{ + Title: monitor.Title{ + Default: prettyName(catalogerName), + }, + ID: catalogerName, + ParentID: monitor.PackageCatalogingTaskID, + Context: "", + HideOnSuccess: true, + } + + t := bus.StartCatalogerTask(info, -1, "") + + pkgs, relationships, err := c.Catalog(ctx, resolver) + if err != nil { + return fmt.Errorf("unable to catalog packages with %q: %w", c.Name(), err) + } + + log.WithFields("cataloger", c.Name()).Debugf("discovered %d packages", len(pkgs)) + + for i, p := range pkgs { + if cfg.DataGenerationConfig.GenerateCPEs { + // generate CPEs (note: this is excluded from package ID, so is safe to mutate) + // we might have binary classified CPE already with the package so we want to append here + dictionaryCPE, ok := cpe.DictionaryFind(p) + if ok { + log.Tracef("used CPE dictionary to find CPE for %s package %q: %s", p.Type, p.Name, dictionaryCPE.Attributes.BindToFmtString()) + p.CPEs = append(p.CPEs, dictionaryCPE) + } else { + p.CPEs = append(p.CPEs, cpe.Generate(p)...) + } + } + + // if we were not able to identify the language we have an opportunity + // to try and get this value from the PURL. Worst case we assert that + // we could not identify the language at either stage and set UnknownLanguage + if p.Language == "" { + p.Language = pkg.LanguageFromPURL(p.PURL) + } + + if cfg.RelationshipsConfig.PackageFileOwnership { + // create file-to-package relationships for files owned by the package + owningRelationships, err := packageFileOwnershipRelationships(p, resolver) + if err != nil { + log.Warnf("unable to create any package-file relationships for package name=%q type=%q: %w", p.Name, p.Type, err) + } else { + relationships = append(relationships, owningRelationships...) + } + } + + pkgs[i] = p + } + + sbom.AddPackages(pkgs...) + sbom.AddRelationships(relationships...) + t.Add(int64(len(pkgs))) + + t.SetCompleted() + log.WithFields("name", c.Name()).Trace("package cataloger completed") + + return nil + } + tags = append(tags, pkgcataloging.PackageTag) + + return NewTask(c.Name(), fn, tags...) +} + +func prettyName(s string) string { + if s == "" { + return "" + } + + // Convert first character to uppercase + r := []rune(s) + r[0] = unicode.ToUpper(r[0]) + + return strings.ReplaceAll(string(r), "-", " ") +} + +func packageFileOwnershipRelationships(p pkg.Package, resolver file.PathResolver) ([]artifact.Relationship, error) { + fileOwner, ok := p.Metadata.(pkg.FileOwner) + if !ok { + return nil, nil + } + + locations := map[artifact.ID]file.Location{} + + for _, path := range fileOwner.OwnedFiles() { + pathRefs, err := resolver.FilesByPath(path) + if err != nil { + return nil, fmt.Errorf("unable to find path for path=%q: %w", path, err) + } + + if len(pathRefs) == 0 { + // ideally we want to warn users about missing files from a package, however, it is very common for + // container image authors to delete files that are not needed in order to keep image sizes small. Adding + // a warning here would be needlessly noisy (even for popular base images). + continue + } + + for _, ref := range pathRefs { + if oldRef, ok := locations[ref.Coordinates.ID()]; ok { + log.Debugf("found path duplicate of %s", oldRef.RealPath) + } + locations[ref.Coordinates.ID()] = ref + } + } + + var relationships []artifact.Relationship + for _, location := range locations { + relationships = append(relationships, artifact.Relationship{ + From: p, + To: location.Coordinates, + Type: artifact.ContainsRelationship, + }) + } + return relationships, nil +} diff --git a/internal/task/package_tasks.go b/internal/task/package_tasks.go new file mode 100644 index 00000000000..4d92497d913 --- /dev/null +++ b/internal/task/package_tasks.go @@ -0,0 +1,131 @@ +package task + +import ( + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/alpine" + "github.com/anchore/syft/syft/pkg/cataloger/arch" + "github.com/anchore/syft/syft/pkg/cataloger/binary" + "github.com/anchore/syft/syft/pkg/cataloger/cpp" + "github.com/anchore/syft/syft/pkg/cataloger/dart" + "github.com/anchore/syft/syft/pkg/cataloger/debian" + "github.com/anchore/syft/syft/pkg/cataloger/dotnet" + "github.com/anchore/syft/syft/pkg/cataloger/elixir" + "github.com/anchore/syft/syft/pkg/cataloger/erlang" + "github.com/anchore/syft/syft/pkg/cataloger/gentoo" + "github.com/anchore/syft/syft/pkg/cataloger/githubactions" + "github.com/anchore/syft/syft/pkg/cataloger/golang" + "github.com/anchore/syft/syft/pkg/cataloger/haskell" + "github.com/anchore/syft/syft/pkg/cataloger/java" + "github.com/anchore/syft/syft/pkg/cataloger/javascript" + "github.com/anchore/syft/syft/pkg/cataloger/kernel" + "github.com/anchore/syft/syft/pkg/cataloger/nix" + "github.com/anchore/syft/syft/pkg/cataloger/php" + "github.com/anchore/syft/syft/pkg/cataloger/python" + "github.com/anchore/syft/syft/pkg/cataloger/r" + "github.com/anchore/syft/syft/pkg/cataloger/redhat" + "github.com/anchore/syft/syft/pkg/cataloger/ruby" + "github.com/anchore/syft/syft/pkg/cataloger/rust" + sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom" + "github.com/anchore/syft/syft/pkg/cataloger/swift" + "github.com/anchore/syft/syft/pkg/cataloger/wordpress" +) + +//nolint:funlen +func DefaultPackageTaskFactories() PackageTaskFactories { + return []packageTaskFactory{ + // OS package installed catalogers /////////////////////////////////////////////////////////////////////////// + newSimplePackageTaskFactory(arch.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "alpm", "archlinux"), + newSimplePackageTaskFactory(alpine.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "apk", "alpine"), + newSimplePackageTaskFactory(debian.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "dpkg", "debian"), + newSimplePackageTaskFactory(gentoo.NewPortageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "portage", "gentoo"), + newSimplePackageTaskFactory(redhat.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "rpm", "redhat"), + + // OS package declared catalogers /////////////////////////////////////////////////////////////////////////// + newSimplePackageTaskFactory(redhat.NewArchiveCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.OSTag, "linux", "rpm", "redhat"), + + // language-specific package installed catalogers /////////////////////////////////////////////////////////////////////////// + newSimplePackageTaskFactory(cpp.NewConanInfoCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "cpp", "conan"), + newSimplePackageTaskFactory(javascript.NewPackageCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "javascript", "node"), + newSimplePackageTaskFactory(php.NewComposerInstalledCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "php", "composer"), + newSimplePackageTaskFactory(r.NewPackageCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "r"), + newSimplePackageTaskFactory(ruby.NewInstalledGemSpecCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "ruby", "gem", "gemspec"), + newSimplePackageTaskFactory(rust.NewAuditBinaryCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "rust", "binary"), + + // language-specific package declared catalogers /////////////////////////////////////////////////////////////////////////// + newSimplePackageTaskFactory(cpp.NewConanCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "cpp", "conan"), + newSimplePackageTaskFactory(dart.NewPubspecLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dart"), + newSimplePackageTaskFactory(dotnet.NewDotnetDepsCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#"), + newSimplePackageTaskFactory(elixir.NewMixLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "elixir"), + newSimplePackageTaskFactory(erlang.NewRebarLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "erlang"), + newSimplePackageTaskFactory(erlang.NewOTPCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "erlang", "otp"), + newSimplePackageTaskFactory(haskell.NewHackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "haskell", "hackage", "cabal"), + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return golang.NewGoModuleFileCataloger(cfg.PackagesConfig.Golang) + }, + pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "go", "golang", "gomod", + ), + newSimplePackageTaskFactory(java.NewGradleLockfileCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "java", "gradle"), + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return java.NewPomCataloger(cfg.PackagesConfig.JavaArchive) + }, + pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "java", "maven", + ), + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return javascript.NewLockCataloger(cfg.PackagesConfig.JavaScript) + }, + pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "javascript", "node", "npm", + ), + newSimplePackageTaskFactory(php.NewComposerLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "php", "composer"), + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return python.NewPackageCataloger(cfg.PackagesConfig.Python) + }, + pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "python", + ), + newSimplePackageTaskFactory(ruby.NewGemFileLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "ruby", "gem"), + newSimplePackageTaskFactory(ruby.NewGemSpecCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "ruby", "gem", "gemspec"), + newSimplePackageTaskFactory(rust.NewCargoLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "rust", "cargo"), + newSimplePackageTaskFactory(swift.NewCocoapodsCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swift", "cocoapods"), + newSimplePackageTaskFactory(swift.NewSwiftPackageManagerCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swift", "spm"), + + // language-specific package for both image and directory scans (but not necessarily declared) //////////////////////////////////////// + newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "dotnet", "c#", "binary"), + newSimplePackageTaskFactory(python.NewInstalledPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "python"), + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return golang.NewGoModuleBinaryCataloger(cfg.PackagesConfig.Golang) + }, + pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "go", "golang", "gomod", "binary", + ), + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return java.NewArchiveCataloger(cfg.PackagesConfig.JavaArchive) + }, + pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "java", "maven", + ), + newSimplePackageTaskFactory(java.NewNativeImageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "java"), + newSimplePackageTaskFactory(nix.NewStoreCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "nix"), + + // other package catalogers /////////////////////////////////////////////////////////////////////////// + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return binary.NewClassifierCataloger(cfg.PackagesConfig.Binary) + }, + pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", + ), + newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"), + newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"), + newPackageTaskFactory( + func(cfg CatalogingFactoryConfig) pkg.Cataloger { + return kernel.NewLinuxKernelCataloger(cfg.PackagesConfig.LinuxKernel) + }, + pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "linux", "kernel", + ), + newSimplePackageTaskFactory(sbomCataloger.NewCataloger, "sbom"), // note: not evidence of installed packages + newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"), + } +} diff --git a/internal/task/relationship_tasks.go b/internal/task/relationship_tasks.go new file mode 100644 index 00000000000..2732d04e893 --- /dev/null +++ b/internal/task/relationship_tasks.go @@ -0,0 +1,35 @@ +package task + +import ( + "context" + + "github.com/anchore/syft/internal/relationship" + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/source" +) + +var _ artifact.Identifiable = (*sourceIdentifierAdapter)(nil) + +type sourceIdentifierAdapter struct { + desc source.Description +} + +func (s sourceIdentifierAdapter) ID() artifact.ID { + return artifact.ID(s.desc.ID) +} + +func NewRelationshipsTask(cfg cataloging.RelationshipsConfig, src source.Description) Task { + fn := func(_ context.Context, _ file.Resolver, builder sbomsync.Builder) error { + relationship.Finalize( + builder, + cfg, + &sourceIdentifierAdapter{desc: src}) + + return nil + } + + return NewTask("relationships-cataloger", fn) +} diff --git a/internal/task/selection.go b/internal/task/selection.go new file mode 100644 index 00000000000..bd0cacdfbeb --- /dev/null +++ b/internal/task/selection.go @@ -0,0 +1,176 @@ +package task + +import ( + "fmt" + "sort" + + "github.com/scylladb/go-set/strset" + + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" +) + +// Selection represents the users request for a subset of tasks to run and the resulting set of task names that were +// selected. Additionally, all tokens that were matched on to reach the returned conclusion are also provided. +type Selection struct { + Request pkgcataloging.SelectionRequest + Result *strset.Set + TokensByTask map[string]TokenSelection +} + +// TokenSelection represents the tokens that were matched on to either include or exclude a given task (based on expression evaluation). +type TokenSelection struct { + SelectedOn *strset.Set + DeselectedOn *strset.Set +} + +func newTokenSelection(selected, deselected []string) TokenSelection { + return TokenSelection{ + SelectedOn: strset.New(selected...), + DeselectedOn: strset.New(deselected...), + } +} + +func (ts *TokenSelection) merge(other ...TokenSelection) { + for _, o := range other { + if ts.SelectedOn != nil { + ts.SelectedOn.Add(o.SelectedOn.List()...) + } + if ts.DeselectedOn != nil { + ts.DeselectedOn.Add(o.DeselectedOn.List()...) + } + } +} + +func newSelection() Selection { + return Selection{ + Result: strset.New(), + TokensByTask: make(map[string]TokenSelection), + } +} + +// Select parses the given expressions as two sets: expressions that represent a "set" operation, and expressions that +// represent all other operations. The parsed expressions are then evaluated against the given tasks to return +// a subset (or the same) set of tasks. +func Select(allTasks []Task, selectionRequest pkgcataloging.SelectionRequest) ([]Task, Selection, error) { + nodes := newExpressionsFromSelectionRequest(newExpressionContext(allTasks), selectionRequest) + + finalTasks, selection := selectByExpressions(allTasks, nodes) + + selection.Request = selectionRequest + + return finalTasks, selection, nodes.Validate() +} + +// selectByExpressions the set of tasks to run based on the given expression(s). +func selectByExpressions(ts tasks, nodes Expressions) (tasks, Selection) { + if len(nodes) == 0 { + return ts, newSelection() + } + + finalSet := newSet() + selectionSet := newSet() + addSet := newSet() + removeSet := newSet() + + allSelections := make(map[string]TokenSelection) + + nodes = nodes.Clone() + sort.Sort(nodes) + + for i, node := range nodes { + if len(node.Errors) > 0 { + continue + } + selectedTasks, selections := evaluateExpression(ts, node) + + for name, ss := range selections { + if selection, exists := allSelections[name]; exists { + ss.merge(selection) + } + allSelections[name] = ss + } + + if len(selectedTasks) == 0 { + log.WithFields("selection", fmt.Sprintf("%q", node.String())).Warn("no cataloger tasks selected found for given selection (this might be a misconfiguration)") + } + + switch node.Operation { + case SetOperation: + finalSet.Add(selectedTasks...) + case AddOperation, "": + addSet.Add(selectedTasks...) + case RemoveOperation: + removeSet.Add(selectedTasks...) + case SubSelectOperation: + selectionSet.Add(selectedTasks...) + default: + nodes[i].Errors = append(nodes[i].Errors, ErrInvalidOperator) + } + } + + if len(selectionSet.tasks) > 0 { + finalSet.Intersect(selectionSet.Tasks()...) + } + finalSet.Remove(removeSet.Tasks()...) + finalSet.Add(addSet.Tasks()...) + + finalTasks := finalSet.Tasks() + + return finalTasks, Selection{ + Result: strset.New(finalTasks.Names()...), + TokensByTask: allSelections, + } +} + +// evaluateExpression returns the set of tasks that match the given expression (as well as all tokens that were matched +// on to reach the returned conclusion). +func evaluateExpression(ts tasks, node Expression) ([]Task, map[string]TokenSelection) { + selection := make(map[string]TokenSelection) + var finalTasks []Task + + for _, t := range ts { + if !isSelected(t, node.Operand) { + continue + } + + s := newTokenSelection(nil, nil) + + switch node.Operation { + case SetOperation, SubSelectOperation, AddOperation: + s.SelectedOn.Add(node.Operand) + case RemoveOperation: + s.DeselectedOn.Add(node.Operand) + } + + finalTasks = append(finalTasks, t) + + if og, exists := selection[t.Name()]; exists { + s.merge(og) + } + + selection[t.Name()] = s + } + return finalTasks, selection +} + +// isSelected returns true if the given task matches the given token. If the token is "all" then the task is always selected. +func isSelected(td Task, token string) bool { + if token == "all" { + return true + } + + if ts, ok := td.(Selector); ok { + // use the selector to verify all tags + if ts.HasAllSelectors(token) { + return true + } + } + + // only do exact name matching + if td.Name() == token { + return true + } + + return false +} diff --git a/internal/task/selection_test.go b/internal/task/selection_test.go new file mode 100644 index 00000000000..d834480df8d --- /dev/null +++ b/internal/task/selection_test.go @@ -0,0 +1,369 @@ +package task + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/file" +) + +func dummyTask(name string, tags ...string) Task { + return NewTask(name, func(ctx context.Context, resolver file.Resolver, sbom sbomsync.Builder) error { + panic("not implemented") + }, tags...) +} + +// note: this test fixture does not need to be kept up to date here, but makes a great test subject +func createDummyTasks() tasks { + return []Task{ + // OS package installed catalogers + dummyTask("alpm-db-cataloger", "directory", "installed", "image", "os", "alpm", "archlinux"), + dummyTask("apk-db-cataloger", "directory", "installed", "image", "os", "apk", "alpine"), + dummyTask("dpkg-db-cataloger", "directory", "installed", "image", "os", "dpkg", "debian"), + dummyTask("portage-cataloger", "directory", "installed", "image", "os", "portage", "gentoo"), + dummyTask("rpm-db-cataloger", "directory", "installed", "image", "os", "rpm", "redhat"), + + // OS package declared catalogers + dummyTask("rpm-archive-cataloger", "declared", "directory", "os", "rpm", "redhat"), + + // language-specific package installed catalogers + dummyTask("conan-info-cataloger", "installed", "image", "language", "cpp", "conan"), + dummyTask("javascript-package-cataloger", "installed", "image", "language", "javascript", "node"), + dummyTask("php-composer-installed-cataloger", "installed", "image", "language", "php", "composer"), + dummyTask("ruby-installed-gemspec-cataloger", "installed", "image", "language", "ruby", "gem", "gemspec"), + dummyTask("rust-cargo-lock-cataloger", "installed", "image", "language", "rust", "binary"), + + // language-specific package declared catalogers + dummyTask("conan-cataloger", "declared", "directory", "language", "cpp", "conan"), + dummyTask("dart-pubspec-lock-cataloger", "declared", "directory", "language", "dart"), + dummyTask("dotnet-deps-cataloger", "declared", "directory", "language", "dotnet", "c#"), + dummyTask("elixir-mix-lock-cataloger", "declared", "directory", "language", "elixir"), + dummyTask("erlang-rebar-lock-cataloger", "declared", "directory", "language", "erlang"), + dummyTask("javascript-lock-cataloger", "declared", "directory", "language", "javascript", "node", "npm"), + + // language-specific package for both image and directory scans (but not necessarily declared) + dummyTask("dotnet-portable-executable-cataloger", "directory", "installed", "image", "language", "dotnet", "c#"), + dummyTask("python-installed-package-cataloger", "directory", "installed", "image", "language", "python"), + dummyTask("go-module-binary-cataloger", "directory", "installed", "image", "language", "go", "golang", "gomod", "binary"), + dummyTask("java-archive-cataloger", "directory", "installed", "image", "language", "java", "maven"), + dummyTask("graalvm-native-image-cataloger", "directory", "installed", "image", "language", "java"), + + // other package catalogers + dummyTask("binary-cataloger", "declared", "directory", "image", "binary"), + dummyTask("github-actions-usage-cataloger", "declared", "directory", "github", "github-actions"), + dummyTask("github-action-workflow-usage-cataloger", "declared", "directory", "github", "github-actions"), + dummyTask("sbom-cataloger", "declared", "directory", "image", "sbom"), + } +} + +func TestSelect(t *testing.T) { + + tests := []struct { + name string + allTasks []Task + basis []string + expressions []string + wantNames []string + wantTokens map[string]TokenSelection + wantRequest pkgcataloging.SelectionRequest + wantErr assert.ErrorAssertionFunc + }{ + { + name: "empty input", + allTasks: []Task{}, + basis: []string{}, + expressions: []string{}, + wantNames: []string{}, + wantTokens: map[string]TokenSelection{}, + wantRequest: pkgcataloging.SelectionRequest{}, + }, + { + name: "use default tasks", + allTasks: createDummyTasks(), + basis: []string{ + "image", + }, + expressions: []string{}, + wantNames: []string{ + "alpm-db-cataloger", + "apk-db-cataloger", + "dpkg-db-cataloger", + "portage-cataloger", + "rpm-db-cataloger", + "conan-info-cataloger", + "javascript-package-cataloger", + "php-composer-installed-cataloger", + "ruby-installed-gemspec-cataloger", + "rust-cargo-lock-cataloger", + "dotnet-portable-executable-cataloger", + "python-installed-package-cataloger", + "go-module-binary-cataloger", + "java-archive-cataloger", + "graalvm-native-image-cataloger", + "binary-cataloger", + "sbom-cataloger", + }, + wantTokens: map[string]TokenSelection{ + "alpm-db-cataloger": newTokenSelection([]string{"image"}, nil), + "apk-db-cataloger": newTokenSelection([]string{"image"}, nil), + "dpkg-db-cataloger": newTokenSelection([]string{"image"}, nil), + "portage-cataloger": newTokenSelection([]string{"image"}, nil), + "rpm-db-cataloger": newTokenSelection([]string{"image"}, nil), + "conan-info-cataloger": newTokenSelection([]string{"image"}, nil), + "javascript-package-cataloger": newTokenSelection([]string{"image"}, nil), + "php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil), + "ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil), + "rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil), + "dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil), + "python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil), + "go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil), + "java-archive-cataloger": newTokenSelection([]string{"image"}, nil), + "graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil), + "binary-cataloger": newTokenSelection([]string{"image"}, nil), + "sbom-cataloger": newTokenSelection([]string{"image"}, nil), + }, + wantRequest: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + }, + { + name: "select, add, and remove tasks", + allTasks: createDummyTasks(), + basis: []string{ + "image", + }, + expressions: []string{ + "+github-actions-usage-cataloger", + "-dpkg", + "os", + }, + wantNames: []string{ + "alpm-db-cataloger", + "apk-db-cataloger", + "portage-cataloger", + "rpm-db-cataloger", + "github-actions-usage-cataloger", + }, + wantTokens: map[string]TokenSelection{ + // selected + "alpm-db-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "apk-db-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "dpkg-db-cataloger": newTokenSelection([]string{"image", "os"}, []string{"dpkg"}), + "portage-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "rpm-db-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "github-actions-usage-cataloger": newTokenSelection([]string{"github-actions-usage-cataloger"}, nil), + + // ultimately not selected + "rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil), + "conan-info-cataloger": newTokenSelection([]string{"image"}, nil), + "javascript-package-cataloger": newTokenSelection([]string{"image"}, nil), + "php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil), + "ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil), + "rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil), + "dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil), + "python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil), + "go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil), + "java-archive-cataloger": newTokenSelection([]string{"image"}, nil), + "graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil), + "binary-cataloger": newTokenSelection([]string{"image"}, nil), + "sbom-cataloger": newTokenSelection([]string{"image"}, nil), + }, + wantRequest: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + SubSelectTags: []string{"os"}, + RemoveNamesOrTags: []string{"dpkg"}, + AddNames: []string{"github-actions-usage-cataloger"}, + }, + }, + { + name: "allow for partial selections", + allTasks: createDummyTasks(), + basis: []string{ + "image", + }, + expressions: []string{ + // valid... + "+github-actions-usage-cataloger", + "-dpkg", + "os", + // invalid... + "+python", + "rust-cargo-lock-cataloger", + }, + wantNames: []string{ + "alpm-db-cataloger", + "apk-db-cataloger", + "portage-cataloger", + "rpm-db-cataloger", + "github-actions-usage-cataloger", + }, + wantTokens: map[string]TokenSelection{ + // selected + "alpm-db-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "apk-db-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "dpkg-db-cataloger": newTokenSelection([]string{"image", "os"}, []string{"dpkg"}), + "portage-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "rpm-db-cataloger": newTokenSelection([]string{"image", "os"}, nil), + "github-actions-usage-cataloger": newTokenSelection([]string{"github-actions-usage-cataloger"}, nil), + + // ultimately not selected + "rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil), + "conan-info-cataloger": newTokenSelection([]string{"image"}, nil), + "javascript-package-cataloger": newTokenSelection([]string{"image"}, nil), + "php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil), + "ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil), + "rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil), + "dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil), + "python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil), // note: there is no python token used for selection + "go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil), + "java-archive-cataloger": newTokenSelection([]string{"image"}, nil), + "graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil), + "binary-cataloger": newTokenSelection([]string{"image"}, nil), + "sbom-cataloger": newTokenSelection([]string{"image"}, nil), + }, + wantRequest: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + SubSelectTags: []string{"os", "rust-cargo-lock-cataloger"}, + RemoveNamesOrTags: []string{"dpkg"}, + AddNames: []string{"github-actions-usage-cataloger", "python"}, + }, + wantErr: assert.Error, // !important! + }, + { + name: "select all tasks", + allTasks: createDummyTasks(), + basis: []string{ + "all", + }, + expressions: []string{}, + wantNames: []string{ + "alpm-db-cataloger", + "apk-db-cataloger", + "dpkg-db-cataloger", + "portage-cataloger", + "rpm-db-cataloger", + "rpm-archive-cataloger", + "conan-info-cataloger", + "javascript-package-cataloger", + "php-composer-installed-cataloger", + "ruby-installed-gemspec-cataloger", + "rust-cargo-lock-cataloger", + "conan-cataloger", + "dart-pubspec-lock-cataloger", + "dotnet-deps-cataloger", + "elixir-mix-lock-cataloger", + "erlang-rebar-lock-cataloger", + "javascript-lock-cataloger", + "dotnet-portable-executable-cataloger", + "python-installed-package-cataloger", + "go-module-binary-cataloger", + "java-archive-cataloger", + "graalvm-native-image-cataloger", + "binary-cataloger", + "github-actions-usage-cataloger", + "github-action-workflow-usage-cataloger", + "sbom-cataloger", + }, + wantTokens: map[string]TokenSelection{ + "alpm-db-cataloger": newTokenSelection([]string{"all"}, nil), + "apk-db-cataloger": newTokenSelection([]string{"all"}, nil), + "dpkg-db-cataloger": newTokenSelection([]string{"all"}, nil), + "portage-cataloger": newTokenSelection([]string{"all"}, nil), + "rpm-db-cataloger": newTokenSelection([]string{"all"}, nil), + "rpm-archive-cataloger": newTokenSelection([]string{"all"}, nil), + "conan-info-cataloger": newTokenSelection([]string{"all"}, nil), + "javascript-package-cataloger": newTokenSelection([]string{"all"}, nil), + "php-composer-installed-cataloger": newTokenSelection([]string{"all"}, nil), + "ruby-installed-gemspec-cataloger": newTokenSelection([]string{"all"}, nil), + "rust-cargo-lock-cataloger": newTokenSelection([]string{"all"}, nil), + "conan-cataloger": newTokenSelection([]string{"all"}, nil), + "dart-pubspec-lock-cataloger": newTokenSelection([]string{"all"}, nil), + "dotnet-deps-cataloger": newTokenSelection([]string{"all"}, nil), + "elixir-mix-lock-cataloger": newTokenSelection([]string{"all"}, nil), + "erlang-rebar-lock-cataloger": newTokenSelection([]string{"all"}, nil), + "javascript-lock-cataloger": newTokenSelection([]string{"all"}, nil), + "dotnet-portable-executable-cataloger": newTokenSelection([]string{"all"}, nil), + "python-installed-package-cataloger": newTokenSelection([]string{"all"}, nil), + "go-module-binary-cataloger": newTokenSelection([]string{"all"}, nil), + "java-archive-cataloger": newTokenSelection([]string{"all"}, nil), + "graalvm-native-image-cataloger": newTokenSelection([]string{"all"}, nil), + "binary-cataloger": newTokenSelection([]string{"all"}, nil), + "github-actions-usage-cataloger": newTokenSelection([]string{"all"}, nil), + "github-action-workflow-usage-cataloger": newTokenSelection([]string{"all"}, nil), + "sbom-cataloger": newTokenSelection([]string{"all"}, nil), + }, + wantRequest: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"all"}, + }, + }, + { + name: "set default with multiple tags", + allTasks: createDummyTasks(), + basis: []string{ + "gemspec", + "python", + }, + expressions: []string{}, + wantNames: []string{ + "ruby-installed-gemspec-cataloger", + "python-installed-package-cataloger", + }, + wantTokens: map[string]TokenSelection{ + "ruby-installed-gemspec-cataloger": newTokenSelection([]string{"gemspec"}, nil), + "python-installed-package-cataloger": newTokenSelection([]string{"python"}, nil), + }, + wantRequest: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"gemspec", "python"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = assert.NoError + } + + req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...) + + got, gotEvidence, err := Select(tt.allTasks, req) + tt.wantErr(t, err) + if err != nil { + // dev note: this is useful for debugging when needed... + //for _, e := range gotEvidence.Request.Expressions { + // t.Logf("expression (errors %q): %#v", e.Errors, e) + //} + + // note: we DON'T bail early in validations... this is because we should always return the full set of + // of selected tasks and surrounding evidence. + } + + gotNames := make([]string, 0) + for _, g := range got { + gotNames = append(gotNames, g.Name()) + } + + assert.Equal(t, tt.wantNames, gotNames) + + // names in selection should match all tasks returned + require.Len(t, tt.wantNames, gotEvidence.Result.Size(), "selected tasks should match all tasks returned (but does not)") + assert.ElementsMatch(t, tt.wantNames, gotEvidence.Result.List(), "selected tasks should match all tasks returned (but does not)") + + setCompare := cmp.Comparer(func(x, y *strset.Set) bool { + return x.IsEqual(y) + }) + + if d := cmp.Diff(tt.wantTokens, gotEvidence.TokensByTask, setCompare); d != "" { + t.Errorf("unexpected tokens by task (-want +got):\n%s", d) + } + assert.Equal(t, tt.wantRequest, gotEvidence.Request) + + }) + } +} diff --git a/internal/task/set.go b/internal/task/set.go new file mode 100644 index 00000000000..6d2b91dafe4 --- /dev/null +++ b/internal/task/set.go @@ -0,0 +1,70 @@ +package task + +type set struct { + order []string + tasks map[string]Task +} + +func newSet(tasks ...Task) *set { + s := &set{ + order: []string{}, + tasks: make(map[string]Task), + } + + s.Add(tasks...) + + return s +} + +func (ts *set) Len() int { + return len(ts.tasks) +} + +func (ts *set) Add(tasks ...Task) { + for _, t := range tasks { + taskName := t.Name() + if _, exists := ts.tasks[taskName]; exists { + continue + } + ts.tasks[taskName] = t + ts.order = append(ts.order, taskName) + } +} + +func (ts *set) Remove(tasks ...Task) { + for _, t := range tasks { + taskName := t.Name() + if _, exists := ts.tasks[taskName]; !exists { + continue + } + + delete(ts.tasks, taskName) + for i, t := range ts.order { + if t == taskName { + ts.order = append(ts.order[:i], ts.order[i+1:]...) + break + } + } + } +} + +func (ts *set) Intersect(tasks ...Task) { + other := newSet(tasks...) + result := newSet() + for _, taskName := range ts.order { + // we make a new set to prevent the original set from being modified while we are iterating over "order" + if _, exists := other.tasks[taskName]; exists { + // note: keep the original task and ordering + result.Add(ts.tasks[taskName]) + } + } + *ts = *result +} + +func (ts set) Tasks() tasks { + var result []Task + for _, name := range ts.order { + result = append(result, ts.tasks[name]) + } + return result +} diff --git a/internal/task/set_test.go b/internal/task/set_test.go new file mode 100644 index 00000000000..d5bfea8a170 --- /dev/null +++ b/internal/task/set_test.go @@ -0,0 +1,154 @@ +package task + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/file" +) + +var _ Task = (*mockTask)(nil) + +type mockTask struct { + name string +} + +func (m mockTask) Execute(_ context.Context, _ file.Resolver, _ sbomsync.Builder) error { + panic("implement me") +} + +func (m mockTask) Name() string { + return m.name +} + +func Test_set_Add(t *testing.T) { + tests := []struct { + name string + initialTasks []Task + newTasks []Task + expected []string + }{ + { + name: "add unique tasks", + initialTasks: []Task{mockTask{"task2"}, mockTask{"task1"}}, + newTasks: []Task{mockTask{"task3"}}, + expected: []string{ + "task2", // note order is honored + "task1", + "task3", + }, + }, + { + name: "add duplicate tasks", + initialTasks: []Task{mockTask{"task1"}, mockTask{"task2"}}, + newTasks: []Task{mockTask{"task1"}}, + expected: []string{ + "task1", + "task2", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := newSet(tt.initialTasks...) + s.Add(tt.newTasks...) + got := s.Tasks() + var gotNames []string + for _, tsk := range got { + gotNames = append(gotNames, tsk.Name()) + } + assert.Equal(t, tt.expected, gotNames) + }) + } +} + +func Test_set_Remove(t *testing.T) { + tests := []struct { + name string + initialTasks []Task + tasksToRemove []Task + expectedOrder []string + }{ + { + name: "remove existing tasks", + initialTasks: []Task{mockTask{"task1"}, mockTask{"task2"}, mockTask{"task3"}}, + tasksToRemove: []Task{mockTask{"task2"}}, + expectedOrder: []string{"task1", "task3"}, + }, + { + name: "remove non-existing tasks", + initialTasks: []Task{mockTask{"task1"}, mockTask{"task2"}}, + tasksToRemove: []Task{mockTask{"task3"}}, + expectedOrder: []string{"task1", "task2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := newSet(tt.initialTasks...) + s.Remove(tt.tasksToRemove...) + assert.Equal(t, tt.expectedOrder, s.order) + }) + } +} + +func Test_set_Intersect(t *testing.T) { + tests := []struct { + name string + initialTasks []Task + intersectTasks []Task + expectedOrder []string + }{ + { + name: "intersect with overlapping tasks", + initialTasks: []Task{mockTask{"task1"}, mockTask{"task2"}}, + intersectTasks: []Task{mockTask{"task2"}, mockTask{"task3"}}, + expectedOrder: []string{"task2"}, + }, + { + name: "intersect with non-overlapping tasks", + initialTasks: []Task{mockTask{"task1"}, mockTask{"task4"}}, + intersectTasks: []Task{mockTask{"task2"}, mockTask{"task3"}}, + expectedOrder: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := newSet(tt.initialTasks...) + s.Intersect(tt.intersectTasks...) + assert.Equal(t, tt.expectedOrder, s.order) + }) + } +} + +func Test_set_Tasks(t *testing.T) { + tests := []struct { + name string + initialTasks []Task + expectedTasks tasks + }{ + { + name: "empty set", + initialTasks: []Task{}, + expectedTasks: nil, + }, + { + name: "get tasks from set", + initialTasks: []Task{mockTask{"task1"}, mockTask{"task2"}}, + expectedTasks: []Task{mockTask{"task1"}, mockTask{"task2"}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := newSet(tt.initialTasks...) + resultTasks := s.Tasks() + assert.Equal(t, tt.expectedTasks, resultTasks) + }) + } +} diff --git a/internal/task/task.go b/internal/task/task.go new file mode 100644 index 00000000000..a1d9add056a --- /dev/null +++ b/internal/task/task.go @@ -0,0 +1,89 @@ +package task + +import ( + "context" + "fmt" + "sort" + + "github.com/scylladb/go-set/strset" + + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/syft/file" +) + +var _ interface { + Task + Selector +} = (*task)(nil) + +// Task is a function that can wrap a cataloger to populate the SBOM with data (coordinated through the mutex). +type Task interface { + Name() string + Execute(context.Context, file.Resolver, sbomsync.Builder) error +} + +type Selector interface { + HasAllSelectors(...string) bool + Selectors() []string +} + +type tasks []Task + +type task struct { + name string + selectors *strset.Set + task func(context.Context, file.Resolver, sbomsync.Builder) error +} + +func NewTask(name string, tsk func(context.Context, file.Resolver, sbomsync.Builder) error, tags ...string) Task { + if tsk == nil { + panic(fmt.Errorf("task cannot be nil")) + } + tags = append(tags, name) + return &task{ + name: name, + selectors: strset.New(tags...), + task: tsk, + } +} + +func (t task) HasAllSelectors(ids ...string) bool { + // tags or name + return t.selectors.Has(ids...) +} + +func (t task) Selectors() []string { + return t.selectors.List() +} + +func (t task) Name() string { + return t.name +} + +func (t task) Execute(ctx context.Context, resolver file.Resolver, sbom sbomsync.Builder) error { + return t.task(ctx, resolver, sbom) +} + +func (ts tasks) Names() []string { + var names []string + for _, td := range ts { + names = append(names, td.Name()) + } + return names +} + +func (ts tasks) Tags() []string { + tags := strset.New() + for _, td := range ts { + if s, ok := td.(Selector); ok { + tags.Add(s.Selectors()...) + } + + tags.Remove(td.Name()) + } + + tagsList := tags.List() + sort.Strings(tagsList) + + return tagsList +} diff --git a/schema/json/schema-11.0.2.json b/schema/json/schema-11.0.2.json new file mode 100644 index 00000000000..9fcdcbb74a7 --- /dev/null +++ b/schema/json/schema-11.0.2.json @@ -0,0 +1,2023 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/11.0.2/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableMetadata": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/PythonRequirementsMetadata" + }, + { + "$ref": "#/$defs/RDescriptionFileMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + }, + { + "$ref": "#/$defs/SwiftPackageManagerMetadata" + }, + { + "$ref": "#/$defs/WordpressPluginMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonRequirementsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "RDescriptionFileMetadata": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerMetadata": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "WordpressPluginMetadata": { + "properties": { + "plugin_name": { + "type": "string" + }, + "author": { + "type": "string" + }, + "author_uri": { + "type": "string" + } + }, + "type": "object", + "required": [ + "plugin_name" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-14.0.0.json b/schema/json/schema-14.0.0.json new file mode 100644 index 00000000000..0c4988fd776 --- /dev/null +++ b/schema/json/schema-14.0.0.json @@ -0,0 +1,2148 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/14.0.0/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-15.0.0.json b/schema/json/schema-15.0.0.json new file mode 100644 index 00000000000..63e0f110d8b --- /dev/null +++ b/schema/json/schema-15.0.0.json @@ -0,0 +1,2147 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/15.0.0/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-16.0.0.json b/schema/json/schema-16.0.0.json new file mode 100644 index 00000000000..d282375e0a3 --- /dev/null +++ b/schema/json/schema-16.0.0.json @@ -0,0 +1,2164 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.0/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-16.0.1.json b/schema/json/schema-16.0.1.json new file mode 100644 index 00000000000..33abb0598ba --- /dev/null +++ b/schema/json/schema-16.0.1.json @@ -0,0 +1,2220 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.1/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-16.0.2.json b/schema/json/schema-16.0.2.json new file mode 100644 index 00000000000..58a68b0c91c --- /dev/null +++ b/schema/json/schema-16.0.2.json @@ -0,0 +1,2258 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.2/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-16.0.3.json b/schema/json/schema-16.0.3.json new file mode 100644 index 00000000000..e846be24170 --- /dev/null +++ b/schema/json/schema-16.0.3.json @@ -0,0 +1,2284 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.3/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-16.0.4.json b/schema/json/schema-16.0.4.json new file mode 100644 index 00000000000..cba3ff9855b --- /dev/null +++ b/schema/json/schema-16.0.4.json @@ -0,0 +1,2304 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.4/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + }, + { + "$ref": "#/$defs/WordpressPluginEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "WordpressPluginEntry": { + "properties": { + "pluginInstallDirectory": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorUri": { + "type": "string" + } + }, + "type": "object", + "required": [ + "pluginInstallDirectory" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-latest.json b/schema/json/schema-latest.json new file mode 100644 index 00000000000..cba3ff9855b --- /dev/null +++ b/schema/json/schema-latest.json @@ -0,0 +1,2304 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.4/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + }, + { + "$ref": "#/$defs/WordpressPluginEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "WordpressPluginEntry": { + "properties": { + "pluginInstallDirectory": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorUri": { + "type": "string" + } + }, + "type": "object", + "required": [ + "pluginInstallDirectory" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/syft/cataloging/archive_search.go b/syft/cataloging/archive_search.go new file mode 100644 index 00000000000..bfaf0a1b2a9 --- /dev/null +++ b/syft/cataloging/archive_search.go @@ -0,0 +1,23 @@ +package cataloging + +type ArchiveSearchConfig struct { + IncludeIndexedArchives bool `yaml:"include-indexed-archives" json:"include-indexed-archives" mapstructure:"include-indexed-archives"` + IncludeUnindexedArchives bool `yaml:"include-unindexed-archives" json:"include-unindexed-archives" mapstructure:"include-unindexed-archives"` +} + +func DefaultArchiveSearchConfig() ArchiveSearchConfig { + return ArchiveSearchConfig{ + IncludeIndexedArchives: true, + IncludeUnindexedArchives: false, + } +} + +func (c ArchiveSearchConfig) WithIncludeIndexedArchives(include bool) ArchiveSearchConfig { + c.IncludeIndexedArchives = include + return c +} + +func (c ArchiveSearchConfig) WithIncludeUnindexedArchives(include bool) ArchiveSearchConfig { + c.IncludeUnindexedArchives = include + return c +} diff --git a/syft/cataloging/data_generation.go b/syft/cataloging/data_generation.go new file mode 100644 index 00000000000..8505298f7e6 --- /dev/null +++ b/syft/cataloging/data_generation.go @@ -0,0 +1,16 @@ +package cataloging + +type DataGenerationConfig struct { + GenerateCPEs bool `yaml:"generate-cpes" json:"generate-cpes" mapstructure:"generate-cpes"` +} + +func DefaultDataGenerationConfig() DataGenerationConfig { + return DataGenerationConfig{ + GenerateCPEs: true, + } +} + +func (c DataGenerationConfig) WithGenerateCPEs(generate bool) DataGenerationConfig { + c.GenerateCPEs = generate + return c +} diff --git a/syft/cataloging/filecataloging/config.go b/syft/cataloging/filecataloging/config.go new file mode 100644 index 00000000000..cc639c2f17b --- /dev/null +++ b/syft/cataloging/filecataloging/config.go @@ -0,0 +1,86 @@ +package filecataloging + +import ( + "crypto" + "encoding/json" + "fmt" + "strings" + + intFile "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/file/cataloger/executable" + "github.com/anchore/syft/syft/file/cataloger/filecontent" +) + +type Config struct { + Selection file.Selection `yaml:"selection" json:"selection" mapstructure:"selection"` + Hashers []crypto.Hash `yaml:"hashers" json:"hashers" mapstructure:"hashers"` + Content filecontent.Config `yaml:"content" json:"content" mapstructure:"content"` + Executable executable.Config `yaml:"executable" json:"executable" mapstructure:"executable"` +} + +type configMarshaledForm struct { + Selection file.Selection `yaml:"selection" json:"selection" mapstructure:"selection"` + Hashers []string `yaml:"hashers" json:"hashers" mapstructure:"hashers"` + Content filecontent.Config `yaml:"content" json:"content" mapstructure:"content"` +} + +func DefaultConfig() Config { + hashers, err := intFile.Hashers("sha256") + if err != nil { + log.WithFields("error", err).Warn("unable to create file hashers") + } + return Config{ + Selection: file.FilesOwnedByPackageSelection, + Hashers: hashers, + Content: filecontent.DefaultConfig(), + Executable: executable.DefaultConfig(), + } +} + +func (cfg Config) MarshalJSON() ([]byte, error) { + marshaled := configMarshaledForm{ + Selection: cfg.Selection, + Hashers: hashersToString(cfg.Hashers), + } + return json.Marshal(marshaled) +} + +func hashersToString(hashers []crypto.Hash) []string { + var result []string + for _, h := range hashers { + result = append(result, strings.ToLower(h.String())) + } + return result +} + +func (cfg *Config) UnmarshalJSON(data []byte) error { + var marshaled configMarshaledForm + if err := json.Unmarshal(data, &marshaled); err != nil { + return err + } + + hashers, err := intFile.Hashers(marshaled.Hashers...) + if err != nil { + return fmt.Errorf("unable to parse configured hashers: %w", err) + } + cfg.Selection = marshaled.Selection + cfg.Hashers = hashers + return nil +} + +func (cfg Config) WithSelection(selection file.Selection) Config { + cfg.Selection = selection + return cfg +} + +func (cfg Config) WithHashers(hashers ...crypto.Hash) Config { + cfg.Hashers = intFile.NormalizeHashes(hashers) + return cfg +} + +func (cfg Config) WithContentConfig(content filecontent.Config) Config { + cfg.Content = content + return cfg +} diff --git a/syft/cataloging/filecataloging/config_test.go b/syft/cataloging/filecataloging/config_test.go new file mode 100644 index 00000000000..ca62dccb1a1 --- /dev/null +++ b/syft/cataloging/filecataloging/config_test.go @@ -0,0 +1,74 @@ +package filecataloging + +import ( + "crypto" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/file" +) + +func TestConfig_MarshalJSON(t *testing.T) { + tests := []struct { + name string + cfg Config + want []byte + wantErr require.ErrorAssertionFunc + }{ + { + name: "converts hashers to strings", + cfg: Config{ + Selection: file.FilesOwnedByPackageSelection, + Hashers: []crypto.Hash{crypto.SHA256}, + }, + want: []byte(`{"selection":"owned-by-package","hashers":["sha-256"],"content":{"globs":null,"skip-files-above-size":0}}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = require.NoError + } + got, err := tt.cfg.MarshalJSON() + tt.wantErr(t, err) + if err != nil { + return + } + if d := cmp.Diff(got, tt.want); d != "" { + t.Errorf("MarshalJSON() mismatch (-want +got):\n%s", d) + } + }) + } +} + +func TestConfig_UnmarshalJSON(t *testing.T) { + + tests := []struct { + name string + data []byte + want Config + wantErr bool + }{ + { + name: "converts strings to hashers", + data: []byte(`{"selection":"owned-by-package","hashers":["sha-256"]}`), + want: Config{ + Selection: file.FilesOwnedByPackageSelection, + Hashers: []crypto.Hash{crypto.SHA256}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := Config{} + if err := cfg.UnmarshalJSON(tt.data); (err != nil) != tt.wantErr { + t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + } + + assert.Equal(t, tt.want, cfg) + }) + } +} diff --git a/syft/cataloging/pkgcataloging/cataloger_reference.go b/syft/cataloging/pkgcataloging/cataloger_reference.go new file mode 100644 index 00000000000..715350032b6 --- /dev/null +++ b/syft/cataloging/pkgcataloging/cataloger_reference.go @@ -0,0 +1,23 @@ +package pkgcataloging + +import "github.com/anchore/syft/syft/pkg" + +type CatalogerReference struct { + Cataloger pkg.Cataloger + AlwaysEnabled bool + Tags []string +} + +func NewCatalogerReference(cataloger pkg.Cataloger, tags []string) CatalogerReference { + return CatalogerReference{ + Cataloger: cataloger, + Tags: tags, + } +} + +func NewAlwaysEnabledCatalogerReference(cataloger pkg.Cataloger) CatalogerReference { + return CatalogerReference{ + Cataloger: cataloger, + AlwaysEnabled: true, + } +} diff --git a/syft/cataloging/pkgcataloging/config.go b/syft/cataloging/pkgcataloging/config.go new file mode 100644 index 00000000000..ac55de7efe5 --- /dev/null +++ b/syft/cataloging/pkgcataloging/config.go @@ -0,0 +1,59 @@ +package pkgcataloging + +import ( + "github.com/anchore/syft/syft/pkg/cataloger/binary" + "github.com/anchore/syft/syft/pkg/cataloger/golang" + "github.com/anchore/syft/syft/pkg/cataloger/java" + "github.com/anchore/syft/syft/pkg/cataloger/javascript" + "github.com/anchore/syft/syft/pkg/cataloger/kernel" + "github.com/anchore/syft/syft/pkg/cataloger/python" +) + +type Config struct { + Binary binary.ClassifierCatalogerConfig `yaml:"binary" json:"binary" mapstructure:"binary"` + Golang golang.CatalogerConfig `yaml:"golang" json:"golang" mapstructure:"golang"` + JavaArchive java.ArchiveCatalogerConfig `yaml:"java-archive" json:"java-archive" mapstructure:"java-archive"` + JavaScript javascript.CatalogerConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"` + LinuxKernel kernel.LinuxKernelCatalogerConfig `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"` + Python python.CatalogerConfig `yaml:"python" json:"python" mapstructure:"python"` +} + +func DefaultConfig() Config { + return Config{ + Binary: binary.DefaultClassifierCatalogerConfig(), + Golang: golang.DefaultCatalogerConfig(), + LinuxKernel: kernel.DefaultLinuxKernelCatalogerConfig(), + Python: python.DefaultCatalogerConfig(), + JavaArchive: java.DefaultArchiveCatalogerConfig(), + } +} + +func (c Config) WithBinaryConfig(cfg binary.ClassifierCatalogerConfig) Config { + c.Binary = cfg + return c +} + +func (c Config) WithGolangConfig(cfg golang.CatalogerConfig) Config { + c.Golang = cfg + return c +} + +func (c Config) WithJavascriptConfig(cfg javascript.CatalogerConfig) Config { + c.JavaScript = cfg + return c +} + +func (c Config) WithLinuxKernelConfig(cfg kernel.LinuxKernelCatalogerConfig) Config { + c.LinuxKernel = cfg + return c +} + +func (c Config) WithPythonConfig(cfg python.CatalogerConfig) Config { + c.Python = cfg + return c +} + +func (c Config) WithJavaArchiveConfig(cfg java.ArchiveCatalogerConfig) Config { + c.JavaArchive = cfg + return c +} diff --git a/syft/cataloging/pkgcataloging/selection_request.go b/syft/cataloging/pkgcataloging/selection_request.go new file mode 100644 index 00000000000..7dc711e3ae7 --- /dev/null +++ b/syft/cataloging/pkgcataloging/selection_request.go @@ -0,0 +1,65 @@ +package pkgcataloging + +import ( + "strings" +) + +type SelectionRequest struct { + DefaultNamesOrTags []string `json:"default,omitempty"` + SubSelectTags []string `json:"selection,omitempty"` + AddNames []string `json:"addition,omitempty"` + RemoveNamesOrTags []string `json:"removal,omitempty"` +} + +func NewSelectionRequest() SelectionRequest { + return SelectionRequest{} +} + +func (s SelectionRequest) WithExpression(expressions ...string) SelectionRequest { + expressions = cleanSelection(expressions) + for _, expr := range expressions { + switch { + case strings.HasPrefix(expr, "+"): + s = s.WithAdditions(strings.TrimPrefix(expr, "+")) + case strings.HasPrefix(expr, "-"): + s = s.WithRemovals(strings.TrimPrefix(expr, "-")) + default: + s = s.WithSubSelections(expr) + } + } + return s +} + +func (s SelectionRequest) WithDefaults(nameOrTags ...string) SelectionRequest { + s.DefaultNamesOrTags = append(s.DefaultNamesOrTags, nameOrTags...) + return s +} + +func (s SelectionRequest) WithSubSelections(tags ...string) SelectionRequest { + s.SubSelectTags = append(s.SubSelectTags, tags...) + return s +} + +func (s SelectionRequest) WithAdditions(names ...string) SelectionRequest { + s.AddNames = append(s.AddNames, names...) + return s +} + +func (s SelectionRequest) WithRemovals(nameOrTags ...string) SelectionRequest { + s.RemoveNamesOrTags = append(s.RemoveNamesOrTags, nameOrTags...) + return s +} + +func cleanSelection(tags []string) []string { + var cleaned []string + for _, tag := range tags { + for _, t := range strings.Split(tag, ",") { + t = strings.TrimSpace(t) + if t == "" { + continue + } + cleaned = append(cleaned, t) + } + } + return cleaned +} diff --git a/syft/cataloging/pkgcataloging/tags.go b/syft/cataloging/pkgcataloging/tags.go new file mode 100644 index 00000000000..5c7b46bb077 --- /dev/null +++ b/syft/cataloging/pkgcataloging/tags.go @@ -0,0 +1,24 @@ +package pkgcataloging + +const ( + // InstalledTag is to identify packages found to be positively installed. + InstalledTag = "installed" + + // DeclaredTag is to identify packages described but not necessarily installed. + DeclaredTag = "declared" + + // ImageTag indicates the cataloger should be used when cataloging images. + ImageTag = "image" + + // DirectoryTag indicates the cataloger should be used when cataloging directories. + DirectoryTag = "directory" + + // PackageTag should be used to identify catalogers that are package-based. + PackageTag = "package" + + // OSTag should be used to identify catalogers that cataloging OS packages. + OSTag = "os" + + // LanguageTag should be used to identify catalogers that cataloging language-specific packages. + LanguageTag = "language" +) diff --git a/syft/cataloging/relationships.go b/syft/cataloging/relationships.go new file mode 100644 index 00000000000..1dbbd283235 --- /dev/null +++ b/syft/cataloging/relationships.go @@ -0,0 +1,39 @@ +package cataloging + +type RelationshipsConfig struct { + // PackageFileOwnership will include package-to-file relationships that indicate which files are owned by which packages. + PackageFileOwnership bool `yaml:"package-file-ownership" json:"package-file-ownership" mapstructure:"package-file-ownership"` + + // PackageFileOwnershipOverlap will include package-to-package relationships that indicate one package is owned by another due to files claimed to be owned by one package are also evidence of another package's existence. + // For example, if an RPM package is installed and claims to own /etc/app/package.lock and a separate NPM package was discovered by cataloging /etc/app/package.lock, then the two packages will + // have ownership overlap relationship. + PackageFileOwnershipOverlap bool `yaml:"package-file-ownership-overlap" json:"package-file-ownership-overlap" mapstructure:"package-file-ownership-overlap"` + + // ExcludeBinaryPackagesWithFileOwnershipOverlap will exclude binary packages from the package catalog that are evident by files also owned by another package. + // For example, if a binary package representing the /bin/python binary is discovered and there is a python RPM package installed which claims to + // orn /bin/python, then the binary package will be excluded from the catalog altogether if this configuration is set to true. + ExcludeBinaryPackagesWithFileOwnershipOverlap bool `yaml:"exclude-binary-packages-with-file-ownership-overlap" json:"exclude-binary-packages-with-file-ownership-overlap" mapstructure:"exclude-binary-packages-with-file-ownership-overlap"` +} + +func DefaultRelationshipsConfig() RelationshipsConfig { + return RelationshipsConfig{ + PackageFileOwnership: true, + PackageFileOwnershipOverlap: true, + ExcludeBinaryPackagesWithFileOwnershipOverlap: true, + } +} + +func (c RelationshipsConfig) WithPackageFileOwnership(ownership bool) RelationshipsConfig { + c.PackageFileOwnership = ownership + return c +} + +func (c RelationshipsConfig) WithPackageFileOwnershipOverlap(overlap bool) RelationshipsConfig { + c.PackageFileOwnershipOverlap = overlap + return c +} + +func (c RelationshipsConfig) WithExcludeBinaryPackagesWithFileOwnershipOverlap(exclude bool) RelationshipsConfig { + c.ExcludeBinaryPackagesWithFileOwnershipOverlap = exclude + return c +} diff --git a/syft/cataloging/search.go b/syft/cataloging/search.go new file mode 100644 index 00000000000..762494ade25 --- /dev/null +++ b/syft/cataloging/search.go @@ -0,0 +1,18 @@ +package cataloging + +import "github.com/anchore/syft/syft/source" + +type SearchConfig struct { + Scope source.Scope `yaml:"scope" json:"scope" mapstructure:"scope"` +} + +func DefaultSearchConfig() SearchConfig { + return SearchConfig{ + Scope: source.SquashedScope, + } +} + +func (c SearchConfig) WithScope(scope source.Scope) SearchConfig { + c.Scope = scope + return c +} diff --git a/syft/configuration_audit_trail.go b/syft/configuration_audit_trail.go new file mode 100644 index 00000000000..78f43fece61 --- /dev/null +++ b/syft/configuration_audit_trail.go @@ -0,0 +1,80 @@ +package syft + +import ( + "encoding/json" + "reflect" + + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/cataloging/filecataloging" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" +) + +// configurationAuditTrail is all input configuration was used to generate the SBOM +type configurationAuditTrail struct { + Search cataloging.SearchConfig `json:"search" yaml:"search" mapstructure:"search"` + Relationships cataloging.RelationshipsConfig `json:"relationships" yaml:"relationships" mapstructure:"relationships"` + DataGeneration cataloging.DataGenerationConfig `json:"data-generation" yaml:"data-generation" mapstructure:"data-generation"` + Packages pkgcataloging.Config `json:"packages" yaml:"packages" mapstructure:"packages"` + Files filecataloging.Config `json:"files" yaml:"files" mapstructure:"files"` + Catalogers catalogerManifest `json:"catalogers" yaml:"catalogers" mapstructure:"catalogers"` + ExtraConfigs any `json:"extra,omitempty" yaml:"extra" mapstructure:"extra"` +} + +type catalogerManifest struct { + Requested pkgcataloging.SelectionRequest `json:"requested" yaml:"requested" mapstructure:"requested"` + Used []string `json:"used" yaml:"used" mapstructure:"used"` +} + +type marshalAPIConfiguration configurationAuditTrail + +func (cfg configurationAuditTrail) MarshalJSON() ([]byte, error) { + // since the api configuration is placed into the SBOM in an empty interface, and we want a stable ordering of + // keys (not guided by the struct ordering) we need to convert the struct to a map. This is best done with + // simply marshalling and unmarshalling. Mapstructure is used to ensure we are honoring all json struct + // tags. Once we have a map, we can lean on the stable ordering of json map keys in the stdlib. This is an + // implementation detail that can be at least relied on until Go 2 (at which point it can change). + // This dance allows us to guarantee ordering of keys in the configuration section of the SBOM. + + initialJSON, err := json.Marshal(marshalAPIConfiguration(cfg)) + if err != nil { + return nil, err + } + + var dataMap map[string]interface{} + if err := json.Unmarshal(initialJSON, &dataMap); err != nil { + return nil, err + } + + if v, exists := dataMap["extra"]; exists && v == nil { + // remove the extra key if it renders as nil + delete(dataMap, "extra") + } + + return marshalSorted(dataMap) +} + +// marshalSorted recursively marshals a map with sorted keys +func marshalSorted(m interface{}) ([]byte, error) { + if reflect.TypeOf(m).Kind() != reflect.Map { + return json.Marshal(m) + } + + val := reflect.ValueOf(m) + sortedMap := make(map[string]interface{}) + + for _, key := range val.MapKeys() { + value := val.MapIndex(key).Interface() + + if value != nil && reflect.TypeOf(value).Kind() == reflect.Map { + sortedValue, err := marshalSorted(value) + if err != nil { + return nil, err + } + sortedMap[key.String()] = json.RawMessage(sortedValue) + } else { + sortedMap[key.String()] = value + } + } + + return json.Marshal(sortedMap) +} diff --git a/syft/configuration_audit_trail_test.go b/syft/configuration_audit_trail_test.go new file mode 100644 index 00000000000..56e9a20d78e --- /dev/null +++ b/syft/configuration_audit_trail_test.go @@ -0,0 +1,346 @@ +package syft + +import ( + "bytes" + "crypto" + "encoding/json" + "fmt" + "io" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/go-multierror" + "github.com/iancoleman/strcase" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/cataloging/filecataloging" + "github.com/anchore/syft/syft/file" +) + +func Test_configurationAuditTrail_StructTags(t *testing.T) { + // we need to ensure that the output for any configuration is well-formed and follows conventions. + // We ensure that: + // 1. all fields have a JSON tag + // 2. the tag value follows lowercase kebab-case style + + jsonTags := getJSONTags(t, configurationAuditTrail{}) + + for _, tag := range jsonTags { + assertLowercaseKebab(t, tag) + } + +} + +func getJSONTags(t *testing.T, v interface{}) []string { + var tags []string + err := collectJSONTags(t, reflect.ValueOf(v), &tags, "", "") + require.NoError(t, err) + return tags +} + +func collectJSONTags(t *testing.T, v reflect.Value, tags *[]string, parentTag string, path string) error { + var errs error + + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return errs + } + + tType := v.Type() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := tType.Field(i) + + curPath := path + "." + fieldType.Name + + // account for embeddings + if fieldType.Anonymous { + embeddedField := field + + if embeddedField.Kind() == reflect.Ptr { + // this can be enhanced in the future if the need arises... + errs = multierror.Append(errs, fmt.Errorf("field '%s' is a pointer to an embedded struct, this is not supported in the test helper", curPath)) + } + + if embeddedField.Kind() == reflect.Struct { + err := collectJSONTags(t, field, tags, parentTag, curPath) + if err != nil { + errs = multierror.Append(errs, err) + } + } + + continue + } + + var tag string + var ok bool + if fieldType.PkgPath == "" { + tag, ok = fieldType.Tag.Lookup("json") + if !ok || (tag == "" && parentTag == "") { + errs = multierror.Append(errs, fmt.Errorf("field '%s' does not have a json tag", curPath)) + return errs + } + if tag != "" && tag != "-" { + *tags = append(*tags, tag) + } + } + + if field.Kind() == reflect.Struct || (field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct) { + err := collectJSONTags(t, field, tags, tag, curPath) + if err != nil { + errs = multierror.Append(errs, err) + } + } + } + return errs +} + +func assertLowercaseKebab(t *testing.T, tag string) { + t.Helper() + require.NotEmpty(t, tag) + assert.Equal(t, strcase.ToKebab(tag), tag) +} + +func Test_collectJSONTags(t *testing.T) { + // though this is not used in production, this is a sensitive and complex enough of a check to warrant testing the test helper. + type good struct { + A string `json:"a"` + } + + type missing struct { + A string `json:"a"` + B string + } + + type exclude struct { + A string `json:"a"` + B string `json:"-"` + } + + type goodEmbedded struct { + good `json:""` + } + + type badEmbedded struct { + missing `json:""` + } + + // simply not covered and require further development to support + type goodPtrEmbedded struct { + *good `json:""` + } + + // simply not covered and require further development to support + type badPtrEmbedded struct { + *missing `json:""` + } + + tests := []struct { + name string + v interface{} + want []string + wantErr require.ErrorAssertionFunc + }{ + { + name: "good", + v: good{}, + want: []string{ + "a", + }, + }, + { + name: "missing", + v: missing{}, + wantErr: require.Error, + }, + { + name: "exclude", + v: exclude{}, + want: []string{ + "a", + }, + }, + { + name: "bad embedded", + v: badEmbedded{}, + wantErr: require.Error, + }, + { + name: "good embedded", + v: goodEmbedded{}, + want: []string{ + "a", + }, + }, + // these cases are simply not covered and require further development to support + { + name: "bad ptr embedded", + v: badPtrEmbedded{}, + wantErr: require.Error, + }, + { + name: "good ptr embedded", + v: goodPtrEmbedded{}, + want: []string{ + "a", + }, + wantErr: require.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = require.NoError + } + + var tags []string + + err := collectJSONTags(t, reflect.ValueOf(tt.v), &tags, "", "") + + tt.wantErr(t, err) + if err != nil { + return + } + + assert.Equal(t, tt.want, tags) + }) + } + +} + +func Test_configurationAuditTrail_MarshalJSON(t *testing.T) { + + tests := []struct { + name string + cfg configurationAuditTrail + assert func(t *testing.T, got []byte) + }{ + { + name: "ensure other marshallers are called", + cfg: configurationAuditTrail{ + + Files: filecataloging.Config{ + Selection: file.FilesOwnedByPackageSelection, + Hashers: []crypto.Hash{ + crypto.SHA256, + }, + }, + }, + // the custom file marshaller swaps ints for strings for hashers + assert: func(t *testing.T, got []byte) { + assert.Contains(t, string(got), `"hashers":["sha-256"]`) + }, + }, + { + name: "ensure maps are sorted", + cfg: configurationAuditTrail{}, + assert: func(t *testing.T, got []byte) { + assert.NoError(t, assertJSONKeysSorted(got)) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + got, err := tt.cfg.MarshalJSON() + require.NoError(t, err) + if tt.assert == nil { + t.Fatal("assert function must be provided") + } + tt.assert(t, got) + + }) + } +} + +// assertJSONKeysSorted asserts that all keys in JSON maps are sorted. +func assertJSONKeysSorted(jsonBytes []byte) error { + var errs error + decoder := json.NewDecoder(bytes.NewReader(jsonBytes)) + var keys []string + var inObject bool + + for { + token, err := decoder.Token() + if err != nil { + if err == io.EOF { + break + } + errs = multierror.Append(errs, fmt.Errorf("error decoding JSON: %w", err)) + } + + switch v := token.(type) { + case json.Delim: + switch v { + case '{': + inObject = true + keys = nil // Reset keys for a new object + case '}': + inObject = false + if !sort.StringsAreSorted(keys) { + errs = multierror.Append(errs, fmt.Errorf("Keys are not sorted: %v", keys)) + } + } + case string: + if inObject && v != "" { + keys = append(keys, v) + } + } + } + return errs +} + +func Test_assertJSONKeysSorted(t *testing.T) { + // this test function is sufficiently complicated enough to warrant its own test... + + sorted := []byte(`{"a":1,"b":2}`) + unsorted := []byte(`{"b":2,"a":1}`) + + nestedSorted := []byte(`{"a":1,"b":{"a":1,"b":2}}`) + nestedUnsorted := []byte(`{"a":1,"b":{"b":2,"a":1}}`) + + tests := []struct { + name string + json []byte + wantErr require.ErrorAssertionFunc + }{ + { + name: "sorted", + json: sorted, + wantErr: require.NoError, + }, + { + name: "unsorted", + json: unsorted, + wantErr: require.Error, + }, + { + name: "nested sorted", + json: nestedSorted, + wantErr: require.NoError, + }, + { + name: "nested unsorted", + json: nestedUnsorted, + wantErr: require.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = require.NoError + } + + err := assertJSONKeysSorted(tt.json) + tt.wantErr(t, err) + }) + + } +} diff --git a/syft/cpe/by_source_then_specificity.go b/syft/cpe/by_source_then_specificity.go new file mode 100644 index 00000000000..85948449ca6 --- /dev/null +++ b/syft/cpe/by_source_then_specificity.go @@ -0,0 +1,39 @@ +package cpe + +import "sort" + +type BySourceThenSpecificity []CPE + +func (b BySourceThenSpecificity) Len() int { + return len(b) +} + +func (b BySourceThenSpecificity) Less(i, j int) bool { + sourceOrder := map[Source]int{ + NVDDictionaryLookupSource: 1, + DeclaredSource: 2, + GeneratedSource: 3, + } + + getRank := func(source Source) int { + if rank, exists := sourceOrder[source]; exists { + return rank + } + return 4 // Sourced we don't know about can't be assigned special priority, so + // are considered ties. + } + iSource := b[i].Source + jSource := b[j].Source + rankI, rankJ := getRank(iSource), getRank(jSource) + if rankI != rankJ { + return rankI < rankJ + } + + return isMoreSpecific(b[i].Attributes, b[j].Attributes) +} + +func (b BySourceThenSpecificity) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +var _ sort.Interface = (*BySourceThenSpecificity)(nil) diff --git a/syft/cpe/by_source_then_specificity_test.go b/syft/cpe/by_source_then_specificity_test.go new file mode 100644 index 00000000000..9687447f9d8 --- /dev/null +++ b/syft/cpe/by_source_then_specificity_test.go @@ -0,0 +1,74 @@ +package cpe + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBySourceThenSpecificity(t *testing.T) { + tests := []struct { + name string + input []CPE + want []CPE + }{ + { + name: "empty case", + }, + { + name: "nvd before generated", + input: []CPE{ + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", GeneratedSource), + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + }, + want: []CPE{ + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", GeneratedSource), + }, + }, + { + name: "declared before generated", + input: []CPE{ + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", GeneratedSource), + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", DeclaredSource), + }, + want: []CPE{ + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", DeclaredSource), + Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*", GeneratedSource), + }, + }, + { + name: "most specific attributes of equal sources", + input: []CPE{ + Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + Must("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*", NVDDictionaryLookupSource), + }, + want: []CPE{ + Must("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*", NVDDictionaryLookupSource), + Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + }, + }, + { + name: "most specific attributes of unknown sources", + input: []CPE{ + Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*", ""), + Must("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*", "some-other-unknown-source"), + Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", "some-unknown-source"), + }, + want: []CPE{ + Must("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*", "some-other-unknown-source"), + Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*", ""), + Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", "some-unknown-source"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sort.Sort(BySourceThenSpecificity(tt.input)) + assert.Equal(t, tt.want, tt.input) + }) + } +} diff --git a/syft/cpe/by_specificity.go b/syft/cpe/by_specificity.go index ad1a9adb8e5..016d94ec859 100644 --- a/syft/cpe/by_specificity.go +++ b/syft/cpe/by_specificity.go @@ -2,21 +2,26 @@ package cpe import ( "sort" - - "github.com/facebookincubator/nvdtools/wfn" ) var _ sort.Interface = (*BySpecificity)(nil) -type BySpecificity []wfn.Attributes +type BySpecificity []Attributes func (c BySpecificity) Len() int { return len(c) } func (c BySpecificity) Swap(i, j int) { c[i], c[j] = c[j], c[i] } func (c BySpecificity) Less(i, j int) bool { - iScore := weightedCountForSpecifiedFields(c[i]) - jScore := weightedCountForSpecifiedFields(c[j]) + return isMoreSpecific(c[i], c[j]) +} + +// Returns true if i is more specific than j, with some +// tie breaking mechanisms to make sorting equally-specific cpe Attributes +// deterministic. +func isMoreSpecific(i, j Attributes) bool { + iScore := weightedCountForSpecifiedFields(i) + jScore := weightedCountForSpecifiedFields(j) // check weighted sort first if iScore != jScore { @@ -24,28 +29,28 @@ func (c BySpecificity) Less(i, j int) bool { } // sort longer fields to top - if countFieldLength(c[i]) != countFieldLength(c[j]) { - return countFieldLength(c[i]) > countFieldLength(c[j]) + if countFieldLength(i) != countFieldLength(j) { + return countFieldLength(i) > countFieldLength(j) } // if score and length are equal then text sort // note that we are not using String from the syft pkg - // as we are not encoding/decoding this CPE string so we don't - // need the proper quoted version of the CPE. - return c[i].BindToFmtString() < c[j].BindToFmtString() + // as we are not encoding/decoding this Attributes string so we don't + // need the proper quoted version of the Attributes. + return i.BindToFmtString() < j.BindToFmtString() } -func countFieldLength(cpe wfn.Attributes) int { +func countFieldLength(cpe Attributes) int { return len(cpe.Part + cpe.Vendor + cpe.Product + cpe.Version + cpe.TargetSW) } -func weightedCountForSpecifiedFields(cpe wfn.Attributes) int { - checksForSpecifiedField := []func(cpe wfn.Attributes) (bool, int){ - func(cpe wfn.Attributes) (bool, int) { return cpe.Part != "", 2 }, - func(cpe wfn.Attributes) (bool, int) { return cpe.Vendor != "", 3 }, - func(cpe wfn.Attributes) (bool, int) { return cpe.Product != "", 4 }, - func(cpe wfn.Attributes) (bool, int) { return cpe.Version != "", 1 }, - func(cpe wfn.Attributes) (bool, int) { return cpe.TargetSW != "", 1 }, +func weightedCountForSpecifiedFields(cpe Attributes) int { + checksForSpecifiedField := []func(cpe Attributes) (bool, int){ + func(cpe Attributes) (bool, int) { return cpe.Part != "", 2 }, + func(cpe Attributes) (bool, int) { return cpe.Vendor != "", 3 }, + func(cpe Attributes) (bool, int) { return cpe.Product != "", 4 }, + func(cpe Attributes) (bool, int) { return cpe.Version != "", 1 }, + func(cpe Attributes) (bool, int) { return cpe.TargetSW != "", 1 }, } weightedCount := 0 diff --git a/syft/cpe/by_specificity_test.go b/syft/cpe/by_specificity_test.go index 33c0d33368c..24df8db71b7 100644 --- a/syft/cpe/by_specificity_test.go +++ b/syft/cpe/by_specificity_test.go @@ -10,81 +10,81 @@ import ( func Test_BySpecificity(t *testing.T) { tests := []struct { name string - input []CPE - expected []CPE + input []Attributes + expected []Attributes }{ { name: "sort strictly by wfn *", - input: []CPE{ - Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:*:package:1:*:*:*:*:some:*:*"), - Must("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*"), - Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"), + input: []Attributes{ + MustAttributes("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:*:package:1:*:*:*:*:some:*:*"), + MustAttributes("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*"), + MustAttributes("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"), }, - expected: []CPE{ - Must("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*"), - Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:*:package:1:*:*:*:*:some:*:*"), - Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*"), + expected: []Attributes{ + MustAttributes("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*"), + MustAttributes("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:*:package:1:*:*:*:*:some:*:*"), + MustAttributes("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*"), }, }, { name: "sort strictly by field length", - input: []CPE{ - Must("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:1:333:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:666666:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:1:1:*:*:*:*:4444:*:*"), + input: []Attributes{ + MustAttributes("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:1:333:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:666666:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:1:1:*:*:*:*:4444:*:*"), }, - expected: []CPE{ - Must("cpe:2.3:a:1:666666:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:1:1:*:*:*:*:4444:*:*"), - Must("cpe:2.3:a:1:1:333:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), + expected: []Attributes{ + MustAttributes("cpe:2.3:a:1:666666:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:1:1:*:*:*:*:4444:*:*"), + MustAttributes("cpe:2.3:a:1:1:333:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), }, }, { name: "sort by mix of field length and specificity", - input: []CPE{ - Must("cpe:2.3:a:1:666666:*:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:*:1:1:*:*:*:*:4444:*:*"), - Must("cpe:2.3:a:1:*:333:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), + input: []Attributes{ + MustAttributes("cpe:2.3:a:1:666666:*:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:*:1:1:*:*:*:*:4444:*:*"), + MustAttributes("cpe:2.3:a:1:*:333:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), }, - expected: []CPE{ - Must("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:1:666666:*:*:*:*:*:1:*:*"), - Must("cpe:2.3:a:*:1:1:*:*:*:*:4444:*:*"), - Must("cpe:2.3:a:1:*:333:*:*:*:*:*:*:*"), + expected: []Attributes{ + MustAttributes("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:1:666666:*:*:*:*:*:1:*:*"), + MustAttributes("cpe:2.3:a:*:1:1:*:*:*:*:4444:*:*"), + MustAttributes("cpe:2.3:a:1:*:333:*:*:*:*:*:*:*"), }, }, { name: "sort by mix of field length, specificity, dash", - input: []CPE{ - Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine_keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine-keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine-keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine_keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), + input: []Attributes{ + MustAttributes("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine_keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine-keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine-keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine_keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), }, - expected: []CPE{ - Must("cpe:2.3:a:alpine-keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine-keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine_keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine_keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), + expected: []Attributes{ + MustAttributes("cpe:2.3:a:alpine-keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine-keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine_keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine_keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine:alpine-keys:2.3-r1:*:*:*:*:*:*:*"), + MustAttributes("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*"), }, }, } diff --git a/syft/cpe/cpe.go b/syft/cpe/cpe.go index 410b802d71e..fe678977d9c 100644 --- a/syft/cpe/cpe.go +++ b/syft/cpe/cpe.go @@ -8,7 +8,57 @@ import ( "github.com/facebookincubator/nvdtools/wfn" ) -type CPE = wfn.Attributes +// CPE contains the attributes of an NVD Attributes and a string +// describing where Syft got the Attributes, e.g. generated by heuristics +// vs looked up in the NVD Attributes dictionary +type CPE struct { + Attributes Attributes + Source Source +} + +type Source string + +func (c Source) String() string { + return string(c) +} + +const ( + GeneratedSource Source = "syft-generated" + NVDDictionaryLookupSource Source = "nvd-cpe-dictionary" + DeclaredSource Source = "declared" +) + +const Any = "" + +type Attributes struct { + Part string + Vendor string + Product string + Version string + Update string + Edition string + SWEdition string + TargetSW string + TargetHW string + Other string + Language string +} + +func (c Attributes) asAttributes() wfn.Attributes { + return wfn.Attributes(c) +} + +func fromAttributes(a wfn.Attributes) Attributes { + return Attributes(a) +} + +func (c Attributes) BindToFmtString() string { + return c.asAttributes().BindToFmtString() +} + +func NewWithAny() Attributes { + return fromAttributes(*(wfn.NewAttributesWithAny())) +} const ( allowedCPEPunctuation = "-!\"#$%&'()+,./:;<=>@[]^`{|}~" @@ -16,36 +66,55 @@ const ( // This regex string is taken from // https://csrc.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd which has the official cpe spec -// This first part matches CPE urls and the second part matches binding strings +// This first part matches Attributes urls and the second part matches binding strings const cpeRegexString = ((`^([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})`) + - // Or match the CPE binding string + // Or match the Attributes binding string // Note that we had to replace '`' with '\x60' to escape the backticks `|(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&'\(\)\+,/:;<=>@\[\]\^\x60\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&'\(\)\+,/:;<=>@\[\]\^\x60\{\|}~]))+(\?*|\*?))|[\*\-])){4})$`) var cpeRegex = regexp.MustCompile(cpeRegexString) -// New will parse a formatted CPE string and return a CPE object. Some input, such as the existence of whitespace +func New(value string, source Source) (CPE, error) { + attributes, err := NewAttributes(value) + if err != nil { + return CPE{}, err + } + return CPE{ + Attributes: attributes, + Source: source, + }, nil +} + +// NewAttributes will parse a formatted Attributes string and return a Attributes object. Some input, such as the existence of whitespace // characters is allowed, however, a more strict validation is done after this sanitization process. -func New(cpeStr string) (CPE, error) { - // get a CPE object based on the given string --don't validate yet since it may be possible to escape select cases on the callers behalf +func NewAttributes(cpeStr string) (Attributes, error) { + // get a Attributes object based on the given string --don't validate yet since it may be possible to escape select cases on the callers behalf c, err := newWithoutValidation(cpeStr) if err != nil { - return CPE{}, fmt.Errorf("unable to parse CPE string: %w", err) + return Attributes{}, fmt.Errorf("unable to parse Attributes string: %w", err) } - // ensure that this CPE can be validated after being fully sanitized - if ValidateString(String(c)) != nil { - return CPE{}, err + // ensure that this Attributes can be validated after being fully sanitized + if ValidateString(c.String()) != nil { + return Attributes{}, err } - // we don't return the sanitized string, as this is a concern for later when creating CPE strings. In fact, since + // we don't return the sanitized string, as this is a concern for later when creating Attributes strings. In fact, since // sanitization is lossy (whitespace is replaced, not escaped) it's important that the raw values are left as. return c, nil } // Must returns a CPE or panics if the provided string is not valid -func Must(cpeStr string) CPE { - c, err := New(cpeStr) +func Must(cpeStr string, source Source) CPE { + c := MustAttributes(cpeStr) + return CPE{ + Attributes: c, + Source: source, + } +} + +func MustAttributes(cpeStr string) Attributes { + c, err := NewAttributes(cpeStr) if err != nil { panic(err) } @@ -53,47 +122,49 @@ func Must(cpeStr string) CPE { } func ValidateString(cpeStr string) error { - // We should filter out all CPEs that do not match the official CPE regex - // The facebook nvdtools parser can sometimes incorrectly parse invalid CPE strings + // We should filter out all CPEs that do not match the official Attributes regex + // The facebook nvdtools parser can sometimes incorrectly parse invalid Attributes strings if !cpeRegex.MatchString(cpeStr) { - return fmt.Errorf("failed to parse CPE=%q as it doesn't match the regex=%s", cpeStr, cpeRegexString) + return fmt.Errorf("failed to parse Attributes=%q as it doesn't match the regex=%s", cpeStr, cpeRegexString) } return nil } -func newWithoutValidation(cpeStr string) (CPE, error) { +func newWithoutValidation(cpeStr string) (Attributes, error) { value, err := wfn.Parse(cpeStr) if err != nil { - return CPE{}, fmt.Errorf("failed to parse CPE=%q: %w", cpeStr, err) + return Attributes{}, fmt.Errorf("failed to parse Attributes=%q: %w", cpeStr, err) } if value == nil { - return CPE{}, fmt.Errorf("failed to parse CPE=%q", cpeStr) + return Attributes{}, fmt.Errorf("failed to parse Attributes=%q", cpeStr) } - // we need to compare the raw data since we are constructing CPEs in other locations - value.Vendor = normalizeField(value.Vendor) - value.Product = normalizeField(value.Product) - value.Language = normalizeField(value.Language) - value.Version = normalizeField(value.Version) - value.TargetSW = normalizeField(value.TargetSW) - value.Part = normalizeField(value.Part) - value.Edition = normalizeField(value.Edition) - value.Other = normalizeField(value.Other) - value.SWEdition = normalizeField(value.SWEdition) - value.TargetHW = normalizeField(value.TargetHW) - value.Update = normalizeField(value.Update) + syftCPE := fromAttributes(*value) - return *value, nil + // we need to compare the raw data since we are constructing CPEs in other locations + syftCPE.Vendor = normalizeField(syftCPE.Vendor) + syftCPE.Product = normalizeField(syftCPE.Product) + syftCPE.Language = normalizeField(syftCPE.Language) + syftCPE.Version = normalizeField(syftCPE.Version) + syftCPE.TargetSW = normalizeField(syftCPE.TargetSW) + syftCPE.Part = normalizeField(syftCPE.Part) + syftCPE.Edition = normalizeField(syftCPE.Edition) + syftCPE.Other = normalizeField(syftCPE.Other) + syftCPE.SWEdition = normalizeField(syftCPE.SWEdition) + syftCPE.TargetHW = normalizeField(syftCPE.TargetHW) + syftCPE.Update = normalizeField(syftCPE.Update) + + return syftCPE, nil } func normalizeField(field string) string { - // replace spaces with underscores (per section 5.3.2 of the CPE spec v 2.3) + // replace spaces with underscores (per section 5.3.2 of the Attributes spec v 2.3) field = strings.ReplaceAll(field, " ", "_") // keep dashes and forward slashes unescaped if field == "*" { - return wfn.Any + return Any } return stripSlashes(field) } @@ -112,8 +183,8 @@ func stripSlashes(s string) string { return sb.String() } -func String(c CPE) string { - output := CPE{} +func (c Attributes) String() string { + output := Attributes{} output.Vendor = sanitize(c.Vendor) output.Product = sanitize(c.Product) output.Language = sanitize(c.Language) diff --git a/syft/cpe/cpe_test.go b/syft/cpe/cpe_test.go index 9af8f210542..4b3f60ae0ab 100644 --- a/syft/cpe/cpe_test.go +++ b/syft/cpe/cpe_test.go @@ -7,42 +7,43 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func Test_New(t *testing.T) { +func Test_NewAttributes(t *testing.T) { tests := []struct { name string input string - expected CPE + expected Attributes }{ { name: "gocase", input: `cpe:/a:10web:form_maker:1.0.0::~~~wordpress~~`, - expected: Must(`cpe:2.3:a:10web:form_maker:1.0.0:*:*:*:*:wordpress:*:*`), + expected: MustAttributes(`cpe:2.3:a:10web:form_maker:1.0.0:*:*:*:*:wordpress:*:*`), }, { name: "dashes", input: `cpe:/a:7-zip:7-zip:4.56:beta:~~~windows~~`, - expected: Must(`cpe:2.3:a:7-zip:7-zip:4.56:beta:*:*:*:windows:*:*`), + expected: MustAttributes(`cpe:2.3:a:7-zip:7-zip:4.56:beta:*:*:*:windows:*:*`), }, { name: "URL escape characters", input: `cpe:/a:%240.99_kindle_books_project:%240.99_kindle_books:6::~~~android~~`, - expected: Must(`cpe:2.3:a:\$0.99_kindle_books_project:\$0.99_kindle_books:6:*:*:*:*:android:*:*`), + expected: MustAttributes(`cpe:2.3:a:\$0.99_kindle_books_project:\$0.99_kindle_books:6:*:*:*:*:android:*:*`), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual, err := New(test.input) + actual, err := NewAttributes(test.input) if err != nil { - t.Fatalf("got an error while creating CPE: %+v", err) + t.Fatalf("got an error while creating Attributes: %+v", err) } - if String(actual) != String(test.expected) { - t.Errorf("mismatched entries:\n\texpected:%+v\n\t actual:%+v\n", String(test.expected), String(actual)) + if d := cmp.Diff(actual, test.expected); d != "" { + t.Errorf("Attributes mismatch (-want +got):\n%s", d) } }) @@ -81,9 +82,9 @@ func Test_normalizeCpeField(t *testing.T) { func Test_CPEParser(t *testing.T) { var testCases []struct { - CPEString string `json:"cpe-string"` - CPEUrl string `json:"cpe-url"` - WFN CPE `json:"wfn"` + CPEString string `json:"cpe-string"` + CPEUrl string `json:"cpe-url"` + WFN Attributes `json:"wfn"` } out, err := os.ReadFile("test-fixtures/cpe-data.json") require.NoError(t, err) @@ -91,14 +92,14 @@ func Test_CPEParser(t *testing.T) { for _, test := range testCases { t.Run(test.CPEString, func(t *testing.T) { - c1, err := New(test.CPEString) + c1, err := NewAttributes(test.CPEString) assert.NoError(t, err) - c2, err := New(test.CPEUrl) + c2, err := NewAttributes(test.CPEUrl) assert.NoError(t, err) assert.Equal(t, c1, c2) assert.Equal(t, c1, test.WFN) assert.Equal(t, c2, test.WFN) - assert.Equal(t, String(test.WFN), test.CPEString) + assert.Equal(t, test.WFN.String(), test.CPEString) }) } } @@ -160,16 +161,16 @@ func Test_InvalidCPE(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - c, err := New(test.in) + c, err := NewAttributes(test.in) if test.expectedErr { assert.Error(t, err) if t.Failed() { - t.Logf("got CPE: %q details: %+v", String(c), c) + t.Logf("got Attributes: %q details: %+v", c, c) } return } require.NoError(t, err) - assert.Equal(t, test.expected, String(c)) + assert.Equal(t, test.expected, c.String()) }) } } @@ -178,12 +179,12 @@ func Test_RoundTrip(t *testing.T) { tests := []struct { name string cpe string - parsedCPE CPE + parsedCPE Attributes }{ { name: "normal", cpe: "cpe:2.3:a:some-vendor:name:3.2:*:*:*:*:*:*:*", - parsedCPE: CPE{ + parsedCPE: Attributes{ Part: "a", Vendor: "some-vendor", Product: "name", @@ -193,7 +194,7 @@ func Test_RoundTrip(t *testing.T) { { name: "escaped colon", cpe: "cpe:2.3:a:some-vendor:name:1\\:3.2:*:*:*:*:*:*:*", - parsedCPE: CPE{ + parsedCPE: Attributes{ Part: "a", Vendor: "some-vendor", Product: "name", @@ -203,7 +204,7 @@ func Test_RoundTrip(t *testing.T) { { name: "escaped forward slash", cpe: "cpe:2.3:a:test\\/some-vendor:name:3.2:*:*:*:*:*:*:*", - parsedCPE: CPE{ + parsedCPE: Attributes{ Part: "a", Vendor: "test/some-vendor", Product: "name", @@ -214,14 +215,14 @@ func Test_RoundTrip(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - // CPE string must be preserved through a round trip - assert.Equal(t, test.cpe, String(Must(test.cpe))) - // The parsed CPE must be the same after a round trip - assert.Equal(t, Must(test.cpe), Must(String(Must(test.cpe)))) - // The test case parsed CPE must be the same after parsing the input string - assert.Equal(t, test.parsedCPE, Must(test.cpe)) - // The test case parsed CPE must produce the same string as the input cpe - assert.Equal(t, String(test.parsedCPE), test.cpe) + // Attributes string must be preserved through a round trip + assert.Equal(t, test.cpe, MustAttributes(test.cpe).String()) + // The parsed Attributes must be the same after a round trip + assert.Equal(t, MustAttributes(test.cpe), MustAttributes(MustAttributes(test.cpe).String())) + // The test case parsed Attributes must be the same after parsing the input string + assert.Equal(t, test.parsedCPE, MustAttributes(test.cpe)) + // The test case parsed Attributes must produce the same string as the input cpe + assert.Equal(t, test.parsedCPE.String(), test.cpe) }) } } diff --git a/syft/cpe/merge_cpes.go b/syft/cpe/merge_cpes.go index 5c5c7daf276..951869145ab 100644 --- a/syft/cpe/merge_cpes.go +++ b/syft/cpe/merge_cpes.go @@ -1,25 +1,27 @@ package cpe import ( + "fmt" "sort" ) -func Merge(a, b []CPE) (result []CPE) { - aCPEs := make(map[string]CPE) - - // keep all CPEs from a and create a quick string-based lookup - for _, aCPE := range a { - aCPEs[aCPE.BindToFmtString()] = aCPE - result = append(result, aCPE) +// Merge returns unique SourcedCPEs that are found in A or B +// Two SourcedCPEs are identical if their source and normalized string are identical +func Merge(a, b []CPE) []CPE { + var result []CPE + dedupe := make(map[string]CPE) + key := func(scpe CPE) string { + return fmt.Sprintf("%s:%s", scpe.Source.String(), scpe.Attributes.BindToFmtString()) } - - // keep all unique CPEs from b - for _, bCPE := range b { - if _, exists := aCPEs[bCPE.BindToFmtString()]; !exists { - result = append(result, bCPE) - } + for _, s := range a { + dedupe[key(s)] = s } - - sort.Sort(BySpecificity(result)) + for _, s := range b { + dedupe[key(s)] = s + } + for _, val := range dedupe { + result = append(result, val) + } + sort.Sort(BySourceThenSpecificity(result)) return result } diff --git a/syft/cpe/merge_cpes_test.go b/syft/cpe/merge_cpes_test.go index 80ef32e7ab0..747affcf4f9 100644 --- a/syft/cpe/merge_cpes_test.go +++ b/syft/cpe/merge_cpes_test.go @@ -16,18 +16,20 @@ func Test_Merge(t *testing.T) { name: "merge, removing duplicates and ordered", input: [][]CPE{ { - Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"), + Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*", DeclaredSource), + Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", GeneratedSource), }, { - Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"), + Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*", DeclaredSource), + Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", GeneratedSource), }, }, expected: []CPE{ - Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"), - Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*"), + Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*", NVDDictionaryLookupSource), + Must("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*", DeclaredSource), + Must("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*", DeclaredSource), + Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", GeneratedSource), }, }, } diff --git a/syft/create_sbom.go b/syft/create_sbom.go new file mode 100644 index 00000000000..91eaf8d0399 --- /dev/null +++ b/syft/create_sbom.go @@ -0,0 +1,142 @@ +package syft + +import ( + "context" + "fmt" + "sort" + "time" + + "github.com/dustin/go-humanize" + "github.com/scylladb/go-set/strset" + "github.com/wagoodman/go-progress" + + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/sbomsync" + "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/event/monitor" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +// CreateSBOM creates a software bill-of-materials from the given source. If the CreateSBOMConfig is nil, then +// default options will be used. +func CreateSBOM(ctx context.Context, src source.Source, cfg *CreateSBOMConfig) (*sbom.SBOM, error) { + if cfg == nil { + cfg = DefaultCreateSBOMConfig() + } + if err := cfg.validate(); err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + + srcMetadata := src.Describe() + + taskGroups, audit, err := cfg.makeTaskGroups(srcMetadata) + if err != nil { + return nil, err + } + + resolver, err := src.FileResolver(cfg.Search.Scope) + if err != nil { + return nil, fmt.Errorf("unable to get file resolver: %w", err) + } + + s := sbom.SBOM{ + Source: srcMetadata, + Descriptor: sbom.Descriptor{ + Name: cfg.ToolName, + Version: cfg.ToolVersion, + Configuration: configurationAuditTrail{ + Search: cfg.Search, + Relationships: cfg.Relationships, + DataGeneration: cfg.DataGeneration, + Packages: cfg.Packages, + Files: cfg.Files, + Catalogers: *audit, + ExtraConfigs: cfg.ToolConfiguration, + }, + }, + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(), + }, + } + + catalogingProgress := monitorCatalogingTask(src.ID(), taskGroups) + packageCatalogingProgress := monitorPackageCatalogingTask(s.Artifacts.Packages) + + builder := sbomsync.NewBuilder(&s) + for i := range taskGroups { + err := task.NewTaskExecutor(taskGroups[i], cfg.Parallelism).Execute(ctx, resolver, builder, catalogingProgress) + if err != nil { + // TODO: tie this to the open progress monitors... + return nil, fmt.Errorf("failed to run tasks: %w", err) + } + } + + packageCatalogingProgress.SetCompleted() + catalogingProgress.SetCompleted() + + return &s, nil +} + +func monitorPackageCatalogingTask(pkgs *pkg.Collection) *monitor.CatalogerTaskProgress { + info := monitor.GenericTask{ + Title: monitor.Title{ + Default: "Packages", + }, + ID: monitor.PackageCatalogingTaskID, + HideOnSuccess: false, + ParentID: monitor.TopLevelCatalogingTaskID, + } + + prog := bus.StartCatalogerTask(info, -1, "") + + go func() { + ticker := time.NewTicker(200 * time.Millisecond) + defer ticker.Stop() + + for { + <-ticker.C + + count := humanize.Comma(int64(pkgs.PackageCount())) + prog.AtomicStage.Set(fmt.Sprintf("%s packages", count)) + + if progress.IsCompleted(prog) { + break + } + } + }() + + return prog +} + +func monitorCatalogingTask(srcID artifact.ID, tasks [][]task.Task) *monitor.CatalogerTaskProgress { + info := monitor.GenericTask{ + Title: monitor.Title{ + Default: "Catalog contents", + WhileRunning: "Cataloging contents", + OnSuccess: "Cataloged contents", + }, + ID: monitor.TopLevelCatalogingTaskID, + Context: string(srcID), + HideOnSuccess: false, + } + + var length int64 + for _, tg := range tasks { + length += int64(len(tg)) + } + + return bus.StartCatalogerTask(info, length, "") +} + +func formatTaskNames(tasks []task.Task) []string { + set := strset.New() + for _, td := range tasks { + set.Add(td.Name()) + } + list := set.List() + sort.Strings(list) + return list +} diff --git a/syft/create_sbom_config.go b/syft/create_sbom_config.go new file mode 100644 index 00000000000..8acfc63f61a --- /dev/null +++ b/syft/create_sbom_config.go @@ -0,0 +1,364 @@ +package syft + +import ( + "context" + "errors" + "fmt" + "runtime/debug" + "strings" + + "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/cataloging/filecataloging" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +// CreateSBOMConfig specifies all parameters needed for creating an SBOM. +type CreateSBOMConfig struct { + // required configuration input to specify how cataloging should be performed + Search cataloging.SearchConfig + Relationships cataloging.RelationshipsConfig + DataGeneration cataloging.DataGenerationConfig + Packages pkgcataloging.Config + Files filecataloging.Config + Parallelism int + CatalogerSelection pkgcataloging.SelectionRequest + + // audit what tool is being used to generate the SBOM + ToolName string + ToolVersion string + ToolConfiguration interface{} + + packageTaskFactories task.PackageTaskFactories + packageCatalogerReferences []pkgcataloging.CatalogerReference +} + +func DefaultCreateSBOMConfig() *CreateSBOMConfig { + return &CreateSBOMConfig{ + Search: cataloging.DefaultSearchConfig(), + Relationships: cataloging.DefaultRelationshipsConfig(), + DataGeneration: cataloging.DefaultDataGenerationConfig(), + Packages: pkgcataloging.DefaultConfig(), + Files: filecataloging.DefaultConfig(), + Parallelism: 1, + packageTaskFactories: task.DefaultPackageTaskFactories(), + + // library consumers are free to override the tool values to fit their needs, however, we have some sane defaults + // to ensure that SBOMs generated don't have missing tool metadata. + ToolName: "syft", + ToolVersion: syftVersion(), + } +} + +func syftVersion() string { + // extract the syft version from the go module info from the current binary that is running. This is useful for + // library consumers to at least encode the version of syft that was used to generate the SBOM. Note: we don't + // use the version info from main because it's baked in with ldflags, which we don't control for library consumers. + // This approach won't work in all cases though, such as when the binary is stripped of the buildinfo section. + + buildInfo, ok := debug.ReadBuildInfo() + if !ok { + return "" + } + + for _, d := range buildInfo.Deps { + if d.Path == "github.com/anchore/syft" && d.Version != "(devel)" { + return d.Version + } + } + + return "" +} + +// WithTool allows for setting the specific name, version, and any additional configuration that is not captured +// in the syft default API configuration. This could cover inputs for catalogers that were user-provided, thus, +// is not visible to the syft API, but would be useful to see in the SBOM output. +func (c *CreateSBOMConfig) WithTool(name, version string, cfg ...any) *CreateSBOMConfig { + c.ToolName = name + c.ToolVersion = version + c.ToolConfiguration = cfg + return c +} + +// WithParallelism allows for setting the number of concurrent cataloging tasks that can be performed at once +func (c *CreateSBOMConfig) WithParallelism(p int) *CreateSBOMConfig { + if p < 1 { + // TODO: warn? + p = 1 + } + c.Parallelism = p + return c +} + +// WithSearchConfig allows for setting the specific search configuration for cataloging. +func (c *CreateSBOMConfig) WithSearchConfig(cfg cataloging.SearchConfig) *CreateSBOMConfig { + c.Search = cfg + return c +} + +// WithRelationshipsConfig allows for defining the specific relationships that should be captured during cataloging. +func (c *CreateSBOMConfig) WithRelationshipsConfig(cfg cataloging.RelationshipsConfig) *CreateSBOMConfig { + c.Relationships = cfg + return c +} + +// WithDataGenerationConfig allows for defining what data elements that cannot be discovered from the underlying +// target being scanned that should be generated after package creation. +func (c *CreateSBOMConfig) WithDataGenerationConfig(cfg cataloging.DataGenerationConfig) *CreateSBOMConfig { + c.DataGeneration = cfg + return c +} + +// WithPackagesConfig allows for defining any specific behavior for syft-implemented catalogers. +func (c *CreateSBOMConfig) WithPackagesConfig(cfg pkgcataloging.Config) *CreateSBOMConfig { + c.Packages = cfg + return c +} + +// WithFilesConfig allows for defining file-based cataloging parameters. +func (c *CreateSBOMConfig) WithFilesConfig(cfg filecataloging.Config) *CreateSBOMConfig { + c.Files = cfg + return c +} + +// WithoutFiles allows for disabling file cataloging altogether. +func (c *CreateSBOMConfig) WithoutFiles() *CreateSBOMConfig { + c.Files = filecataloging.Config{ + Selection: file.NoFilesSelection, + Hashers: nil, + } + return c +} + +// WithCatalogerSelection allows for adding to, removing from, or sub-selecting the final set of catalogers by name or tag. +func (c *CreateSBOMConfig) WithCatalogerSelection(selection pkgcataloging.SelectionRequest) *CreateSBOMConfig { + c.CatalogerSelection = selection + return c +} + +// WithoutCatalogers removes all catalogers from the final set of catalogers. This is useful if you want to only use +// user-provided catalogers (without the default syft-provided catalogers). +func (c *CreateSBOMConfig) WithoutCatalogers() *CreateSBOMConfig { + c.packageTaskFactories = nil + c.packageCatalogerReferences = nil + return c +} + +// WithCatalogers allows for adding user-provided catalogers to the final set of catalogers that will always be run +// regardless of the source type or any cataloger selections provided. +func (c *CreateSBOMConfig) WithCatalogers(catalogerRefs ...pkgcataloging.CatalogerReference) *CreateSBOMConfig { + c.packageCatalogerReferences = append(c.packageCatalogerReferences, catalogerRefs...) + + return c +} + +// makeTaskGroups considers the entire configuration and finalizes the set of tasks to be run. Tasks are run in +// groups, where each task in a group can be run concurrently, while tasks in different groups must be run serially. +// The final set of task groups is returned along with a cataloger manifest that describes the catalogers that were +// selected and the tokens that were sensitive to this selection (both for adding and removing from the final set). +func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task, *catalogerManifest, error) { + var taskGroups [][]task.Task + + // generate package and file tasks based on the configuration + environmentTasks := c.environmentTasks() + relationshipsTasks := c.relationshipTasks(src) + fileTasks := c.fileTasks() + pkgTasks, selectionEvidence, err := c.packageTasks(src) + if err != nil { + return nil, nil, err + } + + // combine the user-provided and configured tasks + if c.Files.Selection == file.FilesOwnedByPackageSelection { + // special case: we need the package info when we are cataloging files owned by packages + taskGroups = append(taskGroups, pkgTasks, fileTasks) + } else { + taskGroups = append(taskGroups, append(pkgTasks, fileTasks...)) + } + + // all relationship work must be done after all nodes (files and packages) have been cataloged + if len(relationshipsTasks) > 0 { + taskGroups = append(taskGroups, relationshipsTasks) + } + + // identifying the environment (i.e. the linux release) must be done first as this is required for package cataloging + taskGroups = append( + [][]task.Task{ + environmentTasks, + }, + taskGroups..., + ) + + return taskGroups, &catalogerManifest{ + Requested: selectionEvidence.Request, + Used: formatTaskNames(pkgTasks), + }, nil +} + +// fileTasks returns the set of tasks that should be run to catalog files. +func (c *CreateSBOMConfig) fileTasks() []task.Task { + var tsks []task.Task + + if t := task.NewFileDigestCatalogerTask(c.Files.Selection, c.Files.Hashers...); t != nil { + tsks = append(tsks, t) + } + if t := task.NewFileMetadataCatalogerTask(c.Files.Selection); t != nil { + tsks = append(tsks, t) + } + if t := task.NewFileContentCatalogerTask(c.Files.Content); t != nil { + tsks = append(tsks, t) + } + if t := task.NewExecutableCatalogerTask(c.Files.Selection, c.Files.Executable); t != nil { + tsks = append(tsks, t) + } + + return tsks +} + +// packageTasks returns the set of tasks that should be run to catalog packages. +func (c *CreateSBOMConfig) packageTasks(src source.Description) ([]task.Task, *task.Selection, error) { + cfg := task.CatalogingFactoryConfig{ + SearchConfig: c.Search, + RelationshipsConfig: c.Relationships, + DataGenerationConfig: c.DataGeneration, + PackagesConfig: c.Packages, + } + + persistentTasks, selectableTasks, err := c.allPackageTasks(cfg) + if err != nil { + return nil, nil, fmt.Errorf("unable to create package cataloger tasks: %w", err) + } + + req, err := finalSelectionRequest(c.CatalogerSelection, src) + if err != nil { + return nil, nil, err + } + + finalTasks, selection, err := task.Select(selectableTasks, *req) + if err != nil { + return nil, nil, err + } + + finalTasks = append(finalTasks, persistentTasks...) + + if len(finalTasks) == 0 { + return nil, nil, fmt.Errorf("no catalogers selected") + } + + return finalTasks, &selection, nil +} + +func finalSelectionRequest(req pkgcataloging.SelectionRequest, src source.Description) (*pkgcataloging.SelectionRequest, error) { + if len(req.DefaultNamesOrTags) == 0 { + defaultTag, err := findDefaultTag(src) + if err != nil { + return nil, fmt.Errorf("unable to determine default cataloger tag: %w", err) + } + + if defaultTag != "" { + req.DefaultNamesOrTags = append(req.DefaultNamesOrTags, defaultTag) + } + + req.RemoveNamesOrTags = replaceDefaultTagReferences(defaultTag, req.RemoveNamesOrTags) + req.SubSelectTags = replaceDefaultTagReferences(defaultTag, req.SubSelectTags) + } + + return &req, nil +} + +func (c *CreateSBOMConfig) allPackageTasks(cfg task.CatalogingFactoryConfig) ([]task.Task, []task.Task, error) { + persistentPackageTasks, selectablePackageTasks, err := c.userPackageTasks(cfg) + if err != nil { + return nil, nil, err + } + + tsks, err := c.packageTaskFactories.Tasks(cfg) + if err != nil { + return nil, nil, fmt.Errorf("unable to create package cataloger tasks: %w", err) + } + + return persistentPackageTasks, append(tsks, selectablePackageTasks...), nil +} + +func (c *CreateSBOMConfig) userPackageTasks(cfg task.CatalogingFactoryConfig) ([]task.Task, []task.Task, error) { + var ( + persistentPackageTasks []task.Task + selectablePackageTasks []task.Task + ) + + for _, catalogerRef := range c.packageCatalogerReferences { + if catalogerRef.Cataloger == nil { + return nil, nil, errors.New("provided cataloger reference without a cataloger") + } + if catalogerRef.AlwaysEnabled { + persistentPackageTasks = append(persistentPackageTasks, task.NewPackageTask(cfg, catalogerRef.Cataloger, catalogerRef.Tags...)) + continue + } + if len(catalogerRef.Tags) == 0 { + return nil, nil, errors.New("provided cataloger reference without tags") + } + selectablePackageTasks = append(selectablePackageTasks, task.NewPackageTask(cfg, catalogerRef.Cataloger, catalogerRef.Tags...)) + } + + return persistentPackageTasks, selectablePackageTasks, nil +} + +// relationshipTasks returns the set of tasks that should be run to generate additional relationships as well as +// prune existing relationships. +func (c *CreateSBOMConfig) relationshipTasks(src source.Description) []task.Task { + var tsks []task.Task + + if t := task.NewRelationshipsTask(c.Relationships, src); t != nil { + tsks = append(tsks, t) + } + return tsks +} + +// environmentTasks returns the set of tasks that should be run to identify what is being scanned or the context +// of where it is being scanned. Today this is used to identify the linux distribution release for container images +// being scanned. +func (c *CreateSBOMConfig) environmentTasks() []task.Task { + var tsks []task.Task + + if t := task.NewEnvironmentTask(); t != nil { + tsks = append(tsks, t) + } + return tsks +} + +func (c *CreateSBOMConfig) validate() error { + if c.Relationships.ExcludeBinaryPackagesWithFileOwnershipOverlap { + if !c.Relationships.PackageFileOwnershipOverlap { + return fmt.Errorf("invalid configuration: to exclude binary packages based on file ownership overlap relationships, cataloging file ownership overlap relationships must be enabled") + } + } + return nil +} + +// Create creates an SBOM from the given source with the current SBOM configuration. +func (c *CreateSBOMConfig) Create(ctx context.Context, src source.Source) (*sbom.SBOM, error) { + return CreateSBOM(ctx, src, c) +} + +func findDefaultTag(src source.Description) (string, error) { + switch m := src.Metadata.(type) { + case source.StereoscopeImageSourceMetadata: + return pkgcataloging.ImageTag, nil + case source.FileSourceMetadata, source.DirectorySourceMetadata: + return pkgcataloging.DirectoryTag, nil + default: + return "", fmt.Errorf("unable to determine default cataloger tag for source type=%T", m) + } +} + +func replaceDefaultTagReferences(defaultTag string, lst []string) []string { + for i, tag := range lst { + if strings.ToLower(tag) == "default" { + lst[i] = defaultTag + } + } + return lst +} diff --git a/syft/create_sbom_config_test.go b/syft/create_sbom_config_test.go new file mode 100644 index 00000000000..f7ee03d3d51 --- /dev/null +++ b/syft/create_sbom_config_test.go @@ -0,0 +1,506 @@ +package syft + +import ( + "context" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/cataloging" + "github.com/anchore/syft/syft/cataloging/filecataloging" + "github.com/anchore/syft/syft/cataloging/pkgcataloging" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +var _ pkg.Cataloger = (*dummyCataloger)(nil) + +type dummyCataloger struct { + name string +} + +func newDummyCataloger(name string) pkg.Cataloger { + return dummyCataloger{name: name} +} + +func (d dummyCataloger) Name() string { + return d.name +} + +func (d dummyCataloger) Catalog(_ context.Context, _ file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { + return nil, nil, nil +} + +func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) { + pkgIntersect := func(intersect ...string) []string { + var sets []*strset.Set + for _, s := range intersect { + sets = append(sets, strset.New(pkgCatalogerNamesWithTagOrName(t, s)...)) + } + + intersectSet := strset.Intersection(sets...) + + slice := intersectSet.List() + + sort.Strings(slice) + + return slice + } + + addTo := func(slice []string, add ...string) []string { + slice = append(slice, add...) + sort.Strings(slice) + return slice + } + + imgSrc := source.Description{ + Metadata: source.StereoscopeImageSourceMetadata{}, + } + + dirSrc := source.Description{ + Metadata: source.DirectorySourceMetadata{}, + } + + fileSrc := source.Description{ + Metadata: source.FileSourceMetadata{}, + } + + tests := []struct { + name string + src source.Description + cfg *CreateSBOMConfig + wantTaskNames [][]string + wantManifest *catalogerManifest + wantErr require.ErrorAssertionFunc + }{ + { + name: "default catalogers for image source", + src: imgSrc, + cfg: DefaultCreateSBOMConfig(), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + pkgCatalogerNamesWithTagOrName(t, "image"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + Used: pkgCatalogerNamesWithTagOrName(t, "image"), + }, + wantErr: require.NoError, + }, + { + name: "default catalogers for directory source", + src: dirSrc, + cfg: DefaultCreateSBOMConfig(), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + pkgCatalogerNamesWithTagOrName(t, "directory"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"directory"}, + }, + Used: pkgCatalogerNamesWithTagOrName(t, "directory"), + }, + wantErr: require.NoError, + }, + { + // note, the file source acts like a directory scan + name: "default catalogers for file source", + src: fileSrc, + cfg: DefaultCreateSBOMConfig(), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + pkgCatalogerNamesWithTagOrName(t, "directory"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"directory"}, + }, + Used: pkgCatalogerNamesWithTagOrName(t, "directory"), + }, + wantErr: require.NoError, + }, + { + name: "no file digest cataloger", + src: imgSrc, + cfg: DefaultCreateSBOMConfig().WithFilesConfig(filecataloging.DefaultConfig().WithHashers()), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + pkgCatalogerNamesWithTagOrName(t, "image"), + fileCatalogerNames(false, true, true), // note: the digest cataloger is not included + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + Used: pkgCatalogerNamesWithTagOrName(t, "image"), + }, + wantErr: require.NoError, + }, + { + name: "select no file catalogers", + src: imgSrc, + cfg: DefaultCreateSBOMConfig().WithFilesConfig(filecataloging.DefaultConfig().WithSelection(file.NoFilesSelection)), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + pkgCatalogerNamesWithTagOrName(t, "image"), + // note: there are no file catalogers in their own group + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + Used: pkgCatalogerNamesWithTagOrName(t, "image"), + }, + wantErr: require.NoError, + }, + { + name: "select all file catalogers", + src: imgSrc, + cfg: DefaultCreateSBOMConfig().WithFilesConfig(filecataloging.DefaultConfig().WithSelection(file.AllFilesSelection)), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + // note: there is a single group of catalogers for pkgs and files + append( + pkgCatalogerNamesWithTagOrName(t, "image"), + fileCatalogerNames(true, true, true)..., + ), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + Used: pkgCatalogerNamesWithTagOrName(t, "image"), + }, + wantErr: require.NoError, + }, + { + name: "user-provided persistent cataloger is always run (image)", + src: imgSrc, + cfg: DefaultCreateSBOMConfig().WithCatalogers( + pkgcataloging.NewAlwaysEnabledCatalogerReference(newDummyCataloger("persistent")), + ), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + Used: addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"), + }, + wantErr: require.NoError, + }, + { + name: "user-provided persistent cataloger is always run (directory)", + src: dirSrc, + cfg: DefaultCreateSBOMConfig().WithCatalogers( + pkgcataloging.NewAlwaysEnabledCatalogerReference(newDummyCataloger("persistent")), + ), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"directory"}, + }, + Used: addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"), + }, + wantErr: require.NoError, + }, + { + name: "user-provided persistent cataloger is always run (user selection does not affect this)", + src: imgSrc, + cfg: DefaultCreateSBOMConfig().WithCatalogers( + pkgcataloging.NewAlwaysEnabledCatalogerReference(newDummyCataloger("persistent")), + ).WithCatalogerSelection(pkgcataloging.NewSelectionRequest().WithSubSelections("javascript")), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + addTo(pkgIntersect("image", "javascript"), "persistent"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + SubSelectTags: []string{"javascript"}, + }, + Used: addTo(pkgIntersect("image", "javascript"), "persistent"), + }, + wantErr: require.NoError, + }, + { + name: "user-provided cataloger runs when selected", + src: imgSrc, + cfg: DefaultCreateSBOMConfig().WithCatalogers( + pkgcataloging.NewCatalogerReference(newDummyCataloger("user-provided"), []string{"image"}), + ), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + Used: addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"), + }, + wantErr: require.NoError, + }, + { + name: "user-provided cataloger NOT run when NOT selected", + src: imgSrc, + cfg: DefaultCreateSBOMConfig().WithCatalogers( + pkgcataloging.NewCatalogerReference(newDummyCataloger("user-provided"), []string{"bogus-selector-will-never-be-used"}), + ), + wantTaskNames: [][]string{ + environmentCatalogerNames(), + pkgCatalogerNamesWithTagOrName(t, "image"), + fileCatalogerNames(true, true, true), + relationshipCatalogerNames(), + }, + wantManifest: &catalogerManifest{ + Requested: pkgcataloging.SelectionRequest{ + DefaultNamesOrTags: []string{"image"}, + }, + Used: pkgCatalogerNamesWithTagOrName(t, "image"), + }, + wantErr: require.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = require.NoError + } + + // sanity check + require.NotEmpty(t, tt.wantTaskNames) + for _, group := range tt.wantTaskNames { + require.NotEmpty(t, group) + } + + // test the subject + gotTasks, gotManifest, err := tt.cfg.makeTaskGroups(tt.src) + tt.wantErr(t, err) + if err != nil { + return + } + + gotNames := taskGroupNames(gotTasks) + + if d := cmp.Diff( + tt.wantTaskNames, + gotNames, + // order within a group does not matter + cmpopts.SortSlices(func(a, b string) bool { + return a < b + }), + ); d != "" { + t.Errorf("mismatched task group names (-want +got):\n%s", d) + } + + if d := cmp.Diff(tt.wantManifest, gotManifest); d != "" { + t.Errorf("mismatched cataloger manifest (-want +got):\n%s", d) + } + }) + } +} + +func pkgCatalogerNamesWithTagOrName(t *testing.T, token string) []string { + var names []string + cfg := task.DefaultCatalogingFactoryConfig() + for _, factory := range task.DefaultPackageTaskFactories() { + cat := factory(cfg) + + name := cat.Name() + + if selector, ok := cat.(task.Selector); ok { + if selector.HasAllSelectors(token) { + names = append(names, name) + continue + } + } + if name == token { + names = append(names, name) + } + } + + // these thresholds are arbitrary but should be large enough to catch any major changes + switch token { + case "image": + require.Greater(t, len(names), 18, "minimum cataloger sanity check failed token") + case "directory": + require.Greater(t, len(names), 25, "minimum cataloger sanity check failed token") + default: + require.Greater(t, len(names), 0, "minimum cataloger sanity check failed token") + } + + sort.Strings(names) + return names +} + +func fileCatalogerNames(digest, metadata, executable bool) []string { + var names []string + if digest { + names = append(names, "file-digest-cataloger") + } + if executable { + names = append(names, "file-executable-cataloger") + } + if metadata { + names = append(names, "file-metadata-cataloger") + } + return names +} + +func relationshipCatalogerNames() []string { + return []string{"relationships-cataloger"} +} + +func environmentCatalogerNames() []string { + return []string{"environment-cataloger"} +} + +func taskGroupNames(groups [][]task.Task) [][]string { + var names [][]string + for _, group := range groups { + var groupNames []string + for _, tsk := range group { + groupNames = append(groupNames, tsk.Name()) + } + names = append(names, groupNames) + } + return names +} + +func Test_replaceDefaultTagReferences(t *testing.T) { + + tests := []struct { + name string + lst []string + want []string + }{ + { + name: "no default tag", + lst: []string{"foo", "bar"}, + want: []string{"foo", "bar"}, + }, + { + name: "replace default tag", + lst: []string{"foo", "default", "bar"}, + want: []string{"foo", "replacement", "bar"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, replaceDefaultTagReferences("replacement", tt.lst)) + }) + } +} + +func Test_findDefaultTag(t *testing.T) { + + tests := []struct { + name string + src source.Description + want string + wantErr require.ErrorAssertionFunc + }{ + { + name: "image", + src: source.Description{ + Metadata: source.StereoscopeImageSourceMetadata{}, + }, + want: pkgcataloging.ImageTag, + }, + { + name: "directory", + src: source.Description{ + Metadata: source.DirectorySourceMetadata{}, + }, + want: pkgcataloging.DirectoryTag, + }, + { + name: "file", + src: source.Description{ + Metadata: source.FileSourceMetadata{}, + }, + want: pkgcataloging.DirectoryTag, // not a mistake... + }, + { + name: "unknown", + src: source.Description{ + Metadata: struct{}{}, + }, + 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 := findDefaultTag(tt.src) + tt.wantErr(t, err) + if err != nil { + return + } + assert.Equal(t, tt.want, got) + }) + } +} + +func TestCreateSBOMConfig_validate(t *testing.T) { + tests := []struct { + name string + cfg *CreateSBOMConfig + wantErr assert.ErrorAssertionFunc + }{ + { + name: "incompatible ExcludeBinaryPackagesWithFileOwnershipOverlap selection", + cfg: DefaultCreateSBOMConfig(). + WithRelationshipsConfig( + cataloging.DefaultRelationshipsConfig(). + WithExcludeBinaryPackagesWithFileOwnershipOverlap(true). + WithPackageFileOwnershipOverlap(false), + ), + wantErr: assert.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = assert.NoError + } + tt.wantErr(t, tt.cfg.validate()) + }) + } +} diff --git a/syft/event/event.go b/syft/event/event.go index b18ff9decdb..6c760af66aa 100644 --- a/syft/event/event.go +++ b/syft/event/event.go @@ -14,15 +14,6 @@ const ( // Events from the syft library - // PackageCatalogerStarted is a partybus event that occurs when the package cataloging has begun - PackageCatalogerStarted partybus.EventType = typePrefix + "-package-cataloger-started-event" - - // FileMetadataCatalogerStarted is a partybus event that occurs when the file metadata cataloging has begun - FileMetadataCatalogerStarted partybus.EventType = typePrefix + "-file-metadata-cataloger-started-event" - - // FileDigestsCatalogerStarted is a partybus event that occurs when the file digests cataloging has begun - FileDigestsCatalogerStarted partybus.EventType = typePrefix + "-file-digests-cataloger-started-event" - // FileIndexingStarted is a partybus event that occurs when the directory resolver begins indexing a filesystem FileIndexingStarted partybus.EventType = typePrefix + "-file-indexing-started-event" diff --git a/syft/event/monitor/cataloger_task.go b/syft/event/monitor/cataloger_task.go deleted file mode 100644 index 4a06132e79e..00000000000 --- a/syft/event/monitor/cataloger_task.go +++ /dev/null @@ -1,55 +0,0 @@ -package monitor - -import ( - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/syft/event" -) - -// TODO: this should be refactored to support read-only/write-only access using idioms of the progress lib - -type CatalogerTask struct { - prog *progress.Manual - // Title - Title string - // TitleOnCompletion a string to use as title when completed - TitleOnCompletion string - // SubStatus indicates this progress should be rendered as a sub-item - SubStatus bool - // RemoveOnCompletion indicates this progress line will be removed when completed - RemoveOnCompletion bool - // value is the value to display -- not public as SetValue needs to be called to initialize this progress - value string -} - -func (e *CatalogerTask) init() { - e.prog = progress.NewManual(-1) - - bus.Publish(partybus.Event{ - Type: event.CatalogerTaskStarted, - Source: e, - }) -} - -func (e *CatalogerTask) SetCompleted() { - if e.prog != nil { - e.prog.SetCompleted() - } -} - -func (e *CatalogerTask) SetValue(value string) { - if e.prog == nil { - e.init() - } - e.value = value -} - -func (e *CatalogerTask) GetValue() string { - return e.value -} - -func (e *CatalogerTask) GetMonitor() *progress.Manual { - return e.prog -} diff --git a/syft/event/monitor/cataloger_task_progress.go b/syft/event/monitor/cataloger_task_progress.go new file mode 100644 index 00000000000..49f438fd038 --- /dev/null +++ b/syft/event/monitor/cataloger_task_progress.go @@ -0,0 +1,15 @@ +package monitor + +import ( + "github.com/wagoodman/go-progress" +) + +const ( + TopLevelCatalogingTaskID = "cataloging" + PackageCatalogingTaskID = "package-cataloging" +) + +type CatalogerTaskProgress struct { + *progress.AtomicStage + *progress.Manual +} diff --git a/syft/event/monitor/generic_task.go b/syft/event/monitor/generic_task.go index cf5a6ea6df2..b4ed48d2d7d 100644 --- a/syft/event/monitor/generic_task.go +++ b/syft/event/monitor/generic_task.go @@ -18,6 +18,19 @@ type Title struct { } type GenericTask struct { - Title Title - Context string + + // required fields + + Title Title + + // optional format fields + + HideOnSuccess bool + HideStageOnSuccess bool + + // optional fields + + ID string + ParentID string + Context string } diff --git a/syft/event/parsers/parsers.go b/syft/event/parsers/parsers.go index 5f7f2d7c8aa..52a11d30922 100644 --- a/syft/event/parsers/parsers.go +++ b/syft/event/parsers/parsers.go @@ -12,7 +12,6 @@ import ( "github.com/anchore/syft/syft/event" "github.com/anchore/syft/syft/event/monitor" - "github.com/anchore/syft/syft/pkg/cataloger" ) type ErrBadPayload struct { @@ -40,45 +39,6 @@ func checkEventType(actual, expected partybus.EventType) error { return nil } -func ParsePackageCatalogerStarted(e partybus.Event) (*cataloger.Monitor, error) { - if err := checkEventType(e.Type, event.PackageCatalogerStarted); err != nil { - return nil, err - } - - monitor, ok := e.Value.(cataloger.Monitor) - if !ok { - return nil, newPayloadErr(e.Type, "Value", e.Value) - } - - return &monitor, nil -} - -func ParseFileMetadataCatalogingStarted(e partybus.Event) (progress.StagedProgressable, error) { - if err := checkEventType(e.Type, event.FileMetadataCatalogerStarted); err != nil { - return nil, err - } - - prog, ok := e.Value.(progress.StagedProgressable) - if !ok { - return nil, newPayloadErr(e.Type, "Value", e.Value) - } - - return prog, nil -} - -func ParseFileDigestsCatalogingStarted(e partybus.Event) (progress.StagedProgressable, error) { - if err := checkEventType(e.Type, event.FileDigestsCatalogerStarted); err != nil { - return nil, err - } - - prog, ok := e.Value.(progress.StagedProgressable) - if !ok { - return nil, newPayloadErr(e.Type, "Value", e.Value) - } - - return prog, nil -} - func ParseFileIndexingStarted(e partybus.Event) (string, progress.StagedProgressable, error) { if err := checkEventType(e.Type, event.FileIndexingStarted); err != nil { return "", nil, err @@ -97,17 +57,24 @@ func ParseFileIndexingStarted(e partybus.Event) (string, progress.StagedProgress return path, prog, nil } -func ParseCatalogerTaskStarted(e partybus.Event) (*monitor.CatalogerTask, error) { +func ParseCatalogerTaskStarted(e partybus.Event) (progress.StagedProgressable, *monitor.GenericTask, error) { if err := checkEventType(e.Type, event.CatalogerTaskStarted); err != nil { - return nil, err + return nil, nil, err + } + + var mon progress.StagedProgressable + + source, ok := e.Source.(monitor.GenericTask) + if !ok { + return nil, nil, newPayloadErr(e.Type, "Source", e.Source) } - source, ok := e.Source.(*monitor.CatalogerTask) + mon, ok = e.Value.(progress.StagedProgressable) if !ok { - return nil, newPayloadErr(e.Type, "Source", e.Source) + mon = nil } - return source, nil + return mon, &source, nil } func ParseAttestationStartedEvent(e partybus.Event) (io.Reader, progress.Progressable, *monitor.GenericTask, error) { diff --git a/syft/file/cataloger/executable/cataloger.go b/syft/file/cataloger/executable/cataloger.go new file mode 100644 index 00000000000..e75eb813f45 --- /dev/null +++ b/syft/file/cataloger/executable/cataloger.go @@ -0,0 +1,247 @@ +package executable + +import ( + "bytes" + "debug/elf" + "debug/macho" + "encoding/binary" + "fmt" + "sort" + + "github.com/bmatcuk/doublestar/v4" + "github.com/dustin/go-humanize" + + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/internal/mimetype" + "github.com/anchore/syft/syft/event/monitor" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/internal/unionreader" +) + +type Config struct { + MIMETypes []string `json:"mime-types" yaml:"mime-types" mapstructure:"mime-types"` + Globs []string `json:"globs" yaml:"globs" mapstructure:"globs"` +} + +type Cataloger struct { + config Config +} + +func DefaultConfig() Config { + m := mimetype.ExecutableMIMETypeSet.List() + sort.Strings(m) + return Config{ + MIMETypes: m, + Globs: nil, + } +} + +func NewCataloger(cfg Config) *Cataloger { + return &Cataloger{ + config: cfg, + } +} + +func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.Executable, error) { + locs, err := resolver.FilesByMIMEType(i.config.MIMETypes...) + if err != nil { + return nil, fmt.Errorf("unable to get file locations for binaries: %w", err) + } + + locs, err = filterByGlobs(locs, i.config.Globs) + if err != nil { + return nil, err + } + + prog := catalogingProgress(int64(len(locs))) + + results := make(map[file.Coordinates]file.Executable) + for _, loc := range locs { + prog.AtomicStage.Set(loc.Path()) + + reader, err := resolver.FileContentsByLocation(loc) + if err != nil { + // TODO: known-unknowns + log.WithFields("error", err).Warnf("unable to get file contents for %q", loc.RealPath) + continue + } + + uReader, err := unionreader.GetUnionReader(reader) + if err != nil { + // TODO: known-unknowns + log.WithFields("error", err).Warnf("unable to get union reader for %q", loc.RealPath) + continue + } + + exec, err := processExecutable(loc, uReader) + if err != nil { + log.WithFields("error", err).Warnf("unable to process executable %q", loc.RealPath) + } + if exec != nil { + prog.Increment() + results[loc.Coordinates] = *exec + } + } + + log.Debugf("executable cataloger processed %d files", len(results)) + + prog.AtomicStage.Set(fmt.Sprintf("%s executables", humanize.Comma(prog.Current()))) + prog.SetCompleted() + + return results, nil +} + +func catalogingProgress(locations int64) *monitor.CatalogerTaskProgress { + info := monitor.GenericTask{ + Title: monitor.Title{ + Default: "Executables", + }, + ParentID: monitor.TopLevelCatalogingTaskID, + } + + return bus.StartCatalogerTask(info, locations, "") +} + +func filterByGlobs(locs []file.Location, globs []string) ([]file.Location, error) { + if len(globs) == 0 { + return locs, nil + } + var filteredLocs []file.Location + for _, loc := range locs { + matches, err := locationMatchesGlob(loc, globs) + if err != nil { + return nil, err + } + if matches { + filteredLocs = append(filteredLocs, loc) + } + } + return filteredLocs, nil +} + +func locationMatchesGlob(loc file.Location, globs []string) (bool, error) { + for _, glob := range globs { + for _, path := range []string{loc.RealPath, loc.AccessPath} { + if path == "" { + continue + } + matches, err := doublestar.Match(glob, path) + if err != nil { + return false, fmt.Errorf("unable to match glob %q to path %q: %w", glob, path, err) + } + if matches { + return true, nil + } + } + } + return false, nil +} + +func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file.Executable, error) { + data := file.Executable{} + + // determine the executable format + + format, err := findExecutableFormat(reader) + if err != nil { + return nil, fmt.Errorf("unable to determine executable kind: %w", err) + } + + if format == "" { + log.Debugf("unable to determine executable format for %q", loc.RealPath) + return nil, nil + } + + data.Format = format + + securityFeatures, err := findSecurityFeatures(format, reader) + if err != nil { + log.WithFields("error", err).Tracef("unable to determine security features for %q", loc.RealPath) + return nil, nil + } + + data.SecurityFeatures = securityFeatures + + return &data, nil +} + +func findExecutableFormat(reader unionreader.UnionReader) (file.ExecutableFormat, error) { + // read the first sector of the file + buf := make([]byte, 512) + n, err := reader.ReadAt(buf, 0) + if err != nil { + return "", fmt.Errorf("unable to read first sector of file: %w", err) + } + if n < 512 { + return "", fmt.Errorf("unable to read enough bytes to determine executable format") + } + + switch { + case isMacho(buf): + return file.MachO, nil + case isPE(buf): + return file.PE, nil + case isELF(buf): + return file.ELF, nil + } + + return "", nil +} + +func isMacho(by []byte) bool { + // sourced from https://github.com/gabriel-vasile/mimetype/blob/02af149c0dfd1444d9256fc33c2012bb3153e1d2/internal/magic/binary.go#L44 + + if classOrMachOFat(by) && by[7] < 20 { + return true + } + + if len(by) < 4 { + return false + } + + be := binary.BigEndian.Uint32(by) + le := binary.LittleEndian.Uint32(by) + + return be == macho.Magic32 || + le == macho.Magic32 || + be == macho.Magic64 || + le == macho.Magic64 +} + +// Java bytecode and Mach-O binaries share the same magic number. +// More info here https://github.com/threatstack/libmagic/blob/master/magic/Magdir/cafebabe +func classOrMachOFat(in []byte) bool { + // sourced from https://github.com/gabriel-vasile/mimetype/blob/02af149c0dfd1444d9256fc33c2012bb3153e1d2/internal/magic/binary.go#L44 + + // There should be at least 8 bytes for both of them because the only way to + // quickly distinguish them is by comparing byte at position 7 + if len(in) < 8 { + return false + } + + return bytes.HasPrefix(in, []byte{0xCA, 0xFE, 0xBA, 0xBE}) +} + +func isPE(by []byte) bool { + return bytes.HasPrefix(by, []byte("MZ")) +} + +func isELF(by []byte) bool { + return bytes.HasPrefix(by, []byte(elf.ELFMAG)) +} + +func findSecurityFeatures(format file.ExecutableFormat, reader unionreader.UnionReader) (*file.ELFSecurityFeatures, error) { + // TODO: add support for PE and MachO + switch format { //nolint: gocritic + case file.ELF: + return findELFSecurityFeatures(reader) //nolint: gocritic + case file.PE: + // return findPESecurityFeatures(reader) + return nil, nil + case file.MachO: + // return findMachOSecurityFeatures(reader) + return nil, nil + } + return nil, fmt.Errorf("unsupported executable format: %q", format) +} diff --git a/syft/file/cataloger/executable/elf.go b/syft/file/cataloger/executable/elf.go new file mode 100644 index 00000000000..c1218255e39 --- /dev/null +++ b/syft/file/cataloger/executable/elf.go @@ -0,0 +1,221 @@ +package executable + +import ( + "debug/elf" + "regexp" + "strings" + + "github.com/scylladb/go-set/strset" + + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/internal/unionreader" +) + +func findELFSecurityFeatures(reader unionreader.UnionReader) (*file.ELFSecurityFeatures, error) { + f, err := elf.NewFile(reader) + if err != nil { + return nil, nil + } + + features := file.ELFSecurityFeatures{ + SymbolTableStripped: isElfSymbolTableStripped(f), + StackCanary: checkElfStackCanary(f), + NoExecutable: checkElfNXProtection(f), + RelocationReadOnly: checkElfRelROProtection(f), + PositionIndependentExecutable: isELFPIE(f), + DynamicSharedObject: isELFDSO(f), + LlvmSafeStack: checkLLVMSafeStack(f), + LlvmControlFlowIntegrity: checkLLVMControlFlowIntegrity(f), + ClangFortifySource: checkClangFortifySource(f), + } + + return &features, nil +} + +func isElfSymbolTableStripped(file *elf.File) bool { + return file.Section(".symtab") == nil +} + +func checkElfStackCanary(file *elf.File) *bool { + return hasAnyDynamicSymbols(file, "__stack_chk_fail", "__stack_chk_guard") +} + +func hasAnyDynamicSymbols(file *elf.File, symbolNames ...string) *bool { + dynSyms, err := file.DynamicSymbols() + if err != nil { + // TODO: known-unknowns + log.WithFields("error", err).Trace("unable to read dynamic symbols from elf file") + return nil + } + + nameSet := strset.New(symbolNames...) + + for _, sym := range dynSyms { + if nameSet.Has(sym.Name) { + return boolRef(true) + } + } + return boolRef(false) +} + +func boolRef(b bool) *bool { + return &b +} + +func checkElfNXProtection(file *elf.File) bool { + // find the program headers until you find the GNU_STACK segment + for _, prog := range file.Progs { + if prog.Type == elf.PT_GNU_STACK { + // check if the GNU_STACK segment is executable + return prog.Flags&elf.PF_X == 0 + } + } + + return false +} + +func checkElfRelROProtection(f *elf.File) file.RelocationReadOnly { + // background on relro https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro + hasRelro := false + hasBindNow := hasBindNowDynTagOrFlag(f) + + for _, prog := range f.Progs { + if prog.Type == elf.PT_GNU_RELRO { + hasRelro = true + break + } + } + + switch { + case hasRelro && hasBindNow: + return file.RelocationReadOnlyFull + case hasRelro: + return file.RelocationReadOnlyPartial + default: + return file.RelocationReadOnlyNone + } +} + +func hasBindNowDynTagOrFlag(f *elf.File) bool { + if hasElfDynTag(f, elf.DT_BIND_NOW) { + // support older binaries... + return true + } + + // "DT_BIND_NOW ... use has been superseded by the DF_BIND_NOW flag" + // source: https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html + return hasElfDynFlag(f, elf.DF_BIND_NOW) +} + +func hasElfDynFlag(f *elf.File, flag elf.DynFlag) bool { + vals, err := f.DynValue(elf.DT_FLAGS) + if err != nil { + // TODO: known-unknowns + log.WithFields("error", err).Trace("unable to read DT_FLAGS from elf file") + return false + } + for _, val := range vals { + if val&uint64(flag) != 0 { + return true + } + } + return false +} + +func hasElfDynFlag1(f *elf.File, flag elf.DynFlag1) bool { + vals, err := f.DynValue(elf.DT_FLAGS_1) + if err != nil { + // TODO: known-unknowns + log.WithFields("error", err).Trace("unable to read DT_FLAGS_1 from elf file") + return false + } + for _, val := range vals { + if val&uint64(flag) != 0 { + return true + } + } + return false +} + +func hasElfDynTag(f *elf.File, tag elf.DynTag) bool { + // source https://github.com/golang/go/blob/9b4b3e5acca2dabe107fa2c3ed963097d78a4562/src/cmd/cgo/internal/testshared/shared_test.go#L280 + + ds := f.SectionByType(elf.SHT_DYNAMIC) + if ds == nil { + return false + } + d, err := ds.Data() + if err != nil { + return false + } + + for len(d) > 0 { + var t elf.DynTag + switch f.Class { + case elf.ELFCLASS32: + t = elf.DynTag(f.ByteOrder.Uint32(d[0:4])) + d = d[8:] + case elf.ELFCLASS64: + t = elf.DynTag(f.ByteOrder.Uint64(d[0:8])) + d = d[16:] + } + if t == tag { + return true + } + } + return false +} + +func isELFPIE(f *elf.File) bool { + // being a shared object is not sufficient to be a PIE, the explicit flag must be set also + return isELFDSO(f) && hasElfDynFlag1(f, elf.DF_1_PIE) +} + +func isELFDSO(f *elf.File) bool { + return f.Type == elf.ET_DYN +} + +func checkLLVMSafeStack(file *elf.File) *bool { + // looking for the presence of https://github.com/microsoft/compiler-rt/blob/30b3b8cb5c9a0854f2f40f187c6f6773561a35f2/lib/safestack/safestack.cc#L207 + return hasAnyDynamicSymbols(file, "__safestack_init") +} + +func checkLLVMControlFlowIntegrity(file *elf.File) *bool { + // look for any symbols that are functions and end with ".cfi" + dynSyms, err := file.Symbols() + if err != nil { + // TODO: known-unknowns + log.WithFields("error", err).Trace("unable to read symbols from elf file") + return nil + } + + for _, sym := range dynSyms { + if isFunction(sym) && strings.HasSuffix(sym.Name, ".cfi") { + return boolRef(true) + } + } + return boolRef(false) +} + +func isFunction(sym elf.Symbol) bool { + return elf.ST_TYPE(sym.Info) == elf.STT_FUNC +} + +var fortifyPattern = regexp.MustCompile(`__\w+_chk@.+`) + +func checkClangFortifySource(file *elf.File) *bool { + dynSyms, err := file.Symbols() + if err != nil { + // TODO: known-unknowns + log.WithFields("error", err).Trace("unable to read symbols from elf file") + return nil + } + + for _, sym := range dynSyms { + if isFunction(sym) && fortifyPattern.MatchString(sym.Name) { + return boolRef(true) + } + } + return boolRef(false) +} diff --git a/syft/file/cataloger/executable/elf_test.go b/syft/file/cataloger/executable/elf_test.go new file mode 100644 index 00000000000..1ca484ff001 --- /dev/null +++ b/syft/file/cataloger/executable/elf_test.go @@ -0,0 +1,162 @@ +package executable + +import ( + "os" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/internal/unionreader" +) + +func Test_findELFSecurityFeatures(t *testing.T) { + + readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader { + t.Helper() + f, err := os.Open(filepath.Join("test-fixtures", fixture)) + require.NoError(t, err) + return f + } + + tests := []struct { + name string + fixture string + want *file.ELFSecurityFeatures + wantStripped bool + wantErr require.ErrorAssertionFunc + }{ + { + name: "detect canary", + fixture: "bin/with_canary", + want: &file.ELFSecurityFeatures{ + StackCanary: boolRef(true), // ! important ! + RelocationReadOnly: file.RelocationReadOnlyNone, + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect nx", + fixture: "bin/with_nx", + want: &file.ELFSecurityFeatures{ + StackCanary: boolRef(false), + NoExecutable: true, // ! important ! + RelocationReadOnly: file.RelocationReadOnlyNone, + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect relro", + fixture: "bin/with_relro", + want: &file.ELFSecurityFeatures{ + StackCanary: boolRef(false), + RelocationReadOnly: file.RelocationReadOnlyFull, // ! important ! + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect partial relro", + fixture: "bin/with_partial_relro", + want: &file.ELFSecurityFeatures{ + StackCanary: boolRef(false), + RelocationReadOnly: file.RelocationReadOnlyPartial, // ! important ! + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect pie", + fixture: "bin/with_pie", + want: &file.ELFSecurityFeatures{ + StackCanary: boolRef(false), + RelocationReadOnly: file.RelocationReadOnlyNone, + PositionIndependentExecutable: true, // ! important ! + DynamicSharedObject: true, // ! important ! + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect dso", + fixture: "bin/pie_false_positive.so", + want: &file.ELFSecurityFeatures{ + StackCanary: boolRef(false), + RelocationReadOnly: file.RelocationReadOnlyPartial, + NoExecutable: true, + PositionIndependentExecutable: false, // ! important ! + DynamicSharedObject: true, // ! important ! + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect safestack", + fixture: "bin/with_safestack", + want: &file.ELFSecurityFeatures{ + NoExecutable: true, + StackCanary: boolRef(false), + RelocationReadOnly: file.RelocationReadOnlyPartial, + PositionIndependentExecutable: false, + DynamicSharedObject: false, + LlvmSafeStack: boolRef(true), // ! important ! + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect cfi", + fixture: "bin/with_cfi", + want: &file.ELFSecurityFeatures{ + NoExecutable: true, + StackCanary: boolRef(false), + RelocationReadOnly: file.RelocationReadOnlyPartial, + PositionIndependentExecutable: false, + DynamicSharedObject: false, + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(true), // ! important ! + ClangFortifySource: boolRef(false), + }, + }, + { + name: "detect fortify", + fixture: "bin/with_fortify", + want: &file.ELFSecurityFeatures{ + NoExecutable: true, + StackCanary: boolRef(false), + RelocationReadOnly: file.RelocationReadOnlyPartial, + PositionIndependentExecutable: false, + DynamicSharedObject: false, + LlvmSafeStack: boolRef(false), + LlvmControlFlowIntegrity: boolRef(false), + ClangFortifySource: boolRef(true), // ! important ! + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = require.NoError + } + got, err := findELFSecurityFeatures(readerForFixture(t, tt.fixture)) + tt.wantErr(t, err) + if err != nil { + return + } + + if d := cmp.Diff(tt.want, got); d != "" { + t.Errorf("findELFSecurityFeatures() mismatch (-want +got):\n%s", d) + } + }) + } +} diff --git a/syft/file/cataloger/executable/test-fixtures/.gitignore b/syft/file/cataloger/executable/test-fixtures/.gitignore new file mode 100644 index 00000000000..89845afaa3a --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/.gitignore @@ -0,0 +1,3 @@ +bin +actual_verify +Dockerfile.sha256 \ No newline at end of file diff --git a/syft/file/cataloger/executable/test-fixtures/Dockerfile b/syft/file/cataloger/executable/test-fixtures/Dockerfile new file mode 100644 index 00000000000..46425e08fd7 --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/Dockerfile @@ -0,0 +1,16 @@ +FROM gcc:9.5.0 + +RUN apt update -y && apt install -y clang cmake git make m4 pkg-config zlib1g-dev + +## from https://github.com/runsafesecurity/selfrando/blob/tb-v0.4.2/docs/linux-build-instructions.md +#RUN git clone https://github.com/runsafesecurity/selfrando.git && \ +# export SR_ARCH=`uname -m | sed s/i686/x86/` && \ +# cd selfrando && \ +# cmake . -DSR_DEBUG_LEVEL=env -DCMAKE_BUILD_TYPE=Release -DSR_BUILD_LIBELF=1 \ +# -DSR_ARCH=$SR_ARCH -DSR_LOG=console \ +# -DSR_FORCE_INPLACE=1 -G "Unix Makefiles" \ +# -DCMAKE_INSTALL_PREFIX:PATH=$PWD/out/$SR_ARCH +#RUN cd selfrando && make -j`nprocs --all` +#RUN cd selfrando && make install + +RUN curl -o /bin/checksec https://raw.githubusercontent.com/slimm609/checksec.sh/2.6.0/checksec && chmod +x /bin/checksec diff --git a/syft/file/cataloger/executable/test-fixtures/Makefile b/syft/file/cataloger/executable/test-fixtures/Makefile new file mode 100644 index 00000000000..1cff6183e1e --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/Makefile @@ -0,0 +1,34 @@ +BIN=./bin +TOOL_IMAGE=localhost/syft-bin-build-tools:latest +VERIFY_FILE=actual_verify + +all: build verify + +tools-check: + @sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1) + +# for selfrando... +# docker buildx build --platform linux/amd64 -t $(TOOL_IMAGE) . + +tools: + @(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256) + +build: tools + mkdir -p $(BIN) + docker run -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make + +verify: tools + @rm -f $(VERIFY_FILE) + docker run -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make verify > $(VERIFY_FILE) + @python ./differ expected_verify $(VERIFY_FILE) + +debug: + docker run -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash + +cache.fingerprint: + @find project Dockerfile Makefile -type f -exec md5sum {} + | awk '{print $1}' | sort | tee cache.fingerprint + +clean: + rm -f $(BIN)/* + +.PHONY: build verify debug build-image build-bins clean dockerfile-check cache.fingerprint diff --git a/syft/file/cataloger/executable/test-fixtures/differ/__main__.py b/syft/file/cataloger/executable/test-fixtures/differ/__main__.py new file mode 100644 index 00000000000..83f7347f8f8 --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/differ/__main__.py @@ -0,0 +1,53 @@ +import json +import sys + + +def load_json(file_path): + with open(file_path, 'r') as file: + return json.load(file) + + +def diff_json(json1, json2): + differences_found = False + + for key in json1: + if key not in json2: + print(f'missing key "{key}"') + continue + + differences = [] + for subkey in json1[key]: + if subkey not in json2[key]: + differences.append(f' - "{subkey}": expected "{json1[key][subkey]}" but was MISSING') + continue + + if subkey in json2[key] and json1[key][subkey] != json2[key][subkey]: + differences.append(f' - "{subkey}": expected "{json1[key][subkey]}" got "{json2[key][subkey]}"') + + if differences: + differences_found = True + print(f'{key}') + for diff in differences: + print(diff) + print() + + return differences_found + + +def main(): + if len(sys.argv) != 3: + print("Usage: python ./differ file1.json file2.json") + sys.exit(1) + + json1 = load_json(sys.argv[1]) + json2 = load_json(sys.argv[2]) + + if diff_json(json1, json2): + print("FAIL: unexpected security feature differences") + sys.exit(1) + else: + print("PASS: all security features accounted for") + sys.exit(0) + + +main() diff --git a/syft/file/cataloger/executable/test-fixtures/expected_verify b/syft/file/cataloger/executable/test-fixtures/expected_verify new file mode 100644 index 00000000000..8f7562c1cd2 --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/expected_verify @@ -0,0 +1 @@ +{ "dir": { "name":"../bin" }, "../bin/no_protection": { "relro":"no","canary":"no","nx":"no","pie":"no","clangcfi":"no","safestack":"no","rpath":"no","runpath":"yes","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/no_protection"}, "../bin/with_nx": { "relro":"no","canary":"no","nx":"yes","pie":"no","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_nx"}, "../bin/with_rpath": { "relro":"no","canary":"no","nx":"no","pie":"no","clangcfi":"no","safestack":"no","rpath":"yes","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_rpath"}, "../bin/with_canary": { "relro":"no","canary":"yes","nx":"no","pie":"no","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_canary"}, "../bin/with_relro": { "relro":"full","canary":"no","nx":"no","pie":"no","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_relro"}, "../bin/with_pie": { "relro":"no","canary":"no","nx":"no","pie":"yes","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_pie"}, "../bin/with_runpath": { "relro":"no","canary":"no","nx":"no","pie":"no","clangcfi":"no","safestack":"no","rpath":"no","runpath":"yes","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_runpath"}, "../bin/with_safestack": { "relro":"partial","canary":"no","nx":"yes","pie":"no","clangcfi":"no","safestack":"yes","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"3", "filename":"../bin/with_safestack"}, "../bin/pie_false_positive.so": { "relro":"partial","canary":"no","nx":"yes","pie":"dso","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"0", "filename":"../bin/pie_false_positive.so"}, "../bin/with_cfi": { "relro":"partial","canary":"no","nx":"yes","pie":"no","clangcfi":"yes","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_cfi"}, "../bin/with_partial_relro": { "relro":"partial","canary":"no","nx":"no","pie":"no","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/with_partial_relro"}, "../bin/protected": { "relro":"full","canary":"yes","nx":"yes","pie":"yes","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"no","fortified":"0","fortify-able":"2", "filename":"../bin/protected"}, "../bin/with_fortify": { "relro":"partial","canary":"no","nx":"yes","pie":"no","clangcfi":"no","safestack":"no","rpath":"no","runpath":"no","symbols":"yes","fortify_source":"yes","fortified":"1","fortify-able":"2", "filename":"../bin/with_fortify"}} \ No newline at end of file diff --git a/syft/file/cataloger/executable/test-fixtures/project/Makefile b/syft/file/cataloger/executable/test-fixtures/project/Makefile new file mode 100644 index 00000000000..17edd3f81b2 --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/project/Makefile @@ -0,0 +1,93 @@ +### GCC Options ############################################ +CANARY := -fstack-protector +NO_CANARY := -fno-stack-protector + +SHARED_OBJ := -shared + +RELRO := -z relro -z now +PARTIAL_RELRO := -z relro +NO_RELRO := -z norelro + +NX := -z noexecstack +NO_NX := -z execstack + +PIE := -fpic -pie +NO_PIE := -no-pie + +# deprecated +RPATH := -Wl,--disable-new-dtags,-rpath,./libs + +# replaces RPATH (thus us mutually exclusive with it) +RUNPATH := -Wl,-rpath,./libs + +GCCFLAGS := -g + +### Clang Options ############################################ + +SAFE_STACK := -fsanitize=safe-stack + +CFI := -flto -fvisibility=hidden -fsanitize=cfi + +FORTIFY := -O2 -D_FORTIFY_SOURCE=2 + +### Common Options ############################################ + +SRC := main.c +LIB_SRC := lib.c +BIN := ../bin + +BINS := $(BIN)/no_protection $(BIN)/with_nx $(BIN)/pie_false_positive.so $(BIN)/with_pie $(BIN)/with_canary $(BIN)/with_relro $(BIN)/with_partial_relro $(BIN)/with_rpath $(BIN)/with_runpath $(BIN)/with_safestack $(BIN)/with_cfi $(BIN)/with_fortify $(BIN)/protected +#.PHONY: verify $(BIN)/no_protection $(BIN)/with_nx $(BIN)/pie_false_positive.so $(BIN)/with_pie $(BIN)/with_canary $(BIN)/with_relro $(BIN)/with_partial_relro $(BIN)/with_rpath $(BIN)/with_runpath $(BIN)/with_safestack $(BIN)/with_cfi $(BIN)/with_fortify $(BIN)/protected +.PHONY: verify clean all + +all: $(BINS) + + +$(BIN)/no_protection : $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NO_NX) $(NO_RELRO) $(NO_PIE) $(RUNPATH) + +$(BIN)/with_nx : $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NX) $(NO_RELRO) $(NO_PIE) + +$(BIN)/pie_false_positive.so: $(LIB_SRC) + gcc $< -c -Wall -Werror -fpic $(LIB_SRC) + gcc -shared -o $@ lib.o ; rm lib.o + +$(BIN)/with_pie: $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NO_NX) $(NO_RELRO) $(PIE) + +$(BIN)/with_canary: $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(CANARY) $(NO_NX) $(NO_RELRO) $(NO_PIE) + +$(BIN)/with_relro: $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NO_NX) $(RELRO) $(NO_PIE) + +$(BIN)/with_partial_relro: $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NO_NX) $(PARTIAL_RELRO) $(NO_PIE) + +$(BIN)/with_rpath: $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NO_NX) $(NO_RELRO) $(NO_PIE) $(RPATH) + +$(BIN)/with_runpath: $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NO_NX) $(NO_RELRO) $(NO_PIE) $(RUNPATH) + +$(BIN)/with_safestack: $(SRC) + clang $< -o $@ $(SAFE_STACK) + +$(BIN)/with_cfi: $(SRC) + clang $< -o $@ $(CFI) + +$(BIN)/with_fortify: $(SRC) + clang $< -o $@ $(FORTIFY) + +#$(BIN)/with_selfrando: $(SRC) +# srenv gcc $< -o $@ $(GCCFLAGS) $(NO_CANARY) $(NO_NX) $(NO_RELRO) $(NO_PIE) + +$(BIN)/protected: $(SRC) + gcc $< -o $@ $(GCCFLAGS) $(CANARY) $(NX) $(RELRO) $(PIE) + +verify: + @/bin/checksec --dir=$(BIN) --extended --output=json + +clean: + rm -rf $(BINS) \ No newline at end of file diff --git a/syft/file/cataloger/executable/test-fixtures/project/lib.c b/syft/file/cataloger/executable/test-fixtures/project/lib.c new file mode 100644 index 00000000000..f665b71dc9e --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/project/lib.c @@ -0,0 +1,6 @@ +#include + +void foo(void) +{ + puts("Share me!"); +} \ No newline at end of file diff --git a/syft/file/cataloger/executable/test-fixtures/project/lib.h b/syft/file/cataloger/executable/test-fixtures/project/lib.h new file mode 100644 index 00000000000..15f9be76b49 --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/project/lib.h @@ -0,0 +1,6 @@ +#ifndef foo_h__ +#define foo_h__ + +extern void foo(void); + +#endif // foo_h__ \ No newline at end of file diff --git a/syft/file/cataloger/executable/test-fixtures/project/main.c b/syft/file/cataloger/executable/test-fixtures/project/main.c new file mode 100644 index 00000000000..6bb5257acf9 --- /dev/null +++ b/syft/file/cataloger/executable/test-fixtures/project/main.c @@ -0,0 +1,106 @@ +#include +#include +#include + +// source: https://github.com/trailofbits/clang-cfi-showcase/blob/master/cfi_icall.c + +typedef int (*int_arg_fn)(int); +typedef int (*float_arg_fn)(float); + +static int int_arg(int arg) { + printf("In %s: (%d)\n", __FUNCTION__, arg); + return 0; +} + +static int float_arg(float arg) { + printf("CFI should protect transfer to here\n"); + printf("In %s: (%f)\n", __FUNCTION__, (double)arg); + return 0; +} + +static int bad_int_arg(int arg) { + printf("CFI will not protect transfer to here\n"); + printf("In %s: (%d)\n", __FUNCTION__, arg); + return 0; +} + +static int not_entry_point(int arg) { + // nop sled for x86 / x86-64 + // these instructions act as a buffer + // for an indirect control flow transfer to skip + // a valid function entry point, but continue + // to execute normal code + __asm__ volatile ( + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" + ); + printf("CFI ensures control flow only transfers to potentially valid destinations\n"); + printf("In %s: (%d)\n", __FUNCTION__, arg); + // need to exit or the program will segfault anyway, + // since the indirect call skipped the function preamble + exit(arg); +} + +struct foo { + int_arg_fn int_funcs[1]; + int_arg_fn bad_int_funcs[1]; + float_arg_fn float_funcs[1]; + int_arg_fn not_entries[1]; +}; + +// the struct aligns the function pointer arrays +// so indexing past the end will reliably +// call working function pointers +static struct foo f = { + .int_funcs = {int_arg}, + .bad_int_funcs = {bad_int_arg}, + .float_funcs = {float_arg}, + .not_entries = {(int_arg_fn)((uintptr_t)(not_entry_point)+0x20)} +}; + +void simple1() { + char buf[16]; + fgets(buf, sizeof(buf), stdin); + printf(buf); +} + +void simple2() { + char buf[16]; + scanf("%s", buf); +} + + +int main(int argc, char **argv) { + if(argc != 2) { + printf("Usage: %s