Skip to content

Commit

Permalink
Merge pull request #2150 from GaloisInc/tullsen/add-cve-checks
Browse files Browse the repository at this point in the history
Add CVE checks for Haskell, Rust, and Python packages
  • Loading branch information
mtullsen authored Nov 28, 2024
2 parents e0f4079 + 6ac2047 commit 69a2dd3
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
src/SAWScript/Lexer.hs
src/SAWScript/Parser.hs
bin
/bin
cabal.sandbox.config
cabal.project.local
dist
Expand Down
1 change: 1 addition & 0 deletions cve-reports/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cabal-audit-src/
121 changes: 121 additions & 0 deletions cve-reports/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# CVE Checks
## Background.

Here we generate reports for listing any used packages that are
subject to CVEs. We do this for three languages: Haskell, Rust, and Python.

For background, approach, and useful boilerplate, refer to
https://github.com/GaloisInc/hello-cve-scans.git

## Prerequisites

First, build the software of interest:
- sawscript itself
- doc/rust-tutorial/code/salsa20 (a Rust project)
- saw-remote-api/python (a Python project)

Ensure you have the following installed (beyond what's required by sawscript):
* wget
* rust >= 1.70
* jq (json query, command line tool used to finagle json files)

## Quick Start

Run this script to build & install software needed to do the checks:
```
cve-reports/bin/cvecheck-build.sh
```

Run this script to run all three checks, freeform text to stdout:
```
cve-reports/bin/cvecheck-all.sh
```

To generate all three *JSON* reports (each with their own json structure):
```
cve-reports/bin/cvecheck-all.sh --write-json
```

Individual reports can be run also:
```
cve-reports/bin/cvecheck-hs.sh # generate the Haskell CVE report
cve-reports/bin/cvecheck-rs.sh # generate the Rust CVE report
cve-reports/bin/cvecheck-py.sh # generate the Python CVE report
```
As well as the json output for each language:
```
cve-reports/bin/cvecheck-hs.sh --write-json
cve-reports/bin/cvecheck-rs.sh --write-json
cve-reports/bin/cvecheck-py.sh --write-json
```

A concise rollup of the three JSON reports can be created with:
```
cve-reports/bin/cvecheck-consolidated.sh
```
which prints a sequence of normalized JSON values to the standard
output for the known CVEs. The form of each CVE is like this example:
```
{
"language": "Haskell",
"id": "HSEC-2023-0007",
"title": "readFloat: memory exhaustion with large exponent",
"package": {
"name": "base",
"version": "4.17.2.1"
}
}
```
Note that `cvecheck-consolidated.sh` does not generate the respective
JSON reports, the user must do that with the above shell scripts.

## What we check

We check these three projects
* the top level Haskell project as captured in saw-script.cabal.
* the Python project in `saw-remote-api/python/`.
* the Rust project in `doc/rust-tutorial/code/salsa20`.

We ignore the projects (packages, etc.) in `dep/`, if any are used in
the above projects, the dependencies should be reflected in the
builds of the above.

NOTE: if further rust or python projects get added, they need to be
added to the scripts.

## Caveats

For further background refer to
https://github.com/GaloisInc/hello-cve-scans.git
but in a nutshell
- we may report false positives, as using a package does not imply
exercising the code in a package (or environment) that exhibits the
vulnerability.

## Further details on the CVE checks for our three languages:
### Haskell: `bin/cvecheck-hs.sh`

We use the `MangoIV/cabal-audit` package
https://github.com/MangoIV/cabal-audit.git
which is not to be confused with the `cabal-audit` in hackage.
The latter is hopelessly out of date, while the former

- is beta quality, but is being maintained
- calls the cabal libraries and thus (for better or worse), inherits
the cabal settings and environment.
- uses the advisories in
https://github.com/haskell/security-advisories.git
(this repo contains the database as well as some customized Haskell packages.)

Note that the advisory database is checked and downloaded each time
that `cabal-audit` is called.

### Python: `bin/cvecheck-py.sh`

We use the `python-audit` package.
Since are using `poetry`, we need to first "extract" a
requirements.txt using `poetry export`.

### Rust: `bin/cvecheck-rs.sh`

We use the `cargo-audit` package.
14 changes: 14 additions & 0 deletions cve-reports/bin/cvecheck-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
# cvecheck-all.sh: do all CVE checks
# usage: cvecheck-all.sh

THIS_DIR=$(realpath $(dirname $0))

echo "\ncvecheck-all.sh: check Haskell"
${THIS_DIR}/cvecheck-hs.sh $*

echo "\ncvecheck-all.sh: check Python"
${THIS_DIR}/cvecheck-py.sh $*

echo "\ncvecheck-all.sh: check Rust"
${THIS_DIR}/cvecheck-rs.sh $*
58 changes: 58 additions & 0 deletions cve-reports/bin/cvecheck-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/sh
# cvecheck-build.sh: build what's needed to do CVE checks
# usage: ./cvecheck-build.sh

set -e

TOP_DIR=$(realpath $(dirname $0)/../..)
TOP_BIN_DIR=${TOP_DIR}/bin
MY_BIN_DIR=$(realpath $(dirname $0))


##################################################################
##### For Haskell: build MangoIV/cabal-audit: ####

# We build cabal-audit "out of line" from the current cabal due to its
# complicated mix of non-hackage dependencies.
#
# (Maybe we can change this once MargoIV/cabal-audit becomes more
# mature and the required dependencies are in hackage.)

cd ${MY_BIN_DIR}/..

if [ ! -e cabal-audit-src ]; then
echo "\ncvecheck-build.sh: acquire cabal-audit source ..."
wget -O cabal-audit.tar.gz \
https://github.com/MangoIV/cabal-audit/archive/2fe849d.tar.gz
# software's a little finicky, so we pick a known commit.
tar xzvf cabal-audit.tar.gz
rm cabal-audit.tar.gz
mv cabal-audit-* cabal-audit-src
fi

echo "\ncvecheck-build.sh: build cabal-audit source ..."
cd cabal-audit-src
cabal build

echo "\ncvecheck-build.sh: install cabal-audit in ${TOP_BIN_DIR}."
cp -pv $(cabal list-bin -v0 exe:cabal-audit) ${TOP_BIN_DIR}


##################################################################
##### For Rust: cargo-audit ####

cd ${TOP_DIR}/doc/rust-tutorial/code/salsa20

echo "\ninstalling cargo-audit."
cargo install cargo-audit


##################################################################
##### For Python: pip-audit ####

cd ${TOP_DIR}/saw-remote-api/python

echo "\ninstalling pip-audit."
pip install pip-audit


39 changes: 39 additions & 0 deletions cve-reports/bin/cvecheck-consolidated.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/sh
# cvecheck-all.sh: do all CVE checks
# usage: cvecheck-all.sh

set -e

export THIS_DIR=$(realpath $(dirname $0))
. ${THIS_DIR}/variables.sh

JQ='jq'
JQARGS='--indent 2'

if ! which ${JQ} >/dev/null ; then
echo "no ${JQ} executable found"
exit 1
fi

# debugging/testing
# HS_JSON_FILE=~/src/GaloisInc/hello-cve-scans/hs-project/T.json
# PY_JSON_FILE=~/src/GaloisInc/hello-cve-scans/py-project/T-pip-audit.json
# RS_JSON_FILE=~/src/GaloisInc/hello-cve-scans/rust-project/T.json

# echo consolidating
# for f in ${HS_JSON_FILE} ${PY_JSON_FILE} ${RS_JSON_FILE}; do
# echo " " $f
# done;
# echo into ${COMMON_JSON_FILE}

${JQ} ${JQARGS} \
'to_entries .[] | {language: "Haskell", id: .value.advisories.[0].id, title: .value.advisories.[0].summary, package: {name: .key, version: .value.version}}' \
${HS_JSON_FILE}

${JQ} ${JQARGS} \
'.dependencies | .[] | select(.vulns != []) | {language: "Python", id: .vulns.[0].id , title: "", package: {name: .name, version: .version}}' \
${PY_JSON_FILE}

${JQ} ${JQARGS} \
'.vulnerabilities.list | .[] | ( {language: "Rust", id: .advisory.id , title: .advisory.title, package: {name: .package.name, version: .package.version}})' \
${RS_JSON_FILE}
29 changes: 29 additions & 0 deletions cve-reports/bin/cvecheck-hs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/sh
# cvecheck-hs.sh: do CVE checks
# usage: cvecheck-hs.sh

set -e

export THIS_DIR=$(realpath $(dirname $0))
. ${THIS_DIR}/variables.sh

AUDIT_EXE=${TOP_DIR}/bin/cabal-audit

echo
echo "Running cabal-audit, at top level:"

if [ ! -x ${AUDIT_EXE} ] ; then
echo error: ${AUDIT_EXE} does not exist.
exit 1
elif [ $# == 0 ] ; then
ARGS=""
elif [ $# -eq 1 ] && [ $1 = "--write-json" ] ; then
ARGS="--json -o ${HS_JSON_FILE}"
else
echo "Usage: cvecheck-hs.sh [--write-json]"
exit 1
fi

cd ${TOP_DIR}

${AUDIT_EXE} ${ARGS}
28 changes: 28 additions & 0 deletions cve-reports/bin/cvecheck-py.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/sh
# cvecheck-py.sh: do CVE checks
# usage: cvecheck-py.sh [--write-json]

set -e

export THIS_DIR=$(realpath $(dirname $0))
. ${THIS_DIR}/variables.sh

PROJ_DIR=${TOP_DIR}/saw-remote-api/python

echo
echo "Running pip-audit in ${PROJ_DIR}:"

if [ $# == 0 ] ; then
ARGS=""
elif [ $# -eq 1 ] && [ $1 = "--write-json" ] ; then
ARGS="-f json -o ${PY_JSON_FILE}"
else
echo "Usage: cvecheck-py.sh [--write-json]"
exit 1
fi

cd ${PROJ_DIR}

poetry export -frequirements.txt -o tmp-requirements.txt

pip-audit -r ./tmp-requirements.txt ${ARGS}
24 changes: 24 additions & 0 deletions cve-reports/bin/cvecheck-rs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/sh
# cvecheck-rs.sh: do CVE checks
# usage: cvecheck-rs.sh [--write-json]

set -e

export THIS_DIR=$(realpath $(dirname $0))
. ${THIS_DIR}/variables.sh

PROJ_DIR=${TOP_DIR}/doc/rust-tutorial/code/salsa20

echo
echo "Running cargo-audit in ${PROJ_DIR}:"

cd ${PROJ_DIR}

if [ $# == 0 ] ; then
cargo audit
elif [ $# -eq 1 ] && [ $1 = "--write-json" ] ; then
cargo audit --json > ${RS_JSON_FILE}
else
echo "Usage: cvecheck-rs.sh [--write-json]"
exit 1
fi
5 changes: 5 additions & 0 deletions cve-reports/bin/variables.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
TOP_DIR=$(realpath ${THIS_DIR}/../..)
HS_JSON_FILE=$(realpath ${THIS_DIR}/..)/hs-cves.json
PY_JSON_FILE=$(realpath ${THIS_DIR}/..)/py-cves.json
RS_JSON_FILE=$(realpath ${THIS_DIR}/..)/rs-cves.json
COMMON_JSON_FILE=$(realpath ${THIS_DIR}/..)/all-cves.json
1 change: 1 addition & 0 deletions saw-remote-api/python/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ share/python-wheels/
.installed.cfg
*.egg
MANIFEST
tmp-requirements.txt

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down

0 comments on commit 69a2dd3

Please sign in to comment.