jeffersongoncalves/secure-lock-cli
GitHub: jeffersongoncalves/secure-lock-cli
安全锁命令行工具,用于审计依赖项漏洞并判断更新安全性。
Stars: 1 | Forks: 0

# 安全锁命令行工具
`secure-lock` 对项目的依赖项进行审计,并为每个包回答三个问题:
1. **是否有更新的版本?**(注册表查找)
2. **安装的版本是否存在漏洞?**(咨询数据库查找)
3. **可用的更新实际上是否安全?** — 即目标版本是否真的离开了漏洞范围,而不仅仅是更新?
该工具源于最近一波供应链攻击(Composer 和 npm 上的受损害/漏洞包),它将 *有用的升级,修复了漏洞* 与 *无用的升级,仍然暴露* 区分开来。
它涵盖了 **Composer** (`composer.lock`) 和共享 npm 生态系统的 JavaScript 管理器:**npm** (`package-lock.json` v1/v2/v3 和 `npm-shrinkwrap.json`),**pnpm** (`pnpm-lock.yaml` lockfileVersion 5/6/9),**bun** (`bun.lock` 文本锁文件) 和 **yarn** (`yarn.lock` 经典 v1 和 berry v2+)。`ECO` 列显示了包来源的实际管理器,而咨询和建议与共享 npm 生态系统进行解析。
使用 [Laravel Zero](https://laravel-zero.com) 构建,并模仿了此 monorepo 中的其他 CLIs。

每个包都会与注册表和 GitHub 咨询数据库进行核对,然后进行分类 — 这里六个包已发布修复(`SAFE`),一个只有更新的版本(`UPDATE`)。
## 要求
- PHP `^8.2`
- 一个 `composer.lock` 和/或一个 `package-lock.json` 以进行审计
- 可选的 `GITHUB_TOKEN`(将 GitHub 咨询 API 速率限制从 ~60 req/h 提高到 5000 req/h)
## 安装
### 全局(推荐)
```
composer global require jeffersongoncalves/secure-lock-cli
```
只要 Composer 的全局 `vendor/bin` 在其中,二进制 `secure-lock` 就会在您的 `PATH` 上。
### 从源
```
git clone https://github.com/jeffersongoncalves/secure-lock-cli.git
cd secure-lock-cli
composer install
```
## 使用
```
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
```
JavaScript 锁文件会根据优先级自动检测在项目目录中(`pnpm` > `bun` > `yarn` > `npm`);通过传递 `--pnpm`/`--bun`/`--yarn`/`--npm` 来显式指定一个。
### 选项
```
--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)
```
## 判决
对于每个包,该工具会将针对 **当前** 版本的咨询与仍然针对 **最新** 版本的咨询进行比较:
| 判决 | 徽章 | 意义 |
|---------|-------|---------|
| `VULN` | `● VULN` (红色) | 现在存在漏洞,没有发布的修复 |
| `RISKY_UPDATE` | `● RISKY` (洋红色) | 存在更新但仍然暴露 |
| `UNKNOWN` | `● UNKNOWN` (黄色) | 咨询查找失败 — 状态未验证 |
| `SAFE_UPDATE` | `● SAFE` (绿色) | 更新 **修复** 漏洞 |
| `UPDATE` | `● UPDATE` (青色) | 更新版本,没有已知漏洞 |
| `OK` | `● OK` (灰色) | 最新且干净 |
该表按风险排序(`VULN` > `RISKY` > `UNKNOWN` > `SAFE` > `UPDATE` > `OK`),而 `NOTE` 列显示最高严重性的咨询为 `SEVERITY CVE-XXXX (+N)`(如果存在,则为 CVE,否则为 GHSA id)。
## 修复
通过传递 `--fix` 来打印每个当前存在漏洞的包的每个管理器的升级命令。目标是大于已安装版本的最小 **安全** 版本,而不是最新的版本,这样升级是最小的且经过验证的 — 而不仅仅是“最新的”:

注意目标是最小的安全版本,而不是最新的:
`guzzlehttp/guzzle` 升级到 `6.5.8`(不是 `7.10.5`),而 `phpunit` 升级到 `9.6.33`。
**直接与间接。** 直接依赖项会得到一个简单的 `add`/`require`/`install`。一个 *间接* 依赖项不能以这种方式固定 — 一个 `npm install` 不会达到嵌套版本 — 因此建议使用管理器的覆盖机制:`overrides`(npm/bun)、`pnpm.overrides`(pnpm)或 `resolutions`(yarn)在 `package.json` 中。Composer 总是使用 `composer require`,这也固定了间接包。
没有版本可以离开漏洞范围的包(`VULN`)将被跳过。
在 `--json` 模式下,每个包都会获得一个 `fix` 对象(`{target, command, transitive}`)或 `null`。
## 抑制咨询
否则,已接受或无法修复的风险将永远使 CI 失败。传递一个或多个 `--ignore` 标志,或将 `secure-lock.json` 提交到项目根目录(自动检测,或使用 `--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
```
被忽略的咨询不再计入判决。带有 `expires` 日期的条目在日期过后停止抑制,因此延迟的风险会重新出现而不是被遗忘。
## GitHub 代码扫描(SARIF)
`--sarif` 输出 SARIF 2.1.0,GitHub 可以将其导入以在存储库的 **Security › Code scanning** 选项卡中显示结果:
```
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
```
## 退出代码(用于 CI)
- `0` — 没有关键风险。
- `1` — 找到 `VULN` 或 `RISKY_UPDATE`(使管道失败)。
- `2` — 输入错误(找不到锁文件、无效的 JSON 等)。
### GitHub 动作
```
- run: secure-lock --no-dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
## 工作原理
- **注册表** — Composer:`repo.packagist.org/p2/{name}.json`,遍历每个版本并忽略预发布版本(`alpha|beta|rc|dev|next|canary|nightly`)以找到最高的稳定版本。npm:`registry.npmjs.org/{name}`(`/` 编码为 `%2F` 以用于范围包),使用 `dist-tags.latest`。
- **咨询** — GitHub 咨询数据库 REST API:`GET /advisories?affects={name}&ecosystem={eco}`。`affects` 参数接收 **只有包名称**(例如 `guzzlehttp/guzzle`) — 生态系统在其自己的参数中。前缀它(`composer:...`)返回 `200`,但列表静默为空。
- **冗余回退** — 当包的 GitHub 查找失败(例如,没有令牌的速率限制)时,会查询第二个来源,以便恢复结果而不是留下 `UNKNOWN`:Composer 的 **Packagist Security Advisories API** 和 npm/pnpm/bun/yarn 的 **npm audit** 批量端点。每个都是针对所有失败的包的一个批量请求。因此,可以不使用任何令牌就对每个生态系统进行审计。使用 `--no-packagist`/`--no-npm-audit` 禁用。
- **Semver** — 比较和范围满足使用 `composer/semver`,包括 GHSA 范围,其中逗号表示逻辑 AND(例如 `>= 7.0.0, < 7.4.5`)。
- **并发** — 每个包的注册表和咨询查找都会与 `Http::pool`(上限)并发执行,因此大型锁文件可以分几波审计,而不是一次请求一次。
- **缓存** — 注册表/咨询响应会以可配置的 TTL 存储到磁盘上,以便重复运行不会触碰到 API 速率限制。失败的查找永远不会缓存,因此下一次运行会重试暂时性错误。
## 保持 CLI 更新
从发布的 PHAR 安装时:
```
secure-lock self-update # download and install the latest release
secure-lock self-update --check # only check, don't install
```
通过 Composer 安装时:
```
composer global update jeffersongoncalves/secure-lock-cli
```
## 开发
```
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::fake()` 模拟 HTTP 调用。在测试期间创建的固定锁文件位于 `tests/tmp/`(git 忽略)。
## 发布
1. 合并更改到 `main` — CI 构建 `builds/secure-lock` 对最新的标签,并将其提交回。
2. 创建一个新的 GitHub 发布(标签 `X.Y.Z`,没有 `v` 前缀)。
3. `publish-phar.yml` 工作流程将 `secure-lock.phar` 附带到发布,并 `update-changelog.yml` 更新 `CHANGELOG.md` + `version.txt`。
## 路线图
核心功能集已完整(多生态系统审计、安全更新分类、`--fix`、冗余咨询回退、SARIF)。欢迎通过问题提出想法。标签:Composer, ffuf, GitHub Advanced Security, GitHub API, Laravel Zero, npm, 供应链攻击, 包管理, 反取证, 安全修复, 安全加固, 安全可观测性, 安全响应, 安全扫描, 安全测试, 安全漏洞数据库, 安全评估, 攻击性安全, 时序注入, 暗色界面, 版本控制, 软件更新, 软件漏洞