@yottameta/yotta-chain 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-08-27)
4
+
5
+ - 初始版本:零依赖供应链依赖校验引擎(Python 3.8+ 标准库,纯本地离线)。
6
+ - 生态:npm(package.json + package-lock v1/v2/v3 + npm-shrinkwrap + .npmrc 作用域仓库)、
7
+ Python(requirements*.txt + pyproject.toml(PEP 621 / poetry)+ poetry.lock + Pipfile / Pipfile.lock)、
8
+ Maven(pom.xml 基础:未固定版本 / SNAPSHOT / 可疑仓库 URL)。
9
+ - 检测:
10
+ - 依赖混淆:scope 私有仓库配置 vs 实际解析仓库、同一包多仓库混合、可疑仓库 URL(http / IP / localhost)、
11
+ pip extra-index 公共回退、poetry secondary 源、Pipfile 公私源混配;
12
+ - lockfile 一致性:清单条目缺失 / 版本范围不满足(npm semver + PEP 440)/ 根信息不一致 /
13
+ 悬空引用 / 缺 integrity / 同版本多来源冲突;
14
+ - 卫生:缺失锁文件 / 未固定版本(* / latest)/ Maven SNAPSHOT;
15
+ - typo-squat:依赖名与知名 npm / PyPI 包编辑距离 ≤ 2 提示。
16
+ - SBOM-lite:CycloneDX 1.5 子集 JSON(components + dependencies + purl,scope / direct / resolved / integrity)。
17
+ - 输出:text / JSON / CSV;scan 退出码 0(干净)/ 1(有发现)/ 4(错误),--gate 可调 CI 闸门。
18
+ - 测试:52/52 全绿。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 YottaMeta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,11 @@
1
+ # NOTICE — YottaMeta 品牌声明
2
+
3
+ 「YottaMeta」「元链」「yotta-chain」「元察」「yotta-logwatch」「元情」「yotta-intel」「元钥」「yotta-secret」「元史」「yotta-logs」「元忆」「yotta-memory」以及本家族各技能名称(yotta-* 前缀)是 YottaMeta 的品牌与标识。
4
+
5
+ 本软件以 MIT 许可证开源,任何人均可自由使用、修改与分发。若你在其基础上制作派生作品:
6
+
7
+ 1. 不得继续使用 YottaMeta 或本家族名称(yotta-*、元链、元察、元情、元钥、元史、元忆 等)作为派生作品的名称;
8
+ 2. 不得暗示派生作品由 YottaMeta 官方维护、认可或与之存在关联;
9
+ 3. 建议在派生作品中明确声明「与 YottaMeta 官方无关联」。
10
+
11
+ 上游来源致谢:本技能由 YottaMeta 全新实现(零依赖自研 + 中文教学),供应链依赖校验方向参考开源社区 supply-chain 类技能(如 dependency-confusion / lockfile 审计 / SBOM 的检测思路)与 kali-claw 防御素材库的域映射(MIT,OpenClaw Contributors),无上游代码。
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ <p align="center"><b>Language</b>: English · <a href="./README.zh-CN.md">中文</a></p>
2
+
3
+ <p align="center">
4
+ <img src="assets/banner.png" alt="yotta-chain banner" width="100%" />
5
+ </p>
6
+
7
+ <h1 align="center">yotta-chain · 元链 (Yuanlian)</h1>
8
+
9
+ <p align="center">YottaMeta's zero-dependency <b>supply-chain dependency validator</b>: detects <b>dependency confusion, lockfile inconsistencies, missing lockfiles, unpinned versions and typosquatting</b> across npm / Python / Maven by parsing manifests and lockfiles locally, and generates <b>SBOM-lite (CycloneDX 1.5 subset)</b>.</p>
10
+ <p align="center">Activates when the user needs to check a project's dependencies for supply-chain risks before build / release / CI, verify a lockfile matches its manifest, assess dependency-confusion exposure, or generate an SBOM — <b>fully local and offline: no online CVE lookups, no package-registry queries, no data leaves the machine</b>.</p>
11
+ <p align="center">No external tools required (Python 3.8+ standard library); Windows + Linux + macOS; every finding ships a severity, a plain-language explanation and a fix hint.</p>
12
+
13
+ <p align="center">
14
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue" /></a>
15
+ <a href="https://agentskills.io/"><img alt="Standard: agentskills.io" src="https://img.shields.io/badge/standard-agentskills.io-orange" /></a>
16
+ <a href="https://www.npmjs.com/package/@yottameta/yotta-chain"><img alt="npm package" src="https://img.shields.io/npm/v/@yottameta/yotta-chain" /></a>
17
+ <a href="https://github.com/YottaMeta/yotta-chain"><img alt="GitHub stars" src="https://img.shields.io/github/stars/YottaMeta/yotta-chain" /></a>
18
+ <a href="https://github.com/YottaMeta/yotta-chain/commits/main"><img alt="last commit" src="https://img.shields.io/github/last-commit/YottaMeta/yotta-chain" /></a>
19
+ <a href="https://github.com/YottaMeta/yotta-chain"><img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen" /></a>
20
+ </p>
21
+
22
+ ## What it is
23
+
24
+ Supply-chain attacks target the parts developers trust: dependency confusion (a private package name is squatted on a public registry), typosquatting, stale or hand-edited lockfiles, missing integrity hashes. Yuanlian packages "supply-chain triage" into a zero-dependency engine that reads your manifests and lockfiles **locally** — no Trivy / Snyk / npm audit needed.
25
+
26
+ It is agent-agnostic and works in any agent supporting Agent Skills. **Fully local and offline** — no online CVE databases, no registry queries, no data leaves the machine.
27
+
28
+ ## Core value
29
+
30
+ - **Zero-dependency engine** — npm semver + PEP 440 range checks, TOML / JSON / requirements parsers, all built with Python 3.8+ standard library.
31
+ - **Dependency confusion** — scope registry configured in `.npmrc` but resolved from a public registry; the same package resolved from multiple registries; suspicious registry URLs (http / IP literal / localhost); pip extra-index and poetry secondary-source public fallback.
32
+ - **Lockfile consistency** — manifest entry missing from lockfile, locked version out of the declared range, root name / version mismatch, dangling references, missing integrity, same version with conflicting sources.
33
+ - **Hygiene** — missing lockfile, unpinned versions (`*` / `latest` / no constraint), Maven SNAPSHOT dependencies.
34
+ - **Typosquatting** — dependency names within edit distance 2 of well-known npm / PyPI packages are flagged for manual review.
35
+ - **SBOM-lite** — CycloneDX 1.5 subset JSON (components + dependencies + purl, with scope / direct / resolved / integrity as properties).
36
+ - **Three output modes** — text / JSON / CSV, plus a `--gate` exit-code gate for CI.
37
+
38
+ ## Why use it
39
+
40
+ | Advantage | Description |
41
+ |---|---|
42
+ | **Zero dependency** | Python 3.8+ standard library; no daemon / database / external scanner; Windows + Linux + macOS |
43
+ | **Fully local offline** | Parses existing manifests / lockfiles only; no online CVE lookups, no registry queries, no data leaves the machine |
44
+ | **Deterministic signals** | Registry config vs actual resolution, range math (npm semver / PEP 440), integrity presence — not a random URL list |
45
+ | **CI-friendly** | `scan --gate high` exits 1 only when findings reach a chosen severity |
46
+ | **Teaching layer** | Every rule ships a Chinese plain-language explanation and a fix hint |
47
+
48
+ ## Commands
49
+
50
+ | Command | Purpose |
51
+ |---|---|
52
+ | `scan` | Validate a project directory (auto-detect npm / python / maven) |
53
+ | `sbom` | Generate SBOM-lite (CycloneDX 1.5 subset JSON or text) |
54
+ | `version` | Print version |
55
+
56
+ `scan` exit codes: **0** = no findings at or above `--gate`; **1** = findings; **4** = usage / path / unsupported-manifest error. Default `--gate=info` (any finding exits 1); use `--gate high` for a stricter CI gate.
57
+
58
+ ## Quick start
59
+
60
+ Windows: use `python`; Linux/macOS: use `python3`.
61
+
62
+ ```bash
63
+ # scan the current project (auto-detects npm / python / maven)
64
+ python3 scripts/yotta_chain.py scan --path ./
65
+
66
+ # only medium and above, JSON output
67
+ python3 scripts/yotta_chain.py scan --path ./src --level medium --format json
68
+
69
+ # CI gate: exit 1 only when high-severity findings exist
70
+ python3 scripts/yotta_chain.py scan --path . --gate high; echo $?
71
+
72
+ # generate SBOM-lite (CycloneDX 1.5 subset JSON)
73
+ python3 scripts/yotta_chain.py sbom --path . --output sbom.json
74
+
75
+ # view the SBOM as text
76
+ python3 scripts/yotta_chain.py sbom --path . --format text
77
+
78
+ # version
79
+ python3 scripts/yotta_chain.py version
80
+ ```
81
+
82
+ ## Installation
83
+
84
+ Install the skill into your agent:
85
+
86
+ | Method | Command |
87
+ |---|---|
88
+ | npm (recommended) | `npx -y @yottameta/yotta-chain --agent codex` (or `--dir <path>` / `-g`) |
89
+ | Shell script | `bash install.sh --agent <name>` (see `bash install.sh --list`) |
90
+ | Manual | Copy the skill folder into your agent's skills directory |
91
+
92
+ Installing into a project: run `npx -y @yottameta/yotta-chain` or `bash install.sh` inside the project to install into the detected project-level directory.
93
+
94
+ ## Usage with an AI agent
95
+
96
+ Tell the agent the context, e.g.:
97
+
98
+ ```text
99
+ Before we release, run yotta-chain scan on the repo (gate high) and summarize the findings with fix suggestions.
100
+ ```
101
+
102
+ The agent runs the engine, reports findings by severity, and explains each rule with the Chinese plain-language guidance in `references/rules.md`.
103
+
104
+ ## Detection rules
105
+
106
+ | Rule | Severity | What it means |
107
+ |---|---|---|
108
+ | `confusion_scope_registry` | high | Scope has a private registry in `.npmrc`, but the lockfile resolves it from a public registry |
109
+ | `confusion_mixed_registry` | high | The same package is resolved from multiple registry hosts |
110
+ | `lockfile_missing_entry` | high | A manifest dependency is absent from the lockfile |
111
+ | `lockfile_range_unsatisfied` | high | Locked version does not satisfy the declared range (npm semver / PEP 440) |
112
+ | `lockfile_dangling_ref` | high | A lockfile package depends on a package not present in the lockfile |
113
+ | `lockfile_duplicate_conflict` | high | Same name + version with conflicting resolved / integrity sources |
114
+ | `missing_lockfile` | medium | Dependencies declared but no lockfile committed |
115
+ | `lockfile_root_mismatch` | medium | Lockfile root name / version differs from the manifest |
116
+ | `lockfile_integrity_missing` | medium | Lockfile entry has no integrity / hash |
117
+ | `confusion_extra_index` | medium | pip / poetry / pipenv mix a public registry with a private one (public becomes a fallback) |
118
+ | `confusion_suspicious_registry` | medium | Registry / index URL is http, an IP literal or a localhost address |
119
+ | `confusion_registry_mismatch` | medium | A private default registry is configured but packages resolve from a public registry |
120
+ | `unpinned` | low / medium | Dependency has no fixed version (`*` / `latest` / no constraint) |
121
+ | `typosquat` | low | Name within edit distance 2 of a well-known package — review manually |
122
+ | `snapshot` | low | Maven dependency uses a SNAPSHOT version |
123
+
124
+ ## Supported ecosystems (v0.1.0)
125
+
126
+ - **npm** — `package.json` + `package-lock.json` (v1 / v2 / v3) / `npm-shrinkwrap.json` + `.npmrc` (per-scope registries);
127
+ - **Python** — `requirements*.txt` (with `--index-url` / `--extra-index-url` / `-r` recursion), `pyproject.toml` (PEP 621 / poetry), `poetry.lock`, `Pipfile` / `Pipfile.lock`;
128
+ - **Maven** — `pom.xml` (basic: unpinned / SNAPSHOT / suspicious repository URLs / property + dependencyManagement resolution).
129
+ - `yarn.lock` / `pnpm-lock.yaml` / `go.mod` / `Cargo.lock` are not yet supported in v0.1.0 (see CHANGELOG).
130
+
131
+ ## Boundaries
132
+
133
+ - Reads local files only; no networking, no CVE databases, no package-registry queries, no data leaves the machine.
134
+ - No online CVE comparison — that is the domain of Trivy / Snyk / npm audit; this engine provides local deterministic parsing and heuristic signals.
135
+ - Dependency-confusion detection is a **local approximation**: confirming "a private name is squatted publicly" needs an online check; the engine emits strong signals for manual review.
136
+ - Read-only; it never edits lockfiles or upgrades dependencies.
137
+
138
+ ## Development & validation
139
+
140
+ ```bash
141
+ python3 -m py_compile scripts/yotta_chain.py
142
+ python3 scripts/test_yotta_chain.py # 52/52
143
+ ```
144
+
145
+ ## Changelog
146
+
147
+ Version history lives in [CHANGELOG.md](./CHANGELOG.md).
148
+
149
+ ## License
150
+
151
+ [MIT](./LICENSE)
@@ -0,0 +1,151 @@
1
+ <p align="center"><b>Language</b>: <a href="./README.md">English</a> · 中文</p>
2
+
3
+ <p align="center">
4
+ <img src="assets/banner.png" alt="yotta-chain banner" width="100%" />
5
+ </p>
6
+
7
+ <h1 align="center">yotta-chain · 元链 (Yuanlian)</h1>
8
+
9
+ <p align="center">YottaMeta 的零依赖供应链依赖校验引擎:本地解析 npm / Python / Maven 的依赖清单与锁文件,检测<b>依赖混淆、lockfile 不一致、缺失锁文件、未固定版本、typo-squat</b>,并生成 <b>SBOM-lite(CycloneDX 1.5 子集)</b>。</p>
10
+ <p align="center">触发场景:构建 / 发布 / CI 前检查项目依赖是否存在供应链风险、核对锁文件与清单是否一致、排查依赖混淆暴露面、生成 SBOM。</p>
11
+ <p align="center">纯 Python 3.8+ 标准库实现,零外部依赖,Windows + Linux + macOS 通用;纯本地离线——不做在线 CVE 比对、不查询公共包仓库、不发送任何数据。</p>
12
+
13
+ <p align="center">
14
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue" /></a>
15
+ <a href="https://agentskills.io/"><img alt="Standard: agentskills.io" src="https://img.shields.io/badge/standard-agentskills.io-orange" /></a>
16
+ <a href="https://www.npmjs.com/package/@yottameta/yotta-chain"><img alt="npm package" src="https://img.shields.io/npm/v/@yottameta/yotta-chain" /></a>
17
+ <a href="https://github.com/YottaMeta/yotta-chain"><img alt="GitHub stars" src="https://img.shields.io/github/stars/YottaMeta/yotta-chain" /></a>
18
+ <a href="https://github.com/YottaMeta/yotta-chain/commits/main"><img alt="last commit" src="https://img.shields.io/github/last-commit/YottaMeta/yotta-chain" /></a>
19
+ <a href="https://github.com/YottaMeta/yotta-chain"><img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen" /></a>
20
+ </p>
21
+
22
+ ## 这是什么
23
+
24
+ 供应链攻击瞄准的是开发者默认信任的部分:依赖混淆(私有包名被攻击者在公共仓库同名抢占)、typo-squat 仿冒包、过期或被手工改动的锁文件、缺失完整性哈希。元链把「供应链排查」打包成零依赖引擎,**本地**读取你的清单与锁文件——不需要 Trivy / Snyk / npm audit。
25
+
26
+ 它与任何平台无关,是智能体无关的工具包,支持 Agent Skills 的智能体都能调用。**纯本地离线**——不做在线 CVE 比对、不查询包仓库、不发送任何数据。
27
+
28
+ ## 核心价值
29
+
30
+ - **零依赖引擎**——npm semver + PEP 440 版本范围判定、TOML / JSON / requirements 解析器,全部用 Python 3.8+ 标准库实现。
31
+ - **依赖混淆**——.npmrc 里配置了私有仓库的 scope 却从公共仓库解析;同一包被多个仓库解析;可疑仓库地址(http / IP 字面量 / 本机);pip extra-index 与 poetry secondary 源造成的公共回退。
32
+ - **lockfile 一致性**——清单条目在锁文件缺失、锁定版本超出声明范围、根 name / version 不一致、悬空引用、缺 integrity、同版本多来源冲突。
33
+ - **卫生**——缺失锁文件、未固定版本(`*` / `latest` / 无约束)、Maven SNAPSHOT 依赖。
34
+ - **typo-squat**——依赖名与知名 npm / PyPI 包编辑距离 ≤ 2 时提示人工复核。
35
+ - **SBOM-lite**——CycloneDX 1.5 子集 JSON(components + dependencies + purl,scope / direct / resolved / integrity 作为属性)。
36
+ - **三种输出**——text / JSON / CSV,外加 `--gate` 退出码闸门供 CI 使用。
37
+
38
+ ## 为什么用它
39
+
40
+ | 优势 | 说明 |
41
+ |---|---|
42
+ | **零依赖** | Python 3.8+ 标准库;无常驻服务 / 数据库 / 外部扫描器;Windows + Linux + macOS |
43
+ | **纯本地离线** | 只解析已存在的清单 / 锁文件;不做在线 CVE 比对、不查询仓库、不发送任何数据 |
44
+ | **确定性信号** | 仓库配置 vs 实际解析来源、版本范围数学(npm semver / PEP 440)、完整性存在性——不是随机 URL 清单 |
45
+ | **CI 友好** | `scan --gate high` 只在达到指定严重度时退出 1 |
46
+ | **教学层** | 每条规则都带中文直白解释与修复提示 |
47
+
48
+ ## 命令
49
+
50
+ | 命令 | 用途 |
51
+ |---|---|
52
+ | `scan` | 校验项目目录(自动识别 npm / python / maven) |
53
+ | `sbom` | 生成 SBOM-lite(CycloneDX 1.5 子集 JSON 或文本) |
54
+ | `version` | 显示版本 |
55
+
56
+ `scan` 退出码:**0** = 未发现达到 `--gate` 级别的风险;**1** = 有发现;**4** = 用法 / 路径 / 无受支持清单错误。默认 `--gate=info`(任何发现即退出 1);CI 可用 `--gate high` 收紧。
57
+
58
+ ## 快速使用
59
+
60
+ Windows 用 python,Linux/macOS 用 python3。
61
+
62
+ ```bash
63
+ # 扫描当前项目(自动识别 npm / python / maven)
64
+ python3 scripts/yotta_chain.py scan --path ./
65
+
66
+ # 只看 medium 及以上,JSON 输出
67
+ python3 scripts/yotta_chain.py scan --path ./src --level medium --format json
68
+
69
+ # CI 闸门:达到 high 才退出码 1
70
+ python3 scripts/yotta_chain.py scan --path . --gate high; echo $?
71
+
72
+ # 生成 SBOM-lite(CycloneDX 1.5 子集 JSON)
73
+ python3 scripts/yotta_chain.py sbom --path . --output sbom.json
74
+
75
+ # 文本形式查看 SBOM
76
+ python3 scripts/yotta_chain.py sbom --path . --format text
77
+
78
+ # 版本
79
+ python3 scripts/yotta_chain.py version
80
+ ```
81
+
82
+ ## 安装
83
+
84
+ 把技能装进你的智能体:
85
+
86
+ | 方式 | 命令 |
87
+ |---|---|
88
+ | npm(推荐) | `npx -y @yottameta/yotta-chain --agent codex`(或 `--dir <路径>` / `-g`) |
89
+ | 脚本 | `bash install.sh --agent <名称>`(`bash install.sh --list` 查看支持的智能体) |
90
+ | 手动 | 把技能目录复制到智能体的 skills 目录 |
91
+
92
+ 装到项目:在项目内运行 `npx -y @yottameta/yotta-chain` 或 `bash install.sh`,会装到检测到的项目级目录。
93
+
94
+ ## 让智能体使用
95
+
96
+ 给智能体下指令即可,例如:
97
+
98
+ ```text
99
+ 发布前用 yotta-chain scan --gate high 扫一遍仓库,按严重度汇总发现并给出修复建议。
100
+ ```
101
+
102
+ 智能体会运行引擎、按严重度汇报发现,并用 `references/rules.md` 里的中文说明逐条解释。
103
+
104
+ ## 检测规则
105
+
106
+ | 规则 | 严重度 | 说明 |
107
+ |---|---|---|
108
+ | `confusion_scope_registry` | high | .npmrc 为某 scope 配置私有仓库,锁文件却从公共仓库解析(依赖混淆) |
109
+ | `confusion_mixed_registry` | high | 同一包被解析自多个不同仓库主机 |
110
+ | `lockfile_missing_entry` | high | 清单声明的依赖在锁文件中缺失 |
111
+ | `lockfile_range_unsatisfied` | high | 锁定版本不满足声明范围(npm semver / PEP 440) |
112
+ | `lockfile_dangling_ref` | high | 锁文件某包依赖的包不在锁文件包列表 |
113
+ | `lockfile_duplicate_conflict` | high | 同名同版本存在多个不同 resolved / integrity 来源 |
114
+ | `missing_lockfile` | medium | 声明了依赖但没有锁文件 |
115
+ | `lockfile_root_mismatch` | medium | 锁文件根 name / version 与清单不一致 |
116
+ | `lockfile_integrity_missing` | medium | 锁文件条目缺少 integrity / 哈希 |
117
+ | `confusion_extra_index` | medium | pip / poetry / pipenv 同时配置公共仓库与私有源(公共成为回退源) |
118
+ | `confusion_suspicious_registry` | medium | 仓库 / 索引地址是 http、IP 字面量或本机地址 |
119
+ | `confusion_registry_mismatch` | medium | 配置了私有默认仓库,包却从公共仓库解析 |
120
+ | `unpinned` | low / medium | 依赖未固定版本(`*` / `latest` / 无约束) |
121
+ | `typosquat` | low | 名字与知名包编辑距离 ≤ 2,疑似拼写仿冒 |
122
+ | `snapshot` | low | Maven 依赖使用 SNAPSHOT 版本 |
123
+
124
+ ## 支持的生态(v0.1.0)
125
+
126
+ - **npm** — `package.json` + `package-lock.json`(v1 / v2 / v3)/ `npm-shrinkwrap.json` + `.npmrc`(作用域仓库映射);
127
+ - **Python** — `requirements*.txt`(含 `--index-url` / `--extra-index-url` / `-r` 递归)、`pyproject.toml`(PEP 621 / poetry)、`poetry.lock`、`Pipfile` / `Pipfile.lock`;
128
+ - **Maven** — `pom.xml`(基础:未固定版本 / SNAPSHOT / 可疑仓库 URL / 属性与 dependencyManagement 解析)。
129
+ - `yarn.lock` / `pnpm-lock.yaml` / `go.mod` / `Cargo.lock` v0.1.0 暂不支持(见 CHANGELOG)。
130
+
131
+ ## 边界
132
+
133
+ - 只读本地文件;不联网、不做在线 CVE 比对、不查询包仓库、不发送任何数据。
134
+ - 不做在线 CVE 比对——那是 Trivy / Snyk / npm audit 的地盘;本引擎提供本地确定性解析与启发式信号。
135
+ - 依赖混淆检测是**本地近似**:真正确认「私有包名被公共仓库抢占」需要在线核对,引擎给出强信号供人工复核。
136
+ - 只读不写:绝不改锁文件、不升级依赖。
137
+
138
+ ## 开发与校验
139
+
140
+ ```bash
141
+ python3 -m py_compile scripts/yotta_chain.py
142
+ python3 scripts/test_yotta_chain.py # 52/52
143
+ ```
144
+
145
+ ## Changelog
146
+
147
+ 版本历史见 [CHANGELOG.md](./CHANGELOG.md)。
148
+
149
+ ## License
150
+
151
+ [MIT](./LICENSE)
package/SKILL.md ADDED
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: yotta-chain
3
+ version: 0.1.0
4
+ description: 元链 —— 跨智能体的供应链依赖校验技能:零依赖自研引擎本地解析 npm(package.json / package-lock v1-v3 / .npmrc)与 Python(requirements / pyproject.toml / poetry.lock / Pipfile)及 Maven pom.xml,检测依赖混淆(私有包名被公共仓库同名抢占 / 混合仓库 / 可疑仓库 URL / extra-index 回退)、lockfile 与清单不一致、缺失锁文件、未固定版本、typo-squat 仿冒命名,并生成 SBOM-lite(CycloneDX 1.5 子集)。触发:用户要在构建 / 发布 / CI 前检查项目依赖是否存在供应链风险、核对锁文件与清单是否一致、排查依赖混淆风险或生成 SBOM 时。边界:纯本地离线解析,不做在线 CVE 比对、不查询公共包仓库、不发送任何数据;结果只是「需人工复核的风险信号」,是否真实需人工核实;仅用于已获授权 / 自有资产 / 教学环境。
5
+ license: MIT
6
+ ---
7
+
8
+ # 元链(yotta-chain)
9
+
10
+ 跨智能体的供应链依赖校验技能:零依赖自研引擎**本地解析** npm / Python / Maven 依赖清单与锁文件,
11
+ 检测**依赖混淆 / lockfile 一致性 / 缺失锁文件 / typo-squat** 四类供应链风险,
12
+ 并生成 **SBOM-lite(CycloneDX 1.5 子集)**。
13
+
14
+ 纯 Python 3.8+ 标准库实现,零外部依赖;Windows + Linux + macOS 通用。
15
+ **纯本地离线**:不做在线 CVE 比对、不查询公共包仓库、不发送任何数据。
16
+
17
+ ## 何时使用
18
+
19
+ - 构建 / 发布 / CI 前检查项目依赖是否存在供应链风险;
20
+ - 核对 package-lock.json / poetry.lock / Pipfile.lock 与清单是否一致;
21
+ - 排查依赖混淆暴露面(私有包名 / 混合仓库 / extra-index 公共回退);
22
+ - 生成 SBOM-lite 用于依赖清单审计与合规留痕。
23
+
24
+ **Do NOT trigger**:
25
+
26
+ - 不做在线 CVE 比对(snyk / trivy / npm audit / safety 的地盘);
27
+ - 不查询公共包仓库、不联网、不发送任何数据;
28
+ - 不自动改锁文件 / 升级依赖——发现后由人工处理;
29
+ - 不扫描他人系统;只解析**已存在**的本地依赖文件;
30
+ - 结果只是「风险信号」,是否真实需人工复核;仅用于已获授权 / 自有资产 / 教学环境。
31
+
32
+ ## 快速使用
33
+
34
+ Windows 用 python,Linux/macOS 用 python3。
35
+
36
+ ```bash
37
+ # 扫描项目目录(自动识别 npm / python / maven)
38
+ python3 scripts/yotta_chain.py scan --path ./
39
+
40
+ # 只看 medium 及以上,JSON 输出
41
+ python3 scripts/yotta_chain.py scan --path ./src --level medium --format json
42
+
43
+ # CI 闸门:达到 high 才退出码 1(默认 gate=info,任何发现即 1)
44
+ python3 scripts/yotta_chain.py scan --path . --gate high; echo $?
45
+
46
+ # 生成 SBOM-lite(CycloneDX 1.5 子集 JSON)
47
+ python3 scripts/yotta_chain.py sbom --path . --output sbom.json
48
+
49
+ # 文本形式查看 SBOM
50
+ python3 scripts/yotta_chain.py sbom --path . --format text
51
+
52
+ # 版本
53
+ python3 scripts/yotta_chain.py version
54
+ ```
55
+
56
+ 退出码:**scan 0** = 未发现达到 gate 级别的风险;**1** = 发现;**4** = 用法 / 路径 / 无受支持清单错误。
57
+
58
+ ## 检测规则一览
59
+
60
+ | 规则 | 严重度 | 说明 |
61
+ |---|---|---|
62
+ | confusion_scope_registry | high | .npmrc 为某 scope 配置私有仓库,但锁文件里该 scope 包实际解析自公共仓库(依赖混淆) |
63
+ | confusion_mixed_registry | high | 同一包在锁文件里被解析自多个不同仓库主机 |
64
+ | lockfile_missing_entry | high | 清单声明了依赖,锁文件里却没有 |
65
+ | lockfile_range_unsatisfied | high | 锁文件版本不满足清单声明的范围(npm semver / PEP 440) |
66
+ | lockfile_dangling_ref | high | 锁文件里某包依赖的包不存在于锁文件包列表 |
67
+ | lockfile_duplicate_conflict | high | 同一包同版本存在多个不同 resolved / integrity 来源 |
68
+ | missing_lockfile | medium | 声明了依赖但没有锁文件 |
69
+ | lockfile_root_mismatch | medium | 锁文件根 name / version 与清单不一致 |
70
+ | lockfile_integrity_missing | medium | 锁文件条目缺少 integrity / 哈希 |
71
+ | confusion_extra_index | medium | pip / poetry / pipenv 同时配置公共仓库与私有源(公共成为回退源) |
72
+ | confusion_suspicious_registry | medium | 仓库 / 索引地址为 http、IP 字面量或本机地址 |
73
+ | confusion_registry_mismatch | medium | 配置了私有默认仓库,但包实际解析自公共仓库 |
74
+ | unpinned | low / medium | 依赖未固定版本(npm `*` / `latest`、requirements 无约束、Maven 无 version) |
75
+ | typosquat | low | 依赖名与知名包编辑距离 ≤ 2,疑似拼写仿冒 |
76
+ | snapshot | low | Maven 依赖使用 SNAPSHOT 版本 |
77
+
78
+ 完整规则、判定逻辑与修复指引见 `references/rules.md`。
79
+
80
+ ## 支持的生态(v0.1.0)
81
+
82
+ - **npm**:package.json + package-lock.json(v1 / v2 / v3)/ npm-shrinkwrap.json + .npmrc(作用域仓库映射);
83
+ - **Python**:requirements*.txt(含 --index-url / --extra-index-url / -r 递归)、pyproject.toml(PEP 621 / poetry)、
84
+ poetry.lock、Pipfile / Pipfile.lock;
85
+ - **Maven**:pom.xml(基础:未固定版本 / SNAPSHOT / 可疑仓库 URL / dependencyManagement 属性解析)。
86
+ - yarn.lock / pnpm-lock.yaml / go.mod / Cargo.lock:v0.1.0 暂不支持,见 CHANGELOG 后续计划。
87
+
88
+ ## 与家族协同
89
+
90
+ - **元盾 yotta-guardian**:供应链检查结果可作为 CI 闸门(--gate),在发布 / 合并前拦截高风险依赖;
91
+ - **元钥 yotta-secret**:同一仓库先查硬编码密钥,再查依赖供应链风险;
92
+ - **元察 yotta-logwatch**:运行时异常日志与供应链风险交叉印证。
93
+
94
+ ## 边界
95
+
96
+ - 只读本地文件;不联网、不查询 CVE 库 / 包仓库、不发送任何数据;
97
+ - 不做在线 CVE 比对(那是 snyk / trivy / npm audit 的地盘),只做本地确定性解析与启发式信号;
98
+ - 依赖混淆检测是**本地近似**:真正确认「私有包名被公共仓库抢占」需要在线核对,本引擎给的是强信号 + 人工复核;
99
+ - 不自动修复;发现后由人工处理。
100
+
101
+ ## 开发与校验
102
+
103
+ ```bash
104
+ python3 -m py_compile scripts/yotta_chain.py
105
+ python3 scripts/test_yotta_chain.py # 52/52
106
+ ```
107
+
108
+ ## Changelog
109
+
110
+ 版本历史见 `CHANGELOG.md`(本技能不内嵌版本历史表)。
Binary file
package/bin/install.js ADDED
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * yotta-chain 跨平台安装器(YottaSkills)
4
+ * 用法:
5
+ * npx -y @yottameta/yotta-chain --agent <name> # 按智能体默认用户级目录安装(推荐)
6
+ * npx -y @yottameta/yotta-chain --dir PATH # 装到指定目录(用户改了目录的智能体)
7
+ * npx -y @yottameta/yotta-chain -g # 安装到全部已知智能体用户级目录
8
+ * npx -y @yottameta/yotta-chain # 安装到检测到的项目级目录
9
+ * npx -y @yottameta/yotta-chain --list # 列出智能体 -> 默认目录
10
+ */
11
+ 'use strict';
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const os = require('os');
15
+
16
+ const SKILL_NAME = 'yotta-chain';
17
+ const PKG_ROOT = path.join(__dirname, '..');
18
+
19
+ // 智能体 -> 用户级默认技能目录(dirs 按优先级排列;--agent 装到第一个)
20
+ // 依据官方文档:.agents/skills 并非通用目录,被 OpenCode / Cursor / Cline / Amp /
21
+ // Kimi / Gemini CLI / GitHub Copilot 等读取;Claude Code 与 Codex 默认不读 .agents。
22
+ const AGENT_DIRS = {
23
+ claude: { label: 'Claude Code', dirs: ['.claude/skills'] },
24
+ cursor: { label: 'Cursor', dirs: ['.cursor/skills', '.agents/skills'] },
25
+ codex: { label: 'Codex', dirs: ['.codex/skills'] }, // 特判:$CODEX_HOME/skills
26
+ gemini: { label: 'Gemini CLI', dirs: ['.gemini/skills', '.agents/skills'] },
27
+ goose: { label: 'Goose', dirs: ['.config/goose/skills', '.agents/skills'] },
28
+ amp: { label: 'Amp', dirs: ['.config/agents/skills', '.agents/skills'] },
29
+ opencode: { label: 'OpenCode', dirs: ['.config/opencode/skills'] }, // 特判:$XDG_CONFIG_HOME
30
+ windsurf: { label: 'Windsurf', dirs: ['.codeium/windsurf/skills'] },
31
+ workbuddy: { label: 'WorkBuddy', dirs: ['.workbuddy/skills'] },
32
+ kiro: { label: 'Kiro', dirs: ['.kiro/skills'] },
33
+ trae: { label: 'Trae Code CLI', dirs: ['.traecli/skills'] },
34
+ 'trae-cn': { label: 'Trae IDE(国内)', dirs: ['.trae-cn/skills'] },
35
+ qwen: { label: 'Qwen Code', dirs: ['.qwen/skills'] },
36
+ comate: { label: 'Comate 文心快码', dirs: ['.comate/skills'] },
37
+ codebuddy: { label: 'CodeBuddy Code', dirs: ['.codebuddy/skills'] },
38
+ kimi: { label: 'Kimi Code CLI', dirs: ['.kimi/skills'] },
39
+ agents: { label: '通用 AGENTS.md', dirs: ['.agents/skills'] },
40
+ };
41
+
42
+ // Codex 用户级目录特判:优先 $CODEX_HOME/skills,否则 ~/.codex/skills
43
+ function codexUserDir() {
44
+ const base = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
45
+ return path.join(base, 'skills');
46
+ }
47
+
48
+ // OpenCode 用户级目录特判:优先 $XDG_CONFIG_HOME/opencode/skills,否则 ~/.config/opencode/skills
49
+ function opencodeUserDir() {
50
+ const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
51
+ return path.join(base, 'opencode', 'skills');
52
+ }
53
+
54
+ function resolveUserDir(rel) {
55
+ if (rel === '.codex/skills') return codexUserDir();
56
+ if (rel === '.config/opencode/skills') return opencodeUserDir();
57
+ return path.join(os.homedir(), rel);
58
+ }
59
+
60
+ function installTo(dest) {
61
+ const target = path.join(dest, SKILL_NAME);
62
+ fs.mkdirSync(target, { recursive: true });
63
+ copyDir(PKG_ROOT, target, new Set(['package.json', 'bin', 'node_modules', '.git']));
64
+ console.log('installed -> ' + target);
65
+ }
66
+
67
+ function copyDir(src, dst, skip) {
68
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
69
+ if (skip.has(entry.name)) continue;
70
+ const s = path.join(src, entry.name);
71
+ const d = path.join(dst, entry.name);
72
+ if (entry.isDirectory()) {
73
+ fs.mkdirSync(d, { recursive: true });
74
+ copyDir(s, d, skip);
75
+ } else if (entry.isFile()) {
76
+ fs.copyFileSync(s, d);
77
+ }
78
+ }
79
+ }
80
+
81
+ function displayDir(rel) {
82
+ if (process.platform === 'win32') return '%USERPROFILE%\\' + rel.replace(/\//g, '\\');
83
+ return '~/' + rel;
84
+ }
85
+
86
+ function main() {
87
+ const args = process.argv.slice(2);
88
+ const isGlobal = args.includes('-g') || args.includes('--global');
89
+ const list = args.includes('--list') || args.includes('-l');
90
+ let explicitDir = null;
91
+ const di = args.indexOf('--dir');
92
+ if (di !== -1 && args[di + 1]) explicitDir = args[di + 1];
93
+ let agent = null;
94
+ const ai = args.indexOf('--agent');
95
+ if (ai !== -1 && args[ai + 1]) agent = String(args[ai + 1]).toLowerCase();
96
+
97
+ if (list) {
98
+ console.log('智能体 -> 默认技能目录(--agent <name> 装到第一个,用户级):');
99
+ for (const [key, v] of Object.entries(AGENT_DIRS)) {
100
+ const resolved = v.dirs.map(displayDir);
101
+ console.log(' ' + key.padEnd(10) + v.label.padEnd(18) + resolved.join('、'));
102
+ }
103
+ console.log('\n说明:Windows 用 %USERPROFILE%,Linux/macOS 用 ~;仅收录有官方默认目录的智能体。');
104
+ console.log('改了目录的请用 --dir <路径>,不要依赖默认位置;若设置了 CODEX_HOME / XDG_CONFIG_HOME,安装自动以该变量为准。');
105
+ return;
106
+ }
107
+
108
+ if (explicitDir) { installTo(explicitDir); return; }
109
+
110
+ if (agent) {
111
+ const info = AGENT_DIRS[agent];
112
+ if (!info) {
113
+ console.log('未收录智能体: ' + agent + '。请用 --dir <路径> 指定技能目录。');
114
+ console.log('可用: ' + Object.keys(AGENT_DIRS).join(', '));
115
+ return;
116
+ }
117
+ installTo(resolveUserDir(info.dirs[0]));
118
+ console.log('完成。');
119
+ return;
120
+ }
121
+
122
+ if (isGlobal) {
123
+ const seen = new Set();
124
+ for (const v of Object.values(AGENT_DIRS)) {
125
+ for (const d of v.dirs) {
126
+ if (seen.has(d)) continue;
127
+ seen.add(d);
128
+ installTo(resolveUserDir(d));
129
+ }
130
+ }
131
+ console.log('完成。');
132
+ return;
133
+ }
134
+
135
+ const PROJECT_DIRS = [
136
+ '.claude/skills',
137
+ '.cursor/skills',
138
+ '.codex/skills',
139
+ '.config/goose/skills',
140
+ '.config/agents/skills',
141
+ '.opencode/skills',
142
+ '.codeium/windsurf/skills',
143
+ '.workbuddy/skills',
144
+ '.kiro/skills',
145
+ '.traecli/skills',
146
+ '.gemini/skills',
147
+ '.trae-cn/skills',
148
+ '.qwen/skills',
149
+ '.comate/skills',
150
+ '.codebuddy/skills',
151
+ '.kimi/skills',
152
+ '.agents/skills',
153
+ ];
154
+ let installedAny = false;
155
+ for (const d of PROJECT_DIRS) {
156
+ if (fs.existsSync(d)) { installTo(d); installedAny = true; }
157
+ }
158
+ if (!installedAny) {
159
+ console.log('未检测到项目级智能体目录。可手动复制,或用 --agent <name> / -g 装到用户级。');
160
+ }
161
+ }
162
+
163
+ main();