SAST for Node.js with GitLab CI: Secure your Node.js applications with Static Application Security Testing (SAST) integrated into your GitLab CI pipeline. Automate vulnerability scanning, identify security flaws early, and improve your Node.js application security posture. Learn how to implement SAST for Node.js projects using GitLab CI for continuous security.

Static Application Security for Nodejs (with Gitlab CI)


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 snykretireaudit. 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!