@astrasyncai/verification-gateway 3.10.0 → 3.10.1

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 (66) hide show
  1. package/dist/adapter-interface/interface.d.mts +1 -1
  2. package/dist/adapter-interface/interface.d.ts +1 -1
  3. package/dist/adapters/express.js +1 -1
  4. package/dist/adapters/express.js.map +1 -1
  5. package/dist/adapters/express.mjs +1 -1
  6. package/dist/adapters/express.mjs.map +1 -1
  7. package/dist/adapters/mcp.js +1 -1
  8. package/dist/adapters/mcp.js.map +1 -1
  9. package/dist/adapters/mcp.mjs +1 -1
  10. package/dist/adapters/mcp.mjs.map +1 -1
  11. package/dist/adapters/nextjs.js +1 -1
  12. package/dist/adapters/nextjs.js.map +1 -1
  13. package/dist/adapters/nextjs.mjs +1 -1
  14. package/dist/adapters/nextjs.mjs.map +1 -1
  15. package/dist/adapters/sdk.js +1 -1
  16. package/dist/adapters/sdk.js.map +1 -1
  17. package/dist/adapters/sdk.mjs +1 -1
  18. package/dist/adapters/sdk.mjs.map +1 -1
  19. package/dist/bin/astrasync-claude-hook.js +273 -25
  20. package/dist/bin/astrasync-guard.js +255 -23
  21. package/dist/bin/astrasync.js +270 -24
  22. package/dist/browser/background.js +254 -22
  23. package/dist/browser/background.js.map +1 -1
  24. package/dist/browser/background.mjs +254 -22
  25. package/dist/browser/background.mjs.map +1 -1
  26. package/dist/browser/browser-adapter.d.mts +1 -1
  27. package/dist/browser/browser-adapter.d.ts +1 -1
  28. package/dist/claude-code/claude-code-adapter.d.mts +1 -1
  29. package/dist/claude-code/claude-code-adapter.d.ts +1 -1
  30. package/dist/cli/index.d.mts +1 -1
  31. package/dist/cli/index.d.ts +1 -1
  32. package/dist/cursor/cursor-adapter.d.mts +1 -1
  33. package/dist/cursor/cursor-adapter.d.ts +1 -1
  34. package/dist/cursor/extension.d.mts +1 -1
  35. package/dist/cursor/extension.d.ts +1 -1
  36. package/dist/cursor/extension.js +254 -22
  37. package/dist/cursor/extension.js.map +1 -1
  38. package/dist/cursor/extension.mjs +254 -22
  39. package/dist/cursor/extension.mjs.map +1 -1
  40. package/dist/gateway/gateway.d.mts +27 -2
  41. package/dist/gateway/gateway.d.ts +27 -2
  42. package/dist/gateway/gateway.js +254 -22
  43. package/dist/gateway/gateway.js.map +1 -1
  44. package/dist/gateway/gateway.mjs +254 -22
  45. package/dist/gateway/gateway.mjs.map +1 -1
  46. package/dist/git-trigger/git-hooks.d.mts +1 -1
  47. package/dist/git-trigger/git-hooks.d.ts +1 -1
  48. package/dist/index.d.mts +1 -1
  49. package/dist/index.d.ts +1 -1
  50. package/dist/index.js +1 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/index.mjs +1 -1
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/local-evaluator/evaluator.d.mts +1 -1
  55. package/dist/local-evaluator/evaluator.d.ts +1 -1
  56. package/dist/local-evaluator/evaluator.js +165 -7
  57. package/dist/local-evaluator/evaluator.js.map +1 -1
  58. package/dist/local-evaluator/evaluator.mjs +165 -7
  59. package/dist/local-evaluator/evaluator.mjs.map +1 -1
  60. package/dist/{types-D0lCtFQ7.d.mts → types-BV6abBnF.d.mts} +6 -0
  61. package/dist/{types-YbiXYKx1.d.ts → types-C23_uaY8.d.ts} +6 -0
  62. package/dist/verify.js +1 -1
  63. package/dist/verify.js.map +1 -1
  64. package/dist/verify.mjs +1 -1
  65. package/dist/verify.mjs.map +1 -1
  66. package/package.json +1 -1
@@ -4543,7 +4543,9 @@ var init_types = __esm({
4543
4543
  "git push",
4544
4544
  "npm publish",
4545
4545
  "docker",
4546
- "kubectl"
4546
+ "kubectl",
4547
+ "gh pr create",
4548
+ "gh pr merge"
4547
4549
  ];
4548
4550
  SENSITIVE_PATHS = [
4549
4551
  ".ssh",
@@ -4564,12 +4566,178 @@ var init_types = __esm({
4564
4566
  }
4565
4567
  });
4566
4568
 
4569
+ // src/local-evaluator/command-matcher.ts
4570
+ function buildRiskList(entries) {
4571
+ const singleWord = /* @__PURE__ */ new Set();
4572
+ const multiWord = [];
4573
+ for (const entry of entries) {
4574
+ const words = entry.toLowerCase().trim().split(/\s+/);
4575
+ if (words.length === 1) singleWord.add(words[0]);
4576
+ else multiWord.push(words);
4577
+ }
4578
+ multiWord.sort((a, b) => b.length - a.length);
4579
+ return { singleWord, multiWord };
4580
+ }
4581
+ function stripHeredocBodies(text) {
4582
+ const lines = text.split("\n");
4583
+ const kept = [];
4584
+ let terminator = null;
4585
+ for (const line of lines) {
4586
+ if (terminator !== null) {
4587
+ if (line.trim() === terminator) terminator = null;
4588
+ continue;
4589
+ }
4590
+ kept.push(line);
4591
+ const heredoc = line.match(/<<-?\s*['"]?(\w+)['"]?/);
4592
+ if (heredoc) terminator = heredoc[1];
4593
+ }
4594
+ return kept.join("\n");
4595
+ }
4596
+ function extractDollarSubstitutions(text, collect) {
4597
+ let result = text;
4598
+ let start = result.indexOf("$(");
4599
+ let iterations = 0;
4600
+ while (start !== -1 && iterations < 50) {
4601
+ iterations++;
4602
+ let depth = 0;
4603
+ let end = result.length;
4604
+ for (let i = start + 1; i < result.length; i++) {
4605
+ if (result[i] === "(") depth++;
4606
+ else if (result[i] === ")") {
4607
+ depth--;
4608
+ if (depth === 0) {
4609
+ end = i;
4610
+ break;
4611
+ }
4612
+ }
4613
+ }
4614
+ collect(result.slice(start + 2, end));
4615
+ result = `${result.slice(0, start)} ${result.slice(Math.min(end + 1, result.length))}`;
4616
+ start = result.indexOf("$(");
4617
+ }
4618
+ return result;
4619
+ }
4620
+ function extractBacktickSubstitutions(text, collect) {
4621
+ if (!text.includes("`")) return text;
4622
+ const chunks = text.split("`");
4623
+ const kept = [];
4624
+ for (let i = 0; i < chunks.length; i++) {
4625
+ if (i % 2 === 1) collect(chunks[i]);
4626
+ else kept.push(chunks[i]);
4627
+ }
4628
+ return kept.join(" ");
4629
+ }
4630
+ function basename(token) {
4631
+ const slash = token.lastIndexOf("/");
4632
+ return slash === -1 ? token : token.slice(slash + 1);
4633
+ }
4634
+ function matchSegment(segment, list) {
4635
+ const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
4636
+ let i = 0;
4637
+ let hops = 0;
4638
+ while (i < tokens.length) {
4639
+ while (i < tokens.length && (tokens[i] === "export" || ASSIGN_RE.test(tokens[i]))) i++;
4640
+ if (i >= tokens.length) return null;
4641
+ const cmd = basename(tokens[i]);
4642
+ const significant = [cmd, ...tokens.slice(i + 1).filter((t) => !t.startsWith("-"))];
4643
+ for (const entry of list.multiWord) {
4644
+ if (entry.length <= significant.length && entry.every((w, j) => significant[j] === w)) {
4645
+ return entry.join(" ");
4646
+ }
4647
+ }
4648
+ if (list.singleWord.has(cmd)) return cmd;
4649
+ if (WRAPPERS.has(cmd) && hops < MAX_WRAPPER_HOPS) {
4650
+ hops++;
4651
+ i++;
4652
+ while (i < tokens.length && (tokens[i].startsWith("-") || ASSIGN_RE.test(tokens[i]))) i++;
4653
+ continue;
4654
+ }
4655
+ return null;
4656
+ }
4657
+ return null;
4658
+ }
4659
+ function collectMatches(command, list, matches, depth) {
4660
+ if (depth > MAX_RECURSION_DEPTH || command.length > MAX_COMMAND_LENGTH) return;
4661
+ let text = command.toLowerCase();
4662
+ text = stripHeredocBodies(text);
4663
+ const recurse = (inner) => collectMatches(inner, list, matches, depth + 1);
4664
+ text = extractDollarSubstitutions(text, recurse);
4665
+ text = extractBacktickSubstitutions(text, recurse);
4666
+ text = text.replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
4667
+ text = text.replace(/(^|\s)#[^\n]*/g, " ");
4668
+ const segments = text.split(/\|\||&&|[;|&\n]|[({]/);
4669
+ for (const segment of segments) {
4670
+ const matched = matchSegment(segment, list);
4671
+ if (matched) matches.add(matched);
4672
+ }
4673
+ }
4674
+ function matchHighRiskCommands(command, highRiskCommands = HIGH_RISK_COMMANDS) {
4675
+ if (!command) return [];
4676
+ const matches = /* @__PURE__ */ new Set();
4677
+ collectMatches(command, buildRiskList(highRiskCommands), matches, 0);
4678
+ return [...matches];
4679
+ }
4680
+ function isLocalhostOnlyNetworkCommand(command) {
4681
+ const text = command.toLowerCase();
4682
+ const tokens = text.split(/\s+/).map((t) => t.replace(/^["'`]+|["'`,;)]+$/g, ""));
4683
+ let localCount = 0;
4684
+ for (const token of tokens) {
4685
+ let host = null;
4686
+ if (token.includes("://")) {
4687
+ let rest = token.slice(token.indexOf("://") + 3);
4688
+ const at = rest.indexOf("@");
4689
+ if (at !== -1) rest = rest.slice(at + 1);
4690
+ host = rest.split(/[/?#]/)[0];
4691
+ } else if (/^[a-z0-9.[\]:-]+:\d+(\/.*)?$/.test(token)) {
4692
+ host = token.split("/")[0];
4693
+ } else if (/^(localhost|127\.0\.0\.1|\[?::1\]?)(\/.*)?$/.test(token)) {
4694
+ host = token.split("/")[0];
4695
+ }
4696
+ if (host === null) continue;
4697
+ const bare = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "");
4698
+ if (LOCAL_HOST_RE.test(bare)) {
4699
+ localCount++;
4700
+ } else {
4701
+ return false;
4702
+ }
4703
+ }
4704
+ return localCount > 0;
4705
+ }
4706
+ var WRAPPERS, ASSIGN_RE, MAX_WRAPPER_HOPS, MAX_RECURSION_DEPTH, MAX_COMMAND_LENGTH, LOCAL_HOST_RE;
4707
+ var init_command_matcher = __esm({
4708
+ "src/local-evaluator/command-matcher.ts"() {
4709
+ "use strict";
4710
+ init_types();
4711
+ WRAPPERS = /* @__PURE__ */ new Set([
4712
+ "sudo",
4713
+ "doas",
4714
+ "env",
4715
+ "nohup",
4716
+ "time",
4717
+ "timeout",
4718
+ "nice",
4719
+ "ionice",
4720
+ "stdbuf",
4721
+ "xargs",
4722
+ "command",
4723
+ "exec",
4724
+ "builtin"
4725
+ ]);
4726
+ ASSIGN_RE = /^[a-z_][a-z0-9_]*=/;
4727
+ MAX_WRAPPER_HOPS = 4;
4728
+ MAX_RECURSION_DEPTH = 3;
4729
+ MAX_COMMAND_LENGTH = 1e5;
4730
+ LOCAL_HOST_RE = /^(localhost|127\.0\.0\.1|::1|[a-z0-9.-]*\.localhost)$/;
4731
+ }
4732
+ });
4733
+
4567
4734
  // src/local-evaluator/evaluator.ts
4568
4735
  var LocalEvaluator;
4569
4736
  var init_evaluator = __esm({
4570
4737
  "src/local-evaluator/evaluator.ts"() {
4571
4738
  "use strict";
4572
4739
  init_types();
4740
+ init_command_matcher();
4573
4741
  LocalEvaluator = class {
4574
4742
  constructor(policy) {
4575
4743
  this.requestCounts = /* @__PURE__ */ new Map();
@@ -4719,12 +4887,10 @@ var init_evaluator = __esm({
4719
4887
  return score;
4720
4888
  }
4721
4889
  if (context.purpose === "shell.exec" && context.target) {
4722
- const targetLower = context.target.toLowerCase();
4723
- for (const cmd of HIGH_RISK_COMMANDS) {
4724
- if (targetLower.startsWith(cmd) || targetLower.includes(` ${cmd} `) || targetLower.includes(` ${cmd}`)) {
4725
- score = Math.max(score, 80);
4726
- break;
4727
- }
4890
+ const matched = matchHighRiskCommands(context.target);
4891
+ const localProbeOnly = matched.length > 0 && matched.every((cmd) => cmd === "curl" || cmd === "wget") && isLocalhostOnlyNetworkCommand(context.target);
4892
+ if (matched.length > 0 && !localProbeOnly) {
4893
+ score = Math.max(score, 80);
4728
4894
  }
4729
4895
  }
4730
4896
  if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
@@ -4823,7 +4989,7 @@ var SDK_VERSION;
4823
4989
  var init_version = __esm({
4824
4990
  "src/version.ts"() {
4825
4991
  "use strict";
4826
- SDK_VERSION = "3.10.0";
4992
+ SDK_VERSION = "3.10.1";
4827
4993
  }
4828
4994
  });
4829
4995
 
@@ -5839,21 +6005,93 @@ var init_online = __esm({
5839
6005
  }
5840
6006
  });
5841
6007
 
6008
+ // src/gateway/redact.ts
6009
+ function redactSecrets(text) {
6010
+ let result = text;
6011
+ for (const { type: type2, re, replacement } of REDACTION_PATTERNS) {
6012
+ result = result.replace(re, replacement ?? marker(type2));
6013
+ }
6014
+ return result;
6015
+ }
6016
+ function redactDeep(value) {
6017
+ if (typeof value === "string") {
6018
+ return redactSecrets(value);
6019
+ }
6020
+ if (Array.isArray(value)) {
6021
+ return value.map((item) => redactDeep(item));
6022
+ }
6023
+ if (value !== null && typeof value === "object" && !(value instanceof Date)) {
6024
+ const out = {};
6025
+ for (const [k, v] of Object.entries(value)) {
6026
+ out[k] = redactDeep(v);
6027
+ }
6028
+ return out;
6029
+ }
6030
+ return value;
6031
+ }
6032
+ var marker, REDACTION_PATTERNS;
6033
+ var init_redact = __esm({
6034
+ "src/gateway/redact.ts"() {
6035
+ "use strict";
6036
+ marker = (type2) => `[REDACTED:${type2}]`;
6037
+ REDACTION_PATTERNS = [
6038
+ // Multi-line blocks first so later single-line patterns don't shred them.
6039
+ {
6040
+ type: "private-key",
6041
+ re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g
6042
+ },
6043
+ // URL userinfo credentials (https://user:pass@, x-access-token:ghp_…@).
6044
+ {
6045
+ type: "url-credentials",
6046
+ re: /([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi,
6047
+ replacement: `$1${marker("url-credentials")}@`
6048
+ },
6049
+ // Known token formats.
6050
+ { type: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,255}\b/g },
6051
+ { type: "github-token", re: /\bgithub_pat_[A-Za-z0-9_]{20,255}\b/g },
6052
+ { type: "astrasync-key", re: /\bkya_[A-Fa-f0-9]{32,128}\b/g },
6053
+ { type: "openai-key", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g },
6054
+ { type: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/g },
6055
+ { type: "jwt", re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
6056
+ // Bearer/token headers.
6057
+ {
6058
+ type: "bearer-token",
6059
+ re: /\b(bearer\s+)[a-z0-9._~+/=-]{16,}/gi,
6060
+ replacement: `$1${marker("bearer-token")}`
6061
+ },
6062
+ // Generic KEY=value / key: value assignments with secret-ish names.
6063
+ {
6064
+ type: "assigned-secret",
6065
+ re: /\b((?:password|passwd|secret|token|api[_-]?key|apikey|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*)(['"]?)[^\s'"&;]{8,}\2/gi,
6066
+ replacement: `$1$2${marker("assigned-secret")}$2`
6067
+ }
6068
+ ];
6069
+ }
6070
+ });
6071
+
5842
6072
  // src/gateway/modes/hybrid.ts
6073
+ function delay(ms) {
6074
+ return new Promise((resolve) => {
6075
+ const timer = setTimeout(() => resolve(FLUSH_TIMEOUT), ms);
6076
+ if (typeof timer.unref === "function") timer.unref();
6077
+ });
6078
+ }
5843
6079
  function verifyHybrid(evaluator, syncQueue, context) {
5844
6080
  const decision = evaluator.evaluate(context);
5845
6081
  syncQueue.enqueue(context, decision);
5846
6082
  return decision;
5847
6083
  }
5848
- var import_fs2, import_path, MAX_QUEUE_SIZE, MAX_RETRIES, HybridSyncQueue;
6084
+ var import_fs2, import_path, MAX_QUEUE_SIZE, MAX_RETRIES, FLUSH_TIMEOUT, HybridSyncQueue;
5849
6085
  var init_hybrid = __esm({
5850
6086
  "src/gateway/modes/hybrid.ts"() {
5851
6087
  "use strict";
5852
6088
  import_fs2 = require("fs");
5853
6089
  import_path = require("path");
5854
6090
  init_online();
6091
+ init_redact();
5855
6092
  MAX_QUEUE_SIZE = 1e3;
5856
6093
  MAX_RETRIES = 3;
6094
+ FLUSH_TIMEOUT = /* @__PURE__ */ Symbol("flush-timeout");
5857
6095
  HybridSyncQueue = class {
5858
6096
  constructor(config, persistPath) {
5859
6097
  this.queue = [];
@@ -5874,12 +6112,12 @@ var init_hybrid = __esm({
5874
6112
  /**
5875
6113
  * Stop periodic sync and flush remaining entries.
5876
6114
  */
5877
- async stop() {
6115
+ async stop(flushOptions = {}) {
5878
6116
  if (this.timer) {
5879
6117
  clearInterval(this.timer);
5880
6118
  this.timer = null;
5881
6119
  }
5882
- await this.flush();
6120
+ await this.flush(flushOptions);
5883
6121
  this.saveToDisk();
5884
6122
  }
5885
6123
  /**
@@ -5891,8 +6129,8 @@ var init_hybrid = __esm({
5891
6129
  }
5892
6130
  this.queue.push({
5893
6131
  id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
5894
- context,
5895
- decision,
6132
+ context: redactDeep(context),
6133
+ decision: redactDeep(decision),
5896
6134
  timestamp: /* @__PURE__ */ new Date(),
5897
6135
  retryCount: 0,
5898
6136
  status: "pending"
@@ -5902,16 +6140,21 @@ var init_hybrid = __esm({
5902
6140
  /**
5903
6141
  * Flush pending entries to the API.
5904
6142
  */
5905
- async flush() {
6143
+ async flush(options = {}) {
5906
6144
  if (this.processing) return;
5907
6145
  this.processing = true;
5908
6146
  try {
5909
6147
  const pending = this.queue.filter((e) => e.status === "pending");
5910
- for (const entry of pending) {
5911
- try {
5912
- await verifyOnline(this.config, entry.context);
6148
+ const batch = typeof options.limit === "number" ? pending.slice(0, options.limit) : pending;
6149
+ for (const entry of batch) {
6150
+ const attempt = verifyOnline(this.config, entry.context, this.config.agentId).then(() => "synced").catch(() => "error");
6151
+ const result = options.perEntryTimeoutMs ? await Promise.race([attempt, delay(options.perEntryTimeoutMs)]) : await attempt;
6152
+ if (result === FLUSH_TIMEOUT) {
6153
+ break;
6154
+ }
6155
+ if (result === "synced") {
5913
6156
  entry.status = "synced";
5914
- } catch {
6157
+ } else {
5915
6158
  entry.retryCount++;
5916
6159
  if (entry.retryCount >= MAX_RETRIES) {
5917
6160
  entry.status = "failed";
@@ -5970,6 +6213,7 @@ var init_trace_logger = __esm({
5970
6213
  "use strict";
5971
6214
  import_fs3 = require("fs");
5972
6215
  import_path2 = require("path");
6216
+ init_redact();
5973
6217
  MAX_FILE_SIZE = 10 * 1024 * 1024;
5974
6218
  MAX_FILES = 10;
5975
6219
  TraceLogger = class {
@@ -5989,8 +6233,8 @@ var init_trace_logger = __esm({
5989
6233
  id: this.generateId(),
5990
6234
  timestamp: /* @__PURE__ */ new Date(),
5991
6235
  type: "evaluation",
5992
- context,
5993
- decision
6236
+ context: redactDeep(context),
6237
+ decision: redactDeep(decision)
5994
6238
  });
5995
6239
  }
5996
6240
  /**
@@ -6118,10 +6362,12 @@ var init_gateway = __esm({
6118
6362
  }
6119
6363
  /**
6120
6364
  * Graceful shutdown. Flushes sync queue and cleans up resources.
6365
+ * Stateless callers (hooks) pass bounded flush options so sync never
6366
+ * sits on a tool call's critical path.
6121
6367
  */
6122
- async shutdown() {
6368
+ async shutdown(options) {
6123
6369
  if (this.syncQueue) {
6124
- await this.syncQueue.stop();
6370
+ await this.syncQueue.stop(options?.syncFlush ?? {});
6125
6371
  this.syncQueue = null;
6126
6372
  }
6127
6373
  this.evaluator = null;
@@ -6144,7 +6390,7 @@ var init_gateway = __esm({
6144
6390
  let decision;
6145
6391
  switch (this.config.mode) {
6146
6392
  case "online":
6147
- decision = await verifyOnline(this.config, context, astraId);
6393
+ decision = await verifyOnline(this.config, context, astraId ?? this.config.agentId);
6148
6394
  break;
6149
6395
  case "local":
6150
6396
  if (!this.evaluator) throw new Error("Local evaluator not initialized");
@@ -6712,7 +6958,7 @@ async function cmdUninstallHooks() {
6712
6958
  function resolveClaudeHookCommand() {
6713
6959
  const sibling = path.join(__dirname, "astrasync-claude-hook.js");
6714
6960
  if (fs.existsSync(sibling)) {
6715
- return `node "${sibling}"`;
6961
+ return `"${process.execPath}" "${sibling}"`;
6716
6962
  }
6717
6963
  return "astrasync-claude-hook";
6718
6964
  }