Sinker is a Python tool to automate:
- execution of dockerized container scanning tools;
- merge of multiple reports;
- post-execution tasks.
- Running these tools manually can be boring and time-consuming when there are multiple images and deployments
- Parsing their reports manually to get a unified view of the findings is challenging
- Which container scanning tool (if only one) should you select for a given project?
- Users looking to automate container scanning tools and post-execution tasks
- Users looking to benchmark container scanning tools
- Linux or mac OS (tested with mac OS 10.15.7)
- Python 3 (tested with version 3.10.4)
- Docker Engine (tested with version 20.10.12)
- Docker module for Python (tested with version 5.0.3)
To install Python pre-requisites:
$ pip install -r requirements.txt
The tools automated here don't have the same features. To learn more, check their websites.
- Syft v0.40.1
- Grype v0.34.7
- Trivy v0.25.3
- Snyk v1.883.0 (standalone)
- Clone this repo (
git clone https://github.com/mmartins000/sinker.git
) - Run
python3 core/sinker.py -h
to learn the options - Compare the results of the tools.
To build and run a Docker image, check the last section of this document.
Sinker will:
- Download Docker images for the scanners and the targets
- Run scanners against targets
- Parse the reports generated by the scanners
- Write a JSON file with the execution summary and findings
- Run enabled integrations (if any)
- Clean up (depending on selected options)
Check and edit the default config file sinker.json
or create your own.
You can replace file targets.json
for a file that contains a list images to assess.
Example of file targets.json
(to change to another filename, change the configuration in sinker.json
):
{
"images": [
"alpine:3.12.1"
]
}
Another example, with a list of images:
{
"images": [
"alpine:3.12.1",
"alpine:3.15",
"postgres:14.2",
"python"
]
}
The flag --targets
can also be used. Options are:
- A JSON file, like
targets.json
- A directory, with YAML files (to assess images in Kubernetes or docker-compose files)
- A single YAML file
Sinker can create Jira issues for the findings. If it finds a vulnerability in a public image, it will only create on issue for the whole image, because it assumes you don't own it and won't fix the vulnerabilities yourself. If it finds vulnerabilities for a private image, it will create one issue for each vulnerability. Filters will determine the findings in the report and sent to integrated tools. Check the last section for details.
If you plan to use Snyk or Jira, don't forget to configure the credentials.
- For Snyk, SNYK_TOKEN value in your account.
- For Jira, username/password, token or cookie.
Help menu:
$ python3 sinker/core.py -h
usage: core.py [-h] [--version] [--config CONFIG] [-v] [-i] [--skip-syft] [-g] [-t] [-s] [--force-docker-pull FORCEDOCKERPULL] [-o OUTPUT] [--targets TARGETS]
[--no-update] [--only-updates] [--fresh-start] [--only-cleanup] [--only-process-json PROCESSJSON]
[--fail-on-critical | --fail-on-high | --fail-on-medium | --fail-on-low]
options:
-h, --help show this help message and exit
--version Print current version and exit
--config <filename> Config file
-v, --verbose Verbose mode
-i, --ignore-root Ignore being executed as root
--skip-syft Skip Syft execution
-g, --skip-grype Skip Grype execution
-t, --skip-trivy Skip Trivy execution
-s, --skip-snyk Skip Snyk execution
--force-docker-pull [ always | missing | never ]
Configure when Docker should pull the image. Default: missing
-o <OUTPUT_FOLDER>, --output <OUTPUT_FOLDER>
Override output_folder parameter in config file
--targets TARGETS Override targets file parameter in config file
--no-update Do not update vulnerability DBs
--only-updates Update vulnerability DBs and exit
--fresh-start Erase images and vulnerability DBs
--only-cleanup Execute a clean up and exit
--only-process-json <Sinker JSON filename>
Skip scanners and process a previously generated JSON
--fail-on-critical Exit with failed signal if critical severity vulnerabilities are found
--fail-on-high Exit with failed signal if high severity vulnerabilities are found
--fail-on-medium Exit with failed signal if medium severity vulnerabilities are found
--fail-on-low Exit with failed signal if low severity vulnerabilities are found
Sample execution with image 'alpine:3.12.1' and Grype, Trivy and Snyk:
$ python3 ./sinker/core.py -v --config /tmp/sinker/sinker.json --targets alpine:3.12.1
Sinker v0.1.1
Summary: Run Grype, Trivy, Snyk in alpine:3.12.1
Docker image alpine:3.12.1 found and will not be downloaded.
Docker image anchore/syft:latest found and will not be downloaded.
Docker image anchore/grype:latest found and will not be downloaded.
Docker image snyk/snyk:docker found and will not be downloaded.
Docker image aquasec/trivy:latest found and will not be downloaded.
Updating vulnerability database for Trivy...
Grype vulnerability database is already up-to-date.
Starting container image scans.
Scanning target alpine:3.12.1.
Running Syft on alpine:3.12.1...
Docker ran Syft container in alpine:3.12.1, it took 1.6 seconds and the report was saved in /tmp/sinker/alpine_3.12.1-sbom_syft_spdx.json.
Running Snyk on alpine:3.12.1...
Docker ran Snyk container in alpine:3.12.1, it took 6.48 seconds and the report was saved in /tmp/sinker/alpine_3.12.1-results_snyk.json.
Running Grype on alpine:3.12.1...
Docker ran Grype container in alpine:3.12.1, it took 3.56 seconds and the report was saved in /tmp/sinker/alpine_3.12.1-results_grype.json.
Running Trivy on alpine:3.12.1...
Docker ran Trivy container in alpine:3.12.1, it took 1.21 seconds and the report was saved in /tmp/sinker/alpine_3.12.1-results_trivy.json.
Finished scanning container image alpine:3.12.1.
Finished scanning all targets.
Sinker JSON saved to /tmp/sinker/sinker_results.json
Done.
Sample JSON output of command $ python3 ./sinker/core.py -v --config /tmp/sinker/sinker.json --targets alpine:3.12.1
:
{
"sinker": {
"script_version": "0.1.1",
"json_schema_version": "1",
"url": "https://github.com/mmartins000/sinker",
"command_line_args": null,
"docker_version": null,
"start_time": "2022-04-08 03:22:49",
"end_time": "2022-04-08 03:23:27",
"scanners_count": 3,
"targets_count": 1,
"duration_seconds": 38,
"extended_duration": "38 seconds"
},
"reporting": {
"alpine:3.12.1": {
"summary": {
"findings": 49,
"findings_by_scanner": {
"grype": 49,
"trivy": 47,
"snyk": 47
},
"findings_by_severity": {
"critical": 3,
"high": 30,
"medium": 12,
"low": 4,
"negligible": 0,
"unknown": 0
},
"unanimous_findings": 47,
"disputed_findings": 2
},
(...)
Sample table output of command $ python3 ./sinker/core.py -v --config /tmp/sinker/sinker.json --targets alpine:3.12.1
:
+---------------+--------------+-----------+----------------+----------+------------+---------------+
| Target Image | Artifact | Upstream | ID | Severity | Version | Fixed Version |
+---------------+--------------+-----------+----------------+----------+------------+---------------+
| alpine:3.12.1 | apk-tools | apk-tools | CVE-2021-36159 | critical | 2.10.5-r1 | 2.10.7-r0 |
| alpine:3.12.1 | libcrypto1.1 | openssl | CVE-2021-3711 | critical | 1.1.1g-r0 | 1.1.1l-r0 |
| alpine:3.12.1 | libssl1.1 | openssl | CVE-2021-3711 | critical | 1.1.1g-r0 | 1.1.1l-r0 |
| alpine:3.12.1 | apk-tools | apk-tools | CVE-2021-30139 | high | 2.10.5-r1 | 2.10.6-r0 |
(...)
The results can be filtered using a JSON filter file, specified in the JSON config file. This filter file is a list of dict objects. The following filter will remove from the results all findings that match severity == "high" and below:
[
{
"severity": "high"
}
]
The following filter contains two entries: one just like the above, and the next will remove all findings that match severity == "medium" and below AND "artifact" == "apk-tools"
[
{
"severity": "high"
},
{
"severity": "medium",
"artifact": "apk-tools",
},
]
In the example above, if a finding matches one of the filter entries (OR condition) it will be removed from the report (JSON and text table, and later on, from the integrations).
The available fields are:
"id"
"description"
"artifact"
"upstream"
"severity"
"installed_version"
"fixed_version"
"cvss_v2score"
"cvss_v2vector"
"cvss_v3score"
"cvss_v3vector"
"datasource"
"datasource_nvd"
"datasource_mitre"
"found_by"
Sinker can be executed from a Docker container.
To build the image on your computer:
$ docker image build --rm --tag 'sinker:latest' .
To run it using the same example as shown in previous sections:
$ docker run --rm -v '/var/run/docker.sock:/var/run/docker.sock' -v '/tmp:/tmp' sinker:latest --ignore-root -v --config /tmp/sinker/sinker.json --targets alpine:3.12.1