@perrylink/dsh-skill-pack-security-provider 1.3.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.
Files changed (45) hide show
  1. package/README.md +93 -0
  2. package/cordis.patch.yml +10 -0
  3. package/lib/index.js +82 -0
  4. package/lib/types/index.d.ts +30 -0
  5. package/pack/skills/dependency-audit/SKILL.md +136 -0
  6. package/pack/skills/dependency-audit/references/license-and-lockfile.md +56 -0
  7. package/pack/skills/dependency-audit/references/pnpm-audit-reading.md +63 -0
  8. package/pack/skills/incident-response/SKILL.md +83 -0
  9. package/pack/skills/incident-response/references/runbook-and-postmortem.md +82 -0
  10. package/pack/skills/prompt-injection-review/SKILL.md +119 -0
  11. package/pack/skills/prompt-injection-review/references/injection-surfaces.md +74 -0
  12. package/pack/skills/secret-scan/SKILL.md +123 -0
  13. package/pack/skills/secret-scan/references/redaction-and-remediation.md +58 -0
  14. package/pack/skills/secret-scan/references/tool-usage.md +83 -0
  15. package/pack/skills/security-audit/SKILL.md +111 -0
  16. package/pack/skills/security-audit/references/report-template.md +53 -0
  17. package/pack/skills/security-audit/references/risk-classification.md +40 -0
  18. package/pack/skills/supply-chain-review/SKILL.md +96 -0
  19. package/pack/skills/supply-chain-review/references/install-script-checks.md +34 -0
  20. package/pack/skills/supply-chain-review/references/typosquat-and-reproducibility.md +71 -0
  21. package/pack/skills/threat-model/SKILL.md +96 -0
  22. package/pack/skills/threat-model/references/stride-and-attack-tree.md +76 -0
  23. package/pack/skills/vuln-intel/SKILL.md +93 -0
  24. package/pack/skills/vuln-intel/references/advisory-sources.md +45 -0
  25. package/pack/skills-en/dependency-audit/SKILL.md +135 -0
  26. package/pack/skills-en/dependency-audit/references/license-and-lockfile.md +56 -0
  27. package/pack/skills-en/dependency-audit/references/pnpm-audit-reading.md +63 -0
  28. package/pack/skills-en/incident-response/SKILL.md +83 -0
  29. package/pack/skills-en/incident-response/references/runbook-and-postmortem.md +82 -0
  30. package/pack/skills-en/prompt-injection-review/SKILL.md +118 -0
  31. package/pack/skills-en/prompt-injection-review/references/injection-surfaces.md +74 -0
  32. package/pack/skills-en/secret-scan/SKILL.md +122 -0
  33. package/pack/skills-en/secret-scan/references/redaction-and-remediation.md +58 -0
  34. package/pack/skills-en/secret-scan/references/tool-usage.md +83 -0
  35. package/pack/skills-en/security-audit/SKILL.md +109 -0
  36. package/pack/skills-en/security-audit/references/report-template.md +53 -0
  37. package/pack/skills-en/security-audit/references/risk-classification.md +40 -0
  38. package/pack/skills-en/supply-chain-review/SKILL.md +95 -0
  39. package/pack/skills-en/supply-chain-review/references/install-script-checks.md +34 -0
  40. package/pack/skills-en/supply-chain-review/references/typosquat-and-reproducibility.md +71 -0
  41. package/pack/skills-en/threat-model/SKILL.md +96 -0
  42. package/pack/skills-en/threat-model/references/stride-and-attack-tree.md +76 -0
  43. package/pack/skills-en/vuln-intel/SKILL.md +93 -0
  44. package/pack/skills-en/vuln-intel/references/advisory-sources.md +45 -0
  45. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # 可选 provider 插件(npm bundle)/ Optional provider plugin (npm bundle)
2
+
3
+ [English](#english) | [中文](#中文)
4
+
5
+ 本目录是一个**可选的** DSH 插件:把包内技能目录注册进 `ctx.skills`,免去把技能复制到扫描根目录的步骤。技能包本体不依赖它。默认发布中文版 `skills/`,`language: 'en'` 发布英文版 `skills-en/`。包已声明 `dsh.bundle` 并发布在 npm:`dsh plugin add @perrylink/dsh-skill-pack-security-provider` 一键挂载。
6
+
7
+ ## English
8
+
9
+ This directory is an **optional** DSH plugin: it registers the pack's skill directory on `ctx.skills`, so the skills do not need to be copied into a scanned root. The pack itself does not depend on it. It publishes the Chinese edition `skills/` by default; `language: 'en'` publishes the English edition `skills-en/`. The package declares `dsh.bundle` and is published on npm: `dsh plugin add @perrylink/dsh-skill-pack-security-provider` mounts it in one command.
10
+
11
+ ### Design
12
+
13
+ - Reuses the official `FileSystemSkillProvider` (the provider class exported by `@deepseek-ai/dsh-skill-filesystem`): frontmatter parsing semantics are byte-identical with the official local provider (same fail-closed rules, kebab-case naming, invocation policy) — zero copied parsing logic.
14
+ - Configuration `{ includeDefaultRoots: false, customSkillDirs: [pack skills], watch: false }`: exposes only the pack's skills and never conflicts with user/project skill roots; static content needs no watcher.
15
+ - Registration is an effect: `ctx.effect(function* () { yield ctx.skills.registerProvider(...) })` — the returned disposer is the registry's effect disposer; the provider lifecycle (signal abort → dispose) is owned by the official class.
16
+ - Misconfiguration fails loud: an empty or nonexistent `skillsDir` (or a default resolution that finds neither layout) throws at `apply()` time instead of silently mounting zero skills.
17
+ - Root resolution supports two layouts: the repository layout (`skills/` beside `provider/`) and the published layout (`pack/skills` embedded beside `lib/` by `prepack`).
18
+
19
+ ### Configuration
20
+
21
+ | Key | Type | Default | Meaning |
22
+ |---|---|---|---|
23
+ | `language` | `'zh' \| 'en'` | `'zh'` | Which edition to publish: the Chinese `skills/` or the English `skills-en/`. Ignored when `skillsDir` is set. |
24
+ | `skillsDir` | string | — | Explicit skills root; overrides the `language`-derived default. Must exist and contain at least one `<skill>/SKILL.md` bundle, otherwise the plugin refuses to load. |
25
+ | `watch` | boolean | `false` | Whether to watch the packaged directory; packaged content is static. |
26
+
27
+ ### Mounting (any one)
28
+
29
+ 1. **npm bundle (recommended)**: `dsh plugin add @perrylink/dsh-skill-pack-security-provider`. The `dsh.bundle` manifest applies `cordis.patch.yml`, which mounts the Chinese edition; edit the patch row's `config.language` for English.
30
+ 2. Copy this directory into a project plugin directory and load it by local path in `cordis.yml`:
31
+
32
+ ```yaml
33
+ plugins:
34
+ - name: ./provider
35
+ ```
36
+
37
+ 3. During development, source-launch: the `dsh` CLI source launch runs through the tsx ESM hook, so point it directly at `provider/src/index.ts`.
38
+
39
+ ### Dependencies, build, and packaging
40
+
41
+ - peerDependencies: `@deepseek-ai/cordis@^4.0.1`, `@deepseek-ai/dsh-skill-filesystem@0.1.0-rc.6`, `@deepseek-ai/schemastery@^3.18.1` (all published on the npm registry — the provider installs and builds standalone).
42
+ - Build: `pnpm install --frozen-lockfile && pnpm run build` (`tsc` emits `lib/index.js` + `lib/types/index.d.ts`; the committed `pnpm-lock.yaml` keeps the build reproducible).
43
+ - Packaging: `prepack` runs `scripts/copy-skills.mjs`, which embeds both editions into `pack/`; `files` then ships `lib/`, `pack/`, and `cordis.patch.yml`, so `pnpm pack` produces a self-contained tarball (npm `files` cannot reach outside the package directory, which is why the copy exists).
44
+ - Contract notes: `name` + `inject: ['skills']` + `apply(ctx, config)`; the configuration is a Schemastery schema (no plain objects); `providerName` is unique within the registry layer (`'skill-pack-security'`).
45
+ - Publishing: published on npm as `@perrylink/dsh-skill-pack-security-provider` — the `@dsh-skill-pack-security` scope was not owned by this publisher, so the package was renamed; `cordis.patch.yml`'s `name` row and every README reference carry the published name. Publish new versions with `npm publish --access public` (see `docs/release-checklist.md` step 9).
46
+
47
+ ### Verification
48
+
49
+ Check 8 of `../verify/verify-skill-pack.mts` exercises this plugin: mounting the default config lists the 8 Chinese skills (`provider === 'skill-pack-security'`), `language: 'en'` lists the English edition, an explicit `skillsDir` mounts that root, empty/nonexistent `skillsDir` values are rejected, and after dispose the registries are empty. CI additionally builds the plugin standalone, packs it, and asserts the tarball carries `lib/`, both embedded editions in `pack/`, and `cordis.patch.yml` (`provider` job in `.github/workflows/verify.yml`).
50
+
51
+ ## 中文
52
+
53
+ 本目录是**可选**的 DSH 插件:把包内技能目录注册进 `ctx.skills`,免去复制技能到扫描根目录。技能包本体不依赖它。默认发布中文版 `skills/`,`language: 'en'` 发布英文版 `skills-en/`。包已声明 `dsh.bundle` 并发布在 npm:`dsh plugin add @perrylink/dsh-skill-pack-security-provider` 一键挂载。
54
+
55
+ ### 设计
56
+
57
+ - 复用官方 `FileSystemSkillProvider`(`@deepseek-ai/dsh-skill-filesystem` 导出的 provider 类):frontmatter 解析语义与官方本地提供方逐字节一致(同样的 fail-closed 规则、kebab-case 命名、invocation 策略),零复制解析逻辑。
58
+ - 配置 `{ includeDefaultRoots: false, customSkillDirs: [包内技能目录], watch: false }`:只暴露本包技能,不与用户/项目技能根冲突;静态内容不启用 watcher。
59
+ - 注册即 effect:`ctx.effect(function* () { yield ctx.skills.registerProvider(...) })`,返回的 disposer 就是注册表提供的 effect disposer;provider 生命周期(signal abort → dispose)由官方类接管。
60
+ - 错误配置响亮失败:`skillsDir` 为空、不存在或默认解析找不到任何布局时,`apply()` 直接抛错,绝不静默挂载零技能。
61
+ - 根目录解析支持两种布局:仓库布局(`skills/` 与 `provider/` 并列)与发布布局(`prepack` 把双语言版嵌入包内 `pack/skills`,与 `lib/` 并列)。
62
+
63
+ ### 配置
64
+
65
+ | 键 | 类型 | 默认 | 含义 |
66
+ |---|---|---|---|
67
+ | `language` | `'zh' \| 'en'` | `'zh'` | 发布哪个语言版:中文 `skills/` 或英文 `skills-en/`;设置了 `skillsDir` 时忽略 |
68
+ | `skillsDir` | string | — | 显式指定技能根目录,覆盖 `language` 推导的默认值;必须存在且含至少一个 `<skill>/SKILL.md` 包,否则插件拒绝加载 |
69
+ | `watch` | boolean | `false` | 是否监听包内技能目录;静态内容无需监听 |
70
+
71
+ ### 挂载方式(任选)
72
+
73
+ 1. **npm bundle(推荐)**:执行 `dsh plugin add @perrylink/dsh-skill-pack-security-provider`。`dsh.bundle` 清单应用 `cordis.patch.yml`,默认挂载中文版;要英文版改 patch 行里的 `config.language`。
74
+ 2. 复制本目录进项目插件目录,在 `cordis.yml` 里以本地路径加载:
75
+
76
+ ```yaml
77
+ plugins:
78
+ - name: ./provider
79
+ ```
80
+
81
+ 3. 开发期 source-launch:`dsh` CLI 源码启动走 tsx ESM hook,直接指到 `provider/src/index.ts`。
82
+
83
+ ### 依赖、构建与打包
84
+
85
+ - peerDependencies:`@deepseek-ai/cordis@^4.0.1`、`@deepseek-ai/dsh-skill-filesystem@0.1.0-rc.6`、`@deepseek-ai/schemastery@^3.18.1`(均已发布在 npm registry,插件可独立安装构建)。
86
+ - 构建:`pnpm install --frozen-lockfile && pnpm run build`(`tsc` 输出 `lib/index.js` + `lib/types/index.d.ts`;提交的 `pnpm-lock.yaml` 保证构建可复现)。
87
+ - 打包:`prepack` 执行 `scripts/copy-skills.mjs` 把双语言版嵌入 `pack/`;`files` 随包发布 `lib/`、`pack/` 与 `cordis.patch.yml`,因此 `pnpm pack` 产出即自包含 tarball(npm `files` 不能包含包外路径,这是拷贝步骤存在的原因)。
88
+ - 契约要点:`name` + `inject: ['skills']` + `apply(ctx, config)`;配置为 Schemastery schema(禁止普通对象);`providerName` 在注册表层内唯一('skill-pack-security')。
89
+ - 发布:已发布在 npm,包名 `@perrylink/dsh-skill-pack-security-provider`——scope `@dsh-skill-pack-security` 不为本发布者所有,故改名;`cordis.patch.yml` 的 `name` 行与全部 README 引用均已同步为发布名。新版本用 `npm publish --access public` 发布(见 `docs/release-checklist.md` 第 9 步)。
90
+
91
+ ### 验证
92
+
93
+ `../verify/verify-skill-pack.mts` 第 8 项实测本插件:默认配置挂载列出 8 个中文技能(`provider === 'skill-pack-security'`),`language: 'en'` 列出英文版,显式 `skillsDir` 挂载指定根目录,空/不存在的 `skillsDir` 被拒绝,dispose 后注册表为空。CI 另有 `provider` job(`.github/workflows/verify.yml`)独立构建并打包,断言 tarball 含 `lib/`、`pack/` 双语言版与 `cordis.patch.yml`。
@@ -0,0 +1,10 @@
1
+ # Bundle patch applied over the selected DSH profile when this package is
2
+ # listed via `dsh plugin add`. The row id is deployment-local; the `name`
3
+ # must match this package.json's `name`. The default publishes the Chinese
4
+ # edition; set `config.language: en` for the English edition.
5
+
6
+ - insert:
7
+ - id: skill-pack-security
8
+ name: '@perrylink/dsh-skill-pack-security-provider'
9
+ config:
10
+ language: zh
package/lib/index.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Optional provider plugin for dsh-skill-pack-security.
3
+ *
4
+ * Registers one SkillProvider on `ctx.skills` whose candidates are this
5
+ * package's own `skills/` directory, reusing the official
6
+ * `FileSystemSkillProvider` so frontmatter parsing semantics are byte-identical
7
+ * with the built-in local provider (same fail-closed rules, same kebab-case
8
+ * names, same invocation policy). The pack itself works without this plugin —
9
+ * installing it only avoids copying skills into a scanned root.
10
+ *
11
+ * @module dsh-skill-pack-security/provider
12
+ */
13
+ import { existsSync, readdirSync } from 'node:fs';
14
+ import { dirname, join, resolve } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { FileSystemSkillProvider } from '@deepseek-ai/dsh-skill-filesystem';
17
+ import z from '@deepseek-ai/schemastery';
18
+ export const name = 'skill-pack-security';
19
+ export const inject = ['skills'];
20
+ export const Config = z.object({
21
+ watch: z.boolean().default(false),
22
+ language: z.union(['zh', 'en']).default('zh'),
23
+ skillsDir: z.string().min(1),
24
+ });
25
+ /** Directory of this module: `provider/src` under tsx or `provider/lib` when built. */
26
+ const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
27
+ /**
28
+ * Candidate pack roots, in preference order. The repository layout keeps
29
+ * `skills/` and `skills-en/` beside `provider/`, two levels above this module;
30
+ * the published npm layout embeds both editions in `pack/` beside `lib/`.
31
+ */
32
+ const PACK_ROOT_CANDIDATES = [join(MODULE_DIR, '..', '..'), join(MODULE_DIR, '..', 'pack')];
33
+ /** Whether a directory contains at least one `<skill>/SKILL.md` bundle. */
34
+ function hasSkillBundles(dir) {
35
+ if (!existsSync(dir))
36
+ return false;
37
+ let entries;
38
+ try {
39
+ entries = readdirSync(dir, { withFileTypes: true });
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ return entries.some(entry => entry.isDirectory() && existsSync(join(dir, entry.name, 'SKILL.md')));
45
+ }
46
+ /**
47
+ * Resolve the skills root to publish and fail loud on misconfiguration:
48
+ * an explicit `skillsDir` must exist and hold bundles, and the derived
49
+ * default must find one of the supported layouts — never mount silently with
50
+ * zero skills.
51
+ * @param skillsDir - explicit config override, if any.
52
+ * @param language - edition to select for the derived default.
53
+ * @returns the validated skills root.
54
+ */
55
+ function resolveSkillsRoot(skillsDir, language) {
56
+ if (skillsDir !== undefined) {
57
+ const root = resolve(skillsDir);
58
+ if (!hasSkillBundles(root)) {
59
+ throw new Error(`skill-pack-security: config.skillsDir "${skillsDir}" does not exist or contains no <skill>/SKILL.md bundles`);
60
+ }
61
+ return root;
62
+ }
63
+ const edition = language === 'zh' ? 'skills' : 'skills-en';
64
+ for (const candidate of PACK_ROOT_CANDIDATES) {
65
+ const root = join(candidate, edition);
66
+ if (hasSkillBundles(root))
67
+ return root;
68
+ }
69
+ throw new Error(`skill-pack-security: no ${language} edition found near ${MODULE_DIR}; expected the repository layout (${edition}/ beside provider/) or the published layout (pack/${edition}/ inside the package). Set config.skillsDir to an explicit root.`);
70
+ }
71
+ /** Register the packaged skills directory as a custom-root provider. */
72
+ export function apply(ctx, config = {}) {
73
+ const skillsRoot = resolveSkillsRoot(config.skillsDir, config.language ?? 'zh');
74
+ ctx.effect(function* () {
75
+ yield ctx.skills.registerProvider(control => new FileSystemSkillProvider(ctx, control, {
76
+ providerName: 'skill-pack-security',
77
+ includeDefaultRoots: false,
78
+ customSkillDirs: [skillsRoot],
79
+ watch: config.watch ?? false,
80
+ }));
81
+ });
82
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Optional provider plugin for dsh-skill-pack-security.
3
+ *
4
+ * Registers one SkillProvider on `ctx.skills` whose candidates are this
5
+ * package's own `skills/` directory, reusing the official
6
+ * `FileSystemSkillProvider` so frontmatter parsing semantics are byte-identical
7
+ * with the built-in local provider (same fail-closed rules, same kebab-case
8
+ * names, same invocation policy). The pack itself works without this plugin —
9
+ * installing it only avoids copying skills into a scanned root.
10
+ *
11
+ * @module dsh-skill-pack-security/provider
12
+ */
13
+ import type { Context } from '@deepseek-ai/cordis';
14
+ import type Schema from '@deepseek-ai/schemastery';
15
+ export declare const name = "skill-pack-security";
16
+ export declare const inject: string[];
17
+ /** The skill language the provider publishes. */
18
+ export type PackLanguage = 'zh' | 'en';
19
+ /** Configuration for the packaged skill provider. */
20
+ export interface Config {
21
+ /** Whether to watch the packaged skills directory; packaged content is static, so default false. */
22
+ watch?: boolean;
23
+ /** Language edition to publish: the Chinese `skills/` or the English `skills-en/`. Ignored when `skillsDir` is set. */
24
+ language?: PackLanguage;
25
+ /** Explicit skills root; overrides the `language`-derived default. Must be a non-empty path to an existing root with `<skill>/SKILL.md` bundles. */
26
+ skillsDir?: string;
27
+ }
28
+ export declare const Config: Schema<Config>;
29
+ /** Register the packaged skills directory as a custom-root provider. */
30
+ export declare function apply(ctx: Context, config?: Config): void;
@@ -0,0 +1,136 @@
1
+ ---
2
+ name: dependency-audit
3
+ description: '依赖供应链审计:pnpm/npm audit 输出与退出码解读、license 与投毒风险检查清单、锁文件漂移检测命令。任务要求审计项目依赖的已知漏洞、许可证风险、可疑包或 lockfile 一致性并出结论时用;单独安装/升级某个依赖或纯功能开发不展开本流程。'
4
+ whenToUse: '用户要求审计或盘点项目依赖安全(漏洞、license、投毒、锁文件漂移)、解读 audit 报告、判断某个依赖能否引入,或写依赖审计结论时使用;单个依赖的普通升级与纯功能开发不触发本技能。'
5
+ metadata:
6
+ pack: dsh-skill-pack-security
7
+ version: '1.3.0'
8
+ ---
9
+
10
+ # 依赖审计(dependency-audit)
11
+
12
+ 目标:对仓库依赖面给出**每条结论都附命令证据**的审计结果。输出分七块:已知漏洞、license、投毒风险、锁文件漂移、多生态漏洞、SBOM 清单、provenance/签名。
13
+
14
+ ## 1. 定位包管理器与锁文件
15
+
16
+ ```sh
17
+ git ls-files -- 'package.json' 'pnpm-lock.yaml' 'package-lock.json' 'yarn.lock' 'bun.lockb' 'npm-shrinkwrap.json'
18
+ node --version; pnpm --version
19
+ osv-scanner --version
20
+ ```
21
+
22
+ 样例输出(pnpm 仓库):`package.json` 与 `pnpm-lock.yaml` 各一行。
23
+ 判据:锁文件决定后续命令族(pnpm→第 2 节,npm→同节 npm 变体);**多个锁文件并存 = 仓库异常**,写进发现;版本号写进报告(audit 数据随 registry 与工具版本变化);`osv-scanner` 不可用只影响第 6 节,注明即可。
24
+
25
+ ## 2. 已知漏洞:pnpm audit
26
+
27
+ ```sh
28
+ pnpm audit --prod --json > audit.json; echo $LASTEXITCODE
29
+ ```
30
+
31
+ (bash 用 `$?`;PowerShell 用 `$LASTEXITCODE`。)
32
+
33
+ - 退出码:0 = 无已知漏洞;非 0 = 有漏洞 **或 registry 不可达**(stderr 含 `fetch`/`ECONNREFUSED`/`ETIMEDOUT` 时为网络失败,不是发现漏洞——重试后再说)。
34
+ - `--prod` 只审生产依赖;需要全量视图时补跑 `pnpm audit --json`,devDependencies 部分按下文的降档规则处理。
35
+ - 输出样例(`advisories` 是按 advisory id 键控的**对象**,下例为其一个值;字段以实际输出为准):
36
+
37
+ ```json
38
+ { "id": "GHSA-xxxx-yyyy-zzzz", "severity": "high",
39
+ "module_name": "example-lib", "vulnerable_versions": "<2.3.0",
40
+ "patched_versions": ">=2.3.1", "recommendation": "Upgrade to 2.3.1",
41
+ "found": { "paths": ["prod-dep@1.0.0 > example-lib@2.2.9"] } }
42
+ ```
43
+
44
+ - 解读规则:
45
+ - `severity` 只信 registry 值(low/moderate/high/critical),不自行推断。
46
+ - 每个 advisory 查 `patched_versions` 是否存在;不存在 = 暂无修复版本,记录"无修复版本",不要声称"升级即可修复"。
47
+ - 影响路径只在 devDependencies 中 → 默认降一档报告,除非该 devDep 参与构建产物(用代码证据证明,不能口头认定)。
48
+ - 误报/误判判据(全表见 `references/pnpm-audit-reading.md`):advisory 状态 disputed/withdrawn、版本范围不含当前版本、路径不可达(不可达必须有调用点证据:`pnpm why <包>` + 源码 grep 无引用)。
49
+ - npm 项目变体:`npm audit --json`(退出码同为 0/非 0;结构是 `vulnerabilities` 对象而非 `advisories` 对象,样例见 references)。
50
+
51
+ ## 3. license 检查
52
+
53
+ ```sh
54
+ pnpm licenses list --json
55
+ ```
56
+
57
+ 样例行:`{ "name": "example-lib", "license": "MIT" }`(输出结构以实际为准)。
58
+ 找三类问题:
59
+
60
+ 1. **无声明**:license 字段为空/null → 记录"无许可证声明"(用法本身即合规风险)。
61
+ 2. **强 copyleft**:直接依赖中出现 GPL/AGPL/SSPL/CPAL 等(完整清单见 `references/license-and-lockfile.md`)→ 定位用途:`pnpm why <包名>` 给出依赖链。
62
+ 3. **非 SPDX**:值含 `SEE LICENSE IN <file>` → `git ls-files -- '<包目录>/**/LICENSE*'` 或解包读该文件再定论。
63
+
64
+ 判据:license 风险结论 = 包名 + 依赖链 + 许可证 + 用途;找不到用途(源码无 import)的按"未使用依赖"另记一条。
65
+
66
+ ## 4. 投毒风险检查清单(对每个"新/可疑"依赖逐项打勾)
67
+
68
+ 完整命令与阈值表见 `references/license-and-lockfile.md`,五项速记:
69
+
70
+ 1. **名称相似性**:`npm view <包> time.created`(样例:`2026-08-10T02:00:00.000Z`)。判据:创建 < 30 天且下载量极低 → 高风险标记,转 `supply-chain-review` 做 typosquat 判定。
71
+ 2. **install 脚本**:`npm view <包> scripts --json`(样例:`{ "postinstall": "node scripts/download.js" }`)。非空 → 转 `supply-chain-review` 第 1 节逐条查危险特征。
72
+ 3. **发布者与仓库**:`npm view <包> repository.url maintainers --json`。判据:repository 缺失/指向可疑 fork + 维护者历史为零 → 记录。
73
+ 4. **网络行为**:`npm pack <包> --pack-destination .tmp` 后 `grep -rnE 'https?://' .tmp/<包>/` 看请求域。判据:出现与包用途无关的域名 → 记录并人工复核。
74
+ 5. **provenance**:`npm view <包> provenance --json`。判据:无 provenance 不等于恶意,但写进风险记录。
75
+
76
+ 判据:单条命中只是"记录",**两条及以上同时命中才升级为"发现"**——防止单项误判。
77
+
78
+ ## 5. 锁文件漂移检测
79
+
80
+ 步骤与命令:
81
+
82
+ ```sh
83
+ git diff HEAD -- pnpm-lock.yaml | head -n 40
84
+ pnpm install --frozen-lockfile
85
+ grep -c 'integrity' pnpm-lock.yaml
86
+ ```
87
+
88
+ - 步骤 1 判据:diff 非空 = 锁文件有改动,逐块看是否意外(合并冲突残留 `<<<<<<<` 也算)。
89
+ - 步骤 2 判据:CI 语义下任何漂移立即失败。
90
+ 样例失败输出:`ERR_PNPM_OUTDATED_LOCKFILE Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with package.json`
91
+ 本地通过 + CI 失败 = 平台差异(optionalDependencies)→ 逐包核对,**不要关掉 frozen-lockfile**。
92
+ - 步骤 3 判据:integrity 条目数 ≈ 依赖条目数;明显偏少 = 锁文件被手改/损坏。
93
+ - 漂移原因分类与复核命令、lockfileVersion 对照表见 `references/license-and-lockfile.md`。
94
+
95
+ ## 6. 多生态与离线:osv-scanner
96
+
97
+ ```sh
98
+ osv-scanner scan -r .
99
+ ```
100
+
101
+ 样例输出行(以实际输出为准):
102
+
103
+ ```
104
+ Scanning dir .
105
+ Scanned <project>/package-lock.json file and found 2 packages
106
+ ```
107
+
108
+ - 判据:退出码 0 = 未发现;非 0 = 有漏洞或参数错误(stderr 区分)。`-r .` 自动识别目录中的全部锁文件(pnpm/npm/yarn/bun/pip/Cargo/Go/Maven 等);单文件用 `osv-scanner scan lockfile <文件>`。
109
+ - 与 pnpm audit 差异:osv-scanner 查 OSV 数据库(聚合 GitHub Advisories 等来源),覆盖 pnpm audit 看不到的其他生态;两边不一致时按 advisory id 逐条核对,不互相当作误报依据。
110
+ - 离线路径:`osv-scanner scan -r . --offline`(配合本地 OSV 数据)用于 registry 不可达环境;报告注明数据版本。
111
+
112
+ ## 7. SBOM 资产清单(机器可复核的盘点)
113
+
114
+ ```sh
115
+ trivy sbom . --format cyclonedx -o sbom.cdx.json
116
+ # 或 syft dir:. -o spdx-json=sbom.spdx.json
117
+ ```
118
+
119
+ 样例输出:退出码 0,并打印产物路径(`sbom.cdx.json`)。
120
+ 判据:SBOM 作为依赖盘点附录随报告提交;条目数与 `pnpm licenses list` 条目数量级一致,不一致 = 记录并说明原因。SBOM 不含密钥但含依赖拓扑,按报告同等级别保管。
121
+
122
+ ## 8. provenance 与签名
123
+
124
+ ```sh
125
+ npm view <包> provenance --json
126
+ npm view <包> dist.integrity --json
127
+ npm audit signatures
128
+ ```
129
+
130
+ - 判据:`provenance` 非空 = 包由 CI 构建并带构建来源声明;`dist.integrity` 与锁文件中同版本包的 `integrity` 值必须一致,不一致 = 锁文件被手改或包被替换,立即升级为发现。
131
+ - `npm audit signatures` 校验 registry 签名:退出码 0 = 通过;非 0 输出列出签名缺失/无效的包,记录并人工复核来源。
132
+ - 无 provenance 不等于恶意,但写进风险记录(第 4 节第 5 项同规则)。
133
+
134
+ ## 结论格式
135
+
136
+ 每条结论 = 断言 + 命令 + 输出摘要 + 误报排除说明("我排除了 X,因为 <证据>")。无证据的担忧写进"观察",不进"发现"。
@@ -0,0 +1,56 @@
1
+ # license、投毒与锁文件漂移(dependency-audit/references/license-and-lockfile.md)
2
+
3
+ 主文件第 3、4、5 节的完整清单与阈值表。
4
+
5
+ ## license 检查命令矩阵
6
+
7
+ | 检查 | 命令 | 命中样例 | 判据 |
8
+ |---|---|---|---|
9
+ | 全量清单 | `pnpm licenses list --json` | `{ "name": "x", "license": "MIT" }` | 结构以实际输出为准;license 为空/null = 无声明 |
10
+ | 单包查询 | `npm view <包> license --json` | `"MIT"` 或 `"SEE LICENSE IN LICENSE"` | `SEE LICENSE IN` → 解包读该文件再定论 |
11
+ | 依赖链定位 | `pnpm why <包>` | `prod-dep@1.0.0 → x@2.0.0` | 用于 copyleft 用途定位 |
12
+ | 源码引用核对 | `grep -rn '<包名>' <src> --include='*.ts' --include='*.js'` | 命中 import 行 | 无命中 = "未使用依赖"另记一条 |
13
+
14
+ ## copyleft 强传染清单(出现在**直接依赖**时必查)
15
+
16
+ GPL-2.0 / GPL-2.0+ / GPL-3.0 / GPL-3.0+ / AGPL-3.0 / SSPL-1.0 / CPAL-1.0 / EUPL-1.1 / EUPL-1.2 / OSL-3.0。
17
+ 弱 copyleft(MPL-2.0、EPL-2.0、LGPL-3.0)只记录,不升级为发现,除非被静态链接(该判断需要构建产物证据,禁止口头断言)。
18
+ 非 SPDX 标识(如 `MIT OR custom`、裸 `BSD`)记录为"标识不规范",人工核对实际文件。
19
+
20
+ ## 投毒检查五项:命令 + 阈值表
21
+
22
+ | # | 检查 | 命令 | 高风险阈值 | 说明 |
23
+ |---|---|---|---|---|
24
+ | 1 | 名称相似性 | `npm view <包> time.created`;`npm view <包> --json` 看 downloads | created < 30 天且周下载 < 100 | 转 supply-chain-review 做编辑距离判定 |
25
+ | 2 | install 脚本 | `npm view <包> scripts --json` | preinstall/install/postinstall 非空 | 非空即展开查危险特征(转 supply-chain-review 第 1 节) |
26
+ | 3 | 发布者/仓库 | `npm view <包> repository.url maintainers --json` | repository 缺失或指向 fork;maintainers 历史为零 | 单条只记录 |
27
+ | 4 | 网络行为 | `npm pack <包> --pack-destination .tmp`;`grep -rnE 'https?://' .tmp/<包>/` | 出现与用途无关域名 | 解包目录是临时文件,查完删除 |
28
+ | 5 | provenance | `npm view <包> provenance --json` | 无 provenance | 无 ≠ 恶意,写进风险记录 |
29
+
30
+ 升级规则:**两条及以上命中才从"记录"升级为"发现"**;单条命中不阻断、不误杀小众正常包。
31
+
32
+ ## 锁文件漂移:命令与分类
33
+
34
+ ```sh
35
+ git diff HEAD -- pnpm-lock.yaml | head -n 40 # 看改动内容
36
+ pnpm install --frozen-lockfile # CI 语义复验
37
+ grep -c 'integrity' pnpm-lock.yaml # 完整性条目数
38
+ ```
39
+
40
+ 漂移原因分类与复核命令:
41
+
42
+ | 原因 | 特征 | 复核命令 | 处理 |
43
+ |---|---|---|---|
44
+ | 手改 package.json 未重装 | package.json 与锁文件版本不符 | `pnpm install --frozen-lockfile` 失败 + 报 ERR_PNPM_OUTDATED_LOCKFILE | 重装并提交锁文件 |
45
+ | 合并冲突误解决 | 锁文件含 `<<<<<<<`/`=======` 残留 | `grep -nE '^(<<<<<<<|=======|>>>>>>>)' pnpm-lock.yaml` | 重新合并解决 |
46
+ | 平台可选依赖 | 本地通过、CI 失败 | CI 上跑 `pnpm install --frozen-lockfile` 看失败包名 | 逐包核对平台条件,不关 frozen-lockfile |
47
+
48
+ ## lockfileVersion 与 pnpm 大版本对照(样例,以实际为准)
49
+
50
+ | lockfileVersion | pnpm 大版本 |
51
+ |---|---|
52
+ | 5.x(含 `lockfileVersion: 5.4`) | 7 |
53
+ | 6.0 | 8 |
54
+ | 9.0 | 9 / 10 |
55
+
56
+ 判据:lockfileVersion 与工具大版本不匹配 = 环境不一致,先统一 pnpm 版本再继续审计(`git grep -n 'lockfileVersion' pnpm-lock.yaml | head -n 1` 取值)。
@@ -0,0 +1,63 @@
1
+ # audit 输出解读(dependency-audit/references/pnpm-audit-reading.md)
2
+
3
+ 主文件第 2 节的完整字段字典、退出码表与误判规则。
4
+
5
+ ## 退出码表
6
+
7
+ | 命令 | 退出码 | 含义 | 下一步 |
8
+ |---|---|---|---|
9
+ | `pnpm audit --prod --json` | 0 | 无已知漏洞 | 通过 |
10
+ | 同上 | 非 0 | 有漏洞 **或** registry 不可达 | 看 stderr:含 `fetch`/`ECONNREFUSED`/`ETIMEDOUT` = 网络失败,重试;否则读 JSON |
11
+ | `npm audit --json` | 0 / 非 0 | 同 pnpm | 同上 |
12
+
13
+ 判据:**网络失败不是发现**。重试两次仍失败 → 报告写"audit 未执行(registry 不可达)",并改走离线路径(读已提交的锁文件版本对照公开 advisory 列表,标注为人工核对)。
14
+
15
+ ## pnpm audit JSON 字段字典(样例,以实际输出为准)
16
+
17
+ ```json
18
+ {
19
+ "auditReportVersion": 2,
20
+ "advisories": {
21
+ "GHSA-xxxx-yyyy-zzzz": {
22
+ "id": "GHSA-xxxx-yyyy-zzzz",
23
+ "severity": "high",
24
+ "module_name": "example-lib",
25
+ "vulnerable_versions": "<2.3.0",
26
+ "patched_versions": ">=2.3.1",
27
+ "recommendation": "Upgrade to 2.3.1",
28
+ "found": { "paths": ["prod-dep@1.0.0 > example-lib@2.2.9"] }
29
+ }
30
+ },
31
+ "metadata": { "vulnerabilities": { "info": 0, "low": 1, "moderate": 2, "high": 3, "critical": 4 } }
32
+ }
33
+ ```
34
+
35
+ | 字段 | 用途 | 判据 |
36
+ |---|---|---|
37
+ | `severity` | 只信 registry 值 | 不自行推断;report 里原样引用 |
38
+ | `vulnerable_versions` / `patched_versions` | 版本范围 | `patched_versions` 缺失 = 无修复版本,写"无修复版本",禁止说"升级即可修复" |
39
+ | `found.paths` | 依赖路径 | 路径是否含 devDep 前缀决定是否降档(见下) |
40
+ | `metadata.vulnerabilities` | 汇总计数 | 与逐条核对,数量不符 = 输出截断/重试 |
41
+
42
+ ## 误判规则(每条都要命令证据)
43
+
44
+ 1. **devDep 降档**:路径只出现在 devDependencies 且该包不参与构建产物。证据命令:`pnpm why <包>` 确认路径;构建产物引用证据:`grep -rn '<包名>' <打包/构建配置> <产物入口>`。无证据 → 不降档。
45
+ 2. **advisory 状态**:disputed/withdrawn 的 advisory 在 audit 输出中通常已排除;若旧版工具仍报,用 advisory id 查源(`npm view` 无法查 GHSA,用 GitHub Advisory Database 页面/web_search 核对状态)后标注。
46
+ 3. **不可达路径**:声称"代码没用到"必须同时给出 `pnpm why <包>` 的路径 + 源码 grep 无引用;只有其一 = 观察。
47
+ 4. **版本范围**:audit 已按安装版本过滤;但全局/peer 安装的版本不在 lockfile 中 → 用 `pnpm ls -r --depth 0` 与实际 node_modules 核对(`node -p "require('<包>/package.json').version"`)。
48
+
49
+ ## npm audit 差异
50
+
51
+ `npm audit --json` 输出结构:
52
+
53
+ ```json
54
+ { "auditReportVersion": 2,
55
+ "vulnerabilities": {
56
+ "example-lib": {
57
+ "severity": "high", "via": [{ "source": 1099999, "name": "example-lib", "range": "<2.3.0" }],
58
+ "effects": [], "range": "<2.3.0", "fixAvailable": { "name": "example-lib", "version": "2.3.1", "isSemVerMajor": false },
59
+ "isDirect": true
60
+ } } }
61
+ ```
62
+
63
+ 判据:`fixAvailable` 为 false/缺失 = 无修复版本;`isDirect` 区分直接/传递依赖(传递依赖的修复方式是通过直接依赖升级)。
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: incident-response
3
+ description: 'agent 环境安全事件响应:分类→控制蔓延→取证留痕→恢复→复盘的分步处置流程,覆盖密钥泄露、提示注入触发、依赖投毒、未授权操作四类事件,每条处置附命令证据。DSH/agent 环境出现疑似安全事件需要按流程处置与复盘时用;日常开发与例行维护不用。'
4
+ whenToUse: 'agent 环境(DSH 会话、插件、MCP、CI)出现疑似安全事件——密钥泄露、被注入执行了未授权操作、依赖投毒、权限异常——需要响应、留证与复盘时使用;没有事件迹象的日常开发不触发本技能。'
5
+ metadata:
6
+ pack: dsh-skill-pack-security
7
+ version: '1.3.0'
8
+ ---
9
+
10
+ # 事件响应(incident-response)
11
+
12
+ 原则:**先控制,再取证,后恢复**。控制动作(轮换/撤销/停用)不依赖完整归因——疑似即执行;取证在控制之后做,避免"边查边扩散"。全程不写密钥明文,报告遵守 `secret-scan` 脱敏规范。
13
+
14
+ ## 1. 确认与分类
15
+
16
+ 先回答两个问题:发生了什么(类型),影响面多大(范围)。四类事件与初步证据:
17
+
18
+ | 事件类型 | 初步证据 | 立即动作(见第 2 节) |
19
+ |---|---|---|
20
+ | 密钥泄露 | 扫描告警、公开仓库出现 token | 轮换 + 撤销 |
21
+ | 提示注入触发 | 会话执行了上下文外指令(下载/外发/改配置) | 停用触发源 + 隔离 |
22
+ | 依赖投毒 | 新依赖出现异常 install 脚本/网络请求 | 回滚依赖 + 锁文件冻结 |
23
+ | 未授权操作 | 仓库/CI 出现非本人提交、非预期运行 | 撤销凭据 + 暂停自动化 |
24
+
25
+ ```sh
26
+ git rev-parse HEAD
27
+ git log -1 --format='%H %cd' --date=iso-strict
28
+ ```
29
+
30
+ 预期输出样例:`a1b2c3d4...` 与提交时间。
31
+ 判据:先记录当前 HEAD——后续所有证据命令都相对它复核;不确定类型时先按"密钥泄露"假设处置(轮换成本最低、最安全)。
32
+
33
+ ## 2. 控制蔓延(疑似即执行,不等归因)
34
+
35
+ - 密钥类:按 `secret-scan` 第 6 节顺序——先轮换、再撤销;轮换完成判据 = 新值生效且旧值在厂商控制台已撤销。
36
+ - 注入/未授权操作类:停用触发源——下线相关 MCP/插件、暂停 agent 会话与相关 CI:
37
+
38
+ ```sh
39
+ git ls-files -- 'cordis.yml' '**/cordis.yml' '.mcp.json' '.github/workflows/**'
40
+ dsh --profile <配置文件> --dump-config
41
+ ```
42
+
43
+ 样例输出:配置与工作流路径列表;dump 输出中能看到已挂载插件清单。
44
+ 判据:dump 输出里逐个核对插件/MCP 是否受信;存疑的立即从配置移除(改配置 → 新会话生效)。控制完成判据 = 事件源已停用且 24 小时内无同类新动作。
45
+
46
+ ## 3. 取证留痕(时间线 + 证据包)
47
+
48
+ ```sh
49
+ git log --format='%H %cd %s' --date=iso-strict --since='<事发时间>' --all
50
+ # Windows:Get-ChildItem $env:DSH_HOME -Recurse -File | Sort-Object LastWriteTime -Descending | Select-Object -First 20
51
+ # macOS/Linux:find ~/.dsh -type f -newermt '<事发时间>' -ls
52
+ ```
53
+
54
+ 样例输出:一行一条提交(哈希 + 时间 + 摘要);会话目录里按时间倒序的文件列表。
55
+ 判据:时间线每条 = 时间 + 主体 + 动作 + 证据引用;**只记录可复核的事实**,推测写进"待验证"区。证据包 = 命令输出原文 + 相关文件副本,按 `secret-scan` 脱敏规范处理后再共享。
56
+
57
+ ## 4. 恢复
58
+
59
+ ```sh
60
+ git revert --no-commit <可疑提交>
61
+ # 或回退整个分支到已知良好提交:git reset --hard <已知良好提交>(先备份!)
62
+ ```
63
+
64
+ 样例输出:revert 后 `git status` 显示待提交的逆变更。
65
+ 判据:恢复顺序——先撤恶意/可疑变更,再恢复受影响服务,最后恢复自动化(CI/agent)并观察一个周期。恢复完成判据 = 时间线中所有异常点都有对应处置记录,且观察期内无复发。
66
+
67
+ ## 5. 复盘与加固
68
+
69
+ 复盘模板(五段:时间线、根因、影响评估、处置记录、加固项)见 `references/runbook-and-postmortem.md`。加固项从根因反推,每项都要可验证:
70
+
71
+ - 密钥类根因 → 补 `gitleaks protect --staged` 门禁(`secret-scan`);
72
+ - 注入类根因 → 按 `prompt-injection-review` 过一遍注入面并落地缓解;
73
+ - 依赖类根因 → 按 `supply-chain-review` 收紧引入流程(pin action、禁 install 脚本);
74
+ - 权限类根因 → 最小权限改造:删除多余凭据、限制 CI 令牌范围。
75
+
76
+ 复盘完成判据 = 每个加固项都有验证命令或验收标准。
77
+
78
+ ## 与其他技能的分工
79
+
80
+ - `secret-scan`:密钥泄露事件的检测、分级与轮换流程细节。
81
+ - `prompt-injection-review`:注入类事件的注入面排查与缓解清单。
82
+ - `supply-chain-review` / `dependency-audit`:依赖类事件的引入向量分析与依赖审计。
83
+ - `security-audit`:事件平息后的全量审计复查。
@@ -0,0 +1,82 @@
1
+ # 处置明细与复盘模板(incident-response/references/runbook-and-postmortem.md)
2
+
3
+ 主文件第 1–5 节的完整处置明细、时间线模板与复盘模板。
4
+
5
+ ## 四类事件处置明细
6
+
7
+ ### 密钥泄露
8
+
9
+ | 步骤 | 动作 | 验收命令/判据 |
10
+ |---|---|---|
11
+ | 1 轮换 | 在签发方生成新值并替换全部使用处 | 新值生效(服务用新值可正常认证) |
12
+ | 2 撤销 | 厂商控制台撤销旧值 | 控制台显示已撤销 |
13
+ | 3 定位 | `git grep -n '<前 6 字符>' <提交哈希> -- '<路径>'` 找泄露位置 | 输出命中行且为凭据格式 |
14
+ | 4 清除历史(可选) | `git filter-repo --path <文件> --invert-paths`(备份 + 通知协作者两个前置缺一不做) | 重写后扫描不再命中 |
15
+ | 5 门禁 | `gitleaks protect --staged` 或 CI 门禁 | 故意提交假密钥被拦截 |
16
+
17
+ ### 提示注入触发
18
+
19
+ | 步骤 | 动作 | 验收 |
20
+ |---|---|---|
21
+ | 1 停用 | 移除触发源(MCP/网页流程/恶意仓库) | dump 配置中源已消失 |
22
+ | 2 隔离 | 暂停会话与相关自动化,检查是否执行了下载/外发 | 24 小时无同类动作 |
23
+ | 3 排查 | 按 `prompt-injection-review` 逐面过三问 | 每个注入面有结论 |
24
+ | 4 加固 | 分帧声明 + 写操作审批门 + 网页内容隔离 | 复测同一注入文本不再触发动作 |
25
+
26
+ ### 依赖投毒
27
+
28
+ | 步骤 | 动作 | 验收 |
29
+ |---|---|---|
30
+ | 1 回滚 | `git revert --no-commit <引入提交>` 或锁文件回退 | 依赖树回到引入前 |
31
+ | 2 冻结 | CI 保持 `--frozen-lockfile`,暂停 dependabot/renovate 合并 | 无自动依赖变更 |
32
+ | 3 分析 | 按 `supply-chain-review` 第 1 节查 install 脚本/网络请求 | 每个新包有结论 |
33
+ | 4 收紧 | action pin SHA、禁止未评审 install 脚本 | 新 PR 的依赖被评审规则拦截 |
34
+
35
+ ### 未授权操作
36
+
37
+ | 步骤 | 动作 | 验收 |
38
+ |---|---|---|
39
+ | 1 撤销 | 撤销全部相关凭据(GitHub token、CI secrets、会话凭据) | 旧凭据调用返回 401 |
40
+ | 2 暂停 | 暂停 CI/自动化,冻结仓库写权限 | 观察期无新写入 |
41
+ | 3 取证 | `git log --since` 时间线 + 平台审计日志 | 每个可疑动作有记录 |
42
+ | 4 加固 | 最小权限、令牌范围收紧、双因素开启 | 权限矩阵重新评审通过 |
43
+
44
+ ## 时间线模板
45
+
46
+ | 时间(UTC) | 主体(人/服务/会话) | 动作 | 证据引用(命令 + 输出位置) | 复核状态 |
47
+ |---|---|---|---|---|
48
+ | 2026-08-14T10:30Z | CI(部署 token) | 非预期执行 deploy.yml | `git log --since` 第 3 条 | 已复核 |
49
+
50
+ 规则:时间用 ISO-8601;"主体"必须能追溯到具体凭据/会话;证据引用指回证据包文件,不贴原文(原文按 `secret-scan` 脱敏)。
51
+
52
+ ## 证据包清单
53
+
54
+ 1. 时间线表(本文件模板);
55
+ 2. 命令输出原文:`git log`/`git diff`/dump 配置/扫描告警(脱敏后);
56
+ 3. 相关文件副本:涉及的工作流、配置、锁文件 diff;
57
+ 4. 平台侧记录:CI 运行记录、厂商控制台撤销截图/记录。
58
+
59
+ ## 复盘五段模板
60
+
61
+ ```markdown
62
+ # <事件名> 复盘
63
+ ## 1. 时间线
64
+ (表格,见上)
65
+ ## 2. 根因
66
+ ("为什么发生"——区分直接原因与系统性原因)
67
+ ## 3. 影响评估
68
+ (泄露面/执行面/可用性,用证据命令支撑)
69
+ ## 4. 处置记录
70
+ (对照第 2 节控制清单逐项勾)
71
+ ## 5. 加固项
72
+ | 加固项 | 对应根因 | 验收命令/标准 | 状态 |
73
+ ```
74
+
75
+ ## 加固项验收表(示例)
76
+
77
+ | 加固项 | 根因 | 验收 |
78
+ |---|---|---|
79
+ | pre-commit gitleaks 门禁 | 密钥明文入库 | 提交假密钥被拦截 |
80
+ | 系统注入增加分帧声明 | 网页内容触发指令 | 复测注入文本无动作 |
81
+ | action 全部 pin SHA | tag 可被移动 | `git grep -nE 'uses: [A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+@v[0-9]' -- '.github/workflows/**'` 无输出 |
82
+ | CI 令牌收窄到单仓库 | 令牌权限过大 | 越权调用返回 403 |