Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CVE checks for Haskell & Python packages #1769

Merged
merged 3 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ cryptol.wixpdb

# happy-generated files
src/Cryptol/Parser.hs

# tempfile used for CVE checks:
cryptol-remote-api/python/tmp-requirements.txt
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/
116 changes: 116 additions & 0 deletions cve-reports/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# CVE Checks
## Background.

Here we generate reports for listing any used packages that are
subject to CVEs. We do this for both Haskell 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:
- cryptol itself
- cryptol-remote-api/python (a Python project), e.g.,
```
cd cryptol-remote-api/python
poetry install
```

Ensure you have the following installed (beyond what's required by cryptol):
* wget
* 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 both checks, writing freeform text to stdout:
```
cve-reports/bin/cvecheck-all.sh
```

Run this to generate both *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-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-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 both these projects
* the top level Haskell project as captured in saw-script.cabal.
* the Python project in `cryptol-remote-api/python/`.

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 projects get added to the repo, 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`.
11 changes: 11 additions & 0 deletions cve-reports/bin/cvecheck-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/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 $*
48 changes: 48 additions & 0 deletions cve-reports/bin/cvecheck-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh
# cvecheck-build.sh: build what's needed to do CVE checks
# usage: ./cvecheck-build.sh

set -e

TOP_DIR=$(realpath $(dirname $0)/../..)
MY_BIN_DIR=$(realpath $(dirname $0))
PYTHON_DIR=${TOP_DIR}/cryptol-remote-api/python

##################################################################
##### 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 ${MY_BIN_DIR}."
cp -pv $(cabal list-bin -v0 exe:cabal-audit) ${MY_BIN_DIR}


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

cd ${PYTHON_DIR}

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


34 changes: 34 additions & 0 deletions cve-reports/bin/cvecheck-consolidated.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/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

# echo consolidating
# for f in ${HS_JSON_FILE} ${PY_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}
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=${THIS_DIR}/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}/cryptol-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}
4 changes: 4 additions & 0 deletions cve-reports/bin/variables.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
TOP_DIR=$(realpath ${THIS_DIR}/../..)
HS_JSON_FILE=$(realpath ${THIS_DIR}/..)/hs-cves.json
PY_JSON_FILE=$(realpath ${THIS_DIR}/..)/py-cves.json
COMMON_JSON_FILE=$(realpath ${THIS_DIR}/..)/all-cves.json
Loading