Published September 20, 202612 min read

Nightly Security Checks

Outcome

A pull-request gate blocks high or critical dependency findings and broken builds, while a separate nightly run reports new vulnerability, update, and framework-support risks to a named human owner.

Ice package blocks passing through a blue security scanner while a cracked red block is diverted for review.

Share this guide

Share this guide on LinkedIn

Summarize with AI

ChatGPTPerplexityClaude
Table of Contents

A Green Build Is Not A Security Result

A repository can stay untouched overnight and still become riskier by morning. A new advisory can change what is known about an installed dependency even when the code has not changed.

Lint checks code rules. Build checks whether the application compiles. Neither one checks the dependency tree against newly published advisories.

Think of the build as testing whether a door still opens. A vulnerability check asks whether its lock has appeared on a recall list. You need both answers.

Two Jobs, Not One

Most small sites try to run everything in one workflow, and then weaken it because a noisy check keeps blocking merges.

Split the work instead. A pull-request job is a gate: few checks, all of them hard failures, all of them worth stopping a merge for. A nightly job is a report: it may be broader, noisier, and allowed to say things that do not block anything.

The gate protects the deploy. The report protects your attention. Mixing them produces a check nobody trusts and nobody reads.

Outdated Is Not Vulnerable

npm outdated lists packages behind their latest release. That is a maintenance signal, not a security finding, and treating it as one is how teams end up with a permanently red pipeline.

Gate releases on the vulnerability threshold you chose and on checks that genuinely broke. Use the outdated list to plan work, not to fail a run.

A Report Nobody Reads Is Not A Control

The part that fails in practice is not the YAML. It is the last step: a scheduled workflow goes red at 02:00, nobody is subscribed to the notification, and the failure sits in the Actions tab for three weeks.

Name one person. Turn on failed-run notifications for them. Then break the workflow on purpose and confirm the message actually arrives.

Steps

Guide

  1. 1

    Confirm The Commands Before Automating Them

    Read Guide

    Run the intended checks from the repository root before placing them in CI.

    npm ci
    npm audit --audit-level=high
    npm run lint
    npm run build

    npm ci installs the locked dependency tree and fails when package.json and the lockfile disagree. The npm documentation explains that --audit-level=high changes the severity that produces a non-zero exit; it does not hide lower-severity findings.

    Use high and critical findings as the initial deploy threshold, then review every reported severity. The threshold is a release rule, not a claim that moderate findings never matter.

  2. 2

    Block Risky Pull Requests Before Main

    Read Guide

    Create .github/workflows/security-checks.yml. Keep the job name stable because the branch rule will require that exact check.

    name: Security checks
    
    on:
      pull_request:
        branches: [main]
      workflow_dispatch:
    
    permissions:
      contents: read
    
    jobs:
      verify:
        name: audit-lint-build
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v7
          - uses: actions/setup-node@v7
            with:
              node-version-file: .nvmrc
              cache: npm
          - run: npm ci
          - run: npm audit --audit-level=high
          - run: npm run lint
          - run: npm run build

    Replace main, .nvmrc, and the script names with the project's real values. If the repository has no .nvmrc, pin the version directly with node-version: 24 instead. This job must not deploy or modify dependencies.

    Action majors move faster than the guides that quote them. Check the current major of every action you copy, and let Dependabot's github-actions ecosystem keep them current afterwards.

    Protect the deploy branch in GitHub and require audit-lint-build before merging. GitHub's required-status-check rules make the merge wait until the check succeeds. If the host deploys every commit on main, blocking the merge is what stops the deploy.

  3. Use The Nightly Run For Wider Reporting

    Create .github/workflows/nightly-security.yml. The nightly job repeats the deterministic checks and adds dependency freshness reporting.

    name: Nightly dependency report
    
    on:
      schedule:
        - cron: "17 2 * * *"
      workflow_dispatch:
    
    permissions:
      contents: read
    
    jobs:
      report:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v7
          - uses: actions/setup-node@v7
            with:
              node-version-file: .nvmrc
              cache: npm
          - run: npm ci
          - run: npm audit --audit-level=high
          - run: npm outdated || true
          - run: npm run lint
          - run: npm run build

    The example runs at minute 17 rather than at the start of the hour. GitHub warns that scheduled workflows can be delayed during heavy load, especially near minute zero, and scheduled workflows run from the default branch.

    npm outdated || true is deliberately informational. Outdated does not mean vulnerable. Keep audit, lint, and build as real failure conditions, but use the outdated list to plan maintenance.

    Enable failed GitHub Actions notifications for the owner. Run the workflow manually with a temporary failure and confirm that the alert arrives. A report nobody sees is not a control.

  4. Enable Dependabot Detection And Update Pull Requests

    Enable the dependency graph, Dependabot alerts, and Dependabot security updates in the repository settings. Alerts surface vulnerable dependencies; security updates can propose pull requests that remediate them.

    Add .github/dependabot.yml so stale npm packages and GitHub Actions references also receive planned update pull requests.

    version: 2
    updates:
      - package-ecosystem: npm
        directory: /
        schedule:
          interval: weekly
        groups:
          npm-minor-and-patch:
            update-types:
              - minor
              - patch
    
      - package-ecosystem: github-actions
        directory: /
        schedule:
          interval: monthly

    The grouped minor and patch updates matter more than they look. Ungrouped, a busy dependency tree can open a dozen separate pull requests a week, and the usual outcome is that all of them get ignored.

    A Dependabot pull request is a proposal, not an approval. Make it pass the same audit, lint, and build job as a human pull request. Review release notes and test major changes before merging.

    If Dependabot cannot resolve a private dependency, configure the required private-registry access. Do not weaken the check to make the alert disappear.

  5. Check Framework Support Separately

    npm outdated compares installed, wanted, and latest package versions. It does not decide whether the framework major still receives security fixes.

    Add a monthly task that compares the installed framework major with its official support policy. The Next.js support policy keeps the current major in Active LTS and the previous one in Maintenance LTS, and recommends serving production applications from one of the two.

    That distinction is the point of a separate check. A framework can be fully up to date against its own branch and still sit on a major that no longer receives security fixes, and no dependency tool will tell you.

    Record the framework major, support status, check date, and owner in a maintenance issue. Link to the live policy instead of freezing today's supported version numbers into the workflow.

  6. 6

    Let The AI Reviewer Report, Never Merge

    Read Guide

    A second nightly run can ask an AI coding agent to review the day's diff for subtler issues: removed checks, broader permissions, missing validation, or risky code paths that deterministic tools do not understand.

    Give it the diff, project rules, and check results. Ask for file references, severity, reasoning, and a verification step. Do not give this review job permission to commit, merge, change settings, or deploy.

    AI review is advisory. It can miss a real issue or report a false one. Keep npm audit, lint, and build as deterministic checks, and keep a person responsible for triage.

  7. Prove The Failure Path Works

    Open a test pull request that deliberately fails lint and confirm GitHub blocks the merge. Trigger the nightly workflow manually and confirm the named owner receives the failure notification.

    Review the Dependabot page and confirm both configured ecosystems show a recent check. Then upgrade the framework in a branch. If the change conflicts with another package or breaks the application, npm ci, lint, or build should fail before anyone ships it.

    The purpose is not to create failures. It is to prove the pipeline stops one before production.

Be Aware

The workflow uses npm in a pnpm or Yarn repository.

Use the package manager that owns the committed lockfile and replace the install, audit, cache, and outdated commands together.

The audit passes but the site is assumed to be secure.

Treat audit as one known-advisory signal. Keep framework support checks, application review, updates, and human triage.

Every outdated package fails the nightly run.

Keep npm outdated informational. Gate releases on the chosen vulnerability threshold and broken deterministic checks.

Dependabot opens more pull requests than anyone reviews.

Group minor and patch updates, keep majors separate, and set the interval to the cadence the owner can actually clear.

Dependabot creates a framework upgrade that conflicts with other packages.

Review the release notes and let npm ci, lint, and build fail the pull request before merge. Resolve the compatibility problem in the branch.

The nightly workflow fails but nobody responds.

Assign one owner, enable failed-run notifications, and test delivery with a controlled manual failure.

Nightly Security Review Task

Copy / paste

Review this website repository without changing it.

Inputs:
- today's git diff
- package.json and the committed lockfile
- GitHub Actions workflow files
- Dependabot configuration
- the latest audit, lint, and build output
- the framework's official support policy

Checks:
1. Confirm the pull-request job installs from the lockfile, audits at the approved severity threshold, lints, and builds.
2. Confirm the nightly job reports vulnerabilities and outdated dependencies, then lints and builds.
3. Flag removed checks, broader permissions, unsupported framework versions, ignored failures, and dependency changes that need human review.
4. For each finding, provide severity, file and line reference, reasoning, and one verification step.
5. Separate confirmed failures from hypotheses.

Stop conditions:
- Do not edit files.
- Do not run an automatic fix.
- Do not open, approve, or merge a pull request.
- Do not change repository settings.
- Do not deploy.
- If a required input is missing, report it instead of guessing.

Return a short report for the named human owner.

About the author

Nikita Goncharenko

Nikita Goncharenko

AI Fast Integrator

Nikita Goncharenko uses AI as a practical delivery layer for research, coding, documentation, content systems, and faster decisions.