Skip to content

aminhanifm/LockSignal

Repository files navigation

LockSignal

LockSignal explains what changed between two npm package-lock.json files and highlights changes that deserve human review.

Lockfiles are precise but difficult to inspect manually. A one-line dependency update can replace sources, remove integrity data, introduce install scripts, or expand a transitive tree across hundreds of lines. LockSignal turns that noise into a deterministic report with the exact evidence, a plain-language reason, and a suggested review step.

LockSignal is an explainability tool, not a vulnerability scanner. It does not label packages safe or malicious and cannot guarantee that a dependency is trustworthy.

Why LockSignal exists

Reviewers should not need to infer the meaningful parts of a lockfile change from a raw JSON diff. LockSignal answers:

  • Which installed package locations were added, removed, upgraded, or downgraded?
  • Was a change direct or transitive, and did its runtime/development/optional classification change?
  • Did a package move to Git, a local file, insecure HTTP, or a different registry hostname?
  • Did integrity data disappear or change unexpectedly?
  • Is a package newly marked with hasInstallScript?
  • Did the tree or duplicate-version count expand?

Every answer comes from the two supplied lockfiles. There are no registry lookups, accounts, API keys, telemetry, AI calls, or backend services.

What it detects

  • Added and removed installed package locations
  • Semantic-version upgrades, downgrades, and direct major-version changes
  • Non-semver version changes
  • Direct and transitive dependency status
  • Runtime, development, and optional classification changes
  • Resolved URL, protocol, source kind, and hostname changes
  • Integrity additions, removals, and changes
  • Newly introduced hasInstallScript flags
  • New Git, file, insecure HTTP, and custom-registry sources
  • Increased and reduced duplicate-version counts
  • Dependency-tree expansion and contraction
  • Scoped packages, nested installations, and multiple installed versions

What it does not detect

LockSignal does not:

  • Query vulnerability, malware, provenance, or package-reputation databases
  • Fetch package metadata or resolved URLs
  • Install packages or execute lifecycle scripts
  • Analyze package source code or tarball contents
  • Confirm maintainer identity or registry ownership
  • Support yarn, pnpm, Bun, or npm lockfile version 1 in v0.1.0
  • Determine whether a package is safe, malicious, or suitable for production

Browser workflow

Try the interactive LockSignal demo

The static browser application runs at the canonical portfolio URL above.

  1. Load or drag the Before package-lock.json.
  2. Load or drag the After package-lock.json.
  3. Run the comparison.
  4. Review severity totals and package-change counts.
  5. Search or filter by severity, direct/transitive status, and change type.
  6. Expand a finding to inspect its exact lockfile evidence.
  7. Export a Markdown or versioned JSON report.

The demo includes three synthetic examples: a quiet patch, a major update with a new install script and tree expansion, and a source/integrity case. Browser inputs are limited to 10 MB per file and remain in memory for the current tab.

CLI installation and examples

After the package is published:

npm install --global @aminhanifm/locksignal

Compare two files:

locksignal compare before-package-lock.json after-package-lock.json

Write Markdown without overwriting an existing file:

locksignal compare before-package-lock.json after-package-lock.json \
  --format markdown \
  --output lockfile-review.md

Produce JSON for another tool and fail when a warning or high finding is present:

locksignal compare before-package-lock.json after-package-lock.json \
  --format json \
  --fail-on warning

Available options:

--format text|markdown|json
--output <file>
--force
--fail-on none|warning|high
--no-color
--version
--help

Reports go to stdout by default. Errors go to stderr. --output refuses to replace an existing file unless --force is present.

Exit codes

Code Meaning
0 Comparison completed and the --fail-on threshold was not reached
1 Comparison completed, but a finding reached the requested threshold
2 Usage, file I/O, validation, identical-input, or report-writing error

--fail-on warning matches warning and high findings. --fail-on high matches only high findings. --fail-on none is the default.

Noisy diff, short report

A raw lockfile update might span dozens of changed lines:

- "@fixture/builder": "1.8.2"
+ "@fixture/builder": "2.0.0"
+ "hasInstallScript": true
+ "@fixture/adapter": "1.0.0"
+ "@fixture/shared": "2.0.0"

LockSignal turns it into reviewable statements:

Findings  HIGH 0  WARNING 3  INFO 8
Changes   +2  -0  ↑2  ↓0

[WARNING] @fixture/builder · major-version
  Direct dependency @fixture/builder crosses a semantic-version major boundary.
  review: Read the migration guide and release notes, then test behavior affected
  by breaking changes.

[WARNING] @fixture/builder · install-script
  @fixture/builder is newly marked as having an install script.
  review: Inspect the package's install scripts before installing dependencies.

The names, URLs, and hashes in committed examples are synthetic.

TypeScript API

import {
  compareLockfiles,
  createMarkdownReport,
  parseLockfile,
  summarizeComparison,
} from "@aminhanifm/locksignal";

const before = parseLockfile(beforeText, { label: "before-package-lock.json" });
const after = parseLockfile(afterText, { label: "after-package-lock.json" });

const comparison = compareLockfiles(before, after);
const summary = summarizeComparison(comparison);
const markdown = createMarkdownReport(comparison);

console.log(summary.severities.high);
console.log(markdown);

The JSON-compatible comparison result is versioned and contains:

{
  "schemaVersion": "1.0.0",
  "toolVersion": "0.1.0",
  "supportedLockfileVersions": [2, 3],
  "inputs": {
    "before": {
      "label": "before-package-lock.json",
      "lockfileVersion": 3,
      "packageCount": 42,
      "fingerprint": "81c15fca"
    },
    "after": {
      "label": "after-package-lock.json",
      "lockfileVersion": 3,
      "packageCount": 45,
      "fingerprint": "2ad0eb13"
    }
  },
  "packageChanges": [],
  "findings": [],
  "summary": {}
}

The actual result includes typed input metadata, package-change records, explicit findings, and complete summary counts. It does not embed either full lockfile.

Parsing failures throw a LockfileError with a structured detail object and one of these codes:

FILE_TOO_LARGE, INVALID_JSON, NOT_NPM_LOCKFILE, MISSING_FIELD, UNSUPPORTED_VERSION, INVALID_PACKAGE_ENTRY, or IDENTICAL_INPUTS.

Severity rules

LockSignal intentionally avoids a numeric “security score.” Findings use deterministic rules:

High

  • A new or changed source resolves through Git, a local file, or insecure HTTP
  • Integrity disappears from a package that still resolves as a registry tarball

Warning

  • A package is newly marked with hasInstallScript
  • Integrity changes without a corresponding version or resolved-source change
  • The source hostname changes
  • A direct dependency crosses a semantic-version major boundary
  • A package starts resolving through a non-default registry hostname

Info

  • A package location is added or removed
  • A version, source URL, integrity value, or dependency classification changes without a stronger rule
  • Duplicate installed versions increase or decrease
  • The installed dependency tree expands or contracts

Severity expresses review priority from lockfile evidence. It is not a claim about package intent or safety.

Privacy and security model

  • Comparison is read-only.
  • The core never uses filesystem or browser APIs.
  • The CLI only reads the two requested files and optionally writes a report.
  • The browser processes files in memory and uses no localStorage or sessionStorage.
  • No runtime network requests, telemetry, uploads, registry queries, or resolved-URL fetches are made.
  • Package scripts are never executed and analyzed dependencies are never installed.
  • React escapes displayed user-controlled values; Markdown and JSON exporters escape their formats.

Lockfiles can contain private registry URLs, organization names, and other operational details. Review exported reports before sharing them.

See SECURITY.md for reporting guidance and the security boundary.

Architecture

src/core/      environment-independent parser, normalization, comparison, findings, reports
src/cli/       importable CLI functions and a minimal executable wrapper
demo/          static React + Vite review application
test/fixtures/ synthetic npm v2/v3 lockfiles
test/          core, reporting, and CLI tests
e2e/           Playwright browser and mobile journeys
scripts/       packed npm consumer and browser-runner verification

The browser and CLI consume the same core result. Package identity is normalized by installation location and package name, so nested and duplicate installations remain distinct. Output arrays are sorted and finding IDs are derived from stable evidence, making repeat runs deterministic.

The executable wrapper is separate from the reusable CLI module. This keeps importing CLI helpers side-effect free and ensures npm’s .bin wrappers work on Windows, Linux, and macOS.

Supported lockfile versions

Version 0.1.0 supports npm package-lock.json lockfile versions 2 and 3 with a packages map and root packages[""] entry.

Unsupported versions and malformed shapes are rejected explicitly. LockSignal does not silently coerce a yarn, pnpm, Bun, npm v1, shrinkwrap-only, or arbitrary JSON file into a comparison.

Limitations

  • Package changes are matched by installed location; a package moving locations appears as one removal and one addition.
  • Semantic-version direction and major boundaries require complete valid versions such as 1.2.3. Other version strings are reported as changed without inferred ordering.
  • Registry classification treats registry.npmjs.org as the default npm registry. Other HTTPS hosts are reported as custom registries for review, even when intentionally configured.
  • Git, file, and HTTP signals identify source changes only; they do not inspect the referenced content.
  • Integrity comparison uses the literal lockfile values. LockSignal does not download tarballs to recompute hashes.
  • Workspace links and unusual npm-generated entries are normalized conservatively and may produce fewer source findings.
  • Results explain the two snapshots only; they do not infer why npm selected a transitive version.

Development and testing

Requirements: Node.js 22 or 24 and npm.

npm install
npm run dev

The demo development URL is:

http://127.0.0.1:4174/projects/LockSignal/

Quality commands:

npm run format
npm run lint
npm run typecheck
npm test
npm run test:coverage
npm run build
npm run build:demo
npm run verify:package
npm run test:e2e

npm run build:demo writes the static site to demo-dist. npm run verify:package creates a tarball in a temporary directory, installs it in a clean consumer, imports the API, and executes locksignal --version.

CI runs formatting, linting, strict type checks, tests, coverage, package and demo builds, pack inspection, packed installation, and browser journeys across Ubuntu/Windows and Node.js 22/24 as appropriate.

Contributing

Contributions are welcome. Start with CONTRIBUTING.md, use synthetic fixtures, and preserve the local-first, deterministic, explainable, read-only product principles.

Roadmap

Potential post-0.1 work, subject to explicit design and test coverage:

  • More explanation for workspace and peer-dependency topology
  • Configurable policies built from explicit rules rather than a score
  • Additional report integrations that preserve local processing
  • Carefully scoped support for other lockfile ecosystems

Yarn, pnpm, Bun, registry lookups, vulnerability databases, GitHub Actions, and automatic remediation are intentionally outside v0.1.0.

License

MIT © 2026 Amin Hanif. See LICENSE.

About

Explain npm package-lock.json changes and highlight changes that deserve human review.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages