After this lesson, you will be able to: Audit project dependencies with npm audit, automate updates with Dependabot, add Snyk scanning to CI, understand why lockfiles belong in version control, and treat every dependency as attack surface.
Most of your app is code you did not write. A supply-chain attack compromises you through a dependency. This lesson covers real incidents (event-stream, XZ Utils, left-pad), npm audit, Dependabot, Snyk in CI, lockfiles, and the discipline of minimal dependencies.
A supply-chain attack compromises you by compromising something you depend on. event-stream (2018): a popular package was handed to a new maintainer who added code to steal Bitcoin wallets. XZ Utils (2024): a multi-year social-engineering campaign planted a backdoor in a core Linux compression library, nearly reaching every server on earth before a developer noticed a tiny performance anomaly. left-pad (2016): an 11-line package was unpublished and broke builds across the internet, showing how much rests on tiny, unowned dependencies. These are not hypotheticals.
npm audit cross-references your dependency tree against a vulnerability database and reports severity.
$ npm audit# found 3 vulnerabilities (1 low, 1 moderate, 1 high)$ npm audit fix # applies safe, in-range updates$ npm audit fix --force # may install breaking major versions: review first# For a finding you cannot fix yet (no patch upstream):# - assess whether the vulnerable code path is even reachable in your app# - document the decision; revisit when a patch ships
Dependabot (built into GitHub) opens automated pull requests when a dependency has an update or a known vulnerability. Configure update frequency in .github/dependabot.yml, review the changelog, let CI run, and merge. Snyk goes further than npm audit: it catches issues in transitive dependencies, suggests minimal upgrade paths, and integrates as a GitHub Actions step that can fail the build on a high-severity finding. The free tier covers individual and small projects.
A minimal GitHub Actions step that fails the build on high-severity issues.
# .github/workflows/security.yml- name: Snyk dependency scanuses: snyk/actions/node@masterenv:SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}with:args: --severity-threshold=high
package-lock.json and yarn.lock pin the exact version of every dependency, including transitive ones, so every install and every CI run gets the identical tree. Commit them. Deleting and regenerating a lockfile is sometimes a deliberate security step (to pull patched transitive versions), but normally you keep it stable. The deeper discipline: every dependency is attack surface and maintenance burden. Before adding a package ask: does this justify the risk, is it actively maintained, and how many transitive dependencies does it drag in? Sometimes ten lines of your own code beats a package with forty sub-dependencies.
Pick the best reason.
Running npm audit fix --force without reading what it changes, and shipping a breaking major bump. Ignoring audit output entirely because 'it is just dev dependencies' (build-time compromise is still compromise). Not committing the lockfile, so CI installs a different tree than you tested. Adding a heavy package for a one-line need. Auto-merging Dependabot PRs without CI, so a bad update ships unreviewed. Assuming a high download count means a package is safe (event-stream was hugely popular).
Sign in and purchase access to unlock this lesson.