All Docs
FeaturesCalmony PayUpdated March 15, 2026

SEC-26: Supply Chain Hardening — Enforcing package-lock.json and npm ci

SEC-26: Supply Chain Hardening — Enforcing package-lock.json and npm ci

Introduced in v1.0.60

Calmony Pay is a payment platform. A compromised dependency has a direct path to payment credential exfiltration or transaction tampering. This release closes SEC-26 by enforcing a committed lock file and reproducible installs across the entire build pipeline.


The Problem

Prior to v1.0.60, no package-lock.json was committed to the repository and CI used npm install. This created four concrete risks:

  1. Non-deterministic buildsnpm install resolves the highest compatible version at the time it runs. The versions installed on a developer's machine, in CI, and in production could all differ silently.
  2. Supply chain attack surface — without pinned versions, a hijacked or typosquatted package on the npm registry can enter the build between runs with no visible change to any tracked file.
  3. Dependabot blocked — GitHub Dependabot requires a lock file to open automated version-bump PRs. Without one it cannot function.
  4. Divergent audit resultsnpm audit in CI may flag different vulnerabilities than the actual production dependency tree if the resolved versions differ.

What Changed

.npmrc (new file)

A project-level .npmrc is now committed with three settings:

# Always generate and maintain a package-lock.json
package-lock=true

# Enforce SSL certificate validation for all registry requests
strict-ssl=true

# Pin to the public registry — prevents dependency confusion attacks
registry=https://registry.npmjs.org/
  • package-lock=true makes it impossible to bypass lock file generation with --no-package-lock.
  • strict-ssl=true prevents SSL stripping on registry requests.
  • Explicitly pinning registry closes the dependency confusion attack vector, where a private package name could be hijacked on the public registry.

CI: lock-file-check job

A new job runs in parallel with the existing build job. If package-lock.json is absent from the repository, it fails with a detailed error:

SEC-26: package-lock.json is missing from the repository.

WHY THIS MATTERS:
  1. Non-deterministic builds — npm install may resolve different versions
     across developer machines, CI, and production.
  2. Supply chain risk — without pinned versions, a hijacked package on
     the npm registry can silently compromise the build.
  3. Dependabot cannot open version-bump PRs without a lock file.
  4. npm audit results may differ from the actual production dependency tree.

FIX:
  Run 'npm install' locally, then commit the generated package-lock.json.

Once package-lock.json is present, the job verifies it is committed and reports its lockfileVersion.

CI: npm ci in the build job

The install step in the build job now selects the correct command based on whether a lock file exists:

- name: Install dependencies
  run: |
    if [ -f "package-lock.json" ]; then
      echo "✓ package-lock.json found — using 'npm ci' for reproducible install"
      npm ci
    else
      echo "⚠ No package-lock.json found — falling back to 'npm install'."
      npm install
    fi

npm ci differs from npm install in two important ways:

  • It installs exactly the versions recorded in package-lock.json — no version resolution.
  • It deletes node_modules before installing, preventing stale package accumulation.

Bootstrap: Action Required

The enforcement gate introduced by this release requires package-lock.json to exist. Until a developer commits it, the lock-file-check job will warn on every CI run.

To complete the bootstrap, run the following on the relevant branch:

npm install                          # generates package-lock.json
git add package-lock.json
git commit -m "chore: add package-lock.json for reproducible builds (SEC-26)"
git push

After this commit:

  • lock-file-check will pass cleanly
  • build will always use npm ci
  • Dependabot will activate
  • The continue-on-error: true flag on lock-file-check in ci.yml should be removed

Developer Guidelines

  • Never use --no-package-lock.npmrc enforces this, but avoid it in scripts too.
  • Always commit package-lock.json — treat it as a first-class source file, not a build artefact.
  • Run npm ci locally (not npm install) when you want to reproduce the exact CI environment.
  • Run npm install only when you intend to update or add dependencies, and always commit the resulting lock file changes in the same PR.
  • Do not add package-lock.json to .gitignore — it must remain tracked.