Given the wide range of platforms, languages and build systems used by FINOS projects, finding one solution that secures a codebase is not an easy task, especially considering the incredible amount of libraries available in public library repositories, which can be easily used, embedded, integrated and re-published; this proliferation of artifacts have dramatically influenced software development:
Let's first recap requirements, based on the considerations made above; a code scan should be:
The proactive/reactive approach is crucial to enforce security, as it guarantees - granted that changes are always submitted via Pull Requests - that the code will always be free of CVEs (or at most for less than a day):
Based on the requirements discussed above, we tried to consolidate a list of technical specifications:
It's worth emphasizing the importance of having a mechanism for ignoring warnings and errors, which may well be false positives. Without it, developers will eventually disable any security tool. And it’s important to store it in the codebase so that changes can be easily done by developers using the Git collaboration workflow.
In this codebase you'll find a folder for each of the build platforms listed below. Each folder includes a Hello World
project, with a build descriptor that:
In the .github/workflows
folder you'll find a GitHub Action for each of these projects, that you can simply copy/paste into your GitHub repository and edit to align to your project layout:
cve-scanning.yml
(and semgrep.yml
), which allows FINOS to monitor which projects are/aren't adopting the tool; the file name is also reflected in the on: / push: / path:
section of the actionworking-directory
configurations.github/workflows
) into your project; make sure to call them cve-scanning.yml
, so that FINOS monitoring tools can find easily find it.Actions
tab, you can select the CVE Scanning
action and Create status badge
, which will allow you to copy Markdown code for your README.md
file that shows a badge with the result of the last action run; this is quite useful for consumers to see that code is scanned and that no CVEs were spotted in the main codebase branch.The OWASP Dependency Check (or simply OWASP DC) is a code scanning tool that supports multiple languages, some of which are listed below; it is widely adopted and makes life easier, especially for multi-language projects, as it provides a standard way to define scanning configurations.
It also provides Docker images and GitHub Actions that are nightly built, including the latest and greatest CVE dictionaries that are used to scan project dependencies, allowing the scanning process to be self-contained yet fast (and less error-prone due to usage quota or connectivity issues).
We have used the OWASP Dependency Check Action to run the scanning continuosly, across the following build platforms:
In these examples, the OWASP DC Action is also responsible to upload a report as build artifact, which you can access from the Github's Actions tab.
You can suppress false positives by creating an allow-list.xml file and adding CVEs. For more information, refer to the OWASP Dependency Check documentation.
You can exclude dependencies from the scan by adding --exclude
in the args of your action see the OWASP Dependency Check documentation. There are various command line arguments available for your specific needs.
The NodeJS sample project uses AuditJS, a library built by Sonatype which provides a very good alternative to npm audit
; you can read more about their comparison on https://blog.sonatype.com/compare-npm-audit-versus-auditjs .
The project descriptor pulls the chokidar 2.0.3
dependency, which contains some CVEs that are ignored into the list of ignored errors.
To run AuditJS
locally:
package.json
filerm -rf node_modules
npm ci --prod
; if using yarn, the command should be yarn install --production --frozen-lockfile
npx --yes auditjs ossi
--whitelist allow-list.json
to the command on step 4pkg:npm/is_js@0.9.0 - 1 vulnerability found!
The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning.yml
; make sure to adapt the code to your project layout.
The Python sample project uses the safety
library, which checks requirements.txt
entries against the NVD database.
The python sample project defines a dependency on insecure-package
, which pulls a CVE that is ignored in the safety-policy.yml
, in order to demo how to manage false positives.
If you're using Poetry, you can simply export your libaries into a requirements.txt
file and then follow the steps above, using:
poetry install
poetry export --without-hashes -f requirements.txt --output requirements.txt
To run Safety
locally:
requirements.txt
filepython --version
, otherwise the version of safety
that you're able to use would be quite outdatedpython -m venv .
./bin/pip install safety
- we need to run this step since the scanning will run through all libraries available in the current Python environment./bin/safety check --full-report -r requirements.txt
--policy-file safety-policy.yml
to the command on step 4Vulnerability found in insecure-package version 0.1.0
The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning-python.yml
; make sure to adapt the code to your project layout.
The Maven sample project uses the OWASP Dependency Check plugin for Maven to scan runtime dependencies for known vulnerabilities.
To run the Maven Dependency Check Plugin
locally:
pom.xml
file (it supports multi-module builds)mvn org.owasp:dependency-check-maven:check -DfailBuildOnCVSS=7
-DsuppressionFile="allow-list.xml"
to the command on step 2The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning.yml
; make sure to adapt the code to your project layout.
The Gradle sample project uses the OWASP Dependency Check plugin for Gradle. Sadly, Gradle doesn't allow to invoke plugins without altering the build manifest, namely build.gradle
; follow instructions below to know how to add code scanning in your project.
To run the Gradle Dependency Check Plugin
locally:
build.gradle
file<suppress>
itemsdependencyCheck
setup from build.gradle file into your build.gradle
file./gradlew dependencyCheckAnalyze
The build.gradle
file defines a (commented) dependency on struts2
version 2.3.8, which contains the CVE that led to the (famous) equifax hack. By uncommenting it, the build is expected to fail, assuming that CVEs are not suppressed by the allow-list.xml
file, used to manage false positives.
The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning.yml
; make sure to adapt the code to your project layout.
The Scala sample project uses the OWASP Dependency Check plugin for SBT to scan runtime dependencies for known vulnerabilities.
To run the Scala Dependency Check Plugin
locally:
dependencyCheckFailBuildOnCVSS
and dependencyCheckSuppressionFiles
configurations from build.sbt file in your projectsbt-dependency-check
plugin definition from plugins.sbt into your projectsbt dependencyCheck
The build.sbt
file defines a (commented) dependency on struts2
version 2.3.8, which contains the CVE that led to the (famous) equifax hack. By uncommenting it, the build is expected to fail, assuming that CVEs are not suppressed by the allow-list.xml
file, used to manage false positives.
If you want to test dependencyCheck
:
build.sbt
dependencyCheckSuppressionFiles ++= List(file("../allow-list.xml")),
sbt dependencyCheck
The build should fail and show all CVEs pulled by struts2.The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning.yml
; make sure to adapt the code to your project layout.
To keep your library dependencies, sbt plugins, and Scala and sbt versions up-to-date, checkout Scala Steward.
The Rust sample project uses Cargo audit to scan runtime dependencies for known vulnerabilities.
To run Cargo Audit
locally:
cargo install --force cargo-audit
cargo audit
--ignore RUSTSEC-2020-0071
to the command on step 3The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning.yml
; make sure to adapt the code to your project layout.
For more information about Cargo audit configuration, visit https://docs.rs/cargo-audit/0.17.0/cargo_audit/config/index.html
The .NET sample project uses the dotnet CLI to scan runtime dependencies for known vulnerabilities.
To run dotnet
locally:
.csproj
file is defineddotnet build
dotnet list package --vulnerable --include-transitive
Newtonsoft.Json 12.0.3 12.0.3 High https://github.com/advisories/GHSA-5crp-9r3c-p9vr
The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning.yml
; make sure to adapt the code to your project layout.
Unfortunately there is no way yet to ignore warnings and errors for dotnet
, although it may be possible to add some bash logic into the GitHub Action to achieve it.
Docker scanning can be very useful to check if downstream Docker images are affected by vulnerabilities; it also scans for OS components and provides a solution for projects using C and C++ code.
There are many CLI tools that perform a docker image scanning; the easiest one is docker scan, as you'll probably have the docker
command installed in your local environment, assuming you're already working with Docker.
For GitHub Actions, we are using [trivy][trivy.dev], wrapped into this GitHub Action.
To run locally, follow instructions on how to install trivy locally, then run:
docker build -f Dockerfile -t user/image-name:latest .
trivy image user/image-name:latest
The GitHub action can be copied from here into your repo under .github/workflows/cve-scanning.yml
; make sure to adapt the code to your project layout.
Unfortunately there is no way yet to ignore warnings and errors, although it may be possible to add some bash logic into the GitHub Action to achieve it.
If your project is built using other languages or build platforms, checkout the list of analyzers offered by the OWASP Dependency Check plugin.
There is also a GitHub Dependency Check Action that uses a nightly build of the CVE database, along with the Dependency check plugin.
Keeping dependency versions up to date is a hard task, given the big amount of downstream libraries used by today's software projects and their frequent release cadence; adopting a tool to automate it can drastically save time for developers.
Github ships with Dependabot, which can be easily enabled on every repository, but we found Renovate to be easier and more powerful to use, you can read this comparison article, if you're interested.
Assuming that Renovate is running on your project, the CVE scanning tools mentioned above would generate way less alerts, making it easier to manage project's security; as a result, we strongly advise to enable Renovate first, then add CVE scanning tools.
In order to enable Renovate:
Renovate will create a GitHub issue (titled Renovate Dashboard
) with the recap of the actions that it will take and a one Pull Request for each depdendency version update; please note that:
Renovate Dashboard
issue.Note that Renovate can be configured to group multiple updates together, using the groupName
feature, which can save a lot of developers time, expecially on large codebases.
To identify bugs in the hosted source code, that is, code that is written and hosted in your own repository, there are several tools out there; the one that proved to work well for us is https://semgrep.dev , and we designed a GitHub Action in .github/workflows/semgrep.yml
that continuously scans the code upon every change.
Semgrep supports a long list of programming languages and defines a rich list of rulesets that tests the code against.
It also provides ways to ignore false positives by:
// nosemgrep
(or # nosemgrep
) comment on top of the code block that causes the error.semgrepignore
file with a list of file names that should be ignored during the scanIn order to use it, you need to
SEMGREP_APP_TOKEN
, with the token earlier created as value. If you want to enable scanning on a FINOS hosted repository, please email help@finos.org and they will take care of setting the SEMGREP_APP_TOKEN
secret on the GitHub repository and enabling the Semgrep GitHub app.semgrep scan --error --config auto
In order to test it locally, make sure to:
Settings
menu optionexport SEMGREP_APP_TOKEN=<your personal semgrep token>
- to aggregate results into FINOS (private) dashboardsemgrep scan --error --config auto
from the root folder, here the docs to install semgrep locallyTo enforce compliance of open source projects, it is crucial to validate that inbound libraries adopt a license that is "compatible" with the outbound one in terms of rights and obligations; for FINOS, the outbound license used is the Apache License v2.0.
There are hundreds of different open source licenses, some of which have conflicting clauses (you can learn more on tldrlegal.com), and it's sometimes hard to understand the consequences of adopting a library with a different license than the outbound one, especially without having some legal background or knowledge.
For this reason, we are working on automated tasks to continuously scan licenses being pulled within FINOS projects; such tools should be able to either:
Right now, we have managed to automate license scanning and reporting on:
allowed-licenses.json
file allows to ignore specific libraries/licenses, whereas license-normalizer-bundle.json
allows to define license aliases, such as Apache License Version 2.0
and Apache License, Version 2.0
. Note that the build.gradle
needs to be changed to make use of the Gradle-License-Report pluginFor more info about compliance requirements at FINOS, checkout our Contribution Compliance Requirements and License Categories pages.
For any bug, question or enhancement request, please create a GitHub Issue
git checkout -b feature/fooBar
)git commit -am 'Add some fooBar'
)git push origin feature/fooBar
)NOTE: Commits and pull requests to FINOS repositories will only be accepted from those contributors with an active, executed Individual Contributor License Agreement (ICLA) with FINOS OR who are covered under an existing and active Corporate Contribution License Agreement (CCLA) executed with FINOS. Commits from individuals not covered under an ICLA or CCLA will be flagged and blocked by the FINOS Clabot tool (or EasyCLA). Please note that some CCLAs require individuals/employees to be explicitly named on the CCLA.
Need an ICLA? Unsure if you are covered under an existing CCLA? Email help@finos.org
Copyright 2022 FINOS
Distributed under the Apache License, Version 2.0.
SPDX-License-Identifier: Apache-2.0