upstream-radar 0.37.0 → 0.38.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/README.md CHANGED
@@ -63,15 +63,15 @@ baseline report, and defined in [`schemas/upstream-downstream-ir.schema.json`](s
63
63
 
64
64
  ```bash
65
65
  # No DSH profile, API key, or network state required
66
- npx --yes upstream-radar@0.37.0 demo
66
+ npx --yes upstream-radar@0.38.0 demo
67
67
 
68
68
  # Scan a public DSH plugin repository without installing it
69
- npx --yes upstream-radar@0.37.0 scan \
69
+ npx --yes upstream-radar@0.38.0 scan \
70
70
  https://github.com/PlutoKeating/dsh-lark-bot \
71
71
  --fail-on never
72
72
 
73
73
  # Review a real browser plugin users would install, then check two DSH releases
74
- npx --yes upstream-radar@0.37.0 review dsh-plugin dsh-cloudflare-browser-run@0.1.1 \
74
+ npx --yes upstream-radar@0.38.0 review dsh-plugin dsh-cloudflare-browser-run@0.1.1 \
75
75
  --dsh-version 0.1.0-rc.6,0.1.0-rc.7
76
76
  ```
77
77
 
@@ -91,6 +91,30 @@ These are real, reproducible cases in this repository—not synthetic “vulnera
91
91
 
92
92
  We report a confirmed vulnerability only when the affected exact version and runtime path are supported by the available evidence. Development-only hits, missing data, and advisory-source outages remain visibly different states.
93
93
 
94
+ ## Monitor the findings we already found
95
+
96
+ The repository now has a focused watch for seven real DSH plugins from the first
97
+ batch. It re-runs the source scan and the exact npm artifact review, then stores
98
+ only trusted observations in [`state.json`](examples/dsh/finding-watch/state.json)
99
+ and writes the current author-facing result to
100
+ [`report.md`](examples/dsh/finding-watch/report.md).
101
+
102
+ ```bash
103
+ pnpm run monitor:dsh-findings
104
+ ```
105
+
106
+ The watch distinguishes `persisting`, `added`, `resolved`, `changed`, and
107
+ `unknown`. A failed registry request never becomes “resolved”; a source fix also
108
+ does not erase a finding that remains in the published npm artifact. The same
109
+ loop runs daily in [the dedicated GitHub Actions workflow](.github/workflows/dsh-finding-watch.yml)
110
+ and commits the state only when a trusted observation changes. It does not install
111
+ plugins, execute lifecycle scripts, load DSH, or call an LLM.
112
+
113
+ The current run is already useful: `dsh-msg-hub@0.1.8` has removed the old source
114
+ lockfile findings, but its npm artifact still reaches `protobufjs@7.6.5` with a
115
+ `postinstall`; `dsh-wsl-workspace` now resolves `koffi@3.1.6`, while the native
116
+ install step remains. The other reviewed install/lifecycle findings persist.
117
+
94
118
  ## The dependency graph behind every alert
95
119
 
96
120
  ```text
@@ -107,11 +131,11 @@ Two copies of `parser` are different nodes. An alert names the exact version and
107
131
  For a collection of saved reports, build the reverse index that turns an upstream package update into affected plugins:
108
132
 
109
133
  ```bash
110
- npx --yes upstream-radar@0.37.0 graph reverse ./reports \
134
+ npx --yes upstream-radar@0.38.0 graph reverse ./reports \
111
135
  --output reverse-dependency-index.json
112
136
 
113
137
  # Ask: which plugins currently depend on this exact package?
114
- npx --yes upstream-radar@0.37.0 graph reverse ./reports \
138
+ npx --yes upstream-radar@0.38.0 graph reverse ./reports \
115
139
  --package parser@2.9.0
116
140
 
117
141
  # Rebuild the checked-in index from the real first 50 DSH plugin reports
@@ -130,7 +154,7 @@ To route an upstream old → new change to that index, pass it to the always-on
130
154
  observer:
131
155
 
132
156
  ```bash
133
- npx --yes upstream-radar@0.37.0 observe ./targets.yml \
157
+ npx --yes upstream-radar@0.38.0 observe ./targets.yml \
134
158
  --reverse-index ./reverse-dependency-index.json \
135
159
  --state ./observations.json \
136
160
  --report ./upstream-radar-observer.md
@@ -156,7 +180,7 @@ replay baseline → one Agent task → quiet run without network access.
156
180
  The repository already contains a reusable, composite Action in [`action.yml`](action.yml). It runs the same frozen Radar check in CI and writes a short Job Summary.
157
181
 
158
182
  ```yaml
159
- - uses: MicroMilo/upstream-radar@v0.37.0
183
+ - uses: MicroMilo/upstream-radar@v0.38.0
160
184
  with:
161
185
  config: upstream-radar.config.json
162
186
  fail-on: high
@@ -180,7 +204,7 @@ See the [consumer workflow](examples/github-actions/consumer/README.md) for conf
180
204
  pnpm add upstream-radar
181
205
 
182
206
  # Generate a reviewable DSH profile inventory from the installed profile
183
- npx --yes upstream-radar@0.37.0 setup
207
+ npx --yes upstream-radar@0.38.0 setup
184
208
  ```
185
209
 
186
210
  For Feishu/webhook routing, DSH Agent handoff, observer state, report schemas, and troubleshooting, use the [full Chinese guide](docs/README.zh-CN.md). The [architecture notes](docs/architecture.md) explain the boundaries and evidence model.
@@ -0,0 +1,31 @@
1
+ export declare const FINDING_WATCH_SCHEMA: 'upstream-radar.finding-watch/v1alpha1';
2
+ export type FindingWatchTransitionStatus = 'added' | 'resolved' | 'changed' | 'persisting';
3
+ export interface FindingWatchRecord {
4
+ code: string;
5
+ severity: string;
6
+ summary: string;
7
+ detail?: string;
8
+ evidence?: unknown;
9
+ fingerprint: string;
10
+ }
11
+ export interface FindingWatchTransition {
12
+ code: string;
13
+ status: FindingWatchTransitionStatus;
14
+ previousCount: number;
15
+ currentCount: number;
16
+ }
17
+ export interface FindingWatchDelta {
18
+ changed: boolean;
19
+ transitions: FindingWatchTransition[];
20
+ }
21
+ export interface FindingWatchInput {
22
+ code?: unknown;
23
+ severity?: unknown;
24
+ summary?: unknown;
25
+ detail?: unknown;
26
+ evidence?: unknown;
27
+ }
28
+ export declare function normalizeFinding(input: FindingWatchInput): FindingWatchRecord | undefined;
29
+ export declare function normalizeFindings(inputs: readonly FindingWatchInput[], watchedCodes: readonly string[]): FindingWatchRecord[];
30
+ export declare function compareFindings(previous: readonly FindingWatchRecord[] | undefined, current: readonly FindingWatchRecord[], watchedCodes: readonly string[]): FindingWatchDelta;
31
+ //# sourceMappingURL=finding-watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-watch.d.ts","sourceRoot":"","sources":["../../src/finding-watch.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,EAAG,uCAAgD,CAAA;AAEpF,MAAM,MAAM,4BAA4B,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,CAAA;AAE1F,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,4BAA4B,CAAA;IACpC,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,sBAAsB,EAAE,CAAA;CACtC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAgBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,SAAS,CAWzF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAM7H;AAaD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,EACnD,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,iBAAiB,CAmBnB"}
@@ -0,0 +1,70 @@
1
+ import { createHash } from 'node:crypto';
2
+ export const FINDING_WATCH_SCHEMA = 'upstream-radar.finding-watch/v1alpha1';
3
+ function canonicalize(value) {
4
+ if (Array.isArray(value))
5
+ return value.map(canonicalize);
6
+ if (typeof value !== 'object' || value === null)
7
+ return value;
8
+ return Object.fromEntries(Object.entries(value)
9
+ .sort(([left], [right]) => left.localeCompare(right))
10
+ .map(([key, item]) => [key, canonicalize(item)]));
11
+ }
12
+ function fingerprint(value) {
13
+ return `sha256:${createHash('sha256').update(JSON.stringify(canonicalize(value))).digest('hex')}`;
14
+ }
15
+ export function normalizeFinding(input) {
16
+ if (typeof input.code !== 'string' || input.code === '')
17
+ return undefined;
18
+ const severity = typeof input.severity === 'string' && input.severity !== '' ? input.severity : 'unknown';
19
+ const summary = typeof input.summary === 'string' && input.summary !== '' ? input.summary : input.code;
20
+ const detail = typeof input.detail === 'string' && input.detail !== '' ? input.detail : undefined;
21
+ const evidence = input.evidence;
22
+ const identity = { code: input.code, severity, summary, ...(detail === undefined ? {} : { detail }), ...(evidence === undefined ? {} : { evidence }) };
23
+ return {
24
+ ...identity,
25
+ fingerprint: fingerprint(identity),
26
+ };
27
+ }
28
+ export function normalizeFindings(inputs, watchedCodes) {
29
+ const watched = new Set(watchedCodes);
30
+ return inputs
31
+ .map(normalizeFinding)
32
+ .filter((finding) => finding !== undefined && watched.has(finding.code))
33
+ .sort((left, right) => left.code.localeCompare(right.code) || left.fingerprint.localeCompare(right.fingerprint));
34
+ }
35
+ function groupedFingerprints(findings) {
36
+ const groups = new Map();
37
+ for (const finding of findings) {
38
+ const current = groups.get(finding.code) ?? [];
39
+ current.push(finding.fingerprint);
40
+ groups.set(finding.code, current);
41
+ }
42
+ for (const values of groups.values())
43
+ values.sort();
44
+ return groups;
45
+ }
46
+ export function compareFindings(previous, current, watchedCodes) {
47
+ const oldGroups = groupedFingerprints(previous ?? []);
48
+ const currentGroups = groupedFingerprints(current);
49
+ const codes = [...new Set([...watchedCodes, ...oldGroups.keys(), ...currentGroups.keys()])].sort();
50
+ const transitions = [];
51
+ for (const code of codes) {
52
+ const oldValues = oldGroups.get(code) ?? [];
53
+ const currentValues = currentGroups.get(code) ?? [];
54
+ let status;
55
+ if (oldValues.length === 0 && currentValues.length > 0)
56
+ status = 'added';
57
+ else if (oldValues.length > 0 && currentValues.length === 0)
58
+ status = 'resolved';
59
+ else if (JSON.stringify(oldValues) !== JSON.stringify(currentValues))
60
+ status = 'changed';
61
+ else
62
+ status = 'persisting';
63
+ transitions.push({ code, status, previousCount: oldValues.length, currentCount: currentValues.length });
64
+ }
65
+ return {
66
+ changed: transitions.some(item => item.status !== 'persisting'),
67
+ transitions,
68
+ };
69
+ }
70
+ //# sourceMappingURL=finding-watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-watch.js","sourceRoot":"","sources":["../../src/finding-watch.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export declare const TOOL_VERSION = "0.37.0";
1
+ export declare const TOOL_VERSION = "0.38.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,2 +1,2 @@
1
- export const TOOL_VERSION = '0.37.0';
1
+ export const TOOL_VERSION = '0.38.0';
2
2
  //# sourceMappingURL=version.js.map
@@ -48,6 +48,7 @@ npx --yes upstream-radar@latest quickstart
48
48
  | --- | --- | --- |
49
49
  | 让在线 DSH Agent 持续跟进 | [`setup`](#安装到-dsh) | 自动刷新 profile 里的实际依赖图,只把有变化的事件交给对应项目的 Agent。 |
50
50
  | 观察 DSH 插件上游变化 | [`observe`](#观察-dsh-插件的上游变化) | 定时比较 GitHub commit、npm 发布物、manifest 和可选锁文件依赖图,只把重要变化交给 Agent。 |
51
+ | 监控已经发现的问题 | [DSH known finding watch](../examples/dsh/finding-watch/README.md) | 对第一批 7 个真实插件重复检查源码与精确 npm 发布物,把问题标成持续、修复、变化或无法确认。 |
51
52
  | 30 秒开始监控一个公开插件 | [最小 GitHub Actions workflow](../examples/github-actions/upstream-observer-minimal.yml) | 复制一个文件、改仓库地址,每天得到报告,不需要安装或构建 Radar。 |
52
53
  | 收到第一条告警后知道先做什么 | [`radar next`](#安装到-dsh) | 一个只读命令选出最高优先级事件,并指向 DSH task、已验证分析或下一轮检查。 |
53
54
  | 启动 DSH 前检查 profile | [`profile-check`](#启动-dsh-前先检查-profile) | 读取实际锁文件和 patch,提前拦住缺失 loader 包、重复 loader id 以及 release-age 回退风险。 |
@@ -96,7 +97,7 @@ FIRST EPSS estimated exploitation probability: 97.2% (percentile 100.0%)
96
97
  想立即检查一个真实发布的 DSH 插件,可以在空目录直接运行:
97
98
 
98
99
  ```bash
99
- npx --yes upstream-radar@0.37.0 inspect dsh-feishu-bot@0.15.8 --deep
100
+ npx --yes upstream-radar@0.38.0 inspect dsh-feishu-bot@0.15.8 --deep
100
101
  ```
101
102
 
102
103
  它会直接输出简短的准入结论、覆盖情况、依赖数量、漏洞数量和下一步,不需要先
@@ -109,7 +110,7 @@ npx --yes upstream-radar@0.37.0 inspect dsh-feishu-bot@0.15.8 --deep
109
110
  npm 解析环境中目前无法建立完整依赖图:
110
111
 
111
112
  ```bash
112
- npx --yes upstream-radar@0.37.0 inspect \
113
+ npx --yes upstream-radar@0.38.0 inspect \
113
114
  @sanqi-normal/dsh-webui-market-plugin@0.5.4 \
114
115
  --deep --fail-on never
115
116
  ```
@@ -280,6 +281,23 @@ OpenAI-compatible 模型,先设置
280
281
  这是新的上游变化闭环:Radar 不在每轮都重复做一次大扫描,而是为每个
281
282
  插件保存一个观察点,然后回答“从上次观察到现在到底变了什么”。
282
283
 
284
+ ### 只监控已经发现的问题
285
+
286
+ 如果目标不是重新发现所有问题,而是持续回答“上次报告的这条问题现在还在不在”,运行仓库内置的专用观察清单:
287
+
288
+ ```bash
289
+ pnpm run monitor:dsh-findings
290
+ ```
291
+
292
+ 它会对 `examples/dsh/finding-watch/targets.json` 中的 7 个真实 DSH 插件分别做两次检查:
293
+
294
+ 1. 扫描公开 GitHub 源码,不安装依赖、不运行插件;
295
+ 2. 按源码当前版本检查精确 npm 发布物,依赖解析使用禁用 lifecycle script 的环境。
296
+
297
+ 它把源码和发布物分开保存,因为维护者可能已经修了 GitHub 仓库,但还没有重新发布 npm 包。每条观察结果只有在检查成功时才会推进;网络失败是 `unknown`,不会被写成“已修复”。状态保存在 [`state.json`](../examples/dsh/finding-watch/state.json),可读报告在 [`report.md`](../examples/dsh/finding-watch/report.md),每日 workflow 是 [`dsh-finding-watch.yml`](../.github/workflows/dsh-finding-watch.yml)。
298
+
299
+ 当前真实结果中,`dsh-msg-hub@0.1.8` 的源码侧已经没有旧的 lockfile 图不可用/根版本过期问题,但同版本 npm 发布物仍可达 `protobufjs@7.6.5` 的 `postinstall`;`dsh-wsl-workspace` 由 `koffi@3.1.5` 变成了 `3.1.6`,但安装脚本仍然存在。这个入口的用途是监控这些已知问题的生命周期,不把每次定时运行都伪装成新的漏洞发现。
300
+
283
301
  ```text
284
302
  targets.yml
285
303
 
@@ -317,9 +335,9 @@ npm: dsh-feishu-bot@0.15.8
317
335
  会继续显示为 `incomplete`,不会被当成已经确认的故障。
318
336
 
319
337
  ```bash
320
- npx --yes upstream-radar@0.37.0 graph reverse ./plugin-reports \
338
+ npx --yes upstream-radar@0.38.0 graph reverse ./plugin-reports \
321
339
  --output ./reverse-dependency-index.json
322
- npx --yes upstream-radar@0.37.0 observe ./targets.yml \
340
+ npx --yes upstream-radar@0.38.0 observe ./targets.yml \
323
341
  --reverse-index ./reverse-dependency-index.json \
324
342
  --state ./observations.json --report ./upstream-radar-observer.md
325
343
  ```
@@ -357,7 +375,7 @@ Radar 会在三层目录内自动找到它;npm 名称和源码 manifest 不一
357
375
  想明确选择锁文件时再加 `--lockfile`:
358
376
 
359
377
  ```bash
360
- npx --yes upstream-radar@0.37.0 observe \
378
+ npx --yes upstream-radar@0.38.0 observe \
361
379
  https://github.com/PlutoKeating/dsh-lark-bot \
362
380
  --state ./observations.json --report ./upstream-radar-observer.md
363
381
  ```
@@ -380,7 +398,7 @@ workspace 边保留为未解析证据;不会安装或启动 DSH。
380
398
  会额外指定 `--package`:
381
399
 
382
400
  ```bash
383
- npx --yes upstream-radar@0.37.0 observe \
401
+ npx --yes upstream-radar@0.38.0 observe \
384
402
  https://github.com/PlutoKeating/dsh-lark-bot \
385
403
  --package dsh-feishu-bot \
386
404
  --lockfile pnpm-lock.yaml --lockfile-type pnpm \
@@ -658,7 +676,7 @@ cd my-dsh-plugin
658
676
  pnpm install --ignore-scripts
659
677
 
660
678
  # 把插件放进 DSH profile 前,先读取精确依赖图。
661
- pnpm dlx --package=upstream-radar@0.37.0 upstream-radar graph pnpm-lock pnpm-lock.yaml --json
679
+ pnpm dlx --package=upstream-radar@0.38.0 upstream-radar graph pnpm-lock pnpm-lock.yaml --json
662
680
  ```
663
681
 
664
682
  这棵图会保留精确的 DSH 包版本,也会把未解析的可选 peer 明确显示出来;它不会加载生成的插件,也不会运行 lifecycle script。审查后,把下面这个完整 workflow 复制到 `.github/workflows/upstream-radar.yml`:
@@ -680,7 +698,7 @@ jobs:
680
698
  runs-on: ubuntu-latest
681
699
  steps:
682
700
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
683
- - uses: MicroMilo/upstream-radar@v0.37.0
701
+ - uses: MicroMilo/upstream-radar@v0.38.0
684
702
  with:
685
703
  fail-on: high
686
704
  fail-on-compatibility: breaking
@@ -691,7 +709,7 @@ Action 会自动识别唯一的 `pnpm-lock.yaml`,检查同一棵精确依赖
691
709
  也可以直接检查一个真实发布的 DSH 包:
692
710
 
693
711
  ```bash
694
- npx --yes upstream-radar@0.37.0 inspect dsh-feishu-bot@0.15.8 --deep
712
+ npx --yes upstream-radar@0.38.0 inspect dsh-feishu-bot@0.15.8 --deep
695
713
  ```
696
714
 
697
715
  这次检查的结论是 `REVIEW`:registry 完整性、签名、provenance 和 89 个已解析包都
@@ -704,7 +722,7 @@ npx --yes upstream-radar@0.37.0 inspect dsh-feishu-bot@0.15.8 --deep
704
722
  如果不想分别执行 `inspect`、打包和 `probe`,可以用一个命令完成一次 DSH 插件审查:
705
723
 
706
724
  ```bash
707
- pnpm dlx --package=upstream-radar@0.37.0 upstream-radar review dsh-plugin \
725
+ pnpm dlx --package=upstream-radar@0.38.0 upstream-radar review dsh-plugin \
708
726
  dsh-cloudflare-browser-run@0.1.1 \
709
727
  --dsh-version 0.1.0-rc.6,0.1.0-rc.7
710
728
  ```
@@ -870,7 +888,7 @@ pnpm run try:dsh
870
888
  在把项目接入兼容性门禁前,可以先运行离线规则 benchmark:
871
889
 
872
890
  ```bash
873
- pnpm dlx --package=upstream-radar@0.37.0 upstream-radar benchmark compatibility
891
+ pnpm dlx --package=upstream-radar@0.38.0 upstream-radar benchmark compatibility
874
892
  ```
875
893
 
876
894
  它覆盖六类契约:安全补丁、只需要项目分析的变化、不兼容的 DSH peer、发布者明确声明 breaking、候选传递依赖漏洞,以及候选依赖图不完整。这个命令不会联网、安装包、加载插件或启动 DSH;它验证的是 Radar 的确定性规则以及 `breaking`/`any` 门禁行为,不是运行时兼容性证明。
@@ -883,7 +901,7 @@ pnpm dlx --package=upstream-radar@0.37.0 upstream-radar benchmark compatibility
883
901
  # 打包精确版本,并明确不运行它的 lifecycle script。
884
902
  npm pack --ignore-scripts dsh-plugin@1.2.3
885
903
 
886
- pnpm dlx --package=upstream-radar@0.37.0 upstream-radar probe dsh-load \
904
+ pnpm dlx --package=upstream-radar@0.38.0 upstream-radar probe dsh-load \
887
905
  ./dsh-plugin-1.2.3.tgz \
888
906
  --dsh-version 0.1.0-rc.6
889
907
  ```
@@ -909,7 +927,7 @@ pnpm run showcase:dsh-probe
909
927
  如果要比较多个 DSH 版本,可以使用矩阵入口:
910
928
 
911
929
  ```bash
912
- pnpm dlx --package=upstream-radar@0.37.0 upstream-radar probe dsh-matrix \
930
+ pnpm dlx --package=upstream-radar@0.38.0 upstream-radar probe dsh-matrix \
913
931
  ./dsh-plugin-1.2.3.tgz \
914
932
  --dsh-version 0.1.0-rc.3 \
915
933
  --dsh-version 0.1.0-rc.6 \
@@ -927,7 +945,7 @@ JSON 结果结构见[矩阵结果 schema](../schemas/dsh-load-matrix.schema.json
927
945
  ```yaml
928
946
  steps:
929
947
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
930
- - uses: MicroMilo/upstream-radar@v0.37.0
948
+ - uses: MicroMilo/upstream-radar@v0.38.0
931
949
  with:
932
950
  fail-on: high
933
951
  # 可选:把确定性的 DSH/插件兼容性破坏也作为 CI 失败条件
@@ -936,12 +954,12 @@ steps:
936
954
  threat-intel: true
937
955
  ```
938
956
 
939
- 这个 Action 只是 `radar check --frozen --state :memory: --fail-on high --fail-on-compatibility breaking --json` 的薄封装。`--frozen` 是有意的:它只使用配置文件里的依赖图,不会尝试读取 runner 上不存在的本地 DSH profile。`threat-intel` 默认是 `false`,这样普通 CI 门禁不会因为额外查询变重;设置为 `true` 后,Job Summary 和原始 JSON 会包含 CISA KEV 与 FIRST EPSS 的优先级证据。每次运行彼此独立;发现达到阈值的漏洞或选择的兼容性变化时返回 `2`,运行或漏洞源出错时返回 `1`。`breaking` 只拦截有 confirmed/strong 信号的兼容性事件,`any` 会拦截所有活动兼容性事件,默认值是 `never`。除了原始 JSON 日志,Action 还会把经过转义的简短摘要写入 GitHub Job Summary,定时任务失败时可以直接看到受影响的包、准确依赖路径、已经发布的修复版本(如果有)、一行优先级证据和建议的下一步。这个入口不会投递 DSH Agent 任务,也不会修改分支;需要持续监控和项目级分析时,仍使用原生 DSH bundle。建议把 Action 固定到类似 `v0.37.0` 的发布标签,并根据团队策略固定 checkout Action。
957
+ 这个 Action 只是 `radar check --frozen --state :memory: --fail-on high --fail-on-compatibility breaking --json` 的薄封装。`--frozen` 是有意的:它只使用配置文件里的依赖图,不会尝试读取 runner 上不存在的本地 DSH profile。`threat-intel` 默认是 `false`,这样普通 CI 门禁不会因为额外查询变重;设置为 `true` 后,Job Summary 和原始 JSON 会包含 CISA KEV 与 FIRST EPSS 的优先级证据。每次运行彼此独立;发现达到阈值的漏洞或选择的兼容性变化时返回 `2`,运行或漏洞源出错时返回 `1`。`breaking` 只拦截有 confirmed/strong 信号的兼容性事件,`any` 会拦截所有活动兼容性事件,默认值是 `never`。除了原始 JSON 日志,Action 还会把经过转义的简短摘要写入 GitHub Job Summary,定时任务失败时可以直接看到受影响的包、准确依赖路径、已经发布的修复版本(如果有)、一行优先级证据和建议的下一步。这个入口不会投递 DSH Agent 任务,也不会修改分支;需要持续监控和项目级分析时,仍使用原生 DSH bundle。建议把 Action 固定到类似 `v0.38.0` 的发布标签,并根据团队策略固定 checkout Action。
940
958
 
941
959
  如果仓库还没有提交 Radar 配置,最短接入方式是省略 `config`、`pnpm-lock` 和 `npm-lock`。checkout 之后,Action 会自动使用唯一存在的 `pnpm-lock.yaml` 或 `package-lock.json`,生成临时的审查清单,再执行同一个 frozen 检查:
942
960
 
943
961
  ```yaml
944
- - uses: MicroMilo/upstream-radar@v0.37.0
962
+ - uses: MicroMilo/upstream-radar@v0.38.0
945
963
  with:
946
964
  fail-on: high
947
965
  ```
@@ -951,7 +969,7 @@ steps:
951
969
  如果要在插件进入 DSH 前审查精确发布物,可以增加 `inspect-package`:
952
970
 
953
971
  ```yaml
954
- - uses: MicroMilo/upstream-radar@v0.37.0
972
+ - uses: MicroMilo/upstream-radar@v0.38.0
955
973
  with:
956
974
  inspect-package: dsh-cloudflare-browser-run@0.1.1
957
975
  # review 是安全默认值;只有允许覆盖不完整时才使用 block
@@ -963,7 +981,7 @@ steps:
963
981
  如果仓库只有 pnpm 锁文件,还没有提交 Radar 配置,可以让 Action 在同一个 job 中生成配置;可直接复制[pnpm workflow 示例](../examples/github-actions/upstream-radar-pnpm.yml):
964
982
 
965
983
  ```yaml
966
- - uses: MicroMilo/upstream-radar@v0.37.0
984
+ - uses: MicroMilo/upstream-radar@v0.38.0
967
985
  with:
968
986
  pnpm-lock: pnpm-lock.yaml
969
987
  fail-on: high
@@ -977,7 +995,7 @@ npm 项目可以改用 `npm-lock: package-lock.json`;`pnpm-lock` 和 `npm-lock
977
995
  调用方需要先 checkout 仓库。这个 Action 不会安装项目依赖,也不会执行项目的 lifecycle script;它只读取提交到仓库的依赖图并查询配置中的上游漏洞源。如果需要完全显式的底层命令,等价写法是:
978
996
 
979
997
  ```bash
980
- pnpm dlx --package=upstream-radar@0.37.0 upstream-radar radar check \
998
+ pnpm dlx --package=upstream-radar@0.38.0 upstream-radar radar check \
981
999
  ./upstream-radar.config.json --frozen --state :memory: --fail-on high \
982
1000
  --fail-on-compatibility breaking --json
983
1001
  ```
@@ -985,7 +1003,7 @@ pnpm dlx --package=upstream-radar@0.37.0 upstream-radar radar check \
985
1003
  如果还要检查一个已发布插件能否跨多个 DSH 版本加载,可以增加三个 input:
986
1004
 
987
1005
  ```yaml
988
- - uses: MicroMilo/upstream-radar@v0.37.0
1006
+ - uses: MicroMilo/upstream-radar@v0.38.0
989
1007
  id: radar
990
1008
  with:
991
1009
  config: upstream-radar.config.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "upstream-radar",
3
- "version": "0.37.0",
3
+ "version": "0.38.0",
4
4
  "description": "Always-on dependency security monitoring for DeepSeek Harness (DSH) plugins: find exact installed or candidate transitive vulnerable paths and breaking updates, then route evidence to a DSH Agent.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -95,6 +95,7 @@
95
95
  "showcase:dsh-case": "pnpm run build && node scripts/dsh-case-showcase.mjs",
96
96
  "showcase:dsh-case:report": "pnpm run build && node scripts/dsh-case-showcase.mjs --write-report",
97
97
  "showcase:observer": "pnpm run build && node scripts/upstream-observer-showcase.mjs",
98
+ "monitor:dsh-findings": "pnpm run build && node scripts/monitor-dsh-findings.mjs",
98
99
  "refresh:dsh-batch": "pnpm run build && node scripts/refresh-dsh-batch-index.mjs",
99
100
  "showcase:pnpm-lock": "pnpm run build && node scripts/pnpm-lock-showcase.mjs",
100
101
  "showcase:pnpm-lock:monitor": "pnpm run build && node scripts/pnpm-lock-monitor-showcase.mjs",