@astrasyncai/verification-gateway 3.9.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.
- package/dist/adapter-interface/interface.d.mts +3 -2
- package/dist/adapter-interface/interface.d.ts +3 -2
- package/dist/adapters/express.d.mts +3 -2
- package/dist/adapters/express.d.ts +3 -2
- package/dist/adapters/express.js +151 -2
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +151 -2
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +2 -1
- package/dist/adapters/mcp.d.ts +2 -1
- package/dist/adapters/mcp.js +1 -1
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +1 -1
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +3 -2
- package/dist/adapters/nextjs.d.ts +3 -2
- package/dist/adapters/nextjs.js +151 -2
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +151 -2
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +3 -2
- package/dist/adapters/sdk.d.ts +3 -2
- package/dist/adapters/sdk.js +1 -1
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +1 -1
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +3 -2
- package/dist/agent/index.d.ts +3 -2
- package/dist/bin/astrasync-claude-hook.js +276 -25
- package/dist/bin/astrasync-guard.js +258 -23
- package/dist/bin/astrasync.js +282 -24
- package/dist/browser/background.js +257 -22
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +257 -22
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +3 -2
- package/dist/browser/browser-adapter.d.ts +3 -2
- package/dist/claude-code/claude-code-adapter.d.mts +3 -2
- package/dist/claude-code/claude-code-adapter.d.ts +3 -2
- package/dist/cli/index.d.mts +3 -2
- package/dist/cli/index.d.ts +3 -2
- package/dist/cursor/cursor-adapter.d.mts +3 -2
- package/dist/cursor/cursor-adapter.d.ts +3 -2
- package/dist/cursor/extension.d.mts +3 -2
- package/dist/cursor/extension.d.ts +3 -2
- package/dist/cursor/extension.js +257 -22
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +257 -22
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +2 -1
- package/dist/edge-config.d.ts +2 -1
- package/dist/{express-sGXhDdd0.d.ts → express-ClNtDhhc.d.ts} +1 -1
- package/dist/{express-DXUyvv7t.d.mts → express-DCXa1oqj.d.mts} +1 -1
- package/dist/gateway/gateway.d.mts +29 -3
- package/dist/gateway/gateway.d.ts +29 -3
- package/dist/gateway/gateway.js +257 -22
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +257 -22
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +3 -2
- package/dist/git-trigger/git-hooks.d.ts +3 -2
- package/dist/{index-Dalih3EB.d.mts → index-BrNKDmiR.d.mts} +1 -1
- package/dist/{index-C61VL_OW.d.ts → index-CfHurO1P.d.ts} +1 -1
- package/dist/{index-CMg-vjEK.d.ts → index-D2ztkumS.d.ts} +1 -1
- package/dist/{index-DmmdehNu.d.mts → index-Dr_XiN65.d.mts} +1 -1
- package/dist/index.d.mts +10 -9
- package/dist/index.d.ts +10 -9
- package/dist/index.js +218 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +211 -11
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +3 -2
- package/dist/local-evaluator/evaluator.d.ts +3 -2
- package/dist/local-evaluator/evaluator.js +165 -7
- package/dist/local-evaluator/evaluator.js.map +1 -1
- package/dist/local-evaluator/evaluator.mjs +165 -7
- package/dist/local-evaluator/evaluator.mjs.map +1 -1
- package/dist/metadata-capture.d.mts +87 -0
- package/dist/metadata-capture.d.ts +87 -0
- package/dist/metadata-capture.js +184 -0
- package/dist/metadata-capture.js.map +1 -0
- package/dist/metadata-capture.mjs +154 -0
- package/dist/metadata-capture.mjs.map +1 -0
- package/dist/{nextjs-DwUW-Nm1.d.ts → nextjs-CEBVBsdi.d.ts} +1 -1
- package/dist/{nextjs-F_2pbPIA.d.mts → nextjs-DcQIk6Zj.d.mts} +1 -1
- package/dist/platform-signatures.d.mts +60 -10
- package/dist/platform-signatures.d.ts +60 -10
- package/dist/platform-signatures.js +37 -10
- package/dist/platform-signatures.js.map +1 -1
- package/dist/platform-signatures.mjs +35 -9
- package/dist/platform-signatures.mjs.map +1 -1
- package/dist/{sdk-Clob2-0A.d.mts → sdk-CZkbSOeO.d.mts} +1 -1
- package/dist/{sdk-CfHANs4A.d.ts → sdk-H0JCLW2y.d.ts} +1 -1
- package/dist/transport/index.d.mts +3 -2
- package/dist/transport/index.d.ts +3 -2
- package/dist/{types-ACMsV07B.d.mts → types-9tns4wyl.d.ts} +12 -0
- package/dist/{types-DmSDgPpG.d.ts → types-BV6abBnF.d.mts} +7 -1
- package/dist/{types-BtqUvK8v.d.mts → types-C23_uaY8.d.ts} +7 -1
- package/dist/{types-ACMsV07B.d.ts → types-sy3tq8lp.d.mts} +12 -0
- package/dist/ui/index.d.mts +2 -1
- package/dist/ui/index.d.ts +2 -1
- package/dist/verify.d.mts +8 -1
- package/dist/verify.d.ts +8 -1
- package/dist/verify.js +1 -1
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +1 -1
- package/dist/verify.mjs.map +1 -1
- package/package.json +6 -1
|
@@ -49,7 +49,9 @@ var HIGH_RISK_COMMANDS = [
|
|
|
49
49
|
"git push",
|
|
50
50
|
"npm publish",
|
|
51
51
|
"docker",
|
|
52
|
-
"kubectl"
|
|
52
|
+
"kubectl",
|
|
53
|
+
"gh pr create",
|
|
54
|
+
"gh pr merge"
|
|
53
55
|
];
|
|
54
56
|
var SENSITIVE_PATHS = [
|
|
55
57
|
".ssh",
|
|
@@ -68,6 +70,164 @@ var SENSITIVE_PATHS = [
|
|
|
68
70
|
".pypirc"
|
|
69
71
|
];
|
|
70
72
|
|
|
73
|
+
// src/local-evaluator/command-matcher.ts
|
|
74
|
+
var WRAPPERS = /* @__PURE__ */ new Set([
|
|
75
|
+
"sudo",
|
|
76
|
+
"doas",
|
|
77
|
+
"env",
|
|
78
|
+
"nohup",
|
|
79
|
+
"time",
|
|
80
|
+
"timeout",
|
|
81
|
+
"nice",
|
|
82
|
+
"ionice",
|
|
83
|
+
"stdbuf",
|
|
84
|
+
"xargs",
|
|
85
|
+
"command",
|
|
86
|
+
"exec",
|
|
87
|
+
"builtin"
|
|
88
|
+
]);
|
|
89
|
+
var ASSIGN_RE = /^[a-z_][a-z0-9_]*=/;
|
|
90
|
+
var MAX_WRAPPER_HOPS = 4;
|
|
91
|
+
var MAX_RECURSION_DEPTH = 3;
|
|
92
|
+
var MAX_COMMAND_LENGTH = 1e5;
|
|
93
|
+
var LOCAL_HOST_RE = /^(localhost|127\.0\.0\.1|::1|[a-z0-9.-]*\.localhost)$/;
|
|
94
|
+
function buildRiskList(entries) {
|
|
95
|
+
const singleWord = /* @__PURE__ */ new Set();
|
|
96
|
+
const multiWord = [];
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
const words = entry.toLowerCase().trim().split(/\s+/);
|
|
99
|
+
if (words.length === 1) singleWord.add(words[0]);
|
|
100
|
+
else multiWord.push(words);
|
|
101
|
+
}
|
|
102
|
+
multiWord.sort((a, b) => b.length - a.length);
|
|
103
|
+
return { singleWord, multiWord };
|
|
104
|
+
}
|
|
105
|
+
function stripHeredocBodies(text) {
|
|
106
|
+
const lines = text.split("\n");
|
|
107
|
+
const kept = [];
|
|
108
|
+
let terminator = null;
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
if (terminator !== null) {
|
|
111
|
+
if (line.trim() === terminator) terminator = null;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
kept.push(line);
|
|
115
|
+
const heredoc = line.match(/<<-?\s*['"]?(\w+)['"]?/);
|
|
116
|
+
if (heredoc) terminator = heredoc[1];
|
|
117
|
+
}
|
|
118
|
+
return kept.join("\n");
|
|
119
|
+
}
|
|
120
|
+
function extractDollarSubstitutions(text, collect) {
|
|
121
|
+
let result = text;
|
|
122
|
+
let start = result.indexOf("$(");
|
|
123
|
+
let iterations = 0;
|
|
124
|
+
while (start !== -1 && iterations < 50) {
|
|
125
|
+
iterations++;
|
|
126
|
+
let depth = 0;
|
|
127
|
+
let end = result.length;
|
|
128
|
+
for (let i = start + 1; i < result.length; i++) {
|
|
129
|
+
if (result[i] === "(") depth++;
|
|
130
|
+
else if (result[i] === ")") {
|
|
131
|
+
depth--;
|
|
132
|
+
if (depth === 0) {
|
|
133
|
+
end = i;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
collect(result.slice(start + 2, end));
|
|
139
|
+
result = `${result.slice(0, start)} ${result.slice(Math.min(end + 1, result.length))}`;
|
|
140
|
+
start = result.indexOf("$(");
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
function extractBacktickSubstitutions(text, collect) {
|
|
145
|
+
if (!text.includes("`")) return text;
|
|
146
|
+
const chunks = text.split("`");
|
|
147
|
+
const kept = [];
|
|
148
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
149
|
+
if (i % 2 === 1) collect(chunks[i]);
|
|
150
|
+
else kept.push(chunks[i]);
|
|
151
|
+
}
|
|
152
|
+
return kept.join(" ");
|
|
153
|
+
}
|
|
154
|
+
function basename(token) {
|
|
155
|
+
const slash = token.lastIndexOf("/");
|
|
156
|
+
return slash === -1 ? token : token.slice(slash + 1);
|
|
157
|
+
}
|
|
158
|
+
function matchSegment(segment, list) {
|
|
159
|
+
const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
|
|
160
|
+
let i = 0;
|
|
161
|
+
let hops = 0;
|
|
162
|
+
while (i < tokens.length) {
|
|
163
|
+
while (i < tokens.length && (tokens[i] === "export" || ASSIGN_RE.test(tokens[i]))) i++;
|
|
164
|
+
if (i >= tokens.length) return null;
|
|
165
|
+
const cmd = basename(tokens[i]);
|
|
166
|
+
const significant = [cmd, ...tokens.slice(i + 1).filter((t) => !t.startsWith("-"))];
|
|
167
|
+
for (const entry of list.multiWord) {
|
|
168
|
+
if (entry.length <= significant.length && entry.every((w, j) => significant[j] === w)) {
|
|
169
|
+
return entry.join(" ");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (list.singleWord.has(cmd)) return cmd;
|
|
173
|
+
if (WRAPPERS.has(cmd) && hops < MAX_WRAPPER_HOPS) {
|
|
174
|
+
hops++;
|
|
175
|
+
i++;
|
|
176
|
+
while (i < tokens.length && (tokens[i].startsWith("-") || ASSIGN_RE.test(tokens[i]))) i++;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
function collectMatches(command, list, matches, depth) {
|
|
184
|
+
if (depth > MAX_RECURSION_DEPTH || command.length > MAX_COMMAND_LENGTH) return;
|
|
185
|
+
let text = command.toLowerCase();
|
|
186
|
+
text = stripHeredocBodies(text);
|
|
187
|
+
const recurse = (inner) => collectMatches(inner, list, matches, depth + 1);
|
|
188
|
+
text = extractDollarSubstitutions(text, recurse);
|
|
189
|
+
text = extractBacktickSubstitutions(text, recurse);
|
|
190
|
+
text = text.replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
|
|
191
|
+
text = text.replace(/(^|\s)#[^\n]*/g, " ");
|
|
192
|
+
const segments = text.split(/\|\||&&|[;|&\n]|[({]/);
|
|
193
|
+
for (const segment of segments) {
|
|
194
|
+
const matched = matchSegment(segment, list);
|
|
195
|
+
if (matched) matches.add(matched);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function matchHighRiskCommands(command, highRiskCommands = HIGH_RISK_COMMANDS) {
|
|
199
|
+
if (!command) return [];
|
|
200
|
+
const matches = /* @__PURE__ */ new Set();
|
|
201
|
+
collectMatches(command, buildRiskList(highRiskCommands), matches, 0);
|
|
202
|
+
return [...matches];
|
|
203
|
+
}
|
|
204
|
+
function isLocalhostOnlyNetworkCommand(command) {
|
|
205
|
+
const text = command.toLowerCase();
|
|
206
|
+
const tokens = text.split(/\s+/).map((t) => t.replace(/^["'`]+|["'`,;)]+$/g, ""));
|
|
207
|
+
let localCount = 0;
|
|
208
|
+
for (const token of tokens) {
|
|
209
|
+
let host = null;
|
|
210
|
+
if (token.includes("://")) {
|
|
211
|
+
let rest = token.slice(token.indexOf("://") + 3);
|
|
212
|
+
const at = rest.indexOf("@");
|
|
213
|
+
if (at !== -1) rest = rest.slice(at + 1);
|
|
214
|
+
host = rest.split(/[/?#]/)[0];
|
|
215
|
+
} else if (/^[a-z0-9.[\]:-]+:\d+(\/.*)?$/.test(token)) {
|
|
216
|
+
host = token.split("/")[0];
|
|
217
|
+
} else if (/^(localhost|127\.0\.0\.1|\[?::1\]?)(\/.*)?$/.test(token)) {
|
|
218
|
+
host = token.split("/")[0];
|
|
219
|
+
}
|
|
220
|
+
if (host === null) continue;
|
|
221
|
+
const bare = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "");
|
|
222
|
+
if (LOCAL_HOST_RE.test(bare)) {
|
|
223
|
+
localCount++;
|
|
224
|
+
} else {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return localCount > 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
71
231
|
// src/local-evaluator/evaluator.ts
|
|
72
232
|
var LocalEvaluator = class {
|
|
73
233
|
constructor(policy) {
|
|
@@ -218,12 +378,10 @@ var LocalEvaluator = class {
|
|
|
218
378
|
return score;
|
|
219
379
|
}
|
|
220
380
|
if (context.purpose === "shell.exec" && context.target) {
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
381
|
+
const matched = matchHighRiskCommands(context.target);
|
|
382
|
+
const localProbeOnly = matched.length > 0 && matched.every((cmd) => cmd === "curl" || cmd === "wget") && isLocalhostOnlyNetworkCommand(context.target);
|
|
383
|
+
if (matched.length > 0 && !localProbeOnly) {
|
|
384
|
+
score = Math.max(score, 80);
|
|
227
385
|
}
|
|
228
386
|
}
|
|
229
387
|
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
@@ -3534,7 +3692,7 @@ function getTrustLevel(score) {
|
|
|
3534
3692
|
}
|
|
3535
3693
|
|
|
3536
3694
|
// src/version.ts
|
|
3537
|
-
var SDK_VERSION = "3.
|
|
3695
|
+
var SDK_VERSION = "3.10.1";
|
|
3538
3696
|
|
|
3539
3697
|
// src/well-known.ts
|
|
3540
3698
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -3950,6 +4108,9 @@ async function verify(config, request, options) {
|
|
|
3950
4108
|
// src/adapters/stepup-poll.ts
|
|
3951
4109
|
var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
|
|
3952
4110
|
|
|
4111
|
+
// src/metadata-capture.ts
|
|
4112
|
+
var MAX_TOTAL_BYTES = 16 * 1024;
|
|
4113
|
+
|
|
3953
4114
|
// src/adapters/express.ts
|
|
3954
4115
|
var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
3955
4116
|
|
|
@@ -4089,8 +4250,75 @@ async function verifyOnline(config, context, astraId) {
|
|
|
4089
4250
|
// src/gateway/modes/hybrid.ts
|
|
4090
4251
|
var import_fs2 = require("fs");
|
|
4091
4252
|
var import_path = require("path");
|
|
4253
|
+
|
|
4254
|
+
// src/gateway/redact.ts
|
|
4255
|
+
var marker = (type2) => `[REDACTED:${type2}]`;
|
|
4256
|
+
var REDACTION_PATTERNS = [
|
|
4257
|
+
// Multi-line blocks first so later single-line patterns don't shred them.
|
|
4258
|
+
{
|
|
4259
|
+
type: "private-key",
|
|
4260
|
+
re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g
|
|
4261
|
+
},
|
|
4262
|
+
// URL userinfo credentials (https://user:pass@, x-access-token:ghp_…@).
|
|
4263
|
+
{
|
|
4264
|
+
type: "url-credentials",
|
|
4265
|
+
re: /([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi,
|
|
4266
|
+
replacement: `$1${marker("url-credentials")}@`
|
|
4267
|
+
},
|
|
4268
|
+
// Known token formats.
|
|
4269
|
+
{ type: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,255}\b/g },
|
|
4270
|
+
{ type: "github-token", re: /\bgithub_pat_[A-Za-z0-9_]{20,255}\b/g },
|
|
4271
|
+
{ type: "astrasync-key", re: /\bkya_[A-Fa-f0-9]{32,128}\b/g },
|
|
4272
|
+
{ type: "openai-key", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g },
|
|
4273
|
+
{ type: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/g },
|
|
4274
|
+
{ type: "jwt", re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
|
|
4275
|
+
// Bearer/token headers.
|
|
4276
|
+
{
|
|
4277
|
+
type: "bearer-token",
|
|
4278
|
+
re: /\b(bearer\s+)[a-z0-9._~+/=-]{16,}/gi,
|
|
4279
|
+
replacement: `$1${marker("bearer-token")}`
|
|
4280
|
+
},
|
|
4281
|
+
// Generic KEY=value / key: value assignments with secret-ish names.
|
|
4282
|
+
{
|
|
4283
|
+
type: "assigned-secret",
|
|
4284
|
+
re: /\b((?:password|passwd|secret|token|api[_-]?key|apikey|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*)(['"]?)[^\s'"&;]{8,}\2/gi,
|
|
4285
|
+
replacement: `$1$2${marker("assigned-secret")}$2`
|
|
4286
|
+
}
|
|
4287
|
+
];
|
|
4288
|
+
function redactSecrets(text) {
|
|
4289
|
+
let result = text;
|
|
4290
|
+
for (const { type: type2, re, replacement } of REDACTION_PATTERNS) {
|
|
4291
|
+
result = result.replace(re, replacement ?? marker(type2));
|
|
4292
|
+
}
|
|
4293
|
+
return result;
|
|
4294
|
+
}
|
|
4295
|
+
function redactDeep(value) {
|
|
4296
|
+
if (typeof value === "string") {
|
|
4297
|
+
return redactSecrets(value);
|
|
4298
|
+
}
|
|
4299
|
+
if (Array.isArray(value)) {
|
|
4300
|
+
return value.map((item) => redactDeep(item));
|
|
4301
|
+
}
|
|
4302
|
+
if (value !== null && typeof value === "object" && !(value instanceof Date)) {
|
|
4303
|
+
const out = {};
|
|
4304
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4305
|
+
out[k] = redactDeep(v);
|
|
4306
|
+
}
|
|
4307
|
+
return out;
|
|
4308
|
+
}
|
|
4309
|
+
return value;
|
|
4310
|
+
}
|
|
4311
|
+
|
|
4312
|
+
// src/gateway/modes/hybrid.ts
|
|
4092
4313
|
var MAX_QUEUE_SIZE = 1e3;
|
|
4093
4314
|
var MAX_RETRIES = 3;
|
|
4315
|
+
var FLUSH_TIMEOUT = /* @__PURE__ */ Symbol("flush-timeout");
|
|
4316
|
+
function delay(ms) {
|
|
4317
|
+
return new Promise((resolve) => {
|
|
4318
|
+
const timer = setTimeout(() => resolve(FLUSH_TIMEOUT), ms);
|
|
4319
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
4320
|
+
});
|
|
4321
|
+
}
|
|
4094
4322
|
var HybridSyncQueue = class {
|
|
4095
4323
|
constructor(config, persistPath) {
|
|
4096
4324
|
this.queue = [];
|
|
@@ -4111,12 +4339,12 @@ var HybridSyncQueue = class {
|
|
|
4111
4339
|
/**
|
|
4112
4340
|
* Stop periodic sync and flush remaining entries.
|
|
4113
4341
|
*/
|
|
4114
|
-
async stop() {
|
|
4342
|
+
async stop(flushOptions = {}) {
|
|
4115
4343
|
if (this.timer) {
|
|
4116
4344
|
clearInterval(this.timer);
|
|
4117
4345
|
this.timer = null;
|
|
4118
4346
|
}
|
|
4119
|
-
await this.flush();
|
|
4347
|
+
await this.flush(flushOptions);
|
|
4120
4348
|
this.saveToDisk();
|
|
4121
4349
|
}
|
|
4122
4350
|
/**
|
|
@@ -4128,8 +4356,8 @@ var HybridSyncQueue = class {
|
|
|
4128
4356
|
}
|
|
4129
4357
|
this.queue.push({
|
|
4130
4358
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
4131
|
-
context,
|
|
4132
|
-
decision,
|
|
4359
|
+
context: redactDeep(context),
|
|
4360
|
+
decision: redactDeep(decision),
|
|
4133
4361
|
timestamp: /* @__PURE__ */ new Date(),
|
|
4134
4362
|
retryCount: 0,
|
|
4135
4363
|
status: "pending"
|
|
@@ -4139,16 +4367,21 @@ var HybridSyncQueue = class {
|
|
|
4139
4367
|
/**
|
|
4140
4368
|
* Flush pending entries to the API.
|
|
4141
4369
|
*/
|
|
4142
|
-
async flush() {
|
|
4370
|
+
async flush(options = {}) {
|
|
4143
4371
|
if (this.processing) return;
|
|
4144
4372
|
this.processing = true;
|
|
4145
4373
|
try {
|
|
4146
4374
|
const pending = this.queue.filter((e) => e.status === "pending");
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4375
|
+
const batch = typeof options.limit === "number" ? pending.slice(0, options.limit) : pending;
|
|
4376
|
+
for (const entry of batch) {
|
|
4377
|
+
const attempt = verifyOnline(this.config, entry.context, this.config.agentId).then(() => "synced").catch(() => "error");
|
|
4378
|
+
const result = options.perEntryTimeoutMs ? await Promise.race([attempt, delay(options.perEntryTimeoutMs)]) : await attempt;
|
|
4379
|
+
if (result === FLUSH_TIMEOUT) {
|
|
4380
|
+
break;
|
|
4381
|
+
}
|
|
4382
|
+
if (result === "synced") {
|
|
4150
4383
|
entry.status = "synced";
|
|
4151
|
-
}
|
|
4384
|
+
} else {
|
|
4152
4385
|
entry.retryCount++;
|
|
4153
4386
|
if (entry.retryCount >= MAX_RETRIES) {
|
|
4154
4387
|
entry.status = "failed";
|
|
@@ -4225,8 +4458,8 @@ var TraceLogger = class {
|
|
|
4225
4458
|
id: this.generateId(),
|
|
4226
4459
|
timestamp: /* @__PURE__ */ new Date(),
|
|
4227
4460
|
type: "evaluation",
|
|
4228
|
-
context,
|
|
4229
|
-
decision
|
|
4461
|
+
context: redactDeep(context),
|
|
4462
|
+
decision: redactDeep(decision)
|
|
4230
4463
|
});
|
|
4231
4464
|
}
|
|
4232
4465
|
/**
|
|
@@ -4343,10 +4576,12 @@ var AstraSyncGateway = class {
|
|
|
4343
4576
|
}
|
|
4344
4577
|
/**
|
|
4345
4578
|
* Graceful shutdown. Flushes sync queue and cleans up resources.
|
|
4579
|
+
* Stateless callers (hooks) pass bounded flush options so sync never
|
|
4580
|
+
* sits on a tool call's critical path.
|
|
4346
4581
|
*/
|
|
4347
|
-
async shutdown() {
|
|
4582
|
+
async shutdown(options) {
|
|
4348
4583
|
if (this.syncQueue) {
|
|
4349
|
-
await this.syncQueue.stop();
|
|
4584
|
+
await this.syncQueue.stop(options?.syncFlush ?? {});
|
|
4350
4585
|
this.syncQueue = null;
|
|
4351
4586
|
}
|
|
4352
4587
|
this.evaluator = null;
|
|
@@ -4369,7 +4604,7 @@ var AstraSyncGateway = class {
|
|
|
4369
4604
|
let decision;
|
|
4370
4605
|
switch (this.config.mode) {
|
|
4371
4606
|
case "online":
|
|
4372
|
-
decision = await verifyOnline(this.config, context, astraId);
|
|
4607
|
+
decision = await verifyOnline(this.config, context, astraId ?? this.config.agentId);
|
|
4373
4608
|
break;
|
|
4374
4609
|
case "local":
|
|
4375
4610
|
if (!this.evaluator) throw new Error("Local evaluator not initialized");
|
|
@@ -4674,6 +4909,8 @@ var ClaudeCodeAdapter = class {
|
|
|
4674
4909
|
// src/claude-code/hook.ts
|
|
4675
4910
|
var MAX_STDIN_BYTES = 1024 * 1024;
|
|
4676
4911
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
4912
|
+
var HYBRID_FLUSH_LIMIT = 3;
|
|
4913
|
+
var HYBRID_FLUSH_TIMEOUT_MS = 1500;
|
|
4677
4914
|
function resolveGuardConfig(env, payloadCwd, home = (0, import_os.homedir)()) {
|
|
4678
4915
|
const candidateRoots = [env.CLAUDE_PROJECT_DIR, payloadCwd, home].filter(
|
|
4679
4916
|
(root) => typeof root === "string" && root.length > 0
|
|
@@ -4726,13 +4963,25 @@ async function processHookPayload(payloadJson, options = {}) {
|
|
|
4726
4963
|
}
|
|
4727
4964
|
const failOpen = envFailOpen || resolved.settings.failOpen === true;
|
|
4728
4965
|
try {
|
|
4966
|
+
let mode = resolved.settings.mode === "hybrid" ? "hybrid" : "local";
|
|
4967
|
+
if (mode === "hybrid" && !(resolved.settings.apiBaseUrl && resolved.settings.apiKey)) {
|
|
4968
|
+
console.error(
|
|
4969
|
+
"AstraSync Local Guard: guard.json requests hybrid mode but apiBaseUrl/apiKey are missing \u2014 running local-only"
|
|
4970
|
+
);
|
|
4971
|
+
mode = "local";
|
|
4972
|
+
}
|
|
4729
4973
|
const gatewayConfig = {
|
|
4730
|
-
mode
|
|
4974
|
+
mode,
|
|
4731
4975
|
policyFile: resolved.policyFile,
|
|
4732
4976
|
posture: resolved.settings.posture ?? "active",
|
|
4733
4977
|
traceEnabled: true,
|
|
4734
4978
|
tracePath: (0, import_path3.join)(resolved.guardDir, "traces"),
|
|
4735
|
-
debug: false
|
|
4979
|
+
debug: false,
|
|
4980
|
+
...mode === "hybrid" && {
|
|
4981
|
+
apiBaseUrl: resolved.settings.apiBaseUrl,
|
|
4982
|
+
apiKey: resolved.settings.apiKey,
|
|
4983
|
+
agentId: resolved.settings.agentId
|
|
4984
|
+
}
|
|
4736
4985
|
};
|
|
4737
4986
|
const gateway = options.gatewayFactory ? options.gatewayFactory(gatewayConfig) : new AstraSyncGateway(gatewayConfig);
|
|
4738
4987
|
await gateway.initialize();
|
|
@@ -4745,7 +4994,9 @@ async function processHookPayload(payloadJson, options = {}) {
|
|
|
4745
4994
|
await adapter.initialize({ gateway, adapterOptions: {} });
|
|
4746
4995
|
const output = await adapter.evaluatePayload(payload);
|
|
4747
4996
|
await adapter.shutdown();
|
|
4748
|
-
await gateway.shutdown(
|
|
4997
|
+
await gateway.shutdown({
|
|
4998
|
+
syncFlush: { limit: HYBRID_FLUSH_LIMIT, perEntryTimeoutMs: HYBRID_FLUSH_TIMEOUT_MS }
|
|
4999
|
+
});
|
|
4749
5000
|
return { output, exitCode: 0 };
|
|
4750
5001
|
} catch (err) {
|
|
4751
5002
|
if (failOpen) return { output: null, exitCode: 0 };
|