├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── oss-project-board-add.yaml │ ├── release-drafter.yml │ ├── tag-release.yml │ ├── test.yml │ └── update-grype-release.yml ├── .gitignore ├── .grype.yaml ├── .husky └── pre-commit ├── .jest └── setEnvVars.js ├── .nsprc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GrypeVersion.js ├── LICENSE ├── README.md ├── RELEASE.md ├── action.yml ├── dist └── index.js ├── dos2unix.js ├── download-grype └── action.yml ├── eslint.config.mjs ├── index.js ├── jest.config.js ├── package-lock.json ├── package.json ├── scripts ├── install-and-update-grype.js └── start_registry_and_push_images.sh └── tests ├── README.md ├── action.test.js ├── dist.test.js ├── fixtures ├── image-alpine-match-coverage │ ├── Dockerfile │ ├── etc │ │ └── os-release │ └── lib │ │ └── apk │ │ └── db │ │ └── installed ├── image-centos-match-coverage │ ├── Dockerfile │ ├── etc │ │ └── os-release │ └── var │ │ └── lib │ │ └── rpm │ │ ├── Packages │ │ └── generate-fixture.sh ├── image-debian-match-coverage │ ├── Dockerfile │ ├── java │ │ ├── example-java-app-maven-0.1.0.jar │ │ └── generate-fixtures.md │ ├── javascript │ │ └── pkg-json │ │ │ └── package.json │ ├── python │ │ └── dist-info │ │ │ ├── METADATA │ │ │ └── top_level.txt │ ├── ruby │ │ ├── Gemfile.lock │ │ └── specifications │ │ │ └── bundler.gemspec │ ├── usr │ │ └── lib │ │ │ └── os-release │ └── var │ │ └── lib │ │ └── dpkg │ │ └── status ├── localbuild │ └── Dockerfile ├── npm-project │ ├── package-lock.json │ └── package.json ├── test_sbom.spdx.json └── yarn-project │ ├── package.json │ └── yarn.lock ├── grype_command.test.js ├── mocks.js └── sarif_output.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | indent_size = 2 6 | max_line_length = 80 7 | ij_javascript_enforce_trailing_comma = keep 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # force unix line endings for consistent ncc output 2 | *.js text eol=lf 3 | package.json text eol=lf 4 | package-lock.json text eol=lf 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 10 8 | 9 | - package-ecosystem: npm 10 | directory: / 11 | schedule: 12 | interval: "daily" 13 | open-pull-requests-limit: 10 14 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: "v$RESOLVED_VERSION" 2 | tag-template: "v$RESOLVED_VERSION" 3 | categories: 4 | - title: "🚀 Features" 5 | labels: 6 | - "feature" 7 | - "enhancement" 8 | - title: "🐛 Bug Fixes" 9 | labels: 10 | - "fix" 11 | - "bugfix" 12 | - "bug" 13 | change-template: "- $TITLE (#$NUMBER) [[$AUTHOR](https://github.com/$AUTHOR)]" 14 | version-resolver: 15 | major: 16 | labels: 17 | - "major" 18 | minor: 19 | labels: 20 | - "minor" 21 | patch: 22 | labels: 23 | - "patch" 24 | default: patch 25 | 26 | exclude-labels: 27 | - 'changelog-ignore' 28 | 29 | autolabeler: 30 | - label: 'changelog-ignore' 31 | title: 32 | - '/chore[(]\s*deps-dev\s*[)]\s*[:].*/i' 33 | 34 | template: | 35 | ## New in scan-action v$RESOLVED_VERSION 36 | 37 | $CHANGES 38 | -------------------------------------------------------------------------------- /.github/workflows/oss-project-board-add.yaml: -------------------------------------------------------------------------------- 1 | name: Add to OSS board 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - reopened 8 | - transferred 9 | - labeled 10 | 11 | jobs: 12 | 13 | run: 14 | uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main" 15 | secrets: 16 | token: ${{ secrets.OSS_PROJECT_GH_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Draft release notes 13 | uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/tag-release.yml: -------------------------------------------------------------------------------- 1 | name: Re-tag releases 2 | 3 | on: 4 | release: 5 | types: [published, edited] 6 | 7 | jobs: 8 | actions-tagger: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: Actions-R-Us/actions-tagger@330ddfac760021349fef7ff62b372f2f691c20fb # v2.0.3 12 | with: 13 | publish_latest_tag: true 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Tests" 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | build: # make sure build/ci work properly and there is no faked build ncc built scripts 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 16 | with: 17 | node-version-file: package.json 18 | - run: npm ci 19 | - run: npm run audit 20 | - run: npm run build 21 | - run: git status --porcelain 22 | - run: git diff --ws-error-highlight=all | cat -v 23 | - run: git diff --exit-code 24 | 25 | test: 26 | runs-on: ubuntu-latest 27 | services: 28 | registry: 29 | image: registry:2 30 | ports: 31 | - 5000:5000 32 | steps: 33 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 34 | - name: Build images 35 | run: | 36 | for distro in alpine centos debian; do 37 | docker build -t localhost:5000/match-coverage/$distro ./tests/fixtures/image-$distro-match-coverage 38 | docker push localhost:5000/match-coverage/$distro:latest 39 | done 40 | - name: Inspect 41 | run: | 42 | docker images -a 43 | for distro in alpine centos debian; do 44 | docker buildx imagetools inspect localhost:5000/match-coverage/$distro:latest 45 | done 46 | - run: npm ci 47 | - run: npm run audit 48 | - run: npm run test-ci 49 | 50 | test-download-action: 51 | runs-on: ubuntu-latest 52 | 53 | steps: 54 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 55 | with: 56 | path: ./ 57 | 58 | - name: "Donwload Grype v0.54.0" 59 | id: grype 60 | uses: ./download-grype # anchore/scan-action/download-grype 61 | with: 62 | grype-version: v0.54.0 63 | 64 | - name: "Check Grype version before scan-action" 65 | run: ${{ steps.grype.outputs.cmd }} version | egrep "^Version:.*0.54.0$" 66 | 67 | - name: "Scan test image" 68 | uses: ./ 69 | with: 70 | image: "alpine:latest" 71 | fail-build: false # to prevent fail due to vuln:s on test image 72 | 73 | - name: "Check Grype version after scan-action" 74 | run: ${{ steps.grype.outputs.cmd }} version | egrep "^Version:.*0.54.0$" 75 | 76 | test-all: 77 | strategy: 78 | matrix: 79 | config: [ 80 | {image: 'alpine:latest'}, 81 | {path: 'tests/fixtures/npm-project'}, 82 | {sbom: 'tests/fixtures/test_sbom.spdx.json'}, 83 | ] 84 | os: [ubuntu-latest, windows-latest, macos-latest] 85 | output-format: [sarif, json, table] 86 | runs-on: ${{ matrix.os }} 87 | steps: 88 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 89 | - uses: ./ 90 | id: scan 91 | with: 92 | image: ${{ matrix.config.image }} 93 | path: ${{ matrix.config.path }} 94 | sbom: ${{ matrix.config.sbom }} 95 | output-format: ${{ matrix.output-format }} 96 | fail-build: false 97 | 98 | - name: Validate file exists 99 | if: ${{ matrix.output-format != 'table' }} 100 | run: test -f '${{ steps.scan.outputs[matrix.output-format] }}' 101 | -------------------------------------------------------------------------------- /.github/workflows/update-grype-release.yml: -------------------------------------------------------------------------------- 1 | name: PR for latest Grype release 2 | on: 3 | schedule: 4 | # 7:04 UTC (2:04 am EST) 5 | - cron: "4 7 * * *" 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | jobs: 11 | upgrade-grype: 12 | runs-on: ubuntu-latest 13 | if: github.repository == 'anchore/scan-action' 14 | steps: 15 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 16 | - name: Get latest Grype version 17 | id: latest-version 18 | env: 19 | GITHUB_TOKEN: ${{ github.token }} 20 | run: | 21 | LATEST_VERSION=$(gh release view --json name -q '.name' -R anchore/grype) 22 | echo "exports.GRYPE_VERSION = \"$LATEST_VERSION\";" > GrypeVersion.js 23 | # install husky hooks and dependencies: 24 | npm install 25 | npm run build 26 | # export the version for use with create-pull-request: 27 | echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT 28 | - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0 29 | id: generate-token 30 | with: 31 | app_id: ${{ secrets.TOKEN_APP_ID }} 32 | private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} 33 | - uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 34 | with: 35 | signoff: true 36 | delete-branch: true 37 | branch: auto/latest-grype 38 | labels: dependencies 39 | commit-message: "chore(deps): update Grype to ${{ steps.latest-version.outputs.LATEST_VERSION }}" 40 | title: "chore(deps): update Grype to ${{ steps.latest-version.outputs.LATEST_VERSION }}" 41 | body: "Update Grype to ${{ steps.latest-version.outputs.LATEST_VERSION }}" 42 | token: ${{ steps.generate-token.outputs.token }} 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # tests 2 | tests/functional/output 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | anchore-reports/ 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules/ 46 | jspm_packages/ 47 | 48 | # TypeScript v1 declaration files 49 | typings/ 50 | 51 | # TypeScript cache 52 | *.tsbuildinfo 53 | 54 | # Optional npm cache directory 55 | .npm 56 | 57 | # Optional eslint cache 58 | .eslintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variables file 76 | .env 77 | .env.test 78 | 79 | # parcel-bundler cache (https://parceljs.org/) 80 | .cache 81 | 82 | # Next.js build output 83 | .next 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | 88 | # Gatsby files 89 | .cache/ 90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 91 | # https://nextjs.org/blog/next-9-1#public-directory-support 92 | # public 93 | 94 | # vuepress build output 95 | .vuepress/dist 96 | 97 | # Serverless directories 98 | .serverless/ 99 | 100 | # FuseBox cache 101 | .fusebox/ 102 | 103 | # DynamoDB Local files 104 | .dynamodb/ 105 | 106 | # TernJS port file 107 | .tern-port 108 | 109 | # IDE files/dirs 110 | .vscode 111 | .idea 112 | 113 | # Exclude python test artifacts 114 | /act 115 | /venv/* 116 | /tests/functional/__pycache__/* 117 | 118 | # grype db for tests 119 | /grype-db 120 | /grype 121 | 122 | # Action temporary files 123 | /results.* 124 | -------------------------------------------------------------------------------- /.grype.yaml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - vulnerability: CVE-2021-32804 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | npm run precommit 6 | -------------------------------------------------------------------------------- /.jest/setEnvVars.js: -------------------------------------------------------------------------------- 1 | process.env["RUNNER_TOOL_CACHE"] = "/tmp/actions/cache"; 2 | process.env["RUNNER_TEMP"] = "/tmp/actions/temp"; 3 | -------------------------------------------------------------------------------- /.nsprc: -------------------------------------------------------------------------------- 1 | { 2 | "1092310": { 3 | "active": true, 4 | "notes": "Ignored since we don't use the vulnerable regex method" 5 | }, 6 | "1092460": { 7 | "active": true, 8 | "notes": "We are not using untrusted user data for any regexes in scan-action" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | [opensource@anchore.com](mailto:opensource@anchore.com). 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to scan-action 2 | 3 | If you are looking to contribute to this project and want to open a GitHub pull request ("PR"), there are a few guidelines of what we are looking for in patches. Make sure you go through this document and ensure that your code proposal is aligned. 4 | 5 | ## Sign off your work 6 | 7 | The `sign-off` is an added line at the end of the explanation for the commit, certifying that you wrote it or otherwise have the right to submit it as an open-source patch. By submitting a contribution, you agree to be bound by the terms of the DCO Version 1.1 and Apache License Version 2.0. 8 | 9 | Signing off a commit certifies the below Developer's Certificate of Origin (DCO): 10 | 11 | ```text 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. 37 | ``` 38 | 39 | All contributions to this project are licensed under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/). 40 | 41 | When committing your change, you can add the required line manually so that it looks like this: 42 | 43 | ```text 44 | Signed-off-by: John Doe 45 | ``` 46 | 47 | Alternatively, configure your Git client with your name and email to use the `-s` flag when creating a commit: 48 | 49 | ```text 50 | $ git config --global user.name "John Doe" 51 | $ git config --global user.email "john.doe@example.com" 52 | ``` 53 | 54 | Creating a signed-off commit is then possible with `-s` or `--signoff`: 55 | 56 | ```text 57 | $ git commit -s -m "this is a commit message" 58 | ``` 59 | 60 | To double-check that the commit was signed-off, look at the log output: 61 | 62 | ```text 63 | $ git log -1 64 | commit 37ceh170e4hb283bb73d958f2036ee5k07e7fde7 (HEAD -> issue-35, origin/main, main) 65 | Author: John Doe 66 | Date: Mon Aug 1 11:27:13 2020 -0400 67 | 68 | this is a commit message 69 | 70 | Signed-off-by: John Doe 71 | ``` 72 | 73 | [//]: # "TODO: Commit guidelines, granular commits" 74 | [//]: # "TODO: Commit guidelines, descriptive messages" 75 | [//]: # "TODO: Commit guidelines, commit title, extra body description" 76 | [//]: # "TODO: PR title and description" 77 | 78 | ## Test your changes 79 | 80 | Ensure that your changes have passed the test suite. For more information on working with this project's tests, see [Developing Tests](./tests/README.md#developing-tests). 81 | 82 | ## Document your changes 83 | 84 | When proposed changes are modifying user-facing functionality or output, it is expected the PR will include updates to the documentation as well. 85 | -------------------------------------------------------------------------------- /GrypeVersion.js: -------------------------------------------------------------------------------- 1 | exports.GRYPE_VERSION = "v0.92.0"; 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Anchore, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Action for Vulnerability Scanning 2 | 3 | **:zap: _Find threats in files or containers at lightning speed_ :zap:** 4 | 5 | [![Test Status][test-img]][test] 6 | [![GitHub release](https://img.shields.io/github/release/anchore/scan-action.svg)](https://github.com/anchore/scan-action/releases/latest) 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/anchore/scan-action/blob/main/LICENSE) 8 | [![Join our Discourse](https://img.shields.io/badge/Discourse-Join-blue?logo=discourse)](https://anchore.com/discourse) 9 | 10 | 11 | This is a GitHub Action for invoking the [Grype](https://github.com/anchore/grype) scanner and returning the vulnerabilities found, 12 | and optionally fail if a vulnerability is found with a configurable severity level. 13 | 14 | Use this in your workflows to quickly verify files or containers' content after a build and before pushing, allowing PRs, or deploying updates. 15 | 16 | The action invokes the `grype` command-line tool, with these benefits: 17 | 18 | - Runs locally, without sending data outbound - no credentials required! 19 | - Speedy scan operations 20 | - Scans both paths and container images 21 | - Easy failure evaluation depending on vulnerability severity 22 | 23 | The example workflows have lots of usage examples for scanning both containers and directories. 24 | 25 | By default, a scan will produce very detailed output on system packages like an RPM or DEB, but also language-based packages. These are some of the supported packages and libraries: 26 | 27 | Supported Linux Distributions: 28 | 29 | - Alpine 30 | - BusyBox 31 | - CentOS and RedHat 32 | - Debian and Debian-based distros like Ubuntu 33 | 34 | Supported packages and libraries: 35 | 36 | - Ruby Bundles 37 | - Python Wheel, Egg, `requirements.txt` 38 | - JavaScript NPM/Yarn 39 | - Java JAR/EAR/WAR, Jenkins plugins JPI/HPI 40 | - Go modules 41 | 42 | ## Container scanning 43 | 44 | The simplest workflow for scanning a `localbuild/testimage` container: 45 | 46 | ```yaml 47 | - name: Set up Docker Buildx 48 | uses: docker/setup-buildx-action@v2 49 | 50 | - name: build local container 51 | uses: docker/build-push-action@v4 52 | with: 53 | tags: localbuild/testimage:latest 54 | push: false 55 | load: true 56 | 57 | - name: Scan image 58 | uses: anchore/scan-action@v6 59 | with: 60 | image: "localbuild/testimage:latest" 61 | ``` 62 | 63 | ## Directory scanning 64 | 65 | To scan a directory, add the following step: 66 | 67 | ```yaml 68 | - name: Scan current project 69 | uses: anchore/scan-action@v6 70 | with: 71 | path: "." 72 | ``` 73 | 74 | The `path` key allows any valid path for the current project. The root of the path (`"."` in this example) is the repository root. 75 | 76 | ## Scanning an SBOM file 77 | 78 | Use the `sbom` key to scan an SBOM file: 79 | 80 | ```yaml 81 | - name: Create SBOM 82 | uses: anchore/sbom-action@v0 83 | with: 84 | format: spdx-json 85 | output-file: "${{ github.event.repository.name }}-sbom.spdx.json" 86 | 87 | - name: Scan SBOM 88 | uses: anchore/scan-action@v6 89 | with: 90 | sbom: "${{ github.event.repository.name }}-sbom.spdx.json" 91 | ``` 92 | 93 | ## Failing a build on vulnerability severity 94 | 95 | By default, if any vulnerability at `medium` or higher is seen, the build fails. To have the build step fail in cases where there are vulnerabilities with a severity level different than the default, set the `severity-cutoff` field to one of `low`, `high`, or `critical`: 96 | 97 | With a different severity level: 98 | 99 | ```yaml 100 | - name: Scan image 101 | uses: anchore/scan-action@v6 102 | with: 103 | image: "localbuild/testimage:latest" 104 | fail-build: true 105 | severity-cutoff: critical 106 | ``` 107 | 108 | Optionally, change the `fail-build` field to `false` to avoid failing the build regardless of severity: 109 | 110 | ```yaml 111 | - name: Scan image 112 | uses: anchore/scan-action@v6 113 | with: 114 | image: "localbuild/testimage:latest" 115 | fail-build: false 116 | ``` 117 | 118 | ### Action Inputs 119 | 120 | The inputs `image`, `path`, and `sbom` are mutually exclusive to specify the source to scan; all the other keys are optional. These are all the available keys to configure this action, along with the defaults: 121 | 122 | | Input Name | Description | Default Value | 123 | |---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| 124 | | `image` | The image to scan | N/A | 125 | | `path` | The file path to scan | N/A | 126 | | `sbom` | The SBOM to scan | N/A | 127 | | `registry-username` | The registry username to use when authenticating to an external registry | | 128 | | `registry-password` | The registry password to use when authenticating to an external registry | | 129 | | `fail-build` | Fail the build if a vulnerability is found with a higher severity. That severity defaults to `medium` and can be set with `severity-cutoff`. | `true` | 130 | | `output-format` | Set the output parameter after successful action execution. Valid choices are `json`, `sarif`, `cyclonedx-xml`, `cyclonedx-json`, and `table`; where `table` output will also display in the logs. | `sarif` | 131 | | `output-file` | File to output the Grype scan results to. Defaults to a file in the system temp directory, available in the action outputs | | 132 | | `severity-cutoff` | Optionally specify the minimum vulnerability severity to trigger a failure. Valid choices are "negligible", "low", "medium", "high" and "critical". Any vulnerability with a severity less than this value will lead to a "warning" result. Default is "medium". | `medium` | 133 | | `only-fixed` | Specify whether to only report vulnerabilities that have a fix available. | `false` | 134 | | `add-cpes-if-none` | Specify whether to autogenerate missing CPEs. | `false` | 135 | | `by-cve` | Specify whether to orient results by CVE rather than GHSA. | `false` | 136 | | `vex` | Specify a list of VEX documents to consider when producing scanning results. | `false` | 137 | | `cache-db` | Cache the Grype DB in GitHub action cache and restore before checking for updates | `false` | 138 | | `grype-version` | An optional Grype version to download, defaults to the pinned version in [GrypeVersion.js](GrypeVersion.js). | | 139 | 140 | ### Action Outputs 141 | 142 | | Output Name | Description | Type | 143 | |------------------|--------------------------------------------------------------------------------|--------| 144 | | `sarif` | Path to the SARIF report file, if `output-format` is `sarif` | string | 145 | | `json` | Path to the report file , if `output-format` is `json` | string | 146 | | `cyclonedx-xml` | Path to the CycloneDX report file, if `output-format` is `cyclonedx` | string | 147 | | `cyclonedx-json` | Path to the CycloneDX JSON report file, if `output-format` is `cyclonedx-json` | string | 148 | 149 | ### Example Workflows 150 | 151 | Assuming your repository has a Dockerfile in the root directory: 152 | 153 | ```yaml 154 | name: Container Image CI 155 | on: [push] 156 | jobs: 157 | build: 158 | runs-on: ubuntu-latest 159 | steps: 160 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 161 | - name: Build the container image 162 | run: docker build . --file Dockerfile --tag localbuild/testimage:latest 163 | - uses: anchore/scan-action@v6 164 | with: 165 | image: "localbuild/testimage:latest" 166 | fail-build: true 167 | ``` 168 | 169 | Same example as above, but with SARIF output format - as is the default, the action will generate a SARIF report, which can be uploaded and then displayed as a Code Scanning Report in the GitHub UI. 170 | 171 | > :bulb: Code Scanning is a Github service that is currently in Beta. [Follow the instructions on how to enable this service for your project](https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/enabling-code-scanning-for-a-repository). 172 | 173 | ```yaml 174 | name: Container Image CI 175 | on: [push] 176 | jobs: 177 | build: 178 | runs-on: ubuntu-latest 179 | # Permissions key is required for CodeQL SARIF Upload, per the docs: 180 | # https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github 181 | permissions: 182 | security-events: write 183 | steps: 184 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 185 | - name: Build the Container image 186 | run: docker build . --file Dockerfile --tag localbuild/testimage:latest 187 | - uses: anchore/scan-action@v6 188 | id: scan 189 | with: 190 | image: "localbuild/testimage:latest" 191 | - name: upload Anchore scan SARIF report 192 | uses: github/codeql-action/upload-sarif@v3 193 | with: 194 | sarif_file: ${{ steps.scan.outputs.sarif }} 195 | ``` 196 | 197 | Optionally, you can add a step to inspect the SARIF report produced: 198 | 199 | ```yaml 200 | - name: Inspect action SARIF report 201 | run: cat ${{ steps.scan.outputs.sarif }} 202 | ``` 203 | 204 | ## Additional configuration 205 | 206 | You may add a `.grype.yaml` file at your repository root 207 | for more [Grype configuration](https://github.com/anchore/grype#configuration) 208 | such as [ignoring certain matches](https://github.com/anchore/grype#specifying-matches-to-ignore). 209 | 210 | ## anchore/scan-action/download-grype 211 | 212 | A sub-action to [download Grype](download-grype/action.yml) and optionally cache the Grype DB. 213 | 214 | Input parameters: 215 | 216 | | Parameter | Description | Default | 217 | |-----------------|--------------------------------------------------------------------------------------------------------------|---------| 218 | | `grype-version` | An optional Grype version to download, defaults to the pinned version in [GrypeVersion.js](GrypeVersion.js). | | 219 | | `cache-db` | Cache the Grype DB in GitHub action cache and restore before checking for updates | `false` | 220 | 221 | Output parameters: 222 | 223 | | Parameter | Description | 224 | | --------- | -------------------------------------------------------------------- | 225 | | `cmd` | a reference to the [Grype](https://github.com/anchore/grype) binary. | 226 | 227 | `cmd` can be referenced in a workflow like other output parameters: 228 | `${{ steps..outputs.cmd }}` 229 | 230 | Example usage: 231 | 232 | ```yaml 233 | - uses: anchore/scan-action/download-grype@v3 234 | id: grype 235 | - run: ${{steps.grype.outputs.cmd}} dir:. 236 | ``` 237 | 238 | ## Contributing 239 | 240 | We love contributions, feedback, and bug reports. For issues with the invocation of this action, file [issues](https://github.com/anchore/scan-action/issues) in this repository. 241 | 242 | For contributing, see [Contributing](CONTRIBUTING.md). 243 | 244 | ## More Information 245 | 246 | For documentation on Grype itself, including other output capabilities, see the [grype project](https://github.com/anchore/grype) 247 | 248 | Connect with the community directly on [Discourse](https://anchore.com/discourse). 249 | 250 | [test]: https://github.com/anchore/scan-action 251 | [test-img]: https://github.com/anchore/scan-action/workflows/Tests/badge.svg 252 | 253 | ## Diagnostics 254 | 255 | This action makes extensive use of GitHub Action debug logging, 256 | which can be enabled as [described here](https://github.com/actions/toolkit/blob/master/docs/action-debugging.md) 257 | by setting a secret in your repository of `ACTIONS_STEP_DEBUG` to `true`. 258 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | A release of scan-action comprises: 4 | - a new semver git tag from the current tip of the main branch 5 | - a new [github release](https://github.com/anchore/scan-action/releases) with a changelog 6 | - the action distributable committed into the repo at `dist/` 7 | 8 | Ideally releasing should be done often with small increments when possible. Unless a 9 | breaking change is blocking the release, or no fixes/features have been merged, a good 10 | target release cadence is between every 1 or 2 weeks. 11 | 12 | 13 | ## Creating a release 14 | 15 | Releases are automatically drafted on every push to the main branch. Please see the [github releases page](https://github.com/anchore/scan-action/releases) for the latest draft. To publish the release: 16 | 17 | - Click "edit" (the pencil icon) 18 | - Modify the changelog as needed (for instance, if grype was bumped multiple times, include only the latest version bump entry) 19 | - Click "publish" 20 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: "Anchore Container Scan" 2 | description: "Scan docker containers with Grype for vulnerabilities" 3 | branding: 4 | color: blue 5 | icon: check-circle 6 | inputs: 7 | image: 8 | description: 'The image to scan. This option is mutually exclusive with "path" and "sbom". ' 9 | required: false 10 | path: 11 | description: 'The path to scan. This option is mutually exclusive with "image" and "sbom".' 12 | required: false 13 | sbom: 14 | description: 'The SBOM file to scan. This option is mutually exclusive with "path" and "image".' 15 | required: false 16 | fail-build: 17 | description: "Set to false to avoid failing based on severity-cutoff. Default is to fail when severity-cutoff is reached (or surpassed)" 18 | required: false 19 | default: "true" 20 | output-format: 21 | description: 'Set the output parameter after successful action execution. Valid choices are "json", "sarif", "cyclonedx", "cyclonedx-json" and "table".' 22 | required: false 23 | default: "sarif" 24 | output-file: 25 | description: 'The file to output the grype scan results to' 26 | required: false 27 | severity-cutoff: 28 | description: 'Optionally specify the minimum vulnerability severity to trigger an "error" level ACS result. Valid choices are "negligible", "low", "medium", "high" and "critical". Any vulnerability with a severity less than this value will lead to a "warning" result. Default is "medium".' 29 | required: false 30 | default: "medium" 31 | only-fixed: 32 | description: "Specify whether to only report vulnerabilities that have a fix available. Default is false." 33 | required: false 34 | default: "false" 35 | add-cpes-if-none: 36 | description: "Specify whether to autogenerate missing CPEs. Default is false." 37 | required: false 38 | default: "false" 39 | by-cve: 40 | description: "Specify whether to orient results by CVE rather than GHSA. Default is false." 41 | required: false 42 | default: "false" 43 | grype-version: 44 | description: "A specific version of Grype to install" 45 | required: false 46 | vex: 47 | description: "Specify a list of VEX documents to consider when producing scanning results." 48 | required: false 49 | cache-db: 50 | description: "Cache the Grype DB in GitHub action cache and restore before checking for updates" 51 | required: false 52 | outputs: 53 | sarif: 54 | description: "Path to a SARIF report file for the scan" 55 | json: 56 | description: "Path to a JSON report file for the scan" 57 | cyclonedx-xml: 58 | description: "Path to a CycloneDX XML report file for the scan" 59 | cyclonedx-json: 60 | description: "Path to a CycloneDX JSON report file for the scan" 61 | runs: 62 | using: "node20" 63 | main: "dist/index.js" 64 | -------------------------------------------------------------------------------- /dos2unix.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | for (const f of process.argv.slice(2)) { 3 | console.log("dos2unix.js", f); 4 | let contents = fs.readFileSync(f, { encoding: "utf-8" }); 5 | contents = contents.replace(/\r/g, ""); 6 | fs.writeFileSync(f, contents, { encoding: "utf-8" }); 7 | } 8 | -------------------------------------------------------------------------------- /download-grype/action.yml: -------------------------------------------------------------------------------- 1 | name: "Download Grype" 2 | author: "Anchore" 3 | description: "Downloads the Grype binary and provides a path to execute it" 4 | branding: 5 | color: blue 6 | icon: check-circle 7 | inputs: 8 | grype-version: 9 | description: "A specific version of Grype to install" 10 | required: false 11 | run: 12 | description: "Flag to indicate which sub-action to run" 13 | required: false 14 | default: "download-grype" 15 | cache-db: 16 | description: "Cache the Grype DB in GitHub action cache and restore before checking for updates" 17 | required: false 18 | outputs: 19 | cmd: 20 | description: "An absolute path to the Grype executable" 21 | runs: 22 | using: "node20" 23 | main: "../dist/index.js" 24 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import path from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | import js from "@eslint/js"; 5 | import { FlatCompat } from "@eslint/eslintrc"; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | const compat = new FlatCompat({ 10 | baseDirectory: __dirname, 11 | recommendedConfig: js.configs.recommended, 12 | allConfig: js.configs.all 13 | }); 14 | 15 | export default [...compat.extends("eslint:recommended"), { 16 | languageOptions: { 17 | globals: { 18 | ...globals.commonjs, 19 | ...globals.jest, 20 | ...globals.node, 21 | Atomics: "readonly", 22 | SharedArrayBuffer: "readonly", 23 | }, 24 | 25 | ecmaVersion: 2018, 26 | sourceType: "commonjs", 27 | }, 28 | 29 | rules: {}, 30 | }]; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const tools = require("@actions/tool-cache"); 2 | const core = require("@actions/core"); 3 | const cache = require("@actions/cache"); 4 | const exec = require("@actions/exec"); 5 | const fs = require("fs"); 6 | const os = require("os"); 7 | const path = require("path"); 8 | const process = require("process"); 9 | const stream = require("stream"); 10 | const { GRYPE_VERSION } = require("./GrypeVersion"); 11 | 12 | const grypeVersion = core.getInput("grype-version") || GRYPE_VERSION; 13 | const grypeExecutableName = isWindows() ? "grype.exe" : "grype"; 14 | 15 | async function downloadGrypeWindowsWorkaround(version) { 16 | const versionNoV = version.replace(/^v/, ""); 17 | // example URL: https://github.com/anchore/grype/releases/download/v0.79.2/grype_0.79.2_windows_amd64.zip 18 | const url = `https://github.com/anchore/grype/releases/download/${version}/grype_${versionNoV}_windows_amd64.zip`; 19 | core.info(`Downloading grype from ${url}`); 20 | const zipPath = await tools.downloadTool(url); 21 | core.debug(`Zip saved to ${zipPath}`); 22 | const toolDir = await tools.extractZip(zipPath); 23 | core.debug(`Zip extracted to ${toolDir}`); 24 | const binaryPath = path.join(toolDir, grypeExecutableName); 25 | core.debug(`Grype path is ${binaryPath}`); 26 | return binaryPath; 27 | } 28 | 29 | function isWindows() { 30 | return process.platform === "win32"; 31 | } 32 | 33 | /* download grype and return a path to the executable */ 34 | async function downloadGrype(version) { 35 | if (isWindows()) { 36 | return await downloadGrypeWindowsWorkaround(version); 37 | } 38 | 39 | const installScriptUrl = `https://raw.githubusercontent.com/anchore/grype/main/install.sh`; 40 | core.info(`Downloading grype ${version} via ${installScriptUrl}`); 41 | 42 | // TODO: when grype starts supporting unreleased versions, support it here 43 | // Download the installer, and run 44 | const installScriptPath = await tools.downloadTool(installScriptUrl); 45 | const installToDir = fs.mkdtempSync( 46 | path.join(os.tmpdir(), "grype-download-"), 47 | ); 48 | 49 | const { stdout, exitCode } = await runCommand("sh", [ 50 | installScriptPath, 51 | "-d", 52 | "-b", 53 | installToDir, 54 | version, 55 | ]); 56 | if (exitCode !== 0) { 57 | core.error("Error installing grype:"); 58 | core.error(stdout); 59 | throw new Error("error installing grype"); 60 | } 61 | return path.join(installToDir, isWindows() ? "grype.exe" : "grype"); 62 | } 63 | 64 | async function installGrype(version) { 65 | core.info(`Installing grype ${version}`); 66 | 67 | let grypePath = tools.find(grypeExecutableName, version); 68 | if (!grypePath) { 69 | // Not found, install it 70 | grypePath = await downloadGrype(version); 71 | // Cache the downloaded file, get path to directory 72 | grypePath = await tools.cacheFile( 73 | grypePath, 74 | grypeExecutableName, 75 | grypeExecutableName, 76 | version, 77 | ); 78 | } 79 | return path.join(grypePath, grypeExecutableName); 80 | } 81 | 82 | // Determines if multiple arguments are defined 83 | function multipleDefined(...args) { 84 | let defined = false; 85 | for (const a of args) { 86 | if (defined && a) { 87 | return true; 88 | } 89 | if (a) { 90 | defined = true; 91 | } 92 | } 93 | return false; 94 | } 95 | 96 | function sourceInput() { 97 | const image = core.getInput("image"); 98 | const path = core.getInput("path"); 99 | const sbom = core.getInput("sbom"); 100 | 101 | if (multipleDefined(image, path, sbom)) { 102 | throw new Error( 103 | "The following options are mutually exclusive: image, path, sbom", 104 | ); 105 | } 106 | 107 | if (image) { 108 | return image; 109 | } 110 | 111 | if (sbom) { 112 | return "sbom:" + sbom; 113 | } 114 | 115 | if (!path) { 116 | // Default to the CWD 117 | return "dir:."; 118 | } 119 | 120 | return "dir:" + path; 121 | } 122 | 123 | async function run() { 124 | try { 125 | core.debug(new Date().toTimeString()); 126 | // Grype accepts several input options, initially this action is supporting both `image` and `path`, so 127 | // a check must happen to ensure one is selected at least, and then return it 128 | const source = sourceInput(); 129 | const failBuild = core.getInput("fail-build") || "true"; 130 | const outputFormat = core.getInput("output-format") || "sarif"; 131 | const severityCutoff = core.getInput("severity-cutoff") || "medium"; 132 | const onlyFixed = core.getInput("only-fixed") || "false"; 133 | const addCpesIfNone = core.getInput("add-cpes-if-none") || "false"; 134 | const byCve = core.getInput("by-cve") || "false"; 135 | const vex = core.getInput("vex") || ""; 136 | const cacheDb = core.getInput("cache-db") || "false"; 137 | const outputFile = core.getInput("output-file") || ""; 138 | const out = await runScan({ 139 | source, 140 | failBuild, 141 | severityCutoff, 142 | onlyFixed, 143 | outputFile, 144 | outputFormat, 145 | addCpesIfNone, 146 | byCve, 147 | vex, 148 | cacheDb, 149 | }); 150 | Object.keys(out).map((key) => { 151 | core.setOutput(key, out[key]); 152 | }); 153 | } catch (error) { 154 | core.setFailed(error.message); 155 | } 156 | } 157 | 158 | async function getDbDir(grypeCommand) { 159 | const { stdout } = await runCommand( 160 | grypeCommand, 161 | ["config", "--load"], 162 | process.env, 163 | ); 164 | for (let line of stdout.split("\n")) { 165 | line = line.trim(); 166 | if (line.startsWith("cache-dir:")) { 167 | line = line.replace("cache-dir:", ""); 168 | line = line.replace("'~", os.homedir()); 169 | line = line.replaceAll("'", ""); 170 | line = line.trim(); 171 | return line; 172 | } 173 | } 174 | throw new Error("unable to get grype db cache directory"); 175 | } 176 | 177 | async function getDbBuildTime(grypeCommand) { 178 | const { stdout, stderr, exitCode } = await runCommand( 179 | grypeCommand, 180 | ["db", "status", "-vv"], 181 | process.env, 182 | ); 183 | if (exitCode !== 0) { 184 | core.debug("nonzero exit from grype db status; exitCode: " + exitCode); 185 | core.debug("stdout:"); 186 | core.debug(stdout); 187 | core.debug("stderr:"); 188 | core.debug(stderr); 189 | return; 190 | } 191 | for (let line of stdout.split("\n")) { 192 | line = line.trim(); 193 | if (line.startsWith("Built:")) { 194 | line = line.replace("Built:", ""); 195 | // 2024-07-25 01:30:47 +0000 UTC 196 | return new Date(line.trim()); 197 | } 198 | } 199 | } 200 | 201 | async function updateDb(grypeCommand) { 202 | const { stdout, exitCode } = await runCommand( 203 | grypeCommand, 204 | ["db", "update", "-vv"], 205 | process.env, 206 | ); 207 | if (exitCode !== 0) { 208 | throw new Error("unable to update db: " + stdout); 209 | } 210 | } 211 | 212 | // attempts to get an up-to-date database and from cache or update it, 213 | // throws an exception if unable to get a database or use the cache 214 | async function updateDbWithCache(grypeCommand) { 215 | if (!cache.isFeatureAvailable()) { 216 | throw new Error("cache not available"); 217 | } 218 | 219 | const cacheDir = await getDbDir(grypeCommand); 220 | 221 | // we want the cache to be shared by as many compatible branches as possible, so do not use a 222 | // unique key across matrix builds. even when there is a timing conflict, there is a database 223 | // available as expected 224 | // see: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key 225 | const cacheKey = `grype-db-${grypeVersion}`; 226 | await cache.restoreCache([cacheDir], cacheKey, [], {}, true); 227 | 228 | const cachedDbBuildTime = await getDbBuildTime(grypeCommand); 229 | if (cachedDbBuildTime) { 230 | core.info( 231 | `Restored grype db from cache with db build time ${cachedDbBuildTime}`, 232 | ); 233 | } 234 | 235 | // updateDb will throw an exception on error and potentially skip downloading 236 | // a database if no update exists 237 | await updateDb(grypeCommand); 238 | 239 | // if the database was not updated, don't re-cache it 240 | const currentDbBuildTime = await getDbBuildTime(grypeCommand); 241 | if (`${cachedDbBuildTime}` === `${currentDbBuildTime}`) { 242 | core.debug( 243 | `Skipping caching grype db, with build time ${cachedDbBuildTime}`, 244 | ); 245 | return; 246 | } 247 | 248 | core.debug(`Caching grype db with key ${cacheKey}`); 249 | 250 | // this needs to be able to be found by restoreCache, above 251 | await cache.saveCache([cacheDir], cacheKey, {}, true); 252 | } 253 | 254 | async function runCommand(cmd, cmdArgs, env) { 255 | let stdout = ""; 256 | let stderr = ""; 257 | 258 | // This /dev/null writable stream is required so the entire Grype output 259 | // is not written to the GitHub action log. the listener below 260 | // will actually capture the output 261 | const outStream = new stream.Writable({ 262 | write(buffer, encoding, next) { 263 | next(); 264 | }, 265 | }); 266 | 267 | const exitCode = await core.group(`${cmd} ${cmdArgs.join(" ")}`, () => { 268 | return exec.exec(cmd, cmdArgs, { 269 | env, 270 | ignoreReturnCode: true, 271 | outStream, 272 | listeners: { 273 | stdout(buffer) { 274 | stdout += buffer.toString(); 275 | }, 276 | stderr(buffer) { 277 | stderr += buffer.toString(); 278 | }, 279 | debug(message) { 280 | core.debug(message); 281 | }, 282 | }, 283 | }); 284 | }); 285 | 286 | core.debug(stdout); 287 | 288 | return { stdout, stderr, exitCode }; 289 | } 290 | 291 | async function runScan({ 292 | source, 293 | failBuild, 294 | severityCutoff, 295 | onlyFixed, 296 | outputFile, 297 | outputFormat, 298 | addCpesIfNone, 299 | byCve, 300 | vex, 301 | cacheDb, 302 | }) { 303 | const out = {}; 304 | 305 | const env = { 306 | ...process.env, 307 | GRYPE_CHECK_FOR_APP_UPDATE: "false", 308 | }; 309 | 310 | const registryUser = core.getInput("registry-username"); 311 | const registryPass = core.getInput("registry-password"); 312 | 313 | if (registryUser || registryPass) { 314 | env.GRYPE_REGISTRY_AUTH_USERNAME = registryUser; 315 | env.GRYPE_REGISTRY_AUTH_PASSWORD = registryPass; 316 | if (!registryUser || !registryPass) { 317 | core.warning( 318 | "WARNING: registry-username and registry-password must be specified together", 319 | ); 320 | } 321 | } 322 | 323 | const SEVERITY_LIST = ["negligible", "low", "medium", "high", "critical"]; 324 | const FORMAT_LIST = [ 325 | "sarif", 326 | "json", 327 | "table", 328 | "cyclonedx-xml", 329 | "cyclonedx-json", 330 | ]; 331 | let cmdArgs = []; 332 | 333 | if (core.isDebug()) { 334 | cmdArgs.push(`-vv`); 335 | } 336 | 337 | failBuild = failBuild.toLowerCase() === "true"; 338 | onlyFixed = onlyFixed.toLowerCase() === "true"; 339 | addCpesIfNone = addCpesIfNone.toLowerCase() === "true"; 340 | byCve = byCve.toLowerCase() === "true"; 341 | cacheDb = cache.isFeatureAvailable() && cacheDb.toLowerCase() === "true"; 342 | 343 | cmdArgs.push("-o", outputFormat); 344 | 345 | // always output to a file, this is read later to print table output 346 | if (!outputFile) { 347 | outputFile = path.join( 348 | fs.mkdtempSync(path.join(os.tmpdir(), "grype-")), 349 | "output", 350 | ); 351 | } 352 | cmdArgs.push("--file", outputFile); 353 | 354 | if ( 355 | !SEVERITY_LIST.some( 356 | (item) => 357 | typeof severityCutoff.toLowerCase() === "string" && 358 | item === severityCutoff.toLowerCase(), 359 | ) 360 | ) { 361 | throw new Error( 362 | `Invalid severity-cutoff value is set to ${severityCutoff} - must be one of ${SEVERITY_LIST.join(", ")}`, 363 | ); 364 | } 365 | if ( 366 | !FORMAT_LIST.some( 367 | (item) => 368 | typeof outputFormat.toLowerCase() === "string" && 369 | item === outputFormat.toLowerCase(), 370 | ) 371 | ) { 372 | throw new Error( 373 | `Invalid output-format value is set to ${outputFormat} - must be one of: ${FORMAT_LIST.join(", ")}`, 374 | ); 375 | } 376 | 377 | core.debug(`Installing grype version ${grypeVersion}`); 378 | const grypeCommand = await installGrype(grypeVersion); 379 | 380 | if (cacheDb) { 381 | await updateDbWithCache(grypeCommand); 382 | // since the db was updated and cached separately, skip when running grype 383 | env.GRYPE_DB_AUTO_UPDATE = "false"; 384 | } 385 | 386 | core.debug("Source: " + source); 387 | core.debug("Fail Build: " + failBuild); 388 | core.debug("Severity Cutoff: " + severityCutoff); 389 | core.debug("Only Fixed: " + onlyFixed); 390 | core.debug("Add Missing CPEs: " + addCpesIfNone); 391 | core.debug("Orient by CVE: " + byCve); 392 | core.debug("Output Format: " + outputFormat); 393 | core.debug("Cache DB: " + cacheDb); 394 | 395 | core.debug("Creating options for GRYPE analyzer"); 396 | 397 | // Run the grype analyzer 398 | if (severityCutoff !== "") { 399 | cmdArgs.push("--fail-on"); 400 | cmdArgs.push(severityCutoff.toLowerCase()); 401 | } 402 | if (onlyFixed === true) { 403 | cmdArgs.push("--only-fixed"); 404 | } 405 | if (addCpesIfNone === true) { 406 | cmdArgs.push("--add-cpes-if-none"); 407 | } 408 | if (byCve === true) { 409 | cmdArgs.push("--by-cve"); 410 | } 411 | if (vex) { 412 | cmdArgs.push("--vex"); 413 | cmdArgs.push(vex); 414 | } 415 | cmdArgs.push(source); 416 | 417 | const { exitCode } = await runCommand(grypeCommand, cmdArgs, env); 418 | 419 | out[outputFormat] = outputFile; 420 | if (outputFormat === "table") { 421 | try { 422 | const report = fs.readFileSync(outputFile); 423 | core.info(report.toString()); 424 | } catch (e) { 425 | core.warning(`error writing table output contents: ${e}`); 426 | } 427 | } 428 | 429 | // If there is a non-zero exit status code there are a couple of potential reporting paths 430 | if (exitCode > 0) { 431 | if (!severityCutoff) { 432 | // There was a non-zero exit status but it wasn't because of failing severity, this must be 433 | // a grype problem 434 | core.warning("grype had a non-zero exit status when running"); 435 | } else if (failBuild === true) { 436 | core.setFailed( 437 | `Failed minimum severity level. Found vulnerabilities with level '${severityCutoff}' or higher`, 438 | ); 439 | } else { 440 | // There is a non-zero exit status code with severity cut off, although there is still a chance this is grype 441 | // that is broken, it will most probably be a failed severity. Using warning here will make it bubble up in the 442 | // Actions UI 443 | core.warning( 444 | `Failed minimum severity level. Found vulnerabilities with level '${severityCutoff}' or higher`, 445 | ); 446 | } 447 | } 448 | return out; 449 | } 450 | 451 | module.exports = { 452 | run, 453 | runScan, 454 | installGrype, 455 | }; 456 | 457 | if (require.main === module) { 458 | const entrypoint = core.getInput("run"); 459 | switch (entrypoint) { 460 | case "download-grype": { 461 | installGrype(grypeVersion).then(async (path) => { 462 | core.info(`Downloaded Grype to: ${path}`); 463 | core.setOutput("cmd", path); 464 | 465 | // optionally restore, update and cache the db 466 | if ( 467 | cache.isFeatureAvailable() && 468 | (core.getInput("cache-db") || "").toLowerCase() === "true" 469 | ) { 470 | await updateDbWithCache(path); 471 | } 472 | }); 473 | break; 474 | } 475 | default: { 476 | run().then(); 477 | } 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupFiles: ["/.jest/setEnvVars.js"], 3 | verbose: true, 4 | maxConcurrency: 1, 5 | maxWorkers: 1, 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anchore-scan-action", 3 | "version": "2.0.2", 4 | "description": "Anchore image scan github action", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib", 8 | "test": "tests" 9 | }, 10 | "scripts": { 11 | "install-and-update-grype": "RUNNER_TOOL_CACHE='grype' RUNNER_TEMP='grype' node ./scripts/install-and-update-grype.js", 12 | "audit": "better-npm-audit audit --production", 13 | "lint": "eslint index.js", 14 | "test": "npm run lint && npm run start-registry && npm run install-and-update-grype && npm run build && npm run run-tests", 15 | "test-ci": "npm run lint && npm run install-and-update-grype && npm run build && npm run run-tests", 16 | "start-registry": "sh ./scripts/start_registry_and_push_images.sh", 17 | "run-tests": "GRYPE_DB_AUTO_UPDATE=false GRYPE_DB_VALIDATE_AGE=false jest --runInBand", 18 | "test:update-snapshots": "eslint index.js && npm run install-and-update-grype && GRYPE_DB_AUTO_UPDATE=false GRYPE_DB_VALIDATE_AGE=false jest --runInBand --updateSnapshot", 19 | "build": "ncc build ./index.js && node dos2unix.js dist/index.js", 20 | "precommit": "npm run prettier && npm run build && git add dist/", 21 | "prepare": "husky install", 22 | "prettier": "prettier -w *.js && prettier -w tests/*.js", 23 | "update-deps": "ncu -u && npm i && npm audit fix" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/anchore/anchore-scan-action.git" 28 | }, 29 | "keywords": [ 30 | "image", 31 | "scanning" 32 | ], 33 | "author": "Anchore Inc.", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/anchore/anchore-scan-action/issues" 37 | }, 38 | "homepage": "https://github.com/anchore/anchore-scan-action#readme", 39 | "dependencies": { 40 | "@actions/cache": "^4.0.0", 41 | "@actions/core": "^1.11.1", 42 | "@actions/exec": "^1.1.1", 43 | "@actions/tool-cache": "^2.0.2", 44 | "lodash": "^4.17.21" 45 | }, 46 | "devDependencies": { 47 | "@microsoft/jest-sarif": "^1.0.0-beta.0", 48 | "@vercel/ncc": "^0.38.3", 49 | "better-npm-audit": "^3.11.0", 50 | "eslint": "^9.26.0", 51 | "husky": "^9.1.7", 52 | "jest": "^29.7.0", 53 | "lint-staged": "^16.0.0", 54 | "prettier": "^3.5.3", 55 | "tar": "^7.4.3", 56 | "tslib": "^2.8.1" 57 | }, 58 | "lint-staged": { 59 | "*.js": "prettier --write" 60 | }, 61 | "engines": { 62 | "node": ">=v20.11.0" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /scripts/install-and-update-grype.js: -------------------------------------------------------------------------------- 1 | const { execFile } = require("child_process"); 2 | const { installGrype } = require("../index"); 3 | const { GRYPE_VERSION } = require("../GrypeVersion"); 4 | 5 | (async () => { 6 | try { 7 | const pinnedDB = 8 | "https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-04-01T01:31:39Z_1743480497.tar.zst"; 9 | const path = await installGrype(process.argv[2] || GRYPE_VERSION); 10 | console.log("Installed to:", path); 11 | 12 | execFile(path, ["db", "import", pinnedDB], (error, stdout, stderr) => { 13 | console.log("Importing db from: ", pinnedDB); 14 | if (error) { 15 | console.error("Error running db update:", stderr); 16 | process.exit(1); 17 | } 18 | console.log(stdout); 19 | }); 20 | } catch (e) { 21 | console.error("Failed to install or update Grype DB:", e); 22 | process.exit(1); 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /scripts/start_registry_and_push_images.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Remove existing container named 'registry' if it exists 5 | if docker ps -a --format '{{.Names}}' | grep -Eq '^registry$'; then 6 | echo "Removing existing 'registry' container..." 7 | docker rm -f registry 8 | fi 9 | 10 | # Start a new registry container 11 | docker run -d -p 5000:5000 --name registry registry:2 12 | 13 | # Build and push images 14 | for distro in alpine centos debian; do 15 | docker build -t localhost:5000/match-coverage/$distro ./tests/fixtures/image-$distro-match-coverage 16 | docker push localhost:5000/match-coverage/$distro:latest 17 | done 18 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Developing tests 2 | 3 | Some tests require a docker registry running locally on port 5000 as well as 4 | some images built. 5 | 6 | ``` 7 | docker run -d -p 5000:5000 --name registry registry:2 8 | 9 | for distro in alpine centos debian; do 10 | docker build -t localhost:5000/match-coverage/$distro ./tests/fixtures/image-$distro-match-coverage 11 | docker push localhost:5000/match-coverage/$distro:latest 12 | done 13 | ``` 14 | 15 | Then, just run: 16 | 17 | ``` 18 | npm test 19 | ``` 20 | -------------------------------------------------------------------------------- /tests/action.test.js: -------------------------------------------------------------------------------- 1 | const githubActionsCore = require("@actions/core"); 2 | const githubActionsCache = require("@actions/cache"); 3 | const githubActionsExec = require("@actions/exec"); 4 | const { cleanup, mock, mockIO, setEnv, tmpdir, runAction } = require("./mocks"); 5 | const { run } = require("../index"); 6 | 7 | jest.setTimeout(90000); // 90 seconds; tests were timing out in CI. https://github.com/anchore/scan-action/pull/249 8 | 9 | describe("Github action", () => { 10 | afterEach(cleanup); 11 | 12 | it("runs with inputs requested", async () => { 13 | const requestedInputs = {}; 14 | const expectedInputs = { 15 | image: "", 16 | path: "tests/fixtures/npm-project", 17 | "fail-build": "true", 18 | "output-format": "json", 19 | "severity-cutoff": "medium", 20 | "add-cpes-if-none": "true", 21 | vex: "test.vex", 22 | }; 23 | 24 | mock(githubActionsCore, { 25 | getInput(name) { 26 | requestedInputs[name] = true; 27 | return expectedInputs[name]; 28 | }, 29 | // ignore setFailed calls that set process.exitCode due to https://github.com/jestjs/jest/issues/14501 30 | setFailed() {}, 31 | }); 32 | 33 | await run(); 34 | 35 | Object.keys(expectedInputs).map((name) => { 36 | expect(requestedInputs[name]).toBeTruthy(); 37 | }); 38 | }); 39 | 40 | it("runs with json report", async () => { 41 | const outputs = mockIO({ 42 | image: "", 43 | path: "tests/fixtures/npm-project", 44 | "fail-build": "true", 45 | "output-file": "./results.json", 46 | "output-format": "json", 47 | "severity-cutoff": "medium", 48 | "add-cpes-if-none": "true", 49 | }); 50 | 51 | await run(); 52 | 53 | expect(outputs["sarif"]).toBeFalsy(); 54 | expect(outputs["json"]).toBe("./results.json"); 55 | }); 56 | 57 | it("runs with sarif report", async () => { 58 | const outputs = mockIO({ 59 | image: "", 60 | path: "tests/fixtures/npm-project", 61 | "fail-build": "true", 62 | "output-file": "./results.sarif", 63 | "output-format": "sarif", 64 | "severity-cutoff": "medium", 65 | "add-cpes-if-none": "true", 66 | }); 67 | 68 | await run(); 69 | 70 | expect(outputs["sarif"]).toBe("./results.sarif"); 71 | }); 72 | 73 | it("runs with table output", async () => { 74 | const { stdout, outputs } = await runAction({ 75 | image: "localhost:5000/match-coverage/debian:latest", 76 | "fail-build": "true", 77 | "output-format": "table", 78 | "severity-cutoff": "medium", 79 | "add-cpes-if-none": "true", 80 | }); 81 | 82 | expect(stdout).toContain("VULNERABILITY"); 83 | 84 | expect(outputs["sarif"]).toBeFalsy(); 85 | expect(outputs["json"]).toBeFalsy(); 86 | }); 87 | 88 | it("runs with cyclonedx-xml output", async () => { 89 | const outputs = mockIO({ 90 | image: "", 91 | path: "tests/fixtures/npm-project", 92 | "fail-build": "true", 93 | "output-format": "cyclonedx-xml", 94 | "output-file": "./results.cdx.xml", 95 | "severity-cutoff": "medium", 96 | "add-cpes-if-none": "true", 97 | }); 98 | 99 | await run(); 100 | 101 | expect(outputs["cyclonedx-xml"]).toBe("./results.cdx.xml"); 102 | }); 103 | 104 | it("runs with cyclonedx-json output", async () => { 105 | const outputs = mockIO({ 106 | image: "", 107 | path: "tests/fixtures/npm-project", 108 | "fail-build": "true", 109 | "output-format": "cyclonedx-json", 110 | "severity-cutoff": "medium", 111 | "add-cpes-if-none": "true", 112 | }); 113 | 114 | await run(); 115 | 116 | expect(outputs["cyclonedx-json"]).toBeDefined(); 117 | }); 118 | 119 | it("runs with environment variables", async () => { 120 | mockIO({ 121 | path: "tests/fixtures/npm-project", 122 | }); 123 | 124 | let call = {}; // commandLine, args, options 125 | 126 | const originalExec = githubActionsExec.exec; 127 | mock(githubActionsExec, { 128 | exec(commandLine, args, options) { 129 | call = { 130 | commandLine, 131 | args, 132 | options, 133 | }; 134 | return originalExec(commandLine, args, options); 135 | }, 136 | }); 137 | 138 | setEnv({ BOGUS_ENVIRONMENT_VARIABLE: "bogus" }); 139 | 140 | await run(); 141 | 142 | expect(call.options).toBeDefined(); 143 | expect(call.options.env.BOGUS_ENVIRONMENT_VARIABLE).toEqual("bogus"); 144 | }); 145 | 146 | it("errors with image and path", async () => { 147 | const { failure } = await runAction({ 148 | image: "some-image", 149 | path: "some-path", 150 | }); 151 | 152 | expect(failure).toContain( 153 | "The following options are mutually exclusive: image, path, sbom", 154 | ); 155 | }); 156 | 157 | it("errors with image and sbom", async () => { 158 | const { failure } = await runAction({ 159 | image: "some-image", 160 | sbom: "some-sbom", 161 | }); 162 | 163 | expect(failure).toContain( 164 | "The following options are mutually exclusive: image, path, sbom", 165 | ); 166 | }); 167 | 168 | it("errors with path and sbom", async () => { 169 | const { failure } = await runAction({ 170 | path: "some-path", 171 | sbom: "some-image", 172 | }); 173 | 174 | expect(failure).toContain( 175 | "The following options are mutually exclusive: image, path, sbom", 176 | ); 177 | }); 178 | 179 | it("fails due to vulnerabilities found", async () => { 180 | const { failure } = await runAction({ 181 | image: "localhost:5000/match-coverage/debian:latest", 182 | }); 183 | 184 | expect(failure).toContain("Failed minimum severity level."); 185 | }); 186 | 187 | it("runs with sbom", async () => { 188 | const { failure } = await runAction({ 189 | sbom: "fixtures/test_sbom.spdx.json", 190 | }); 191 | 192 | expect(failure).toContain("Failed minimum severity level."); 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /tests/dist.test.js: -------------------------------------------------------------------------------- 1 | const child_process = require("child_process"); 2 | const path = require("path"); 3 | 4 | // these are smoke tests that the dist build was created correctly, 5 | // functional tests should be in action.test.js 6 | describe("scan-action dist build", () => { 7 | it("runs download-grype", () => { 8 | const { stdout } = runDistBuild({ 9 | run: "download-grype", 10 | sbom: "fixtures/test_sbom.spdx.json", // should be ignored 11 | }); 12 | expect(stdout).toContain("Downloaded Grype"); 13 | expect(stdout).not.toContain("Failed minimum severity level."); 14 | }); 15 | 16 | it("fails due to vulnerabilities found", () => { 17 | const { stdout } = runDistBuild({ 18 | image: "localhost:5000/match-coverage/debian:latest", 19 | }); 20 | expect(stdout).toContain("Failed minimum severity level."); 21 | }); 22 | 23 | it("runs with sbom", () => { 24 | const { stdout } = runDistBuild({ 25 | sbom: "fixtures/test_sbom.spdx.json", 26 | }); 27 | expect(stdout).toContain("Failed minimum severity level."); 28 | }); 29 | }); 30 | 31 | // Execute the action, and return any outputs 32 | function runDistBuild(inputs) { 33 | const repoRootDir = path.dirname(__dirname); 34 | const distPath = path.join(repoRootDir, "dist", "index.js"); 35 | 36 | // Set up the environment variables 37 | const env = { 38 | PATH: process.env.PATH, 39 | RUNNER_TEMP: process.env.RUNNER_TEMP, 40 | RUNNER_TOOL_CACHE: process.env.RUNNER_TOOL_CACHE, 41 | GRYPE_DB_AUTO_UPDATE: "false", 42 | GRYPE_DB_VALIDATE_AGE: "false", 43 | }; 44 | // this is brittle and may need to be updated, but is currently how input are passed to the process: 45 | // reverse core.js: const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; 46 | for (const k in inputs) { 47 | // NOTE: there is a bug with node exec where environment variables with dashes 48 | // are not always preserved - we will just have to rely on defaults in the code 49 | env[`INPUT_${k}`.toUpperCase()] = inputs[k]; 50 | } 51 | 52 | // capture stdout and exit code, and execute the command 53 | let exitCode = 0; 54 | let stdout; 55 | try { 56 | stdout = child_process 57 | .execSync(`node ${distPath}`, { 58 | env, 59 | }) 60 | .toString("utf8"); 61 | } catch (error) { 62 | exitCode = error.status; 63 | stdout = error.stdout.toString("utf8"); 64 | } 65 | 66 | return { 67 | exitCode, 68 | stdout, 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /tests/fixtures/image-alpine-match-coverage/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | COPY . . -------------------------------------------------------------------------------- /tests/fixtures/image-alpine-match-coverage/etc/os-release: -------------------------------------------------------------------------------- 1 | NAME="Alpine Linux" 2 | ID=alpine 3 | VERSION_ID=3.12.0 4 | PRETTY_NAME="Alpine Linux v3.12" 5 | HOME_URL="https://alpinelinux.org/" 6 | BUG_REPORT_URL="https://bugs.alpinelinux.org/" 7 | -------------------------------------------------------------------------------- /tests/fixtures/image-alpine-match-coverage/lib/apk/db/installed: -------------------------------------------------------------------------------- 1 | C:Q1z0MwWQKfva+S+q7XmOBYFfQgW/k= 2 | P:libvncserver 3 | V:0.9.9 4 | A:x86_64 5 | S:166239 6 | I:389120 7 | T:Library to make writing a vnc server easy 8 | U:http://libvncserver.sourceforge.net/ 9 | L:GPL-2.0-or-later 10 | o:libvncserver 11 | m:A. Wilcox 12 | t:1572818861 13 | c:bf1ec813f662f128fc6b70f37ef1c0474bb24488 14 | D:so:libc.musl-x86_64.so.1 so:libgcrypt.so.20 so:libgnutls.so.30 so:libjpeg.so.8 so:libpng16.so.16 so:libz.so.1 15 | p:so:libvncclient.so.1=1.0.0 so:libvncserver.so.1=1.0.0 16 | F:usr 17 | F:usr/lib 18 | R:libvncclient.so.1 19 | a:0:0:777 20 | Z:Q1quyp/JcSPFQhtQFjMUYdMwRvAWM= 21 | R:libvncserver.so.1.0.0 22 | a:0:0:755 23 | Z:Q16Pd1AqyqQRMwiFfbUt9XkYnkapw= 24 | R:libvncserver.so.1 25 | a:0:0:777 26 | Z:Q184HrHsxEBqnsH4QNxeU5w8alhKI= 27 | R:libvncclient.so.1.0.0 28 | a:0:0:755 29 | Z:Q1IEjCrEwVlQt2GjIsb3o39vcgqMg= 30 | -------------------------------------------------------------------------------- /tests/fixtures/image-centos-match-coverage/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | COPY . . -------------------------------------------------------------------------------- /tests/fixtures/image-centos-match-coverage/etc/os-release: -------------------------------------------------------------------------------- 1 | NAME="CentOS Linux" 2 | VERSION="8 (Core)" 3 | ID="centos" 4 | ID_LIKE="rhel fedora" 5 | VERSION_ID="8" 6 | PLATFORM_ID="platform:el8" 7 | PRETTY_NAME="CentOS Linux 8 (Core)" 8 | ANSI_COLOR="0;31" 9 | CPE_NAME="cpe:/o:centos:centos:8" 10 | HOME_URL="https://www.centos.org/" 11 | BUG_REPORT_URL="https://bugs.centos.org/" -------------------------------------------------------------------------------- /tests/fixtures/image-centos-match-coverage/var/lib/rpm/Packages: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anchore/scan-action/251fd2c39f60e9dee3293aa6715bfd709ba02952/tests/fixtures/image-centos-match-coverage/var/lib/rpm/Packages -------------------------------------------------------------------------------- /tests/fixtures/image-centos-match-coverage/var/lib/rpm/generate-fixture.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eux 3 | 4 | docker create --name generate-rpmdb-fixture centos:latest sh -c 'tail -f /dev/null' 5 | 6 | function cleanup { 7 | docker kill generate-rpmdb-fixture 8 | docker rm generate-rpmdb-fixture 9 | } 10 | trap cleanup EXIT 11 | 12 | docker start generate-rpmdb-fixture 13 | docker exec -i --tty=false generate-rpmdb-fixture bash <<-EOF 14 | mkdir -p /scratch 15 | cd /scratch 16 | rpm --initdb --dbpath /scratch 17 | curl -sSLO https://github.com/wagoodman/dive/releases/download/v0.9.2/dive_0.9.2_linux_amd64.rpm 18 | rpm --dbpath /scratch -ivh dive_0.9.2_linux_amd64.rpm 19 | rm dive_0.9.2_linux_amd64.rpm 20 | rpm --dbpath /scratch -qa 21 | EOF 22 | 23 | docker cp generate-rpmdb-fixture:/scratch/Packages . 24 | -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | COPY . . -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/java/example-java-app-maven-0.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anchore/scan-action/251fd2c39f60e9dee3293aa6715bfd709ba02952/tests/fixtures/image-debian-match-coverage/java/example-java-app-maven-0.1.0.jar -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/java/generate-fixtures.md: -------------------------------------------------------------------------------- 1 | See the syft/cataloger/java/test-fixtures/java-builds dir to generate test fixtures and copy to here manually. 2 | -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/javascript/pkg-json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "6.14.6", 3 | "name": "npm", 4 | "description": "a package manager for JavaScript", 5 | "keywords": [ 6 | "install", 7 | "modules", 8 | "package manager", 9 | "package.json" 10 | ], 11 | "preferGlobal": true, 12 | "config": { 13 | "publishtest": false 14 | }, 15 | "homepage": "https://docs.npmjs.com/", 16 | "author": "Isaac Z. Schlueter (http://blog.izs.me)", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/npm/cli" 20 | }, 21 | "bugs": { 22 | "url": "https://npm.community/c/bugs" 23 | }, 24 | "directories": { 25 | "bin": "./bin", 26 | "doc": "./doc", 27 | "lib": "./lib", 28 | "man": "./man" 29 | }, 30 | "main": "./lib/npm.js", 31 | "bin": { 32 | "npm": "./bin/npm-cli.js", 33 | "npx": "./bin/npx-cli.js" 34 | }, 35 | "dependencies": { 36 | "JSONStream": "^1.3.5", 37 | "abbrev": "~1.1.1", 38 | "ansicolors": "~0.3.2", 39 | "write-file-atomic": "^2.4.3" 40 | }, 41 | "bundleDependencies": [ 42 | "abbrev", 43 | "ansicolors", 44 | "ansistyles", 45 | "write-file-atomic" 46 | ], 47 | "devDependencies": { 48 | "deep-equal": "^1.0.1", 49 | "get-stream": "^4.1.0", 50 | "licensee": "^7.0.3", 51 | "marked": "^0.6.3", 52 | "marked-man": "^0.6.0", 53 | "npm-registry-couchapp": "^2.7.4", 54 | "npm-registry-mock": "^1.3.1", 55 | "require-inject": "^1.4.4", 56 | "sprintf-js": "^1.1.2", 57 | "standard": "^11.0.1", 58 | "tacks": "^1.3.0", 59 | "tap": "^12.7.0", 60 | "tar-stream": "^2.1.0" 61 | }, 62 | "scripts": { 63 | "dumpconf": "env | grep npm | sort | uniq", 64 | "prepare": "node bin/npm-cli.js rebuild && node bin/npm-cli.js --no-audit --no-timing prune --prefix=. --no-global && rimraf test/*/*/node_modules && make -j4 mandocs", 65 | "preversion": "bash scripts/update-authors.sh && git add AUTHORS && git commit -m \"update AUTHORS\" || true", 66 | "licenses": "licensee --production --errors-only", 67 | "tap": "tap -J --timeout 300 --no-esm", 68 | "tap-cover": "tap -J --nyc-arg=--cache --coverage --timeout 600 --no-esm", 69 | "lint": "standard", 70 | "pretest": "npm run lint", 71 | "test": "npm run test-tap --", 72 | "test:nocleanup": "NO_TEST_CLEANUP=1 npm run test --", 73 | "sudotest": "sudo npm run tap -- \"test/tap/*.js\"", 74 | "sudotest:nocleanup": "sudo NO_TEST_CLEANUP=1 npm run tap -- \"test/tap/*.js\"", 75 | "posttest": "rimraf test/npm_cache*", 76 | "test-coverage": "npm run tap-cover -- \"test/tap/*.js\" \"test/network/*.js\"", 77 | "test-tap": "npm run tap -- \"test/tap/*.js\" \"test/network/*.js\"", 78 | "test-node": "tap --timeout 240 \"test/tap/*.js\" \"test/network/*.js\"" 79 | }, 80 | "license": "Artistic-2.0", 81 | "engines": { 82 | "node": "6 >=6.2.0 || 8 || >=9.3.0" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/python/dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: Pygments 3 | Version: 2.6.1 4 | Summary: Pygments is a syntax highlighting package written in Python. 5 | Home-page: https://pygments.org/ 6 | Author: Georg Brandl 7 | Author-email: georg@python.org 8 | License: BSD License 9 | Keywords: syntax highlighting 10 | Platform: any 11 | Classifier: License :: OSI Approved :: BSD License 12 | Classifier: Intended Audience :: Developers 13 | Classifier: Intended Audience :: End Users/Desktop 14 | Classifier: Intended Audience :: System Administrators 15 | Classifier: Development Status :: 6 - Mature 16 | Classifier: Programming Language :: Python 17 | Classifier: Programming Language :: Python :: 3 18 | Classifier: Programming Language :: Python :: 3.5 19 | Classifier: Programming Language :: Python :: 3.6 20 | Classifier: Programming Language :: Python :: 3.7 21 | Classifier: Programming Language :: Python :: 3.8 22 | Classifier: Programming Language :: Python :: Implementation :: CPython 23 | Classifier: Programming Language :: Python :: Implementation :: PyPy 24 | Classifier: Operating System :: OS Independent 25 | Classifier: Topic :: Text Processing :: Filters 26 | Classifier: Topic :: Utilities 27 | Requires-Python: >=3.5 28 | 29 | 30 | Pygments 31 | ~~~~~~~~ 32 | 33 | Pygments is a syntax highlighting package written in Python. 34 | 35 | It is a generic syntax highlighter suitable for use in code hosting, forums, 36 | wikis or other applications that need to prettify source code. Highlights 37 | are: 38 | 39 | * a wide range of over 500 languages and other text formats is supported 40 | * special attention is paid to details, increasing quality by a fair amount 41 | * support for new languages and formats are added easily 42 | * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences 43 | * it is usable as a command-line tool and as a library 44 | 45 | :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS. 46 | :license: BSD, see LICENSE for details. 47 | 48 | -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/python/dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | pygments -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/ruby/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | rails (4.1.1) 5 | activerecord (= 4.1.1) 6 | 7 | PLATFORMS 8 | ruby 9 | 10 | DEPENDENCIES 11 | rails (= 4.1.1) -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/ruby/specifications/bundler.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # -*- encoding: utf-8 -*- 3 | # stub: bundler 2.1.4 ruby lib 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "bundler".freeze 7 | s.version = "2.1.4" 8 | 9 | s.required_rubygems_version = Gem::Requirement.new(">= 2.5.2".freeze) if s.respond_to? :required_rubygems_version= 10 | s.metadata = { "bug_tracker_uri" => "https://github.com/bundler/bundler/issues", "changelog_uri" => "https://github.com/bundler/bundler/blob/master/CHANGELOG.md", "homepage_uri" => "https://bundler.io/", "source_code_uri" => "https://github.com/bundler/bundler/" } if s.respond_to? :metadata= 11 | s.require_paths = ["lib".freeze] 12 | s.authors = ["Andr\u00E9 Arko".freeze, "Samuel Giddins".freeze, "Colby Swandale".freeze, "Hiroshi Shibata".freeze, "David Rodr\u00EDguez".freeze, "Grey Baker".freeze, "Stephanie Morillo".freeze, "Chris Morris".freeze, "James Wen".freeze, "Tim Moore".freeze, "Andr\u00E9 Medeiros".freeze, "Jessica Lynn Suttles".freeze, "Terence Lee".freeze, "Carl Lerche".freeze, "Yehuda Katz".freeze] 13 | s.bindir = "exe".freeze 14 | s.date = "2020-01-05" 15 | s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably".freeze 16 | s.email = ["team@bundler.io".freeze] 17 | s.executables = ["bundle".freeze, "bundler".freeze] 18 | s.files = ["exe/bundle".freeze, "exe/bundler".freeze] 19 | s.homepage = "https://bundler.io".freeze 20 | s.licenses = ["MIT".freeze] 21 | s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) 22 | s.rubygems_version = "3.1.2".freeze 23 | s.summary = "The best way to manage your application's dependencies".freeze 24 | 25 | s.installed_by_version = "3.1.2" if s.respond_to? :installed_by_version 26 | end -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/usr/lib/os-release: -------------------------------------------------------------------------------- 1 | PRETTY_NAME="Debian GNU/Linux 8 (jessie)" 2 | NAME="Debian GNU/Linux" 3 | VERSION_ID="8" 4 | VERSION="8 (jessie)" 5 | ID=debian 6 | HOME_URL="http://www.debian.org/" 7 | SUPPORT_URL="http://www.debian.org/support" 8 | BUG_REPORT_URL="https://bugs.debian.org/" 9 | -------------------------------------------------------------------------------- /tests/fixtures/image-debian-match-coverage/var/lib/dpkg/status: -------------------------------------------------------------------------------- 1 | Package: apt 2 | Status: install ok installed 3 | Priority: required 4 | Section: admin 5 | Installed-Size: 4064 6 | Maintainer: APT Development Team 7 | Architecture: amd64 8 | Version: 1.8.2 9 | Source: apt-dev 10 | Replaces: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~) 11 | Provides: apt-transport-https (= 1.8.2) 12 | Depends: adduser, gpgv | gpgv2 | gpgv1, debian-archive-keyring, libapt-pkg5.0 (>= 1.7.0~alpha3~), libc6 (>= 2.15), libgcc1 (>= 1:3.0), libgnutls30 (>= 3.6.6), libseccomp2 (>= 1.0.1), libstdc++6 (>= 5.2) 13 | Recommends: ca-certificates 14 | Suggests: apt-doc, aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2), gnupg | gnupg2 | gnupg1, powermgmt-base 15 | Breaks: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~), aptitude (<< 0.8.10) 16 | Conffiles: 17 | /etc/apt/apt.conf.d/01autoremove 76120d358bc9037bb6358e737b3050b5 18 | /etc/cron.daily/apt-compat 49e9b2cfa17849700d4db735d04244f3 19 | /etc/kernel/postinst.d/apt-auto-removal 4ad976a68f045517cf4696cec7b8aa3a 20 | /etc/logrotate.d/apt 179f2ed4f85cbaca12fa3d69c2a4a1c3 21 | Description: commandline package manager 22 | This package provides commandline tools for searching and 23 | managing as well as querying information about packages 24 | as a low-level access to all features of the libapt-pkg library. 25 | . 26 | These include: 27 | * apt-get for retrieval of packages and information about them 28 | from authenticated sources and for installation, upgrade and 29 | removal of packages together with their dependencies 30 | * apt-cache for querying available information about installed 31 | as well as installable packages 32 | * apt-cdrom to use removable media as a source for packages 33 | * apt-config as an interface to the configuration settings 34 | * apt-key as an interface to manage authentication keys 35 | 36 | -------------------------------------------------------------------------------- /tests/fixtures/localbuild/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | -------------------------------------------------------------------------------- /tests/fixtures/npm-project/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-project", 3 | "version": "0.12.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "chownr": { 8 | "version": "2.0.0", 9 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", 10 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" 11 | }, 12 | "fs-minipass": { 13 | "version": "2.1.0", 14 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", 15 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", 16 | "requires": { 17 | "minipass": "^3.0.0" 18 | } 19 | }, 20 | "js-tokens": { 21 | "version": "4.0.0", 22 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 23 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 24 | }, 25 | "loose-envify": { 26 | "version": "1.4.0", 27 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 28 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 29 | "requires": { 30 | "js-tokens": "^3.0.0 || ^4.0.0" 31 | } 32 | }, 33 | "minipass": { 34 | "version": "3.1.3", 35 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", 36 | "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", 37 | "requires": { 38 | "yallist": "^4.0.0" 39 | } 40 | }, 41 | "minizlib": { 42 | "version": "2.1.2", 43 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", 44 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", 45 | "requires": { 46 | "minipass": "^3.0.0", 47 | "yallist": "^4.0.0" 48 | } 49 | }, 50 | "mkdirp": { 51 | "version": "1.0.4", 52 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 53 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" 54 | }, 55 | "object-assign": { 56 | "version": "4.1.1", 57 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 58 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 59 | }, 60 | "prop-types": { 61 | "version": "15.7.2", 62 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 63 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 64 | "requires": { 65 | "loose-envify": "^1.4.0", 66 | "object-assign": "^4.1.1", 67 | "react-is": "^16.8.1" 68 | } 69 | }, 70 | "react": { 71 | "version": "16.14.0", 72 | "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", 73 | "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", 74 | "requires": { 75 | "loose-envify": "^1.1.0", 76 | "object-assign": "^4.1.1", 77 | "prop-types": "^15.6.2" 78 | } 79 | }, 80 | "react-is": { 81 | "version": "16.13.1", 82 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 83 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 84 | }, 85 | "tar": { 86 | "version": "6.1.0", 87 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", 88 | "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", 89 | "requires": { 90 | "chownr": "^2.0.0", 91 | "fs-minipass": "^2.0.0", 92 | "minipass": "^3.0.0", 93 | "minizlib": "^2.1.1", 94 | "mkdirp": "^1.0.3", 95 | "yallist": "^4.0.0" 96 | } 97 | }, 98 | "yallist": { 99 | "version": "4.0.0", 100 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 101 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /tests/fixtures/npm-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-project", 3 | "version": "0.12.1", 4 | "description": "Basic NPM-based project", 5 | "main": "index.js", 6 | "author": "test@test", 7 | "license": "MIT", 8 | "dependencies": { 9 | "react": "^16.14.0", 10 | "tar": "^6.1.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/fixtures/test_sbom.spdx.json: -------------------------------------------------------------------------------- 1 | { 2 | "SPDXID": "SPDXRef-DOCUMENT", 3 | "name": "test/alpine-3.15.0", 4 | "spdxVersion": "SPDX-2.2", 5 | "creationInfo": { 6 | "created": "2022-04-04T08:49:26.476015Z", 7 | "creators": ["Organization: Anchore, Inc", "Tool: syft-0.42.4"], 8 | "licenseListVersion": "3.16" 9 | }, 10 | "dataLicense": "CC0-1.0", 11 | "documentNamespace": "https://anchore.com/syft/image/test/alpine-3.15.0-1.2.0-33a507f3-0f8b-4d63-97d8-543bb595b7f4", 12 | "packages": [ 13 | { 14 | "SPDXID": "SPDXRef-58efe7db3446006", 15 | "name": "alpine-baselayout", 16 | "licenseConcluded": "GPL-2.0-only", 17 | "description": "Alpine base dir structure and init scripts", 18 | "downloadLocation": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", 19 | "externalRefs": [ 20 | { 21 | "referenceCategory": "SECURITY", 22 | "referenceLocator": "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r18:*:*:*:*:*:*:*", 23 | "referenceType": "cpe23Type" 24 | }, 25 | { 26 | "referenceCategory": "SECURITY", 27 | "referenceLocator": "cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.2.0-r18:*:*:*:*:*:*:*", 28 | "referenceType": "cpe23Type" 29 | }, 30 | { 31 | "referenceCategory": "SECURITY", 32 | "referenceLocator": "cpe:2.3:a:alpine_baselayout:alpine-baselayout:3.2.0-r18:*:*:*:*:*:*:*", 33 | "referenceType": "cpe23Type" 34 | }, 35 | { 36 | "referenceCategory": "SECURITY", 37 | "referenceLocator": "cpe:2.3:a:alpine_baselayout:alpine_baselayout:3.2.0-r18:*:*:*:*:*:*:*", 38 | "referenceType": "cpe23Type" 39 | }, 40 | { 41 | "referenceCategory": "SECURITY", 42 | "referenceLocator": "cpe:2.3:a:alpine:alpine-baselayout:3.2.0-r18:*:*:*:*:*:*:*", 43 | "referenceType": "cpe23Type" 44 | }, 45 | { 46 | "referenceCategory": "SECURITY", 47 | "referenceLocator": "cpe:2.3:a:alpine:alpine_baselayout:3.2.0-r18:*:*:*:*:*:*:*", 48 | "referenceType": "cpe23Type" 49 | }, 50 | { 51 | "referenceCategory": "PACKAGE_MANAGER", 52 | "referenceLocator": "pkg:alpine/alpine-baselayout@3.2.0-r18?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.15.0", 53 | "referenceType": "purl" 54 | } 55 | ], 56 | "filesAnalyzed": false, 57 | "licenseDeclared": "GPL-2.0-only", 58 | "originator": "Person: Natanael Copa ", 59 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 60 | "versionInfo": "3.2.0-r18" 61 | }, 62 | { 63 | "SPDXID": "SPDXRef-fcb0c1584e831bc", 64 | "name": "alpine-keys", 65 | "licenseConcluded": "MIT", 66 | "description": "Public keys for Alpine Linux packages", 67 | "downloadLocation": "https://alpinelinux.org", 68 | "externalRefs": [ 69 | { 70 | "referenceCategory": "SECURITY", 71 | "referenceLocator": "cpe:2.3:a:alpine-keys:alpine-keys:2.4-r1:*:*:*:*:*:*:*", 72 | "referenceType": "cpe23Type" 73 | }, 74 | { 75 | "referenceCategory": "SECURITY", 76 | "referenceLocator": "cpe:2.3:a:alpine-keys:alpine_keys:2.4-r1:*:*:*:*:*:*:*", 77 | "referenceType": "cpe23Type" 78 | }, 79 | { 80 | "referenceCategory": "SECURITY", 81 | "referenceLocator": "cpe:2.3:a:alpine_keys:alpine-keys:2.4-r1:*:*:*:*:*:*:*", 82 | "referenceType": "cpe23Type" 83 | }, 84 | { 85 | "referenceCategory": "SECURITY", 86 | "referenceLocator": "cpe:2.3:a:alpine_keys:alpine_keys:2.4-r1:*:*:*:*:*:*:*", 87 | "referenceType": "cpe23Type" 88 | }, 89 | { 90 | "referenceCategory": "SECURITY", 91 | "referenceLocator": "cpe:2.3:a:alpine:alpine-keys:2.4-r1:*:*:*:*:*:*:*", 92 | "referenceType": "cpe23Type" 93 | }, 94 | { 95 | "referenceCategory": "SECURITY", 96 | "referenceLocator": "cpe:2.3:a:alpine:alpine_keys:2.4-r1:*:*:*:*:*:*:*", 97 | "referenceType": "cpe23Type" 98 | }, 99 | { 100 | "referenceCategory": "PACKAGE_MANAGER", 101 | "referenceLocator": "pkg:alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.15.0", 102 | "referenceType": "purl" 103 | } 104 | ], 105 | "filesAnalyzed": false, 106 | "licenseDeclared": "MIT", 107 | "originator": "Person: Natanael Copa ", 108 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 109 | "versionInfo": "2.4-r1" 110 | }, 111 | { 112 | "SPDXID": "SPDXRef-63e383018474a687", 113 | "name": "apk-tools", 114 | "licenseConcluded": "GPL-2.0-only", 115 | "description": "Alpine Package Keeper - package manager for alpine", 116 | "downloadLocation": "https://gitlab.alpinelinux.org/alpine/apk-tools", 117 | "externalRefs": [ 118 | { 119 | "referenceCategory": "SECURITY", 120 | "referenceLocator": "cpe:2.3:a:apk-tools:apk-tools:2.12.7-r3:*:*:*:*:*:*:*", 121 | "referenceType": "cpe23Type" 122 | }, 123 | { 124 | "referenceCategory": "SECURITY", 125 | "referenceLocator": "cpe:2.3:a:apk-tools:apk_tools:2.12.7-r3:*:*:*:*:*:*:*", 126 | "referenceType": "cpe23Type" 127 | }, 128 | { 129 | "referenceCategory": "SECURITY", 130 | "referenceLocator": "cpe:2.3:a:apk_tools:apk-tools:2.12.7-r3:*:*:*:*:*:*:*", 131 | "referenceType": "cpe23Type" 132 | }, 133 | { 134 | "referenceCategory": "SECURITY", 135 | "referenceLocator": "cpe:2.3:a:apk_tools:apk_tools:2.12.7-r3:*:*:*:*:*:*:*", 136 | "referenceType": "cpe23Type" 137 | }, 138 | { 139 | "referenceCategory": "SECURITY", 140 | "referenceLocator": "cpe:2.3:a:apk:apk-tools:2.12.7-r3:*:*:*:*:*:*:*", 141 | "referenceType": "cpe23Type" 142 | }, 143 | { 144 | "referenceCategory": "SECURITY", 145 | "referenceLocator": "cpe:2.3:a:apk:apk_tools:2.12.7-r3:*:*:*:*:*:*:*", 146 | "referenceType": "cpe23Type" 147 | }, 148 | { 149 | "referenceCategory": "PACKAGE_MANAGER", 150 | "referenceLocator": "pkg:alpine/apk-tools@2.12.7-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.15.0", 151 | "referenceType": "purl" 152 | } 153 | ], 154 | "filesAnalyzed": false, 155 | "licenseDeclared": "GPL-2.0-only", 156 | "originator": "Person: Natanael Copa ", 157 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 158 | "versionInfo": "2.12.7-r3" 159 | }, 160 | { 161 | "SPDXID": "SPDXRef-fa0b3237f7ac675f", 162 | "name": "busybox", 163 | "licenseConcluded": "GPL-2.0-only", 164 | "description": "Size optimized toolbox of many common UNIX utilities", 165 | "downloadLocation": "https://busybox.net/", 166 | "externalRefs": [ 167 | { 168 | "referenceCategory": "SECURITY", 169 | "referenceLocator": "cpe:2.3:a:busybox:busybox:1.34.1-r3:*:*:*:*:*:*:*", 170 | "referenceType": "cpe23Type" 171 | }, 172 | { 173 | "referenceCategory": "PACKAGE_MANAGER", 174 | "referenceLocator": "pkg:alpine/busybox@1.34.1-r3?arch=x86_64&upstream=busybox&distro=alpine-3.15.0", 175 | "referenceType": "purl" 176 | } 177 | ], 178 | "filesAnalyzed": false, 179 | "licenseDeclared": "GPL-2.0-only", 180 | "originator": "Person: Natanael Copa ", 181 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 182 | "versionInfo": "1.34.1-r3" 183 | }, 184 | { 185 | "SPDXID": "SPDXRef-7eb965466c0157d3", 186 | "name": "ca-certificates-bundle", 187 | "licenseConcluded": "MPL-2.0 AND MIT", 188 | "description": "Pre generated bundle of Mozilla certificates", 189 | "downloadLocation": "https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/", 190 | "externalRefs": [ 191 | { 192 | "referenceCategory": "SECURITY", 193 | "referenceLocator": "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20191127-r7:*:*:*:*:*:*:*", 194 | "referenceType": "cpe23Type" 195 | }, 196 | { 197 | "referenceCategory": "SECURITY", 198 | "referenceLocator": "cpe:2.3:a:ca-certificates-bundle:ca_certificates_bundle:20191127-r7:*:*:*:*:*:*:*", 199 | "referenceType": "cpe23Type" 200 | }, 201 | { 202 | "referenceCategory": "SECURITY", 203 | "referenceLocator": "cpe:2.3:a:ca_certificates_bundle:ca-certificates-bundle:20191127-r7:*:*:*:*:*:*:*", 204 | "referenceType": "cpe23Type" 205 | }, 206 | { 207 | "referenceCategory": "SECURITY", 208 | "referenceLocator": "cpe:2.3:a:ca_certificates_bundle:ca_certificates_bundle:20191127-r7:*:*:*:*:*:*:*", 209 | "referenceType": "cpe23Type" 210 | }, 211 | { 212 | "referenceCategory": "SECURITY", 213 | "referenceLocator": "cpe:2.3:a:ca-certificates:ca-certificates-bundle:20191127-r7:*:*:*:*:*:*:*", 214 | "referenceType": "cpe23Type" 215 | }, 216 | { 217 | "referenceCategory": "SECURITY", 218 | "referenceLocator": "cpe:2.3:a:ca-certificates:ca_certificates_bundle:20191127-r7:*:*:*:*:*:*:*", 219 | "referenceType": "cpe23Type" 220 | }, 221 | { 222 | "referenceCategory": "SECURITY", 223 | "referenceLocator": "cpe:2.3:a:ca_certificates:ca-certificates-bundle:20191127-r7:*:*:*:*:*:*:*", 224 | "referenceType": "cpe23Type" 225 | }, 226 | { 227 | "referenceCategory": "SECURITY", 228 | "referenceLocator": "cpe:2.3:a:ca_certificates:ca_certificates_bundle:20191127-r7:*:*:*:*:*:*:*", 229 | "referenceType": "cpe23Type" 230 | }, 231 | { 232 | "referenceCategory": "SECURITY", 233 | "referenceLocator": "cpe:2.3:a:ca:ca-certificates-bundle:20191127-r7:*:*:*:*:*:*:*", 234 | "referenceType": "cpe23Type" 235 | }, 236 | { 237 | "referenceCategory": "SECURITY", 238 | "referenceLocator": "cpe:2.3:a:ca:ca_certificates_bundle:20191127-r7:*:*:*:*:*:*:*", 239 | "referenceType": "cpe23Type" 240 | }, 241 | { 242 | "referenceCategory": "PACKAGE_MANAGER", 243 | "referenceLocator": "pkg:alpine/ca-certificates-bundle@20191127-r7?arch=x86_64&upstream=ca-certificates&distro=alpine-3.15.0", 244 | "referenceType": "purl" 245 | } 246 | ], 247 | "filesAnalyzed": false, 248 | "licenseDeclared": "MPL-2.0 AND MIT", 249 | "originator": "Person: Natanael Copa ", 250 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 251 | "versionInfo": "20191127-r7" 252 | }, 253 | { 254 | "SPDXID": "SPDXRef-c79db4d080889701", 255 | "name": "glibc", 256 | "licenseConcluded": "NOASSERTION", 257 | "description": "GNU C Library compatibility layer", 258 | "downloadLocation": "https://github.com/sgerrand/alpine-pkg-glibc", 259 | "externalRefs": [ 260 | { 261 | "referenceCategory": "SECURITY", 262 | "referenceLocator": "cpe:2.3:a:glibc:glibc:2.34-r0:*:*:*:*:*:*:*", 263 | "referenceType": "cpe23Type" 264 | }, 265 | { 266 | "referenceCategory": "PACKAGE_MANAGER", 267 | "referenceLocator": "pkg:alpine/glibc@2.34-r0?arch=x86_64&upstream=glibc&distro=alpine-3.15.0", 268 | "referenceType": "purl" 269 | } 270 | ], 271 | "filesAnalyzed": false, 272 | "licenseDeclared": "NOASSERTION", 273 | "originator": "Person: Sasha Gerrand ", 274 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 275 | "versionInfo": "2.34-r0" 276 | }, 277 | { 278 | "SPDXID": "SPDXRef-f292a8b6c0ff2d58", 279 | "name": "libc-utils", 280 | "licenseConcluded": "BSD-2-Clause AND BSD-3-Clause", 281 | "description": "Meta package to pull in correct libc", 282 | "downloadLocation": "https://alpinelinux.org", 283 | "externalRefs": [ 284 | { 285 | "referenceCategory": "SECURITY", 286 | "referenceLocator": "cpe:2.3:a:libc-utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", 287 | "referenceType": "cpe23Type" 288 | }, 289 | { 290 | "referenceCategory": "SECURITY", 291 | "referenceLocator": "cpe:2.3:a:libc-utils:libc_utils:0.7.2-r3:*:*:*:*:*:*:*", 292 | "referenceType": "cpe23Type" 293 | }, 294 | { 295 | "referenceCategory": "SECURITY", 296 | "referenceLocator": "cpe:2.3:a:libc_utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", 297 | "referenceType": "cpe23Type" 298 | }, 299 | { 300 | "referenceCategory": "SECURITY", 301 | "referenceLocator": "cpe:2.3:a:libc_utils:libc_utils:0.7.2-r3:*:*:*:*:*:*:*", 302 | "referenceType": "cpe23Type" 303 | }, 304 | { 305 | "referenceCategory": "SECURITY", 306 | "referenceLocator": "cpe:2.3:a:libc:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", 307 | "referenceType": "cpe23Type" 308 | }, 309 | { 310 | "referenceCategory": "SECURITY", 311 | "referenceLocator": "cpe:2.3:a:libc:libc_utils:0.7.2-r3:*:*:*:*:*:*:*", 312 | "referenceType": "cpe23Type" 313 | }, 314 | { 315 | "referenceCategory": "PACKAGE_MANAGER", 316 | "referenceLocator": "pkg:alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.15.0", 317 | "referenceType": "purl" 318 | } 319 | ], 320 | "filesAnalyzed": false, 321 | "licenseDeclared": "BSD-2-Clause AND BSD-3-Clause", 322 | "originator": "Person: Natanael Copa ", 323 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 324 | "versionInfo": "0.7.2-r3" 325 | }, 326 | { 327 | "SPDXID": "SPDXRef-e095a8632defc75a", 328 | "name": "libcrypto1.1", 329 | "licenseConcluded": "OpenSSL", 330 | "description": "Crypto library from openssl", 331 | "downloadLocation": "https://www.openssl.org/", 332 | "externalRefs": [ 333 | { 334 | "referenceCategory": "SECURITY", 335 | "referenceLocator": "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1l-r7:*:*:*:*:*:*:*", 336 | "referenceType": "cpe23Type" 337 | }, 338 | { 339 | "referenceCategory": "PACKAGE_MANAGER", 340 | "referenceLocator": "pkg:alpine/libcrypto1.1@1.1.1l-r7?arch=x86_64&upstream=openssl&distro=alpine-3.15.0", 341 | "referenceType": "purl" 342 | } 343 | ], 344 | "filesAnalyzed": false, 345 | "licenseDeclared": "OpenSSL", 346 | "originator": "Person: Timo Teras ", 347 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 348 | "versionInfo": "1.1.1l-r7" 349 | }, 350 | { 351 | "SPDXID": "SPDXRef-85be40658b5f830b", 352 | "name": "libretls", 353 | "licenseConcluded": "ISC", 354 | "description": "port of libtls from libressl to openssl", 355 | "downloadLocation": "https://git.causal.agency/libretls/", 356 | "externalRefs": [ 357 | { 358 | "referenceCategory": "SECURITY", 359 | "referenceLocator": "cpe:2.3:a:libretls:libretls:3.3.4-r2:*:*:*:*:*:*:*", 360 | "referenceType": "cpe23Type" 361 | }, 362 | { 363 | "referenceCategory": "PACKAGE_MANAGER", 364 | "referenceLocator": "pkg:alpine/libretls@3.3.4-r2?arch=x86_64&upstream=libretls&distro=alpine-3.15.0", 365 | "referenceType": "purl" 366 | } 367 | ], 368 | "filesAnalyzed": false, 369 | "licenseDeclared": "ISC", 370 | "originator": "Person: Ariadne Conill ", 371 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 372 | "versionInfo": "3.3.4-r2" 373 | }, 374 | { 375 | "SPDXID": "SPDXRef-86f2f98b49f3bc79", 376 | "name": "libssl1.1", 377 | "licenseConcluded": "OpenSSL", 378 | "description": "SSL shared libraries", 379 | "downloadLocation": "https://www.openssl.org/", 380 | "externalRefs": [ 381 | { 382 | "referenceCategory": "SECURITY", 383 | "referenceLocator": "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1l-r7:*:*:*:*:*:*:*", 384 | "referenceType": "cpe23Type" 385 | }, 386 | { 387 | "referenceCategory": "PACKAGE_MANAGER", 388 | "referenceLocator": "pkg:alpine/libssl1.1@1.1.1l-r7?arch=x86_64&upstream=openssl&distro=alpine-3.15.0", 389 | "referenceType": "purl" 390 | } 391 | ], 392 | "filesAnalyzed": false, 393 | "licenseDeclared": "OpenSSL", 394 | "originator": "Person: Timo Teras ", 395 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 396 | "versionInfo": "1.1.1l-r7" 397 | }, 398 | { 399 | "SPDXID": "SPDXRef-768c6a15875e1903", 400 | "name": "musl", 401 | "licenseConcluded": "MIT", 402 | "description": "the musl c library (libc) implementation", 403 | "downloadLocation": "https://musl.libc.org/", 404 | "externalRefs": [ 405 | { 406 | "referenceCategory": "SECURITY", 407 | "referenceLocator": "cpe:2.3:a:musl:musl:1.2.2-r7:*:*:*:*:*:*:*", 408 | "referenceType": "cpe23Type" 409 | }, 410 | { 411 | "referenceCategory": "PACKAGE_MANAGER", 412 | "referenceLocator": "pkg:alpine/musl@1.2.2-r7?arch=x86_64&upstream=musl&distro=alpine-3.15.0", 413 | "referenceType": "purl" 414 | } 415 | ], 416 | "filesAnalyzed": false, 417 | "licenseDeclared": "MIT", 418 | "originator": "Person: Timo Teräs ", 419 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 420 | "versionInfo": "1.2.2-r7" 421 | }, 422 | { 423 | "SPDXID": "SPDXRef-33b44afe74f963db", 424 | "name": "musl-utils", 425 | "licenseConcluded": "MIT", 426 | "description": "the musl c library (libc) implementation", 427 | "downloadLocation": "https://musl.libc.org/", 428 | "externalRefs": [ 429 | { 430 | "referenceCategory": "SECURITY", 431 | "referenceLocator": "cpe:2.3:a:musl-utils:musl-utils:1.2.2-r7:*:*:*:*:*:*:*", 432 | "referenceType": "cpe23Type" 433 | }, 434 | { 435 | "referenceCategory": "SECURITY", 436 | "referenceLocator": "cpe:2.3:a:musl-utils:musl_utils:1.2.2-r7:*:*:*:*:*:*:*", 437 | "referenceType": "cpe23Type" 438 | }, 439 | { 440 | "referenceCategory": "SECURITY", 441 | "referenceLocator": "cpe:2.3:a:musl_utils:musl-utils:1.2.2-r7:*:*:*:*:*:*:*", 442 | "referenceType": "cpe23Type" 443 | }, 444 | { 445 | "referenceCategory": "SECURITY", 446 | "referenceLocator": "cpe:2.3:a:musl_utils:musl_utils:1.2.2-r7:*:*:*:*:*:*:*", 447 | "referenceType": "cpe23Type" 448 | }, 449 | { 450 | "referenceCategory": "SECURITY", 451 | "referenceLocator": "cpe:2.3:a:musl:musl-utils:1.2.2-r7:*:*:*:*:*:*:*", 452 | "referenceType": "cpe23Type" 453 | }, 454 | { 455 | "referenceCategory": "SECURITY", 456 | "referenceLocator": "cpe:2.3:a:musl:musl_utils:1.2.2-r7:*:*:*:*:*:*:*", 457 | "referenceType": "cpe23Type" 458 | }, 459 | { 460 | "referenceCategory": "PACKAGE_MANAGER", 461 | "referenceLocator": "pkg:alpine/musl-utils@1.2.2-r7?arch=x86_64&upstream=musl&distro=alpine-3.15.0", 462 | "referenceType": "purl" 463 | } 464 | ], 465 | "filesAnalyzed": false, 466 | "licenseDeclared": "MIT", 467 | "originator": "Person: Timo Teräs ", 468 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 469 | "versionInfo": "1.2.2-r7" 470 | }, 471 | { 472 | "SPDXID": "SPDXRef-2a3267dd18a70d1f", 473 | "name": "scanelf", 474 | "licenseConcluded": "GPL-2.0-only", 475 | "description": "Scan ELF binaries for stuff", 476 | "downloadLocation": "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities", 477 | "externalRefs": [ 478 | { 479 | "referenceCategory": "SECURITY", 480 | "referenceLocator": "cpe:2.3:a:scanelf:scanelf:1.3.3-r0:*:*:*:*:*:*:*", 481 | "referenceType": "cpe23Type" 482 | }, 483 | { 484 | "referenceCategory": "PACKAGE_MANAGER", 485 | "referenceLocator": "pkg:alpine/scanelf@1.3.3-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.15.0", 486 | "referenceType": "purl" 487 | } 488 | ], 489 | "filesAnalyzed": false, 490 | "licenseDeclared": "GPL-2.0-only", 491 | "originator": "Person: Natanael Copa ", 492 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 493 | "versionInfo": "1.3.3-r0" 494 | }, 495 | { 496 | "SPDXID": "SPDXRef-724ce85e6db29181", 497 | "name": "ssl_client", 498 | "licenseConcluded": "GPL-2.0-only", 499 | "description": "EXternal ssl_client for busybox wget", 500 | "downloadLocation": "https://busybox.net/", 501 | "externalRefs": [ 502 | { 503 | "referenceCategory": "SECURITY", 504 | "referenceLocator": "cpe:2.3:a:ssl-client:ssl-client:1.34.1-r3:*:*:*:*:*:*:*", 505 | "referenceType": "cpe23Type" 506 | }, 507 | { 508 | "referenceCategory": "SECURITY", 509 | "referenceLocator": "cpe:2.3:a:ssl-client:ssl_client:1.34.1-r3:*:*:*:*:*:*:*", 510 | "referenceType": "cpe23Type" 511 | }, 512 | { 513 | "referenceCategory": "SECURITY", 514 | "referenceLocator": "cpe:2.3:a:ssl_client:ssl-client:1.34.1-r3:*:*:*:*:*:*:*", 515 | "referenceType": "cpe23Type" 516 | }, 517 | { 518 | "referenceCategory": "SECURITY", 519 | "referenceLocator": "cpe:2.3:a:ssl_client:ssl_client:1.34.1-r3:*:*:*:*:*:*:*", 520 | "referenceType": "cpe23Type" 521 | }, 522 | { 523 | "referenceCategory": "SECURITY", 524 | "referenceLocator": "cpe:2.3:a:ssl:ssl-client:1.34.1-r3:*:*:*:*:*:*:*", 525 | "referenceType": "cpe23Type" 526 | }, 527 | { 528 | "referenceCategory": "SECURITY", 529 | "referenceLocator": "cpe:2.3:a:ssl:ssl_client:1.34.1-r3:*:*:*:*:*:*:*", 530 | "referenceType": "cpe23Type" 531 | }, 532 | { 533 | "referenceCategory": "PACKAGE_MANAGER", 534 | "referenceLocator": "pkg:alpine/ssl_client@1.34.1-r3?arch=x86_64&upstream=busybox&distro=alpine-3.15.0", 535 | "referenceType": "purl" 536 | } 537 | ], 538 | "filesAnalyzed": false, 539 | "licenseDeclared": "GPL-2.0-only", 540 | "originator": "Person: Natanael Copa ", 541 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 542 | "versionInfo": "1.34.1-r3" 543 | }, 544 | { 545 | "SPDXID": "SPDXRef-ff9f9a115b4ee918", 546 | "name": "zlib", 547 | "licenseConcluded": "Zlib", 548 | "description": "A compression/decompression Library", 549 | "downloadLocation": "https://zlib.net/", 550 | "externalRefs": [ 551 | { 552 | "referenceCategory": "SECURITY", 553 | "referenceLocator": "cpe:2.3:a:zlib:zlib:1.2.11-r3:*:*:*:*:*:*:*", 554 | "referenceType": "cpe23Type" 555 | }, 556 | { 557 | "referenceCategory": "PACKAGE_MANAGER", 558 | "referenceLocator": "pkg:alpine/zlib@1.2.11-r3?arch=x86_64&upstream=zlib&distro=alpine-3.15.0", 559 | "referenceType": "purl" 560 | } 561 | ], 562 | "filesAnalyzed": false, 563 | "licenseDeclared": "Zlib", 564 | "originator": "Person: Natanael Copa ", 565 | "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", 566 | "versionInfo": "1.2.11-r3" 567 | } 568 | ], 569 | "files": [ 570 | { 571 | "SPDXID": "SPDXRef-988a54d89f5c4c09", 572 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 573 | "licenseConcluded": "NOASSERTION", 574 | "fileName": "/bin/busybox" 575 | }, 576 | { 577 | "SPDXID": "SPDXRef-8ec9dcf9b3d1d7ce", 578 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 579 | "licenseConcluded": "NOASSERTION", 580 | "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub" 581 | }, 582 | { 583 | "SPDXID": "SPDXRef-39dcc03ca17480ca", 584 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 585 | "licenseConcluded": "NOASSERTION", 586 | "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub" 587 | }, 588 | { 589 | "SPDXID": "SPDXRef-4d646d694b6380fc", 590 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 591 | "licenseConcluded": "NOASSERTION", 592 | "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub" 593 | }, 594 | { 595 | "SPDXID": "SPDXRef-395f72182f48f77c", 596 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 597 | "licenseConcluded": "NOASSERTION", 598 | "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub" 599 | }, 600 | { 601 | "SPDXID": "SPDXRef-496698ff67ca49fc", 602 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 603 | "licenseConcluded": "NOASSERTION", 604 | "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-61666e3f.rsa.pub" 605 | }, 606 | { 607 | "SPDXID": "SPDXRef-2eaa15c5fc625ebe", 608 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 609 | "licenseConcluded": "NOASSERTION", 610 | "fileName": "/etc/crontabs/root" 611 | }, 612 | { 613 | "SPDXID": "SPDXRef-a53373020dfa8bb4", 614 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 615 | "licenseConcluded": "NOASSERTION", 616 | "fileName": "/etc/fstab" 617 | }, 618 | { 619 | "SPDXID": "SPDXRef-2c0eaf2a7d7dbad", 620 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 621 | "licenseConcluded": "NOASSERTION", 622 | "fileName": "/etc/group" 623 | }, 624 | { 625 | "SPDXID": "SPDXRef-f3ee626693308800", 626 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 627 | "licenseConcluded": "NOASSERTION", 628 | "fileName": "/etc/hostname" 629 | }, 630 | { 631 | "SPDXID": "SPDXRef-38605c90f707fb90", 632 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 633 | "licenseConcluded": "NOASSERTION", 634 | "fileName": "/etc/hosts" 635 | }, 636 | { 637 | "SPDXID": "SPDXRef-60fa740c32339374", 638 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 639 | "licenseConcluded": "NOASSERTION", 640 | "fileName": "/etc/inittab" 641 | }, 642 | { 643 | "SPDXID": "SPDXRef-cd1c702a19149d7d", 644 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 645 | "licenseConcluded": "NOASSERTION", 646 | "fileName": "/etc/logrotate.d/acpid" 647 | }, 648 | { 649 | "SPDXID": "SPDXRef-420fa6f3289d6ee6", 650 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 651 | "licenseConcluded": "NOASSERTION", 652 | "fileName": "/etc/modprobe.d/aliases.conf" 653 | }, 654 | { 655 | "SPDXID": "SPDXRef-ae2cba512a3f4065", 656 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 657 | "licenseConcluded": "NOASSERTION", 658 | "fileName": "/etc/modprobe.d/blacklist.conf" 659 | }, 660 | { 661 | "SPDXID": "SPDXRef-24d0f8d913cd9906", 662 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 663 | "licenseConcluded": "NOASSERTION", 664 | "fileName": "/etc/modprobe.d/i386.conf" 665 | }, 666 | { 667 | "SPDXID": "SPDXRef-d41a5f82a774a6a1", 668 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 669 | "licenseConcluded": "NOASSERTION", 670 | "fileName": "/etc/modprobe.d/kms.conf" 671 | }, 672 | { 673 | "SPDXID": "SPDXRef-dc65dbf355556024", 674 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 675 | "licenseConcluded": "NOASSERTION", 676 | "fileName": "/etc/modules" 677 | }, 678 | { 679 | "SPDXID": "SPDXRef-b3a5f05adcd1cf82", 680 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 681 | "licenseConcluded": "NOASSERTION", 682 | "fileName": "/etc/motd" 683 | }, 684 | { 685 | "SPDXID": "SPDXRef-be5355441673f6dc", 686 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 687 | "licenseConcluded": "NOASSERTION", 688 | "fileName": "/etc/network/if-up.d/dad" 689 | }, 690 | { 691 | "SPDXID": "SPDXRef-80279503190696f", 692 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 693 | "licenseConcluded": "NOASSERTION", 694 | "fileName": "/etc/nsswitch.conf" 695 | }, 696 | { 697 | "SPDXID": "SPDXRef-b499705c36475f74", 698 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 699 | "licenseConcluded": "NOASSERTION", 700 | "fileName": "/etc/passwd" 701 | }, 702 | { 703 | "SPDXID": "SPDXRef-2e3613b244458b5a", 704 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 705 | "licenseConcluded": "NOASSERTION", 706 | "fileName": "/etc/profile" 707 | }, 708 | { 709 | "SPDXID": "SPDXRef-64b20ab568341372", 710 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 711 | "licenseConcluded": "NOASSERTION", 712 | "fileName": "/etc/profile.d/README" 713 | }, 714 | { 715 | "SPDXID": "SPDXRef-84fd54b3f2a2e825", 716 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 717 | "licenseConcluded": "NOASSERTION", 718 | "fileName": "/etc/profile.d/color_prompt.sh.disabled" 719 | }, 720 | { 721 | "SPDXID": "SPDXRef-32701f6d1e056c29", 722 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 723 | "licenseConcluded": "NOASSERTION", 724 | "fileName": "/etc/profile.d/locale.sh" 725 | }, 726 | { 727 | "SPDXID": "SPDXRef-5e12c5188eeb9cb3", 728 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 729 | "licenseConcluded": "NOASSERTION", 730 | "fileName": "/etc/protocols" 731 | }, 732 | { 733 | "SPDXID": "SPDXRef-e7d6b30bf31f933a", 734 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 735 | "licenseConcluded": "NOASSERTION", 736 | "fileName": "/etc/securetty" 737 | }, 738 | { 739 | "SPDXID": "SPDXRef-9ab25fdcabefa4ac", 740 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 741 | "licenseConcluded": "NOASSERTION", 742 | "fileName": "/etc/services" 743 | }, 744 | { 745 | "SPDXID": "SPDXRef-18d9a7fcef583aeb", 746 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 747 | "licenseConcluded": "NOASSERTION", 748 | "fileName": "/etc/shadow" 749 | }, 750 | { 751 | "SPDXID": "SPDXRef-93b858998f2c7034", 752 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 753 | "licenseConcluded": "NOASSERTION", 754 | "fileName": "/etc/shells" 755 | }, 756 | { 757 | "SPDXID": "SPDXRef-b7cfa7f53a05225f", 758 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 759 | "licenseConcluded": "NOASSERTION", 760 | "fileName": "/etc/ssl/certs/ca-certificates.crt" 761 | }, 762 | { 763 | "SPDXID": "SPDXRef-d1029b42eed49dbe", 764 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 765 | "licenseConcluded": "NOASSERTION", 766 | "fileName": "/etc/ssl1.1/ct_log_list.cnf.dist" 767 | }, 768 | { 769 | "SPDXID": "SPDXRef-5f7354d1f6e1cdce", 770 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 771 | "licenseConcluded": "NOASSERTION", 772 | "fileName": "/etc/ssl1.1/openssl.cnf" 773 | }, 774 | { 775 | "SPDXID": "SPDXRef-1a7b85af7f458360", 776 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 777 | "licenseConcluded": "NOASSERTION", 778 | "fileName": "/etc/ssl1.1/openssl.cnf.dist" 779 | }, 780 | { 781 | "SPDXID": "SPDXRef-fb021b79aa9cd553", 782 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 783 | "licenseConcluded": "NOASSERTION", 784 | "fileName": "/etc/sysctl.conf" 785 | }, 786 | { 787 | "SPDXID": "SPDXRef-e6d162458c0b30b0", 788 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 789 | "licenseConcluded": "NOASSERTION", 790 | "fileName": "/etc/udhcpd.conf" 791 | }, 792 | { 793 | "SPDXID": "SPDXRef-e322847d6485c76d", 794 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 795 | "licenseConcluded": "NOASSERTION", 796 | "fileName": "/lib/ld-musl-x86_64.so.1" 797 | }, 798 | { 799 | "SPDXID": "SPDXRef-5f14b5421fba85af", 800 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 801 | "licenseConcluded": "NOASSERTION", 802 | "fileName": "/lib/libapk.so.3.12.0" 803 | }, 804 | { 805 | "SPDXID": "SPDXRef-a00e69b6cf4b0ef0", 806 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 807 | "licenseConcluded": "NOASSERTION", 808 | "fileName": "/lib/libcrypto.so.1.1" 809 | }, 810 | { 811 | "SPDXID": "SPDXRef-a64a40d78e73f3bd", 812 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 813 | "licenseConcluded": "NOASSERTION", 814 | "fileName": "/lib/libssl.so.1.1" 815 | }, 816 | { 817 | "SPDXID": "SPDXRef-bfd3d0235da50adb", 818 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 819 | "licenseConcluded": "NOASSERTION", 820 | "fileName": "/lib/libz.so.1.2.11" 821 | }, 822 | { 823 | "SPDXID": "SPDXRef-82fda88ae28dd50", 824 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 825 | "licenseConcluded": "NOASSERTION", 826 | "fileName": "/lib/sysctl.d/00-alpine.conf" 827 | }, 828 | { 829 | "SPDXID": "SPDXRef-d72447617fa2b70c", 830 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 831 | "licenseConcluded": "NOASSERTION", 832 | "fileName": "/sbin/apk" 833 | }, 834 | { 835 | "SPDXID": "SPDXRef-d59e19c68624688f", 836 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 837 | "licenseConcluded": "NOASSERTION", 838 | "fileName": "/sbin/ldconfig" 839 | }, 840 | { 841 | "SPDXID": "SPDXRef-13d6d27618d264f7", 842 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 843 | "licenseConcluded": "NOASSERTION", 844 | "fileName": "/sbin/mkmntdirs" 845 | }, 846 | { 847 | "SPDXID": "SPDXRef-757b30be1d3baa0b", 848 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 849 | "licenseConcluded": "NOASSERTION", 850 | "fileName": "/usr/bin/getconf" 851 | }, 852 | { 853 | "SPDXID": "SPDXRef-780fcf6f56cca2e0", 854 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 855 | "licenseConcluded": "NOASSERTION", 856 | "fileName": "/usr/bin/getent" 857 | }, 858 | { 859 | "SPDXID": "SPDXRef-ab2d028a906db5df", 860 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 861 | "licenseConcluded": "NOASSERTION", 862 | "fileName": "/usr/bin/iconv" 863 | }, 864 | { 865 | "SPDXID": "SPDXRef-8e69e89855b5ae0f", 866 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 867 | "licenseConcluded": "NOASSERTION", 868 | "fileName": "/usr/bin/ldd" 869 | }, 870 | { 871 | "SPDXID": "SPDXRef-7516f5d905deb6db", 872 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 873 | "licenseConcluded": "NOASSERTION", 874 | "fileName": "/usr/bin/scanelf" 875 | }, 876 | { 877 | "SPDXID": "SPDXRef-711694a1725d661e", 878 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 879 | "licenseConcluded": "NOASSERTION", 880 | "fileName": "/usr/bin/ssl_client" 881 | }, 882 | { 883 | "SPDXID": "SPDXRef-5d76bf96c623951c", 884 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 885 | "licenseConcluded": "NOASSERTION", 886 | "fileName": "/usr/glibc-compat/etc/ld.so.cache" 887 | }, 888 | { 889 | "SPDXID": "SPDXRef-9dd3e9013f1e54bd", 890 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 891 | "licenseConcluded": "NOASSERTION", 892 | "fileName": "/usr/glibc-compat/etc/ld.so.conf" 893 | }, 894 | { 895 | "SPDXID": "SPDXRef-82c96bb588e10d67", 896 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 897 | "licenseConcluded": "NOASSERTION", 898 | "fileName": "/usr/glibc-compat/lib/ld-linux-x86-64.so.2" 899 | }, 900 | { 901 | "SPDXID": "SPDXRef-4a7a2b5ae2d6b5cf", 902 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 903 | "licenseConcluded": "NOASSERTION", 904 | "fileName": "/usr/glibc-compat/lib/libBrokenLocale.so" 905 | }, 906 | { 907 | "SPDXID": "SPDXRef-52fbc70624634004", 908 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 909 | "licenseConcluded": "NOASSERTION", 910 | "fileName": "/usr/glibc-compat/lib/libBrokenLocale.so.1" 911 | }, 912 | { 913 | "SPDXID": "SPDXRef-6437f1bde3c821b0", 914 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 915 | "licenseConcluded": "NOASSERTION", 916 | "fileName": "/usr/glibc-compat/lib/libSegFault.so" 917 | }, 918 | { 919 | "SPDXID": "SPDXRef-d6ffae70409bfe15", 920 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 921 | "licenseConcluded": "NOASSERTION", 922 | "fileName": "/usr/glibc-compat/lib/libanl.so" 923 | }, 924 | { 925 | "SPDXID": "SPDXRef-3976ba8c9585221", 926 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 927 | "licenseConcluded": "NOASSERTION", 928 | "fileName": "/usr/glibc-compat/lib/libanl.so.1" 929 | }, 930 | { 931 | "SPDXID": "SPDXRef-3becf9ec8fa1f923", 932 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 933 | "licenseConcluded": "NOASSERTION", 934 | "fileName": "/usr/glibc-compat/lib/libc.so" 935 | }, 936 | { 937 | "SPDXID": "SPDXRef-af79345b79d69fe6", 938 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 939 | "licenseConcluded": "NOASSERTION", 940 | "fileName": "/usr/glibc-compat/lib/libc.so.6" 941 | }, 942 | { 943 | "SPDXID": "SPDXRef-15aba263971223d0", 944 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 945 | "licenseConcluded": "NOASSERTION", 946 | "fileName": "/usr/glibc-compat/lib/libc_malloc_debug.so" 947 | }, 948 | { 949 | "SPDXID": "SPDXRef-58a0e4430ce601f7", 950 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 951 | "licenseConcluded": "NOASSERTION", 952 | "fileName": "/usr/glibc-compat/lib/libc_malloc_debug.so.0" 953 | }, 954 | { 955 | "SPDXID": "SPDXRef-2791acf2da4e6972", 956 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 957 | "licenseConcluded": "NOASSERTION", 958 | "fileName": "/usr/glibc-compat/lib/libcrypt.so" 959 | }, 960 | { 961 | "SPDXID": "SPDXRef-30fccabe8c22fc92", 962 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 963 | "licenseConcluded": "NOASSERTION", 964 | "fileName": "/usr/glibc-compat/lib/libcrypt.so.1" 965 | }, 966 | { 967 | "SPDXID": "SPDXRef-a5b5bf375773f34a", 968 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 969 | "licenseConcluded": "NOASSERTION", 970 | "fileName": "/usr/glibc-compat/lib/libdl.so.2" 971 | }, 972 | { 973 | "SPDXID": "SPDXRef-12b5ee6878a5ce2", 974 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 975 | "licenseConcluded": "NOASSERTION", 976 | "fileName": "/usr/glibc-compat/lib/libm.so" 977 | }, 978 | { 979 | "SPDXID": "SPDXRef-db77a6ee29af0077", 980 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 981 | "licenseConcluded": "NOASSERTION", 982 | "fileName": "/usr/glibc-compat/lib/libm.so.6" 983 | }, 984 | { 985 | "SPDXID": "SPDXRef-1d18e498dc791aaf", 986 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 987 | "licenseConcluded": "NOASSERTION", 988 | "fileName": "/usr/glibc-compat/lib/libmemusage.so" 989 | }, 990 | { 991 | "SPDXID": "SPDXRef-34a0f7cb42b2af72", 992 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 993 | "licenseConcluded": "NOASSERTION", 994 | "fileName": "/usr/glibc-compat/lib/libmvec.so" 995 | }, 996 | { 997 | "SPDXID": "SPDXRef-d03e9609058d46c8", 998 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 999 | "licenseConcluded": "NOASSERTION", 1000 | "fileName": "/usr/glibc-compat/lib/libmvec.so.1" 1001 | }, 1002 | { 1003 | "SPDXID": "SPDXRef-62213fa9978c78d9", 1004 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1005 | "licenseConcluded": "NOASSERTION", 1006 | "fileName": "/usr/glibc-compat/lib/libnsl.so.1" 1007 | }, 1008 | { 1009 | "SPDXID": "SPDXRef-3f378c4e196e8ea4", 1010 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1011 | "licenseConcluded": "NOASSERTION", 1012 | "fileName": "/usr/glibc-compat/lib/libnss_compat.so" 1013 | }, 1014 | { 1015 | "SPDXID": "SPDXRef-b60b3d46dfdfc38d", 1016 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1017 | "licenseConcluded": "NOASSERTION", 1018 | "fileName": "/usr/glibc-compat/lib/libnss_compat.so.2" 1019 | }, 1020 | { 1021 | "SPDXID": "SPDXRef-4db02b2fb58a68a7", 1022 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1023 | "licenseConcluded": "NOASSERTION", 1024 | "fileName": "/usr/glibc-compat/lib/libnss_db.so" 1025 | }, 1026 | { 1027 | "SPDXID": "SPDXRef-629a7a68d2358878", 1028 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1029 | "licenseConcluded": "NOASSERTION", 1030 | "fileName": "/usr/glibc-compat/lib/libnss_db.so.2" 1031 | }, 1032 | { 1033 | "SPDXID": "SPDXRef-e3b315df562e4775", 1034 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1035 | "licenseConcluded": "NOASSERTION", 1036 | "fileName": "/usr/glibc-compat/lib/libnss_dns.so.2" 1037 | }, 1038 | { 1039 | "SPDXID": "SPDXRef-2dc670991ae2a622", 1040 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1041 | "licenseConcluded": "NOASSERTION", 1042 | "fileName": "/usr/glibc-compat/lib/libnss_files.so.2" 1043 | }, 1044 | { 1045 | "SPDXID": "SPDXRef-36f9616f0064503a", 1046 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1047 | "licenseConcluded": "NOASSERTION", 1048 | "fileName": "/usr/glibc-compat/lib/libnss_hesiod.so" 1049 | }, 1050 | { 1051 | "SPDXID": "SPDXRef-d347bde6b30cc5cd", 1052 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1053 | "licenseConcluded": "NOASSERTION", 1054 | "fileName": "/usr/glibc-compat/lib/libnss_hesiod.so.2" 1055 | }, 1056 | { 1057 | "SPDXID": "SPDXRef-e1dcb4afc4a7bdc5", 1058 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1059 | "licenseConcluded": "NOASSERTION", 1060 | "fileName": "/usr/glibc-compat/lib/libpcprofile.so" 1061 | }, 1062 | { 1063 | "SPDXID": "SPDXRef-854d214990f93289", 1064 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1065 | "licenseConcluded": "NOASSERTION", 1066 | "fileName": "/usr/glibc-compat/lib/libpthread.so.0" 1067 | }, 1068 | { 1069 | "SPDXID": "SPDXRef-fa3cece72e099ba6", 1070 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1071 | "licenseConcluded": "NOASSERTION", 1072 | "fileName": "/usr/glibc-compat/lib/libresolv.so" 1073 | }, 1074 | { 1075 | "SPDXID": "SPDXRef-e0144e1cc707f510", 1076 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1077 | "licenseConcluded": "NOASSERTION", 1078 | "fileName": "/usr/glibc-compat/lib/libresolv.so.2" 1079 | }, 1080 | { 1081 | "SPDXID": "SPDXRef-69dfcda4eedfd60a", 1082 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1083 | "licenseConcluded": "NOASSERTION", 1084 | "fileName": "/usr/glibc-compat/lib/librt.so.1" 1085 | }, 1086 | { 1087 | "SPDXID": "SPDXRef-539706b163b65dba", 1088 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1089 | "licenseConcluded": "NOASSERTION", 1090 | "fileName": "/usr/glibc-compat/lib/libthread_db.so" 1091 | }, 1092 | { 1093 | "SPDXID": "SPDXRef-5fe47f0217a28aff", 1094 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1095 | "licenseConcluded": "NOASSERTION", 1096 | "fileName": "/usr/glibc-compat/lib/libthread_db.so.1" 1097 | }, 1098 | { 1099 | "SPDXID": "SPDXRef-baadc7ba8e0f7dec", 1100 | "comment": "layerID: sha256:a90a5302c69ac77197c053287af79e1cda8a921c33c6b02b6209e45f937a6561", 1101 | "licenseConcluded": "NOASSERTION", 1102 | "fileName": "/usr/glibc-compat/lib/libutil.so.1" 1103 | }, 1104 | { 1105 | "SPDXID": "SPDXRef-4862e08252039e5", 1106 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1107 | "licenseConcluded": "NOASSERTION", 1108 | "fileName": "/usr/lib/engines-1.1/afalg.so" 1109 | }, 1110 | { 1111 | "SPDXID": "SPDXRef-f57c06db35618298", 1112 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1113 | "licenseConcluded": "NOASSERTION", 1114 | "fileName": "/usr/lib/engines-1.1/capi.so" 1115 | }, 1116 | { 1117 | "SPDXID": "SPDXRef-ba1b2107c3063563", 1118 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1119 | "licenseConcluded": "NOASSERTION", 1120 | "fileName": "/usr/lib/engines-1.1/padlock.so" 1121 | }, 1122 | { 1123 | "SPDXID": "SPDXRef-81250f1630c1a804", 1124 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1125 | "licenseConcluded": "NOASSERTION", 1126 | "fileName": "/usr/lib/libtls.so.2.0.3" 1127 | }, 1128 | { 1129 | "SPDXID": "SPDXRef-add734ec170033bd", 1130 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1131 | "licenseConcluded": "NOASSERTION", 1132 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub" 1133 | }, 1134 | { 1135 | "SPDXID": "SPDXRef-59d943ecba7b9db1", 1136 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1137 | "licenseConcluded": "NOASSERTION", 1138 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub" 1139 | }, 1140 | { 1141 | "SPDXID": "SPDXRef-27d8de5355fdb7ba", 1142 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1143 | "licenseConcluded": "NOASSERTION", 1144 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub" 1145 | }, 1146 | { 1147 | "SPDXID": "SPDXRef-ff0560ee36b984a7", 1148 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1149 | "licenseConcluded": "NOASSERTION", 1150 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub" 1151 | }, 1152 | { 1153 | "SPDXID": "SPDXRef-66756a275982c586", 1154 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1155 | "licenseConcluded": "NOASSERTION", 1156 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub" 1157 | }, 1158 | { 1159 | "SPDXID": "SPDXRef-2c8a8c151837aa6e", 1160 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1161 | "licenseConcluded": "NOASSERTION", 1162 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub" 1163 | }, 1164 | { 1165 | "SPDXID": "SPDXRef-79cc1d44454e11b9", 1166 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1167 | "licenseConcluded": "NOASSERTION", 1168 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub" 1169 | }, 1170 | { 1171 | "SPDXID": "SPDXRef-abfd85d1b45289dc", 1172 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1173 | "licenseConcluded": "NOASSERTION", 1174 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5e69ca50.rsa.pub" 1175 | }, 1176 | { 1177 | "SPDXID": "SPDXRef-56080e31fd12fe67", 1178 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1179 | "licenseConcluded": "NOASSERTION", 1180 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub" 1181 | }, 1182 | { 1183 | "SPDXID": "SPDXRef-7803dc5a1a496765", 1184 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1185 | "licenseConcluded": "NOASSERTION", 1186 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub" 1187 | }, 1188 | { 1189 | "SPDXID": "SPDXRef-ccc2b3e76affde68", 1190 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1191 | "licenseConcluded": "NOASSERTION", 1192 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-61666e3f.rsa.pub" 1193 | }, 1194 | { 1195 | "SPDXID": "SPDXRef-3562d93285c5a3c5", 1196 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1197 | "licenseConcluded": "NOASSERTION", 1198 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616a9724.rsa.pub" 1199 | }, 1200 | { 1201 | "SPDXID": "SPDXRef-57149f915867bf12", 1202 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1203 | "licenseConcluded": "NOASSERTION", 1204 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616abc23.rsa.pub" 1205 | }, 1206 | { 1207 | "SPDXID": "SPDXRef-2363acec0a71a382", 1208 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1209 | "licenseConcluded": "NOASSERTION", 1210 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ac3bc.rsa.pub" 1211 | }, 1212 | { 1213 | "SPDXID": "SPDXRef-2dac0f0b0463195c", 1214 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1215 | "licenseConcluded": "NOASSERTION", 1216 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616adfeb.rsa.pub" 1217 | }, 1218 | { 1219 | "SPDXID": "SPDXRef-187efc434122356a", 1220 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1221 | "licenseConcluded": "NOASSERTION", 1222 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ae350.rsa.pub" 1223 | }, 1224 | { 1225 | "SPDXID": "SPDXRef-f059a81847acaad9", 1226 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1227 | "licenseConcluded": "NOASSERTION", 1228 | "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616db30d.rsa.pub" 1229 | }, 1230 | { 1231 | "SPDXID": "SPDXRef-d5ee1ce0839cb21a", 1232 | "comment": "layerID: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", 1233 | "licenseConcluded": "NOASSERTION", 1234 | "fileName": "/usr/share/udhcpc/default.script" 1235 | } 1236 | ], 1237 | "relationships": [ 1238 | { 1239 | "spdxElementId": "SPDXRef-768c6a15875e1903", 1240 | "relationshipType": "CONTAINS", 1241 | "relatedSpdxElement": "SPDXRef-e322847d6485c76d" 1242 | }, 1243 | { 1244 | "spdxElementId": "SPDXRef-768c6a15875e1903", 1245 | "relationshipType": "CONTAINS", 1246 | "relatedSpdxElement": "SPDXRef-e322847d6485c76d" 1247 | }, 1248 | { 1249 | "spdxElementId": "SPDXRef-fa0b3237f7ac675f", 1250 | "relationshipType": "CONTAINS", 1251 | "relatedSpdxElement": "SPDXRef-988a54d89f5c4c09" 1252 | }, 1253 | { 1254 | "spdxElementId": "SPDXRef-fa0b3237f7ac675f", 1255 | "relationshipType": "CONTAINS", 1256 | "relatedSpdxElement": "SPDXRef-988a54d89f5c4c09" 1257 | }, 1258 | { 1259 | "spdxElementId": "SPDXRef-fa0b3237f7ac675f", 1260 | "relationshipType": "CONTAINS", 1261 | "relatedSpdxElement": "SPDXRef-cd1c702a19149d7d" 1262 | }, 1263 | { 1264 | "spdxElementId": "SPDXRef-fa0b3237f7ac675f", 1265 | "relationshipType": "CONTAINS", 1266 | "relatedSpdxElement": "SPDXRef-be5355441673f6dc" 1267 | }, 1268 | { 1269 | "spdxElementId": "SPDXRef-fa0b3237f7ac675f", 1270 | "relationshipType": "CONTAINS", 1271 | "relatedSpdxElement": "SPDXRef-e7d6b30bf31f933a" 1272 | }, 1273 | { 1274 | "spdxElementId": "SPDXRef-fa0b3237f7ac675f", 1275 | "relationshipType": "CONTAINS", 1276 | "relatedSpdxElement": "SPDXRef-e6d162458c0b30b0" 1277 | }, 1278 | { 1279 | "spdxElementId": "SPDXRef-fa0b3237f7ac675f", 1280 | "relationshipType": "CONTAINS", 1281 | "relatedSpdxElement": "SPDXRef-d5ee1ce0839cb21a" 1282 | }, 1283 | { 1284 | "spdxElementId": "SPDXRef-58efe7db3446006", 1285 | "relationshipType": "CONTAINS", 1286 | "relatedSpdxElement": "SPDXRef-2eaa15c5fc625ebe" 1287 | }, 1288 | { 1289 | "spdxElementId": "SPDXRef-58efe7db3446006", 1290 | "relationshipType": "CONTAINS", 1291 | "relatedSpdxElement": "SPDXRef-a53373020dfa8bb4" 1292 | }, 1293 | { 1294 | "spdxElementId": "SPDXRef-58efe7db3446006", 1295 | "relationshipType": "CONTAINS", 1296 | "relatedSpdxElement": "SPDXRef-2c0eaf2a7d7dbad" 1297 | }, 1298 | { 1299 | "spdxElementId": "SPDXRef-58efe7db3446006", 1300 | "relationshipType": "CONTAINS", 1301 | "relatedSpdxElement": "SPDXRef-f3ee626693308800" 1302 | }, 1303 | { 1304 | "spdxElementId": "SPDXRef-58efe7db3446006", 1305 | "relationshipType": "CONTAINS", 1306 | "relatedSpdxElement": "SPDXRef-38605c90f707fb90" 1307 | }, 1308 | { 1309 | "spdxElementId": "SPDXRef-58efe7db3446006", 1310 | "relationshipType": "CONTAINS", 1311 | "relatedSpdxElement": "SPDXRef-60fa740c32339374" 1312 | }, 1313 | { 1314 | "spdxElementId": "SPDXRef-58efe7db3446006", 1315 | "relationshipType": "CONTAINS", 1316 | "relatedSpdxElement": "SPDXRef-420fa6f3289d6ee6" 1317 | }, 1318 | { 1319 | "spdxElementId": "SPDXRef-58efe7db3446006", 1320 | "relationshipType": "CONTAINS", 1321 | "relatedSpdxElement": "SPDXRef-ae2cba512a3f4065" 1322 | }, 1323 | { 1324 | "spdxElementId": "SPDXRef-58efe7db3446006", 1325 | "relationshipType": "CONTAINS", 1326 | "relatedSpdxElement": "SPDXRef-24d0f8d913cd9906" 1327 | }, 1328 | { 1329 | "spdxElementId": "SPDXRef-58efe7db3446006", 1330 | "relationshipType": "CONTAINS", 1331 | "relatedSpdxElement": "SPDXRef-d41a5f82a774a6a1" 1332 | }, 1333 | { 1334 | "spdxElementId": "SPDXRef-58efe7db3446006", 1335 | "relationshipType": "CONTAINS", 1336 | "relatedSpdxElement": "SPDXRef-dc65dbf355556024" 1337 | }, 1338 | { 1339 | "spdxElementId": "SPDXRef-58efe7db3446006", 1340 | "relationshipType": "CONTAINS", 1341 | "relatedSpdxElement": "SPDXRef-b3a5f05adcd1cf82" 1342 | }, 1343 | { 1344 | "spdxElementId": "SPDXRef-58efe7db3446006", 1345 | "relationshipType": "CONTAINS", 1346 | "relatedSpdxElement": "SPDXRef-b499705c36475f74" 1347 | }, 1348 | { 1349 | "spdxElementId": "SPDXRef-58efe7db3446006", 1350 | "relationshipType": "CONTAINS", 1351 | "relatedSpdxElement": "SPDXRef-2e3613b244458b5a" 1352 | }, 1353 | { 1354 | "spdxElementId": "SPDXRef-58efe7db3446006", 1355 | "relationshipType": "CONTAINS", 1356 | "relatedSpdxElement": "SPDXRef-64b20ab568341372" 1357 | }, 1358 | { 1359 | "spdxElementId": "SPDXRef-58efe7db3446006", 1360 | "relationshipType": "CONTAINS", 1361 | "relatedSpdxElement": "SPDXRef-84fd54b3f2a2e825" 1362 | }, 1363 | { 1364 | "spdxElementId": "SPDXRef-58efe7db3446006", 1365 | "relationshipType": "CONTAINS", 1366 | "relatedSpdxElement": "SPDXRef-32701f6d1e056c29" 1367 | }, 1368 | { 1369 | "spdxElementId": "SPDXRef-58efe7db3446006", 1370 | "relationshipType": "CONTAINS", 1371 | "relatedSpdxElement": "SPDXRef-5e12c5188eeb9cb3" 1372 | }, 1373 | { 1374 | "spdxElementId": "SPDXRef-58efe7db3446006", 1375 | "relationshipType": "CONTAINS", 1376 | "relatedSpdxElement": "SPDXRef-9ab25fdcabefa4ac" 1377 | }, 1378 | { 1379 | "spdxElementId": "SPDXRef-58efe7db3446006", 1380 | "relationshipType": "CONTAINS", 1381 | "relatedSpdxElement": "SPDXRef-18d9a7fcef583aeb" 1382 | }, 1383 | { 1384 | "spdxElementId": "SPDXRef-58efe7db3446006", 1385 | "relationshipType": "CONTAINS", 1386 | "relatedSpdxElement": "SPDXRef-93b858998f2c7034" 1387 | }, 1388 | { 1389 | "spdxElementId": "SPDXRef-58efe7db3446006", 1390 | "relationshipType": "CONTAINS", 1391 | "relatedSpdxElement": "SPDXRef-fb021b79aa9cd553" 1392 | }, 1393 | { 1394 | "spdxElementId": "SPDXRef-58efe7db3446006", 1395 | "relationshipType": "CONTAINS", 1396 | "relatedSpdxElement": "SPDXRef-82fda88ae28dd50" 1397 | }, 1398 | { 1399 | "spdxElementId": "SPDXRef-58efe7db3446006", 1400 | "relationshipType": "CONTAINS", 1401 | "relatedSpdxElement": "SPDXRef-13d6d27618d264f7" 1402 | }, 1403 | { 1404 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1405 | "relationshipType": "CONTAINS", 1406 | "relatedSpdxElement": "SPDXRef-8ec9dcf9b3d1d7ce" 1407 | }, 1408 | { 1409 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1410 | "relationshipType": "CONTAINS", 1411 | "relatedSpdxElement": "SPDXRef-39dcc03ca17480ca" 1412 | }, 1413 | { 1414 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1415 | "relationshipType": "CONTAINS", 1416 | "relatedSpdxElement": "SPDXRef-4d646d694b6380fc" 1417 | }, 1418 | { 1419 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1420 | "relationshipType": "CONTAINS", 1421 | "relatedSpdxElement": "SPDXRef-395f72182f48f77c" 1422 | }, 1423 | { 1424 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1425 | "relationshipType": "CONTAINS", 1426 | "relatedSpdxElement": "SPDXRef-496698ff67ca49fc" 1427 | }, 1428 | { 1429 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1430 | "relationshipType": "CONTAINS", 1431 | "relatedSpdxElement": "SPDXRef-66756a275982c586" 1432 | }, 1433 | { 1434 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1435 | "relationshipType": "CONTAINS", 1436 | "relatedSpdxElement": "SPDXRef-187efc434122356a" 1437 | }, 1438 | { 1439 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1440 | "relationshipType": "CONTAINS", 1441 | "relatedSpdxElement": "SPDXRef-add734ec170033bd" 1442 | }, 1443 | { 1444 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1445 | "relationshipType": "CONTAINS", 1446 | "relatedSpdxElement": "SPDXRef-59d943ecba7b9db1" 1447 | }, 1448 | { 1449 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1450 | "relationshipType": "CONTAINS", 1451 | "relatedSpdxElement": "SPDXRef-27d8de5355fdb7ba" 1452 | }, 1453 | { 1454 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1455 | "relationshipType": "CONTAINS", 1456 | "relatedSpdxElement": "SPDXRef-ff0560ee36b984a7" 1457 | }, 1458 | { 1459 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1460 | "relationshipType": "CONTAINS", 1461 | "relatedSpdxElement": "SPDXRef-66756a275982c586" 1462 | }, 1463 | { 1464 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1465 | "relationshipType": "CONTAINS", 1466 | "relatedSpdxElement": "SPDXRef-2c8a8c151837aa6e" 1467 | }, 1468 | { 1469 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1470 | "relationshipType": "CONTAINS", 1471 | "relatedSpdxElement": "SPDXRef-79cc1d44454e11b9" 1472 | }, 1473 | { 1474 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1475 | "relationshipType": "CONTAINS", 1476 | "relatedSpdxElement": "SPDXRef-abfd85d1b45289dc" 1477 | }, 1478 | { 1479 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1480 | "relationshipType": "CONTAINS", 1481 | "relatedSpdxElement": "SPDXRef-56080e31fd12fe67" 1482 | }, 1483 | { 1484 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1485 | "relationshipType": "CONTAINS", 1486 | "relatedSpdxElement": "SPDXRef-7803dc5a1a496765" 1487 | }, 1488 | { 1489 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1490 | "relationshipType": "CONTAINS", 1491 | "relatedSpdxElement": "SPDXRef-ccc2b3e76affde68" 1492 | }, 1493 | { 1494 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1495 | "relationshipType": "CONTAINS", 1496 | "relatedSpdxElement": "SPDXRef-3562d93285c5a3c5" 1497 | }, 1498 | { 1499 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1500 | "relationshipType": "CONTAINS", 1501 | "relatedSpdxElement": "SPDXRef-57149f915867bf12" 1502 | }, 1503 | { 1504 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1505 | "relationshipType": "CONTAINS", 1506 | "relatedSpdxElement": "SPDXRef-2363acec0a71a382" 1507 | }, 1508 | { 1509 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1510 | "relationshipType": "CONTAINS", 1511 | "relatedSpdxElement": "SPDXRef-2dac0f0b0463195c" 1512 | }, 1513 | { 1514 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1515 | "relationshipType": "CONTAINS", 1516 | "relatedSpdxElement": "SPDXRef-187efc434122356a" 1517 | }, 1518 | { 1519 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1520 | "relationshipType": "CONTAINS", 1521 | "relatedSpdxElement": "SPDXRef-f059a81847acaad9" 1522 | }, 1523 | { 1524 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1525 | "relationshipType": "CONTAINS", 1526 | "relatedSpdxElement": "SPDXRef-27d8de5355fdb7ba" 1527 | }, 1528 | { 1529 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1530 | "relationshipType": "CONTAINS", 1531 | "relatedSpdxElement": "SPDXRef-3562d93285c5a3c5" 1532 | }, 1533 | { 1534 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1535 | "relationshipType": "CONTAINS", 1536 | "relatedSpdxElement": "SPDXRef-27d8de5355fdb7ba" 1537 | }, 1538 | { 1539 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1540 | "relationshipType": "CONTAINS", 1541 | "relatedSpdxElement": "SPDXRef-2dac0f0b0463195c" 1542 | }, 1543 | { 1544 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1545 | "relationshipType": "CONTAINS", 1546 | "relatedSpdxElement": "SPDXRef-abfd85d1b45289dc" 1547 | }, 1548 | { 1549 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1550 | "relationshipType": "CONTAINS", 1551 | "relatedSpdxElement": "SPDXRef-2c8a8c151837aa6e" 1552 | }, 1553 | { 1554 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1555 | "relationshipType": "CONTAINS", 1556 | "relatedSpdxElement": "SPDXRef-57149f915867bf12" 1557 | }, 1558 | { 1559 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1560 | "relationshipType": "CONTAINS", 1561 | "relatedSpdxElement": "SPDXRef-56080e31fd12fe67" 1562 | }, 1563 | { 1564 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1565 | "relationshipType": "CONTAINS", 1566 | "relatedSpdxElement": "SPDXRef-f059a81847acaad9" 1567 | }, 1568 | { 1569 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1570 | "relationshipType": "CONTAINS", 1571 | "relatedSpdxElement": "SPDXRef-79cc1d44454e11b9" 1572 | }, 1573 | { 1574 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1575 | "relationshipType": "CONTAINS", 1576 | "relatedSpdxElement": "SPDXRef-2363acec0a71a382" 1577 | }, 1578 | { 1579 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1580 | "relationshipType": "CONTAINS", 1581 | "relatedSpdxElement": "SPDXRef-add734ec170033bd" 1582 | }, 1583 | { 1584 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1585 | "relationshipType": "CONTAINS", 1586 | "relatedSpdxElement": "SPDXRef-59d943ecba7b9db1" 1587 | }, 1588 | { 1589 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1590 | "relationshipType": "CONTAINS", 1591 | "relatedSpdxElement": "SPDXRef-ccc2b3e76affde68" 1592 | }, 1593 | { 1594 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1595 | "relationshipType": "CONTAINS", 1596 | "relatedSpdxElement": "SPDXRef-add734ec170033bd" 1597 | }, 1598 | { 1599 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1600 | "relationshipType": "CONTAINS", 1601 | "relatedSpdxElement": "SPDXRef-ff0560ee36b984a7" 1602 | }, 1603 | { 1604 | "spdxElementId": "SPDXRef-fcb0c1584e831bc", 1605 | "relationshipType": "CONTAINS", 1606 | "relatedSpdxElement": "SPDXRef-7803dc5a1a496765" 1607 | }, 1608 | { 1609 | "spdxElementId": "SPDXRef-7eb965466c0157d3", 1610 | "relationshipType": "CONTAINS", 1611 | "relatedSpdxElement": "SPDXRef-b7cfa7f53a05225f" 1612 | }, 1613 | { 1614 | "spdxElementId": "SPDXRef-7eb965466c0157d3", 1615 | "relationshipType": "CONTAINS", 1616 | "relatedSpdxElement": "SPDXRef-b7cfa7f53a05225f" 1617 | }, 1618 | { 1619 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1620 | "relationshipType": "CONTAINS", 1621 | "relatedSpdxElement": "SPDXRef-b7cfa7f53a05225f" 1622 | }, 1623 | { 1624 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1625 | "relationshipType": "CONTAINS", 1626 | "relatedSpdxElement": "SPDXRef-d1029b42eed49dbe" 1627 | }, 1628 | { 1629 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1630 | "relationshipType": "CONTAINS", 1631 | "relatedSpdxElement": "SPDXRef-5f7354d1f6e1cdce" 1632 | }, 1633 | { 1634 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1635 | "relationshipType": "CONTAINS", 1636 | "relatedSpdxElement": "SPDXRef-1a7b85af7f458360" 1637 | }, 1638 | { 1639 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1640 | "relationshipType": "CONTAINS", 1641 | "relatedSpdxElement": "SPDXRef-a00e69b6cf4b0ef0" 1642 | }, 1643 | { 1644 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1645 | "relationshipType": "CONTAINS", 1646 | "relatedSpdxElement": "SPDXRef-4862e08252039e5" 1647 | }, 1648 | { 1649 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1650 | "relationshipType": "CONTAINS", 1651 | "relatedSpdxElement": "SPDXRef-f57c06db35618298" 1652 | }, 1653 | { 1654 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1655 | "relationshipType": "CONTAINS", 1656 | "relatedSpdxElement": "SPDXRef-ba1b2107c3063563" 1657 | }, 1658 | { 1659 | "spdxElementId": "SPDXRef-e095a8632defc75a", 1660 | "relationshipType": "CONTAINS", 1661 | "relatedSpdxElement": "SPDXRef-a00e69b6cf4b0ef0" 1662 | }, 1663 | { 1664 | "spdxElementId": "SPDXRef-86f2f98b49f3bc79", 1665 | "relationshipType": "CONTAINS", 1666 | "relatedSpdxElement": "SPDXRef-a64a40d78e73f3bd" 1667 | }, 1668 | { 1669 | "spdxElementId": "SPDXRef-86f2f98b49f3bc79", 1670 | "relationshipType": "CONTAINS", 1671 | "relatedSpdxElement": "SPDXRef-a64a40d78e73f3bd" 1672 | }, 1673 | { 1674 | "spdxElementId": "SPDXRef-85be40658b5f830b", 1675 | "relationshipType": "CONTAINS", 1676 | "relatedSpdxElement": "SPDXRef-81250f1630c1a804" 1677 | }, 1678 | { 1679 | "spdxElementId": "SPDXRef-85be40658b5f830b", 1680 | "relationshipType": "CONTAINS", 1681 | "relatedSpdxElement": "SPDXRef-81250f1630c1a804" 1682 | }, 1683 | { 1684 | "spdxElementId": "SPDXRef-724ce85e6db29181", 1685 | "relationshipType": "CONTAINS", 1686 | "relatedSpdxElement": "SPDXRef-711694a1725d661e" 1687 | }, 1688 | { 1689 | "spdxElementId": "SPDXRef-ff9f9a115b4ee918", 1690 | "relationshipType": "CONTAINS", 1691 | "relatedSpdxElement": "SPDXRef-bfd3d0235da50adb" 1692 | }, 1693 | { 1694 | "spdxElementId": "SPDXRef-ff9f9a115b4ee918", 1695 | "relationshipType": "CONTAINS", 1696 | "relatedSpdxElement": "SPDXRef-bfd3d0235da50adb" 1697 | }, 1698 | { 1699 | "spdxElementId": "SPDXRef-63e383018474a687", 1700 | "relationshipType": "CONTAINS", 1701 | "relatedSpdxElement": "SPDXRef-5f14b5421fba85af" 1702 | }, 1703 | { 1704 | "spdxElementId": "SPDXRef-63e383018474a687", 1705 | "relationshipType": "CONTAINS", 1706 | "relatedSpdxElement": "SPDXRef-d72447617fa2b70c" 1707 | }, 1708 | { 1709 | "spdxElementId": "SPDXRef-2a3267dd18a70d1f", 1710 | "relationshipType": "CONTAINS", 1711 | "relatedSpdxElement": "SPDXRef-7516f5d905deb6db" 1712 | }, 1713 | { 1714 | "spdxElementId": "SPDXRef-33b44afe74f963db", 1715 | "relationshipType": "CONTAINS", 1716 | "relatedSpdxElement": "SPDXRef-d59e19c68624688f" 1717 | }, 1718 | { 1719 | "spdxElementId": "SPDXRef-33b44afe74f963db", 1720 | "relationshipType": "CONTAINS", 1721 | "relatedSpdxElement": "SPDXRef-757b30be1d3baa0b" 1722 | }, 1723 | { 1724 | "spdxElementId": "SPDXRef-33b44afe74f963db", 1725 | "relationshipType": "CONTAINS", 1726 | "relatedSpdxElement": "SPDXRef-780fcf6f56cca2e0" 1727 | }, 1728 | { 1729 | "spdxElementId": "SPDXRef-33b44afe74f963db", 1730 | "relationshipType": "CONTAINS", 1731 | "relatedSpdxElement": "SPDXRef-ab2d028a906db5df" 1732 | }, 1733 | { 1734 | "spdxElementId": "SPDXRef-33b44afe74f963db", 1735 | "relationshipType": "CONTAINS", 1736 | "relatedSpdxElement": "SPDXRef-8e69e89855b5ae0f" 1737 | }, 1738 | { 1739 | "spdxElementId": "SPDXRef-c79db4d080889701", 1740 | "relationshipType": "CONTAINS", 1741 | "relatedSpdxElement": "SPDXRef-5d76bf96c623951c" 1742 | }, 1743 | { 1744 | "spdxElementId": "SPDXRef-c79db4d080889701", 1745 | "relationshipType": "CONTAINS", 1746 | "relatedSpdxElement": "SPDXRef-80279503190696f" 1747 | }, 1748 | { 1749 | "spdxElementId": "SPDXRef-c79db4d080889701", 1750 | "relationshipType": "CONTAINS", 1751 | "relatedSpdxElement": "SPDXRef-82c96bb588e10d67" 1752 | }, 1753 | { 1754 | "spdxElementId": "SPDXRef-c79db4d080889701", 1755 | "relationshipType": "CONTAINS", 1756 | "relatedSpdxElement": "SPDXRef-82c96bb588e10d67" 1757 | }, 1758 | { 1759 | "spdxElementId": "SPDXRef-c79db4d080889701", 1760 | "relationshipType": "CONTAINS", 1761 | "relatedSpdxElement": "SPDXRef-5d76bf96c623951c" 1762 | }, 1763 | { 1764 | "spdxElementId": "SPDXRef-c79db4d080889701", 1765 | "relationshipType": "CONTAINS", 1766 | "relatedSpdxElement": "SPDXRef-9dd3e9013f1e54bd" 1767 | }, 1768 | { 1769 | "spdxElementId": "SPDXRef-c79db4d080889701", 1770 | "relationshipType": "CONTAINS", 1771 | "relatedSpdxElement": "SPDXRef-82c96bb588e10d67" 1772 | }, 1773 | { 1774 | "spdxElementId": "SPDXRef-c79db4d080889701", 1775 | "relationshipType": "CONTAINS", 1776 | "relatedSpdxElement": "SPDXRef-4a7a2b5ae2d6b5cf" 1777 | }, 1778 | { 1779 | "spdxElementId": "SPDXRef-c79db4d080889701", 1780 | "relationshipType": "CONTAINS", 1781 | "relatedSpdxElement": "SPDXRef-52fbc70624634004" 1782 | }, 1783 | { 1784 | "spdxElementId": "SPDXRef-c79db4d080889701", 1785 | "relationshipType": "CONTAINS", 1786 | "relatedSpdxElement": "SPDXRef-6437f1bde3c821b0" 1787 | }, 1788 | { 1789 | "spdxElementId": "SPDXRef-c79db4d080889701", 1790 | "relationshipType": "CONTAINS", 1791 | "relatedSpdxElement": "SPDXRef-d6ffae70409bfe15" 1792 | }, 1793 | { 1794 | "spdxElementId": "SPDXRef-c79db4d080889701", 1795 | "relationshipType": "CONTAINS", 1796 | "relatedSpdxElement": "SPDXRef-3976ba8c9585221" 1797 | }, 1798 | { 1799 | "spdxElementId": "SPDXRef-c79db4d080889701", 1800 | "relationshipType": "CONTAINS", 1801 | "relatedSpdxElement": "SPDXRef-3becf9ec8fa1f923" 1802 | }, 1803 | { 1804 | "spdxElementId": "SPDXRef-c79db4d080889701", 1805 | "relationshipType": "CONTAINS", 1806 | "relatedSpdxElement": "SPDXRef-af79345b79d69fe6" 1807 | }, 1808 | { 1809 | "spdxElementId": "SPDXRef-c79db4d080889701", 1810 | "relationshipType": "CONTAINS", 1811 | "relatedSpdxElement": "SPDXRef-15aba263971223d0" 1812 | }, 1813 | { 1814 | "spdxElementId": "SPDXRef-c79db4d080889701", 1815 | "relationshipType": "CONTAINS", 1816 | "relatedSpdxElement": "SPDXRef-58a0e4430ce601f7" 1817 | }, 1818 | { 1819 | "spdxElementId": "SPDXRef-c79db4d080889701", 1820 | "relationshipType": "CONTAINS", 1821 | "relatedSpdxElement": "SPDXRef-2791acf2da4e6972" 1822 | }, 1823 | { 1824 | "spdxElementId": "SPDXRef-c79db4d080889701", 1825 | "relationshipType": "CONTAINS", 1826 | "relatedSpdxElement": "SPDXRef-30fccabe8c22fc92" 1827 | }, 1828 | { 1829 | "spdxElementId": "SPDXRef-c79db4d080889701", 1830 | "relationshipType": "CONTAINS", 1831 | "relatedSpdxElement": "SPDXRef-a5b5bf375773f34a" 1832 | }, 1833 | { 1834 | "spdxElementId": "SPDXRef-c79db4d080889701", 1835 | "relationshipType": "CONTAINS", 1836 | "relatedSpdxElement": "SPDXRef-12b5ee6878a5ce2" 1837 | }, 1838 | { 1839 | "spdxElementId": "SPDXRef-c79db4d080889701", 1840 | "relationshipType": "CONTAINS", 1841 | "relatedSpdxElement": "SPDXRef-db77a6ee29af0077" 1842 | }, 1843 | { 1844 | "spdxElementId": "SPDXRef-c79db4d080889701", 1845 | "relationshipType": "CONTAINS", 1846 | "relatedSpdxElement": "SPDXRef-1d18e498dc791aaf" 1847 | }, 1848 | { 1849 | "spdxElementId": "SPDXRef-c79db4d080889701", 1850 | "relationshipType": "CONTAINS", 1851 | "relatedSpdxElement": "SPDXRef-34a0f7cb42b2af72" 1852 | }, 1853 | { 1854 | "spdxElementId": "SPDXRef-c79db4d080889701", 1855 | "relationshipType": "CONTAINS", 1856 | "relatedSpdxElement": "SPDXRef-d03e9609058d46c8" 1857 | }, 1858 | { 1859 | "spdxElementId": "SPDXRef-c79db4d080889701", 1860 | "relationshipType": "CONTAINS", 1861 | "relatedSpdxElement": "SPDXRef-62213fa9978c78d9" 1862 | }, 1863 | { 1864 | "spdxElementId": "SPDXRef-c79db4d080889701", 1865 | "relationshipType": "CONTAINS", 1866 | "relatedSpdxElement": "SPDXRef-3f378c4e196e8ea4" 1867 | }, 1868 | { 1869 | "spdxElementId": "SPDXRef-c79db4d080889701", 1870 | "relationshipType": "CONTAINS", 1871 | "relatedSpdxElement": "SPDXRef-b60b3d46dfdfc38d" 1872 | }, 1873 | { 1874 | "spdxElementId": "SPDXRef-c79db4d080889701", 1875 | "relationshipType": "CONTAINS", 1876 | "relatedSpdxElement": "SPDXRef-4db02b2fb58a68a7" 1877 | }, 1878 | { 1879 | "spdxElementId": "SPDXRef-c79db4d080889701", 1880 | "relationshipType": "CONTAINS", 1881 | "relatedSpdxElement": "SPDXRef-629a7a68d2358878" 1882 | }, 1883 | { 1884 | "spdxElementId": "SPDXRef-c79db4d080889701", 1885 | "relationshipType": "CONTAINS", 1886 | "relatedSpdxElement": "SPDXRef-e3b315df562e4775" 1887 | }, 1888 | { 1889 | "spdxElementId": "SPDXRef-c79db4d080889701", 1890 | "relationshipType": "CONTAINS", 1891 | "relatedSpdxElement": "SPDXRef-2dc670991ae2a622" 1892 | }, 1893 | { 1894 | "spdxElementId": "SPDXRef-c79db4d080889701", 1895 | "relationshipType": "CONTAINS", 1896 | "relatedSpdxElement": "SPDXRef-36f9616f0064503a" 1897 | }, 1898 | { 1899 | "spdxElementId": "SPDXRef-c79db4d080889701", 1900 | "relationshipType": "CONTAINS", 1901 | "relatedSpdxElement": "SPDXRef-d347bde6b30cc5cd" 1902 | }, 1903 | { 1904 | "spdxElementId": "SPDXRef-c79db4d080889701", 1905 | "relationshipType": "CONTAINS", 1906 | "relatedSpdxElement": "SPDXRef-e1dcb4afc4a7bdc5" 1907 | }, 1908 | { 1909 | "spdxElementId": "SPDXRef-c79db4d080889701", 1910 | "relationshipType": "CONTAINS", 1911 | "relatedSpdxElement": "SPDXRef-854d214990f93289" 1912 | }, 1913 | { 1914 | "spdxElementId": "SPDXRef-c79db4d080889701", 1915 | "relationshipType": "CONTAINS", 1916 | "relatedSpdxElement": "SPDXRef-fa3cece72e099ba6" 1917 | }, 1918 | { 1919 | "spdxElementId": "SPDXRef-c79db4d080889701", 1920 | "relationshipType": "CONTAINS", 1921 | "relatedSpdxElement": "SPDXRef-e0144e1cc707f510" 1922 | }, 1923 | { 1924 | "spdxElementId": "SPDXRef-c79db4d080889701", 1925 | "relationshipType": "CONTAINS", 1926 | "relatedSpdxElement": "SPDXRef-69dfcda4eedfd60a" 1927 | }, 1928 | { 1929 | "spdxElementId": "SPDXRef-c79db4d080889701", 1930 | "relationshipType": "CONTAINS", 1931 | "relatedSpdxElement": "SPDXRef-539706b163b65dba" 1932 | }, 1933 | { 1934 | "spdxElementId": "SPDXRef-c79db4d080889701", 1935 | "relationshipType": "CONTAINS", 1936 | "relatedSpdxElement": "SPDXRef-5fe47f0217a28aff" 1937 | }, 1938 | { 1939 | "spdxElementId": "SPDXRef-c79db4d080889701", 1940 | "relationshipType": "CONTAINS", 1941 | "relatedSpdxElement": "SPDXRef-baadc7ba8e0f7dec" 1942 | }, 1943 | { 1944 | "spdxElementId": "SPDXRef-c79db4d080889701", 1945 | "relationshipType": "CONTAINS", 1946 | "relatedSpdxElement": "SPDXRef-82c96bb588e10d67" 1947 | } 1948 | ] 1949 | } 1950 | -------------------------------------------------------------------------------- /tests/fixtures/yarn-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yarn-project", 3 | "version": "2.5.34", 4 | "description": "Basic Yarn-based project", 5 | "main": "index.js", 6 | "author": "test@test", 7 | "license": "MIT", 8 | "dependencies": { 9 | "react": "16", 10 | "trim": "0.0.2" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/fixtures/yarn-project/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "js-tokens@^3.0.0 || ^4.0.0": 6 | version "4.0.0" 7 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 8 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 9 | 10 | loose-envify@^1.1.0, loose-envify@^1.4.0: 11 | version "1.4.0" 12 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 13 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== 14 | dependencies: 15 | js-tokens "^3.0.0 || ^4.0.0" 16 | 17 | object-assign@^4.1.1: 18 | version "4.1.1" 19 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 20 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 21 | 22 | prop-types@^15.6.2: 23 | version "15.7.2" 24 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" 25 | integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== 26 | dependencies: 27 | loose-envify "^1.4.0" 28 | object-assign "^4.1.1" 29 | react-is "^16.8.1" 30 | 31 | react-is@^16.8.1: 32 | version "16.13.1" 33 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" 34 | integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== 35 | 36 | react@16: 37 | version "16.14.0" 38 | resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" 39 | integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== 40 | dependencies: 41 | loose-envify "^1.1.0" 42 | object-assign "^4.1.1" 43 | prop-types "^15.6.2" 44 | 45 | trim@0.0.2: 46 | version "0.0.2" 47 | resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.2.tgz#b41afc68d6b5fc1a1fceb47b2ac91da258a071d4" 48 | integrity sha512-kTIK/cS0xM3jxJ7toUHlFTxHgix/kmmBgOiqc0gUAoW+NjIRsMB3vkjgAth5XEghYFCQxOdF0p/PHrv1BqTHgA== 49 | -------------------------------------------------------------------------------- /tests/grype_command.test.js: -------------------------------------------------------------------------------- 1 | const githubActionsExec = require("@actions/exec"); 2 | const githubActionsToolCache = require("@actions/tool-cache"); 3 | const { mock, cleanup, mockIO } = require("./mocks"); 4 | 5 | jest.setTimeout(90000); // 90 seconds; tests were timing out in CI. https://github.com/anchore/scan-action/pull/249 6 | 7 | describe("Grype command args", () => { 8 | afterEach(cleanup); 9 | 10 | it("is invoked with dir", async () => { 11 | const args = await mockRun({ 12 | source: "dir:.", 13 | "fail-build": "false", 14 | "output-file": "the-output-file", 15 | "output-format": "sarif", 16 | "severity-cutoff": "high", 17 | version: "0.6.0", 18 | "only-fixed": "false", 19 | "add-cpes-if-none": "false", 20 | "by-cve": "false", 21 | }); 22 | expect(args).toEqual([ 23 | "-o", 24 | "sarif", 25 | "--file", 26 | "the-output-file", 27 | "--fail-on", 28 | "high", 29 | "dir:.", 30 | ]); 31 | }); 32 | 33 | it("is invoked with cyclonedx output", async () => { 34 | const args = await mockRun({ 35 | source: "dir:.", 36 | "fail-build": "false", 37 | "output-file": "the-output-file", 38 | "output-format": "cyclonedx-xml", 39 | "severity-cutoff": "high", 40 | version: "0.6.0", 41 | "only-fixed": "false", 42 | "add-cpes-if-none": "false", 43 | "by-cve": "false", 44 | }); 45 | expect(args).toEqual([ 46 | "-o", 47 | "cyclonedx-xml", 48 | "--file", 49 | "the-output-file", 50 | "--fail-on", 51 | "high", 52 | "dir:.", 53 | ]); 54 | }); 55 | 56 | it("is invoked with cyclonedx-json output", async () => { 57 | const args = await mockRun({ 58 | source: "dir:.", 59 | "fail-build": "false", 60 | "output-file": "the-output-file", 61 | "output-format": "cyclonedx-json", 62 | "severity-cutoff": "high", 63 | version: "0.6.0", 64 | "only-fixed": "false", 65 | "add-cpes-if-none": "false", 66 | "by-cve": "false", 67 | }); 68 | expect(args).toEqual([ 69 | "-o", 70 | "cyclonedx-json", 71 | "--file", 72 | "the-output-file", 73 | "--fail-on", 74 | "high", 75 | "dir:.", 76 | ]); 77 | }); 78 | 79 | it("is invoked with values", async () => { 80 | const args = await mockRun({ 81 | image: "asdf", 82 | "fail-build": "false", 83 | "output-file": "the-output-file", 84 | "output-format": "json", 85 | "severity-cutoff": "low", 86 | version: "0.6.0", 87 | "only-fixed": "false", 88 | "add-cpes-if-none": "false", 89 | "by-cve": "false", 90 | }); 91 | expect(args).toEqual([ 92 | "-o", 93 | "json", 94 | "--file", 95 | "the-output-file", 96 | "--fail-on", 97 | "low", 98 | "asdf", 99 | ]); 100 | }); 101 | 102 | it("adds missing CPEs if requested", async () => { 103 | const args = await mockRun({ 104 | image: "asdf", 105 | "fail-build": "false", 106 | "output-file": "the-output-file", 107 | "output-format": "json", 108 | "severity-cutoff": "low", 109 | version: "0.6.0", 110 | "only-fixed": "false", 111 | "add-cpes-if-none": "true", 112 | "by-cve": "false", 113 | }); 114 | expect(args).toEqual([ 115 | "-o", 116 | "json", 117 | "--file", 118 | "the-output-file", 119 | "--fail-on", 120 | "low", 121 | "--add-cpes-if-none", 122 | "asdf", 123 | ]); 124 | }); 125 | 126 | it("adds VEX processing if requested", async () => { 127 | const args = await mockRun({ 128 | image: "asdf", 129 | "fail-build": "false", 130 | "output-file": "the-output-file", 131 | "output-format": "json", 132 | "severity-cutoff": "low", 133 | version: "0.6.0", 134 | "only-fixed": "false", 135 | "add-cpes-if-none": "true", 136 | "by-cve": "false", 137 | vex: "test.vex", 138 | }); 139 | expect(args).toEqual([ 140 | "-o", 141 | "json", 142 | "--file", 143 | "the-output-file", 144 | "--fail-on", 145 | "low", 146 | "--add-cpes-if-none", 147 | "--vex", 148 | "test.vex", 149 | "asdf", 150 | ]); 151 | }); 152 | 153 | it("with path by cve", async () => { 154 | const args = await mockRun({ 155 | path: "asdf", 156 | "fail-build": "false", 157 | "output-file": "the-output-file", 158 | "output-format": "table", 159 | "severity-cutoff": "low", 160 | "by-cve": "true", 161 | }); 162 | expect(args).toEqual([ 163 | "-o", 164 | "table", 165 | "--file", 166 | "the-output-file", 167 | "--fail-on", 168 | "low", 169 | "--by-cve", 170 | "dir:asdf", 171 | ]); 172 | }); 173 | }); 174 | 175 | async function mockRun(inputs) { 176 | // don't bother downloading grype 177 | mock(githubActionsToolCache, { 178 | find() { 179 | return "grype"; 180 | }, 181 | }); 182 | 183 | // track last exec calls args, pretend any call succeeds 184 | let callArgs; 185 | mock(githubActionsExec, { 186 | async exec(cmd, args) { 187 | callArgs = args; 188 | return 0; 189 | }, 190 | }); 191 | 192 | mockIO(inputs); 193 | 194 | try { 195 | const { run } = require("../index"); 196 | await run(); 197 | } catch (e) { 198 | e; // ignore: this happens trying to parse command output, which we don't care about 199 | } 200 | 201 | // get last invocation args, ignoring the grype binary part and -vv 202 | return (callArgs || []).filter((a) => a !== "-vv"); 203 | } 204 | -------------------------------------------------------------------------------- /tests/mocks.js: -------------------------------------------------------------------------------- 1 | const githubActionsCore = require("@actions/core"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const os = require("os"); 5 | const process = require("process"); 6 | 7 | const cleanups = []; 8 | 9 | module.exports = { 10 | onCleanup(fn) { 11 | cleanups.push(fn); 12 | }, 13 | 14 | cleanup() { 15 | for (let fn = cleanups.pop(); fn; fn = cleanups.pop()) { 16 | fn(); 17 | } 18 | jest.restoreAllMocks(); 19 | }, 20 | 21 | mock(lib, overrides) { 22 | for (const name of Object.keys(overrides)) { 23 | module.exports.onCleanup( 24 | jest.spyOn(lib, name).mockImplementation(overrides[name]).mockRestore, 25 | ); 26 | } 27 | }, 28 | 29 | mockIO(inputs) { 30 | const outputs = {}; 31 | module.exports.mock(githubActionsCore, { 32 | getInput(name) { 33 | return inputs[name]; 34 | }, 35 | setOutput(name, value) { 36 | outputs[name] = value; 37 | }, 38 | // ignore setFailed calls that set process.exitCode due to https://github.com/jestjs/jest/issues/14501 39 | setFailed() {}, 40 | }); 41 | return outputs; 42 | }, 43 | 44 | setEnv(env) { 45 | const originalValues = {}; 46 | for (const k of Object.keys(env)) { 47 | if (k in process.env) { 48 | originalValues[k] = process.env[k]; 49 | } 50 | process.env[k] = env[k]; 51 | } 52 | module.exports.onCleanup(() => { 53 | for (const k of Object.keys(env)) { 54 | if (k in originalValues) { 55 | process.env[k] = originalValues[k]; 56 | } else { 57 | delete process.env[k]; 58 | } 59 | } 60 | }); 61 | }, 62 | 63 | tmpdir() { 64 | const dir = fs.mkdtempSync(path.join(os.tmpdir(), "scan-action-test-")); 65 | module.exports.onCleanup(() => { 66 | if (fs.existsSync(dir)) { 67 | fs.rmSync(dir, { recursive: true }); 68 | } 69 | }); 70 | return dir; 71 | }, 72 | 73 | // runAction mocks input with the provided input, captures stdout, 74 | // sets test db environment variables, and runs the action 75 | async runAction(inputs) { 76 | const outputs = module.exports.mockIO(inputs); 77 | 78 | const { run } = require("../index"); 79 | 80 | let failure = ""; 81 | let stdout = ""; 82 | 83 | const append = (...args) => { 84 | stdout += args.join(" ") + "\n"; 85 | }; 86 | 87 | module.exports.setEnv({ 88 | GRYPE_DB_AUTO_UPDATE: "false", 89 | GRYPE_DB_VALIDATE_AGE: "false", 90 | }); 91 | 92 | module.exports.mock(githubActionsCore, { 93 | error: append, 94 | info: append, 95 | debug: append, 96 | setFailed: (...args) => { 97 | append(...args); 98 | failure = args.join(" "); 99 | }, 100 | }); 101 | 102 | module.exports.mock(console, { 103 | log: append, 104 | error: append, 105 | warn: append, 106 | info: append, 107 | debug: append, 108 | trace: append, 109 | }); 110 | 111 | await run(); 112 | 113 | return { 114 | stdout, 115 | failure, 116 | outputs, 117 | }; 118 | }, 119 | }; 120 | -------------------------------------------------------------------------------- /tests/sarif_output.test.js: -------------------------------------------------------------------------------- 1 | require("@microsoft/jest-sarif"); // for sarif validation 2 | 3 | const fs = require("fs"); 4 | const { runScan } = require("../index"); 5 | 6 | jest.setTimeout(90000); // 90 seconds; tests were timing out in CI. https://github.com/anchore/scan-action/pull/249 7 | 8 | const testSource = async (source, vulnerabilities) => { 9 | if (fs.existsSync("./vulnerabilities.json")) { 10 | fs.unlinkSync("./vulnerabilities.json"); 11 | } 12 | if (fs.existsSync("./results.sarif")) { 13 | fs.unlinkSync("./results.sarif"); 14 | } 15 | 16 | const out = await runScan({ 17 | source, 18 | failBuild: "false", 19 | outputFormat: "sarif", 20 | severityCutoff: "medium", 21 | onlyFixed: "false", 22 | addCpesIfNone: "false", 23 | byCve: "false", 24 | }); 25 | 26 | // expect to get sarif output 27 | const sarifFile = fs.readFileSync(out.sarif, "utf8"); 28 | expect(sarifFile).not.toBeNull(); 29 | 30 | // expect the sarif to be valid 31 | const sarif = JSON.parse(sarifFile); 32 | expect(sarif).toBeValidSarifLog(); 33 | 34 | if (sarif.runs && sarif.runs.length > 0) { 35 | sarif.runs[0].tool.driver.version = ""; 36 | } 37 | 38 | for (let run of sarif.runs || []) { 39 | for (let result of run.results || []) { 40 | for (let loc of result.locations || []) { 41 | for (let l of loc.logicalLocations || []) { 42 | l.fullyQualifiedName = ""; 43 | } 44 | } 45 | } 46 | } 47 | 48 | // expect to find some known error-level vulnerability 49 | if (vulnerabilities.length === 0) { 50 | expect(sarif.runs[0].results.length).toBe(0); 51 | } else { 52 | vulnerabilities.forEach((vuln) => { 53 | expect(sarif.runs[0].results.find((r) => r.ruleId === vuln)).toBeTruthy(); 54 | }); 55 | } 56 | 57 | return sarif; 58 | }; 59 | 60 | describe("SARIF", () => { 61 | it("alpine", async () => { 62 | const sarif = await testSource( 63 | "localhost:5000/match-coverage/alpine:latest", 64 | ["CVE-2014-6051-libvncserver"], 65 | ); 66 | expect(sarif).toBeValidSarifLog(); 67 | }); 68 | it("centos", async () => { 69 | await testSource("localhost:5000/match-coverage/centos:latest", []); 70 | }); 71 | it("debian", async () => { 72 | const sarif = await testSource( 73 | "localhost:5000/match-coverage/debian:latest", 74 | ["GHSA-fp4w-jxhp-m23p-bundler", "GHSA-9w8r-397f-prfh-pygments"], 75 | ); 76 | expect(sarif).toBeValidSarifLog(); 77 | }); 78 | it("npm", async () => { 79 | const sarif = await testSource("dir:tests/fixtures/npm-project", [ 80 | "GHSA-3jfq-g458-7qm9-tar", 81 | ]); 82 | expect(sarif).toBeValidSarifLog(); 83 | }); 84 | it("yarn", async () => { 85 | const sarif = await testSource("dir:tests/fixtures/yarn-project", [ 86 | "GHSA-w5p7-h5w8-2hfq-trim", 87 | ]); 88 | expect(sarif).toBeValidSarifLog(); 89 | }); 90 | }); 91 | --------------------------------------------------------------------------------