Compare commits

..
58 Commits
Author SHA1 Message Date
Alex Goodman 1bf1e643eb restore original release token
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-15 14:41:28 -04:00
Alex Goodman 3ba67a6bcc update json schema to account for optional gemspec fields (#225)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-15 13:56:59 -04:00
Alex Goodman 4a8ee15e87 fix syft version verbiage
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-15 11:50:23 -04:00
Alex Goodman dcb5d6d08e remove greeter action since it is broken for forked PRs (#223)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-15 10:20:09 -04:00
Alex Goodman b3387807c1 allow for gemspec metadata fields to be optional (#218)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-15 09:57:58 -04:00
Alex Goodman 24c4c025e1 Merge pull request #217 from jhujasonw/main
Fixes #212 by increasing buffer size for scanner
2020-10-14 07:29:00 -04:00
Alex Goodman 5d0909f31e add regression test to cover issue #212
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-14 07:22:19 -04:00
Jason Williams 1f64b3cd49 Fixes #212 by increasing buffer size for scanner
Signed-off-by: Jason Williams <jasonw@jhu.edu>
2020-10-13 11:00:55 -04:00
Toure Dunnon 8d25d44ea9 Add homepage field as output to the gemspec metadata (#215)
https://github.com/anchore/syft/issues/214

Signed-off-by: Toure <tdunnon@gmail.com>
2020-10-12 18:59:56 -04:00
Alex Goodman 1fc46291a6 update install script arguments for proper argument processing (#211)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-09 11:04:53 -04:00
Alex Goodman 2b932f8d65 run tests on fork PRs (#210)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-09 07:45:05 -04:00
Alex Goodman 0d3e153959 replace circle ci badge with GHA (#209)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 15:45:26 -04:00
587589bfe3 Add first issue/PR welcome message action (#185)
* Add first issue/PR welcome message action

Signed-off-by: Robert Prince <robert.prince@anchore.com>

* update first-pr-issue message with a simple greeting

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2020-10-08 15:42:46 -04:00
Alfredo Deza 4ff330e396 Merge pull request #208 from anchore/non-prerelase
docs: update the readme to remove pre-release note
2020-10-08 15:34:06 -04:00
Alfredo Deza 949bf7bc91 docs: update the readme to remove pre-release note
Signed-off-by: Alfredo Deza <adeza@anchore.com>
2020-10-08 15:30:50 -04:00
Alex Goodman 956af57d3b update release token name
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 14:37:06 -04:00
Alex Goodman 262a0888a8 fix release pipeline to wait for GHA check names
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 13:57:43 -04:00
Alex Goodman 335a6b022f add acceptance test to cover gem cataloger + fix gem cataloger parent dir (#207)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 13:13:31 -04:00
Alex Goodman da0eb6f20f fix acceptance tests & add notification upon failures (#204)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 11:07:00 -04:00
Toure Dunnon 1be5f5756a Merge pull request #203 from anchore/syft_bundler_wip
Add gemspec support
2020-10-08 11:03:30 -04:00
Alex Goodman 9edbc65bce move unicode regex to static space
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 10:55:57 -04:00
Alex Goodman 398d8903e7 finalize gemspec parser + update json schema to match
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 10:52:14 -04:00
Alex Goodman 46c74865e5 add render unicode in gemspec parser
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 10:52:14 -04:00
Alex Goodman 1f0f6fa3e5 more gemspec tests
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 10:52:03 -04:00
Alex Goodman abdd00cd24 rename gem parsers and catalogers
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 10:23:24 -04:00
Alex Goodman 10b44f5311 split catalogers into two sets, one for images another for directory scans
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 10:23:24 -04:00
Alex Goodman 1c320a8382 defer to GenericCataloger instances for pkg catalogers
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-08 10:23:24 -04:00
Toure 7a8a5419b8 adding ruby gemspec support.
Signed-off-by: Toure Dunnon <toure.dunnon@anchore.com>
2020-10-08 10:19:33 -04:00
Alex Goodman 16b23e7994 add gha pipeline to replace circlei pipeline (#202)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-07 12:20:51 -04:00
Alex Goodman e6b71486b0 modify version command to match grype (#196)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-10-06 13:13:52 -04:00
Dan Luhring 5adfce19e0 Set prerelease to auto (#189)
Signed-off-by: Dan Luhring <dan.luhring@anchore.com>
2020-09-29 16:33:40 -04:00
Dan Luhring b6dfdf16b6 Remove and update TODOs (#194)
* Remove and update TODOs

Signed-off-by: Dan Luhring <dan.luhring@anchore.com>

* Update TODO with link

Signed-off-by: Dan Luhring <dan.luhring@anchore.com>
2020-09-29 11:53:19 -04:00
Alex Goodman b5ce187ba7 replace modfile with stdlib (#187)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-28 17:25:04 -04:00
Alex Goodman 26855a2a9e ignore apk xattr file checksum + remove log.Errorf error wraps (#192)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-28 17:22:17 -04:00
Alex Goodman 4b78d9a1c0 support home dir expansion (#188)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-28 15:02:21 -04:00
Alex Goodman 2844b9878f add release notification
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 23:21:16 -04:00
Alex Goodman 8b81c87d18 remove unreleased tags and exclude size labels
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 22:59:19 -04:00
Alex Goodman 9bd9dad76c remove unrelease changelog option
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 18:11:02 -04:00
Alex Goodman 6d9f9a9b3b pin the two tags used for release autochangelog
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 17:57:51 -04:00
Alex Goodman 50cb5612d0 fix github releaser changelog intake
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 17:20:27 -04:00
Alex Goodman 080f010746 remove tty requirement from release process
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 17:07:59 -04:00
Alex Goodman f4502fc824 Add notifications around the release process (#184)
* add pipeline notification upon release

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add pending notification to release pipeline

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 16:58:56 -04:00
Alex Goodman 03378e976c always return a cleanup function from scope (#183)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 16:20:06 -04:00
Alex Goodman 576fbc898b rm stereoscope from testutils (#181)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 14:53:05 -04:00
Alex Goodman 49800b6747 Normalize the json image/dir source (#180)
* normalize the json image/dir source

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update json image presenter golden file

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 14:07:38 -04:00
Alex Goodman c46d004a3b remove duplicate rows from the summary table (#179)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 10:32:37 -04:00
Alex Goodman 3d91a66536 Add OCI support + use URI schemes for user image input (#178)
* add oci support + update image schemes

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update docs to reflect OCI image sources + URI scheme change

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update to oci-dir

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* bump upstream stereoscope pin

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-25 10:24:24 -04:00
Alex Goodman 45b5cab49a unpin skopeo during mac acceptance tests (#176)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-18 13:54:40 -04:00
Sam Dacanay 1866949eff Merge pull request #175 from anchore/fix-json-keys
Change kebab case to camelCase in json keys throughout app
2020-09-18 09:19:37 -07:00
Samuel Dacanay 0567206b38 Change kebab case to camelCase in json keys throughout app
Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>
2020-09-18 08:55:17 -07:00
Alex GoodmanandAlfredo Deza f3076c3905 Add docs around the main execution path (#172)
* add docs around the main execution path of syft

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* Update syft/lib.go

Co-authored-by: Alfredo Deza <adeza@anchore.com>
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* Update syft/lib.go

Co-authored-by: Alfredo Deza <adeza@anchore.com>
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* wrap line

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Co-authored-by: Alfredo Deza <adeza@anchore.com>
2020-09-17 06:02:51 -04:00
Samuel Dacanay b467964044 port over shell completion w/ cobra from grype, find/replace, etc.
Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>
2020-09-15 09:07:17 -07:00
Praveen Mylavarapu e26627d8b6 remove accessing tag when not available (#174)
Signed-off-by: praveenmylavarapu <saipraveenmylavarapu@gmail.com>
2020-09-15 07:55:08 -04:00
Sam Dacanay 80ee18d20b Merge pull request #171 from anchore/version-output-json
Add support for JSON version output
2020-09-14 13:51:59 -07:00
Samuel Dacanay ef64a6f206 Add support for JSON version output
Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>

Make json version output always verbose, cleanup struct tag

Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>

Use camel case for json

Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>
2020-09-14 13:48:27 -07:00
Sam Dacanay 22dfcb2d0b Merge pull request #170 from anchore/add-distro-mappings-arch-and-opensuseleap
Add support for Arch (which doesnt return a version) and Open Suse Leap
2020-09-14 09:02:28 -07:00
Samuel Dacanay 329b080a37 Add support for Arch (which doesnt return a version) and Open Suse Leap
Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>

remove bad-version test fixture (no longer relevant since it is now an expected case) and add cases for arch and opensuse-leap

Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>

Reduce nesting, simplify empty version logic

Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>
2020-09-13 13:50:56 -07:00
Sam Dacanay 11334b632e Expand matching of requirements.txt file to include any prefixes or suffixes (#168)
* Expand matching of requirements.txt file to include any prefixes or suffixes

Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>

* Add some test cases to integration test (ensure syft can pick up multiple requirements files)

Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>

* Run lint-fix

Signed-off-by: Samuel Dacanay <sam.dacanay@anchore.com>
2020-09-08 10:23:37 -07:00
129 changed files with 2000 additions and 3329 deletions
-129
View File
@@ -1,129 +0,0 @@
version: 2.1
jobs:
run-static-analysis:
parameters:
version:
type: string
docker:
- image: circleci/golang:<< parameters.version >>
environment:
GO111MODULE: "on"
# work around for recent circle CI breaking change
# Error: "Error response from daemon: client version 1.39 is too new. Maximum supported API version is 1.38"
DOCKER_API_VERSION: "1.38"
# 2CPU / 4GB RAM
resource_class: medium
steps:
- checkout
- restore_cache:
keys:
- go-<< parameters.version >>-{{ checksum "go.sum" }}-{{ checksum "Makefile" }}
- run: make ci-bootstrap
- save_cache:
key: go-<< parameters.version >>-{{ checksum "go.sum" }}-{{ checksum "Makefile" }}
paths:
- "/go/pkg/mod"
- ".tmp"
- run:
name: run static analysis
command: make static-analysis
run-tests:
parameters:
version:
type: string
docker:
- image: circleci/golang:<< parameters.version >>
environment:
GO111MODULE: "on"
# work around for recent circle CI breaking change
# Error: "Error response from daemon: client version 1.39 is too new. Maximum supported API version is 1.38"
DOCKER_API_VERSION: "1.38"
# 2CPU / 4GB RAM
resource_class: medium
steps:
- checkout
- restore_cache:
keys:
- go-<< parameters.version >>-{{ checksum "go.sum" }}-{{ checksum "Makefile" }}
- run: make ci-bootstrap
- save_cache:
key: go-<< parameters.version >>-{{ checksum "go.sum" }}-{{ checksum "Makefile" }}
paths:
- "/go/pkg/mod"
- ".tmp"
- setup_remote_docker:
version: 18.06.0-ce
- run:
name: enable docker client
command: |
# all of this to enable "circleci local execute ..." cli commands for /var/run/docker.sock
mkdir -p ${HOME}/.local/bin
cat \<< EOF > ${HOME}/.local/bin/docker
#!/bin/bash
set -xue
sudo -E ${HOME}/.local/bin/docker.bin \$@
EOF
sudo mv /usr/bin/docker ${HOME}/.local/bin/docker.bin
chmod 755 ${HOME}/.local/bin/docker
- run:
name: build cache key for java test-fixture blobs
command: make java-packages-fingerprint
- restore_cache:
keys:
- syft-unit-test-java-cache-{{ checksum "syft/cataloger/java/test-fixtures/java-builds/packages.fingerprint" }}
- run:
name: run unit tests
command: make unit
- save_cache:
key: syft-unit-test-java-cache-{{ checksum "syft/cataloger/java/test-fixtures/java-builds/packages.fingerprint" }}
paths:
- "syft/cataloger/java/test-fixtures/java-builds/packages"
- run:
name: validate syft output against the CycloneDX schema
command: make validate-cyclonedx-schema
- run:
name: build hash key for integration test-fixtures blobs
command: make integration-fingerprint
- restore_cache:
keys:
- syft-integration-test-tar-cache-{{ checksum "test/integration/test-fixtures/tar-cache.fingerprint" }}
- run:
name: run integration tests
command: make integration
- save_cache:
key: syft-integration-test-tar-cache-{{ checksum "test/integration/test-fixtures/tar-cache.fingerprint" }}
paths:
- "test/integration/test-fixtures/tar-cache"
workflows:
# Note: changing this workflow name requires making the same update in the .github/workflows/release.yaml pipeline
"Static Analysis + Unit + Integration":
jobs:
- run-static-analysis:
name: "Static Analysis"
version: "1.14"
- run-tests:
name: "Unit & Integration Tests (go-1.13)"
version: "1.13"
- run-tests:
name: "Unit & Integration Tests (go-latest)"
version: "latest"
+1 -1
View File
@@ -16,5 +16,5 @@ assignees: ''
**Anything else we need to know?**:
**Environment**:
- syft version (use `syft version -v`):
- Output of `syft version`:
- OS (e.g: `cat /etc/os-release` or similar):
+37 -1
View File
@@ -64,6 +64,15 @@ jobs:
name: artifacts
path: snapshot/**/*
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,workflow,job,commit,message,author
text: The syft acceptance tests have failed tragically!
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
if: ${{ failure() }}
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
Acceptance-Linux:
needs: [ Build-Snapshot-Artifacts ]
@@ -80,6 +89,15 @@ jobs:
- name: Run Acceptance Tests (Linux)
run: make acceptance-linux
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,workflow,job,commit,message,author
text: The syft acceptance tests have failed tragically!
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
if: ${{ failure() }}
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
Acceptance-Mac:
needs: [ Build-Snapshot-Artifacts ]
@@ -96,6 +114,15 @@ jobs:
- name: Run Acceptance Tests (Mac)
run: make acceptance-mac
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,workflow,job,commit,message,author
text: The syft acceptance tests have failed tragically!
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
if: ${{ failure() }}
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
Inline-Compare:
needs: [ Build-Snapshot-Artifacts ]
@@ -120,4 +147,13 @@ jobs:
path: snapshot
- name: Compare Anchore inline-scan results against snapshot build output
run: make compare-snapshot
run: make compare-snapshot
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,workflow,job,commit,message,author
text: The syft acceptance tests have failed tragically!
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
if: ${{ failure() }}
+24 -5
View File
@@ -25,13 +25,22 @@ jobs:
git fetch origin main
git merge-base --is-ancestor ${GITHUB_REF##*/} origin/main && echo "${GITHUB_REF##*/} is a commit on main!"
- name: Check static anaylysis, unit, and integration test results
- name: Check static analysis results
uses: fountainhead/action-wait-for-check@v1.0.0
id: sa-unit-int
id: static-analysis
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the circle-ci workflow name (in .circleci/config.yaml)
checkName: "Static Analysis + Unit + Integration"
checkName: "Static-Analysis (1.x, ubuntu-latest)"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check unit + integration results (latest go version)
uses: fountainhead/action-wait-for-check@v1.0.0
id: unit-integration
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the circle-ci workflow name (in .circleci/config.yaml)
checkName: "Tests (1.x, ubuntu-latest)"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check acceptance test results (linux)
@@ -62,9 +71,10 @@ jobs:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Quality gate
if: steps.sa-unit-int.outputs.conclusion != 'success' || steps.inline-compare.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success'
if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit-integration.outputs.conclusion != 'success' || steps.inline-compare.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success'
run: |
echo "Static/Unit/Integration Status: ${{ steps.sa-unit-int.outputs.conclusion }}"
echo "Static Analysis Status: ${{ steps.static-analysis.conclusion }}"
echo "Unit & Integration Test Status: ${{ steps.unit-integration.outputs.conclusion }}"
echo "Acceptance Test (Linux) Status: ${{ steps.acceptance-linux.outputs.conclusion }}"
echo "Acceptance Test (Mac) Status: ${{ steps.acceptance-mac.outputs.conclusion }}"
echo "Inline Compare Status: ${{ steps.inline-compare.outputs.conclusion }}"
@@ -123,6 +133,15 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,workflow,action,eventName
text: "A new Syft release is ready to be manually published: https://github.com/anchore/syft/releases"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
if: ${{ success() }}
- uses: actions/upload-artifact@v2
with:
name: artifacts
@@ -0,0 +1,104 @@
name: 'Static Analysis + Unit + Integration'
on:
workflow_dispatch:
push:
pull_request:
jobs:
Static-Analysis:
strategy:
matrix:
go-version: [1.x]
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v2
- name: Restore bootstrap cache
id: bootstrap-cache
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
${{ github.workspace }}/.tmp
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}-${{ hashFiles('Makefile') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}-
${{ runner.os }}-go-${{ matrix.go-version }}-
- name: Bootstrap project dependencies
if: steps.bootstrap-cache.outputs.cache-hit != 'true'
run: make bootstrap
- name: Bootstrap CI dependencies
run: make ci-bootstrap
- name: Run static analysis
run: make static-analysis
Tests:
strategy:
matrix:
# test the lower bounds of support, and the latest available
go-version: [1.13.x, 1.x]
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v2
- name: Restore bootstrap cache
id: bootstrap-cache
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
${{ github.workspace }}/.tmp
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}-${{ hashFiles('Makefile') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}-
${{ runner.os }}-go-${{ matrix.go-version }}-
- name: Bootstrap project dependencies
if: steps.bootstrap-cache.outputs.cache-hit != 'true'
run: make bootstrap
- name: Bootstrap CI dependencies
run: make ci-bootstrap
- name: Build cache key for java test-fixture blobs (for unit tests)
run: make java-packages-fingerprint
- name: Restore Java test-fixture cache
id: unit-java-cache
uses: actions/cache@v2
with:
path: syft/cataloger/java/test-fixtures/java-builds/packages
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/cataloger/java/test-fixtures/java-builds/packages.fingerprint' ) }}
- name: Run unit tests
run: make unit
- name: Validate syft output against the CycloneDX schema
run: make validate-cyclonedx-schema
- name: Build key for tar cache
run: make integration-fingerprint
- name: Restore integration test cache
uses: actions/cache@v2
with:
path: ${{ github.workspace }}/integration/test-fixtures/cache
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('integration/test-fixtures/cache.fingerprint') }}
- name: Run integration tests
run: make integration
+1 -1
View File
@@ -31,6 +31,7 @@ linters:
- misspell
- nakedret
- nolintlint
- prealloc
- rowserrcheck
- scopelint
- staticcheck
@@ -54,6 +55,5 @@ linters:
# - lll # without a way to specify per-line exception cases, this is not usable
# - maligned # this is an excellent linter, but tricky to optimize and we are not sensitive to memory layout optimizations
# - nestif
# - prealloc # this unnecessarily requires preallocations for slices
# - testpackage
# - wsl
+1 -1
View File
@@ -1,7 +1,7 @@
release:
# If set to auto, will mark the release as not ready for production in case there is an indicator for this in the
# tag e.g. v1.0.0-rc1 .If set to true, will mark the release as not ready for production.
prerelease: false
prerelease: auto
# If set to true, will not auto-publish the release. This is done to allow us to review the changelog before publishing.
draft: true
+40 -28
View File
@@ -27,6 +27,10 @@ ifeq "$(strip $(VERSION))" ""
override VERSION = $(shell git describe --always --tags --dirty)
endif
# used to generate the changelog from the second to last tag to the current tag (used in the release pipeline when the release tag is in place)
LAST_TAG := $(shell git describe --abbrev=0 --tags $(shell git rev-list --tags --max-count=1))
SECOND_TO_LAST_TAG := $(shell git describe --abbrev=0 --tags $(shell git rev-list --tags --skip=1 --max-count=1))
## Variable assertions
ifndef TEMPDIR
@@ -67,7 +71,7 @@ help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BOLD)$(CYAN)%-25s$(RESET)%s\n", $$1, $$2}'
.PHONY: ci-bootstrap
ci-bootstrap: bootstrap
ci-bootstrap:
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y bc jq libxml2-utils
.PHONY: bootstrap
@@ -126,11 +130,11 @@ unit: fixtures ## Run unit tests (with coverage)
.PHONY: integration
integration: ## Run integration tests
$(call title,Running integration tests)
go test -v -tags=integration ./test/integration
go test -tags=integration -v ./test/integration
# note: this is used by CI to determine if the integration test fixture cache (docker image tars) should be busted
integration-fingerprint:
find test/integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee test/integration/test-fixtures/tar-cache.fingerprint
find test/integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee test/integration/test-fixtures/cache.fingerprint
.PHONY: java-packages-fingerprint
java-packages-fingerprint:
@@ -157,20 +161,7 @@ generate-json-schema: clean-json-schema-examples integration ## Generate a new j
.PHONY: clear-test-cache
clear-test-cache: ## Delete all test cache (built docker image tars)
find . -type f -wholename "**/test-fixtures/tar-cache/*.tar" -delete
.PHONY: check-pipeline
check-pipeline: ## Run local CircleCI pipeline locally (sanity check)
$(call title,Check pipeline)
# note: this is meant for local development & testing of the pipeline, NOT to be run in CI
mkdir -p $(TEMPDIR)
circleci config process .circleci/config.yml > .tmp/circleci.yml
circleci local execute -c .tmp/circleci.yml --job "Static Analysis"
circleci local execute -c .tmp/circleci.yml --job "Unit & Integration Tests (go-latest)"
@printf '$(SUCCESS)Pipeline checks pass!$(RESET)\n'
proto:
protoc -I syft/plugin/proto/ syft/plugin/proto/cataloger.proto --go_out=plugins=grpc:syft/plugin/proto/
find . -type f -wholename "**/test-fixtures/cache/*.tar" -delete
.PHONY: build
build: $(SNAPSHOTDIR) ## Build release snapshot binaries and packages
@@ -229,32 +220,53 @@ acceptance-test-rpm-package-install: $(SNAPSHOTDIR)
$(ACC_TEST_IMAGE) \
$(RESULTSDIR)
.PHONY: changlog
changelog:
mkdir -p $(DISTDIR)
.PHONY: changlog-release
changelog-release:
@echo "Last tag: $(SECOND_TO_LAST_TAG)"
@echo "Current tag: $(VERSION)"
@docker run -i --rm \
-v "$(shell pwd)":/usr/local/src/your-app ferrarimarco/github-changelog-generator \
--user anchore \
--project $(BIN) \
-t ${GITHUB_TOKEN} \
--exclude-labels 'duplicate,question,invalid,wontfix,size:small,size:medium,size:large,size:x-large' \
--no-pr-wo-labels \
--no-issues-wo-labels \
--since-tag $(SECOND_TO_LAST_TAG)
@printf '\n$(BOLD)$(CYAN)Release $(VERSION) Changelog$(RESET)\n\n'
@cat CHANGELOG.md
.PHONY: changelog-unreleased
changelog-unreleased: ## show the current changelog that will be produced on the next release (note: requires GITHUB_TOKEN set)
@docker run -it --rm \
-v "$(shell pwd)":/usr/local/src/your-app ferrarimarco/github-changelog-generator \
--user anchore \
--project $(BIN) \
-t ${GITHUB_TOKEN} \
--no-pr-wo-labels \
--no-issues-wo-labels \
--unreleased-only \
--future-release $(VERSION)
--exclude-labels 'duplicate,question,invalid,wontfix,size:small,size:medium,size:large,size:x-large' \
--since-tag $(LAST_TAG)
@printf '\n$(BOLD)$(CYAN)Unreleased Changes (closed PRs and issues will not be in the final changelog)$(RESET)\n'
@docker run -it --rm \
-v $(shell pwd)/CHANGELOG.md:/CHANGELOG.md \
rawkode/mdv \
-t 748.5989 \
/CHANGELOG.md
.PHONY: release
release: clean-dist changelog ## Build and publish final binaries and packages
release: clean-dist changelog-release ## Build and publish final binaries and packages
$(call title,Publishing release artifacts)
# create a config with the dist dir overridden
echo "dist: $(DISTDIR)" > $(TEMPDIR)/goreleaser.yaml
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
# release
BUILD_GIT_TREE_STATE=$(GITTREESTATE) \
$(TEMPDIR)/goreleaser \
bash -c "BUILD_GIT_TREE_STATE=$(GITTREESTATE) $(TEMPDIR)/goreleaser \
--rm-dist \
--config $(TEMPDIR)/goreleaser.yaml \
--release-notes <(cat CHANGELOG.md)
--release-notes <(cat CHANGELOG.md)"
# verify checksum signatures
.github/scripts/verify-signature.sh "$(DISTDIR)"
+17 -10
View File
@@ -1,6 +1,7 @@
# syft
[![CircleCI](https://circleci.com/gh/anchore/syft.svg?style=svg)](https://circleci.com/gh/anchore/syft)
[![Static Analysis + Unit + Integration](https://github.com/anchore/syft/workflows/Static%20Analysis%20+%20Unit%20+%20Integration/badge.svg)](https://github.com/anchore/syft/actions?query=workflow%3A%22Static+Analysis+%2B+Unit+%2B+Integration%22)
[![Acceptance](https://github.com/anchore/syft/workflows/Acceptance/badge.svg)](https://github.com/anchore/syft/actions?query=workflow%3AAcceptance)
[![Go Report Card](https://goreportcard.com/badge/github.com/anchore/syft)](https://goreportcard.com/report/github.com/anchore/syft)
[![GitHub release](https://img.shields.io/github/release/anchore/syft.svg)](https://github.com/anchore/syft/releases/latest)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/anchore/syft/blob/main/LICENSE)
@@ -13,12 +14,13 @@ A CLI tool and go library for generating a Software Bill of Materials (SBOM) fro
- Catalog container images and filesystems to discover packages and libraries.
- Supports packages and libraries from various ecosystems (APK, DEB, RPM, Ruby Bundles, Python Wheel/Egg/requirements.txt, JavaScript NPM/Yarn, Java JAR/EAR/WAR, Jenkins plugins JPI/HPI, Go modules)
- Linux distribution identification (supports Alpine, BusyBox, CentOS/RedHat, Debian/Ubuntu flavored distributions)
- Supports Docker and OCI image formats
> :warning: **This is pre-release software** and it may not work as expected. If you encounter an issue, please [let us know using the issue tracker](https://github.com/anchore/syft/issues).
If you encounter an issue, please [let us know using the issue tracker](https://github.com/anchore/syft/issues).
## Getting started
To generate an SBOM for an image:
To generate an SBOM for a Docker or OCI image:
```
syft <image>
```
@@ -32,19 +34,24 @@ syft <image> --scope all-layers
Syft can generate a SBOM from a variety of sources:
```
# catalog a docker image tar (from the result of "docker image save ... -o image.tar" command)
syft docker-archive://path/to/image.tar
# catalog a container image archive (from the result of `docker image save ...`, `podman save ...`, or `skopeo copy` commands)
syft path/to/image.tar
# catalog a directory
syft dir://path/to/dir
syft path/to/dir
```
By default Syft shows a summary table, however, more detailed `text` and `json` formats are also available.
The output format for Syft is configurable as well:
```
syft <image> -o json
syft <image> -o text
syft <image> -o <format>
```
Where the `format`s available are:
- `json`: Use this to get as much information out of Syft as possible!
- `text`: A row-oriented, human-and-machine-friendly output.
- `cyclonedx`: A XML report conforming to the [CycloneDX 1.2](https://cyclonedx.org/) specification.
- `table`: A columnar summary (default).
## Installation
**Recommended**
@@ -53,7 +60,7 @@ syft <image> -o text
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# install a specific version into a specific dir
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s <RELEASE_VERSION> -b <SOME_BIN_PATH>
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b <SOME_BIN_PATH> <RELEASE_VERSION>
```
**macOS**
+1 -17
View File
@@ -3,10 +3,6 @@ package cmd
import (
"fmt"
"os"
"path"
"github.com/adrg/xdg"
"github.com/anchore/syft/internal"
"github.com/spf13/cobra"
@@ -82,18 +78,6 @@ func setGlobalCliOptions() {
}
rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
// plugins
defaultPluginDir := path.Join(xdg.CacheHome, internal.ApplicationName, "plugins")
flag = "plugins.dir"
rootCmd.Flags().String(
flag, defaultPluginDir,
"plugin storage directory",
)
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
os.Exit(1)
}
}
func initAppConfig() {
@@ -117,7 +101,7 @@ func initLogging() {
logWrapper := logger.NewLogrusLogger(cfg)
syft.SetLogger(logWrapper)
stereoscope.SetLogger(&logger.LogrusNestedLogger{
Logger: logWrapper.Logger.WithField("from-lib", "stereoscope"),
Logger: logWrapper.Logger.WithField("from-lib", "steroscope"),
})
}
+65
View File
@@ -0,0 +1,65 @@
package cmd
import (
"os"
"github.com/spf13/cobra"
)
// completionCmd represents the completion command
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish]",
Short: "Generate a shell completion for Syft (listing local docker images)",
Long: `To load completions (docker image list):
Bash:
$ source <(syft completion bash)
# To load completions for each session, execute once:
Linux:
$ syft completion bash > /etc/bash_completion.d/syft
MacOS:
$ syft completion bash > /usr/local/etc/bash_completion.d/syft
Zsh:
# If shell completion is not already enabled in your environment you will need
# to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ syft completion zsh > "${fpath[1]}/_syft"
# You will need to start a new shell for this setup to take effect.
Fish:
$ syft completion fish | source
# To load completions for each session, execute once:
$ syft completion fish > ~/.config/fish/completions/syft.fish
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var err error
switch args[0] {
case "bash":
err = cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
err = cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
err = cmd.Root().GenFishCompletion(os.Stdout, true)
}
if err != nil {
panic(err)
}
},
}
func init() {
rootCmd.AddCommand(completionCmd)
}
+56 -15
View File
@@ -1,8 +1,10 @@
package cmd
import (
"context"
"fmt"
"os"
"strings"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/bus"
@@ -12,6 +14,9 @@ import (
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/presenter"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/spf13/cobra"
"github.com/wagoodman/go-partybus"
)
@@ -21,10 +26,15 @@ var rootCmd = &cobra.Command{
Short: "A tool for generating a Software Bill Of Materials (SBOM) from container images and filesystems",
Long: internal.Tprintf(`
Supports the following image sources:
{{.appName}} yourrepo/yourimage:tag defaults to using images from a docker daemon
{{.appName}} docker://yourrepo/yourimage:tag explicitly use the docker daemon
{{.appName}} tar://path/to/yourimage.tar use a tarball from disk
{{.appName}} dir://path/to/yourproject read directly from a path in disk
{{.appName}} yourrepo/yourimage:tag defaults to using images from a Docker daemon
{{.appName}} path/to/yourproject a Docker tar, OCI tar, OCI directory, or generic filesystem directory
You can also explicitly specify the scheme to use:
{{.appName}} docker:yourrepo/yourimage:tag explicitly use the Docker daemon
{{.appName}} docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
{{.appName}} oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Podman or otherwise)
{{.appName}} oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
{{.appName}} dir:path/to/yourproject read directly from a path on disk (any directory)
`, map[string]interface{}{
"appName": internal.ApplicationName,
}),
@@ -44,6 +54,20 @@ Supports the following image sources:
os.Exit(1)
}
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Since we use ValidArgsFunction, Cobra will call this AFTER having parsed all flags and arguments provided
dockerImageRepoTags, err := ListLocalDockerImages(toComplete)
if err != nil {
// Indicates that an error occurred and completions should be ignored
return []string{"completion failed"}, cobra.ShellCompDirectiveError
}
if len(dockerImageRepoTags) == 0 {
return []string{"no docker images found"}, cobra.ShellCompDirectiveError
}
// ShellCompDirectiveDefault indicates that the shell will perform its default behavior after completions have
// been provided (without implying other possible directives)
return dockerImageRepoTags, cobra.ShellCompDirectiveDefault
},
}
func startWorker(userInput string) <-chan error {
@@ -68,17 +92,7 @@ func startWorker(userInput string) <-chan error {
}
}
config := syft.Config{
PluginDirectory: appConfig.Plugins.Directory,
}
client, err := syft.NewClient(config)
if err != nil {
errs <- fmt.Errorf("could not initialize syft: %+v", err)
return
}
catalog, scope, distro, err := client.Catalog(userInput, appConfig.ScopeOpt)
catalog, scope, distro, err := syft.Catalog(userInput, appConfig.ScopeOpt)
if err != nil {
errs <- fmt.Errorf("failed to catalog input: %+v", err)
return
@@ -98,3 +112,30 @@ func doRunCmd(_ *cobra.Command, args []string) error {
ux := ui.Select(appConfig.CliOptions.Verbosity > 0, appConfig.Quiet)
return ux(errs, eventSubscription)
}
func ListLocalDockerImages(prefix string) ([]string, error) {
var repoTags = make([]string, 0)
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return repoTags, err
}
// Only want to return tagged images
imageListArgs := filters.NewArgs()
imageListArgs.Add("dangling", "false")
images, err := cli.ImageList(ctx, types.ImageListOptions{All: false, Filters: imageListArgs})
if err != nil {
return repoTags, err
}
for _, image := range images {
// image may have multiple tags
for _, tag := range image.RepoTags {
if strings.HasPrefix(tag, prefix) {
repoTags = append(repoTags, tag)
}
}
}
return repoTags, nil
}
+28 -6
View File
@@ -1,14 +1,18 @@
package cmd
import (
"encoding/json"
"fmt"
"os"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft/presenter"
"github.com/spf13/cobra"
)
var showVerboseVersionInfo bool
var outputFormat string
var versionCmd = &cobra.Command{
Use: "version",
@@ -17,14 +21,15 @@ var versionCmd = &cobra.Command{
}
func init() {
versionCmd.Flags().BoolVarP(&showVerboseVersionInfo, "verbose", "v", false, "show additional version information")
versionCmd.Flags().StringVarP(&outputFormat, "output", "o", string(presenter.TextPresenter), "format to show version information (available=[text, json])")
rootCmd.AddCommand(versionCmd)
}
func printVersion(_ *cobra.Command, _ []string) {
versionInfo := version.FromBuild()
if showVerboseVersionInfo {
switch outputFormat {
case "text":
fmt.Println("Application: ", internal.ApplicationName)
fmt.Println("Version: ", versionInfo.Version)
fmt.Println("BuildDate: ", versionInfo.BuildDate)
@@ -33,7 +38,24 @@ func printVersion(_ *cobra.Command, _ []string) {
fmt.Println("Platform: ", versionInfo.Platform)
fmt.Println("GoVersion: ", versionInfo.GoVersion)
fmt.Println("Compiler: ", versionInfo.Compiler)
} else {
fmt.Printf("%s %s\n", internal.ApplicationName, versionInfo.Version)
case "json":
enc := json.NewEncoder(os.Stdout)
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")
err := enc.Encode(&struct {
version.Version
Application string `json:"application"`
}{
Version: versionInfo,
Application: internal.ApplicationName,
})
if err != nil {
fmt.Printf("failed to show version information: %+v\n", err)
os.Exit(1)
}
default:
fmt.Printf("unsupported output format: %s\n", outputFormat)
os.Exit(1)
}
}
+6 -16
View File
@@ -5,31 +5,26 @@ go 1.14
require (
github.com/adrg/xdg v0.2.1
github.com/anchore/go-rpmdb v0.0.0-20200811175839-cbc751c28e8e
github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/stereoscope v0.0.0-20200903223322-a3806f515470
github.com/anchore/stereoscope v0.0.0-20200925184903-c82da54e98fe
github.com/bmatcuk/doublestar v1.3.1
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
github.com/dustin/go-humanize v1.0.0
github.com/go-test/deep v1.0.6
github.com/golang/protobuf v1.4.2
github.com/google/go-containerregistry v0.1.1 // indirect
github.com/google/uuid v1.1.1
github.com/gookit/color v1.2.7
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect
github.com/hashicorp/go-hclog v0.14.1
github.com/hashicorp/go-multierror v1.1.0
github.com/hashicorp/go-plugin v1.3.0
github.com/hashicorp/go-version v1.2.0
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.1
github.com/olekukonko/tablewriter v0.0.4
github.com/package-url/packageurl-go v0.1.0
github.com/pelletier/go-toml v1.8.0
github.com/rogpeppe/go-internal v1.5.2
github.com/sergi/go-diff v1.1.0
github.com/sirupsen/logrus v1.6.0
github.com/spf13/cobra v1.0.0
github.com/spf13/afero v1.2.2
github.com/spf13/cobra v1.0.1-0.20200909172742-8a63648dd905
github.com/spf13/viper v1.7.0
github.com/wagoodman/go-partybus v0.0.0-20200526224238-eb215533f07d
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240
@@ -37,11 +32,6 @@ require (
github.com/x-cray/logrus-prefixed-formatter v0.5.2
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect
golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 // indirect
google.golang.org/grpc v1.31.1
gopkg.in/ini.v1 v1.57.0 // indirect
golang.org/x/mod v0.3.0
gopkg.in/yaml.v2 v2.3.0
)
+24 -58
View File
@@ -104,10 +104,10 @@ github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvo
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.9 h1:VrfodqvztU8YSOvygU+DN1BGaSGxmrNfqOv5oOuX2Bk=
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab h1:9pygWVFqbY9lPxM0peffumuVDyMuIMzNLyO9uFjJuQo=
github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.10 h1:k5wTrpnVU2/xv8ZuzGkbXVd3js5zJ8RnumPo5RxiIxU=
github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3HV0OhsddkmM=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
@@ -126,13 +126,12 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/anchore/go-rpmdb v0.0.0-20200811175839-cbc751c28e8e h1:kty6r0R2JeaNPeWKSYDC+HW3hkqwFh4PP5TQ8pUPYFw=
github.com/anchore/go-rpmdb v0.0.0-20200811175839-cbc751c28e8e/go.mod h1:iYuIG0Nai8dR0ri3LhZQKUyO1loxUWAGvoWhXDmjy1A=
github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db h1:LWKezJnFTFxNkZ4MzajVf+YWvJS0+7hwFr59u6SS7cw=
github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db/go.mod h1:D3rc2L/q4Hcp9eeX6AIJH4Q+kPjOtJCFhG9za90j+nU=
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8=
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g=
github.com/anchore/stereoscope v0.0.0-20200903223322-a3806f515470 h1:apXyPyfiaL/YQF7VeXRbDDB9TA7dA9rxdq8YbzPk9ko=
github.com/anchore/stereoscope v0.0.0-20200903223322-a3806f515470/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs=
github.com/anchore/stereoscope v0.0.0-20200925184903-c82da54e98fe h1:m4NSyTo2fVUoUHAV/ZVqE/PFMr/y8oz9HRrhWLk9It0=
github.com/anchore/stereoscope v0.0.0-20200925184903-c82da54e98fe/go.mod h1:2Jja/4l0zYggW52og+nn0rut4i+OYjCf9vTyrM8RT4E=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ=
github.com/apex/log v1.3.0 h1:1fyfbPvUwD10nMoh3hY6MXzvZShJQn9/ck7ATgAt5pA=
@@ -181,10 +180,11 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY=
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
@@ -193,7 +193,6 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX
github.com/containerd/containerd v1.3.4 h1:3o0smo5SKY7H6AJCmJhsnCjR2/V2T8VmiHt7seN2/kI=
github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe h1:PEmIrUvwG9Yyv+0WKZqjXfSFDeZjs/q15g0m08BYS9k=
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
@@ -209,10 +208,12 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -321,7 +322,7 @@ github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b h1:ekuhfTjngPhisSjOJ0QWKpPQE8/rbknHaes6WVJj5Hw=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -399,11 +400,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-containerregistry v0.0.0-20200430153450-5cbd060f5c92/go.mod h1:pD1UFYs7MCAx+ZLShBdttcaOSbyc8F9Na/9IZLNwJeA=
github.com/google/go-containerregistry v0.1.0 h1:hL5mVw7cTX3SBr64Arpv+cJH93L+Z9Q6WjckImYLB3g=
github.com/google/go-containerregistry v0.1.0/go.mod h1:npTSyywOeILcgWqd+rvtzGWflIPPcBQhYoOONaY4ltM=
github.com/google/go-containerregistry v0.1.1 h1:AG8FSAfXglim2l5qSrqp5VK2Xl03PiBf25NiTGGamws=
github.com/google/go-containerregistry v0.1.1/go.mod h1:npTSyywOeILcgWqd+rvtzGWflIPPcBQhYoOONaY4ltM=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@@ -444,9 +442,8 @@ github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1K
github.com/gookit/color v1.2.7 h1:4qePMNWZhrmbfYJDix+J4V2l0iVW+6jQGjicELlN14E=
github.com/gookit/color v1.2.7/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/goreleaser/goreleaser v0.136.0 h1:Z+7XPrfGK11s/Sp+a06sx2FzGuCjTBdxN2ubpGvQbjY=
github.com/goreleaser/goreleaser v0.136.0/go.mod h1:wiKrPUeSNh6Wu8nUHxZydSOVQ/OZvOaO7DTtFqie904=
github.com/goreleaser/nfpm v1.2.1/go.mod h1:TtWrABZozuLOttX2uDlYyECfQX7x5XYkVxhjYcR6G9w=
@@ -474,18 +471,12 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-plugin v1.3.0 h1:4d/wJojzvHV1I4i/rrjVaeuyxWrLzDE1mDCyDy8fXS8=
github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
@@ -506,8 +497,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -518,8 +507,6 @@ github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
@@ -599,7 +586,6 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -622,8 +608,6 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
@@ -652,8 +636,6 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
@@ -682,7 +664,7 @@ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/package-url/packageurl-go v0.1.0 h1:efWBc98O/dBZRg1pw2xiDzovnlMjCa9NPnfaiBduh8I=
github.com/package-url/packageurl-go v0.1.0/go.mod h1:C/ApiuWpmbpni4DIOECf6WCjFUZV7O1Fx7VAzrZHgBw=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -755,7 +737,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@@ -781,6 +762,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.0.1-0.20200909172742-8a63648dd905 h1:Xn3y6St5vlEgvxekK6JuArarc79GKAj/jbcsMRCaRH4=
github.com/spf13/cobra v1.0.1-0.20200909172742-8a63648dd905/go.mod h1:jCpPr/pv3OLY1D1nS9lN5HIdIpmOO/WTYJcAmnPq1nU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@@ -803,6 +786,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -835,8 +819,8 @@ github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbd
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg=
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs=
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
@@ -938,7 +922,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -975,11 +958,10 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -996,8 +978,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1016,7 +996,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1028,8 +1007,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1037,6 +1015,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1045,14 +1024,11 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1115,7 +1091,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204192400-7124308813f3/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200210192313-1ace956b0e17/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -1165,7 +1140,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -1192,21 +1166,17 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200519141106-08726f379972/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb h1:ek2py5bOqzR7MR/6obzk0rXUgYCLmjyLnaO9ssT+l6w=
google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 h1:1N7l1PuXZwEK7OhHdmKQROOM75PnUjABGwvVRbLBgFk=
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -1214,8 +1184,6 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1244,8 +1212,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+1 -7
View File
@@ -28,13 +28,7 @@ type Application struct {
Quiet bool `mapstructure:"quiet"`
Log Logging `mapstructure:"log"`
CliOptions CliOnlyOptions
CheckForAppUpdate bool `mapstructure:"check-for-app-update"`
Plugins Plugins `mapstructure:"plugins"`
}
type Plugins struct {
Enabled bool `mapstructure:"enabled"`
Directory string `mapstructure:"dir"`
CheckForAppUpdate bool `mapstructure:"check-for-app-update"`
}
type Logging struct {
+1 -1
View File
@@ -47,7 +47,7 @@ func NewZipFileManifest(archivePath string) (ZipFileManifest, error) {
defer func() {
err = zipReader.Close()
if err != nil {
log.Errorf("unable to close zip archive (%s): %w", archivePath, err)
log.Errorf("unable to close zip archive (%s): %+v", archivePath, err)
}
}()
+1 -1
View File
@@ -43,7 +43,7 @@ func TraverseFilesInZip(archivePath string, visitor func(*zip.File) error, paths
defer func() {
err = zipReader.Close()
if err != nil {
log.Errorf("unable to close zip archive (%s): %w", archivePath, err)
log.Errorf("unable to close zip archive (%s): %+v", archivePath, err)
}
}()
+1 -7
View File
@@ -1,8 +1,6 @@
package log
import (
"github.com/anchore/syft/syft/logger"
)
import "github.com/anchore/syft/syft/logger"
var Log logger.Logger = &nopLogger{}
@@ -10,10 +8,6 @@ func Errorf(format string, args ...interface{}) {
Log.Errorf(format, args...)
}
func Error(args ...interface{}) {
Log.Error(args...)
}
func Infof(format string, args ...interface{}) {
Log.Infof(format, args...)
}
-1
View File
@@ -3,7 +3,6 @@ package log
type nopLogger struct{}
func (l *nopLogger) Errorf(format string, args ...interface{}) {}
func (l *nopLogger) Error(args ...interface{}) {}
func (l *nopLogger) Infof(format string, args ...interface{}) {}
func (l *nopLogger) Info(args ...interface{}) {}
func (l *nopLogger) Debugf(format string, args ...interface{}) {}
-78
View File
@@ -1,78 +0,0 @@
package logger
import (
"bytes"
"strings"
"github.com/hashicorp/go-hclog"
)
// Source: https://github.com/hashicorp/go-hclog/blob/master/stdlog.go (MIT License)
// Provides a io.Writer to shim the data out of *log.Logger
// and back into our Logger. This is basically the only way to
// build upon *log.Logger.
type HCLogStdlogAdapter struct {
log hclog.Logger
inferLevels bool
forceLevel hclog.Level
}
// Take the data, infer the levels if configured, and send it through
// a regular Logger.
func (s *HCLogStdlogAdapter) Write(data []byte) (int, error) {
str := string(bytes.TrimRight(data, " \t\n"))
if s.forceLevel != hclog.NoLevel {
// Use pickLevel to strip log levels included in the line since we are
// forcing the level
_, str := s.pickLevel(str)
// Log at the forced level
s.dispatch(str, s.forceLevel)
} else if s.inferLevels {
level, str := s.pickLevel(str)
s.dispatch(str, level)
} else {
s.log.Info(str)
}
return len(data), nil
}
func (s *HCLogStdlogAdapter) dispatch(str string, level hclog.Level) {
switch level {
case hclog.Trace:
s.log.Trace(str)
case hclog.Debug:
s.log.Debug(str)
case hclog.Info:
s.log.Info(str)
case hclog.Warn:
s.log.Warn(str)
case hclog.Error:
s.log.Error(str)
default:
s.log.Info(str)
}
}
// Detect, based on conventions, what log level this is.
func (s *HCLogStdlogAdapter) pickLevel(str string) (hclog.Level, string) {
switch {
case strings.HasPrefix(str, "[DEBUG]"):
return hclog.Debug, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[TRACE]"):
return hclog.Trace, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[INFO]"):
return hclog.Info, strings.TrimSpace(str[6:])
case strings.HasPrefix(str, "[WARN]"):
return hclog.Warn, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[ERROR]"):
return hclog.Error, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[ERR]"):
return hclog.Error, strings.TrimSpace(str[5:])
default:
return hclog.Info, str
}
}
-148
View File
@@ -1,148 +0,0 @@
package logger
import (
"io"
"log"
"github.com/anchore/syft/syft/logger"
"github.com/hashicorp/go-hclog"
)
// integrity check
var _ hclog.Logger = &LoggerHCLogAdapter{}
// LoggerHCLogAdapter is used to (partially) adapt a syft logger.Logger object to the hclog.Logger interface for syft plugins.
// Note: this does not implement all functionality required by the hclog.Logger.
type LoggerHCLogAdapter struct {
Logger logger.Logger
}
func NewLoggerHCLogAdapter(logger logger.Logger) *LoggerHCLogAdapter {
return &LoggerHCLogAdapter{
Logger: logger,
}
}
// Args are alternating key, val pairs
// keys must be strings
// vals can be any type, but display is implementation specific
// Emit a message and key/value pairs at a provided log level
func (l *LoggerHCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
l.Logger.Info(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the TRACE level
func (l *LoggerHCLogAdapter) Trace(msg string, args ...interface{}) {
l.Logger.Debug(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the DEBUG level
func (l *LoggerHCLogAdapter) Debug(msg string, args ...interface{}) {
l.Logger.Debug(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the INFO level
func (l *LoggerHCLogAdapter) Info(msg string, args ...interface{}) {
l.Logger.Info(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the WARN level
func (l *LoggerHCLogAdapter) Warn(msg string, args ...interface{}) {
l.Logger.Error(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the ERROR level
func (l *LoggerHCLogAdapter) Error(msg string, args ...interface{}) {
l.Logger.Error(append([]interface{}{msg}, args...)...)
}
// Indicate if TRACE logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsTrace() bool {
// TODO: not implemented
return true
}
// Indicate if DEBUG logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsDebug() bool {
// TODO: not implemented
return true
}
// Indicate if INFO logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsInfo() bool {
// TODO: not implemented
return true
}
// Indicate if WARN logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsWarn() bool {
// TODO: not implemented
return true
}
// Indicate if ERROR logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsError() bool {
// TODO: not implemented
return true
}
// ImpliedArgs returns With key/value pairs
func (l *LoggerHCLogAdapter) ImpliedArgs() []interface{} {
// TODO: not implemented
return nil
}
// Creates a sublogger that will always have the given key/value pairs
func (l *LoggerHCLogAdapter) With(args ...interface{}) hclog.Logger {
// TODO: not implemented
return l
}
// Returns the Name of the logger
func (l *LoggerHCLogAdapter) Name() string {
// TODO: not implemented
return ""
}
// Create a logger that will prepend the name string on the front of all messages.
// If the logger already has a name, the new value will be appended to the current
// name. That way, a major subsystem can use this to decorate all it's own logs
// without losing context.
func (l *LoggerHCLogAdapter) Named(name string) hclog.Logger {
// TODO: not implemented
return l
}
// Create a logger that will prepend the name string on the front of all messages.
// This sets the name of the logger to the value directly, unlike Named which honor
// the current name as well.
func (l *LoggerHCLogAdapter) ResetNamed(name string) hclog.Logger {
// TODO: not implemented
return l
}
// Updates the level. This should affect all sub-loggers as well. If an
// implementation cannot update the level on the fly, it should no-op.
func (l *LoggerHCLogAdapter) SetLevel(level hclog.Level) {
// TODO: not implemented
}
// Return a value that conforms to the stdlib log.Logger interface
func (l *LoggerHCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
}
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
func (l *LoggerHCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
return &HCLogStdlogAdapter{
log: l,
inferLevels: opts.InferLevels,
forceLevel: opts.ForceLevel,
}
}
+13 -26
View File
@@ -6,15 +6,10 @@ import (
"io/ioutil"
"os"
"github.com/anchore/syft/syft/logger"
"github.com/sirupsen/logrus"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)
// integrity check
var _ logger.Logger = &LogrusLogger{}
var _ logger.Logger = &LogrusNestedLogger{}
type LogrusConfig struct {
EnableConsole bool
EnableFile bool
@@ -81,50 +76,42 @@ func NewLogrusLogger(cfg LogrusConfig) *LogrusLogger {
}
}
func (l *LogrusLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
func (l *LogrusLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
}
func (l *LogrusLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...)
}
func (l *LogrusLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
}
func (l *LogrusLogger) Error(args ...interface{}) {
l.Logger.Error(args...)
func (l *LogrusLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
}
func (l *LogrusLogger) Info(args ...interface{}) {
l.Logger.Info(args...)
}
func (l *LogrusLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
}
func (l *LogrusNestedLogger) Errorf(format string, args ...interface{}) {
func (l *LogrusLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}
func (l *LogrusNestedLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...)
}
func (l *LogrusNestedLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
}
func (l *LogrusNestedLogger) Error(args ...interface{}) {
l.Logger.Error(args...)
func (l *LogrusNestedLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...)
}
func (l *LogrusNestedLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
}
func (l *LogrusNestedLogger) Info(args ...interface{}) {
l.Logger.Info(args...)
}
func (l *LogrusNestedLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
func (l *LogrusNestedLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}
-181
View File
@@ -1,181 +0,0 @@
package logger
import (
"fmt"
"io"
"log"
"github.com/hashicorp/go-hclog"
"github.com/sirupsen/logrus"
)
// integrity check
var _ hclog.Logger = &LogrusHCLogAdapter{}
var levels = map[hclog.Level]logrus.Level{
hclog.NoLevel: logrus.PanicLevel,
hclog.Error: logrus.ErrorLevel,
hclog.Warn: logrus.WarnLevel,
hclog.Info: logrus.InfoLevel,
hclog.Debug: logrus.DebugLevel,
hclog.Trace: logrus.TraceLevel,
}
// LogrusHCLogAdapter is used to adapt a logrus logger object to the hclog.Logger interface for syft plugins.
type LogrusHCLogAdapter struct {
logger *logrus.Logger
entry *logrus.Entry
}
func NewLogrusHCLogAdapter(logger *logrus.Logger, fields map[string]interface{}) *LogrusHCLogAdapter {
if fields != nil {
return &LogrusHCLogAdapter{
logger: logger,
entry: logger.WithFields(fields),
}
}
return &LogrusHCLogAdapter{
logger: logger,
}
}
func (l *LogrusHCLogAdapter) log(level hclog.Level, msg string, args ...interface{}) {
if l.entry != nil {
l.entry.WithFields(makeArgFields(args...)).Log(levels[level], msg)
return
}
l.logger.WithFields(makeArgFields(args...)).Log(levels[level], msg)
}
// Args are alternating key, val pairs
// keys must be strings
// vals can be any type, but display is implementation specific
// Emit a message and key/value pairs at a provided log level
func (l *LogrusHCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
l.log(level, msg, args...)
}
// Emit a message and key/value pairs at the TRACE level
func (l *LogrusHCLogAdapter) Trace(msg string, args ...interface{}) {
l.log(hclog.Trace, msg, args...)
}
// Emit a message and key/value pairs at the DEBUG level
func (l *LogrusHCLogAdapter) Debug(msg string, args ...interface{}) {
l.log(hclog.Debug, msg, args...)
}
// Emit a message and key/value pairs at the INFO level
func (l *LogrusHCLogAdapter) Info(msg string, args ...interface{}) {
l.log(hclog.Info, msg, args...)
}
// Emit a message and key/value pairs at the WARN level
func (l *LogrusHCLogAdapter) Warn(msg string, args ...interface{}) {
l.log(hclog.Warn, msg, args...)
}
// Emit a message and key/value pairs at the ERROR level
func (l *LogrusHCLogAdapter) Error(msg string, args ...interface{}) {
l.log(hclog.Error, msg, args...)
}
// Indicate if TRACE logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsTrace() bool {
return l.logger.Level <= logrus.TraceLevel
}
// Indicate if DEBUG logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsDebug() bool {
return l.logger.Level <= logrus.DebugLevel
}
// Indicate if INFO logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsInfo() bool {
return l.logger.Level <= logrus.InfoLevel
}
// Indicate if WARN logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsWarn() bool {
return l.logger.Level <= logrus.WarnLevel
}
// Indicate if ERROR logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsError() bool {
return l.logger.Level <= logrus.ErrorLevel
}
// ImpliedArgs returns With key/value pairs
func (l *LogrusHCLogAdapter) ImpliedArgs() []interface{} {
// TODO: not implemented
return nil
}
// Creates a sublogger that will always have the given key/value pairs
func (l *LogrusHCLogAdapter) With(args ...interface{}) hclog.Logger {
l.entry = l.logger.WithFields(makeArgFields(args...))
return l
}
// Returns the Name of the logger
func (l *LogrusHCLogAdapter) Name() string {
// TODO: not implemented
return ""
}
// Create a logger that will prepend the name string on the front of all messages.
// If the logger already has a name, the new value will be appended to the current
// name. That way, a major subsystem can use this to decorate all it's own logs
// without losing context.
func (l *LogrusHCLogAdapter) Named(name string) hclog.Logger {
return NewLogrusHCLogAdapter(l.logger, logrus.Fields{"name": name})
}
// Create a logger that will prepend the name string on the front of all messages.
// This sets the name of the logger to the value directly, unlike Named which honor
// the current name as well.
func (l *LogrusHCLogAdapter) ResetNamed(name string) hclog.Logger {
return NewLogrusHCLogAdapter(l.logger, logrus.Fields{"name": name})
}
// Updates the level. This should affect all sub-loggers as well. If an
// implementation cannot update the level on the fly, it should no-op.
func (l *LogrusHCLogAdapter) SetLevel(level hclog.Level) {
l.logger.SetLevel(levels[level])
}
// Return a value that conforms to the stdlib log.Logger interface
func (l *LogrusHCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
}
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
func (l *LogrusHCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
return &HCLogStdlogAdapter{
log: l,
inferLevels: opts.InferLevels,
forceLevel: opts.ForceLevel,
}
}
func makeArgFields(args ...interface{}) logrus.Fields {
if len(args)%2 != 0 {
panic(fmt.Errorf("odd number of logger key-value pairs (%d): %+v", len(args), args))
}
var key string
var fields = make(logrus.Fields)
for i, arg := range args {
if i%2 == 0 {
key = arg.(string)
} else {
fields[key] = arg
}
}
return fields
}
+7 -7
View File
@@ -16,13 +16,13 @@ var platform = fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
// Version defines the application version details (generally from build information)
type Version struct {
Version string // application semantic version
GitCommit string // git SHA at build-time
GitTreeState string // indication of git tree (either "clean" or "dirty") at build-time
BuildDate string // date of the build
GoVersion string // go runtime version at build-time
Compiler string // compiler used at build-time
Platform string // GOOS and GOARCH at build-time
Version string `json:"version"` // application semantic version
GitCommit string `json:"gitCommit"` // git SHA at build-time
GitTreeState string `json:"gitTreeState"` // indication of git tree (either "clean" or "dirty") at build-time
BuildDate string `json:"buildDate"` // date of the build
GoVersion string `json:"goVersion"` // go runtime version at build-time
Compiler string `json:"compiler"` // compiler used at build-time
Platform string `json:"platform"` // GOOS and GOARCH at build-time
}
// FromBuild provides all version details
-5
View File
@@ -1,5 +0,0 @@
#!/usr/bin/env bash
# Source this file in your shell sessions in order to use this shell completion.
complete -W "$(docker images --format '{{.Repository}}:{{.Tag}}')" syft
+168 -126
View File
@@ -4,7 +4,7 @@
"artifacts": {
"items": {
"properties": {
"found-by": {
"foundBy": {
"items": {
"type": "string"
},
@@ -18,7 +18,7 @@
},
{
"properties": {
"layer-index": {
"layerIndex": {
"type": "integer"
},
"path": {
@@ -26,7 +26,7 @@
}
},
"required": [
"layer-index",
"layerIndex",
"path"
],
"type": "object"
@@ -48,43 +48,59 @@
},
"files": {
"items": {
"properties": {
"checksum": {
"anyOf": [
{
"type": "string"
},
"owner-gid": {
"type": "string"
},
"owner-uid": {
"type": "string"
},
"path": {
"type": "string"
},
"permissions": {
"type": "string"
{
"properties": {
"checksum": {
"type": "string"
},
"ownerGid": {
"type": "string"
},
"ownerUid": {
"type": "string"
},
"path": {
"type": "string"
},
"permissions": {
"type": "string"
}
},
"required": [
"checksum",
"ownerGid",
"ownerUid",
"path",
"permissions"
],
"type": "object"
}
},
"required": [
"checksum",
"owner-gid",
"owner-uid",
"path",
"permissions"
],
"type": "object"
]
},
"type": "array"
},
"git-commit-of-apk-port": {
"gitCommitOfApkPort": {
"type": "string"
},
"installed-size": {
"homepage": {
"type": "string"
},
"installedSize": {
"type": "integer"
},
"license": {
"type": "string"
},
"licenses": {
"items": {
"type": "string"
},
"type": "array"
},
"maintainer": {
"type": "string"
},
@@ -95,7 +111,7 @@
},
{
"properties": {
"extra-fields": {
"extraFields": {
"properties": {
"Archiver-Version": {
"type": "string"
@@ -160,60 +176,63 @@
],
"type": "object"
},
"implementation-title": {
"implementationTitle": {
"type": "string"
},
"implementation-vendor": {
"implementationVendor": {
"type": "string"
},
"implementation-version": {
"implementationVersion": {
"type": "string"
},
"manifest-version": {
"manifestVersion": {
"type": "string"
},
"name": {
"type": "string"
},
"specification-title": {
"specificationTitle": {
"type": "string"
},
"specification-vendor": {
"specificationVendor": {
"type": "string"
},
"specification-version": {
"specificationVersion": {
"type": "string"
}
},
"required": [
"extra-fields",
"implementation-title",
"implementation-vendor",
"implementation-version",
"manifest-version",
"extraFields",
"implementationTitle",
"implementationVendor",
"implementationVersion",
"manifestVersion",
"name",
"specification-title",
"specification-vendor",
"specification-version"
"specificationTitle",
"specificationVendor",
"specificationVersion"
],
"type": "object"
}
]
},
"origin-package": {
"name": {
"type": "string"
},
"originPackage": {
"type": "string"
},
"package": {
"type": "string"
},
"parent-package": {
"parentPackage": {
"anyOf": [
{
"type": "null"
},
{
"properties": {
"found-by": {
"foundBy": {
"type": "string"
},
"language": {
@@ -229,7 +248,7 @@
"properties": {
"manifest": {
"properties": {
"extra-fields": {
"extraFields": {
"properties": {
"Archiver-Version": {
"type": "string"
@@ -294,59 +313,59 @@
],
"type": "object"
},
"implementation-title": {
"implementationTitle": {
"type": "string"
},
"implementation-vendor": {
"implementationVendor": {
"type": "string"
},
"implementation-version": {
"implementationVersion": {
"type": "string"
},
"manifest-version": {
"manifestVersion": {
"type": "string"
},
"name": {
"type": "string"
},
"specification-title": {
"specificationTitle": {
"type": "string"
},
"specification-vendor": {
"specificationVendor": {
"type": "string"
},
"specification-version": {
"specificationVersion": {
"type": "string"
}
},
"required": [
"extra-fields",
"implementation-title",
"implementation-vendor",
"implementation-version",
"manifest-version",
"extraFields",
"implementationTitle",
"implementationVendor",
"implementationVersion",
"manifestVersion",
"name",
"specification-title",
"specification-vendor",
"specification-version"
"specificationTitle",
"specificationVendor",
"specificationVersion"
],
"type": "object"
},
"parent-package": {
"parentPackage": {
"type": "null"
},
"pom-properties": {
"pomProperties": {
"properties": {
"Path": {
"type": "string"
},
"artifact-id": {
"artifactId": {
"type": "string"
},
"extra-fields": {
"extraFields": {
"type": "null"
},
"group-id": {
"groupId": {
"type": "string"
},
"name": {
@@ -358,9 +377,9 @@
},
"required": [
"Path",
"artifact-id",
"extra-fields",
"group-id",
"artifactId",
"extraFields",
"groupId",
"name",
"version"
],
@@ -369,8 +388,8 @@
},
"required": [
"manifest",
"parent-package",
"pom-properties"
"parentPackage",
"pomProperties"
],
"type": "object"
},
@@ -385,7 +404,7 @@
}
},
"required": [
"found-by",
"foundBy",
"language",
"licenses",
"manifest",
@@ -398,18 +417,18 @@
}
]
},
"pom-properties": {
"pomProperties": {
"properties": {
"Path": {
"type": "string"
},
"artifact-id": {
"artifactId": {
"type": "string"
},
"extra-fields": {
"extraFields": {
"type": "null"
},
"group-id": {
"groupId": {
"type": "string"
},
"name": {
@@ -421,18 +440,18 @@
},
"required": [
"Path",
"artifact-id",
"extra-fields",
"group-id",
"artifactId",
"extraFields",
"groupId",
"name",
"version"
],
"type": "object"
},
"pull-checksum": {
"pullChecksum": {
"type": "string"
},
"pull-dependencies": {
"pullDependencies": {
"type": "string"
},
"release": {
@@ -444,9 +463,15 @@
"source": {
"type": "string"
},
"sourceRpm": {
"type": "string"
},
"url": {
"type": "string"
},
"vendor": {
"type": "string"
},
"version": {
"type": "string"
}
@@ -464,7 +489,7 @@
}
},
"required": [
"found-by",
"foundBy",
"locations",
"name",
"type",
@@ -474,61 +499,78 @@
},
"type": "array"
},
"directory": {
"type": "string"
},
"image": {
"source": {
"properties": {
"digest": {
"type": "string"
},
"layers": {
"items": {
"properties": {
"digest": {
"type": "string"
},
"media-type": {
"type": "string"
},
"size": {
"type": "integer"
}
"target": {
"anyOf": [
{
"type": "string"
},
"required": [
"digest",
"media-type",
"size"
],
"type": "object"
},
"type": "array"
{
"properties": {
"digest": {
"type": "string"
},
"layers": {
"items": {
"properties": {
"digest": {
"type": "string"
},
"mediaType": {
"type": "string"
},
"size": {
"type": "integer"
}
},
"required": [
"digest",
"mediaType",
"size"
],
"type": "object"
},
"type": "array"
},
"mediaType": {
"type": "string"
},
"size": {
"type": "integer"
},
"tags": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"digest",
"layers",
"mediaType",
"size",
"tags"
],
"type": "object"
}
]
},
"media-type": {
"type": {
"type": "string"
},
"size": {
"type": "integer"
},
"tags": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"digest",
"layers",
"media-type",
"size",
"tags"
"target",
"type"
],
"type": "object"
}
},
"required": [
"artifacts"
"artifacts",
"source"
],
"type": "object"
}
+3 -30
View File
@@ -4,41 +4,14 @@ Package apkdb provides a concrete Cataloger implementation for Alpine DB files.
package apkdb
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.ApkPkg Package Types defined in Alpine DB files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new Alpine DB cataloger object.
func New() *Cataloger {
// NewApkdbCataloger returns a new Alpine DB cataloger object.
func NewApkdbCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/lib/apk/db/installed": parseApkDB,
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "apkdb-cataloger"
}
// SelectFiles returns a set of discovered Alpine DB files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all Alpine DB files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
return common.NewGenericCataloger(nil, globParsers, "apkdb-cataloger")
}
+8 -1
View File
@@ -20,9 +20,14 @@ var _ common.ParserFn = parseApkDB
// parseApkDb parses individual packages from a given Alpine DB file. For more information on specific fields
// see https://wiki.alpinelinux.org/wiki/Apk_spec .
func parseApkDB(_ string, reader io.Reader) ([]pkg.Package, error) {
// larger capacity for the scanner.
const maxScannerCapacity = 1024 * 1024
// a new larger buffer for the scanner
bufScan := make([]byte, maxScannerCapacity)
packages := make([]pkg.Package, 0)
scanner := bufio.NewScanner(reader)
scanner.Buffer(bufScan, maxScannerCapacity)
onDoubleLF := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
for i := 0; i < len(data); i++ {
if i > 0 && data[i-1] == '\n' && data[i] == '\n' {
@@ -93,7 +98,7 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) {
fileRecord = &files[len(files)-1]
case "a":
ownershipFields := strings.Split(value, ":")
if len(ownershipFields) != 3 {
if len(ownershipFields) < 3 {
log.Errorf("unexpected APK ownership field: %q", value)
continue
}
@@ -104,6 +109,8 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) {
fileRecord.OwnerUID = ownershipFields[0]
fileRecord.OwnerGUI = ownershipFields[1]
fileRecord.Permissions = ownershipFields[2]
// note: there are more optional fields available that we are not capturing, e.g.:
// "0:0:755:Q1JaDEHQHBbizhEzoWK1YxuraNU/4="
case "Z":
if fileRecord == nil {
log.Errorf("checksum field with no parent record: %q", value)
+50
View File
@@ -10,6 +10,56 @@ import (
"github.com/anchore/syft/syft/pkg"
)
func TestExtraFileAttributes(t *testing.T) {
tests := []struct {
name string
expected pkg.ApkMetadata
}{
{
name: "test extra file attributes (checksum) are ignored",
expected: pkg.ApkMetadata{
Files: []pkg.ApkFileRecord{
{
Path: "/usr/lib/jvm/java-1.8-openjdk/bin/policytool",
OwnerUID: "0",
OwnerGUI: "0",
Permissions: "755",
Checksum: "Q1M0C9qfC/+kdRiOodeihG2GMRtkE=",
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
file, err := os.Open("test-fixtures/extra-file-attributes")
if err != nil {
t.Fatal("Unable to read test-fixtures/extra-file-attributes: ", err)
}
defer func() {
err := file.Close()
if err != nil {
t.Fatal("closing file failed:", err)
}
}()
reader := bufio.NewReader(file)
entry, err := parseApkDBEntry(reader)
if err != nil {
t.Fatal("Unable to read file contents: ", err)
}
if diff := deep.Equal(entry.Files, test.expected.Files); diff != nil {
for _, d := range diff {
t.Errorf("diff: %+v", d)
}
}
})
}
}
func TestSinglePackage(t *testing.T) {
tests := []struct {
name string
@@ -0,0 +1,22 @@
P:openjdk8-jre
V:8.212.04-r0
A:x86_64
S:359029
I:970752
T:OpenJDK 8 Java Runtime
U:https://icedtea.classpath.org/
L:custom
o:openjdk8
m:Timo Teras <timo.teras@iki.fi>
t:1556993127
c:46badd1aabd3375ae4b41c7194bc448f74b2ea4a
D:java-cacerts nss so:libX11.so.6 so:libXcomposite.so.1 so:libXext.so.6 so:libXi.so.6 so:libXrender.so.1 so:libXtst.so.6 so:libasound.so.2 so:libc.musl-x86_64.so.1 so:libfreetype.so.6 so:libgcc_s.so.1 so:libgif.so.7 so:libjpeg.so.8 so:libpng16.so.16 so:libstdc++.so.6 so:openjdk8:libawt.so so:openjdk8:libjava.so so:openjdk8:libjli.so so:openjdk8:libjvm.so
p:so:openjdk8:libawt_xawt.so=0 so:openjdk8:libfontmanager.so=0 so:openjdk8:libjawt.so=0 so:openjdk8:libjsoundalsa.so=0 so:openjdk8:libsplashscreen.so=0
F:usr
F:usr/lib
F:usr/lib/jvm
F:usr/lib/jvm/java-1.8-openjdk
F:usr/lib/jvm/java-1.8-openjdk/bin
R:policytool
a:0:0:755:Q1JaDEHQHBbizhEzoWK1YxuraNU/4=
Z:Q1M0C9qfC/+kdRiOodeihG2GMRtkE=
-44
View File
@@ -1,44 +0,0 @@
/*
Package bundler provides a concrete Cataloger implementation for Ruby Gemfile.lock bundler files.
*/
package bundler
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.GemPkg Package Types defined in Bundler Gemfile.lock files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new Bundler cataloger object.
func New() *Cataloger {
globParsers := map[string]common.ParserFn{
"**/Gemfile.lock": parseGemfileLockEntries,
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "bundler-cataloger"
}
// SelectFiles returns a set of discovered Gemfile.lock files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all Gemfile.lock files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
}
@@ -1,100 +0,0 @@
package bundler
import (
"os"
"testing"
"github.com/anchore/syft/syft/pkg"
)
var expected = map[string]string{
"actionmailer": "4.1.1",
"actionpack": "4.1.1",
"actionview": "4.1.1",
"activemodel": "4.1.1",
"activerecord": "4.1.1",
"activesupport": "4.1.1",
"arel": "5.0.1.20140414130214",
"bootstrap-sass": "3.1.1.1",
"builder": "3.2.2",
"coffee-rails": "4.0.1",
"coffee-script": "2.2.0",
"coffee-script-source": "1.7.0",
"erubis": "2.7.0",
"execjs": "2.0.2",
"hike": "1.2.3",
"i18n": "0.6.9",
"jbuilder": "2.0.7",
"jquery-rails": "3.1.0",
"json": "1.8.1",
"kgio": "2.9.2",
"libv8": "3.16.14.3",
"mail": "2.5.4",
"mime-types": "1.25.1",
"minitest": "5.3.4",
"multi_json": "1.10.1",
"mysql2": "0.3.16",
"polyglot": "0.3.4",
"rack": "1.5.2",
"rack-test": "0.6.2",
"rails": "4.1.1",
"railties": "4.1.1",
"raindrops": "0.13.0",
"rake": "10.3.2",
"rdoc": "4.1.1",
"ref": "1.0.5",
"sass": "3.2.19",
"sass-rails": "4.0.3",
"sdoc": "0.4.0",
"spring": "1.1.3",
"sprockets": "2.11.0",
"sprockets-rails": "2.1.3",
"sqlite3": "1.3.9",
"therubyracer": "0.12.1",
"thor": "0.19.1",
"thread_safe": "0.3.3",
"tilt": "1.4.1",
"treetop": "1.4.15",
"turbolinks": "2.2.2",
"tzinfo": "1.2.0",
"uglifier": "2.5.0",
"unicorn": "4.8.3",
}
func TestParseGemfileLockEntries(t *testing.T) {
fixture, err := os.Open("test-fixtures/Gemfile.lock")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
actual, err := parseGemfileLockEntries(fixture.Name(), fixture)
if err != nil {
t.Fatalf("failed to parse gemfile lock: %+v", err)
}
if len(actual) != len(expected) {
for _, a := range actual {
t.Log(" ", a)
}
t.Fatalf("unexpected package count: %d!=%d", len(actual), len(expected))
}
for _, a := range actual {
expectedVersion, ok := expected[a.Name]
if !ok {
t.Errorf("unexpected package found: %s", a.Name)
}
if expectedVersion != a.Version {
t.Errorf("unexpected package version (pkg=%s): %s", a.Name, a.Version)
}
if a.Language != pkg.Ruby {
t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language)
}
if a.Type != pkg.BundlerPkg {
t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
}
}
}
+52 -55
View File
@@ -12,61 +12,6 @@ import (
"github.com/wagoodman/go-progress"
)
// Catalog a given scope (container image or filesystem) with the given catalogers, returning all discovered packages.
// In order to efficiently retrieve contents from a underlying container image the content fetch requests are
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
// request.
func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
catalog := pkg.NewCatalog()
fileSelection := make([]file.Reference, 0)
filesProcessed, packagesDiscovered := newMonitor()
// ask catalogers for files to extract from the image tar
for _, a := range catalogers {
catalogerSelection := a.SelectFiles(resolver)
fileSelection = append(fileSelection, catalogerSelection...)
log.Debugf("cataloger '%s' selected '%d' files", a.Name(), len(catalogerSelection))
filesProcessed.N += int64(len(fileSelection))
}
// fetch contents for requested selection by catalogers
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
if err != nil {
return nil, err
}
// perform analysis, accumulating errors for each failed analysis
var errs error
for _, c := range catalogers {
// TODO: check for multiple rounds of analyses by Iterate error
packages, err := c.Catalog(contents)
if err != nil {
errs = multierror.Append(errs, err)
continue
}
log.Debugf("cataloger '%s' discovered '%d' packages", c.Name(), len(packages))
packagesDiscovered.N += int64(len(packages))
catalogerName := c.Name()
for _, p := range packages {
p.FoundBy = catalogerName
catalog.Add(p)
}
}
if errs != nil {
return nil, errs
}
filesProcessed.SetCompleted()
packagesDiscovered.SetCompleted()
return catalog, nil
}
// Monitor provides progress-related data for observing the progress of a Catalog() call (published on the event bus).
type Monitor struct {
FilesProcessed progress.Monitorable // the number of files selected and contents analyzed from all registered catalogers
@@ -87,3 +32,55 @@ func newMonitor() (*progress.Manual, *progress.Manual) {
})
return &filesProcessed, &packagesDiscovered
}
// Catalog a given scope (container image or filesystem) with the given catalogers, returning all discovered packages.
// In order to efficiently retrieve contents from a underlying container image the content fetch requests are
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
// request.
func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
catalog := pkg.NewCatalog()
fileSelection := make([]file.Reference, 0)
filesProcessed, packagesDiscovered := newMonitor()
// ask catalogers for files to extract from the image tar
for _, a := range catalogers {
fileSelection = append(fileSelection, a.SelectFiles(resolver)...)
log.Debugf("cataloger '%s' selected '%d' files", a.Name(), len(fileSelection))
filesProcessed.N += int64(len(fileSelection))
}
// fetch contents for requested selection by catalogers
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
if err != nil {
return nil, err
}
// perform analysis, accumulating errors for each failed analysis
var errs error
for _, a := range catalogers {
// TODO: check for multiple rounds of analyses by Iterate error
packages, err := a.Catalog(contents)
if err != nil {
errs = multierror.Append(errs, err)
continue
}
log.Debugf("cataloger '%s' discovered '%d' packages", a.Name(), len(packages))
packagesDiscovered.N += int64(len(packages))
for _, p := range packages {
catalog.Add(p)
}
}
if errs != nil {
return nil, errs
}
filesProcessed.SetCompleted()
packagesDiscovered.SetCompleted()
return catalog, nil
}
+28 -16
View File
@@ -6,18 +6,15 @@ catalogers defined in child packages as well as the interface definition to impl
package cataloger
import (
"io"
"github.com/anchore/syft/syft/cataloger/dpkg"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/apkdb"
"github.com/anchore/syft/syft/cataloger/bundler"
"github.com/anchore/syft/syft/cataloger/deb"
"github.com/anchore/syft/syft/cataloger/golang"
"github.com/anchore/syft/syft/cataloger/java"
"github.com/anchore/syft/syft/cataloger/javascript"
"github.com/anchore/syft/syft/cataloger/python"
"github.com/anchore/syft/syft/cataloger/rpmdb"
"github.com/anchore/syft/syft/cataloger/ruby"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
@@ -31,20 +28,35 @@ type Cataloger interface {
// SelectFiles discovers and returns specific files that the cataloger would like to inspect the contents of.
SelectFiles(scope.FileResolver) []file.Reference
// Catalog is given the file contents and should return any discovered Packages after analyzing the contents.
Catalog(map[file.Reference]io.Reader) ([]pkg.Package, error)
Catalog(map[file.Reference]string) ([]pkg.Package, error)
// TODO: add "IterationNeeded" error to indicate to the driver to continue with another Select/Catalog pass
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
}
// All returns a slice of all locally defined catalogers (defined in child packages).
func BuiltIn() []Cataloger {
// ImageCatalogers returns a slice of locally implemented catalogers that are fit for detecting installations of packages.
func ImageCatalogers() []Cataloger {
return []Cataloger{
dpkg.New(),
bundler.New(),
python.New(),
rpmdb.New(),
java.New(),
apkdb.New(),
golang.New(),
javascript.New(),
ruby.NewGemSpecCataloger(),
python.NewPythonCataloger(), // TODO: split and replace me
javascript.NewJavascriptCataloger(), // TODO: split and replace me
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
java.NewJavaCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModCataloger(),
}
}
// DirectoryCatalogers returns a slice of locally implemented catalogers that are fit for detecting packages from index files (and select installations)
func DirectoryCatalogers() []Cataloger {
return []Cataloger{
ruby.NewGemFileLockCataloger(),
python.NewPythonCataloger(), // TODO: split and replace me
javascript.NewJavascriptCataloger(), // TODO: split and replace me
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
java.NewJavaCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModCataloger(),
}
}
+24 -16
View File
@@ -4,7 +4,7 @@ Package common provides generic utilities used by multiple catalogers.
package common
import (
"io"
"strings"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal/log"
@@ -15,22 +15,29 @@ import (
// GenericCataloger implements the Catalog interface and is responsible for dispatching the proper parser function for
// a given path or glob pattern. This is intended to be reusable across many package cataloger types.
type GenericCataloger struct {
globParsers map[string]ParserFn
pathParsers map[string]ParserFn
selectedFiles []file.Reference
parsers map[file.Reference]ParserFn
globParsers map[string]ParserFn
pathParsers map[string]ParserFn
selectedFiles []file.Reference
parsers map[file.Reference]ParserFn
upstreamCataloger string
}
// NewGenericCataloger if provided path-to-parser-function and glob-to-parser-function lookups creates a GenericCataloger
func NewGenericCataloger(pathParsers map[string]ParserFn, globParsers map[string]ParserFn) GenericCataloger {
return GenericCataloger{
globParsers: globParsers,
pathParsers: pathParsers,
selectedFiles: make([]file.Reference, 0),
parsers: make(map[file.Reference]ParserFn),
func NewGenericCataloger(pathParsers map[string]ParserFn, globParsers map[string]ParserFn, upstreamCataloger string) *GenericCataloger {
return &GenericCataloger{
globParsers: globParsers,
pathParsers: pathParsers,
selectedFiles: make([]file.Reference, 0),
parsers: make(map[file.Reference]ParserFn),
upstreamCataloger: upstreamCataloger,
}
}
// Name returns a string that uniquely describes the upstream cataloger that this Generic Cataloger represents.
func (a *GenericCataloger) Name() string {
return a.upstreamCataloger
}
// register pairs a set of file references with a parser function for future cataloging (when the file contents are resolved)
func (a *GenericCataloger) register(files []file.Reference, parser ParserFn) {
a.selectedFiles = append(a.selectedFiles, files...)
@@ -51,7 +58,7 @@ func (a *GenericCataloger) SelectFiles(resolver scope.FileResolver) []file.Refer
for path, parser := range a.pathParsers {
files, err := resolver.FilesByPath(file.Path(path))
if err != nil {
log.Errorf("cataloger failed to select files by path: %w", err)
log.Errorf("cataloger failed to select files by path: %+v", err)
}
if files != nil {
a.register(files, parser)
@@ -73,7 +80,7 @@ func (a *GenericCataloger) SelectFiles(resolver scope.FileResolver) []file.Refer
}
// Catalog takes a set of file contents and uses any configured parser functions to resolve and return discovered packages
func (a *GenericCataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
func (a *GenericCataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) {
defer a.clear()
packages := make([]pkg.Package, 0)
@@ -81,18 +88,19 @@ func (a *GenericCataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg
for reference, parser := range a.parsers {
content, ok := contents[reference]
if !ok {
log.Errorf("cataloger missing file content: %+v", reference)
log.Errorf("cataloger '%s' missing file content: %+v", a.upstreamCataloger, reference)
continue
}
entries, err := parser(string(reference.Path), content)
entries, err := parser(string(reference.Path), strings.NewReader(content))
if err != nil {
// TODO: should we fail? or only log?
log.Errorf("cataloger failed to parse entries (reference=%+v): %w", reference, err)
log.Errorf("cataloger '%s' failed to parse entries (reference=%+v): %+v", a.upstreamCataloger, reference, err)
continue
}
for _, entry := range entries {
entry.FoundBy = a.upstreamCataloger
entry.Source = []file.Reference{reference}
packages = append(packages, entry)
@@ -60,9 +60,9 @@ func TestGenericCataloger(t *testing.T) {
"/another-path.txt": parser,
"/last/path.txt": parser,
}
upstream := "some-other-cataloger"
resolver := newTestResolver()
cataloger := NewGenericCataloger(pathParsers, globParsers)
cataloger := NewGenericCataloger(pathParsers, globParsers, upstream)
selected := cataloger.SelectFiles(resolver)
@@ -82,8 +82,9 @@ func TestGenericCataloger(t *testing.T) {
expectedPkgs := make(map[file.Reference]pkg.Package)
for path, ref := range selectionByPath {
expectedPkgs[ref] = pkg.Package{
Source: []file.Reference{ref},
Name: fmt.Sprintf("%s file contents!", path),
FoundBy: upstream,
Source: []file.Reference{ref},
Name: fmt.Sprintf("%s file contents!", path),
}
}
+17
View File
@@ -0,0 +1,17 @@
/*
Package dpkg provides a concrete Cataloger implementation for Debian package DB status files.
*/
package deb
import (
"github.com/anchore/syft/syft/cataloger/common"
)
// NewDpkgdbCataloger returns a new Deb package cataloger object.
func NewDpkgdbCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/var/lib/dpkg/status": parseDpkgStatus,
}
return common.NewGenericCataloger(nil, globParsers, "dpkgdb-cataloger")
}
@@ -1,4 +1,4 @@
package dpkg
package deb
import (
"bufio"
@@ -1,4 +1,4 @@
package dpkg
package deb
import (
"bufio"
-44
View File
@@ -1,44 +0,0 @@
/*
Package dpkg provides a concrete Cataloger implementation for Debian package DB status files.
*/
package dpkg
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.DebPkg Package Types defined in DPKG status files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new Deb package cataloger object.
func New() *Cataloger {
globParsers := map[string]common.ParserFn{
"**/var/lib/dpkg/status": parseDpkgStatus,
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "dpkg-cataloger"
}
// SelectFiles returns a set of discovered DPKG status files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all DPKG status files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
}
+3 -30
View File
@@ -4,41 +4,14 @@ Package golang provides a concrete Cataloger implementation for go.mod files.
package golang
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.GoModulePkg Package Types defined in go.mod files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new Go module cataloger object.
func New() *Cataloger {
// NewGoModCataloger returns a new Go module cataloger object.
func NewGoModCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/go.mod": parseGoMod,
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "go-cataloger"
}
// SelectFiles returns a set of discovered go.mod files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all go.mod files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
return common.NewGenericCataloger(nil, globParsers, "go-cataloger")
}
+1 -2
View File
@@ -6,9 +6,8 @@ import (
"io/ioutil"
"sort"
"github.com/rogpeppe/go-internal/modfile"
"github.com/anchore/syft/syft/pkg"
"golang.org/x/mod/modfile"
)
// parseGoMod takes a go.mod and lists all packages discovered.
+3 -30
View File
@@ -4,42 +4,15 @@ Package java provides a concrete Cataloger implementation for Java archives (jar
package java
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.JavaPkg and pkg.JenkinsPluginPkg Package Types defined in java archive files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new Java archive cataloger object.
func New() *Cataloger {
// NewJavaCataloger returns a new Java archive cataloger object.
func NewJavaCataloger() *common.GenericCataloger {
globParsers := make(map[string]common.ParserFn)
for _, pattern := range archiveFormatGlobs {
globParsers[pattern] = parseJavaArchive
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "java-cataloger"
}
// SelectFiles returns a set of discovered Java archive files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all Java archive files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
return common.NewGenericCataloger(nil, globParsers, "java-cataloger")
}
+1 -1
View File
@@ -19,7 +19,7 @@ func saveArchiveToTmp(reader io.Reader) (string, string, func(), error) {
cleanupFn := func() {
err = os.RemoveAll(tempDir)
if err != nil {
log.Errorf("unable to cleanup jar tempdir: %w", err)
log.Errorf("unable to cleanup jar tempdir: %+v", err)
}
}
@@ -1,4 +1,3 @@
<?jelly escape-by-default='true'?>
<div>
TODO
</div>
+3 -30
View File
@@ -4,42 +4,15 @@ Package javascript provides a concrete Cataloger implementation for JavaScript e
package javascript
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.YarnPkg and pkg.NpmPkg Package Types defined in package-lock.json and yarn.lock files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new JavaScript cataloger object.
func New() *Cataloger {
// NewJavascriptCataloger returns a new JavaScript cataloger object.
func NewJavascriptCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/package-lock.json": parsePackageLock,
"**/yarn.lock": parseYarnLock,
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "javascript-cataloger"
}
// SelectFiles returns a set of discovered Javascript ecosystem files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all Javascript ecosystem files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
return common.NewGenericCataloger(nil, globParsers, "javascript-cataloger")
}
+4 -31
View File
@@ -4,45 +4,18 @@ Package python provides a concrete Cataloger implementation for Python ecosystem
package python
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.WheelPkg, pkg.EggPkg, and pkg.PythonRequirementsPkg Package Types defined in Python ecosystem files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new Python cataloger object.
func New() *Cataloger {
// NewPythonCataloger returns a new Python cataloger object.
func NewPythonCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/*egg-info/PKG-INFO": parseEggMetadata,
"**/*dist-info/METADATA": parseWheelMetadata,
"**/requirements.txt": parseRequirementsTxt,
"**/*requirements*.txt": parseRequirementsTxt,
"**/poetry.lock": parsePoetryLock,
"**/setup.py": parseSetup,
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "python-cataloger"
}
// SelectFiles returns a set of discovered Python ecosystem files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all Python ecosystem files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
return common.NewGenericCataloger(nil, globParsers, "python-cataloger")
}
+3 -31
View File
@@ -4,41 +4,13 @@ Package rpmdb provides a concrete Cataloger implementation for RPM "Package" DB
package rpmdb
import (
"io"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
// Cataloger catalogs pkg.RpmPkg Package Types defined in RPM DB files.
type Cataloger struct {
cataloger common.GenericCataloger
}
// New returns a new RPM DB cataloger object.
func New() *Cataloger {
// NewRpmdbCataloger returns a new RPM DB cataloger object.
func NewRpmdbCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/var/lib/rpm/Packages": parseRpmDB,
}
return &Cataloger{
cataloger: common.NewGenericCataloger(nil, globParsers),
}
}
// Name returns a string that uniquely describes this cataloger.
func (c *Cataloger) Name() string {
return "rpmdb-cataloger"
}
// SelectFiles returns a set of discovered RPM DB files from the user content source.
func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return c.cataloger.SelectFiles(resolver)
}
// Catalog returns the Packages indexed from all RPM DB files discovered.
func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return c.cataloger.Catalog(contents)
return common.NewGenericCataloger(nil, globParsers, "rpmdb-cataloger")
}
+26
View File
@@ -0,0 +1,26 @@
/*
Package bundler provides a concrete Cataloger implementation for Ruby Gemfile.lock bundler files.
*/
package ruby
import (
"github.com/anchore/syft/syft/cataloger/common"
)
// NewGemFileLockCataloger returns a new Bundler cataloger object tailored for parsing index-oriented files (e.g. Gemfile.lock).
func NewGemFileLockCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/Gemfile.lock": parseGemFileLockEntries,
}
return common.NewGenericCataloger(nil, globParsers, "ruby-gemfile-cataloger")
}
// NewGemSpecCataloger returns a new Bundler cataloger object tailored for detecting installations of gems (e.g. Gemspec).
func NewGemSpecCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/specifications/*.gemspec": parseGemSpecEntries,
}
return common.NewGenericCataloger(nil, globParsers, "ruby-gemspec-cataloger")
}
@@ -1,4 +1,4 @@
package bundler
package ruby
import (
"bufio"
@@ -11,12 +11,12 @@ import (
)
// integrity check
var _ common.ParserFn = parseGemfileLockEntries
var _ common.ParserFn = parseGemFileLockEntries
var sectionsOfInterest = internal.NewStringSetFromSlice([]string{"GEM"})
// parseGemfileLockEntries is a parser function for Gemfile.lock contents, returning all Gems discovered.
func parseGemfileLockEntries(_ string, reader io.Reader) ([]pkg.Package, error) {
// parseGemFileLockEntries is a parser function for Gemfile.lock contents, returning all Gems discovered.
func parseGemFileLockEntries(_ string, reader io.Reader) ([]pkg.Package, error) {
pkgs := make([]pkg.Package, 0)
scanner := bufio.NewScanner(reader)
@@ -44,7 +44,7 @@ func parseGemfileLockEntries(_ string, reader io.Reader) ([]pkg.Package, error)
Name: candidate[0],
Version: strings.Trim(candidate[1], "()"),
Language: pkg.Ruby,
Type: pkg.BundlerPkg,
Type: pkg.GemPkg,
})
}
}
@@ -0,0 +1,101 @@
package ruby
import (
"os"
"testing"
"github.com/anchore/syft/syft/pkg"
)
func TestParseGemfileLockEntries(t *testing.T) {
var expectedGems = map[string]string{
"actionmailer": "4.1.1",
"actionpack": "4.1.1",
"actionview": "4.1.1",
"activemodel": "4.1.1",
"activerecord": "4.1.1",
"activesupport": "4.1.1",
"arel": "5.0.1.20140414130214",
"bootstrap-sass": "3.1.1.1",
"builder": "3.2.2",
"coffee-rails": "4.0.1",
"coffee-script": "2.2.0",
"coffee-script-source": "1.7.0",
"erubis": "2.7.0",
"execjs": "2.0.2",
"hike": "1.2.3",
"i18n": "0.6.9",
"jbuilder": "2.0.7",
"jquery-rails": "3.1.0",
"json": "1.8.1",
"kgio": "2.9.2",
"libv8": "3.16.14.3",
"mail": "2.5.4",
"mime-types": "1.25.1",
"minitest": "5.3.4",
"multi_json": "1.10.1",
"mysql2": "0.3.16",
"polyglot": "0.3.4",
"rack": "1.5.2",
"rack-test": "0.6.2",
"rails": "4.1.1",
"railties": "4.1.1",
"raindrops": "0.13.0",
"rake": "10.3.2",
"rdoc": "4.1.1",
"ref": "1.0.5",
"sass": "3.2.19",
"sass-rails": "4.0.3",
"sdoc": "0.4.0",
"spring": "1.1.3",
"sprockets": "2.11.0",
"sprockets-rails": "2.1.3",
"sqlite3": "1.3.9",
"therubyracer": "0.12.1",
"thor": "0.19.1",
"thread_safe": "0.3.3",
"tilt": "1.4.1",
"treetop": "1.4.15",
"turbolinks": "2.2.2",
"tzinfo": "1.2.0",
"uglifier": "2.5.0",
"unicorn": "4.8.3",
}
fixture, err := os.Open("test-fixtures/Gemfile.lock")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
actual, err := parseGemFileLockEntries(fixture.Name(), fixture)
if err != nil {
t.Fatalf("failed to parse gemfile lock: %+v", err)
}
if len(actual) != len(expectedGems) {
for _, a := range actual {
t.Log(" ", a)
}
t.Fatalf("unexpected package count: %d!=%d", len(actual), len(expectedGems))
}
for _, a := range actual {
expectedVersion, ok := expectedGems[a.Name]
if !ok {
t.Errorf("unexpected package found: %s", a.Name)
}
if expectedVersion != a.Version {
t.Errorf("unexpected package version (pkg=%s): %s", a.Name, a.Version)
}
if a.Language != pkg.Ruby {
t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language)
}
if a.Type != pkg.GemPkg {
t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
}
}
}
+135
View File
@@ -0,0 +1,135 @@
package ruby
import (
"bufio"
"encoding/json"
"fmt"
"io"
"regexp"
"strings"
"github.com/mitchellh/mapstructure"
"github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg"
)
// integrity check
var _ common.ParserFn = parseGemFileLockEntries
type postProcessor func(string) []string
// match example: Al\u003Ex ---> 003E
var unicodePattern = regexp.MustCompile(`\\u(?P<unicode>[0-9A-F]{4})`)
var patterns = map[string]*regexp.Regexp{
// match example: name = "railties".freeze ---> railties
"name": regexp.MustCompile(`.*\.name\s*=\s*["']{1}(?P<name>.*)["']{1} *`),
// match example: version = "1.0.4".freeze ---> 1.0.4
"version": regexp.MustCompile(`.*\.version\s*=\s*["']{1}(?P<version>.*)["']{1} *`),
// match example:
// homepage = "https://github.com/anchore/syft".freeze ---> https://github.com/anchore/syft
"homepage": regexp.MustCompile(`.*\.homepage\s*=\s*["']{1}(?P<homepage>.*)["']{1} *`),
// match example: files = ["exe/bundle".freeze, "exe/bundler".freeze] ---> "exe/bundle".freeze, "exe/bundler".freeze
"files": regexp.MustCompile(`.*\.files\s*=\s*\[(?P<files>.*)\] *`),
// match example: authors = ["Andr\u00E9 Arko".freeze, "Samuel Giddins".freeze, "Colby Swandale".freeze,
// "Hiroshi Shibata".freeze, "David Rodr\u00EDguez".freeze, "Grey Baker".freeze...]
"authors": regexp.MustCompile(`.*\.authors\s*=\s*\[(?P<authors>.*)\] *`),
// match example: licenses = ["MIT".freeze] ----> "MIT".freeze
"licenses": regexp.MustCompile(`.*\.licenses\s*=\s*\[(?P<licenses>.*)\] *`),
}
var postProcessors = map[string]postProcessor{
"files": processList,
"authors": processList,
"licenses": processList,
}
func processList(s string) []string {
// nolint:prealloc
var results []string
for _, item := range strings.Split(s, ",") {
results = append(results, strings.Trim(item, "\" "))
}
return results
}
func parseGemSpecEntries(_ string, reader io.Reader) ([]pkg.Package, error) {
var pkgs []pkg.Package
var fields = make(map[string]interface{})
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
sanitizedLine := strings.TrimSpace(line)
sanitizedLine = strings.ReplaceAll(sanitizedLine, ".freeze", "")
sanitizedLine = renderUtf8(sanitizedLine)
if sanitizedLine == "" {
continue
}
for field, pattern := range patterns {
matchMap := matchCaptureGroups(pattern, sanitizedLine)
if value := matchMap[field]; value != "" {
if postProcessor := postProcessors[field]; postProcessor != nil {
fields[field] = postProcessor(value)
} else {
fields[field] = value
}
// TODO: know that a line could actually match on multiple patterns, this is unlikely though
break
}
}
}
if fields["name"] != "" && fields["version"] != "" {
var metadata pkg.GemMetadata
if err := mapstructure.Decode(fields, &metadata); err != nil {
return nil, fmt.Errorf("unable to decode gem metadata: %w", err)
}
pkgs = append(pkgs, pkg.Package{
Name: metadata.Name,
Version: metadata.Version,
Licenses: metadata.Licenses,
Language: pkg.Ruby,
Type: pkg.GemPkg,
Metadata: metadata,
})
}
return pkgs, nil
}
// renderUtf8 takes any string escaped string sub-sections from the ruby string and replaces those sections with the UTF8 runes.
func renderUtf8(s string) string {
fullReplacement := unicodePattern.ReplaceAllStringFunc(s, func(unicodeSection string) string {
var replacement string
// note: the json parser already has support for interpreting hex-representations of unicode escaped strings as unicode runes.
// we can do this ourselves with strconv.Atoi, or leverage the existing json package.
if err := json.Unmarshal([]byte(`"`+unicodeSection+`"`), &replacement); err != nil {
return unicodeSection
}
return replacement
})
return fullReplacement
}
// matchCaptureGroups takes a regular expression and string and returns all of the named capture group results in a map.
func matchCaptureGroups(regEx *regexp.Regexp, str string) map[string]string {
match := regEx.FindStringSubmatch(str)
results := make(map[string]string)
for i, name := range regEx.SubexpNames() {
if i > 0 && i <= len(match) {
results[name] = match[i]
}
}
return results
}
+48
View File
@@ -0,0 +1,48 @@
package ruby
import (
"os"
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/go-test/deep"
)
func TestParseGemspec(t *testing.T) {
var expectedPkg = pkg.Package{
Name: "bundler",
Version: "2.1.4",
Type: pkg.GemPkg,
Licenses: []string{"MIT"},
Language: pkg.Ruby,
Metadata: pkg.GemMetadata{
Name: "bundler",
Version: "2.1.4",
Files: []string{"exe/bundle", "exe/bundler"},
Authors: []string{"André Arko", "Samuel Giddins", "Colby Swandale", "Hiroshi Shibata", "David Rodríguez", "Grey Baker", "Stephanie Morillo", "Chris Morris", "James Wen", "Tim Moore", "André Medeiros", "Jessica Lynn Suttles", "Terence Lee", "Carl Lerche", "Yehuda Katz"},
Licenses: []string{"MIT"},
Homepage: "https://bundler.io",
},
}
fixture, err := os.Open("test-fixtures/bundler.gemspec")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
actual, err := parseGemSpecEntries(fixture.Name(), fixture)
if err != nil {
t.Fatalf("failed to parse gemspec: %+v", err)
}
if len(actual) != 1 {
for _, a := range actual {
t.Log(" ", a)
}
t.Fatalf("unexpected package count: %d!=1", len(actual))
}
for _, d := range deep.Equal(actual[0], expectedPkg) {
t.Errorf("diff: %+v", d)
}
}
@@ -0,0 +1,26 @@
# frozen_string_literal: true
# -*- encoding: utf-8 -*-
# stub: bundler 2.1.4 ruby lib
Gem::Specification.new do |s|
s.name = "bundler".freeze
s.version = "2.1.4"
s.required_rubygems_version = Gem::Requirement.new(">= 2.5.2".freeze) if s.respond_to? :required_rubygems_version=
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=
s.require_paths = ["lib".freeze]
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]
s.bindir = "exe".freeze
s.date = "2020-01-05"
s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably".freeze
s.email = ["team@bundler.io".freeze]
s.executables = ["bundle".freeze, "bundler".freeze]
s.files = ["exe/bundle".freeze, "exe/bundler".freeze]
s.homepage = "https://bundler.io".freeze
s.licenses = ["MIT".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
s.rubygems_version = "3.1.2".freeze
s.summary = "The best way to manage your application's dependencies".freeze
s.installed_by_version = "3.1.2" if s.respond_to? :installed_by_version
end
+11 -1
View File
@@ -20,6 +20,9 @@ func NewUnknownDistro() Distro {
}
func NewDistro(t Type, ver string) (Distro, error) {
if ver == "" {
return Distro{Type: t}, nil
}
verObj, err := hashiVer.NewVersion(ver)
if err != nil {
return Distro{}, fmt.Errorf("could not create distro version: %w", err)
@@ -32,6 +35,9 @@ func NewDistro(t Type, ver string) (Distro, error) {
}
func (d Distro) MajorVersion() string {
if d.Version == nil {
return fmt.Sprint("(version unknown)")
}
return fmt.Sprintf("%d", d.Version.Segments()[0])
}
@@ -40,7 +46,11 @@ func (d Distro) FullVersion() string {
}
func (d Distro) String() string {
return fmt.Sprintf("%s %s", d.Type, d.RawVersion)
versionStr := "(version unknown)"
if d.RawVersion != "" {
versionStr = d.RawVersion
}
return fmt.Sprintf("%s %s", d.Type, versionStr)
}
// Name provides a string repr of the distro
+4 -14
View File
@@ -1,7 +1,6 @@
package distro
import (
"io/ioutil"
"regexp"
"strings"
@@ -11,7 +10,6 @@ import (
)
// returns a distro or nil
// TODO: refactor to io.Reader
type parseFunc func(string) *Distro
type parseEntry struct {
@@ -19,7 +17,6 @@ type parseEntry struct {
fn parseFunc
}
// nolint: funlen
// Identify parses distro-specific files to determine distro metadata like version and release.
func Identify(resolver scope.Resolver) Distro {
distro := NewUnknownDistro()
@@ -51,12 +48,13 @@ identifyLoop:
}
if len(refs) == 0 {
log.Debugf("No Refs found from path: %s", entry.path)
continue
}
for _, ref := range refs {
contentReaders, err := resolver.MultipleFileContentsByRef(ref)
contentReader, ok := contentReaders[ref]
contents, err := resolver.MultipleFileContentsByRef(ref)
content, ok := contents[ref]
if !ok {
log.Infof("no content present for ref: %s", ref)
@@ -68,14 +66,6 @@ identifyLoop:
continue
}
bytes, err := ioutil.ReadAll(contentReader)
if err != nil {
log.Debugf("unable to read contents from %s: %s", entry.path, err)
continue
}
content := string(bytes)
if content == "" {
log.Debugf("no contents in file, skipping: %s", entry.path)
continue
@@ -95,7 +85,7 @@ func assemble(name, version string) *Distro {
distroType, ok := IDMapping[name]
// Both distro and version must be present
if len(name) == 0 || len(version) == 0 {
if len(name) == 0 {
return nil
}
+14 -4
View File
@@ -65,6 +65,15 @@ func TestIdentifyDistro(t *testing.T) {
fixture: "test-fixtures/os/unmatchable",
Type: UnknownDistroType,
},
{
fixture: "test-fixtures/os/opensuse-leap",
Type: OpenSuseLeap,
Version: "15.2.0",
},
{
fixture: "test-fixtures/os/arch",
Type: ArchLinux,
},
}
observedDistros := internal.NewStringSet()
@@ -94,6 +103,11 @@ func TestIdentifyDistro(t *testing.T) {
return
}
if d.Version == nil {
t.Log("Distro doesn't have a Version")
return
}
if d.Version.String() != test.Version {
t.Errorf("expected distro version doesn't match: %v != %v", d.Version.String(), test.Version)
}
@@ -175,10 +189,6 @@ func TestParseOsReleaseFailures(t *testing.T) {
fixture string
name string
}{
{
fixture: "test-fixtures/bad-version",
name: "No version",
},
{
fixture: "test-fixtures/bad-id",
name: "No name ID",
-15
View File
@@ -1,15 +0,0 @@
NAME="Red Hat Enterprise Linux"
VERSION="8.1 (Ootpa)"
ID="rhel"
ID_LIKE="fedora"
PLATFORM_ID="platform:el8"
PRETTY_NAME="Red Hat Enterprise Linux 8.1 (Ootpa)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:redhat:enterprise_linux:8.1:GA"
HOME_URL="https://www.redhat.com/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_BUGZILLA_PRODUCT_VERSION=8.1
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="8.1"
@@ -0,0 +1,10 @@
NAME="Arch Linux"
PRETTY_NAME="Arch Linux"
ID=arch
BUILD_ID=rolling
ANSI_COLOR="38;2;23;147;209"
HOME_URL="https://www.archlinux.org/"
DOCUMENTATION_URL="https://wiki.archlinux.org/"
SUPPORT_URL="https://bbs.archlinux.org/"
BUG_REPORT_URL="https://bugs.archlinux.org/"
LOGO=archlinux
@@ -0,0 +1,10 @@
NAME="openSUSE Leap"
VERSION="15.2"
ID="opensuse-leap"
ID_LIKE="suse opensuse"
VERSION_ID="15.2"
PRETTY_NAME="openSUSE Leap 15.2"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:leap:15.2"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
+17 -13
View File
@@ -11,7 +11,8 @@ const (
Busybox
AmazonLinux
OracleLinux
//ArchLinux
ArchLinux
OpenSuseLeap
)
type Type int
@@ -27,7 +28,8 @@ var distroStr = []string{
"busybox",
"amazn",
"oraclelinux",
//"archlinux",
"archlinux",
"opensuse-leap",
}
var All = []Type{
@@ -40,7 +42,8 @@ var All = []Type{
Busybox,
AmazonLinux,
OracleLinux,
//ArchLinux,
ArchLinux,
OpenSuseLeap,
}
func (t Type) String() string {
@@ -53,14 +56,15 @@ func (t Type) String() string {
// IDMapping connects a distro ID like "ubuntu" to a Distro type
var IDMapping = map[string]Type{
"debian": Debian,
"ubuntu": Ubuntu,
"rhel": RedHat,
"centos": CentOS,
"fedora": Fedora,
"alpine": Alpine,
"busybox": Busybox,
"amzn": AmazonLinux,
"ol": OracleLinux,
//"arch": ArchLinux,
"debian": Debian,
"ubuntu": Ubuntu,
"rhel": RedHat,
"centos": CentOS,
"fedora": Fedora,
"alpine": Alpine,
"busybox": Busybox,
"amzn": AmazonLinux,
"ol": OracleLinux,
"arch": ArchLinux,
"opensuse-leap": OpenSuseLeap,
}
+31 -32
View File
@@ -1,44 +1,37 @@
/*
A "one-stop-shop" for helper utilities for all major functionality provided by child packages of the syft library.
Here is what the main execution path for syft does:
1. Parse a user image string to get a stereoscope image.Source object
2. Invoke all catalogers to catalog the image, adding discovered packages to a single catalog object
3. Invoke a single presenter to show the contents of the catalog
A Scope object encapsulates the image object to be cataloged and the user options (catalog all layers vs. squashed layer),
providing a way to inspect paths and file content within the image. The Scope object, not the image object, is used
throughout the main execution path. This abstraction allows for decoupling of what is cataloged (a docker image, an OCI
image, a filesystem, etc) and how it is cataloged (the individual catalogers).
Similar to the cataloging process, Linux distribution identification is also performed based on what is discovered within the image.
*/
package syft
import (
"fmt"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloger"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/logger"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/plugin"
"github.com/anchore/syft/syft/scope"
"github.com/wagoodman/go-partybus"
)
type Config struct {
PluginDirectory string
}
type Client struct {
config Config
plugins *plugin.Repository
}
func NewClient(config Config) (Client, error) {
plugins, err := plugin.NewRepositoryFromDirectory(config.PluginDirectory)
if err != nil {
return Client{}, err
}
return Client{
config: config,
plugins: plugins,
}, nil
}
// Catalog the given image from a particular perspective (e.g. squashed scope, all-layers scope). Returns the discovered
// set of packages, the identified Linux distribution, and the scope object used to wrap the data source.
func (c *Client) Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) {
func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) {
log.Info("cataloging image")
s, cleanup, err := scope.NewScope(userInput, scoptOpt)
defer cleanup()
@@ -46,9 +39,9 @@ func (c *Client) Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog,
return nil, nil, nil, err
}
d := c.IdentifyDistro(s)
d := IdentifyDistro(s)
catalog, err := c.CatalogFromScope(s)
catalog, err := CatalogFromScope(s)
if err != nil {
return nil, nil, nil, err
}
@@ -58,7 +51,7 @@ func (c *Client) Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog,
// IdentifyDistro attempts to discover what the underlying Linux distribution may be from the available flat files
// provided by the given scope object. If results are inconclusive a "UnknownDistro" Type is returned.
func (c *Client) IdentifyDistro(s scope.Scope) distro.Distro {
func IdentifyDistro(s scope.Scope) distro.Distro {
d := distro.Identify(s.Resolver)
if d.Type != distro.UnknownDistroType {
log.Infof("identified distro: %s", d.String())
@@ -69,15 +62,21 @@ func (c *Client) IdentifyDistro(s scope.Scope) distro.Distro {
}
// Catalog the given scope, which may represent a container image or filesystem. Returns the discovered set of packages.
func (c *Client) CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
log.Info("building the catalog")
pluginCatalogers, deactivatePluginsFn, err := c.plugins.ActivateCatalogers()
defer deactivatePluginsFn()
if err != nil {
return nil, err
// conditionally have two sets of catalogers
var catalogers []cataloger.Cataloger
switch s.Scheme {
case scope.ImageScheme:
catalogers = cataloger.ImageCatalogers()
case scope.DirectoryScheme:
catalogers = cataloger.DirectoryCatalogers()
default:
return nil, fmt.Errorf("unable to determine cataloger set from scheme=%+v", s.Scheme)
}
return cataloger.Catalog(s.Resolver, append(pluginCatalogers, cataloger.BuiltIn()...)...)
return cataloger.Catalog(s.Resolver, catalogers...)
}
// SetLogger sets the logger object used for all syft logging calls.
-1
View File
@@ -5,7 +5,6 @@ package logger
type Logger interface {
Errorf(format string, args ...interface{})
Error(args ...interface{})
Infof(format string, args ...interface{})
Info(args ...interface{})
Debugf(format string, args ...interface{})
+12 -8
View File
@@ -4,10 +4,14 @@ import (
"github.com/package-url/packageurl-go"
)
// ApkMetadata represents all captured data for a Alpine DB package entry. See https://wiki.alpinelinux.org/wiki/Apk_spec for more information.
// ApkMetadata represents all captured data for a Alpine DB package entry.
// See the following sources for more information:
// - https://wiki.alpinelinux.org/wiki/Apk_spec
// - https://git.alpinelinux.org/apk-tools/tree/src/package.c
// - https://git.alpinelinux.org/apk-tools/tree/src/database.c
type ApkMetadata struct {
Package string `mapstructure:"P" json:"package"`
OriginPackage string `mapstructure:"o" json:"origin-package"`
OriginPackage string `mapstructure:"o" json:"originPackage"`
Maintainer string `mapstructure:"m" json:"maintainer"`
Version string `mapstructure:"V" json:"version"`
License string `mapstructure:"L" json:"license"`
@@ -15,18 +19,18 @@ type ApkMetadata struct {
URL string `mapstructure:"U" json:"url"`
Description string `mapstructure:"T" json:"description"`
Size int `mapstructure:"S" json:"size"`
InstalledSize int `mapstructure:"I" json:"installed-size"`
PullDependencies string `mapstructure:"D" json:"pull-dependencies"`
PullChecksum string `mapstructure:"C" json:"pull-checksum"`
GitCommitOfAport string `mapstructure:"c" json:"git-commit-of-apk-port"`
InstalledSize int `mapstructure:"I" json:"installedSize"`
PullDependencies string `mapstructure:"D" json:"pullDependencies"`
PullChecksum string `mapstructure:"C" json:"pullChecksum"`
GitCommitOfAport string `mapstructure:"c" json:"gitCommitOfApkPort"`
Files []ApkFileRecord `json:"files"`
}
// ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records).
type ApkFileRecord struct {
Path string `json:"path"`
OwnerUID string `json:"owner-uid"`
OwnerGUI string `json:"owner-gid"`
OwnerUID string `json:"ownerUid"`
OwnerGUI string `json:"ownerGid"`
Permissions string `json:"permissions"`
Checksum string `json:"checksum"`
}
-2
View File
@@ -8,8 +8,6 @@ import (
"github.com/anchore/syft/internal/log"
)
// TODO: add reader methods (by type, id, fuzzy search, etc)
var nextPackageID int64
// Catalog represents a collection of Packages.
+10
View File
@@ -0,0 +1,10 @@
package pkg
type GemMetadata struct {
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
Files []string `mapstructure:"files" json:"files,omitempty"`
Authors []string `mapstructure:"authors" json:"authors,omitempty"`
Licenses []string `mapstructure:"licenses" json:"licenses,omitempty"`
Homepage string `mapstructure:"homepage" json:"homepage,omitempty"`
}
+13 -13
View File
@@ -5,31 +5,31 @@ import "github.com/package-url/packageurl-go"
// JavaMetadata encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
type JavaMetadata struct {
Manifest *JavaManifest `mapstructure:"Manifest" json:"manifest"`
PomProperties *PomProperties `mapstructure:"PomProperties" json:"pom-properties"`
Parent *Package `json:"parent-package"` // TODO: should this be included in the json output?
PomProperties *PomProperties `mapstructure:"PomProperties" json:"pomProperties"`
Parent *Package `json:"parentPackage"` // TODO: should this be included in the json output?
}
// PomProperties represents the fields of interest extracted from a Java archive's pom.xml file.
type PomProperties struct {
Path string
Name string `mapstructure:"name" json:"name"`
GroupID string `mapstructure:"groupId" json:"group-id"`
ArtifactID string `mapstructure:"artifactId" json:"artifact-id"`
GroupID string `mapstructure:"groupId" json:"groupId"`
ArtifactID string `mapstructure:"artifactId" json:"artifactId"`
Version string `mapstructure:"version" json:"version"`
Extra map[string]string `mapstructure:",remain" json:"extra-fields"`
Extra map[string]string `mapstructure:",remain" json:"extraFields"`
}
// JavaManifest represents the fields of interest extracted from a Java archive's META-INF/MANIFEST.MF file.
type JavaManifest struct {
Name string `mapstructure:"Name" json:"name"`
ManifestVersion string `mapstructure:"Manifest-Version" json:"manifest-version"`
SpecTitle string `mapstructure:"Specification-Title" json:"specification-title"`
SpecVersion string `mapstructure:"Specification-Version" json:"specification-version"`
SpecVendor string `mapstructure:"Specification-Vendor" json:"specification-vendor"`
ImplTitle string `mapstructure:"Implementation-Title" json:"implementation-title"`
ImplVersion string `mapstructure:"Implementation-Version" json:"implementation-version"`
ImplVendor string `mapstructure:"Implementation-Vendor" json:"implementation-vendor"`
Extra map[string]string `mapstructure:",remain" json:"extra-fields"`
ManifestVersion string `mapstructure:"Manifest-Version" json:"manifestVersion"`
SpecTitle string `mapstructure:"Specification-Title" json:"specificationTitle"`
SpecVersion string `mapstructure:"Specification-Version" json:"specificationVersion"`
SpecVendor string `mapstructure:"Specification-Vendor" json:"specificationVendor"`
ImplTitle string `mapstructure:"Implementation-Title" json:"implementationTitle"`
ImplVersion string `mapstructure:"Implementation-Version" json:"implementationVersion"`
ImplVendor string `mapstructure:"Implementation-Vendor" json:"implementationVendor"`
Extra map[string]string `mapstructure:",remain" json:"extraFields"`
}
func (m JavaMetadata) PackageURL() string {
+1 -1
View File
@@ -20,7 +20,7 @@ type Package struct {
id ID // uniquely identifies a package, set by the cataloger
Name string `json:"manifest"` // the package name
Version string `json:"version"` // the version of the package
FoundBy string `json:"found-by"` // the specific cataloger that discovered this package
FoundBy string `json:"foundBy"` // the specific cataloger that discovered this package
Source []file.Reference `json:"sources"` // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
// TODO: should we move licenses into metadata?
Licenses []string `json:"licenses"` // licenses discovered with the package metadata
+3 -2
View File
@@ -1,9 +1,10 @@
package pkg
import (
"testing"
"github.com/anchore/syft/syft/distro"
"github.com/sergi/go-diff/diffmatchpatch"
"testing"
)
func TestPackage_pURL(t *testing.T) {
@@ -56,7 +57,7 @@ func TestPackage_pURL(t *testing.T) {
pkg: Package{
Name: "name",
Version: "v0.1.0",
Type: BundlerPkg,
Type: GemPkg,
},
expected: "pkg:gem/name@v0.1.0",
},
+1 -1
View File
@@ -14,7 +14,7 @@ type RpmMetadata struct {
Epoch int `json:"epoch"`
Arch string `json:"architecture"`
Release string `json:"release"`
SourceRpm string `json:"source-rpm"`
SourceRpm string `json:"sourceRpm"`
Size int `json:"size"`
License string `json:"license"`
Vendor string `json:"vendor"`
+3 -3
View File
@@ -8,7 +8,7 @@ type Type string
const (
UnknownPkg Type = "UnknownPackage"
ApkPkg Type = "apk"
BundlerPkg Type = "bundle"
GemPkg Type = "gem"
DebPkg Type = "deb"
EggPkg Type = "egg"
// PacmanPkg Type = "pacman"
@@ -26,7 +26,7 @@ const (
var AllPkgs = []Type{
ApkPkg,
BundlerPkg,
GemPkg,
DebPkg,
EggPkg,
// PacmanPkg,
@@ -45,7 +45,7 @@ func (t Type) PackageURLType() string {
switch t {
case ApkPkg:
return "alpine"
case BundlerPkg:
case GemPkg:
return packageurl.TypeGem
case DebPkg:
return "deb"
-29
View File
@@ -1,29 +0,0 @@
package plugin
import (
"context"
"github.com/anchore/syft/syft/cataloger"
syftPluginGrpc "github.com/anchore/syft/syft/plugin/grpc"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/hashicorp/go-plugin"
"google.golang.org/grpc"
)
// integrity check
var _ plugin.GRPCPlugin = &CatalogerPlugin{}
// CatalogerPlugin is the implementation of plugin.Plugin that is served/consumed.
type CatalogerPlugin struct {
plugin.NetRPCUnsupportedPlugin
Impl cataloger.Cataloger
}
func (p *CatalogerPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
proto.RegisterCatalogerServer(s, syftPluginGrpc.NewCatalogServer(p.Impl, broker))
return nil
}
func (p *CatalogerPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
return syftPluginGrpc.NewCatalogerClient(proto.NewCatalogerClient(c), broker), nil
}
-10
View File
@@ -1,10 +0,0 @@
package plugin
type Config struct {
Name string
Type Type
Command string
Args []string
Env []string
Sha256 []byte
}
-117
View File
@@ -1,117 +0,0 @@
package grpc
import (
"context"
"fmt"
"io"
"io/ioutil"
"github.com/anchore/syft/syft/cataloger"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/anchore/syft/syft/scope"
"github.com/hashicorp/go-plugin"
"google.golang.org/grpc"
)
var _ cataloger.Cataloger = &CatalogerClient{}
type CatalogerClient struct {
broker *plugin.GRPCBroker
client proto.CatalogerClient
}
func NewCatalogerClient(client proto.CatalogerClient, broker *plugin.GRPCBroker) *CatalogerClient {
return &CatalogerClient{
broker: broker,
client: client,
}
}
func (c *CatalogerClient) Name() string {
resp, err := c.client.Name(context.Background(), &proto.Empty{})
if err != nil {
return fmt.Sprintf("error (%s)", err.Error())
}
return resp.Name
}
func (c *CatalogerClient) SelectFiles(resolver scope.FileResolver) []file.Reference {
fileResolverServer := &FileResolverServer{Impl: resolver}
var s *grpc.Server
serverFunc := func(opts []grpc.ServerOption) *grpc.Server {
s = grpc.NewServer(opts...)
proto.RegisterFileResolverServer(s, fileResolverServer)
return s
}
brokerID := c.broker.NextId()
go c.broker.AcceptAndServe(brokerID, serverFunc)
resp, err := c.client.SelectFiles(context.Background(), &proto.SelectFilesRequest{
FileResolverBrokerId: brokerID,
})
if err != nil {
// TODO: nope
panic(err)
}
s.Stop()
var result []file.Reference
for _, f := range resp.Files {
result = append(result, file.NewFileReferenceWithID(file.Path(f.Path), uint64(f.Id)))
}
return result
}
func (c *CatalogerClient) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
var fileRefContents []*proto.FileReferenceContents
for ref, reader := range contents {
readerContents, err := ioutil.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("could not read %+v contents: %+v", ref, err)
}
fileRefContents = append(fileRefContents, &proto.FileReferenceContents{
Id: int64(ref.ID()),
Path: string(ref.Path),
Contents: string(readerContents),
})
}
resp, err := c.client.Catalog(context.Background(), &proto.CatalogRequest{
Contents: fileRefContents,
})
if err != nil {
return nil, err
}
var result []pkg.Package
for _, p := range resp.Package {
if p != nil {
var sources []file.Reference
for _, s := range p.Source {
sources = append(sources, file.NewFileReferenceWithID(file.Path(s.Path), uint64(s.Id)))
}
// TODO: this is potentially brittle
result = append(result, pkg.Package{
Name: p.Name,
Version: p.Version,
FoundBy: p.FoundBy,
Source: sources,
Licenses: p.Licenses,
Language: pkg.Language(p.Language),
Type: pkg.Type(p.Type),
Metadata: p.Metadata,
})
}
}
return result, nil
}
-99
View File
@@ -1,99 +0,0 @@
package grpc
import (
"context"
"io"
"strings"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/hashicorp/go-plugin"
)
type CatalogerServer struct {
Impl cataloger.Cataloger
broker *plugin.GRPCBroker
}
func NewCatalogServer(impl cataloger.Cataloger, broker *plugin.GRPCBroker) *CatalogerServer {
return &CatalogerServer{
Impl: impl,
broker: broker,
}
}
func (s *CatalogerServer) Name(ctx context.Context, req *proto.Empty) (*proto.NameResponse, error) {
name := s.Impl.Name()
return &proto.NameResponse{
Name: name,
}, nil
}
func (s *CatalogerServer) SelectFiles(ctx context.Context, req *proto.SelectFilesRequest) (*proto.SelectFilesResponse, error) {
conn, err := s.broker.Dial(req.FileResolverBrokerId)
if err != nil {
return nil, err
}
defer conn.Close()
fileResolverClient := &FileResolverClient{proto.NewFileResolverClient(conn)}
fileRefs := s.Impl.SelectFiles(fileResolverClient)
var refs []*proto.FileReference
for _, ref := range fileRefs {
refs = append(refs, &proto.FileReference{
Id: int64(ref.ID()),
Path: string(ref.Path),
})
}
return &proto.SelectFilesResponse{
Files: refs,
}, nil
}
func (s *CatalogerServer) Catalog(ctx context.Context, req *proto.CatalogRequest) (*proto.CatalogResponse, error) {
contents := make(map[file.Reference]io.Reader)
for _, f := range req.Contents {
contents[file.NewFileReferenceWithID(file.Path(f.Path), uint64(f.Id))] = strings.NewReader(f.Contents)
}
packages, err := s.Impl.Catalog(contents)
if err != nil {
return nil, err
}
var results []*proto.Package
for _, p := range packages {
var sources []*proto.FileReference
for _, s := range p.Source {
sources = append(sources, &proto.FileReference{
Id: int64(s.ID()),
Path: string(s.Path),
})
}
var metadata map[string]string
if v, ok := p.Metadata.(map[string]string); ok {
metadata = v
}
// TODO: this is potentially brittle
results = append(results, &proto.Package{
Name: p.Name,
Version: p.Version,
FoundBy: p.FoundBy,
Source: sources,
Licenses: p.Licenses,
Language: uint64(p.Language),
Type: string(p.Type),
// TODO: metadata needs to be more thoroughly thought through
Metadata: metadata,
})
}
return &proto.CatalogResponse{
Package: results,
}, nil
}
-50
View File
@@ -1,50 +0,0 @@
package grpc
import (
"context"
"github.com/anchore/syft/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/plugin/proto"
)
type FileResolverClient struct{ client proto.FileResolverClient }
func (m *FileResolverClient) FilesByPath(paths ...file.Path) ([]file.Reference, error) {
var pathStrs []string
for _, p := range paths {
pathStrs = append(pathStrs, string(p))
}
resp, err := m.client.FilesByPath(context.Background(), &proto.FileResolverRequest{
Paths: pathStrs,
})
if err != nil {
return nil, err
}
var result []file.Reference
for _, ref := range resp.Files {
result = append(result, file.NewFileReferenceWithID(file.Path(ref.Path), uint64(ref.Id)))
}
return result, err
}
func (m *FileResolverClient) FilesByGlob(patterns ...string) ([]file.Reference, error) {
resp, err := m.client.FilesByGlob(context.Background(), &proto.FileResolverRequest{
Paths: patterns,
})
if err != nil {
return nil, err
}
log.Debugf("FilesByGlob Response: %+v", resp)
var result []file.Reference
for _, ref := range resp.Files {
result = append(result, file.NewFileReferenceWithID(file.Path(ref.Path), uint64(ref.Id)))
}
return result, err
}
-55
View File
@@ -1,55 +0,0 @@
package grpc
import (
"context"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/anchore/syft/syft/scope"
)
type FileResolverServer struct {
Impl scope.FileResolver
}
func (m *FileResolverServer) FilesByPath(ctx context.Context, req *proto.FileResolverRequest) (resp *proto.FileResolverResponse, err error) {
var paths []file.Path
for _, p := range req.Paths {
paths = append(paths, file.Path(p))
}
r, err := m.Impl.FilesByPath(paths...)
if err != nil {
return nil, err
}
var refs []*proto.FileReference
for _, ref := range r {
refs = append(refs, &proto.FileReference{
Id: int64(ref.ID()),
Path: string(ref.Path),
})
}
return &proto.FileResolverResponse{
Files: refs,
}, nil
}
func (m *FileResolverServer) FilesByGlob(ctx context.Context, req *proto.FileResolverRequest) (resp *proto.FileResolverResponse, err error) {
r, err := m.Impl.FilesByGlob(req.Paths...)
if err != nil {
return nil, err
}
var refs []*proto.FileReference
for _, ref := range r {
refs = append(refs, &proto.FileReference{
Id: int64(ref.ID()),
Path: string(ref.Path),
})
}
return &proto.FileResolverResponse{
Files: refs,
}, nil
}
-96
View File
@@ -1,96 +0,0 @@
package plugin
import (
"crypto/sha256"
"fmt"
"os/exec"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/logger"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
)
var versionedPlugins = map[int]plugin.PluginSet{
1: {
TypeCataloger.String(): &CatalogerPlugin{},
},
}
type Plugin struct {
Config Config
clientConfig plugin.ClientConfig
client *plugin.Client
}
// TODO: type should be in the name like with terraform "terraform-<TYPE>-<NAME>"
func NewPlugin(config Config) Plugin {
//cmd := exec.Command("sh", "-c", config.Command) //, config.Args...)
cmd := exec.Command(config.Command)
cmd.Env = append(cmd.Env, config.Env...)
var secureConfig *plugin.SecureConfig
if len(config.Sha256) > 0 {
secureConfig = &plugin.SecureConfig{
Checksum: config.Sha256,
Hash: sha256.New(),
}
}
var pluginLogger hclog.Logger
if logrusLogger, ok := log.Log.(*logger.LogrusLogger); ok {
pluginLogger = logger.NewLogrusHCLogAdapter(logrusLogger.Logger, map[string]interface{}{"plugin": config.Name})
} else {
// TODO: this does not fully map features, thus logging will be awkward
// TODO: find a better way to map loggers (expand our interface?)
pluginLogger = logger.NewLoggerHCLogAdapter(log.Log)
}
clientConfig := plugin.ClientConfig{
HandshakeConfig: config.Type.HandshakeConfig(),
VersionedPlugins: versionedPlugins,
SecureConfig: secureConfig,
Cmd: cmd,
Logger: pluginLogger,
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolGRPC,
},
}
return Plugin{
Config: config,
clientConfig: clientConfig,
}
}
func (p *Plugin) Start() (interface{}, error) {
if p.client != nil {
return nil, fmt.Errorf("plugin already started")
}
// start the plugin in a sub process
p.client = plugin.NewClient(&p.clientConfig)
// connect to the sub process via RPC
rpcClient, err := p.client.Client()
if err != nil {
return nil, err
}
// fetch the plugin object meeting the requested interface
raw, err := rpcClient.Dispense(p.Config.Type.String())
if err != nil {
return nil, err
}
return raw, nil
}
func (p *Plugin) Stop() error {
if p.client == nil {
return fmt.Errorf("plugin has not been started")
}
p.client.Kill()
return nil
}
-841
View File
@@ -1,841 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: cataloger.proto
package proto
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type FileReference struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileReference) Reset() { *m = FileReference{} }
func (m *FileReference) String() string { return proto.CompactTextString(m) }
func (*FileReference) ProtoMessage() {}
func (*FileReference) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{0}
}
func (m *FileReference) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileReference.Unmarshal(m, b)
}
func (m *FileReference) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileReference.Marshal(b, m, deterministic)
}
func (m *FileReference) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileReference.Merge(m, src)
}
func (m *FileReference) XXX_Size() int {
return xxx_messageInfo_FileReference.Size(m)
}
func (m *FileReference) XXX_DiscardUnknown() {
xxx_messageInfo_FileReference.DiscardUnknown(m)
}
var xxx_messageInfo_FileReference proto.InternalMessageInfo
func (m *FileReference) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *FileReference) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
type FileReferenceContents struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
Contents string `protobuf:"bytes,3,opt,name=contents,proto3" json:"contents,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileReferenceContents) Reset() { *m = FileReferenceContents{} }
func (m *FileReferenceContents) String() string { return proto.CompactTextString(m) }
func (*FileReferenceContents) ProtoMessage() {}
func (*FileReferenceContents) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{1}
}
func (m *FileReferenceContents) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileReferenceContents.Unmarshal(m, b)
}
func (m *FileReferenceContents) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileReferenceContents.Marshal(b, m, deterministic)
}
func (m *FileReferenceContents) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileReferenceContents.Merge(m, src)
}
func (m *FileReferenceContents) XXX_Size() int {
return xxx_messageInfo_FileReferenceContents.Size(m)
}
func (m *FileReferenceContents) XXX_DiscardUnknown() {
xxx_messageInfo_FileReferenceContents.DiscardUnknown(m)
}
var xxx_messageInfo_FileReferenceContents proto.InternalMessageInfo
func (m *FileReferenceContents) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *FileReferenceContents) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
func (m *FileReferenceContents) GetContents() string {
if m != nil {
return m.Contents
}
return ""
}
type Empty struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{2}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Empty.Unmarshal(m, b)
}
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
}
func (m *Empty) XXX_Merge(src proto.Message) {
xxx_messageInfo_Empty.Merge(m, src)
}
func (m *Empty) XXX_Size() int {
return xxx_messageInfo_Empty.Size(m)
}
func (m *Empty) XXX_DiscardUnknown() {
xxx_messageInfo_Empty.DiscardUnknown(m)
}
var xxx_messageInfo_Empty proto.InternalMessageInfo
type FileResolverRequest struct {
Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileResolverRequest) Reset() { *m = FileResolverRequest{} }
func (m *FileResolverRequest) String() string { return proto.CompactTextString(m) }
func (*FileResolverRequest) ProtoMessage() {}
func (*FileResolverRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{3}
}
func (m *FileResolverRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileResolverRequest.Unmarshal(m, b)
}
func (m *FileResolverRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileResolverRequest.Marshal(b, m, deterministic)
}
func (m *FileResolverRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileResolverRequest.Merge(m, src)
}
func (m *FileResolverRequest) XXX_Size() int {
return xxx_messageInfo_FileResolverRequest.Size(m)
}
func (m *FileResolverRequest) XXX_DiscardUnknown() {
xxx_messageInfo_FileResolverRequest.DiscardUnknown(m)
}
var xxx_messageInfo_FileResolverRequest proto.InternalMessageInfo
func (m *FileResolverRequest) GetPaths() []string {
if m != nil {
return m.Paths
}
return nil
}
type FileResolverResponse struct {
Files []*FileReference `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileResolverResponse) Reset() { *m = FileResolverResponse{} }
func (m *FileResolverResponse) String() string { return proto.CompactTextString(m) }
func (*FileResolverResponse) ProtoMessage() {}
func (*FileResolverResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{4}
}
func (m *FileResolverResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileResolverResponse.Unmarshal(m, b)
}
func (m *FileResolverResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileResolverResponse.Marshal(b, m, deterministic)
}
func (m *FileResolverResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileResolverResponse.Merge(m, src)
}
func (m *FileResolverResponse) XXX_Size() int {
return xxx_messageInfo_FileResolverResponse.Size(m)
}
func (m *FileResolverResponse) XXX_DiscardUnknown() {
xxx_messageInfo_FileResolverResponse.DiscardUnknown(m)
}
var xxx_messageInfo_FileResolverResponse proto.InternalMessageInfo
func (m *FileResolverResponse) GetFiles() []*FileReference {
if m != nil {
return m.Files
}
return nil
}
type NameResponse struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NameResponse) Reset() { *m = NameResponse{} }
func (m *NameResponse) String() string { return proto.CompactTextString(m) }
func (*NameResponse) ProtoMessage() {}
func (*NameResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{5}
}
func (m *NameResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NameResponse.Unmarshal(m, b)
}
func (m *NameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NameResponse.Marshal(b, m, deterministic)
}
func (m *NameResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_NameResponse.Merge(m, src)
}
func (m *NameResponse) XXX_Size() int {
return xxx_messageInfo_NameResponse.Size(m)
}
func (m *NameResponse) XXX_DiscardUnknown() {
xxx_messageInfo_NameResponse.DiscardUnknown(m)
}
var xxx_messageInfo_NameResponse proto.InternalMessageInfo
func (m *NameResponse) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type SelectFilesRequest struct {
FileResolverBrokerId uint32 `protobuf:"varint,1,opt,name=fileResolverBrokerId,proto3" json:"fileResolverBrokerId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SelectFilesRequest) Reset() { *m = SelectFilesRequest{} }
func (m *SelectFilesRequest) String() string { return proto.CompactTextString(m) }
func (*SelectFilesRequest) ProtoMessage() {}
func (*SelectFilesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{6}
}
func (m *SelectFilesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SelectFilesRequest.Unmarshal(m, b)
}
func (m *SelectFilesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SelectFilesRequest.Marshal(b, m, deterministic)
}
func (m *SelectFilesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SelectFilesRequest.Merge(m, src)
}
func (m *SelectFilesRequest) XXX_Size() int {
return xxx_messageInfo_SelectFilesRequest.Size(m)
}
func (m *SelectFilesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SelectFilesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SelectFilesRequest proto.InternalMessageInfo
func (m *SelectFilesRequest) GetFileResolverBrokerId() uint32 {
if m != nil {
return m.FileResolverBrokerId
}
return 0
}
type SelectFilesResponse struct {
Files []*FileReference `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SelectFilesResponse) Reset() { *m = SelectFilesResponse{} }
func (m *SelectFilesResponse) String() string { return proto.CompactTextString(m) }
func (*SelectFilesResponse) ProtoMessage() {}
func (*SelectFilesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{7}
}
func (m *SelectFilesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SelectFilesResponse.Unmarshal(m, b)
}
func (m *SelectFilesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SelectFilesResponse.Marshal(b, m, deterministic)
}
func (m *SelectFilesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SelectFilesResponse.Merge(m, src)
}
func (m *SelectFilesResponse) XXX_Size() int {
return xxx_messageInfo_SelectFilesResponse.Size(m)
}
func (m *SelectFilesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SelectFilesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SelectFilesResponse proto.InternalMessageInfo
func (m *SelectFilesResponse) GetFiles() []*FileReference {
if m != nil {
return m.Files
}
return nil
}
type Package struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
FoundBy string `protobuf:"bytes,3,opt,name=foundBy,proto3" json:"foundBy,omitempty"`
Source []*FileReference `protobuf:"bytes,4,rep,name=source,proto3" json:"source,omitempty"`
Licenses []string `protobuf:"bytes,5,rep,name=licenses,proto3" json:"licenses,omitempty"`
Language uint64 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"`
Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"`
Metadata map[string]string `protobuf:"bytes,8,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Package) Reset() { *m = Package{} }
func (m *Package) String() string { return proto.CompactTextString(m) }
func (*Package) ProtoMessage() {}
func (*Package) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{8}
}
func (m *Package) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Package.Unmarshal(m, b)
}
func (m *Package) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Package.Marshal(b, m, deterministic)
}
func (m *Package) XXX_Merge(src proto.Message) {
xxx_messageInfo_Package.Merge(m, src)
}
func (m *Package) XXX_Size() int {
return xxx_messageInfo_Package.Size(m)
}
func (m *Package) XXX_DiscardUnknown() {
xxx_messageInfo_Package.DiscardUnknown(m)
}
var xxx_messageInfo_Package proto.InternalMessageInfo
func (m *Package) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Package) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Package) GetFoundBy() string {
if m != nil {
return m.FoundBy
}
return ""
}
func (m *Package) GetSource() []*FileReference {
if m != nil {
return m.Source
}
return nil
}
func (m *Package) GetLicenses() []string {
if m != nil {
return m.Licenses
}
return nil
}
func (m *Package) GetLanguage() uint64 {
if m != nil {
return m.Language
}
return 0
}
func (m *Package) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *Package) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
type CatalogRequest struct {
Contents []*FileReferenceContents `protobuf:"bytes,1,rep,name=contents,proto3" json:"contents,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CatalogRequest) Reset() { *m = CatalogRequest{} }
func (m *CatalogRequest) String() string { return proto.CompactTextString(m) }
func (*CatalogRequest) ProtoMessage() {}
func (*CatalogRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{9}
}
func (m *CatalogRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CatalogRequest.Unmarshal(m, b)
}
func (m *CatalogRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CatalogRequest.Marshal(b, m, deterministic)
}
func (m *CatalogRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CatalogRequest.Merge(m, src)
}
func (m *CatalogRequest) XXX_Size() int {
return xxx_messageInfo_CatalogRequest.Size(m)
}
func (m *CatalogRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CatalogRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CatalogRequest proto.InternalMessageInfo
func (m *CatalogRequest) GetContents() []*FileReferenceContents {
if m != nil {
return m.Contents
}
return nil
}
type CatalogResponse struct {
Package []*Package `protobuf:"bytes,1,rep,name=package,proto3" json:"package,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CatalogResponse) Reset() { *m = CatalogResponse{} }
func (m *CatalogResponse) String() string { return proto.CompactTextString(m) }
func (*CatalogResponse) ProtoMessage() {}
func (*CatalogResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{10}
}
func (m *CatalogResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CatalogResponse.Unmarshal(m, b)
}
func (m *CatalogResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CatalogResponse.Marshal(b, m, deterministic)
}
func (m *CatalogResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CatalogResponse.Merge(m, src)
}
func (m *CatalogResponse) XXX_Size() int {
return xxx_messageInfo_CatalogResponse.Size(m)
}
func (m *CatalogResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CatalogResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CatalogResponse proto.InternalMessageInfo
func (m *CatalogResponse) GetPackage() []*Package {
if m != nil {
return m.Package
}
return nil
}
func init() {
proto.RegisterType((*FileReference)(nil), "proto.FileReference")
proto.RegisterType((*FileReferenceContents)(nil), "proto.FileReferenceContents")
proto.RegisterType((*Empty)(nil), "proto.Empty")
proto.RegisterType((*FileResolverRequest)(nil), "proto.FileResolverRequest")
proto.RegisterType((*FileResolverResponse)(nil), "proto.FileResolverResponse")
proto.RegisterType((*NameResponse)(nil), "proto.NameResponse")
proto.RegisterType((*SelectFilesRequest)(nil), "proto.SelectFilesRequest")
proto.RegisterType((*SelectFilesResponse)(nil), "proto.SelectFilesResponse")
proto.RegisterType((*Package)(nil), "proto.Package")
proto.RegisterMapType((map[string]string)(nil), "proto.Package.MetadataEntry")
proto.RegisterType((*CatalogRequest)(nil), "proto.CatalogRequest")
proto.RegisterType((*CatalogResponse)(nil), "proto.CatalogResponse")
}
func init() { proto.RegisterFile("cataloger.proto", fileDescriptor_346aaad5d7f2c092) }
var fileDescriptor_346aaad5d7f2c092 = []byte{
// 541 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xcb, 0x8e, 0xd3, 0x3c,
0x14, 0x56, 0x7a, 0x4b, 0x7b, 0x7a, 0x99, 0x5f, 0x6e, 0xe7, 0x57, 0x08, 0x2c, 0xaa, 0xac, 0xca,
0x45, 0x5d, 0x74, 0x36, 0x15, 0xb3, 0xa2, 0x65, 0x86, 0x8b, 0x04, 0x1a, 0x99, 0x05, 0x6b, 0x4f,
0x7a, 0x5a, 0xa2, 0xa6, 0x71, 0x88, 0xdd, 0x4a, 0x79, 0x19, 0x1e, 0x83, 0x07, 0xe0, 0xc9, 0x50,
0x6c, 0x27, 0xd4, 0x10, 0x21, 0x34, 0xab, 0xf8, 0xe4, 0xfb, 0xfc, 0x9d, 0xcf, 0xe7, 0x02, 0x17,
0x21, 0x93, 0x2c, 0xe6, 0x3b, 0xcc, 0xe6, 0x69, 0xc6, 0x25, 0x27, 0x6d, 0xf5, 0x09, 0xae, 0x60,
0x78, 0x1b, 0xc5, 0x48, 0x71, 0x8b, 0x19, 0x26, 0x21, 0x92, 0x11, 0x34, 0xa2, 0x8d, 0xe7, 0x4c,
0x9d, 0x59, 0x93, 0x36, 0xa2, 0x0d, 0x21, 0xd0, 0x4a, 0x99, 0xfc, 0xe2, 0x35, 0xa6, 0xce, 0xac,
0x47, 0xd5, 0x39, 0xf8, 0x0c, 0x97, 0xd6, 0xa5, 0x35, 0x4f, 0x24, 0x26, 0x52, 0xfc, 0xcb, 0x65,
0xe2, 0x43, 0x37, 0x34, 0x7c, 0xaf, 0xa9, 0xfe, 0x57, 0x71, 0xe0, 0x42, 0xfb, 0xe6, 0x90, 0xca,
0x3c, 0x78, 0x0e, 0x63, 0x9d, 0x41, 0xf0, 0xf8, 0x84, 0x19, 0xc5, 0xaf, 0x47, 0x14, 0x92, 0x4c,
0xa0, 0x5d, 0x68, 0x08, 0xcf, 0x99, 0x36, 0x67, 0x3d, 0xaa, 0x83, 0x60, 0x05, 0x13, 0x9b, 0x2c,
0x52, 0x9e, 0x08, 0x24, 0xcf, 0xa0, 0xbd, 0x8d, 0x62, 0xd4, 0xec, 0xfe, 0x62, 0xa2, 0x5f, 0x3e,
0xb7, 0xac, 0x53, 0x4d, 0x09, 0x02, 0x18, 0x7c, 0x64, 0x07, 0xac, 0xee, 0x12, 0x68, 0x25, 0xec,
0x80, 0xea, 0x2d, 0x3d, 0xaa, 0xce, 0xc1, 0x5b, 0x20, 0x9f, 0x30, 0xc6, 0x50, 0x16, 0x0a, 0xa2,
0xf4, 0xb4, 0x80, 0xc9, 0xf6, 0x2c, 0xfb, 0x2a, 0xe3, 0x7b, 0xcc, 0xde, 0xe9, 0x2a, 0x0c, 0x69,
0x2d, 0x16, 0xbc, 0x82, 0xb1, 0xa5, 0xf4, 0x00, 0xc3, 0x3f, 0x1a, 0xe0, 0xde, 0xb1, 0x70, 0xcf,
0x76, 0xb5, 0x66, 0x89, 0x07, 0xee, 0x09, 0x33, 0x11, 0xf1, 0xc4, 0x54, 0xbf, 0x0c, 0x0b, 0x64,
0xcb, 0x8f, 0xc9, 0x66, 0x95, 0x9b, 0xfa, 0x97, 0x21, 0x79, 0x01, 0x1d, 0xc1, 0x8f, 0x59, 0x88,
0x5e, 0xeb, 0x2f, 0x06, 0x0c, 0xa7, 0x68, 0x64, 0x1c, 0x85, 0x98, 0x08, 0x14, 0x5e, 0x5b, 0xf5,
0xa3, 0x8a, 0x15, 0xc6, 0x92, 0xdd, 0x91, 0xed, 0xd0, 0xeb, 0x4c, 0x9d, 0x59, 0x8b, 0x56, 0x71,
0xe1, 0x56, 0xe6, 0x29, 0x7a, 0xae, 0x76, 0x5b, 0x9c, 0xc9, 0x12, 0xba, 0x07, 0x94, 0x6c, 0xc3,
0x24, 0xf3, 0xba, 0x2a, 0xf7, 0x13, 0x93, 0xdb, 0xbc, 0x71, 0xfe, 0xc1, 0xc0, 0x37, 0x89, 0xcc,
0x72, 0x5a, 0xb1, 0xfd, 0x6b, 0x18, 0x5a, 0x10, 0xf9, 0x0f, 0x9a, 0x7b, 0xcc, 0x4d, 0x2d, 0x8a,
0x63, 0x31, 0x35, 0x27, 0x16, 0x1f, 0xd1, 0x14, 0x42, 0x07, 0x2f, 0x1b, 0x4b, 0x27, 0x78, 0x0f,
0xa3, 0xb5, 0xde, 0x8b, 0xb2, 0x9b, 0xcb, 0xb3, 0xe9, 0x74, 0x2c, 0x23, 0xb5, 0x13, 0x7f, 0x36,
0xbb, 0xd7, 0x70, 0x51, 0x69, 0x99, 0x7e, 0xce, 0xc0, 0x4d, 0xb5, 0x7d, 0xa3, 0x35, 0xb2, 0x1f,
0x45, 0x4b, 0x78, 0xf1, 0xdd, 0x81, 0xde, 0xba, 0xdc, 0x50, 0xf2, 0x14, 0x5a, 0xc5, 0x30, 0x92,
0x81, 0xa1, 0xab, 0x9d, 0xf0, 0xc7, 0x26, 0xb2, 0xe6, 0xf4, 0x35, 0xf4, 0xcf, 0x26, 0x89, 0x3c,
0x32, 0x9c, 0x3f, 0xe7, 0xd4, 0xf7, 0xeb, 0x20, 0xa3, 0xb2, 0x04, 0xd7, 0x64, 0x27, 0x97, 0x86,
0x66, 0xd7, 0xc5, 0xff, 0xff, 0xf7, 0xdf, 0xfa, 0xe6, 0xe2, 0x9b, 0x03, 0x83, 0xf3, 0xe5, 0x23,
0xb7, 0xd0, 0x57, 0xda, 0xab, 0xfc, 0x4e, 0x6d, 0xbb, 0x55, 0x3d, 0x6b, 0x9b, 0xfd, 0xc7, 0xb5,
0x98, 0xb1, 0xf4, 0x4b, 0xe7, 0x4d, 0xcc, 0xef, 0x1f, 0xac, 0x73, 0xdf, 0x51, 0xd8, 0xd5, 0xcf,
0x00, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xfe, 0x04, 0x3e, 0x01, 0x05, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// CatalogerClient is the client API for Cataloger service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CatalogerClient interface {
Name(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*NameResponse, error)
SelectFiles(ctx context.Context, in *SelectFilesRequest, opts ...grpc.CallOption) (*SelectFilesResponse, error)
Catalog(ctx context.Context, in *CatalogRequest, opts ...grpc.CallOption) (*CatalogResponse, error)
}
type catalogerClient struct {
cc grpc.ClientConnInterface
}
func NewCatalogerClient(cc grpc.ClientConnInterface) CatalogerClient {
return &catalogerClient{cc}
}
func (c *catalogerClient) Name(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*NameResponse, error) {
out := new(NameResponse)
err := c.cc.Invoke(ctx, "/proto.Cataloger/Name", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *catalogerClient) SelectFiles(ctx context.Context, in *SelectFilesRequest, opts ...grpc.CallOption) (*SelectFilesResponse, error) {
out := new(SelectFilesResponse)
err := c.cc.Invoke(ctx, "/proto.Cataloger/SelectFiles", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *catalogerClient) Catalog(ctx context.Context, in *CatalogRequest, opts ...grpc.CallOption) (*CatalogResponse, error) {
out := new(CatalogResponse)
err := c.cc.Invoke(ctx, "/proto.Cataloger/Catalog", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CatalogerServer is the server API for Cataloger service.
type CatalogerServer interface {
Name(context.Context, *Empty) (*NameResponse, error)
SelectFiles(context.Context, *SelectFilesRequest) (*SelectFilesResponse, error)
Catalog(context.Context, *CatalogRequest) (*CatalogResponse, error)
}
// UnimplementedCatalogerServer can be embedded to have forward compatible implementations.
type UnimplementedCatalogerServer struct {
}
func (*UnimplementedCatalogerServer) Name(ctx context.Context, req *Empty) (*NameResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Name not implemented")
}
func (*UnimplementedCatalogerServer) SelectFiles(ctx context.Context, req *SelectFilesRequest) (*SelectFilesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SelectFiles not implemented")
}
func (*UnimplementedCatalogerServer) Catalog(ctx context.Context, req *CatalogRequest) (*CatalogResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Catalog not implemented")
}
func RegisterCatalogerServer(s *grpc.Server, srv CatalogerServer) {
s.RegisterService(&_Cataloger_serviceDesc, srv)
}
func _Cataloger_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CatalogerServer).Name(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.Cataloger/Name",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CatalogerServer).Name(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Cataloger_SelectFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SelectFilesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CatalogerServer).SelectFiles(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.Cataloger/SelectFiles",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CatalogerServer).SelectFiles(ctx, req.(*SelectFilesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Cataloger_Catalog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CatalogRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CatalogerServer).Catalog(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.Cataloger/Catalog",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CatalogerServer).Catalog(ctx, req.(*CatalogRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Cataloger_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.Cataloger",
HandlerType: (*CatalogerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Name",
Handler: _Cataloger_Name_Handler,
},
{
MethodName: "SelectFiles",
Handler: _Cataloger_SelectFiles_Handler,
},
{
MethodName: "Catalog",
Handler: _Cataloger_Catalog_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "cataloger.proto",
}
// FileResolverClient is the client API for FileResolver service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type FileResolverClient interface {
FilesByPath(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error)
FilesByGlob(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error)
}
type fileResolverClient struct {
cc grpc.ClientConnInterface
}
func NewFileResolverClient(cc grpc.ClientConnInterface) FileResolverClient {
return &fileResolverClient{cc}
}
func (c *fileResolverClient) FilesByPath(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error) {
out := new(FileResolverResponse)
err := c.cc.Invoke(ctx, "/proto.FileResolver/FilesByPath", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *fileResolverClient) FilesByGlob(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error) {
out := new(FileResolverResponse)
err := c.cc.Invoke(ctx, "/proto.FileResolver/FilesByGlob", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// FileResolverServer is the server API for FileResolver service.
type FileResolverServer interface {
FilesByPath(context.Context, *FileResolverRequest) (*FileResolverResponse, error)
FilesByGlob(context.Context, *FileResolverRequest) (*FileResolverResponse, error)
}
// UnimplementedFileResolverServer can be embedded to have forward compatible implementations.
type UnimplementedFileResolverServer struct {
}
func (*UnimplementedFileResolverServer) FilesByPath(ctx context.Context, req *FileResolverRequest) (*FileResolverResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FilesByPath not implemented")
}
func (*UnimplementedFileResolverServer) FilesByGlob(ctx context.Context, req *FileResolverRequest) (*FileResolverResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FilesByGlob not implemented")
}
func RegisterFileResolverServer(s *grpc.Server, srv FileResolverServer) {
s.RegisterService(&_FileResolver_serviceDesc, srv)
}
func _FileResolver_FilesByPath_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FileResolverRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FileResolverServer).FilesByPath(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.FileResolver/FilesByPath",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FileResolverServer).FilesByPath(ctx, req.(*FileResolverRequest))
}
return interceptor(ctx, in, info, handler)
}
func _FileResolver_FilesByGlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FileResolverRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FileResolverServer).FilesByGlob(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.FileResolver/FilesByGlob",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FileResolverServer).FilesByGlob(ctx, req.(*FileResolverRequest))
}
return interceptor(ctx, in, info, handler)
}
var _FileResolver_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.FileResolver",
HandlerType: (*FileResolverServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "FilesByPath",
Handler: _FileResolver_FilesByPath_Handler,
},
{
MethodName: "FilesByGlob",
Handler: _FileResolver_FilesByGlob_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "cataloger.proto",
}
-67
View File
@@ -1,67 +0,0 @@
syntax = "proto3";
package proto;
message FileReference {
int64 id = 1;
string path = 2;
}
message FileReferenceContents {
int64 id = 1;
string path = 2;
string contents = 3;
}
message Empty {
}
message FileResolverRequest {
repeated string paths = 1;
}
message FileResolverResponse {
repeated FileReference files = 1;
}
message NameResponse {
string name = 1;
}
message SelectFilesRequest {
uint32 fileResolverBrokerId = 1;
}
message SelectFilesResponse {
repeated FileReference files = 1;
}
message Package {
string name = 1;
string version = 2;
string foundBy = 3;
repeated FileReference source = 4;
repeated string licenses = 5;
uint64 language = 6;
string type = 7;
map<string, string> metadata = 8;
}
message CatalogRequest {
repeated FileReferenceContents contents = 1;
}
message CatalogResponse {
repeated Package package = 1;
}
service Cataloger {
rpc Name(Empty) returns (NameResponse);
rpc SelectFiles(SelectFilesRequest) returns (SelectFilesResponse);
rpc Catalog(CatalogRequest) returns (CatalogResponse);
}
service FileResolver {
rpc FilesByPath(FileResolverRequest) returns (FileResolverResponse);
rpc FilesByGlob(FileResolverRequest) returns (FileResolverResponse);
}
-119
View File
@@ -1,119 +0,0 @@
package plugin
import (
"fmt"
"path/filepath"
"strings"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloger"
)
type Repository struct {
byType map[Type][]Plugin
}
func NewRepository() *Repository {
return &Repository{
byType: make(map[Type][]Plugin),
}
}
func NewRepositoryFromDirectory(dir string) (*Repository, error) {
repo := NewRepository()
if dir == "" {
return repo, nil
}
return repo, repo.AddFromDirectory(dir)
}
func (c *Repository) Add(plugin Plugin) {
c.byType[plugin.Config.Type] = append(c.byType[plugin.Config.Type], plugin)
}
func (c *Repository) AddFromDirectory(dir string, pluginTypes ...Type) error {
plugins, err := Discover(dir, pluginTypes...)
if err != nil {
return err
}
for _, plugin := range plugins {
log.Debugf("added plugin: %q", plugin.Config.Name)
c.Add(plugin)
}
log.Debugf("%d plugins added", len(plugins))
return nil
}
func (c *Repository) Get(pluginType Type) []Plugin {
return c.byType[pluginType]
}
func (c *Repository) ActivateCatalogers() ([]cataloger.Cataloger, func(), error) {
var result []cataloger.Cataloger
var plugins []Plugin
var deactivateFn = func() {
for _, plugin := range plugins {
if err := plugin.Stop(); err != nil {
log.Errorf("failed to stop plugin: %w", err)
}
}
}
for _, plugin := range c.Get(TypeCataloger) {
raw, err := plugin.Start()
if err != nil {
return nil, deactivateFn, err
}
plugins = append(plugins, plugin)
theCataloger, ok := raw.(cataloger.Cataloger)
if !ok {
return nil, deactivateFn, fmt.Errorf("activation of cataloger did not return a cataloger object (name=%s type=%T)", plugin.Config.Name, theCataloger)
}
result = append(result, theCataloger)
}
return result, deactivateFn, nil
}
func Discover(dir string, pluginTypes ...Type) ([]Plugin, error) {
var err error
if len(pluginTypes) == 0 {
pluginTypes = AllTypes
}
if !filepath.IsAbs(dir) {
dir, err = filepath.Abs(dir)
if err != nil {
return nil, err
}
}
var plugins []Plugin
for _, pluginType := range pluginTypes {
pluginPrefix := pluginType.String()
pluginGlob := fmt.Sprintf("%s-*", pluginPrefix)
paths, err := filepath.Glob(filepath.Join(dir, pluginGlob))
if err != nil {
return nil, err
}
for _, path := range paths {
// TODO: should we use a config for some of this?
plugins = append(plugins, NewPlugin(Config{
// TODO: should the name be org/name instead of just name? this implies changing the dir storage too
Name: strings.TrimPrefix(filepath.Base(path), pluginPrefix+"-"),
Type: pluginType,
Command: path,
Args: nil, // TODO
Env: nil, // TODO
}))
}
}
return plugins, nil
}
-49
View File
@@ -1,49 +0,0 @@
package plugin
import (
"fmt"
"github.com/hashicorp/go-plugin"
)
var AllTypes = []Type{
TypeCataloger,
}
type Type uint64
const (
TypeUnknown Type = iota
TypeCataloger
)
func (p Type) String() string {
switch p {
case TypeCataloger:
return "cataloger"
default:
return "unknown"
}
}
func (p Type) HandshakeConfig() plugin.HandshakeConfig {
switch p {
case TypeCataloger:
return plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "SYFT_PLUGIN_MAGIC_COOKIE",
MagicCookieValue: "0f86cc7f-6f97-410e-a844-087cd12e36e3",
}
default:
panic(fmt.Errorf("plugin type unsupported"))
}
}
func ParseType(pluginType string) Type {
switch pluginType {
case "cataloger":
return TypeCataloger
default:
return TypeUnknown
}
}
+1 -4
View File
@@ -34,9 +34,7 @@ func NewPresenter(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) *Present
func (pres *Presenter) Present(output io.Writer) error {
bom := NewDocumentFromCatalog(pres.catalog, pres.distro)
srcObj := pres.scope.Source()
switch src := srcObj.(type) {
switch src := pres.scope.Source.(type) {
case scope.DirSource:
bom.BomDescriptor.Component = &BdComponent{
Component: Component{
@@ -54,7 +52,6 @@ func (pres *Presenter) Present(output io.Writer) error {
} else {
imageID = src.Img.Metadata.Digest
}
src.Img.Metadata.Tags[0].TagStr()
bom.BomDescriptor.Component = &BdComponent{
Component: Component{
Type: "container",
+2 -1
View File
@@ -3,6 +3,7 @@ package cyclonedx
import (
"bytes"
"flag"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/syft/distro"
"regexp"
"testing"
@@ -96,7 +97,7 @@ func TestCycloneDxImgsPresenter(t *testing.T) {
var buffer bytes.Buffer
catalog := pkg.NewCatalog()
img, cleanup := testutils.GetFixtureImage(t, "docker-archive", "image-simple")
img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", "image-simple")
defer cleanup()
// populate catalog with test data
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:b3b1786a-2de3-4501-b902-58b701b8ad0e">
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:2bbada20-3e87-44ea-9a56-1aa0e4dd01a0">
<components>
<component type="library">
<name>package1</name>
@@ -21,7 +21,7 @@
</component>
</components>
<bd:metadata>
<bd:timestamp>2020-08-30T21:50:50-04:00</bd:timestamp>
<bd:timestamp>2020-09-23T18:26:58-04:00</bd:timestamp>
<bd:tool>
<bd:vendor>anchore</bd:vendor>
<bd:name>syft</bd:name>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:ab970429-e6a2-44a1-810c-f9ed2b7c3147">
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:94dae829-4d5d-482f-afab-27f43f919e2c">
<components>
<component type="library">
<name>package1</name>
@@ -21,14 +21,14 @@
</component>
</components>
<bd:metadata>
<bd:timestamp>2020-08-30T21:50:50-04:00</bd:timestamp>
<bd:timestamp>2020-09-23T18:26:58-04:00</bd:timestamp>
<bd:tool>
<bd:vendor>anchore</bd:vendor>
<bd:name>syft</bd:name>
<bd:version>[not provided]</bd:version>
</bd:tool>
<bd:component type="container">
<name>index.docker.io/library/anchore-fixture-image-simple</name>
<name>index.docker.io/library/stereoscope-fixture-image-simple</name>
<version>04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7</version>
</bd:component>
</bd:metadata>
+1 -1
View File
@@ -9,7 +9,7 @@ type Artifact struct {
Name string `json:"name"`
Version string `json:"version"`
Type string `json:"type"`
FoundBy []string `json:"found-by"`
FoundBy []string `json:"foundBy"`
Locations Locations `json:"locations,omitempty"`
Metadata interface{} `json:"metadata,omitempty"`
}
+5 -12
View File
@@ -1,16 +1,13 @@
package json
import (
"fmt"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
type Document struct {
Artifacts []Artifact `json:"artifacts"`
Image *Image `json:"image,omitempty"`
Directory *string `json:"directory,omitempty"`
Source Source `json:"source"`
}
func NewDocument(catalog *pkg.Catalog, s scope.Scope) (Document, error) {
@@ -18,15 +15,11 @@ func NewDocument(catalog *pkg.Catalog, s scope.Scope) (Document, error) {
Artifacts: make([]Artifact, 0),
}
srcObj := s.Source()
switch src := srcObj.(type) {
case scope.ImageSource:
doc.Image = NewImage(src)
case scope.DirSource:
doc.Directory = &s.DirSrc.Path
default:
return Document{}, fmt.Errorf("unsupported source: %T", src)
src, err := NewSource(s)
if err != nil {
return Document{}, nil
}
doc.Source = src
for _, p := range catalog.Sorted() {
art, err := NewArtifact(p, s)
+2 -2
View File
@@ -6,12 +6,12 @@ type Image struct {
Layers []Layer `json:"layers"`
Size int64 `json:"size"`
Digest string `json:"digest"`
MediaType string `json:"media-type"`
MediaType string `json:"mediaType"`
Tags []string `json:"tags"`
}
type Layer struct {
MediaType string `json:"media-type"`
MediaType string `json:"mediaType"`
Digest string `json:"digest"`
Size int64 `json:"size"`
}
+2 -3
View File
@@ -11,12 +11,11 @@ type Locations interface{}
type ImageLocation struct {
Path string `json:"path"`
LayerIndex uint `json:"layer-index"`
LayerIndex uint `json:"layerIndex"`
}
func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) {
srcObj := s.Source()
switch src := srcObj.(type) {
switch src := s.Source.(type) {
case scope.ImageSource:
locations := make([]ImageLocation, len(p.Source))
for idx := range p.Source {
+3 -2
View File
@@ -7,6 +7,7 @@ import (
"github.com/anchore/go-testutils"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
"github.com/sergi/go-diff/diffmatchpatch"
@@ -72,11 +73,11 @@ func TestJsonImgsPresenter(t *testing.T) {
testImage := "image-simple"
if *update {
testutils.UpdateGoldenFixtureImage(t, testImage)
imagetest.UpdateGoldenFixtureImage(t, testImage)
}
catalog := pkg.NewCatalog()
img := testutils.GetGoldenFixtureImage(t, testImage)
img := imagetest.GetGoldenFixtureImage(t, testImage)
// populate catalog with test data
catalog.Add(pkg.Package{
+29
View File
@@ -0,0 +1,29 @@
package json
import (
"fmt"
"github.com/anchore/syft/syft/scope"
)
type Source struct {
Type string `json:"type"`
Target interface{} `json:"target"`
}
func NewSource(s scope.Scope) (Source, error) {
switch src := s.Source.(type) {
case scope.ImageSource:
return Source{
Type: "image",
Target: NewImage(src),
}, nil
case scope.DirSource:
return Source{
Type: "directory",
Target: src.Path,
}, nil
default:
return Source{}, fmt.Errorf("unsupported source: %T", src)
}
}
@@ -4,7 +4,7 @@
"name": "package-1",
"version": "1.0.1",
"type": "deb",
"found-by": [
"foundBy": [
"the-cataloger-1"
],
"locations": [
@@ -15,7 +15,7 @@
"name": "package-2",
"version": "2.0.1",
"type": "deb",
"found-by": [
"foundBy": [
"the-cataloger-2"
],
"locations": [
@@ -23,5 +23,8 @@
]
}
],
"directory": "/some/path"
"source": {
"type": "directory",
"target": "/some/path"
}
}
@@ -4,13 +4,13 @@
"name": "package-1",
"version": "1.0.1",
"type": "deb",
"found-by": [
"foundBy": [
"the-cataloger-1"
],
"locations": [
{
"path": "/somefile-1.txt",
"layer-index": 0
"layerIndex": 0
}
]
},
@@ -18,40 +18,43 @@
"name": "package-2",
"version": "2.0.1",
"type": "deb",
"found-by": [
"foundBy": [
"the-cataloger-2"
],
"locations": [
{
"path": "/somefile-2.txt",
"layer-index": 1
"layerIndex": 1
}
]
}
],
"image": {
"layers": [
{
"media-type": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:056c0789fa9ad629ceae6d09713fb035f84115af3c4a88a43aa60f13bc683053",
"size": 22
},
{
"media-type": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:b461c48116592c570a66fed71d5b09662a8172e168b7938cf317af47872cdc9b",
"size": 16
},
{
"media-type": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:00b80053e05c01da485015610d288ce3185fac00d251e2ada02b45a7a7c5f589",
"size": 27
}
],
"size": 65,
"digest": "sha256:3c53d2d891940f8d8e95acb77b58752f54dc5de9d91d19dd90ced2db76256cea",
"media-type": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"anchore-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"
]
"source": {
"type": "image",
"target": {
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:e158b57d6f5a96ef5fd22f2fe76c70b5ba6ff5b2619f9d83125b2aad0492ac7b",
"size": 22
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:da21056e7bf4308ecea0c0836848a7fe92f38fdcf35bc09ee6d98e7ab7beeebf",
"size": 16
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:f0e18aa6032c24659a9c741fc36ca56f589782ea132061ccf6f52b952403da94",
"size": 27
}
],
"size": 65,
"digest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"stereoscope-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"
]
}
}
}

Some files were not shown because too many files have changed in this diff Show More