@astrasyncai/verification-gateway 3.10.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-interface/interface.d.mts +1 -1
- package/dist/adapter-interface/interface.d.ts +1 -1
- package/dist/adapters/express.js +1 -1
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +1 -1
- package/dist/adapters/express.mjs.map +1 -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.js +1 -1
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +1 -1
- package/dist/adapters/nextjs.mjs.map +1 -1
- 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/bin/astrasync-claude-hook.js +317 -35
- package/dist/bin/astrasync-codex-hook.js +5107 -0
- package/dist/bin/astrasync-guard.js +430 -40
- package/dist/bin/astrasync.js +453 -41
- package/dist/browser/background.js +279 -24
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +279 -24
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +1 -1
- package/dist/browser/browser-adapter.d.ts +1 -1
- package/dist/claude-code/claude-code-adapter.d.mts +1 -1
- package/dist/claude-code/claude-code-adapter.d.ts +1 -1
- package/dist/claude-code/claude-code-adapter.js +9 -1
- package/dist/claude-code/claude-code-adapter.js.map +1 -1
- package/dist/claude-code/claude-code-adapter.mjs +9 -1
- package/dist/claude-code/claude-code-adapter.mjs.map +1 -1
- package/dist/cli/index.d.mts +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/codex/index.d.mts +230 -0
- package/dist/codex/index.d.ts +230 -0
- package/dist/codex/index.js +5225 -0
- package/dist/codex/index.js.map +1 -0
- package/dist/codex/index.mjs +5197 -0
- package/dist/codex/index.mjs.map +1 -0
- package/dist/cursor/cursor-adapter.d.mts +1 -1
- package/dist/cursor/cursor-adapter.d.ts +1 -1
- package/dist/cursor/extension.d.mts +1 -1
- package/dist/cursor/extension.d.ts +1 -1
- package/dist/cursor/extension.js +279 -24
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +279 -24
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/gateway/gateway.d.mts +32 -2
- package/dist/gateway/gateway.d.ts +32 -2
- package/dist/gateway/gateway.js +279 -24
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +279 -24
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +1 -1
- package/dist/local-evaluator/evaluator.d.ts +1 -1
- 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/{types-D0lCtFQ7.d.mts → types-BV6abBnF.d.mts} +6 -0
- package/dist/{types-YbiXYKx1.d.ts → types-C23_uaY8.d.ts} +6 -0
- 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 +8 -2
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
import { a as AstraSyncGatewayConfig, P as PDLSSContext, V as VerificationDecision } from '../types-
|
|
1
|
+
import { a as AstraSyncGatewayConfig, P as PDLSSContext, V as VerificationDecision } from '../types-BV6abBnF.mjs';
|
|
2
2
|
import '../types-sy3tq8lp.mjs';
|
|
3
3
|
import '../metadata-capture.mjs';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Hybrid Mode — Local evaluation for speed, async API sync for trust scoring.
|
|
7
|
+
*
|
|
8
|
+
* Immediate: local PDLSS evaluation (< 1ms)
|
|
9
|
+
* Async: queued API verification for audit trail and trust score updates
|
|
10
|
+
*
|
|
11
|
+
* Queue entries are persisted to disk so they survive process restarts.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface FlushOptions {
|
|
15
|
+
/** Max entries to attempt this flush (default: all pending). */
|
|
16
|
+
limit?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Per-entry time budget in ms. A timed-out entry stays pending (no retry
|
|
19
|
+
* penalty — slow network is not a failure) and ends the flush early.
|
|
20
|
+
* Stateless callers (the Claude Code hook) use this to keep sync off the
|
|
21
|
+
* tool-call critical path.
|
|
22
|
+
*/
|
|
23
|
+
perEntryTimeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
5
26
|
/**
|
|
6
27
|
* AstraSyncGateway — Primary API surface for agent verification.
|
|
7
28
|
*
|
|
@@ -37,8 +58,12 @@ declare class AstraSyncGateway {
|
|
|
37
58
|
initialize(): Promise<void>;
|
|
38
59
|
/**
|
|
39
60
|
* Graceful shutdown. Flushes sync queue and cleans up resources.
|
|
61
|
+
* Stateless callers (hooks) pass bounded flush options so sync never
|
|
62
|
+
* sits on a tool call's critical path.
|
|
40
63
|
*/
|
|
41
|
-
shutdown(
|
|
64
|
+
shutdown(options?: {
|
|
65
|
+
syncFlush?: FlushOptions;
|
|
66
|
+
}): Promise<void>;
|
|
42
67
|
/**
|
|
43
68
|
* Evaluate an action against the verification policy.
|
|
44
69
|
* Routes to the appropriate mode handler.
|
|
@@ -66,6 +91,11 @@ declare class AstraSyncGateway {
|
|
|
66
91
|
pending: number;
|
|
67
92
|
total: number;
|
|
68
93
|
} | null;
|
|
94
|
+
/**
|
|
95
|
+
* Record a tool call the calling adapter saw but did not govern
|
|
96
|
+
* (unmapped tool, skipTools, MCP with governMcp off). Trace-only.
|
|
97
|
+
*/
|
|
98
|
+
logSkippedTool(toolName: string, reason: string): void;
|
|
69
99
|
}
|
|
70
100
|
|
|
71
101
|
export { AstraSyncGateway };
|
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
import { a as AstraSyncGatewayConfig, P as PDLSSContext, V as VerificationDecision } from '../types-
|
|
1
|
+
import { a as AstraSyncGatewayConfig, P as PDLSSContext, V as VerificationDecision } from '../types-C23_uaY8.js';
|
|
2
2
|
import '../types-9tns4wyl.js';
|
|
3
3
|
import '../metadata-capture.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Hybrid Mode — Local evaluation for speed, async API sync for trust scoring.
|
|
7
|
+
*
|
|
8
|
+
* Immediate: local PDLSS evaluation (< 1ms)
|
|
9
|
+
* Async: queued API verification for audit trail and trust score updates
|
|
10
|
+
*
|
|
11
|
+
* Queue entries are persisted to disk so they survive process restarts.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface FlushOptions {
|
|
15
|
+
/** Max entries to attempt this flush (default: all pending). */
|
|
16
|
+
limit?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Per-entry time budget in ms. A timed-out entry stays pending (no retry
|
|
19
|
+
* penalty — slow network is not a failure) and ends the flush early.
|
|
20
|
+
* Stateless callers (the Claude Code hook) use this to keep sync off the
|
|
21
|
+
* tool-call critical path.
|
|
22
|
+
*/
|
|
23
|
+
perEntryTimeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
5
26
|
/**
|
|
6
27
|
* AstraSyncGateway — Primary API surface for agent verification.
|
|
7
28
|
*
|
|
@@ -37,8 +58,12 @@ declare class AstraSyncGateway {
|
|
|
37
58
|
initialize(): Promise<void>;
|
|
38
59
|
/**
|
|
39
60
|
* Graceful shutdown. Flushes sync queue and cleans up resources.
|
|
61
|
+
* Stateless callers (hooks) pass bounded flush options so sync never
|
|
62
|
+
* sits on a tool call's critical path.
|
|
40
63
|
*/
|
|
41
|
-
shutdown(
|
|
64
|
+
shutdown(options?: {
|
|
65
|
+
syncFlush?: FlushOptions;
|
|
66
|
+
}): Promise<void>;
|
|
42
67
|
/**
|
|
43
68
|
* Evaluate an action against the verification policy.
|
|
44
69
|
* Routes to the appropriate mode handler.
|
|
@@ -66,6 +91,11 @@ declare class AstraSyncGateway {
|
|
|
66
91
|
pending: number;
|
|
67
92
|
total: number;
|
|
68
93
|
} | null;
|
|
94
|
+
/**
|
|
95
|
+
* Record a tool call the calling adapter saw but did not govern
|
|
96
|
+
* (unmapped tool, skipTools, MCP with governMcp off). Trace-only.
|
|
97
|
+
*/
|
|
98
|
+
logSkippedTool(toolName: string, reason: string): void;
|
|
69
99
|
}
|
|
70
100
|
|
|
71
101
|
export { AstraSyncGateway };
|
package/dist/gateway/gateway.js
CHANGED
|
@@ -44,7 +44,9 @@ var HIGH_RISK_COMMANDS = [
|
|
|
44
44
|
"git push",
|
|
45
45
|
"npm publish",
|
|
46
46
|
"docker",
|
|
47
|
-
"kubectl"
|
|
47
|
+
"kubectl",
|
|
48
|
+
"gh pr create",
|
|
49
|
+
"gh pr merge"
|
|
48
50
|
];
|
|
49
51
|
var SENSITIVE_PATHS = [
|
|
50
52
|
".ssh",
|
|
@@ -63,6 +65,164 @@ var SENSITIVE_PATHS = [
|
|
|
63
65
|
".pypirc"
|
|
64
66
|
];
|
|
65
67
|
|
|
68
|
+
// src/local-evaluator/command-matcher.ts
|
|
69
|
+
var WRAPPERS = /* @__PURE__ */ new Set([
|
|
70
|
+
"sudo",
|
|
71
|
+
"doas",
|
|
72
|
+
"env",
|
|
73
|
+
"nohup",
|
|
74
|
+
"time",
|
|
75
|
+
"timeout",
|
|
76
|
+
"nice",
|
|
77
|
+
"ionice",
|
|
78
|
+
"stdbuf",
|
|
79
|
+
"xargs",
|
|
80
|
+
"command",
|
|
81
|
+
"exec",
|
|
82
|
+
"builtin"
|
|
83
|
+
]);
|
|
84
|
+
var ASSIGN_RE = /^[a-z_][a-z0-9_]*=/;
|
|
85
|
+
var MAX_WRAPPER_HOPS = 4;
|
|
86
|
+
var MAX_RECURSION_DEPTH = 3;
|
|
87
|
+
var MAX_COMMAND_LENGTH = 1e5;
|
|
88
|
+
var LOCAL_HOST_RE = /^(localhost|127\.0\.0\.1|::1|[a-z0-9.-]*\.localhost)$/;
|
|
89
|
+
function buildRiskList(entries) {
|
|
90
|
+
const singleWord = /* @__PURE__ */ new Set();
|
|
91
|
+
const multiWord = [];
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
const words = entry.toLowerCase().trim().split(/\s+/);
|
|
94
|
+
if (words.length === 1) singleWord.add(words[0]);
|
|
95
|
+
else multiWord.push(words);
|
|
96
|
+
}
|
|
97
|
+
multiWord.sort((a, b) => b.length - a.length);
|
|
98
|
+
return { singleWord, multiWord };
|
|
99
|
+
}
|
|
100
|
+
function stripHeredocBodies(text) {
|
|
101
|
+
const lines = text.split("\n");
|
|
102
|
+
const kept = [];
|
|
103
|
+
let terminator = null;
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
if (terminator !== null) {
|
|
106
|
+
if (line.trim() === terminator) terminator = null;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
kept.push(line);
|
|
110
|
+
const heredoc = line.match(/<<-?\s*['"]?(\w+)['"]?/);
|
|
111
|
+
if (heredoc) terminator = heredoc[1];
|
|
112
|
+
}
|
|
113
|
+
return kept.join("\n");
|
|
114
|
+
}
|
|
115
|
+
function extractDollarSubstitutions(text, collect) {
|
|
116
|
+
let result = text;
|
|
117
|
+
let start = result.indexOf("$(");
|
|
118
|
+
let iterations = 0;
|
|
119
|
+
while (start !== -1 && iterations < 50) {
|
|
120
|
+
iterations++;
|
|
121
|
+
let depth = 0;
|
|
122
|
+
let end = result.length;
|
|
123
|
+
for (let i = start + 1; i < result.length; i++) {
|
|
124
|
+
if (result[i] === "(") depth++;
|
|
125
|
+
else if (result[i] === ")") {
|
|
126
|
+
depth--;
|
|
127
|
+
if (depth === 0) {
|
|
128
|
+
end = i;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
collect(result.slice(start + 2, end));
|
|
134
|
+
result = `${result.slice(0, start)} ${result.slice(Math.min(end + 1, result.length))}`;
|
|
135
|
+
start = result.indexOf("$(");
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
function extractBacktickSubstitutions(text, collect) {
|
|
140
|
+
if (!text.includes("`")) return text;
|
|
141
|
+
const chunks = text.split("`");
|
|
142
|
+
const kept = [];
|
|
143
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
144
|
+
if (i % 2 === 1) collect(chunks[i]);
|
|
145
|
+
else kept.push(chunks[i]);
|
|
146
|
+
}
|
|
147
|
+
return kept.join(" ");
|
|
148
|
+
}
|
|
149
|
+
function basename(token) {
|
|
150
|
+
const slash = token.lastIndexOf("/");
|
|
151
|
+
return slash === -1 ? token : token.slice(slash + 1);
|
|
152
|
+
}
|
|
153
|
+
function matchSegment(segment, list) {
|
|
154
|
+
const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
|
|
155
|
+
let i = 0;
|
|
156
|
+
let hops = 0;
|
|
157
|
+
while (i < tokens.length) {
|
|
158
|
+
while (i < tokens.length && (tokens[i] === "export" || ASSIGN_RE.test(tokens[i]))) i++;
|
|
159
|
+
if (i >= tokens.length) return null;
|
|
160
|
+
const cmd = basename(tokens[i]);
|
|
161
|
+
const significant = [cmd, ...tokens.slice(i + 1).filter((t) => !t.startsWith("-"))];
|
|
162
|
+
for (const entry of list.multiWord) {
|
|
163
|
+
if (entry.length <= significant.length && entry.every((w, j) => significant[j] === w)) {
|
|
164
|
+
return entry.join(" ");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (list.singleWord.has(cmd)) return cmd;
|
|
168
|
+
if (WRAPPERS.has(cmd) && hops < MAX_WRAPPER_HOPS) {
|
|
169
|
+
hops++;
|
|
170
|
+
i++;
|
|
171
|
+
while (i < tokens.length && (tokens[i].startsWith("-") || ASSIGN_RE.test(tokens[i]))) i++;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
function collectMatches(command, list, matches, depth) {
|
|
179
|
+
if (depth > MAX_RECURSION_DEPTH || command.length > MAX_COMMAND_LENGTH) return;
|
|
180
|
+
let text = command.toLowerCase();
|
|
181
|
+
text = stripHeredocBodies(text);
|
|
182
|
+
const recurse = (inner) => collectMatches(inner, list, matches, depth + 1);
|
|
183
|
+
text = extractDollarSubstitutions(text, recurse);
|
|
184
|
+
text = extractBacktickSubstitutions(text, recurse);
|
|
185
|
+
text = text.replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
|
|
186
|
+
text = text.replace(/(^|\s)#[^\n]*/g, " ");
|
|
187
|
+
const segments = text.split(/\|\||&&|[;|&\n]|[({]/);
|
|
188
|
+
for (const segment of segments) {
|
|
189
|
+
const matched = matchSegment(segment, list);
|
|
190
|
+
if (matched) matches.add(matched);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function matchHighRiskCommands(command, highRiskCommands = HIGH_RISK_COMMANDS) {
|
|
194
|
+
if (!command) return [];
|
|
195
|
+
const matches = /* @__PURE__ */ new Set();
|
|
196
|
+
collectMatches(command, buildRiskList(highRiskCommands), matches, 0);
|
|
197
|
+
return [...matches];
|
|
198
|
+
}
|
|
199
|
+
function isLocalhostOnlyNetworkCommand(command) {
|
|
200
|
+
const text = command.toLowerCase();
|
|
201
|
+
const tokens = text.split(/\s+/).map((t) => t.replace(/^["'`]+|["'`,;)]+$/g, ""));
|
|
202
|
+
let localCount = 0;
|
|
203
|
+
for (const token of tokens) {
|
|
204
|
+
let host = null;
|
|
205
|
+
if (token.includes("://")) {
|
|
206
|
+
let rest = token.slice(token.indexOf("://") + 3);
|
|
207
|
+
const at = rest.indexOf("@");
|
|
208
|
+
if (at !== -1) rest = rest.slice(at + 1);
|
|
209
|
+
host = rest.split(/[/?#]/)[0];
|
|
210
|
+
} else if (/^[a-z0-9.[\]:-]+:\d+(\/.*)?$/.test(token)) {
|
|
211
|
+
host = token.split("/")[0];
|
|
212
|
+
} else if (/^(localhost|127\.0\.0\.1|\[?::1\]?)(\/.*)?$/.test(token)) {
|
|
213
|
+
host = token.split("/")[0];
|
|
214
|
+
}
|
|
215
|
+
if (host === null) continue;
|
|
216
|
+
const bare = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "");
|
|
217
|
+
if (LOCAL_HOST_RE.test(bare)) {
|
|
218
|
+
localCount++;
|
|
219
|
+
} else {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return localCount > 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
66
226
|
// src/local-evaluator/evaluator.ts
|
|
67
227
|
var LocalEvaluator = class {
|
|
68
228
|
constructor(policy) {
|
|
@@ -213,12 +373,10 @@ var LocalEvaluator = class {
|
|
|
213
373
|
return score;
|
|
214
374
|
}
|
|
215
375
|
if (context.purpose === "shell.exec" && context.target) {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
376
|
+
const matched = matchHighRiskCommands(context.target);
|
|
377
|
+
const localProbeOnly = matched.length > 0 && matched.every((cmd) => cmd === "curl" || cmd === "wget") && isLocalhostOnlyNetworkCommand(context.target);
|
|
378
|
+
if (matched.length > 0 && !localProbeOnly) {
|
|
379
|
+
score = Math.max(score, 80);
|
|
222
380
|
}
|
|
223
381
|
}
|
|
224
382
|
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
@@ -3529,7 +3687,7 @@ function getTrustLevel(score) {
|
|
|
3529
3687
|
}
|
|
3530
3688
|
|
|
3531
3689
|
// src/version.ts
|
|
3532
|
-
var SDK_VERSION = "3.
|
|
3690
|
+
var SDK_VERSION = "3.11.0";
|
|
3533
3691
|
|
|
3534
3692
|
// src/well-known.ts
|
|
3535
3693
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -4044,10 +4202,12 @@ function toGatewayConfig(config) {
|
|
|
4044
4202
|
};
|
|
4045
4203
|
}
|
|
4046
4204
|
function toVerificationRequest(context, astraId) {
|
|
4205
|
+
const dotIndex = context.purpose.indexOf(".");
|
|
4206
|
+
const dotted = dotIndex > 0;
|
|
4047
4207
|
return {
|
|
4048
4208
|
credentials: { astraId },
|
|
4049
|
-
purpose: context.purpose,
|
|
4050
|
-
action: context.action,
|
|
4209
|
+
purpose: dotted ? context.purpose.slice(0, dotIndex) : context.purpose,
|
|
4210
|
+
action: dotted ? context.purpose : context.action,
|
|
4051
4211
|
resourceType: context.resourceType,
|
|
4052
4212
|
resource: context.target,
|
|
4053
4213
|
transactionValue: context.transactionValue,
|
|
@@ -4087,8 +4247,75 @@ async function verifyOnline(config, context, astraId) {
|
|
|
4087
4247
|
// src/gateway/modes/hybrid.ts
|
|
4088
4248
|
var import_fs2 = require("fs");
|
|
4089
4249
|
var import_path = require("path");
|
|
4250
|
+
|
|
4251
|
+
// src/gateway/redact.ts
|
|
4252
|
+
var marker = (type2) => `[REDACTED:${type2}]`;
|
|
4253
|
+
var REDACTION_PATTERNS = [
|
|
4254
|
+
// Multi-line blocks first so later single-line patterns don't shred them.
|
|
4255
|
+
{
|
|
4256
|
+
type: "private-key",
|
|
4257
|
+
re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g
|
|
4258
|
+
},
|
|
4259
|
+
// URL userinfo credentials (https://user:pass@, x-access-token:ghp_…@).
|
|
4260
|
+
{
|
|
4261
|
+
type: "url-credentials",
|
|
4262
|
+
re: /([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi,
|
|
4263
|
+
replacement: `$1${marker("url-credentials")}@`
|
|
4264
|
+
},
|
|
4265
|
+
// Known token formats.
|
|
4266
|
+
{ type: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,255}\b/g },
|
|
4267
|
+
{ type: "github-token", re: /\bgithub_pat_[A-Za-z0-9_]{20,255}\b/g },
|
|
4268
|
+
{ type: "astrasync-key", re: /\bkya_[A-Fa-f0-9]{32,128}\b/g },
|
|
4269
|
+
{ type: "openai-key", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g },
|
|
4270
|
+
{ type: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/g },
|
|
4271
|
+
{ type: "jwt", re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
|
|
4272
|
+
// Bearer/token headers.
|
|
4273
|
+
{
|
|
4274
|
+
type: "bearer-token",
|
|
4275
|
+
re: /\b(bearer\s+)[a-z0-9._~+/=-]{16,}/gi,
|
|
4276
|
+
replacement: `$1${marker("bearer-token")}`
|
|
4277
|
+
},
|
|
4278
|
+
// Generic KEY=value / key: value assignments with secret-ish names.
|
|
4279
|
+
{
|
|
4280
|
+
type: "assigned-secret",
|
|
4281
|
+
re: /\b((?:password|passwd|secret|token|api[_-]?key|apikey|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*)(['"]?)[^\s'"&;]{8,}\2/gi,
|
|
4282
|
+
replacement: `$1$2${marker("assigned-secret")}$2`
|
|
4283
|
+
}
|
|
4284
|
+
];
|
|
4285
|
+
function redactSecrets(text) {
|
|
4286
|
+
let result = text;
|
|
4287
|
+
for (const { type: type2, re, replacement } of REDACTION_PATTERNS) {
|
|
4288
|
+
result = result.replace(re, replacement ?? marker(type2));
|
|
4289
|
+
}
|
|
4290
|
+
return result;
|
|
4291
|
+
}
|
|
4292
|
+
function redactDeep(value) {
|
|
4293
|
+
if (typeof value === "string") {
|
|
4294
|
+
return redactSecrets(value);
|
|
4295
|
+
}
|
|
4296
|
+
if (Array.isArray(value)) {
|
|
4297
|
+
return value.map((item) => redactDeep(item));
|
|
4298
|
+
}
|
|
4299
|
+
if (value !== null && typeof value === "object" && !(value instanceof Date)) {
|
|
4300
|
+
const out = {};
|
|
4301
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4302
|
+
out[k] = redactDeep(v);
|
|
4303
|
+
}
|
|
4304
|
+
return out;
|
|
4305
|
+
}
|
|
4306
|
+
return value;
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
// src/gateway/modes/hybrid.ts
|
|
4090
4310
|
var MAX_QUEUE_SIZE = 1e3;
|
|
4091
4311
|
var MAX_RETRIES = 3;
|
|
4312
|
+
var FLUSH_TIMEOUT = /* @__PURE__ */ Symbol("flush-timeout");
|
|
4313
|
+
function delay(ms) {
|
|
4314
|
+
return new Promise((resolve) => {
|
|
4315
|
+
const timer = setTimeout(() => resolve(FLUSH_TIMEOUT), ms);
|
|
4316
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
4317
|
+
});
|
|
4318
|
+
}
|
|
4092
4319
|
var HybridSyncQueue = class {
|
|
4093
4320
|
constructor(config, persistPath) {
|
|
4094
4321
|
this.queue = [];
|
|
@@ -4109,12 +4336,12 @@ var HybridSyncQueue = class {
|
|
|
4109
4336
|
/**
|
|
4110
4337
|
* Stop periodic sync and flush remaining entries.
|
|
4111
4338
|
*/
|
|
4112
|
-
async stop() {
|
|
4339
|
+
async stop(flushOptions = {}) {
|
|
4113
4340
|
if (this.timer) {
|
|
4114
4341
|
clearInterval(this.timer);
|
|
4115
4342
|
this.timer = null;
|
|
4116
4343
|
}
|
|
4117
|
-
await this.flush();
|
|
4344
|
+
await this.flush(flushOptions);
|
|
4118
4345
|
this.saveToDisk();
|
|
4119
4346
|
}
|
|
4120
4347
|
/**
|
|
@@ -4126,8 +4353,8 @@ var HybridSyncQueue = class {
|
|
|
4126
4353
|
}
|
|
4127
4354
|
this.queue.push({
|
|
4128
4355
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
4129
|
-
context,
|
|
4130
|
-
decision,
|
|
4356
|
+
context: redactDeep(context),
|
|
4357
|
+
decision: redactDeep(decision),
|
|
4131
4358
|
timestamp: /* @__PURE__ */ new Date(),
|
|
4132
4359
|
retryCount: 0,
|
|
4133
4360
|
status: "pending"
|
|
@@ -4137,16 +4364,21 @@ var HybridSyncQueue = class {
|
|
|
4137
4364
|
/**
|
|
4138
4365
|
* Flush pending entries to the API.
|
|
4139
4366
|
*/
|
|
4140
|
-
async flush() {
|
|
4367
|
+
async flush(options = {}) {
|
|
4141
4368
|
if (this.processing) return;
|
|
4142
4369
|
this.processing = true;
|
|
4143
4370
|
try {
|
|
4144
4371
|
const pending = this.queue.filter((e) => e.status === "pending");
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4372
|
+
const batch = typeof options.limit === "number" ? pending.slice(0, options.limit) : pending;
|
|
4373
|
+
for (const entry of batch) {
|
|
4374
|
+
const attempt = verifyOnline(this.config, entry.context, this.config.agentId).then(() => "synced").catch(() => "error");
|
|
4375
|
+
const result = options.perEntryTimeoutMs ? await Promise.race([attempt, delay(options.perEntryTimeoutMs)]) : await attempt;
|
|
4376
|
+
if (result === FLUSH_TIMEOUT) {
|
|
4377
|
+
break;
|
|
4378
|
+
}
|
|
4379
|
+
if (result === "synced") {
|
|
4148
4380
|
entry.status = "synced";
|
|
4149
|
-
}
|
|
4381
|
+
} else {
|
|
4150
4382
|
entry.retryCount++;
|
|
4151
4383
|
if (entry.retryCount >= MAX_RETRIES) {
|
|
4152
4384
|
entry.status = "failed";
|
|
@@ -4223,8 +4455,22 @@ var TraceLogger = class {
|
|
|
4223
4455
|
id: this.generateId(),
|
|
4224
4456
|
timestamp: /* @__PURE__ */ new Date(),
|
|
4225
4457
|
type: "evaluation",
|
|
4226
|
-
context,
|
|
4227
|
-
decision
|
|
4458
|
+
context: redactDeep(context),
|
|
4459
|
+
decision: redactDeep(decision)
|
|
4460
|
+
});
|
|
4461
|
+
}
|
|
4462
|
+
/**
|
|
4463
|
+
* Log a tool call the guard saw but did not govern. One line per skip
|
|
4464
|
+
* keeps coverage holes (unmapped platform tools) visible in the traces
|
|
4465
|
+
* instead of silently indistinguishable from ALLOW.
|
|
4466
|
+
*/
|
|
4467
|
+
logSkipped(toolName, reason) {
|
|
4468
|
+
if (!this.enabled) return;
|
|
4469
|
+
this.write({
|
|
4470
|
+
id: this.generateId(),
|
|
4471
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
4472
|
+
type: "skipped",
|
|
4473
|
+
metadata: { toolName, reason }
|
|
4228
4474
|
});
|
|
4229
4475
|
}
|
|
4230
4476
|
/**
|
|
@@ -4341,10 +4587,12 @@ var AstraSyncGateway = class {
|
|
|
4341
4587
|
}
|
|
4342
4588
|
/**
|
|
4343
4589
|
* Graceful shutdown. Flushes sync queue and cleans up resources.
|
|
4590
|
+
* Stateless callers (hooks) pass bounded flush options so sync never
|
|
4591
|
+
* sits on a tool call's critical path.
|
|
4344
4592
|
*/
|
|
4345
|
-
async shutdown() {
|
|
4593
|
+
async shutdown(options) {
|
|
4346
4594
|
if (this.syncQueue) {
|
|
4347
|
-
await this.syncQueue.stop();
|
|
4595
|
+
await this.syncQueue.stop(options?.syncFlush ?? {});
|
|
4348
4596
|
this.syncQueue = null;
|
|
4349
4597
|
}
|
|
4350
4598
|
this.evaluator = null;
|
|
@@ -4367,7 +4615,7 @@ var AstraSyncGateway = class {
|
|
|
4367
4615
|
let decision;
|
|
4368
4616
|
switch (this.config.mode) {
|
|
4369
4617
|
case "online":
|
|
4370
|
-
decision = await verifyOnline(this.config, context, astraId);
|
|
4618
|
+
decision = await verifyOnline(this.config, context, astraId ?? this.config.agentId);
|
|
4371
4619
|
break;
|
|
4372
4620
|
case "local":
|
|
4373
4621
|
if (!this.evaluator) throw new Error("Local evaluator not initialized");
|
|
@@ -4422,6 +4670,13 @@ var AstraSyncGateway = class {
|
|
|
4422
4670
|
getSyncStatus() {
|
|
4423
4671
|
return this.syncQueue?.getStatus() || null;
|
|
4424
4672
|
}
|
|
4673
|
+
/**
|
|
4674
|
+
* Record a tool call the calling adapter saw but did not govern
|
|
4675
|
+
* (unmapped tool, skipTools, MCP with governMcp off). Trace-only.
|
|
4676
|
+
*/
|
|
4677
|
+
logSkippedTool(toolName, reason) {
|
|
4678
|
+
this.traceLogger?.logSkipped(toolName, reason);
|
|
4679
|
+
}
|
|
4425
4680
|
};
|
|
4426
4681
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4427
4682
|
0 && (module.exports = {
|