diff --git a/.gitignore b/.gitignore index 39d5b7104..45dbd516c 100644 --- a/.gitignore +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/cve-reports/.gitignore b/cve-reports/.gitignore new file mode 100644 index 000000000..cdc240232 --- /dev/null +++ b/cve-reports/.gitignore @@ -0,0 +1 @@ +cabal-audit-src/ diff --git a/cve-reports/README.md b/cve-reports/README.md new file mode 100644 index 000000000..df0a82543 --- /dev/null +++ b/cve-reports/README.md @@ -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`. diff --git a/cve-reports/bin/cvecheck-all.sh b/cve-reports/bin/cvecheck-all.sh new file mode 100755 index 000000000..84279e3de --- /dev/null +++ b/cve-reports/bin/cvecheck-all.sh @@ -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 $* diff --git a/cve-reports/bin/cvecheck-build.sh b/cve-reports/bin/cvecheck-build.sh new file mode 100755 index 000000000..a8658c77c --- /dev/null +++ b/cve-reports/bin/cvecheck-build.sh @@ -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 + + diff --git a/cve-reports/bin/cvecheck-consolidated.sh b/cve-reports/bin/cvecheck-consolidated.sh new file mode 100755 index 000000000..e72073747 --- /dev/null +++ b/cve-reports/bin/cvecheck-consolidated.sh @@ -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} diff --git a/cve-reports/bin/cvecheck-hs.sh b/cve-reports/bin/cvecheck-hs.sh new file mode 100755 index 000000000..45ddc396c --- /dev/null +++ b/cve-reports/bin/cvecheck-hs.sh @@ -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} diff --git a/cve-reports/bin/cvecheck-py.sh b/cve-reports/bin/cvecheck-py.sh new file mode 100755 index 000000000..68e380c0c --- /dev/null +++ b/cve-reports/bin/cvecheck-py.sh @@ -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} diff --git a/cve-reports/bin/variables.sh b/cve-reports/bin/variables.sh new file mode 100644 index 000000000..361037660 --- /dev/null +++ b/cve-reports/bin/variables.sh @@ -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