Npm Audits and GitHub Actions

Pavel Saman
4 min readJul 2, 2022

--

Packages get old, security bugs are founds, new versions are released. It’s a good practice to find out what packages in my project are outdated, so I can take actions and update them to newer versions if that’s desirable.

I’m sure there’re many different tools for that. One of them comes with npm package manager:

$ npm audit

It can also take some options, --audit-level is one of them. For example if I want npm audit to return an error code only when moderate or higher vulnerabilities are found, I set --audit-level moderate

It will hopefully report:

found 0 vulnerabilities

A nice thing about npm audit is it’s easy to run it in a pipeline, for example in GitHub Actions, a job with npm audit might look like this:

jobs:
audit:
name: Audit packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Audit packages
run: npm audit --audit-level moderate
env:
CI: true

With the CI envrionment variable, it runs a bit more efficiently (I think it means it reports less because there’s no need for textual output in pipelines, the return code is what matters the most).

A more complete example could be to run such a check on PR:

name: PR buildon:
pull_request:
workflow_dispatch:
jobs:
audit:
name: Audit packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Audit packages
run: npm audit --audit-level moderate
env:
CI: true
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
build:
name: Build with Node ${{ matrix.node_version }}
runs-on: ubuntu-latest
strategy:
matrix:
node_version:
- 12
- 14
- 15
- 16
- 17
- 18
steps:
- uses: actions/checkout@v3
- id: build
uses: ./.github/actions/build
with:
node_version: ${{ matrix.node_version }}

When a PR is created, reopened, or synchronized, this workflow will kick off.

I can further specify in Settings => Branches what checks have to pass in order to be able to merge the PR into a protected branch:

Branch protection rules — checks on PR
Branch protection rules — checks on PR

This way, it doesn’t matter that I did not specify any needs in the jobs, the jobs can run in parallel and I’ll still not be able to merge before they all pass:

PR — waiting for all checks to pass
PR — waiting for all checks to pass

Only when they all pass, I can merge to the protected branch:

All checks passed, I’m able to merge
All checks passed, I’m able to merge

Or I can set up a periodic check with cron:

name: Static analysison:
schedule:
- cron: '30 8 * * 6'
jobs:
audit:
name: Audit packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Audit packages
run: npm audit --audit-level moderate
env:
CI: true
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

Just two warnings regarding cron in GitHub Actions:

  • they can be delayed, sometimes by a lot of minutes; e.g. this cron job ran about 15 minutes late; I assume and hope this might be an issue only in the free subscription
  • the time is in UTC :) I mean you probably know, but sometimes it can catch me out :)

Or it can all run as a prerequisite for e.g. pushing to an NPM registry:

Release pipeline with audit check
Release pipeline with audit check

In Yaml, it can look like this:

name: Run tests and publishon:
push:
branches:
- master
paths-ignore:
- '.github/**'
- '**.md'
- 'tests/**'
workflow_dispatch:
jobs:
audit:
name: Audit packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Audit packages
run: npm audit --audit-level moderate
env:
CI: true
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
build:
name: Build with Node ${{ matrix.node_version }}
needs:
- audit
- analyze
runs-on: ubuntu-latest
strategy:
matrix:
node_version:
- 12
- 14
- 15
- 16
- 17
- 18
steps:
- uses: actions/checkout@v3
- id: build
uses: ./.github/actions/build
with:
node_version: ${{ matrix.node_version }}
release:
name: Release to npm
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
registry-url: 'https://registry.npmjs.org'
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

This workflow lives in the repo in ./.github/workflows/cd-release.yaml.

Static checks — linting, security checks, package dependencies checks, and more — can be really helpful when maintaining a project over a long period of time. GitHub Actions allow us to easily automate these checks.

--

--

No responses yet