PHPackages                             jeffersongoncalves/secure-lock-cli - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [CLI &amp; Console](/categories/cli)
4. /
5. jeffersongoncalves/secure-lock-cli

ActiveProject[CLI &amp; Console](/categories/cli)

jeffersongoncalves/secure-lock-cli
==================================

CLI tool to audit project dependencies (Composer &amp; npm) for known vulnerabilities and tell whether an available update actually leaves the vulnerable range.

1.7.2(1mo ago)131MITPHPPHP ^8.2CI passing

Since Jun 1Pushed 3w ago1 watchersCompare

[ Source](https://github.com/jeffersongoncalves/secure-lock-cli)[ Packagist](https://packagist.org/packages/jeffersongoncalves/secure-lock-cli)[ Docs](https://github.com/jeffersongoncalves/secure-lock-cli)[ GitHub Sponsors](https://github.com/jeffersongoncalves)[ RSS](/packages/jeffersongoncalves-secure-lock-cli/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (18)Versions (15)Used By (0)

[![Secure Lock CLI](https://raw.githubusercontent.com/jeffersongoncalves/secure-lock-cli/main/art/jeffersongoncalves-secure-lock-cli.png)](https://raw.githubusercontent.com/jeffersongoncalves/secure-lock-cli/main/art/jeffersongoncalves-secure-lock-cli.png)

secure-lock-cli
===============

[](#secure-lock-cli)

`secure-lock` audits a project's dependencies and answers three questions for every package:

1. **Is there a newer version?** (registry lookup)
2. **Is the installed version vulnerable?** (advisory database lookup)
3. **Is the available update actually safe?** — i.e. does the target version really leave the vulnerable range, instead of merely being newer?

Born out of the recent wave of supply-chain attacks (compromised / vulnerable packages on Composer and npm), the tool distinguishes a *useful bump that fixes the flaw* from a *useless bump that stays exposed*.

It covers **Composer** (`composer.lock`) and the JavaScript managers that share the npm ecosystem: **npm** (`package-lock.json` v1/v2/v3 and `npm-shrinkwrap.json`), **pnpm** (`pnpm-lock.yaml` lockfileVersion 5/6/9), **bun** (`bun.lock` text lockfile) and **yarn** (`yarn.lock` classic v1 and berry v2+). The `ECO` column shows the real manager a package came from, while advisories and the registry are resolved against the shared npm ecosystem.

Built with [Laravel Zero](https://laravel-zero.com) and modeled on the other CLIs in this monorepo.

[![secure-lock audit](https://raw.githubusercontent.com/jeffersongoncalves/secure-lock-cli/main/art/screenshot-audit.png)](https://raw.githubusercontent.com/jeffersongoncalves/secure-lock-cli/main/art/screenshot-audit.png)

Every package is checked against the registry and the GitHub Advisory Database, then classified — here six packages have a published fix (`SAFE`) and one only has a newer release (`UPDATE`).

Requirements
------------

[](#requirements)

- PHP `^8.2`
- A `composer.lock` and/or a `package-lock.json` to audit
- Optionally a `GITHUB_TOKEN` (raises the GitHub Advisory API rate limit from ~60 req/h to 5000 req/h)

Install
-------

[](#install)

### Global (recommended)

[](#global-recommended)

```
composer global require jeffersongoncalves/secure-lock-cli
```

The binary `secure-lock` will be on your `PATH` as long as Composer's global `vendor/bin` is in it.

### From source

[](#from-source)

```
git clone https://github.com/jeffersongoncalves/secure-lock-cli.git
cd secure-lock-cli
composer install
```

Usage
-----

[](#usage)

```
secure-lock                      # audit the current project (default command)
secure-lock --only-vuln          # only show packages at risk
secure-lock --no-dev             # ignore dev dependencies
secure-lock --json > audit.json  # structured output for CI
secure-lock --dir=/path/to/proj  # audit a specific directory
secure-lock --fix                # also print upgrade commands
```

The JavaScript lockfile is auto-detected in the project directory by priority (`pnpm` &gt; `bun` &gt; `yarn` &gt; `npm`); pass `--pnpm`/`--bun`/`--yarn`/`--npm` to target one explicitly.

### Options

[](#options)

```
--dir=            Project directory (defaults to the current directory)
--composer=       Explicit path to composer.lock
--npm=            Explicit path to package-lock.json
--pnpm=           Explicit path to pnpm-lock.yaml
--bun=            Explicit path to bun.lock
--yarn=           Explicit path to yarn.lock
--only-vuln           Show only packages at risk
--fix                 Print upgrade commands that leave every vulnerable range
--no-dev              Ignore development dependencies
--ignore=             Advisory id (GHSA or CVE) to suppress; repeatable
--config=             Path to a secure-lock.json (auto-detected otherwise)
--fail-on-unverified  Exit non-zero when an advisory lookup fails
--no-packagist        Disable the Packagist advisory fallback for Composer
--no-npm-audit        Disable the npm audit advisory fallback for JS
--json                Structured JSON output (for CI)
--sarif               SARIF 2.1.0 output (for GitHub code scanning)
--github-token=       GitHub token (or the GITHUB_TOKEN env var)
--cache-ttl=3600      HTTP cache TTL in seconds (0 disables caching)

```

Verdicts
--------

[](#verdicts)

For each package the tool compares the advisories that hit the **current**version against those still hitting the **latest** version:

VerdictBadgeMeaning`VULN``● VULN` (red)vulnerable now, no published fix`RISKY_UPDATE``● RISKY` (magenta)an update exists but stays exposed`UNKNOWN``● UNKNOWN` (yellow)advisory lookup failed — status not verified`SAFE_UPDATE``● SAFE` (green)the update **fixes** the vulnerability`UPDATE``● UPDATE` (cyan)newer version, no known vulnerability`OK``● OK` (gray)up to date and cleanThe table is sorted by risk (`VULN` &gt; `RISKY` &gt; `UNKNOWN` &gt; `SAFE` &gt; `UPDATE` &gt; `OK`) and the `NOTE` column shows the highest-severity advisory as `SEVERITY CVE-XXXX (+N)` (the CVE when present, otherwise the GHSA id).

> **`UNKNOWN` matters.** When an advisory lookup fails (most often the GitHub rate limit without a token), the package is reported as `UNKNOWN` — never as `OK`. A security tool must not turn a failed request into a false "all clear". Set a `GITHUB_TOKEN` to lift the limit, and add `--fail-on-unverified` to make CI fail when anything could not be checked.

Fixing
------

[](#fixing)

Pass `--fix` to also print, per manager, the upgrade command for each currently-vulnerable package. The target is the **smallest** version greater than the installed one that escapes *every* vulnerable range (computed from the advisories' patched versions and the latest release), so the bump is minimal and verified — not just "the newest":

[![secure-lock audit --fix](https://raw.githubusercontent.com/jeffersongoncalves/secure-lock-cli/main/art/screenshot-fix.png)](https://raw.githubusercontent.com/jeffersongoncalves/secure-lock-cli/main/art/screenshot-fix.png)

Note how the target is the *minimum* safe version, not the newest: `guzzlehttp/guzzle` goes to `6.5.8` (not `7.10.5`) and `phpunit` to `9.6.33`.

**Direct vs. transitive.** A direct dependency gets a plain `add`/`require`/`install`. A *transitive* one can't be pinned that way — an `npm install` wouldn't reach the nested version — so the suggestion is the manager's override mechanism instead: `overrides` (npm/bun), `pnpm.overrides`(pnpm) or `resolutions` (yarn) in `package.json`. Composer always uses `composer require`, which pins transitive packages too.

Packages with no version that leaves the vulnerable range (`VULN`) are skipped. In `--json` mode each package gains a `fix` object (`{target, command, transitive}`) or `null`.

Suppressing advisories
----------------------

[](#suppressing-advisories)

Accepted or un-patchable risks would otherwise keep failing CI forever. Pass one or more `--ignore` flags, or commit a `secure-lock.json` to the project root (auto-detected, or point at it with `--config`):

```
{
  "ignore": [
    "CVE-2022-31091",
    { "id": "GHSA-xxxx-yyyy-zzzz", "expires": "2026-12-31" }
  ]
}
```

```
secure-lock --ignore=GHSA-xxxx-yyyy-zzzz --ignore=CVE-2022-31091
```

An ignored advisory no longer counts toward the verdict. Entries with an `expires` date stop suppressing once that date has passed, so a deferred risk re-surfaces instead of being forgotten.

GitHub code scanning (SARIF)
----------------------------

[](#github-code-scanning-sarif)

`--sarif` emits SARIF 2.1.0, which GitHub can ingest to show the findings in the repository's **Security › Code scanning** tab:

```
name: secure-lock
on: [push, pull_request]
permissions:
  security-events: write
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: composer global require jeffersongoncalves/secure-lock-cli
      - run: secure-lock --no-dev --sarif > results.sarif
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        continue-on-error: true
      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif
```

Exit codes (for CI)
-------------------

[](#exit-codes-for-ci)

- `0` — no critical risk.
- `1` — a `VULN` or `RISKY_UPDATE` was found (fails the pipeline).
- `2` — input error (lockfile not found, invalid JSON, etc.).

### GitHub Actions

[](#github-actions)

```
- run: secure-lock --no-dev
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

How it works
------------

[](#how-it-works)

- **Registry** — Composer: `repo.packagist.org/p2/{name}.json`, walking every version and ignoring pre-releases (`alpha|beta|rc|dev|next|canary|nightly`) to find the highest stable. npm: `registry.npmjs.org/{name}` (`/` encoded as `%2F` for scoped packages), using `dist-tags.latest`.
- **Advisories** — the GitHub Advisory Database REST API: `GET /advisories?affects={name}&ecosystem={eco}`. The `affects` parameter receives **only the package name** (e.g. `guzzlehttp/guzzle`) — the ecosystem goes in its own parameter. Prefixing it (`composer:...`) returns `200` with a silently empty list.
- **Redundant fallbacks** — when a package's GitHub lookup fails (e.g. the rate limit without a token), a second source is queried so the result is recovered instead of left `UNKNOWN`: the **Packagist Security Advisories**API for Composer, and the **npm audit** bulk endpoint for npm/pnpm/bun/yarn. Each is one batched request for all failed packages. As a result every ecosystem can be audited with no token at all. Disable with `--no-packagist`/ `--no-npm-audit`.
- **Semver** — comparisons and range satisfaction use `composer/semver`, including GHSA ranges where a comma means logical AND (e.g. `>= 7.0.0, < 7.4.5`).
- **Concurrency** — every package's registry and advisory lookups are fired concurrently with `Http::pool` (capped), so a large lockfile is audited in a few waves instead of one request at a time.
- **Cache** — registry/advisory responses are cached to disk with a configurable TTL so repeated runs don't hit the API rate limits. Failed lookups are never cached, so a transient error is retried next run.

Keep the CLI up to date
-----------------------

[](#keep-the-cli-up-to-date)

When installed from the released PHAR:

```
secure-lock self-update          # download and install the latest release
secure-lock self-update --check  # only check, don't install
```

When installed via Composer:

```
composer global update jeffersongoncalves/secure-lock-cli
```

Development
-----------

[](#development)

```
composer install
composer test       # Pint lint + PHPStan + Pest tests
composer lint       # Auto-fix style
composer analyse    # PHPStan (level 6) static analysis
composer build      # Build the PHAR into builds/secure-lock
```

HTTP calls are mocked in the test suite with `Http::fake()`. Fixture lockfiles created during tests live under `tests/tmp/` (gitignored).

Release
-------

[](#release)

1. Merge changes to `main` — CI builds a fresh `builds/secure-lock` against the latest tag and commits it back.
2. Create a new GitHub release (tag `X.Y.Z`, no `v` prefix).
3. The `publish-phar.yml` workflow attaches `secure-lock.phar` to the release and `update-changelog.yml` updates `CHANGELOG.md` + `version.txt`.

Roadmap
-------

[](#roadmap)

The core feature set is complete (multi-ecosystem audit, safe-update classification, `--fix`, redundant advisory fallbacks, SARIF). Ideas welcome via issues.

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance92

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 95.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

13

Last Release

49d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/411493?v=4)[Jefferson Gonçalves](/maintainers/jeffersongoncalves)[@jeffersongoncalves](https://github.com/jeffersongoncalves)

---

Top Contributors

[![jeffersongoncalves](https://avatars.githubusercontent.com/u/411493?v=4)](https://github.com/jeffersongoncalves "jeffersongoncalves (69 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (3 commits)")

---

Tags

advisoriesclicomposerdependency-auditlaravel-zeronpmphpsecuritysupply-chainvulnerability-scannercomposerclisecurityAuditnpmvulnerabilitiesadvisoriessupply-chain

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/jeffersongoncalves-secure-lock-cli/health.svg)

```
[![Health](https://phpackages.com/badges/jeffersongoncalves-secure-lock-cli/health.svg)](https://phpackages.com/packages/jeffersongoncalves-secure-lock-cli)
```

###  Alternatives

[yoeunes/regex-parser

A powerful PCRE regex parser with lexer, AST builder, validation, ReDoS analysis, and syntax highlighting. Zero dependencies, blazing fast, and production-ready.

30101.0k8](/packages/yoeunes-regex-parser)[lowerends/laravel-security-checker

The Symfony Security Advisories Checker for Laravel

301.5k](/packages/lowerends-laravel-security-checker)[mwguerra/interactive-upgrader

A CLI tool that handles both Composer and npm dependencies for Laravel projects.

2115.1k](/packages/mwguerra-interactive-upgrader)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
