upstream-radar 0.35.0 → 0.36.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.
@@ -96,7 +96,7 @@ FIRST EPSS estimated exploitation probability: 97.2% (percentile 100.0%)
96
96
  想立即检查一个真实发布的 DSH 插件,可以在空目录直接运行:
97
97
 
98
98
  ```bash
99
- npx --yes upstream-radar@0.35.0 inspect dsh-feishu-bot@0.15.8 --deep
99
+ npx --yes upstream-radar@0.36.0 inspect dsh-feishu-bot@0.15.8 --deep
100
100
  ```
101
101
 
102
102
  它会直接输出简短的准入结论、覆盖情况、依赖数量、漏洞数量和下一步,不需要先
@@ -109,7 +109,7 @@ npx --yes upstream-radar@0.35.0 inspect dsh-feishu-bot@0.15.8 --deep
109
109
  npm 解析环境中目前无法建立完整依赖图:
110
110
 
111
111
  ```bash
112
- npx --yes upstream-radar@0.35.0 inspect \
112
+ npx --yes upstream-radar@0.36.0 inspect \
113
113
  @sanqi-normal/dsh-webui-market-plugin@0.5.4 \
114
114
  --deep --fail-on never
115
115
  ```
@@ -309,6 +309,25 @@ npm: dsh-feishu-bot@0.15.8
309
309
  因为一个持续存在的 mismatch 每天重复唤醒 Agent。机器可读定义见
310
310
  [`upstream-downstream-ir.schema.json`](../schemas/upstream-downstream-ir.schema.json)。
311
311
 
312
+ ### 把上游变化路由到受影响插件
313
+
314
+ 单独建立反向索引只能回答“谁使用了某个精确版本”。把它传给 observer 后,Radar
315
+ 会按包名对齐 old → new 变化:即使上游从 `parser@1.0.0` 变成 `parser@2.0.0`,而
316
+ 下游插件还停留在 `parser@1.0.0`,也会把这个插件和完整依赖路径列出来。覆盖不完整
317
+ 会继续显示为 `incomplete`,不会被当成已经确认的故障。
318
+
319
+ ```bash
320
+ npx --yes upstream-radar@0.36.0 graph reverse ./plugin-reports \
321
+ --output ./reverse-dependency-index.json
322
+ npx --yes upstream-radar@0.36.0 observe ./targets.yml \
323
+ --reverse-index ./reverse-dependency-index.json \
324
+ --state ./observations.json --report ./upstream-radar-observer.md
325
+ ```
326
+
327
+ 这一步不安装插件、不执行代码,也不调用模型;它只是把确定性的依赖图证据接到
328
+ always-on 的上游变化任务上。索引结构见
329
+ [`reverse-dependency-index.schema.json`](../schemas/reverse-dependency-index.schema.json)。
330
+
312
331
  可以从[可复制的真实 targets 示例](../examples/upstream-observer/targets.yml)开始。它默认观察公开的 DSH/飞书插件 `PlutoKeating/dsh-lark-bot`,不是模拟项目:
313
332
 
314
333
  ```yaml
@@ -332,7 +351,7 @@ Radar 会在三层目录内自动找到它;npm 名称和源码 manifest 不一
332
351
  想明确选择锁文件时再加 `--lockfile`:
333
352
 
334
353
  ```bash
335
- npx --yes upstream-radar@0.35.0 observe \
354
+ npx --yes upstream-radar@0.36.0 observe \
336
355
  https://github.com/PlutoKeating/dsh-lark-bot \
337
356
  --state ./observations.json --report ./upstream-radar-observer.md
338
357
  ```
@@ -355,7 +374,7 @@ workspace 边保留为未解析证据;不会安装或启动 DSH。
355
374
  会额外指定 `--package`:
356
375
 
357
376
  ```bash
358
- npx --yes upstream-radar@0.35.0 observe \
377
+ npx --yes upstream-radar@0.36.0 observe \
359
378
  https://github.com/PlutoKeating/dsh-lark-bot \
360
379
  --package dsh-feishu-bot \
361
380
  --lockfile pnpm-lock.yaml --lockfile-type pnpm \
@@ -633,7 +652,7 @@ cd my-dsh-plugin
633
652
  pnpm install --ignore-scripts
634
653
 
635
654
  # 把插件放进 DSH profile 前,先读取精确依赖图。
636
- pnpm dlx --package=upstream-radar@0.35.0 upstream-radar graph pnpm-lock pnpm-lock.yaml --json
655
+ pnpm dlx --package=upstream-radar@0.36.0 upstream-radar graph pnpm-lock pnpm-lock.yaml --json
637
656
  ```
638
657
 
639
658
  这棵图会保留精确的 DSH 包版本,也会把未解析的可选 peer 明确显示出来;它不会加载生成的插件,也不会运行 lifecycle script。审查后,把下面这个完整 workflow 复制到 `.github/workflows/upstream-radar.yml`:
@@ -655,7 +674,7 @@ jobs:
655
674
  runs-on: ubuntu-latest
656
675
  steps:
657
676
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
658
- - uses: MicroMilo/upstream-radar@v0.35.0
677
+ - uses: MicroMilo/upstream-radar@v0.36.0
659
678
  with:
660
679
  fail-on: high
661
680
  fail-on-compatibility: breaking
@@ -666,7 +685,7 @@ Action 会自动识别唯一的 `pnpm-lock.yaml`,检查同一棵精确依赖
666
685
  也可以直接检查一个真实发布的 DSH 包:
667
686
 
668
687
  ```bash
669
- npx --yes upstream-radar@0.35.0 inspect dsh-feishu-bot@0.15.8 --deep
688
+ npx --yes upstream-radar@0.36.0 inspect dsh-feishu-bot@0.15.8 --deep
670
689
  ```
671
690
 
672
691
  这次检查的结论是 `REVIEW`:registry 完整性、签名、provenance 和 89 个已解析包都
@@ -679,7 +698,7 @@ npx --yes upstream-radar@0.35.0 inspect dsh-feishu-bot@0.15.8 --deep
679
698
  如果不想分别执行 `inspect`、打包和 `probe`,可以用一个命令完成一次 DSH 插件审查:
680
699
 
681
700
  ```bash
682
- pnpm dlx --package=upstream-radar@0.35.0 upstream-radar review dsh-plugin \
701
+ pnpm dlx --package=upstream-radar@0.36.0 upstream-radar review dsh-plugin \
683
702
  dsh-cloudflare-browser-run@0.1.1 \
684
703
  --dsh-version 0.1.0-rc.6,0.1.0-rc.7
685
704
  ```
@@ -845,7 +864,7 @@ pnpm run try:dsh
845
864
  在把项目接入兼容性门禁前,可以先运行离线规则 benchmark:
846
865
 
847
866
  ```bash
848
- pnpm dlx --package=upstream-radar@0.35.0 upstream-radar benchmark compatibility
867
+ pnpm dlx --package=upstream-radar@0.36.0 upstream-radar benchmark compatibility
849
868
  ```
850
869
 
851
870
  它覆盖六类契约:安全补丁、只需要项目分析的变化、不兼容的 DSH peer、发布者明确声明 breaking、候选传递依赖漏洞,以及候选依赖图不完整。这个命令不会联网、安装包、加载插件或启动 DSH;它验证的是 Radar 的确定性规则以及 `breaking`/`any` 门禁行为,不是运行时兼容性证明。
@@ -858,7 +877,7 @@ pnpm dlx --package=upstream-radar@0.35.0 upstream-radar benchmark compatibility
858
877
  # 打包精确版本,并明确不运行它的 lifecycle script。
859
878
  npm pack --ignore-scripts dsh-plugin@1.2.3
860
879
 
861
- pnpm dlx --package=upstream-radar@0.35.0 upstream-radar probe dsh-load \
880
+ pnpm dlx --package=upstream-radar@0.36.0 upstream-radar probe dsh-load \
862
881
  ./dsh-plugin-1.2.3.tgz \
863
882
  --dsh-version 0.1.0-rc.6
864
883
  ```
@@ -884,7 +903,7 @@ pnpm run showcase:dsh-probe
884
903
  如果要比较多个 DSH 版本,可以使用矩阵入口:
885
904
 
886
905
  ```bash
887
- pnpm dlx --package=upstream-radar@0.35.0 upstream-radar probe dsh-matrix \
906
+ pnpm dlx --package=upstream-radar@0.36.0 upstream-radar probe dsh-matrix \
888
907
  ./dsh-plugin-1.2.3.tgz \
889
908
  --dsh-version 0.1.0-rc.3 \
890
909
  --dsh-version 0.1.0-rc.6 \
@@ -902,7 +921,7 @@ JSON 结果结构见[矩阵结果 schema](../schemas/dsh-load-matrix.schema.json
902
921
  ```yaml
903
922
  steps:
904
923
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
905
- - uses: MicroMilo/upstream-radar@v0.35.0
924
+ - uses: MicroMilo/upstream-radar@v0.36.0
906
925
  with:
907
926
  fail-on: high
908
927
  # 可选:把确定性的 DSH/插件兼容性破坏也作为 CI 失败条件
@@ -911,12 +930,12 @@ steps:
911
930
  threat-intel: true
912
931
  ```
913
932
 
914
- 这个 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.35.0` 的发布标签,并根据团队策略固定 checkout Action。
933
+ 这个 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.36.0` 的发布标签,并根据团队策略固定 checkout Action。
915
934
 
916
935
  如果仓库还没有提交 Radar 配置,最短接入方式是省略 `config`、`pnpm-lock` 和 `npm-lock`。checkout 之后,Action 会自动使用唯一存在的 `pnpm-lock.yaml` 或 `package-lock.json`,生成临时的审查清单,再执行同一个 frozen 检查:
917
936
 
918
937
  ```yaml
919
- - uses: MicroMilo/upstream-radar@v0.35.0
938
+ - uses: MicroMilo/upstream-radar@v0.36.0
920
939
  with:
921
940
  fail-on: high
922
941
  ```
@@ -926,7 +945,7 @@ steps:
926
945
  如果要在插件进入 DSH 前审查精确发布物,可以增加 `inspect-package`:
927
946
 
928
947
  ```yaml
929
- - uses: MicroMilo/upstream-radar@v0.35.0
948
+ - uses: MicroMilo/upstream-radar@v0.36.0
930
949
  with:
931
950
  inspect-package: dsh-cloudflare-browser-run@0.1.1
932
951
  # review 是安全默认值;只有允许覆盖不完整时才使用 block
@@ -938,7 +957,7 @@ steps:
938
957
  如果仓库只有 pnpm 锁文件,还没有提交 Radar 配置,可以让 Action 在同一个 job 中生成配置;可直接复制[pnpm workflow 示例](../examples/github-actions/upstream-radar-pnpm.yml):
939
958
 
940
959
  ```yaml
941
- - uses: MicroMilo/upstream-radar@v0.35.0
960
+ - uses: MicroMilo/upstream-radar@v0.36.0
942
961
  with:
943
962
  pnpm-lock: pnpm-lock.yaml
944
963
  fail-on: high
@@ -952,7 +971,7 @@ npm 项目可以改用 `npm-lock: package-lock.json`;`pnpm-lock` 和 `npm-lock
952
971
  调用方需要先 checkout 仓库。这个 Action 不会安装项目依赖,也不会执行项目的 lifecycle script;它只读取提交到仓库的依赖图并查询配置中的上游漏洞源。如果需要完全显式的底层命令,等价写法是:
953
972
 
954
973
  ```bash
955
- pnpm dlx --package=upstream-radar@0.35.0 upstream-radar radar check \
974
+ pnpm dlx --package=upstream-radar@0.36.0 upstream-radar radar check \
956
975
  ./upstream-radar.config.json --frozen --state :memory: --fail-on high \
957
976
  --fail-on-compatibility breaking --json
958
977
  ```
@@ -960,7 +979,7 @@ pnpm dlx --package=upstream-radar@0.35.0 upstream-radar radar check \
960
979
  如果还要检查一个已发布插件能否跨多个 DSH 版本加载,可以增加三个 input:
961
980
 
962
981
  ```yaml
963
- - uses: MicroMilo/upstream-radar@v0.35.0
982
+ - uses: MicroMilo/upstream-radar@v0.36.0
964
983
  id: radar
965
984
  with:
966
985
  config: upstream-radar.config.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "upstream-radar",
3
- "version": "0.35.0",
3
+ "version": "0.36.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",
@@ -0,0 +1,132 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/MicroMilo/upstream-radar/blob/main/schemas/reverse-dependency-index.schema.json",
4
+ "title": "Upstream Radar reverse dependency index",
5
+ "description": "A deterministic index from exact npm dependency coordinates to downstream plugin graphs.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "generatedAt", "observations", "inputs", "coverage", "plugins", "dependencies"],
9
+ "properties": {
10
+ "schema": { "const": "upstream-radar.reverse-dependency-index/v1alpha1" },
11
+ "generatedAt": { "type": "string", "minLength": 1, "maxLength": 128, "format": "date-time" },
12
+ "observations": { "type": "integer", "minimum": 0 },
13
+ "inputs": { "$ref": "#/$defs/inputs" },
14
+ "coverage": { "$ref": "#/$defs/coverage" },
15
+ "plugins": {
16
+ "type": "array",
17
+ "items": { "$ref": "#/$defs/plugin" }
18
+ },
19
+ "dependencies": {
20
+ "type": "array",
21
+ "items": { "$ref": "#/$defs/dependency" }
22
+ }
23
+ },
24
+ "$defs": {
25
+ "coordinate": {
26
+ "type": "object",
27
+ "additionalProperties": false,
28
+ "required": ["ecosystem", "name", "version"],
29
+ "properties": {
30
+ "ecosystem": { "const": "npm" },
31
+ "name": { "type": "string", "minLength": 1, "maxLength": 512 },
32
+ "version": { "type": "string", "minLength": 1, "maxLength": 512 }
33
+ }
34
+ },
35
+ "project": {
36
+ "type": "object",
37
+ "additionalProperties": false,
38
+ "required": ["id", "name"],
39
+ "properties": {
40
+ "id": { "type": "string", "minLength": 1, "maxLength": 256 },
41
+ "name": { "type": "string", "minLength": 1, "maxLength": 512 }
42
+ }
43
+ },
44
+ "path": {
45
+ "type": "object",
46
+ "additionalProperties": false,
47
+ "required": ["nodes", "kinds"],
48
+ "properties": {
49
+ "nodes": { "type": "array", "minItems": 2, "items": { "type": "string", "minLength": 1, "maxLength": 1024 } },
50
+ "kinds": {
51
+ "type": "array",
52
+ "items": { "enum": ["runtime", "development", "optional", "peer", "host-runtime"] }
53
+ }
54
+ }
55
+ },
56
+ "use": {
57
+ "type": "object",
58
+ "additionalProperties": false,
59
+ "required": ["pluginId", "plugin", "sources", "coverage", "paths"],
60
+ "properties": {
61
+ "pluginId": { "type": "string", "minLength": 1, "maxLength": 1024 },
62
+ "plugin": { "$ref": "#/$defs/coordinate" },
63
+ "project": { "$ref": "#/$defs/project" },
64
+ "sources": { "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 4096 } },
65
+ "coverage": { "enum": ["complete", "incomplete"] },
66
+ "paths": { "type": "array", "items": { "$ref": "#/$defs/path" } }
67
+ }
68
+ },
69
+ "dependency": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": ["dependency", "dependents"],
73
+ "properties": {
74
+ "dependency": { "$ref": "#/$defs/coordinate" },
75
+ "dependents": { "type": "array", "items": { "$ref": "#/$defs/use" } }
76
+ }
77
+ },
78
+ "pluginObservation": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": ["source", "coverage", "unresolved"],
82
+ "properties": {
83
+ "source": { "type": "string", "minLength": 1, "maxLength": 4096 },
84
+ "graphDigest": { "type": "string", "minLength": 1, "maxLength": 512 },
85
+ "coverage": { "enum": ["complete", "incomplete"] },
86
+ "unresolved": { "type": "integer", "minimum": 0 }
87
+ }
88
+ },
89
+ "plugin": {
90
+ "type": "object",
91
+ "additionalProperties": false,
92
+ "required": ["id", "plugin", "observations"],
93
+ "properties": {
94
+ "id": { "type": "string", "minLength": 1, "maxLength": 1024 },
95
+ "plugin": { "$ref": "#/$defs/coordinate" },
96
+ "project": { "$ref": "#/$defs/project" },
97
+ "observations": { "type": "array", "items": { "$ref": "#/$defs/pluginObservation" } }
98
+ }
99
+ },
100
+ "inputs": {
101
+ "type": "object",
102
+ "additionalProperties": false,
103
+ "required": ["files", "loadedFiles", "skipped"],
104
+ "properties": {
105
+ "files": { "type": "integer", "minimum": 0 },
106
+ "loadedFiles": { "type": "integer", "minimum": 0 },
107
+ "skipped": {
108
+ "type": "array",
109
+ "items": {
110
+ "type": "object",
111
+ "additionalProperties": false,
112
+ "required": ["source", "reason"],
113
+ "properties": {
114
+ "source": { "type": "string", "minLength": 1, "maxLength": 4096 },
115
+ "reason": { "type": "string", "minLength": 1, "maxLength": 2048 }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ },
121
+ "coverage": {
122
+ "type": "object",
123
+ "additionalProperties": false,
124
+ "required": ["completeObservations", "incompleteObservations", "unresolvedEdges"],
125
+ "properties": {
126
+ "completeObservations": { "type": "integer", "minimum": 0 },
127
+ "incompleteObservations": { "type": "integer", "minimum": 0 },
128
+ "unresolvedEdges": { "type": "integer", "minimum": 0 }
129
+ }
130
+ }
131
+ }
132
+ }