SAST. Its a thing.ย Take the test to see if you need it. ๐ OK, not that SAST, the one that relates to security silly. Let’s talk about integrating static application security for nodejs from our Gitlab CI as part of a Defense In Depth strategy.
So I’ve been using clair from coreos. Its pretty awesome, but, to my chagrin, it does not cover python / node / go / ruby / …, the majority of the upstream culprits. (It focuses on apk/rpm/deb). So you can get a false sense of security by running it. It shocked me when I did my wiki, but then I fixed those issues and moved on and forgot. So when it came time to do my first node.js ‘express’ app, I ran it, got no hits, and was pleased with myself. Static application security for nodejs achieved?
Not so fast. Turns out you need to look a bit harder, tools like snyk, retire, audit. So I picked two (retire, audit) and added them to my CI pipeline, as below:
stage: scan
artifacts:
name: "$CI_PROJECT_PATH-$CI_COMMIT_REF_NAME"
paths:
- reports/
script: |
echo Analyse container $CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA for vulnerability
docker tag $CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA $CI_PROJECT_PATH:$CI_COMMIT_SHA
clairctl analyze -l -n --log-level debug $CI_PROJECT_PATH:$CI_COMMIT_SHA
echo Generate JSON report
clairctl report -l -f json $CI_PROJECT_PATH:$CI_COMMIT_SHA
echo Generate HTML report
clairctl report -l -f html $CI_PROJECT_PATH:$CI_COMMIT_SHA
docker tag $CI_PROJECT_PATH:$CI_COMMIT_SHA scannee
docker build -t scanner -f Dockerfile.scan .
docker run --rm scanner retire --path /usr/src/app > reports/retire.js.txt 2>&1 || true
docker run --rm scanner retire --path /usr/local/lib/node_modules/npm >> reports/retire.js.txt 2>&1 || true
docker run -w /usr/src/app --rm scanner auditjs -r >> reports/audit.js.txt || true
Now, unlike clair, these actually need to run in the image, or maybe with it mounted somehow. So I created this very simple Dockerfile. It inherits from ‘scannee’, which I tagged the main image as above.
Now, something else I found ‘exciting’. You see that ‘USER root’? Well the origin image (scannee) has a ‘USER nonprivileged’. So here I am increasing my privilege, I did not think you could do that. Hmm.
FROM scannee
ENV NODE_ENV "production"
WORKDIR /usr/src/app
USER root
# See https://github.com/npm/uid-number/issues/3 for why the 'set unsafe-perm'
RUN npm config set unsafe-perm true \
&& npm install -g retire \
&& npm install -g auditjs
Now my CI stage above scans (with clair), then builds a child container augmenting with retire and audit, then runs them. The original layer is unchanged, untouched. I can do my static application security for nodejs as-is.
I thought this was neat, you probably thought it was old hat.
But, it found me another hundred or so issues to dig into. And this is from node:9.11.0-alpine, its not like that is old!