@goplus/agentguard 1.1.13 → 1.1.18

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 (72) hide show
  1. package/README.md +8 -2
  2. package/dist/adapters/openclaw-plugin.d.ts.map +1 -1
  3. package/dist/adapters/openclaw-plugin.js +6 -18
  4. package/dist/adapters/openclaw-plugin.js.map +1 -1
  5. package/dist/cli.js +423 -55
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cloud/client.d.ts +16 -2
  8. package/dist/cloud/client.d.ts.map +1 -1
  9. package/dist/cloud/client.js +56 -9
  10. package/dist/cloud/client.js.map +1 -1
  11. package/dist/cloud/openclaw-notify.d.ts +18 -0
  12. package/dist/cloud/openclaw-notify.d.ts.map +1 -0
  13. package/dist/cloud/openclaw-notify.js +69 -0
  14. package/dist/cloud/openclaw-notify.js.map +1 -0
  15. package/dist/config.d.ts +11 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +41 -0
  18. package/dist/config.js.map +1 -1
  19. package/dist/feed/cron.d.ts +1 -0
  20. package/dist/feed/cron.d.ts.map +1 -1
  21. package/dist/feed/cron.js +309 -19
  22. package/dist/feed/cron.js.map +1 -1
  23. package/dist/feed/selfcheck.d.ts.map +1 -1
  24. package/dist/feed/selfcheck.js +470 -23
  25. package/dist/feed/selfcheck.js.map +1 -1
  26. package/dist/feed/state.d.ts +5 -7
  27. package/dist/feed/state.d.ts.map +1 -1
  28. package/dist/feed/state.js +53 -22
  29. package/dist/feed/state.js.map +1 -1
  30. package/dist/feed/types.d.ts +17 -14
  31. package/dist/feed/types.d.ts.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +3 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/installers.js +81 -19
  37. package/dist/installers.js.map +1 -1
  38. package/dist/runtime/evaluator.d.ts.map +1 -1
  39. package/dist/runtime/evaluator.js +45 -3
  40. package/dist/runtime/evaluator.js.map +1 -1
  41. package/dist/runtime/protect.d.ts.map +1 -1
  42. package/dist/runtime/protect.js +9 -2
  43. package/dist/runtime/protect.js.map +1 -1
  44. package/dist/tests/cli-checkup.test.js +29 -1
  45. package/dist/tests/cli-checkup.test.js.map +1 -1
  46. package/dist/tests/cli-connect.test.d.ts +2 -0
  47. package/dist/tests/cli-connect.test.d.ts.map +1 -0
  48. package/dist/tests/cli-connect.test.js +209 -0
  49. package/dist/tests/cli-connect.test.js.map +1 -0
  50. package/dist/tests/cli-init.test.js +28 -0
  51. package/dist/tests/cli-init.test.js.map +1 -1
  52. package/dist/tests/cli-policy.test.js +72 -0
  53. package/dist/tests/cli-policy.test.js.map +1 -1
  54. package/dist/tests/cli-subscribe.test.js +237 -2
  55. package/dist/tests/cli-subscribe.test.js.map +1 -1
  56. package/dist/tests/feed-cloud.test.js +45 -1
  57. package/dist/tests/feed-cloud.test.js.map +1 -1
  58. package/dist/tests/feed-cron.test.js +274 -19
  59. package/dist/tests/feed-cron.test.js.map +1 -1
  60. package/dist/tests/feed-selfcheck.test.js +61 -13
  61. package/dist/tests/feed-selfcheck.test.js.map +1 -1
  62. package/dist/tests/feed-state.test.js +49 -10
  63. package/dist/tests/feed-state.test.js.map +1 -1
  64. package/dist/tests/installer.test.js +36 -0
  65. package/dist/tests/installer.test.js.map +1 -1
  66. package/dist/tests/integration.test.js +41 -9
  67. package/dist/tests/integration.test.js.map +1 -1
  68. package/dist/tests/runtime-cloud.test.js +65 -0
  69. package/dist/tests/runtime-cloud.test.js.map +1 -1
  70. package/openclaw.plugin.json +4 -0
  71. package/package.json +1 -1
  72. package/skills/agentguard/SKILL.md +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"selfcheck.d.ts","sourceRoot":"","sources":["../../src/feed/selfcheck.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EACV,QAAQ,EAIR,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,UAK/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,UAKhC,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAOpC,CAAC;AAEF,eAAO,MAAM,0BAA0B,UAatC,CAAC;AAEF,eAAO,MAAM,sBAAsB,UAOlC,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AASD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,eAAe,CAAC,CA+B1B;AA8PD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAiBpE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAOjE"}
1
+ {"version":3,"file":"selfcheck.d.ts","sourceRoot":"","sources":["../../src/feed/selfcheck.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EACV,QAAQ,EAIR,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,UAK/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,UAKhC,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAOpC,CAAC;AAEF,eAAO,MAAM,0BAA0B,UAatC,CAAC;AAEF,eAAO,MAAM,sBAAsB,UAOlC,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAcD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,eAAe,CAAC,CAyC1B;AAotBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAiBpE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAOjE"}
@@ -13,6 +13,7 @@ exports.safeRegexTest = safeRegexTest;
13
13
  exports.globMatch = globMatch;
14
14
  const node_fs_1 = require("node:fs");
15
15
  const promises_1 = require("node:fs/promises");
16
+ const glob_1 = require("glob");
16
17
  const node_os_1 = require("node:os");
17
18
  const node_path_1 = require("node:path");
18
19
  const hash_js_1 = require("../utils/hash.js");
@@ -78,6 +79,15 @@ async function runSelfCheckForAdvisory(advisory, options = {}) {
78
79
  if (advisory.withdrawnAt) {
79
80
  return { advisoryId: advisory.id, matchedArtifacts: [], elapsedMs: 0, warnings };
80
81
  }
82
+ const matchers = getSelfCheckMatchers(advisory);
83
+ if (matchers.length === 0) {
84
+ return {
85
+ advisoryId: advisory.id,
86
+ matchedArtifacts: [],
87
+ elapsedMs: Date.now() - startedAt,
88
+ warnings,
89
+ };
90
+ }
81
91
  const artifacts = await listArtifactsForAdvisory(advisory, options, warnings);
82
92
  const cap = options.maxArtifacts ?? 500;
83
93
  const considered = artifacts.slice(0, cap);
@@ -86,7 +96,7 @@ async function runSelfCheckForAdvisory(advisory, options = {}) {
86
96
  }
87
97
  for (const artifact of considered) {
88
98
  try {
89
- const m = await matchArtifact(artifact, advisory.affected);
99
+ const m = await matchArtifact(artifact, advisory.ecosystem, matchers);
90
100
  if (m)
91
101
  matches.push(m);
92
102
  }
@@ -104,7 +114,7 @@ async function runSelfCheckForAdvisory(advisory, options = {}) {
104
114
  async function listArtifactsForAdvisory(advisory, options, warnings) {
105
115
  const overridePaths = advisory.selfCheck?.inspectPaths?.filter((p) => typeof p === 'string') ?? [];
106
116
  if (overridePaths.length > 0) {
107
- return listExplicitArtifacts(overridePaths);
117
+ return listExplicitArtifacts(advisory.ecosystem, overridePaths, options);
108
118
  }
109
119
  switch (advisory.ecosystem) {
110
120
  case 'skill':
@@ -128,24 +138,28 @@ async function listArtifactsForAdvisory(advisory, options, warnings) {
128
138
  return [];
129
139
  }
130
140
  }
131
- async function listExplicitArtifacts(paths) {
132
- const artifacts = [];
133
- for (const path of paths) {
134
- if (!(0, node_fs_1.existsSync)(path))
135
- continue;
136
- artifacts.push({
137
- path,
138
- name: (0, node_path_1.basename)(path),
139
- bodyPath: firstExisting([
140
- (0, node_path_1.join)(path, 'SKILL.md'),
141
- (0, node_path_1.join)(path, 'openclaw.plugin.json'),
142
- (0, node_path_1.join)(path, 'package.json'),
143
- (0, node_path_1.join)(path, 'plugin.json'),
141
+ async function listExplicitArtifacts(ecosystem, paths, options) {
142
+ const expanded = await expandInspectPaths(paths);
143
+ if (expanded.length === 0)
144
+ return [];
145
+ switch (ecosystem) {
146
+ case 'skill':
147
+ return (await listSkillDirs(expanded)).map((path) => ({
144
148
  path,
145
- ]) ?? path,
146
- });
149
+ name: (0, node_path_1.basename)(path),
150
+ bodyPath: (0, node_path_1.join)(path, 'SKILL.md'),
151
+ }));
152
+ case 'plugin':
153
+ return listPluginArtifacts(expanded);
154
+ case 'prompt_injection':
155
+ return listPromptInjectionArtifacts({ ...options, promptInjectionRoots: expanded });
156
+ case 'mcp_server':
157
+ case 'supply_chain':
158
+ case 'url':
159
+ return listFileArtifacts(expanded);
160
+ default:
161
+ return [];
147
162
  }
148
- return artifacts;
149
163
  }
150
164
  /** Enumerate every immediate subdirectory of `roots` that contains a SKILL.md. */
151
165
  async function listSkillDirs(roots) {
@@ -153,6 +167,11 @@ async function listSkillDirs(roots) {
153
167
  for (const root of roots) {
154
168
  if (!(0, node_fs_1.existsSync)(root))
155
169
  continue;
170
+ const rootManifest = (0, node_path_1.join)(root, 'SKILL.md');
171
+ if ((0, node_fs_1.existsSync)(rootManifest)) {
172
+ found.push(root);
173
+ continue;
174
+ }
156
175
  let entries;
157
176
  try {
158
177
  entries = await (0, promises_1.readdir)(root, { withFileTypes: true });
@@ -176,6 +195,18 @@ async function listPluginArtifacts(roots) {
176
195
  for (const root of roots) {
177
196
  if (!(0, node_fs_1.existsSync)(root))
178
197
  continue;
198
+ const rootManifest = firstExisting([
199
+ (0, node_path_1.join)(root, 'openclaw.plugin.json'),
200
+ (0, node_path_1.join)(root, 'package.json'),
201
+ (0, node_path_1.join)(root, '.claude-plugin', 'plugin.json'),
202
+ (0, node_path_1.join)(root, 'plugin.json'),
203
+ (0, node_path_1.join)(root, 'index.js'),
204
+ (0, node_path_1.join)(root, 'index.ts'),
205
+ ]);
206
+ if (rootManifest) {
207
+ found.push({ path: root, name: (0, node_path_1.basename)(root), bodyPath: rootManifest });
208
+ continue;
209
+ }
179
210
  let entries;
180
211
  try {
181
212
  entries = await (0, promises_1.readdir)(root, { withFileTypes: true });
@@ -224,6 +255,26 @@ async function listPromptInjectionArtifacts(options) {
224
255
  const plugins = await listPluginArtifacts(roots);
225
256
  return dedupeArtifacts([...skills, ...plugins]);
226
257
  }
258
+ async function expandInspectPaths(paths) {
259
+ const expanded = [];
260
+ for (const rawPath of paths) {
261
+ const pattern = expandHomeDir(rawPath);
262
+ if (!pattern)
263
+ continue;
264
+ if (hasGlobMagic(pattern)) {
265
+ const matches = await (0, glob_1.glob)(pattern, { absolute: true, nodir: false });
266
+ for (const match of matches) {
267
+ if ((0, node_fs_1.existsSync)(match))
268
+ expanded.push(match);
269
+ }
270
+ continue;
271
+ }
272
+ const resolved = (0, node_path_1.isAbsolute)(pattern) ? pattern : (0, node_path_1.resolve)(pattern);
273
+ if ((0, node_fs_1.existsSync)(resolved))
274
+ expanded.push(resolved);
275
+ }
276
+ return dedupePaths(expanded);
277
+ }
227
278
  function firstExisting(paths) {
228
279
  for (const path of paths) {
229
280
  if ((0, node_fs_1.existsSync)(path))
@@ -231,6 +282,21 @@ function firstExisting(paths) {
231
282
  }
232
283
  return null;
233
284
  }
285
+ function expandHomeDir(path) {
286
+ if (!path.startsWith('~'))
287
+ return path;
288
+ if (path === '~')
289
+ return (0, node_os_1.homedir)();
290
+ if (path.startsWith('~/'))
291
+ return (0, node_path_1.join)((0, node_os_1.homedir)(), path.slice(2));
292
+ return path;
293
+ }
294
+ function hasGlobMagic(path) {
295
+ return /[*?[\]{}]/.test(path);
296
+ }
297
+ function dedupePaths(paths) {
298
+ return [...new Set(paths)];
299
+ }
234
300
  function dedupeArtifacts(artifacts) {
235
301
  const seen = new Set();
236
302
  const result = [];
@@ -243,11 +309,11 @@ function dedupeArtifacts(artifacts) {
243
309
  return result;
244
310
  }
245
311
  /**
246
- * Match one local artifact against an advisory's affected[] matchers.
247
- * Returns the first match found (per matcher precedence: hash > regex > name).
312
+ * Match one local artifact against an advisory matcher set.
313
+ * Returns the first match found (per matcher precedence: hash > version > regex > name).
248
314
  * Returns null when nothing matched.
249
315
  */
250
- async function matchArtifact(artifact, affected) {
316
+ async function matchArtifact(artifact, ecosystem, affected) {
251
317
  const bodyPath = artifact.bodyPath ?? artifact.path;
252
318
  let localHash = null;
253
319
  const wantsHash = affected.some((m) => m.sha256);
@@ -260,14 +326,21 @@ async function matchArtifact(artifact, affected) {
260
326
  }
261
327
  }
262
328
  let body = null;
263
- const wantsBody = affected.some((m) => m.bodyRegex || m.urlPattern || m.domainExact);
329
+ const wantsBody = affected.some((m) => m.bodyRegex || m.urlPattern || m.domainExact || m.namePattern || m.versionRange);
264
330
  if (wantsBody) {
265
331
  body = await readArtifactBody(artifact, bodyPath);
266
332
  }
333
+ const coordinates = body !== null ? extractPackageCoordinates(artifact, ecosystem, body) : [];
267
334
  for (const matcher of affected) {
335
+ if (!matcherMatchesCoordinateSet(matcher, artifact, coordinates)) {
336
+ continue;
337
+ }
268
338
  if (matcher.sha256 && localHash && matcher.sha256.toLowerCase() === localHash.toLowerCase()) {
269
339
  return { path: artifact.path, matchedBy: 'sha256', hash: localHash };
270
340
  }
341
+ if (matcher.versionRange && coordinates.some((candidate) => versionSatisfiesRange(candidate.version, matcher.versionRange))) {
342
+ return { path: artifact.path, matchedBy: 'versionRange' };
343
+ }
271
344
  if (matcher.bodyRegex && body !== null) {
272
345
  if (safeRegexTest(matcher.bodyRegex, body)) {
273
346
  return { path: artifact.path, matchedBy: 'bodyRegex' };
@@ -279,9 +352,12 @@ async function matchArtifact(artifact, affected) {
279
352
  if (matcher.domainExact && body !== null && bodyContainsDomain(body, matcher.domainExact)) {
280
353
  return { path: artifact.path, matchedBy: 'domainExact' };
281
354
  }
282
- if (matcher.namePattern && globMatch(matcher.namePattern, artifact.name)) {
355
+ if (matcher.namePattern) {
283
356
  return { path: artifact.path, matchedBy: 'namePattern' };
284
357
  }
358
+ if (matcher.sha256 || matcher.versionRange || matcher.bodyRegex || matcher.urlPattern || matcher.domainExact) {
359
+ continue;
360
+ }
285
361
  }
286
362
  return null;
287
363
  }
@@ -327,6 +403,377 @@ function containsBareDomain(body, domain) {
327
403
  const re = new RegExp(`(^|[^A-Za-z0-9.-])${escaped}([^A-Za-z0-9.-]|$)`, 'i');
328
404
  return re.test(body);
329
405
  }
406
+ function getSelfCheckMatchers(advisory) {
407
+ if (advisory.selfCheck && Array.isArray(advisory.selfCheck.matchers)) {
408
+ return advisory.selfCheck.matchers.filter(isMatcherLike).map((matcher) => ({
409
+ namePattern: matcher.namePattern,
410
+ sha256: matcher.sha256,
411
+ versionRange: matcher.versionRange,
412
+ bodyRegex: matcher.bodyRegex,
413
+ urlPattern: matcher.urlPattern,
414
+ domainExact: matcher.domainExact,
415
+ }));
416
+ }
417
+ return Array.isArray(advisory.affected) ? advisory.affected.filter(isMatcherLike) : [];
418
+ }
419
+ function isMatcherLike(value) {
420
+ if (!value || typeof value !== 'object')
421
+ return false;
422
+ const matcher = value;
423
+ return [
424
+ 'namePattern',
425
+ 'sha256',
426
+ 'versionRange',
427
+ 'bodyRegex',
428
+ 'urlPattern',
429
+ 'domainExact',
430
+ ].some((key) => typeof matcher[key] === 'string' && matcher[key].length > 0);
431
+ }
432
+ function matcherMatchesCoordinateSet(matcher, artifact, coordinates) {
433
+ const needsCoordinate = Boolean(matcher.namePattern || matcher.versionRange);
434
+ if (!needsCoordinate)
435
+ return true;
436
+ const candidates = dedupeCoordinates([
437
+ ...coordinates,
438
+ { name: artifact.name, version: undefined },
439
+ { name: stripExtension(artifact.name), version: undefined },
440
+ ]);
441
+ return candidates.some((candidate) => {
442
+ if (matcher.namePattern && !globMatch(matcher.namePattern, candidate.name)) {
443
+ return false;
444
+ }
445
+ if (matcher.versionRange && !versionSatisfiesRange(candidate.version, matcher.versionRange)) {
446
+ return false;
447
+ }
448
+ return true;
449
+ });
450
+ }
451
+ function extractPackageCoordinates(artifact, ecosystem, body) {
452
+ const coordinates = [
453
+ ...extractManifestCoordinate(artifact, body),
454
+ ...extractSupplyChainCoordinates(artifact, ecosystem, body),
455
+ ];
456
+ return dedupeCoordinates(coordinates);
457
+ }
458
+ function extractManifestCoordinate(artifact, body) {
459
+ const json = tryParseJson(body);
460
+ if (json && typeof json === 'object' && !Array.isArray(json)) {
461
+ const name = typeof json.name === 'string' ? json.name : artifact.name;
462
+ const version = typeof json.version === 'string' ? json.version : undefined;
463
+ return [{ name, version }];
464
+ }
465
+ const skillName = body.match(/^\s*name:\s*["']?([^\n"']+)["']?\s*$/im)?.[1]?.trim();
466
+ const skillVersion = body.match(/^\s*version:\s*["']?([^\n"']+)["']?\s*$/im)?.[1]?.trim();
467
+ if (skillName || skillVersion) {
468
+ return [{
469
+ name: skillName || artifact.name,
470
+ version: skillVersion,
471
+ }];
472
+ }
473
+ return [];
474
+ }
475
+ function extractSupplyChainCoordinates(artifact, ecosystem, body) {
476
+ if (ecosystem !== 'supply_chain')
477
+ return [];
478
+ const filename = (0, node_path_1.basename)(artifact.path);
479
+ switch (filename) {
480
+ case 'package.json':
481
+ return extractPackageJsonDependencies(body);
482
+ case 'package-lock.json':
483
+ case 'npm-shrinkwrap.json':
484
+ return extractPackageLockDependencies(body);
485
+ case 'requirements.txt':
486
+ return extractRequirementsDependencies(body);
487
+ case 'Cargo.toml':
488
+ return extractCargoTomlDependencies(body);
489
+ case 'Cargo.lock':
490
+ return extractCargoLockDependencies(body);
491
+ case 'go.mod':
492
+ return extractGoModDependencies(body);
493
+ case 'go.sum':
494
+ return extractGoSumDependencies(body);
495
+ default:
496
+ return [];
497
+ }
498
+ }
499
+ function extractPackageJsonDependencies(body) {
500
+ const json = tryParseJson(body);
501
+ if (!json || typeof json !== 'object' || Array.isArray(json))
502
+ return [];
503
+ const fields = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies', 'overrides', 'resolutions'];
504
+ const coordinates = [];
505
+ for (const field of fields) {
506
+ const deps = json[field];
507
+ if (!deps || typeof deps !== 'object' || Array.isArray(deps))
508
+ continue;
509
+ for (const [name, rawVersion] of Object.entries(deps)) {
510
+ if (typeof rawVersion !== 'string')
511
+ continue;
512
+ coordinates.push({ name, version: normalizeVersionCandidate(rawVersion) ?? rawVersion.trim() });
513
+ }
514
+ }
515
+ return coordinates;
516
+ }
517
+ function extractPackageLockDependencies(body) {
518
+ const json = tryParseJson(body);
519
+ if (!json || typeof json !== 'object' || Array.isArray(json))
520
+ return [];
521
+ const coordinates = [];
522
+ const root = json;
523
+ collectPackageLockDeps(root.dependencies, coordinates);
524
+ if (root.packages && typeof root.packages === 'object' && !Array.isArray(root.packages)) {
525
+ for (const [packagePath, meta] of Object.entries(root.packages)) {
526
+ if (!meta || typeof meta !== 'object' || Array.isArray(meta))
527
+ continue;
528
+ const pkg = meta;
529
+ const version = typeof pkg.version === 'string' ? pkg.version : undefined;
530
+ const name = typeof pkg.name === 'string'
531
+ ? pkg.name
532
+ : packagePath.startsWith('node_modules/')
533
+ ? packagePath.slice('node_modules/'.length)
534
+ : '';
535
+ if (name)
536
+ coordinates.push({ name, version });
537
+ }
538
+ }
539
+ return coordinates;
540
+ }
541
+ function collectPackageLockDeps(node, coordinates) {
542
+ if (!node || typeof node !== 'object' || Array.isArray(node))
543
+ return;
544
+ for (const [name, meta] of Object.entries(node)) {
545
+ if (!meta || typeof meta !== 'object' || Array.isArray(meta))
546
+ continue;
547
+ const pkg = meta;
548
+ const version = typeof pkg.version === 'string' ? pkg.version : undefined;
549
+ coordinates.push({ name, version });
550
+ collectPackageLockDeps(pkg.dependencies, coordinates);
551
+ }
552
+ }
553
+ function extractRequirementsDependencies(body) {
554
+ const coordinates = [];
555
+ for (const line of body.split(/\r?\n/)) {
556
+ const trimmed = line.trim();
557
+ if (!trimmed || trimmed.startsWith('#'))
558
+ continue;
559
+ const match = trimmed.match(/^([A-Za-z0-9_.-]+)\s*(?:==|>=|<=|~=|!=|>|<)\s*([A-Za-z0-9+_.-]+)/);
560
+ if (match) {
561
+ coordinates.push({ name: match[1], version: normalizeVersionCandidate(match[2]) ?? match[2] });
562
+ }
563
+ }
564
+ return coordinates;
565
+ }
566
+ function extractCargoTomlDependencies(body) {
567
+ const coordinates = [];
568
+ let inDependencies = false;
569
+ for (const line of body.split(/\r?\n/)) {
570
+ const trimmed = line.trim();
571
+ if (trimmed.startsWith('[')) {
572
+ inDependencies = /^\[(?:workspace\.)?(?:target\.[^.\]]+\.)?(?:dev-|build-)?dependencies\]/.test(trimmed);
573
+ continue;
574
+ }
575
+ if (!inDependencies || !trimmed || trimmed.startsWith('#'))
576
+ continue;
577
+ const match = trimmed.match(/^([A-Za-z0-9_.-]+)\s*=\s*(?:\{\s*version\s*=\s*"([^"]+)"[^}]*\}|"([^"]+)")/);
578
+ if (match) {
579
+ coordinates.push({ name: match[1], version: normalizeVersionCandidate(match[2] || match[3]) ?? (match[2] || match[3]) });
580
+ }
581
+ }
582
+ return coordinates;
583
+ }
584
+ function extractCargoLockDependencies(body) {
585
+ const coordinates = [];
586
+ const re = /\[\[package\]\][\s\S]*?name = "([^"]+)"[\s\S]*?version = "([^"]+)"/g;
587
+ for (const match of body.matchAll(re)) {
588
+ coordinates.push({ name: match[1], version: normalizeVersionCandidate(match[2]) ?? match[2] });
589
+ }
590
+ return coordinates;
591
+ }
592
+ function extractGoModDependencies(body) {
593
+ const coordinates = [];
594
+ for (const line of body.split(/\r?\n/)) {
595
+ const trimmed = line.trim();
596
+ if (!trimmed || trimmed.startsWith('//'))
597
+ continue;
598
+ const match = trimmed.match(/^(?:require\s+)?(\S+)\s+v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
599
+ if (match) {
600
+ coordinates.push({ name: match[1], version: normalizeVersionCandidate(match[2]) ?? match[2] });
601
+ }
602
+ }
603
+ return coordinates;
604
+ }
605
+ function extractGoSumDependencies(body) {
606
+ const coordinates = [];
607
+ for (const line of body.split(/\r?\n/)) {
608
+ const trimmed = line.trim();
609
+ if (!trimmed)
610
+ continue;
611
+ const match = trimmed.match(/^(\S+)\s+v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
612
+ if (match) {
613
+ coordinates.push({ name: match[1], version: normalizeVersionCandidate(match[2]) ?? match[2] });
614
+ }
615
+ }
616
+ return coordinates;
617
+ }
618
+ function tryParseJson(body) {
619
+ try {
620
+ return JSON.parse(body);
621
+ }
622
+ catch {
623
+ return null;
624
+ }
625
+ }
626
+ function stripExtension(name) {
627
+ const extension = (0, node_path_1.extname)(name);
628
+ return extension ? name.slice(0, -extension.length) : name;
629
+ }
630
+ function dedupeCoordinates(coordinates) {
631
+ const seen = new Set();
632
+ const result = [];
633
+ for (const coordinate of coordinates) {
634
+ const normalizedName = coordinate.name.trim();
635
+ if (!normalizedName)
636
+ continue;
637
+ const key = `${normalizedName}\u0000${coordinate.version ?? ''}`;
638
+ if (seen.has(key))
639
+ continue;
640
+ seen.add(key);
641
+ result.push({ name: normalizedName, version: coordinate.version?.trim() });
642
+ }
643
+ return result;
644
+ }
645
+ function versionSatisfiesRange(version, range) {
646
+ if (!range)
647
+ return true;
648
+ const parsedVersion = parseSemver(version);
649
+ if (!parsedVersion)
650
+ return false;
651
+ const comparators = parseComparators(range);
652
+ if (comparators.length === 0)
653
+ return false;
654
+ return comparators.every((comparator) => compareSemver(parsedVersion, comparator.version, comparator.operator));
655
+ }
656
+ function normalizeVersionCandidate(value) {
657
+ if (!value)
658
+ return null;
659
+ const exact = value.trim().match(/^v?(\d+(?:\.\d+){0,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/);
660
+ return exact ? exact[1] : null;
661
+ }
662
+ function parseSemver(input) {
663
+ if (!input)
664
+ return null;
665
+ const match = input.trim().match(/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
666
+ if (!match)
667
+ return null;
668
+ return {
669
+ major: Number(match[1]),
670
+ minor: Number(match[2] ?? '0'),
671
+ patch: Number(match[3] ?? '0'),
672
+ prerelease: match[4],
673
+ };
674
+ }
675
+ function parseComparators(range) {
676
+ const trimmed = range.trim();
677
+ if (!trimmed || trimmed === '*')
678
+ return [];
679
+ if (trimmed.startsWith('^')) {
680
+ const base = parseSemver(trimmed.slice(1));
681
+ if (!base)
682
+ return [];
683
+ return [
684
+ { operator: '>=', version: base },
685
+ { operator: '<', version: caretUpperBound(base) },
686
+ ];
687
+ }
688
+ if (trimmed.startsWith('~')) {
689
+ const base = parseSemver(trimmed.slice(1));
690
+ if (!base)
691
+ return [];
692
+ return [
693
+ { operator: '>=', version: base },
694
+ { operator: '<', version: { major: base.major, minor: base.minor + 1, patch: 0 } },
695
+ ];
696
+ }
697
+ const wildcard = parseWildcardRange(trimmed);
698
+ if (wildcard)
699
+ return wildcard;
700
+ const tokens = trimmed.split(/[\s,]+/).filter(Boolean);
701
+ const comparators = [];
702
+ for (const token of tokens) {
703
+ const match = token.match(/^(<=|>=|<|>|=)?(.+)$/);
704
+ if (!match)
705
+ return [];
706
+ const operator = (match[1] ?? '=');
707
+ const version = parseSemver(match[2]);
708
+ if (!version)
709
+ return [];
710
+ comparators.push({ operator, version });
711
+ }
712
+ return comparators;
713
+ }
714
+ function parseWildcardRange(range) {
715
+ const match = range.match(/^v?(\d+)(?:\.(\d+|x|\*))?(?:\.(\d+|x|\*))?$/i);
716
+ if (!match)
717
+ return null;
718
+ if (![match[2], match[3]].some((part) => part && /^(x|\*)$/i.test(part))) {
719
+ return null;
720
+ }
721
+ const major = Number(match[1]);
722
+ const minorPart = match[2];
723
+ const patchPart = match[3];
724
+ if (!minorPart || /^(x|\*)$/i.test(minorPart)) {
725
+ return [
726
+ { operator: '>=', version: { major, minor: 0, patch: 0 } },
727
+ { operator: '<', version: { major: major + 1, minor: 0, patch: 0 } },
728
+ ];
729
+ }
730
+ const minor = Number(minorPart);
731
+ if (!patchPart || /^(x|\*)$/i.test(patchPart)) {
732
+ return [
733
+ { operator: '>=', version: { major, minor, patch: 0 } },
734
+ { operator: '<', version: { major, minor: minor + 1, patch: 0 } },
735
+ ];
736
+ }
737
+ return null;
738
+ }
739
+ function caretUpperBound(version) {
740
+ if (version.major > 0) {
741
+ return { major: version.major + 1, minor: 0, patch: 0 };
742
+ }
743
+ if (version.minor > 0) {
744
+ return { major: 0, minor: version.minor + 1, patch: 0 };
745
+ }
746
+ return { major: 0, minor: 0, patch: version.patch + 1 };
747
+ }
748
+ function compareSemver(left, right, operator) {
749
+ const order = semverCompare(left, right);
750
+ switch (operator) {
751
+ case '<':
752
+ return order < 0;
753
+ case '<=':
754
+ return order <= 0;
755
+ case '>':
756
+ return order > 0;
757
+ case '>=':
758
+ return order >= 0;
759
+ case '=':
760
+ return order === 0;
761
+ }
762
+ }
763
+ function semverCompare(left, right) {
764
+ for (const key of ['major', 'minor', 'patch']) {
765
+ const delta = left[key] - right[key];
766
+ if (delta !== 0)
767
+ return delta;
768
+ }
769
+ if (!left.prerelease && !right.prerelease)
770
+ return 0;
771
+ if (!left.prerelease)
772
+ return 1;
773
+ if (!right.prerelease)
774
+ return -1;
775
+ return left.prerelease.localeCompare(right.prerelease);
776
+ }
330
777
  /**
331
778
  * Defense against catastrophic backtracking and malformed regex coming
332
779
  * from upstream advisory data: