upstream-radar 0.33.12 → 0.35.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 (76) hide show
  1. package/README.md +100 -1020
  2. package/dist/src/cli.js +365 -19
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/dependency-index.d.ts +76 -0
  5. package/dist/src/dependency-index.d.ts.map +1 -0
  6. package/dist/src/dependency-index.js +199 -0
  7. package/dist/src/dependency-index.js.map +1 -0
  8. package/dist/src/dsh-case.d.ts +50 -0
  9. package/dist/src/dsh-case.d.ts.map +1 -0
  10. package/dist/src/dsh-case.js +88 -0
  11. package/dist/src/dsh-case.js.map +1 -0
  12. package/dist/src/dsh-review.d.ts +46 -0
  13. package/dist/src/dsh-review.d.ts.map +1 -0
  14. package/dist/src/dsh-review.js +209 -0
  15. package/dist/src/dsh-review.js.map +1 -0
  16. package/dist/src/graph.d.ts +5 -1
  17. package/dist/src/graph.d.ts.map +1 -1
  18. package/dist/src/graph.js +56 -10
  19. package/dist/src/graph.js.map +1 -1
  20. package/dist/src/index.d.ts +5 -1
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/index.js +4 -0
  23. package/dist/src/index.js.map +1 -1
  24. package/dist/src/npm.d.ts +11 -0
  25. package/dist/src/npm.d.ts.map +1 -1
  26. package/dist/src/npm.js +122 -12
  27. package/dist/src/npm.js.map +1 -1
  28. package/dist/src/render.d.ts.map +1 -1
  29. package/dist/src/render.js +19 -0
  30. package/dist/src/render.js.map +1 -1
  31. package/dist/src/scan.d.ts.map +1 -1
  32. package/dist/src/scan.js +41 -0
  33. package/dist/src/scan.js.map +1 -1
  34. package/dist/src/types.d.ts +10 -0
  35. package/dist/src/types.d.ts.map +1 -1
  36. package/dist/src/upstream-alignment.d.ts +67 -0
  37. package/dist/src/upstream-alignment.d.ts.map +1 -0
  38. package/dist/src/upstream-alignment.js +263 -0
  39. package/dist/src/upstream-alignment.js.map +1 -0
  40. package/dist/src/upstream-observer.d.ts +70 -0
  41. package/dist/src/upstream-observer.d.ts.map +1 -1
  42. package/dist/src/upstream-observer.js +387 -22
  43. package/dist/src/upstream-observer.js.map +1 -1
  44. package/dist/src/version.d.ts +1 -1
  45. package/dist/src/version.d.ts.map +1 -1
  46. package/dist/src/version.js +1 -1
  47. package/dist/src/version.js.map +1 -1
  48. package/docs/README.zh-CN.md +114 -22
  49. package/examples/cases/dsh-web-ui-issue-71/before/cordis.patch.yml +4 -0
  50. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-client-ui-skin-center/cordis.patch.yml +1 -0
  51. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-client-ui-skin-center/package.json +5 -0
  52. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-web-ui-all/cordis.patch.yml +1 -0
  53. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-web-ui-all/package.json +5 -0
  54. package/examples/cases/dsh-web-ui-issue-71/before/package.json +12 -0
  55. package/examples/cases/dsh-web-ui-issue-71/before/pnpm-lock.yaml +21 -0
  56. package/examples/cases/dsh-web-ui-issue-71/before/pnpm-workspace.yaml +7 -0
  57. package/examples/cases/dsh-web-ui-issue-71/fixed/cordis.patch.yml +3 -0
  58. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-client-ui-skin-center/cordis.patch.yml +1 -0
  59. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-client-ui-skin-center/package.json +5 -0
  60. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-web-ui-all/cordis.patch.yml +1 -0
  61. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-web-ui-all/package.json +5 -0
  62. package/examples/cases/dsh-web-ui-issue-71/fixed/package.json +12 -0
  63. package/examples/cases/dsh-web-ui-issue-71/fixed/pnpm-lock.yaml +18 -0
  64. package/examples/cases/dsh-web-ui-issue-71/fixed/pnpm-workspace.yaml +8 -0
  65. package/examples/cases/dsh-web-ui-issue-71/manual-add/cordis.patch.yml +4 -0
  66. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-center/cordis.patch.yml +1 -0
  67. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-center/package.json +5 -0
  68. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-qq98/cordis.patch.yml +3 -0
  69. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-qq98/package.json +5 -0
  70. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-web-ui-all/cordis.patch.yml +1 -0
  71. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-web-ui-all/package.json +5 -0
  72. package/examples/cases/dsh-web-ui-issue-71/manual-add/package.json +13 -0
  73. package/examples/cases/dsh-web-ui-issue-71/manual-add/pnpm-lock.yaml +21 -0
  74. package/examples/cases/dsh-web-ui-issue-71/manual-add/pnpm-workspace.yaml +5 -0
  75. package/package.json +2 -1
  76. package/schemas/upstream-downstream-ir.schema.json +87 -0
@@ -0,0 +1,199 @@
1
+ import { findDependencyPaths } from './graph.js';
2
+ export const REVERSE_DEPENDENCY_INDEX_SCHEMA = 'upstream-radar.reverse-dependency-index/v1alpha1';
3
+ const MAX_PATHS_PER_DEPENDENCY = 8;
4
+ const MAX_PATH_DEPTH = 64;
5
+ function asRecord(value) {
6
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
7
+ return undefined;
8
+ return value;
9
+ }
10
+ function asString(value) {
11
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
12
+ }
13
+ function asGraph(value) {
14
+ const graph = asRecord(value);
15
+ if (graph === undefined || graph.schema !== 'upstream-radar.dependency-graph/v1alpha1')
16
+ return undefined;
17
+ if (typeof graph.rootNodeId !== 'string' || !Array.isArray(graph.nodes) || !Array.isArray(graph.edges))
18
+ return undefined;
19
+ return graph;
20
+ }
21
+ function graphFromScanReport(value) {
22
+ const evidence = asRecord(value.evidence);
23
+ const npm = asRecord(evidence?.npm);
24
+ const audit = asRecord(npm?.dependencyAudit);
25
+ return asGraph(evidence?.dependencyGraph) ?? asGraph(audit?.graph);
26
+ }
27
+ function packageFromScanReport(value) {
28
+ const target = asRecord(value.target);
29
+ const name = asString(target?.name);
30
+ const version = asString(target?.version);
31
+ if (name === undefined || version === undefined)
32
+ return undefined;
33
+ return { ecosystem: 'npm', name, version };
34
+ }
35
+ function observationsFromScan(value, source, project) {
36
+ const plugin = packageFromScanReport(value);
37
+ const graph = graphFromScanReport(value);
38
+ if (plugin === undefined || graph === undefined)
39
+ return [];
40
+ const pluginId = project === undefined ? `${plugin.name}@${plugin.version}` : `${project.id}:${plugin.name}@${plugin.version}`;
41
+ return [{ source, pluginId, plugin, ...(project === undefined ? {} : { project }), graph }];
42
+ }
43
+ /** Convert supported scan, exact-review, or Radar config JSON into index observations. */
44
+ export function parseReverseDependencyObservations(value, source) {
45
+ const record = asRecord(value);
46
+ if (record === undefined)
47
+ throw new Error(`${source} is not a JSON object`);
48
+ const schema = asString(record.schema);
49
+ if (schema === 'upstream-radar.scan/v1alpha1')
50
+ return observationsFromScan(record, source);
51
+ if (schema === 'upstream-radar.dsh-plugin-review/v1alpha1') {
52
+ const inspection = asRecord(record.inspection);
53
+ return inspection === undefined ? [] : observationsFromScan(inspection, `${source}#inspection`);
54
+ }
55
+ if (schema === 'upstream-radar.radar-config/v1alpha1') {
56
+ if (!Array.isArray(record.projects))
57
+ throw new Error(`${source} has no projects array`);
58
+ const observations = [];
59
+ for (const [projectIndex, projectValue] of record.projects.entries()) {
60
+ const projectRecord = asRecord(projectValue);
61
+ const project = asRecord(projectRecord?.project);
62
+ const projectId = asString(project?.id);
63
+ const projectName = asString(project?.name);
64
+ if (projectId === undefined || projectName === undefined)
65
+ throw new Error(`${source} project ${projectIndex} has no id/name`);
66
+ const plugins = Array.isArray(projectRecord?.plugins) ? projectRecord.plugins : [];
67
+ for (const [pluginIndex, pluginValue] of plugins.entries()) {
68
+ const pluginRecord = asRecord(pluginValue);
69
+ const packageRecord = asRecord(pluginRecord?.package);
70
+ const name = asString(packageRecord?.name);
71
+ const version = asString(packageRecord?.version);
72
+ const graph = asGraph(pluginRecord?.graph);
73
+ if (name === undefined || version === undefined || graph === undefined) {
74
+ throw new Error(`${source} project ${projectIndex} plugin ${pluginIndex} is missing an exact package or graph`);
75
+ }
76
+ observations.push({
77
+ source: `${source}#projects/${projectIndex}/plugins/${pluginIndex}`,
78
+ pluginId: `${projectId}:${name}@${version}`,
79
+ plugin: { ecosystem: 'npm', name, version },
80
+ project: { id: projectId, name: projectName },
81
+ graph,
82
+ });
83
+ }
84
+ }
85
+ return observations;
86
+ }
87
+ throw new Error(`${source} has unsupported schema ${schema ?? '(missing)'}`);
88
+ }
89
+ function packageKey(packageCoordinate) {
90
+ return `${packageCoordinate.name}@${packageCoordinate.version}`;
91
+ }
92
+ function pathKinds(graph, path) {
93
+ const kinds = [];
94
+ for (let index = 0; index < path.length - 1; index += 1) {
95
+ const edge = graph.edges
96
+ .filter(candidate => candidate.from === path[index] && candidate.to === path[index + 1])
97
+ .sort((left, right) => left.kind.localeCompare(right.kind))[0];
98
+ if (edge === undefined)
99
+ throw new Error('dependency graph path references a missing edge');
100
+ kinds.push(edge.kind);
101
+ }
102
+ return kinds;
103
+ }
104
+ function labelsForPath(path) {
105
+ return path.map(node => `${node.name}@${node.version}`);
106
+ }
107
+ function addUnique(items, value) {
108
+ if (!items.some(item => JSON.stringify(item) === JSON.stringify(value)))
109
+ items.push(value);
110
+ }
111
+ /** Build a deterministic dependency -> plugin index from bounded graph observations. */
112
+ export function buildReverseDependencyIndex(observations, options = {}) {
113
+ const dependencies = new Map();
114
+ const plugins = new Map();
115
+ let completeObservations = 0;
116
+ let incompleteObservations = 0;
117
+ let unresolvedEdges = 0;
118
+ for (const observation of observations) {
119
+ const unresolved = observation.graph.unresolved?.length ?? 0;
120
+ const coverage = unresolved === 0 ? 'complete' : 'incomplete';
121
+ if (coverage === 'complete')
122
+ completeObservations += 1;
123
+ else
124
+ incompleteObservations += 1;
125
+ unresolvedEdges += unresolved;
126
+ const plugin = plugins.get(observation.pluginId) ?? {
127
+ id: observation.pluginId,
128
+ plugin: observation.plugin,
129
+ ...(observation.project === undefined ? {} : { project: observation.project }),
130
+ observations: [],
131
+ };
132
+ plugin.observations.push({
133
+ source: observation.source,
134
+ ...(observation.graph.digest === undefined ? {} : { graphDigest: observation.graph.digest }),
135
+ coverage,
136
+ unresolved,
137
+ });
138
+ plugins.set(observation.pluginId, plugin);
139
+ const root = observation.graph.nodes.find(node => node.id === observation.graph.rootNodeId);
140
+ if (root === undefined)
141
+ throw new Error(`${observation.source} graph root node is missing`);
142
+ for (const node of observation.graph.nodes) {
143
+ if (node.id === observation.graph.rootNodeId)
144
+ continue;
145
+ const paths = findDependencyPaths(observation.graph, node.id, {
146
+ maxPaths: MAX_PATHS_PER_DEPENDENCY,
147
+ maxDepth: MAX_PATH_DEPTH,
148
+ });
149
+ if (paths.length === 0)
150
+ continue;
151
+ const dependency = { ecosystem: 'npm', name: node.name, version: node.version };
152
+ const dependencyEntry = dependencies.get(packageKey(dependency)) ?? { dependency, dependents: [] };
153
+ const dependent = dependencyEntry.dependents.find(item => item.pluginId === observation.pluginId);
154
+ const target = dependent ?? {
155
+ pluginId: observation.pluginId,
156
+ plugin: observation.plugin,
157
+ ...(observation.project === undefined ? {} : { project: observation.project }),
158
+ sources: [],
159
+ coverage,
160
+ paths: [],
161
+ };
162
+ addUnique(target.sources, observation.source);
163
+ if (coverage === 'incomplete')
164
+ target.coverage = 'incomplete';
165
+ for (const path of paths) {
166
+ const labels = labelsForPath(path);
167
+ addUnique(target.paths, { nodes: labels, kinds: pathKinds(observation.graph, path.map(item => item.id)) });
168
+ }
169
+ if (dependent === undefined)
170
+ dependencyEntry.dependents.push(target);
171
+ dependencies.set(packageKey(dependency), dependencyEntry);
172
+ }
173
+ }
174
+ for (const plugin of plugins.values()) {
175
+ plugin.observations.sort((left, right) => left.source.localeCompare(right.source));
176
+ }
177
+ const sortedPlugins = [...plugins.values()].sort((left, right) => left.id.localeCompare(right.id));
178
+ const sortedDependencies = [...dependencies.values()].sort((left, right) => packageKey(left.dependency).localeCompare(packageKey(right.dependency)));
179
+ for (const dependency of sortedDependencies) {
180
+ dependency.dependents.sort((left, right) => left.pluginId.localeCompare(right.pluginId));
181
+ for (const dependent of dependency.dependents) {
182
+ dependent.sources.sort();
183
+ dependent.paths.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
184
+ }
185
+ }
186
+ return {
187
+ schema: REVERSE_DEPENDENCY_INDEX_SCHEMA,
188
+ generatedAt: options.generatedAt ?? new Date().toISOString(),
189
+ observations: observations.length,
190
+ inputs: options.inputs ?? { files: observations.length, loadedFiles: observations.length, skipped: [] },
191
+ coverage: { completeObservations, incompleteObservations, unresolvedEdges },
192
+ plugins: sortedPlugins,
193
+ dependencies: sortedDependencies,
194
+ };
195
+ }
196
+ export function findReverseDependencyEntry(index, packageCoordinate) {
197
+ return index.dependencies.find(item => packageKey(item.dependency) === packageKey(packageCoordinate));
198
+ }
199
+ //# sourceMappingURL=dependency-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-index.js","sourceRoot":"","sources":["../../src/dependency-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { type DshProfileCheckReport } from './dsh-profile-check.js';
2
+ export declare const DSH_CASE_SCHEMA: 'upstream-radar.dsh-case/v1alpha1';
3
+ declare const CASE_ID = "dsh-web-ui";
4
+ export interface DshCaseFinding {
5
+ code: string;
6
+ severity: string;
7
+ summary: string;
8
+ detail: string;
9
+ remediation: string;
10
+ }
11
+ export interface DshCaseCheck {
12
+ id: 'before' | 'manual-add' | 'fixed';
13
+ status: DshProfileCheckReport['status'];
14
+ packageManager: DshProfileCheckReport['packageManager'];
15
+ lockfile?: string;
16
+ dependencyNodes: number;
17
+ loaderEntries: DshProfileCheckReport['loaderEntries'];
18
+ findings: DshCaseFinding[];
19
+ }
20
+ export interface DshCaseReport {
21
+ schema: typeof DSH_CASE_SCHEMA;
22
+ case: typeof CASE_ID;
23
+ source: string;
24
+ networkFree: true;
25
+ checks: DshCaseCheck[];
26
+ conclusion: {
27
+ rootCause: string;
28
+ userImpact: string;
29
+ maintainerFix: string;
30
+ whyMonitoringMatters: string;
31
+ confidence: 'high';
32
+ };
33
+ execution: {
34
+ network: false;
35
+ installs: false;
36
+ pluginCode: false;
37
+ dshStart: false;
38
+ dshAgent: false;
39
+ llm: false;
40
+ };
41
+ }
42
+ /**
43
+ * Replay one real DSH profile failure and its maintainer fix from packaged
44
+ * static fixtures. This is intentionally independent of DSH and any model so
45
+ * the first-use command always produces a useful result.
46
+ */
47
+ export declare function createDshCaseReport(): Promise<DshCaseReport>;
48
+ export declare function renderDshCase(report: DshCaseReport): string;
49
+ export {};
50
+ //# sourceMappingURL=dsh-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsh-case.d.ts","sourceRoot":"","sources":["../../src/dsh-case.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAEpF,eAAO,MAAM,eAAe,EAAG,kCAA2C,CAAA;AAE1E,QAAA,MAAM,OAAO,eAAe,CAAA;AAS5B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAA;IACrC,MAAM,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAA;IACvC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IACrD,QAAQ,EAAE,cAAc,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,eAAe,CAAA;IAC9B,IAAI,EAAE,OAAO,OAAO,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,IAAI,CAAA;IACjB,MAAM,EAAE,YAAY,EAAE,CAAA;IACtB,UAAU,EAAE;QACV,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;QAClB,aAAa,EAAE,MAAM,CAAA;QACrB,oBAAoB,EAAE,MAAM,CAAA;QAC5B,UAAU,EAAE,MAAM,CAAA;KACnB,CAAA;IACD,SAAS,EAAE;QACT,OAAO,EAAE,KAAK,CAAA;QACd,QAAQ,EAAE,KAAK,CAAA;QACf,UAAU,EAAE,KAAK,CAAA;QACjB,QAAQ,EAAE,KAAK,CAAA;QACf,QAAQ,EAAE,KAAK,CAAA;QACf,GAAG,EAAE,KAAK,CAAA;KACX,CAAA;CACF;AAoBD;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,aAAa,CAAC,CAuClE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA0B3D"}
@@ -0,0 +1,88 @@
1
+ import { realpath } from 'node:fs/promises';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { checkDshProfile } from './dsh-profile-check.js';
5
+ export const DSH_CASE_SCHEMA = 'upstream-radar.dsh-case/v1alpha1';
6
+ const CASE_ID = 'dsh-web-ui';
7
+ const CASE_SOURCE = 'dsh-web-ui issues #71 and #35';
8
+ const FIXTURE_DIRECTORY = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', 'examples/cases/dsh-web-ui-issue-71');
9
+ function compactCheck(id, report) {
10
+ return {
11
+ id,
12
+ status: report.status,
13
+ packageManager: report.packageManager,
14
+ ...(report.lockfile === undefined ? {} : { lockfile: report.lockfile }),
15
+ dependencyNodes: report.dependencyGraph?.nodes.length ?? 0,
16
+ loaderEntries: report.loaderEntries,
17
+ findings: report.findings.map(finding => ({
18
+ code: finding.code,
19
+ severity: finding.severity,
20
+ summary: finding.summary,
21
+ detail: finding.detail,
22
+ remediation: finding.remediation,
23
+ })),
24
+ };
25
+ }
26
+ /**
27
+ * Replay one real DSH profile failure and its maintainer fix from packaged
28
+ * static fixtures. This is intentionally independent of DSH and any model so
29
+ * the first-use command always produces a useful result.
30
+ */
31
+ export async function createDshCaseReport() {
32
+ const fixtureDirectory = await realpath(FIXTURE_DIRECTORY);
33
+ const checkedAt = [
34
+ '2026-08-17T08:00:00.000Z',
35
+ '2026-08-17T08:01:00.000Z',
36
+ '2026-08-17T08:02:00.000Z',
37
+ ];
38
+ const ids = ['before', 'manual-add', 'fixed'];
39
+ const checks = [];
40
+ for (const [index, id] of ids.entries()) {
41
+ const report = await checkDshProfile({
42
+ profileDirectory: join(fixtureDirectory, id),
43
+ checkedAt: checkedAt[index],
44
+ });
45
+ checks.push(compactCheck(id, report));
46
+ }
47
+ return {
48
+ schema: DSH_CASE_SCHEMA,
49
+ case: CASE_ID,
50
+ source: CASE_SOURCE,
51
+ networkFree: true,
52
+ checks,
53
+ conclusion: {
54
+ rootCause: 'minimumReleaseAge kept the profile on an older bundle layout while the generated patch still inserted a standalone skin loader that the bundled carrier already owned.',
55
+ userImpact: 'The profile is blocked before DSH starts; manually adding the missing package creates a duplicate loader id instead of fixing the profile.',
56
+ maintainerFix: 'Upgrade the related @linxin666 packages, activate the bundled loader row, remove the standalone insert, and exempt the intended packages from the release-age cooling window.',
57
+ whyMonitoringMatters: 'The same static check catches the broken profile, the misleading manual workaround, and the repaired configuration before plugin code runs.',
58
+ confidence: 'high',
59
+ },
60
+ execution: {
61
+ network: false,
62
+ installs: false,
63
+ pluginCode: false,
64
+ dshStart: false,
65
+ dshAgent: false,
66
+ llm: false,
67
+ },
68
+ };
69
+ }
70
+ export function renderDshCase(report) {
71
+ const lines = [
72
+ 'Upstream Radar case — DSH web-ui #71',
73
+ 'Network-free; no install, plugin execution, DSH start, Agent, or LLM.',
74
+ '',
75
+ ];
76
+ for (const check of report.checks) {
77
+ const findings = check.findings.map(finding => finding.code).join(', ');
78
+ const label = check.id === 'before'
79
+ ? 'Before DSH starts'
80
+ : check.id === 'manual-add'
81
+ ? 'Manual package workaround'
82
+ : 'Maintainer fix replay';
83
+ lines.push(`${label}: ${check.status.toUpperCase()} — ${findings === '' ? 'no findings' : findings}`);
84
+ }
85
+ lines.push('', `Root cause: ${report.conclusion.rootCause}`, `User impact: ${report.conclusion.userImpact}`, `Author fix: ${report.conclusion.maintainerFix}`, `Why monitor: ${report.conclusion.whyMonitoringMatters}`, '', 'This is a local evidence replay. It does not claim that the referenced upstream issue is a live vulnerability.', '');
86
+ return `${lines.join('\n')}`;
87
+ }
88
+ //# sourceMappingURL=dsh-case.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsh-case.js","sourceRoot":"","sources":["../../src/dsh-case.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { type InspectNpmOptions } from './npm.js';
2
+ import { type DshLoadMatrixOptions, type DshLoadMatrixReport } from './dsh-probe.js';
3
+ import type { ScanReport } from './types.js';
4
+ export declare const DSH_PLUGIN_REVIEW_SCHEMA: 'upstream-radar.dsh-plugin-review/v1alpha1';
5
+ export type DshPluginReviewStatus = 'allow' | 'warn' | 'review' | 'block' | 'incompatible' | 'unknown';
6
+ export interface DshPluginReviewArtifact {
7
+ path: string;
8
+ cleanup?: () => Promise<void>;
9
+ }
10
+ export interface DshPluginReviewOptions {
11
+ dshVersions: readonly string[];
12
+ registry?: string;
13
+ timeoutMs?: number;
14
+ inspect?: (input: string, options?: InspectNpmOptions) => Promise<ScanReport>;
15
+ pack?: (input: string, options: {
16
+ registry?: string;
17
+ timeoutMs: number;
18
+ }) => Promise<DshPluginReviewArtifact>;
19
+ probe?: (options: DshLoadMatrixOptions) => Promise<DshLoadMatrixReport>;
20
+ }
21
+ export interface DshPluginReviewReport {
22
+ schema: typeof DSH_PLUGIN_REVIEW_SCHEMA;
23
+ target: {
24
+ name: string;
25
+ version: string;
26
+ spec: string;
27
+ };
28
+ inspection: ScanReport;
29
+ compatibility: DshLoadMatrixReport;
30
+ artifact: {
31
+ inspectionDigest: string;
32
+ probeSha256: string;
33
+ matched: boolean;
34
+ };
35
+ status: DshPluginReviewStatus;
36
+ nextStep: string;
37
+ execution: {
38
+ npmPackLifecycleScripts: false;
39
+ pluginCode: false;
40
+ dshBusinessActions: false;
41
+ llm: false;
42
+ };
43
+ }
44
+ export declare function reviewDshPlugin(input: string, options: DshPluginReviewOptions): Promise<DshPluginReviewReport>;
45
+ export declare function renderDshPluginReview(report: DshPluginReviewReport): string;
46
+ //# sourceMappingURL=dsh-review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsh-review.d.ts","sourceRoot":"","sources":["../../src/dsh-review.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmC,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAClF,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,eAAO,MAAM,wBAAwB,EAAG,2CAAoD,CAAA;AAE5F,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,cAAc,GAAG,SAAS,CAAA;AAEtG,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IAC7E,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAA;IAC7G,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAA;CACxE;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,wBAAwB,CAAA;IACvC,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,aAAa,EAAE,mBAAmB,CAAA;IAClC,QAAQ,EAAE;QACR,gBAAgB,EAAE,MAAM,CAAA;QACxB,WAAW,EAAE,MAAM,CAAA;QACnB,OAAO,EAAE,OAAO,CAAA;KACjB,CAAA;IACD,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE;QACT,uBAAuB,EAAE,KAAK,CAAA;QAC9B,UAAU,EAAE,KAAK,CAAA;QACjB,kBAAkB,EAAE,KAAK,CAAA;QACzB,GAAG,EAAE,KAAK,CAAA;KACX,CAAA;CACF;AAyID,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA4CpH;AAMD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAmC3E"}
@@ -0,0 +1,209 @@
1
+ import { mkdtemp, readdir, rm } from 'node:fs/promises';
2
+ import { spawn } from 'node:child_process';
3
+ import { tmpdir } from 'node:os';
4
+ import { join, resolve, sep } from 'node:path';
5
+ import { inspectNpmPackage, parseNpmSpec } from './npm.js';
6
+ import { probeDshLoadMatrix, } from './dsh-probe.js';
7
+ export const DSH_PLUGIN_REVIEW_SCHEMA = 'upstream-radar.dsh-plugin-review/v1alpha1';
8
+ const MAX_PACK_OUTPUT_BYTES = 256 * 1024;
9
+ function bounded(value, maxLength = 2_048) {
10
+ const normalized = value.replace(/[\u0000-\u001f\u007f-\u009f]/g, character => (`\\u${character.codePointAt(0)?.toString(16).padStart(4, '0') ?? '0000'}`));
11
+ return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1)}…`;
12
+ }
13
+ function runCommand(command, args, timeoutMs) {
14
+ return new Promise(resolveResult => {
15
+ const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
16
+ const stdout = [];
17
+ const stderr = [];
18
+ let stdoutBytes = 0;
19
+ let stderrBytes = 0;
20
+ let timedOut = false;
21
+ const collect = (target, chunk, current, assign) => {
22
+ if (current >= MAX_PACK_OUTPUT_BYTES)
23
+ return;
24
+ const remaining = MAX_PACK_OUTPUT_BYTES - current;
25
+ target.push(chunk.subarray(0, remaining));
26
+ assign(current + Math.min(chunk.length, remaining));
27
+ };
28
+ const timer = setTimeout(() => {
29
+ timedOut = true;
30
+ child.kill('SIGTERM');
31
+ }, timeoutMs);
32
+ child.stdout.on('data', chunk => collect(stdout, Buffer.from(chunk), stdoutBytes, value => { stdoutBytes = value; }));
33
+ child.stderr.on('data', chunk => collect(stderr, Buffer.from(chunk), stderrBytes, value => { stderrBytes = value; }));
34
+ child.on('error', () => {
35
+ clearTimeout(timer);
36
+ resolveResult({ code: null, stdout: Buffer.concat(stdout).toString('utf8'), stderr: Buffer.concat(stderr).toString('utf8'), timedOut });
37
+ });
38
+ child.on('close', code => {
39
+ clearTimeout(timer);
40
+ resolveResult({ code, stdout: Buffer.concat(stdout).toString('utf8'), stderr: Buffer.concat(stderr).toString('utf8'), timedOut });
41
+ });
42
+ });
43
+ }
44
+ async function packNpmArtifact(input, options) {
45
+ const directory = await mkdtemp(join(tmpdir(), 'upstream-radar-dsh-review-'));
46
+ const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
47
+ const args = ['pack', '--ignore-scripts', '--json', '--pack-destination', directory, input];
48
+ if (options.registry !== undefined)
49
+ args.push('--registry', options.registry);
50
+ const result = await runCommand(command, args, options.timeoutMs);
51
+ if (result.code !== 0 || result.timedOut) {
52
+ await rm(directory, { recursive: true, force: true }).catch(() => undefined);
53
+ const detail = result.timedOut ? 'npm pack timed out' : `npm pack exited with ${result.code}`;
54
+ throw new Error(`${detail}: ${bounded([result.stdout, result.stderr].filter(Boolean).join('\n'))}`);
55
+ }
56
+ const files = (await readdir(directory)).filter(file => file.endsWith('.tgz'));
57
+ if (files.length !== 1) {
58
+ await rm(directory, { recursive: true, force: true }).catch(() => undefined);
59
+ throw new Error(`npm pack produced ${files.length} tarballs; expected exactly one`);
60
+ }
61
+ const path = resolve(directory, files[0]);
62
+ if (!path.startsWith(`${resolve(directory)}${sep}`)) {
63
+ await rm(directory, { recursive: true, force: true }).catch(() => undefined);
64
+ throw new Error('npm pack returned an artifact outside its temporary directory');
65
+ }
66
+ return {
67
+ path,
68
+ cleanup: async () => rm(directory, { recursive: true, force: true, maxRetries: 2 }).catch(() => undefined),
69
+ };
70
+ }
71
+ function statusFromReports(inspection, compatibility, artifactMatched) {
72
+ if (!artifactMatched)
73
+ return 'unknown';
74
+ if (compatibility.result === 'incompatible')
75
+ return 'incompatible';
76
+ if (compatibility.result === 'unknown')
77
+ return 'unknown';
78
+ return inspection.verdict;
79
+ }
80
+ function nextStepFor(status, inspection, compatibility, artifactMatched) {
81
+ if (!artifactMatched)
82
+ return 'The inspected and probed bytes differ; stop and repeat the review against one exact artifact.';
83
+ if (status === 'incompatible')
84
+ return 'Do not admit this bundle to the affected DSH release; inspect the failed matrix stage and compare the plugin patch with the DSH release.';
85
+ if (status === 'unknown')
86
+ return 'Resolve the incomplete artifact, dependency, or DSH load evidence before treating this plugin as compatible.';
87
+ if (status === 'block')
88
+ return 'Do not install this artifact until the blocking finding is resolved.';
89
+ if (inspection.coverageVerdict === 'incomplete' && inspection.findings.length === 0) {
90
+ const unresolved = inspection.evidence.npm?.dependencyAudit.graph?.unresolved ?? [];
91
+ const provenance = inspection.evidence.npm?.provenance.status;
92
+ const gaps = [
93
+ ...(unresolved.length === 0 ? [] : [`${unresolved.length} unresolved dependency edge(s)`]),
94
+ ...(provenance !== undefined && provenance !== 'verified' ? [`npm provenance ${provenance}`] : []),
95
+ ];
96
+ return gaps.length === 0
97
+ ? 'No implemented risk finding was reported, but coverage is incomplete; inspect the raw evidence before treating the result as a clean approval.'
98
+ : `No implemented risk finding was reported; complete ${gaps.join(' and ')} before treating the result as a clean approval.`;
99
+ }
100
+ if (inspection.coverageVerdict === 'incomplete')
101
+ return 'Review the findings and complete the missing dependency or provenance evidence before treating the result as a clean approval.';
102
+ if (status === 'review' || status === 'warn')
103
+ return 'Review the listed findings before admitting the plugin; the DSH load matrix alone is not a security approval.';
104
+ return `The bundle loaded on all ${compatibility.summary.total} requested DSH versions; continue with normal dependency monitoring before upgrading production.`;
105
+ }
106
+ function renderDependencyCoverage(lines, inspection) {
107
+ const npm = inspection.evidence.npm;
108
+ const audit = npm?.dependencyAudit;
109
+ if (audit === undefined)
110
+ return;
111
+ lines.push(`Dependency graph: ${audit.packages ?? 'not resolved'} packages`, `Unresolved dependency edges: ${audit.graph?.unresolved?.length ?? 0}`, `npm registry signature: ${npm?.registrySignature.status ?? 'not-checked'}`, `npm provenance: ${npm?.provenance.status ?? 'not-checked'}`);
112
+ const unresolved = audit.graph?.unresolved ?? [];
113
+ if (unresolved.length === 0)
114
+ return;
115
+ const nodes = new Map((audit.graph?.nodes ?? []).map(node => [node.id, node]));
116
+ for (const edge of unresolved.slice(0, 3)) {
117
+ const parent = nodes.get(edge.from);
118
+ const parentLabel = parent === undefined ? edge.from : `${parent.name}@${parent.version}`;
119
+ lines.push(` Unresolved: ${display(parentLabel, 512)} -> ${display(`${edge.name}@${edge.spec}`, 512)} [${display(edge.kind, 32)}]`);
120
+ }
121
+ if (unresolved.length > 3)
122
+ lines.push(` ... ${unresolved.length - 3} more unresolved edge(s)`);
123
+ }
124
+ export async function reviewDshPlugin(input, options) {
125
+ const spec = parseNpmSpec(input);
126
+ const timeoutMs = options.timeoutMs ?? 120_000;
127
+ const inspect = options.inspect ?? inspectNpmPackage;
128
+ const pack = options.pack ?? packNpmArtifact;
129
+ const probe = options.probe ?? probeDshLoadMatrix;
130
+ const inspection = await inspect(spec.canonical, {
131
+ deep: true,
132
+ ...(options.registry === undefined ? {} : { registry: options.registry }),
133
+ timeoutMs,
134
+ });
135
+ const artifact = await pack(spec.canonical, {
136
+ ...(options.registry === undefined ? {} : { registry: options.registry }),
137
+ timeoutMs,
138
+ });
139
+ try {
140
+ const compatibility = await probe({
141
+ packagePath: artifact.path,
142
+ dshVersions: options.dshVersions,
143
+ timeoutMs,
144
+ });
145
+ const inspectionDigest = inspection.target.artifactDigest;
146
+ const probeSha256 = compatibility.artifact.sha256;
147
+ const artifactMatched = typeof probeSha256 === 'string'
148
+ && inspectionDigest === `sha256:${probeSha256}`;
149
+ const status = statusFromReports(inspection, compatibility, artifactMatched);
150
+ return {
151
+ schema: DSH_PLUGIN_REVIEW_SCHEMA,
152
+ target: { name: spec.name, version: spec.version, spec: spec.canonical },
153
+ inspection,
154
+ compatibility,
155
+ artifact: { inspectionDigest, probeSha256: probeSha256 ?? 'unknown', matched: artifactMatched },
156
+ status,
157
+ nextStep: nextStepFor(status, inspection, compatibility, artifactMatched),
158
+ execution: {
159
+ npmPackLifecycleScripts: false,
160
+ pluginCode: false,
161
+ dshBusinessActions: false,
162
+ llm: false,
163
+ },
164
+ };
165
+ }
166
+ finally {
167
+ await artifact.cleanup?.();
168
+ }
169
+ }
170
+ function display(value, maxLength = 1_024) {
171
+ return bounded(value, maxLength);
172
+ }
173
+ export function renderDshPluginReview(report) {
174
+ const vulnerabilities = report.inspection.evidence.npm?.dependencyAudit.vulnerabilities;
175
+ const installScriptPackages = report.inspection.evidence.npm?.dependencyAudit.installScriptPackages;
176
+ const installScriptDetails = report.inspection.evidence.npm?.dependencyAudit.installScriptDetails;
177
+ const lines = [
178
+ 'Upstream Radar — DSH plugin review',
179
+ `Plugin: ${display(`${report.target.name}@${report.target.version}`)}`,
180
+ `Overall: ${report.status.toUpperCase()}`,
181
+ `Exact artifact: ${report.artifact.matched ? 'same bytes for inspection and DSH probe' : 'MISMATCH'}`,
182
+ `Dependency review: ${report.inspection.verdict.toUpperCase()} (coverage ${report.inspection.coverageVerdict})`,
183
+ `Known vulnerabilities: ${vulnerabilities === null || vulnerabilities === undefined ? 'not checked' : vulnerabilities.total}`,
184
+ `Install-time dependency scripts: ${installScriptPackages === undefined ? 'not checked' : installScriptPackages.length === 0 ? '0' : installScriptPackages.join(', ')}`,
185
+ `DSH load matrix: ${report.compatibility.result.toUpperCase()} (${report.compatibility.summary.compatible}/${report.compatibility.summary.total} versions loaded)`,
186
+ ];
187
+ renderDependencyCoverage(lines, report.inspection);
188
+ for (const item of report.compatibility.reports) {
189
+ lines.push(` DSH ${display(item.dshVersion, 128)}: ${item.result.toUpperCase()} — ${display(item.reason, 1_024)}`);
190
+ }
191
+ for (const detail of installScriptDetails ?? []) {
192
+ for (const script of detail.scripts)
193
+ lines.push(` ${display(`${detail.package} ${script.name}: ${script.command}`, 2_048)}`);
194
+ }
195
+ if (report.inspection.findings.length === 0) {
196
+ lines.push('Findings: none in the implemented static checks');
197
+ }
198
+ else {
199
+ lines.push(`Findings: ${report.inspection.findings.length}`);
200
+ for (const finding of report.inspection.findings.slice(0, 8)) {
201
+ lines.push(` [${finding.severity.toUpperCase()}] ${display(finding.code)}: ${display(finding.summary)}`);
202
+ if (finding.remediation !== undefined)
203
+ lines.push(` Fix: ${display(finding.remediation, 2_048)}`);
204
+ }
205
+ }
206
+ lines.push(`Next: ${display(report.nextStep, 2_048)}`, 'Boundary: lifecycle scripts disabled; plugin code and business actions not executed; no LLM called.');
207
+ return `${lines.join('\n')}\n`;
208
+ }
209
+ //# sourceMappingURL=dsh-review.js.map