Clojure's software composition analysis (SCA).
clj-watson scans dependencies in a clojure deps.edn
seeking for vulnerable direct/transitive dependencies and build a report with all the information needed to help you understand how the vulnerability manifest in your software.
Note: this is a fork of clj-holmes/clj-watson that has been updated to use DependencyCheck 9.0.x and the new NIST NVD API. Unlike the upstream origin, the main branch is not protected and the commits in this fork have not been signed -- so use this fork at your own discretion. This fork exists so that I can continue to use
clj-watson
if NIST cut off access to the old data feeds that the upstream origin depends on, and it has not yet been updated to use the new NIST NVD API.
- v5.x.x -- uses DependencyCheck 9.0.x and the new NIST NVD API.
- v4.x.x -- uses an earlier version of DependencyCheck and the old NVD data feeds, which have been deprecated.
clj-watson can be added as an alias on a per-project basis in the project deps.edn
file:
;; in :aliases
:clj-watson {:replace-deps {io.github.seancorfield/clj-watson {:git/tag "v5.0.0" :git/sha "acffdad"}}
:main-opts ["-m" "clj-watson.cli" "scan"]}
Then run it with:
clojure -M:clj-watson -p deps.edn
The first time it runs, it will download the vulnerability database, which can take a few minutes. Subsequent runs will be much faster.
It can also be installed as a Clojure CLI tool:
clojure -Ttools install-latest :lib io.github.seancorfield/clj-watson :as clj-watson
Then run it with:
clojure -Tclj-watson scan :deps-edn-path '"deps.edn"' :output '"stdout"'
#or:
clojure -Tclj-watson scan '{:deps-edn-path "deps.edn" :output "stdout"}'
(this is somewhat verbose now but it will be improved over the next few releases)
clj-watson supports two methods for vulnerabilities scan.
dependency-check is the most used method around the clojure/java sca tools, it downloads all vulnerabilities from nvd and stores it in a database (located in /tmp/db
), compose a cpe based on the dependencies, scans all jars in the classpath and matches vulnerabilities using it.
As of version v5.0.0, clj-watson
uses
DependencyCheck
9.0.x
which switches from the earlier NVD data feeds to the new NIST NVD API.
This new API heavily throttles anonymous requests, so it is highly recommended to get an API key in order to use the API efficiently.
Read the NIST NVD announcement for more information.
Once you have an API key, you can provide it to clj-watson
via the nvd.api.key
property in the optional clj-watson.properties
file, either on the classpath
you use to run clj-watson
or via the -w
/ --clj-watson-properties
command-line option:
# clj-watson.properties file
nvd.api.key=...your key here...
It doesn't need to download a database since it uses the github advisory database via the graphql api, matches are made via package name.
But there's a requirements to use it, it's necessary to generate a Github PAT (Personal Access Token) to access graphql api or if you use Github actions it's possible to use their Github token.
Another important thing is that the api has a limit of 5K requests per hour/per PAT.
If you create a PAT or uses the github action token just set it in as an environment variabe named GITHUB_TOKEN
to clj-watson be able to use it.
Sometimes the dependency tree is not under your control and overrides are not possible,
but you can allways allow a CVE for a limited period by adding a config file at resources/clj-watson-config.edn
:
{:allow-list {:cves [{:cve-label "CVE-0000"
:expires "2000-01-01"}
{:cve-label "CVE-00000"
:expires "2000-01-01"}]}}
Since fixing the found vulnerabilities manually could be truly frustrating clj-watson
provides a way to suggest a remediation.
It basically lookups the whole dependency tree finding if the latest version of a parent dependency uses the secure version of the child dependency until it reaches the direct dependency.
Given the following dependency tree,
[dependency-a "v1"]
[dependency-b "v1"]
[dependency-c "v1"]
where the dependency-c
is vulnerable and to fix it is necessary to bump from v1
to v2
clj-watson will try to find a version of dependency-a
that uses dependency-b
in a version that uses dependency-c
on version v2
and then propose a bump to dependency-a
.
{dependency-a {:mvn/version "v4"}}
If clj-watson does not find a version of dependency-b or dependency-a that satisfies this statement it'll propose an exclusion.
{dependency-a {:exclusions [dependency-b]}
dependency-b {:mvn/version "v3"}}
In order to get the auto remediate suggestion it's necessary to provide a --suggest-fix|-s
on the clj-watson execution.
It's possible to install clj-watson as a clojure tool and invoke it.
$ clojure -Ttools install-latest :lib io.github.seancorfield/clj-watson :as clj-watson
$ clojure -Tclj-watson scan '{:output "stdout" :fail-on-result true :deps-edn-path "deps.edn" :suggest-fix true :aliases ["*"] :database-strategy "dependency-check"}'
It can also be called directly.
$ clojure -Sdeps '{:deps {io.github.seancorfield/clj-watson {:git/tag "v5.0.0" :git/sha "acffdad"}}}' -M -m clj-watson.cli scan -p deps.edn
Or you can just add it to your project deps.edn
{:deps {}
:aliases
{:clj-watson {:extra-deps {io.github.seancorfield/clj-watson {:git/tag "v5.0.0" :git/sha "acffdad"}}
:main-opts ["-m" "clj-watson.cli" "scan"]}}}
$ clojure -M:clj-watson scan -\?
NAME:
clj-watson scan - Performs a scan on a deps.edn file
USAGE:
clj-watson scan [command options] [arguments...]
OPTIONS:
-p, --deps-edn-path S* path of deps.edn to scan.
-o, --output edn|json|stdout|stdout-simple|sarif report Output type.
-a, --aliases S Specify a alias that will have the dependencies analysed alongside with the project deps.It's possible to provide multiple aliases. If a * is provided all the aliases are going to be analysed.
-d, --dependency-check-properties S [ONLY APPLIED IF USING DEPENDENCY-CHECK STRATEGY] Path of a dependency-check properties file. If not provided uses resources/dependency-check.properties.
-w, --clj-watson-properties S [ONLY APPLIED IF USING DEPENDENCY-CHECK STRATEGY] Path of an additional, optional properties file.
-t, --database-strategy dependency-check|github-advisory dependency-check Vulnerability database strategy.
-s, --[no-]suggest-fix Suggest a new deps.edn file fixing all vulnerabilities found.
-f, --[no-]fail-on-result Enable or disable fail if results were found (useful for CI/CD).
-?, --help
By default, when using the DEPENDENCY-CHECK strategy, clj-watson will load
its own dependency-check.properties
file, and then look for a
clj-watson.properties
file on the classpath and load that if found, for
additional properties to apply to the dependency-check scan.
If you provide -d
(or --dependency-check-properties
) then clj-watson will
load that file instead of its own dependency-check.properties
file so it
needs to be a complete properties file, not just the properties you want to
override.
If you provide -w
(or --clj-watson-properties
) then clj-watson will load
that file and apply those properties to the dependency-check scan. This is
in addition to the properties loaded from the dependency-check.properties
or the -d
file. This can be useful to override just a few properties.
The minimum necessary to execute clj-watson is to provide the path to a deps.edn
file, but it's recommended that you all provide the -s
option so clj-watson
will try to provide a remediation suggestion to the vulnerabilities.
$ clojure -M:clj-watson -p deps.edn
Downloading/Updating database.
Download/Update completed.
Dependency Information
-----------------------------------------------------
NAME: dependency-e
VERSION: 1
DEPENDENCY FOUND IN:
[dependency-a]
[dependency-b]
[dependency-a]
[dependency-c]
[dependency-d]
FIX SUGGESTION: {dependency-a {:mvn/version "3"}}
Vulnerabilities
-----------------------------------------------------
CVE: CVE-2022-1000000
CVSSV3: 7.5
CVSSV2: 5.0
SUGGESTED BUMP: 1.55
CVE: CVE-2022-2000000
CVSSV3: 5.3
CVSSV2: 5.0
SUGGESTED BUMP: 1.55
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
clj -M:nREPL -m nrepl.cmdline
clj -M:clojure-lsp format
clj -M:clojure-lsp clean-ns