agentic-qe 3.2.2 → 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.claude/helpers/github-safe.js +10 -10
  2. package/package.json +2 -1
  3. package/v3/dist/cli/bundle.js +314 -14
  4. package/v3/dist/coordination/cross-domain-router.d.ts.map +1 -1
  5. package/v3/dist/coordination/cross-domain-router.js +2 -0
  6. package/v3/dist/coordination/cross-domain-router.js.map +1 -1
  7. package/v3/dist/domains/security-compliance/services/semgrep-integration.d.ts.map +1 -1
  8. package/v3/dist/domains/security-compliance/services/semgrep-integration.js +16 -12
  9. package/v3/dist/domains/security-compliance/services/semgrep-integration.js.map +1 -1
  10. package/v3/dist/domains/test-execution/services/e2e-runner.d.ts +1 -0
  11. package/v3/dist/domains/test-execution/services/e2e-runner.d.ts.map +1 -1
  12. package/v3/dist/domains/test-execution/services/e2e-runner.js +9 -12
  13. package/v3/dist/domains/test-execution/services/e2e-runner.js.map +1 -1
  14. package/v3/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts +9 -3
  15. package/v3/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts.map +1 -1
  16. package/v3/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js +103 -26
  17. package/v3/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js.map +1 -1
  18. package/v3/dist/integrations/embeddings/base/EmbeddingGenerator.d.ts.map +1 -1
  19. package/v3/dist/integrations/embeddings/base/EmbeddingGenerator.js +1 -0
  20. package/v3/dist/integrations/embeddings/base/EmbeddingGenerator.js.map +1 -1
  21. package/v3/dist/integrations/embeddings/base/types.d.ts +1 -1
  22. package/v3/dist/integrations/embeddings/base/types.d.ts.map +1 -1
  23. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.d.ts.map +1 -1
  24. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.js +1 -0
  25. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.js.map +1 -1
  26. package/v3/dist/learning/token-tracker.d.ts.map +1 -1
  27. package/v3/dist/learning/token-tracker.js +5 -2
  28. package/v3/dist/learning/token-tracker.js.map +1 -1
  29. package/v3/dist/mcp/bundle.js +383 -25
  30. package/v3/dist/mcp/tools/contract-testing/validate.d.ts +1 -1
  31. package/v3/dist/mcp/tools/contract-testing/validate.d.ts.map +1 -1
  32. package/v3/dist/mcp/tools/contract-testing/validate.js +66 -0
  33. package/v3/dist/mcp/tools/contract-testing/validate.js.map +1 -1
  34. package/v3/dist/shared/utils/safe-expression-evaluator.d.ts +61 -0
  35. package/v3/dist/shared/utils/safe-expression-evaluator.d.ts.map +1 -0
  36. package/v3/dist/shared/utils/safe-expression-evaluator.js +365 -0
  37. package/v3/dist/shared/utils/safe-expression-evaluator.js.map +1 -0
  38. package/v3/dist/test-scheduling/executors/vitest-executor.d.ts.map +1 -1
  39. package/v3/dist/test-scheduling/executors/vitest-executor.js +7 -2
  40. package/v3/dist/test-scheduling/executors/vitest-executor.js.map +1 -1
  41. package/v3/dist/workflows/browser/workflow-loader.d.ts +1 -0
  42. package/v3/dist/workflows/browser/workflow-loader.d.ts.map +1 -1
  43. package/v3/dist/workflows/browser/workflow-loader.js +11 -16
  44. package/v3/dist/workflows/browser/workflow-loader.js.map +1 -1
  45. package/v3/package.json +1 -1
@@ -21320,6 +21320,310 @@ var TestPrioritizerService = class {
21320
21320
  }
21321
21321
  };
21322
21322
 
21323
+ // src/shared/utils/safe-expression-evaluator.ts
21324
+ var PRECEDENCE = {
21325
+ "||": 1,
21326
+ "&&": 2,
21327
+ "===": 3,
21328
+ "!==": 3,
21329
+ "==": 3,
21330
+ "!=": 3,
21331
+ "<": 4,
21332
+ ">": 4,
21333
+ "<=": 4,
21334
+ ">=": 4,
21335
+ "+": 5,
21336
+ "-": 5,
21337
+ "*": 6,
21338
+ "/": 6,
21339
+ "%": 6
21340
+ };
21341
+ var BINARY_OPERATORS = new Set(Object.keys(PRECEDENCE));
21342
+ var UNARY_OPERATORS = /* @__PURE__ */ new Set(["!", "-", "+"]);
21343
+ function tokenize(expr) {
21344
+ const tokens = [];
21345
+ let i = 0;
21346
+ while (i < expr.length) {
21347
+ const char = expr[i];
21348
+ if (/\s/.test(char)) {
21349
+ i++;
21350
+ continue;
21351
+ }
21352
+ if (/\d/.test(char) || char === "." && /\d/.test(expr[i + 1])) {
21353
+ let num = "";
21354
+ while (i < expr.length && /[\d.]/.test(expr[i])) {
21355
+ num += expr[i++];
21356
+ }
21357
+ tokens.push({ type: "NUMBER", value: parseFloat(num), raw: num });
21358
+ continue;
21359
+ }
21360
+ if (char === '"' || char === "'") {
21361
+ const quote = char;
21362
+ let str = "";
21363
+ i++;
21364
+ while (i < expr.length && expr[i] !== quote) {
21365
+ if (expr[i] === "\\" && i + 1 < expr.length) {
21366
+ i++;
21367
+ const escaped = expr[i];
21368
+ switch (escaped) {
21369
+ case "n":
21370
+ str += "\n";
21371
+ break;
21372
+ case "t":
21373
+ str += " ";
21374
+ break;
21375
+ case "r":
21376
+ str += "\r";
21377
+ break;
21378
+ default:
21379
+ str += escaped;
21380
+ }
21381
+ } else {
21382
+ str += expr[i];
21383
+ }
21384
+ i++;
21385
+ }
21386
+ i++;
21387
+ tokens.push({ type: "STRING", value: str, raw: `${quote}${str}${quote}` });
21388
+ continue;
21389
+ }
21390
+ if (/[a-zA-Z_$]/.test(char)) {
21391
+ let ident = "";
21392
+ while (i < expr.length && /[a-zA-Z0-9_$]/.test(expr[i])) {
21393
+ ident += expr[i++];
21394
+ }
21395
+ if (ident === "true") {
21396
+ tokens.push({ type: "BOOLEAN", value: true, raw: ident });
21397
+ } else if (ident === "false") {
21398
+ tokens.push({ type: "BOOLEAN", value: false, raw: ident });
21399
+ } else if (ident === "null") {
21400
+ tokens.push({ type: "NULL", value: null, raw: ident });
21401
+ } else if (ident === "undefined") {
21402
+ tokens.push({ type: "UNDEFINED", value: void 0, raw: ident });
21403
+ } else {
21404
+ tokens.push({ type: "IDENTIFIER", value: ident, raw: ident });
21405
+ }
21406
+ continue;
21407
+ }
21408
+ const twoChar = expr.slice(i, i + 2);
21409
+ const threeChar = expr.slice(i, i + 3);
21410
+ if (threeChar === "===" || threeChar === "!==") {
21411
+ tokens.push({ type: "OPERATOR", value: threeChar, raw: threeChar });
21412
+ i += 3;
21413
+ continue;
21414
+ }
21415
+ if (twoChar === "==" || twoChar === "!=" || twoChar === "<=" || twoChar === ">=" || twoChar === "&&" || twoChar === "||") {
21416
+ tokens.push({ type: "OPERATOR", value: twoChar, raw: twoChar });
21417
+ i += 2;
21418
+ continue;
21419
+ }
21420
+ if (char === "(") {
21421
+ tokens.push({ type: "LPAREN", value: "(", raw: "(" });
21422
+ i++;
21423
+ continue;
21424
+ }
21425
+ if (char === ")") {
21426
+ tokens.push({ type: "RPAREN", value: ")", raw: ")" });
21427
+ i++;
21428
+ continue;
21429
+ }
21430
+ if (char === ".") {
21431
+ tokens.push({ type: "DOT", value: ".", raw: "." });
21432
+ i++;
21433
+ continue;
21434
+ }
21435
+ if ("+-*/%<>!".includes(char)) {
21436
+ tokens.push({ type: "OPERATOR", value: char, raw: char });
21437
+ i++;
21438
+ continue;
21439
+ }
21440
+ throw new Error(`Unexpected character at position ${i}: ${char}`);
21441
+ }
21442
+ tokens.push({ type: "EOF", value: "", raw: "" });
21443
+ return tokens;
21444
+ }
21445
+ var Parser = class {
21446
+ tokens;
21447
+ pos = 0;
21448
+ context;
21449
+ constructor(tokens, context) {
21450
+ this.tokens = tokens;
21451
+ this.context = context;
21452
+ }
21453
+ current() {
21454
+ return this.tokens[this.pos];
21455
+ }
21456
+ advance() {
21457
+ return this.tokens[this.pos++];
21458
+ }
21459
+ expect(type) {
21460
+ const token = this.current();
21461
+ if (token.type !== type) {
21462
+ throw new Error(`Expected ${type}, got ${token.type}`);
21463
+ }
21464
+ return this.advance();
21465
+ }
21466
+ parse() {
21467
+ const result = this.parseExpression(0);
21468
+ if (this.current().type !== "EOF") {
21469
+ throw new Error(`Unexpected token: ${this.current().raw}`);
21470
+ }
21471
+ return result;
21472
+ }
21473
+ parseExpression(minPrecedence) {
21474
+ let left = this.parseUnary();
21475
+ while (true) {
21476
+ const token = this.current();
21477
+ if (token.type !== "OPERATOR" || !BINARY_OPERATORS.has(token.value)) {
21478
+ break;
21479
+ }
21480
+ const precedence = PRECEDENCE[token.value];
21481
+ if (precedence < minPrecedence) {
21482
+ break;
21483
+ }
21484
+ const operator = this.advance().value;
21485
+ const right = this.parseExpression(precedence + 1);
21486
+ left = this.applyBinaryOperator(operator, left, right);
21487
+ }
21488
+ return left;
21489
+ }
21490
+ parseUnary() {
21491
+ const token = this.current();
21492
+ if (token.type === "OPERATOR" && UNARY_OPERATORS.has(token.value)) {
21493
+ const operator = this.advance().value;
21494
+ const operand = this.parseUnary();
21495
+ return this.applyUnaryOperator(operator, operand);
21496
+ }
21497
+ return this.parsePrimary();
21498
+ }
21499
+ parsePrimary() {
21500
+ const token = this.current();
21501
+ switch (token.type) {
21502
+ case "NUMBER":
21503
+ case "STRING":
21504
+ case "BOOLEAN":
21505
+ case "NULL":
21506
+ case "UNDEFINED":
21507
+ this.advance();
21508
+ return token.value;
21509
+ case "IDENTIFIER":
21510
+ return this.parseIdentifier();
21511
+ case "LPAREN":
21512
+ this.advance();
21513
+ const result = this.parseExpression(0);
21514
+ this.expect("RPAREN");
21515
+ return result;
21516
+ default:
21517
+ throw new Error(`Unexpected token: ${token.raw}`);
21518
+ }
21519
+ }
21520
+ parseIdentifier() {
21521
+ let value = this.context;
21522
+ let name = this.advance().value;
21523
+ if (typeof value === "object" && value !== null && name in value) {
21524
+ value = value[name];
21525
+ } else {
21526
+ value = void 0;
21527
+ }
21528
+ while (this.current().type === "DOT") {
21529
+ this.advance();
21530
+ const prop = this.expect("IDENTIFIER").value;
21531
+ if (value !== null && value !== void 0 && typeof value === "object") {
21532
+ value = value[prop];
21533
+ } else {
21534
+ value = void 0;
21535
+ }
21536
+ }
21537
+ return value;
21538
+ }
21539
+ applyBinaryOperator(op, left, right) {
21540
+ switch (op) {
21541
+ case "===":
21542
+ return left === right;
21543
+ case "!==":
21544
+ return left !== right;
21545
+ case "==":
21546
+ return left == right;
21547
+ case "!=":
21548
+ return left != right;
21549
+ case "<":
21550
+ return left < right;
21551
+ case ">":
21552
+ return left > right;
21553
+ case "<=":
21554
+ return left <= right;
21555
+ case ">=":
21556
+ return left >= right;
21557
+ case "&&":
21558
+ return left && right;
21559
+ case "||":
21560
+ return left || right;
21561
+ case "+":
21562
+ return left + right;
21563
+ case "-":
21564
+ return left - right;
21565
+ case "*":
21566
+ return left * right;
21567
+ case "/":
21568
+ return left / right;
21569
+ case "%":
21570
+ return left % right;
21571
+ default:
21572
+ throw new Error(`Unknown operator: ${op}`);
21573
+ }
21574
+ }
21575
+ applyUnaryOperator(op, operand) {
21576
+ switch (op) {
21577
+ case "!":
21578
+ return !operand;
21579
+ case "-":
21580
+ return -operand;
21581
+ case "+":
21582
+ return +operand;
21583
+ default:
21584
+ throw new Error(`Unknown unary operator: ${op}`);
21585
+ }
21586
+ }
21587
+ };
21588
+ function safeEvaluate(expression, context = {}) {
21589
+ if (!expression || typeof expression !== "string") {
21590
+ throw new Error("Expression must be a non-empty string");
21591
+ }
21592
+ const dangerousPatterns = [
21593
+ /\beval\b/i,
21594
+ /\bFunction\b/,
21595
+ /\bconstructor\b/,
21596
+ /\b__proto__\b/,
21597
+ /\bprototype\b/,
21598
+ /\bimport\b/,
21599
+ /\brequire\b/,
21600
+ /\bprocess\b/,
21601
+ /\bglobal\b/,
21602
+ /\bwindow\b/,
21603
+ /\bdocument\b/,
21604
+ /\[\s*['"`]/,
21605
+ // Computed property access with string
21606
+ /\[.*\]/
21607
+ // Any computed property access
21608
+ ];
21609
+ for (const pattern of dangerousPatterns) {
21610
+ if (pattern.test(expression)) {
21611
+ throw new Error(`Expression contains potentially dangerous pattern: ${expression}`);
21612
+ }
21613
+ }
21614
+ const tokens = tokenize(expression.trim());
21615
+ const parser = new Parser(tokens, context);
21616
+ return parser.parse();
21617
+ }
21618
+ function safeEvaluateBoolean(expression, context = {}, defaultValue = false) {
21619
+ try {
21620
+ return Boolean(safeEvaluate(expression, context));
21621
+ } catch (error) {
21622
+ console.warn(`[SafeEvaluator] Failed to evaluate expression: ${expression}`, error);
21623
+ return defaultValue;
21624
+ }
21625
+ }
21626
+
21323
21627
  // src/integrations/browser/types.ts
21324
21628
  var BrowserError = class _BrowserError extends Error {
21325
21629
  constructor(message, code, tool, cause) {
@@ -24688,18 +24992,13 @@ var E2ETestRunnerService = class {
24688
24992
  }
24689
24993
  /**
24690
24994
  * Evaluate conditional expression
24995
+ * Uses safe expression evaluator to prevent code injection (CVE fix)
24691
24996
  */
24692
24997
  evaluateCondition(condition, context) {
24693
- try {
24694
- const evalContext = {
24695
- ...context.variables,
24696
- env: process.env
24697
- };
24698
- const fn = new Function(...Object.keys(evalContext), `return ${condition}`);
24699
- return Boolean(fn(...Object.values(evalContext)));
24700
- } catch {
24701
- return true;
24702
- }
24998
+ const evalContext = {
24999
+ ...context.variables
25000
+ };
25001
+ return safeEvaluateBoolean(condition, evalContext, true);
24703
25002
  }
24704
25003
  /**
24705
25004
  * Get browser context options from test case
@@ -75036,7 +75335,7 @@ var CrossDomainEventRouter = class {
75036
75335
  */
75037
75336
  matchEventType(eventType, pattern) {
75038
75337
  if (pattern === "*") return true;
75039
- const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
75338
+ const regexPattern = pattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*");
75040
75339
  const regex = new RegExp(`^${regexPattern}$`);
75041
75340
  return regex.test(eventType);
75042
75341
  }
@@ -77415,6 +77714,9 @@ init_unified_memory();
77415
77714
  init_real_embeddings();
77416
77715
  import { v4 as uuidv455 } from "uuid";
77417
77716
 
77717
+ // src/integrations/embeddings/index/HNSWIndex.ts
77718
+ import { HierarchicalNSW } from "hnswlib-node";
77719
+
77418
77720
  // src/integrations/agentic-flow/reasoning-bank/pattern-evolution.ts
77419
77721
  init_unified_memory();
77420
77722
  init_real_embeddings();
@@ -81218,6 +81520,7 @@ function createPatternStore(memory, config) {
81218
81520
  }
81219
81521
 
81220
81522
  // src/learning/token-tracker.ts
81523
+ import { randomUUID as randomUUID2 } from "crypto";
81221
81524
  var DEFAULT_COST_CONFIG = {
81222
81525
  costPerInputToken: 3e-3 / 1e3,
81223
81526
  // $3 per 1M input tokens
@@ -81247,7 +81550,7 @@ var TokenMetricsCollectorImpl = class {
81247
81550
  autoSaveTimer = null;
81248
81551
  isDirty = false;
81249
81552
  constructor() {
81250
- this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substring(7)}`;
81553
+ this.sessionId = `session-${Date.now()}-${randomUUID2().substring(0, 8)}`;
81251
81554
  this.sessionStartTime = Date.now();
81252
81555
  this.costConfig = DEFAULT_COST_CONFIG;
81253
81556
  }
@@ -81537,7 +81840,7 @@ var TokenMetricsCollectorImpl = class {
81537
81840
  this.taskMetrics = [];
81538
81841
  this.agentMetrics.clear();
81539
81842
  this.domainMetrics.clear();
81540
- this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substring(7)}`;
81843
+ this.sessionId = `session-${Date.now()}-${randomUUID2().substring(0, 8)}`;
81541
81844
  this.sessionStartTime = Date.now();
81542
81845
  this.cacheHits = 0;
81543
81846
  this.earlyExits = 0;
@@ -85454,6 +85757,34 @@ function generateRecommendations3(vulnerabilities, summary) {
85454
85757
  }
85455
85758
 
85456
85759
  // src/mcp/tools/contract-testing/validate.ts
85760
+ function validateHttpUrl(urlString) {
85761
+ try {
85762
+ const url = new URL(urlString);
85763
+ if (!["http:", "https:"].includes(url.protocol)) {
85764
+ return { valid: false, error: `Invalid protocol: ${url.protocol}. Only http/https allowed.` };
85765
+ }
85766
+ const hostname = url.hostname.toLowerCase();
85767
+ const blockedPatterns = [
85768
+ /^localhost$/i,
85769
+ /^127\./,
85770
+ /^10\./,
85771
+ /^172\.(1[6-9]|2[0-9]|3[01])\./,
85772
+ /^192\.168\./,
85773
+ /^0\.0\.0\.0$/,
85774
+ /^\[::1\]$/,
85775
+ /^169\.254\./
85776
+ // Link-local
85777
+ ];
85778
+ for (const pattern of blockedPatterns) {
85779
+ if (pattern.test(hostname)) {
85780
+ return { valid: false, error: `Blocked hostname: ${hostname}. Cannot access internal/private addresses.` };
85781
+ }
85782
+ }
85783
+ return { valid: true };
85784
+ } catch {
85785
+ return { valid: false, error: `Invalid URL format: ${urlString}` };
85786
+ }
85787
+ }
85457
85788
  var ContractValidateTool = class extends MCPToolBase {
85458
85789
  config = {
85459
85790
  name: "qe/contracts/validate",
@@ -85739,10 +86070,37 @@ var ContractValidateTool = class extends MCPToolBase {
85739
86070
  async verifyAgainstProvider(providerUrl, consumerName, contractContent, format, contractValidator) {
85740
86071
  const failures = [];
85741
86072
  const warnings = [];
86073
+ const providerValidation = validateHttpUrl(providerUrl);
86074
+ if (!providerValidation.valid) {
86075
+ return {
86076
+ provider: providerUrl,
86077
+ consumer: consumerName,
86078
+ passed: false,
86079
+ failures: [{
86080
+ endpoint: providerUrl,
86081
+ type: "validation-error",
86082
+ expected: "Valid HTTP/HTTPS URL",
86083
+ actual: providerValidation.error || "Invalid URL",
86084
+ message: `Provider URL validation failed: ${providerValidation.error}`
86085
+ }],
86086
+ warnings: []
86087
+ };
86088
+ }
85742
86089
  const contract = this.buildContractFromContent(contractContent, format, consumerName);
85743
86090
  for (const endpoint of contract.endpoints) {
85744
86091
  try {
85745
86092
  const url = `${providerUrl}${endpoint.path}`;
86093
+ const urlValidation = validateHttpUrl(url);
86094
+ if (!urlValidation.valid) {
86095
+ failures.push({
86096
+ endpoint: `${endpoint.method} ${endpoint.path}`,
86097
+ type: "validation-error",
86098
+ expected: "Valid URL",
86099
+ actual: urlValidation.error || "Invalid URL",
86100
+ message: `URL validation failed: ${urlValidation.error}`
86101
+ });
86102
+ continue;
86103
+ }
85746
86104
  const response = await fetch(url, {
85747
86105
  method: endpoint.method === "GET" ? "GET" : "OPTIONS",
85748
86106
  headers: {
@@ -89348,7 +89706,7 @@ var QE_GOALS = [
89348
89706
 
89349
89707
  // src/planning/goap-planner.ts
89350
89708
  init_unified_persistence();
89351
- import { randomUUID as randomUUID2 } from "crypto";
89709
+ import { randomUUID as randomUUID3 } from "crypto";
89352
89710
  var GOAPPlanner = class {
89353
89711
  db = null;
89354
89712
  persistence = null;
@@ -89403,7 +89761,7 @@ var GOAPPlanner = class {
89403
89761
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
89404
89762
  `);
89405
89763
  for (const action of allActions) {
89406
- const id = `action-${Date.now()}-${randomUUID2().slice(0, 8)}`;
89764
+ const id = `action-${Date.now()}-${randomUUID3().slice(0, 8)}`;
89407
89765
  insertAction.run(
89408
89766
  id,
89409
89767
  action.name,
@@ -89421,7 +89779,7 @@ var GOAPPlanner = class {
89421
89779
  VALUES (?, ?, ?, ?, ?, ?)
89422
89780
  `);
89423
89781
  for (const goal of QE_GOALS) {
89424
- const id = `goal-${Date.now()}-${randomUUID2().slice(0, 8)}`;
89782
+ const id = `goal-${Date.now()}-${randomUUID3().slice(0, 8)}`;
89425
89783
  insertGoal.run(
89426
89784
  id,
89427
89785
  goal.name,
@@ -89453,7 +89811,7 @@ var GOAPPlanner = class {
89453
89811
  this.recordPlanReuse(reusedPlan.id, true);
89454
89812
  return {
89455
89813
  ...reusedPlan,
89456
- id: `plan-${Date.now()}-${randomUUID2().slice(0, 8)}`,
89814
+ id: `plan-${Date.now()}-${randomUUID3().slice(0, 8)}`,
89457
89815
  initialState: this.cloneState(currentState),
89458
89816
  reusedFrom: reusedPlan.id,
89459
89817
  status: "pending"
@@ -89471,7 +89829,7 @@ var GOAPPlanner = class {
89471
89829
  return null;
89472
89830
  }
89473
89831
  const plan = {
89474
- id: `plan-${Date.now()}-${randomUUID2().slice(0, 8)}`,
89832
+ id: `plan-${Date.now()}-${randomUUID3().slice(0, 8)}`,
89475
89833
  initialState: this.cloneState(currentState),
89476
89834
  goalState: goal,
89477
89835
  actions: actionSequence,
@@ -89852,7 +90210,7 @@ var GOAPPlanner = class {
89852
90210
  */
89853
90211
  async addAction(action) {
89854
90212
  await this.initialize();
89855
- const id = `action-${Date.now()}-${randomUUID2().slice(0, 8)}`;
90213
+ const id = `action-${Date.now()}-${randomUUID3().slice(0, 8)}`;
89856
90214
  this.ensureDb().prepare(
89857
90215
  `
89858
90216
  INSERT INTO goap_actions (
@@ -90014,7 +90372,7 @@ var GOAPPlanner = class {
90014
90372
  ) VALUES (?, ?, ?, ?, ?, ?)
90015
90373
  `
90016
90374
  ).run(
90017
- `sig-${Date.now()}-${randomUUID2().slice(0, 8)}`,
90375
+ `sig-${Date.now()}-${randomUUID3().slice(0, 8)}`,
90018
90376
  plan.id,
90019
90377
  goalHash,
90020
90378
  JSON.stringify(stateVector),
@@ -90098,7 +90456,7 @@ var GOAPPlanner = class {
90098
90456
  */
90099
90457
  async addGoal(goal) {
90100
90458
  await this.initialize();
90101
- const id = `goal-${Date.now()}-${randomUUID2().slice(0, 8)}`;
90459
+ const id = `goal-${Date.now()}-${randomUUID3().slice(0, 8)}`;
90102
90460
  this.ensureDb().prepare(
90103
90461
  `
90104
90462
  INSERT INTO goap_goals (id, name, description, conditions, priority, qe_domain)
@@ -90188,7 +90546,7 @@ function getSharedGOAPPlanner() {
90188
90546
  // src/planning/plan-executor.ts
90189
90547
  init_unified_memory();
90190
90548
  import Database3 from "better-sqlite3";
90191
- import { randomUUID as randomUUID3 } from "crypto";
90549
+ import { randomUUID as randomUUID4 } from "crypto";
90192
90550
  var DEFAULT_CONFIG53 = {
90193
90551
  maxRetries: 2,
90194
90552
  stepTimeoutMs: 6e4,
@@ -90325,7 +90683,7 @@ var PlanExecutor = class {
90325
90683
  async executeWithCallbacks(plan, onStepStart, onStepComplete, initialState) {
90326
90684
  await this.initialize();
90327
90685
  const startTime = Date.now();
90328
- const executionId = `exec-${Date.now()}-${randomUUID3().slice(0, 8)}`;
90686
+ const executionId = `exec-${Date.now()}-${randomUUID4().slice(0, 8)}`;
90329
90687
  this.currentExecution = { planId: plan.id, cancelled: false };
90330
90688
  const result = {
90331
90689
  planId: plan.id,
@@ -90337,7 +90695,7 @@ var PlanExecutor = class {
90337
90695
  };
90338
90696
  let currentState = initialState ? this.cloneState(initialState) : this.cloneState(plan.initialState);
90339
90697
  const steps = plan.actions.map((action, index) => ({
90340
- id: `step-${Date.now()}-${randomUUID3().slice(0, 8)}`,
90698
+ id: `step-${Date.now()}-${randomUUID4().slice(0, 8)}`,
90341
90699
  planId: plan.id,
90342
90700
  action,
90343
90701
  stepOrder: index,
@@ -90861,7 +91219,7 @@ var MockAgentSpawner = class {
90861
91219
  async spawn(agentType, task) {
90862
91220
  await new Promise((resolve5) => setTimeout(resolve5, this.executionDelay));
90863
91221
  const success = Math.random() < this.successRate;
90864
- const agentId = `mock-agent-${randomUUID3().slice(0, 8)}`;
91222
+ const agentId = `mock-agent-${randomUUID4().slice(0, 8)}`;
90865
91223
  if (success) {
90866
91224
  return {
90867
91225
  agentId,
@@ -51,7 +51,7 @@ export interface VerificationResult {
51
51
  }
52
52
  export interface ContractFailure {
53
53
  endpoint: string;
54
- type: 'schema-mismatch' | 'status-code-mismatch' | 'missing-endpoint' | 'response-body-mismatch';
54
+ type: 'schema-mismatch' | 'status-code-mismatch' | 'missing-endpoint' | 'response-body-mismatch' | 'validation-error';
55
55
  expected: string;
56
56
  actual: string;
57
57
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tools/contract-testing/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tools/contract-testing/validate.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,43 @@ import { Version } from '../../../shared/value-objects/index.js';
12
12
  import { ContractValidatorService } from '../../../domains/contract-testing/services/contract-validator.js';
13
13
  import { ApiCompatibilityService } from '../../../domains/contract-testing/services/api-compatibility.js';
14
14
  // ============================================================================
15
+ // Security Helpers
16
+ // ============================================================================
17
+ /**
18
+ * Validate URL to prevent SSRF attacks
19
+ * Only allows HTTP/HTTPS protocols and rejects potentially malicious patterns
20
+ */
21
+ function validateHttpUrl(urlString) {
22
+ try {
23
+ const url = new URL(urlString);
24
+ // Only allow HTTP and HTTPS protocols (prevents javascript:, file:, etc.)
25
+ if (!['http:', 'https:'].includes(url.protocol)) {
26
+ return { valid: false, error: `Invalid protocol: ${url.protocol}. Only http/https allowed.` };
27
+ }
28
+ // Block localhost and private IP ranges (SSRF protection)
29
+ const hostname = url.hostname.toLowerCase();
30
+ const blockedPatterns = [
31
+ /^localhost$/i,
32
+ /^127\./,
33
+ /^10\./,
34
+ /^172\.(1[6-9]|2[0-9]|3[01])\./,
35
+ /^192\.168\./,
36
+ /^0\.0\.0\.0$/,
37
+ /^\[::1\]$/,
38
+ /^169\.254\./, // Link-local
39
+ ];
40
+ for (const pattern of blockedPatterns) {
41
+ if (pattern.test(hostname)) {
42
+ return { valid: false, error: `Blocked hostname: ${hostname}. Cannot access internal/private addresses.` };
43
+ }
44
+ }
45
+ return { valid: true };
46
+ }
47
+ catch {
48
+ return { valid: false, error: `Invalid URL format: ${urlString}` };
49
+ }
50
+ }
51
+ // ============================================================================
15
52
  // Tool Implementation
16
53
  // ============================================================================
17
54
  export class ContractValidateTool extends MCPToolBase {
@@ -292,6 +329,23 @@ export class ContractValidateTool extends MCPToolBase {
292
329
  async verifyAgainstProvider(providerUrl, consumerName, contractContent, format, contractValidator) {
293
330
  const failures = [];
294
331
  const warnings = [];
332
+ // Validate provider URL first (SSRF protection)
333
+ const providerValidation = validateHttpUrl(providerUrl);
334
+ if (!providerValidation.valid) {
335
+ return {
336
+ provider: providerUrl,
337
+ consumer: consumerName,
338
+ passed: false,
339
+ failures: [{
340
+ endpoint: providerUrl,
341
+ type: 'validation-error',
342
+ expected: 'Valid HTTP/HTTPS URL',
343
+ actual: providerValidation.error || 'Invalid URL',
344
+ message: `Provider URL validation failed: ${providerValidation.error}`,
345
+ }],
346
+ warnings: [],
347
+ };
348
+ }
295
349
  // Build contract and verify
296
350
  const contract = this.buildContractFromContent(contractContent, format, consumerName);
297
351
  // Validate each endpoint against the provider
@@ -299,6 +353,18 @@ export class ContractValidateTool extends MCPToolBase {
299
353
  try {
300
354
  // Construct the full URL
301
355
  const url = `${providerUrl}${endpoint.path}`;
356
+ // Validate constructed URL (prevents path traversal attacks)
357
+ const urlValidation = validateHttpUrl(url);
358
+ if (!urlValidation.valid) {
359
+ failures.push({
360
+ endpoint: `${endpoint.method} ${endpoint.path}`,
361
+ type: 'validation-error',
362
+ expected: 'Valid URL',
363
+ actual: urlValidation.error || 'Invalid URL',
364
+ message: `URL validation failed: ${urlValidation.error}`,
365
+ });
366
+ continue;
367
+ }
302
368
  // Make a simple request to verify the endpoint exists
303
369
  const response = await fetch(url, {
304
370
  method: endpoint.method === 'GET' ? 'GET' : 'OPTIONS',