@astrasyncai/verification-gateway 1.0.0 → 2.0.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 +71 -0
- package/dist/adapter-interface/interface.d.ts +71 -0
- package/dist/adapter-interface/interface.js +36 -0
- package/dist/adapter-interface/interface.js.map +1 -0
- package/dist/adapter-interface/interface.mjs +10 -0
- package/dist/adapter-interface/interface.mjs.map +1 -0
- package/dist/adapter-interface/purpose-mapping.d.mts +28 -0
- package/dist/adapter-interface/purpose-mapping.d.ts +28 -0
- package/dist/adapter-interface/purpose-mapping.js +117 -0
- package/dist/adapter-interface/purpose-mapping.js.map +1 -0
- package/dist/adapter-interface/purpose-mapping.mjs +89 -0
- package/dist/adapter-interface/purpose-mapping.mjs.map +1 -0
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +46 -9
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +46 -9
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +19 -9
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +19 -9
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +20 -4
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +20 -4
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -0
- package/dist/agent/index.d.ts +2 -0
- package/dist/agent/index.js +354 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/index.mjs +323 -0
- package/dist/agent/index.mjs.map +1 -0
- package/dist/browser/browser-adapter.d.mts +106 -0
- package/dist/browser/browser-adapter.d.ts +106 -0
- package/dist/browser/browser-adapter.js +286 -0
- package/dist/browser/browser-adapter.js.map +1 -0
- package/dist/browser/browser-adapter.mjs +259 -0
- package/dist/browser/browser-adapter.mjs.map +1 -0
- package/dist/cli/index.d.mts +241 -0
- package/dist/cli/index.d.ts +241 -0
- package/dist/cli/index.js +3734 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +3688 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/cursor/cursor-adapter.d.mts +92 -0
- package/dist/cursor/cursor-adapter.d.ts +92 -0
- package/dist/cursor/cursor-adapter.js +273 -0
- package/dist/cursor/cursor-adapter.js.map +1 -0
- package/dist/cursor/cursor-adapter.mjs +246 -0
- package/dist/cursor/cursor-adapter.mjs.map +1 -0
- package/dist/{express-DUDYpvNZ.d.mts → express-Cp4eg77F.d.mts} +1 -1
- package/dist/{express-BhD3mWsL.d.ts → express-DIEyq1Tz.d.ts} +1 -1
- package/dist/gateway/gateway.d.mts +70 -0
- package/dist/gateway/gateway.d.ts +70 -0
- package/dist/gateway/gateway.js +3726 -0
- package/dist/gateway/gateway.js.map +1 -0
- package/dist/gateway/gateway.mjs +3706 -0
- package/dist/gateway/gateway.mjs.map +1 -0
- package/dist/git-trigger/git-hooks.d.mts +69 -0
- package/dist/git-trigger/git-hooks.d.ts +69 -0
- package/dist/git-trigger/git-hooks.js +244 -0
- package/dist/git-trigger/git-hooks.js.map +1 -0
- package/dist/git-trigger/git-hooks.mjs +221 -0
- package/dist/git-trigger/git-hooks.mjs.map +1 -0
- package/dist/index-BhTbGU-o.d.mts +206 -0
- package/dist/index-Bhfxq9xI.d.ts +206 -0
- package/dist/index-CNkmHmpi.d.ts +89 -0
- package/dist/index-CoLebmwv.d.mts +89 -0
- package/dist/index.d.mts +8 -295
- package/dist/index.d.ts +8 -295
- package/dist/index.js +60 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -21
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +55 -0
- package/dist/local-evaluator/evaluator.d.ts +55 -0
- package/dist/local-evaluator/evaluator.js +272 -0
- package/dist/local-evaluator/evaluator.js.map +1 -0
- package/dist/local-evaluator/evaluator.mjs +244 -0
- package/dist/local-evaluator/evaluator.mjs.map +1 -0
- package/dist/{nextjs-C9FPOjSh.d.ts → nextjs-Cag7libc.d.ts} +1 -1
- package/dist/{nextjs-BtqyLSVQ.d.mts → nextjs-_C_FcJY5.d.mts} +1 -1
- package/dist/{sdk-BkVigGjF.d.ts → sdk-CMPDFUjo.d.ts} +3 -1
- package/dist/{sdk-xCbZgeZx.d.mts → sdk-DAJahT3p.d.mts} +3 -1
- package/dist/transport/index.d.mts +2 -0
- package/dist/transport/index.d.ts +2 -0
- package/dist/transport/index.js +211 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/index.mjs +176 -0
- package/dist/transport/index.mjs.map +1 -0
- package/dist/{types-CS6v75-d.d.mts → types-Bf8pML07.d.mts} +9 -1
- package/dist/{types-CS6v75-d.d.ts → types-Bf8pML07.d.ts} +9 -1
- package/dist/types-BvpGdsv1.d.mts +153 -0
- package/dist/types-Ce2mFJkO.d.ts +153 -0
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +46 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/local-evaluator/evaluator.ts
|
|
21
|
+
var evaluator_exports = {};
|
|
22
|
+
__export(evaluator_exports, {
|
|
23
|
+
LocalEvaluator: () => LocalEvaluator,
|
|
24
|
+
findPurposeRule: () => findPurposeRule
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(evaluator_exports);
|
|
27
|
+
|
|
28
|
+
// src/gateway/types.ts
|
|
29
|
+
var HIGH_RISK_COMMANDS = [
|
|
30
|
+
"rm",
|
|
31
|
+
"rmdir",
|
|
32
|
+
"dd",
|
|
33
|
+
"mkfs",
|
|
34
|
+
"chmod",
|
|
35
|
+
"chown",
|
|
36
|
+
"sudo",
|
|
37
|
+
"su",
|
|
38
|
+
"curl",
|
|
39
|
+
"wget",
|
|
40
|
+
"nc",
|
|
41
|
+
"netcat",
|
|
42
|
+
"ssh",
|
|
43
|
+
"scp",
|
|
44
|
+
"rsync",
|
|
45
|
+
"git push",
|
|
46
|
+
"npm publish",
|
|
47
|
+
"docker",
|
|
48
|
+
"kubectl"
|
|
49
|
+
];
|
|
50
|
+
var SENSITIVE_PATHS = [
|
|
51
|
+
".ssh",
|
|
52
|
+
".aws",
|
|
53
|
+
".gnupg",
|
|
54
|
+
".env",
|
|
55
|
+
"credentials",
|
|
56
|
+
"secrets",
|
|
57
|
+
"password",
|
|
58
|
+
".git/config",
|
|
59
|
+
"/etc",
|
|
60
|
+
"/var",
|
|
61
|
+
"/root",
|
|
62
|
+
"id_rsa",
|
|
63
|
+
".npmrc",
|
|
64
|
+
".pypirc"
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// src/local-evaluator/evaluator.ts
|
|
68
|
+
var LocalEvaluator = class {
|
|
69
|
+
constructor(policy) {
|
|
70
|
+
this.requestCounts = /* @__PURE__ */ new Map();
|
|
71
|
+
this.policy = policy;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Update the policy (e.g. after hot-reload or sync).
|
|
75
|
+
*/
|
|
76
|
+
updatePolicy(policy) {
|
|
77
|
+
this.policy = policy;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Evaluate an action context against the loaded policy.
|
|
81
|
+
*/
|
|
82
|
+
evaluate(context) {
|
|
83
|
+
const purposeRule = this.policy.purposes.find((p) => p.id === context.purpose);
|
|
84
|
+
if (!purposeRule) {
|
|
85
|
+
return { recommendation: "DENY", reason: "Purpose not in policy" };
|
|
86
|
+
}
|
|
87
|
+
if (!purposeRule.allowed) {
|
|
88
|
+
return { recommendation: "DENY", reason: "Purpose explicitly blocked" };
|
|
89
|
+
}
|
|
90
|
+
if (purposeRule.targets && !this.matchesAnyPattern(context.target, purposeRule.targets)) {
|
|
91
|
+
return { recommendation: "DENY", reason: "Target not in allowed list" };
|
|
92
|
+
}
|
|
93
|
+
if (purposeRule.blockedPatterns && this.matchesAnyPattern(context.target, purposeRule.blockedPatterns)) {
|
|
94
|
+
return { recommendation: "DENY", reason: "Target matches blocked pattern" };
|
|
95
|
+
}
|
|
96
|
+
const scopeBlock = this.checkScopeBlock(context);
|
|
97
|
+
if (scopeBlock) {
|
|
98
|
+
return { recommendation: "DENY", reason: scopeBlock };
|
|
99
|
+
}
|
|
100
|
+
const limitViolation = this.checkLimits(context);
|
|
101
|
+
if (limitViolation) {
|
|
102
|
+
return { recommendation: "DENY", reason: limitViolation };
|
|
103
|
+
}
|
|
104
|
+
if (context.purpose === "sub_agent.spawn" && this.policy.selfInstantiation) {
|
|
105
|
+
if (!this.policy.selfInstantiation.allowed) {
|
|
106
|
+
return { recommendation: "DENY", reason: "Sub-agent spawning is not allowed" };
|
|
107
|
+
}
|
|
108
|
+
const depth = context.metadata?.subAgentDepth || 0;
|
|
109
|
+
if (this.policy.selfInstantiation.maxDepth !== void 0 && depth >= this.policy.selfInstantiation.maxDepth) {
|
|
110
|
+
return { recommendation: "DENY", reason: `Sub-agent depth ${depth} exceeds max depth ${this.policy.selfInstantiation.maxDepth}` };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (purposeRule.requiresApproval) {
|
|
114
|
+
return { recommendation: "MANUAL_REVIEW", reason: "Purpose requires approval" };
|
|
115
|
+
}
|
|
116
|
+
const riskDecision = this.checkRiskThresholds(context);
|
|
117
|
+
if (riskDecision) {
|
|
118
|
+
return riskDecision;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
recommendation: "ALLOW",
|
|
122
|
+
reason: "All PDLSS checks passed",
|
|
123
|
+
evaluatedDimensions: {
|
|
124
|
+
purpose: true,
|
|
125
|
+
scope: !!this.policy.scope,
|
|
126
|
+
limits: !!this.policy.limits,
|
|
127
|
+
riskThresholds: !!this.policy.riskThresholds
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
checkScopeBlock(context) {
|
|
132
|
+
const scope = this.policy.scope;
|
|
133
|
+
if (!scope) return null;
|
|
134
|
+
if (scope.blockedDomains) {
|
|
135
|
+
const targetDomain = this.extractDomain(context.target);
|
|
136
|
+
if (this.matchesAnyPattern(targetDomain, scope.blockedDomains)) {
|
|
137
|
+
return `Target blocked by scope: ${context.target}`;
|
|
138
|
+
}
|
|
139
|
+
if (context.networkAccess) {
|
|
140
|
+
for (const domain of context.networkAccess) {
|
|
141
|
+
if (this.matchesAnyPattern(this.extractDomain(domain), scope.blockedDomains)) {
|
|
142
|
+
return `Domain blocked by scope: ${domain}`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (scope.blockedResources && this.matchesAnyPattern(context.target, scope.blockedResources)) {
|
|
148
|
+
return `Resource blocked by scope: ${context.target}`;
|
|
149
|
+
}
|
|
150
|
+
if (scope.allowedDomains && context.networkAccess) {
|
|
151
|
+
for (const domain of context.networkAccess) {
|
|
152
|
+
if (!this.matchesAnyPattern(this.extractDomain(domain), scope.allowedDomains)) {
|
|
153
|
+
return `Domain not in allowed list: ${domain}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
checkLimits(context) {
|
|
160
|
+
const limits = this.policy.limits;
|
|
161
|
+
if (!limits) return null;
|
|
162
|
+
if (limits.maxTransactionAmount !== void 0 && context.transactionValue !== void 0) {
|
|
163
|
+
if (context.transactionValue > limits.maxTransactionAmount) {
|
|
164
|
+
return `Transaction value ${context.transactionValue} exceeds limit ${limits.maxTransactionAmount}`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (limits.maxRequestsPerHour !== void 0) {
|
|
168
|
+
const key = context.purpose;
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
const entry = this.requestCounts.get(key);
|
|
171
|
+
const hourMs = 36e5;
|
|
172
|
+
if (!entry || now - entry.windowStart > hourMs) {
|
|
173
|
+
this.requestCounts.set(key, { count: 1, windowStart: now });
|
|
174
|
+
} else {
|
|
175
|
+
entry.count++;
|
|
176
|
+
if (entry.count > limits.maxRequestsPerHour) {
|
|
177
|
+
return `Rate limit exceeded: ${entry.count}/${limits.maxRequestsPerHour} requests per hour`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
checkRiskThresholds(context) {
|
|
184
|
+
if (!this.policy.riskThresholds) return null;
|
|
185
|
+
const riskScore = this.calculateRiskScore(context);
|
|
186
|
+
const thresholds = this.policy.riskThresholds;
|
|
187
|
+
if (riskScore >= thresholds.autoBlock.min) {
|
|
188
|
+
return { recommendation: "DENY", reason: `Risk score ${riskScore} exceeds block threshold` };
|
|
189
|
+
}
|
|
190
|
+
if (riskScore >= thresholds.requireApproval.min) {
|
|
191
|
+
return { recommendation: "MANUAL_REVIEW", reason: `Risk score ${riskScore} requires approval` };
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
calculateRiskScore(context) {
|
|
196
|
+
let score = 0;
|
|
197
|
+
if (context.riskFactors?.length) {
|
|
198
|
+
const severityScores = {
|
|
199
|
+
low: 10,
|
|
200
|
+
medium: 40,
|
|
201
|
+
high: 70,
|
|
202
|
+
critical: 90
|
|
203
|
+
};
|
|
204
|
+
for (const factor of context.riskFactors) {
|
|
205
|
+
const factorScore = severityScores[factor.severity] || 0;
|
|
206
|
+
if (factorScore > score) score = factorScore;
|
|
207
|
+
}
|
|
208
|
+
return score;
|
|
209
|
+
}
|
|
210
|
+
if (context.purpose === "shell.exec" && context.target) {
|
|
211
|
+
const targetLower = context.target.toLowerCase();
|
|
212
|
+
for (const cmd of HIGH_RISK_COMMANDS) {
|
|
213
|
+
if (targetLower.startsWith(cmd) || targetLower.includes(` ${cmd} `) || targetLower.includes(` ${cmd}`)) {
|
|
214
|
+
score = Math.max(score, 80);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
220
|
+
const targetLower = context.target.toLowerCase();
|
|
221
|
+
for (const sensitivePath of SENSITIVE_PATHS) {
|
|
222
|
+
if (targetLower.includes(sensitivePath.toLowerCase())) {
|
|
223
|
+
score = Math.max(score, 50);
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return score;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Extract domain from a URL-like string (strips protocol and path).
|
|
232
|
+
*/
|
|
233
|
+
extractDomain(value) {
|
|
234
|
+
let domain = value;
|
|
235
|
+
const protoIndex = domain.indexOf("://");
|
|
236
|
+
if (protoIndex !== -1) domain = domain.slice(protoIndex + 3);
|
|
237
|
+
const slashIndex = domain.indexOf("/");
|
|
238
|
+
if (slashIndex !== -1) domain = domain.slice(0, slashIndex);
|
|
239
|
+
const colonIndex = domain.indexOf(":");
|
|
240
|
+
if (colonIndex !== -1) domain = domain.slice(0, colonIndex);
|
|
241
|
+
return domain;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Check if a value matches any of the given glob patterns.
|
|
245
|
+
*/
|
|
246
|
+
matchesAnyPattern(value, patterns) {
|
|
247
|
+
return patterns.some((pattern) => this.matchGlob(value, pattern));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Simple glob pattern matching.
|
|
251
|
+
* Supports * (matches any characters including / and spaces) and ? (single char).
|
|
252
|
+
* Uses the same approach as the backend's matchGlobPattern.
|
|
253
|
+
*/
|
|
254
|
+
matchGlob(value, pattern) {
|
|
255
|
+
if (pattern === value) return true;
|
|
256
|
+
const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
257
|
+
try {
|
|
258
|
+
return new RegExp(`^${regexStr}$`, "i").test(value);
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
function findPurposeRule(policy, purposeId) {
|
|
265
|
+
return policy.purposes.find((p) => p.id === purposeId);
|
|
266
|
+
}
|
|
267
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
268
|
+
0 && (module.exports = {
|
|
269
|
+
LocalEvaluator,
|
|
270
|
+
findPurposeRule
|
|
271
|
+
});
|
|
272
|
+
//# sourceMappingURL=evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/local-evaluator/evaluator.ts","../../src/gateway/types.ts"],"sourcesContent":["/**\n * Local PDLSS Evaluator\n *\n * Evaluates agent actions against a local PDLSS policy.\n * Same logic as the cloud evaluator but runs in-process with no I/O.\n *\n * Evaluation order:\n * 1. Purpose: find matching rule, check if allowed\n * 2. Purpose: check allowed targets (if specified)\n * 3. Purpose: check blocked patterns (if specified)\n * 4. Scope: check global blocked resources/domains\n * 5. Limits: check transaction/rate limits\n * 6. Risk thresholds + approval requirements\n */\n\nimport type { LocalPolicy, PDLSSContext, VerificationDecision, LocalPurposeRule } from '../gateway/types';\nimport { BASE_RISK_SCORES, HIGH_RISK_COMMANDS, SENSITIVE_PATHS } from '../gateway/types';\n\nexport class LocalEvaluator {\n private policy: LocalPolicy;\n private requestCounts: Map<string, { count: number; windowStart: number }> = new Map();\n\n constructor(policy: LocalPolicy) {\n this.policy = policy;\n }\n\n /**\n * Update the policy (e.g. after hot-reload or sync).\n */\n updatePolicy(policy: LocalPolicy): void {\n this.policy = policy;\n }\n\n /**\n * Evaluate an action context against the loaded policy.\n */\n evaluate(context: PDLSSContext): VerificationDecision {\n // 1. Purpose: find matching rule\n const purposeRule = this.policy.purposes.find((p) => p.id === context.purpose);\n if (!purposeRule) {\n return { recommendation: 'DENY', reason: 'Purpose not in policy' };\n }\n if (!purposeRule.allowed) {\n return { recommendation: 'DENY', reason: 'Purpose explicitly blocked' };\n }\n\n // 2. Purpose: check allowed targets\n if (purposeRule.targets && !this.matchesAnyPattern(context.target, purposeRule.targets)) {\n return { recommendation: 'DENY', reason: 'Target not in allowed list' };\n }\n\n // 3. Purpose: check blocked patterns\n if (purposeRule.blockedPatterns && this.matchesAnyPattern(context.target, purposeRule.blockedPatterns)) {\n return { recommendation: 'DENY', reason: 'Target matches blocked pattern' };\n }\n\n // 4. Scope: check global blocked resources/domains\n const scopeBlock = this.checkScopeBlock(context);\n if (scopeBlock) {\n return { recommendation: 'DENY', reason: scopeBlock };\n }\n\n // 5. Limits: check transaction and rate limits\n const limitViolation = this.checkLimits(context);\n if (limitViolation) {\n return { recommendation: 'DENY', reason: limitViolation };\n }\n\n // 6. Self-instantiation: check sub-agent spawning rules\n if (context.purpose === 'sub_agent.spawn' && this.policy.selfInstantiation) {\n if (!this.policy.selfInstantiation.allowed) {\n return { recommendation: 'DENY', reason: 'Sub-agent spawning is not allowed' };\n }\n const depth = (context.metadata?.subAgentDepth as number) || 0;\n if (this.policy.selfInstantiation.maxDepth !== undefined && depth >= this.policy.selfInstantiation.maxDepth) {\n return { recommendation: 'DENY', reason: `Sub-agent depth ${depth} exceeds max depth ${this.policy.selfInstantiation.maxDepth}` };\n }\n }\n\n // 7. Risk thresholds + approval requirements\n if (purposeRule.requiresApproval) {\n return { recommendation: 'MANUAL_REVIEW', reason: 'Purpose requires approval' };\n }\n\n const riskDecision = this.checkRiskThresholds(context);\n if (riskDecision) {\n return riskDecision;\n }\n\n return {\n recommendation: 'ALLOW',\n reason: 'All PDLSS checks passed',\n evaluatedDimensions: {\n purpose: true,\n scope: !!this.policy.scope,\n limits: !!this.policy.limits,\n riskThresholds: !!this.policy.riskThresholds,\n },\n };\n }\n\n private checkScopeBlock(context: PDLSSContext): string | null {\n const scope = this.policy.scope;\n if (!scope) return null;\n\n // Check blocked domains against target and network access\n if (scope.blockedDomains) {\n const targetDomain = this.extractDomain(context.target);\n if (this.matchesAnyPattern(targetDomain, scope.blockedDomains)) {\n return `Target blocked by scope: ${context.target}`;\n }\n if (context.networkAccess) {\n for (const domain of context.networkAccess) {\n if (this.matchesAnyPattern(this.extractDomain(domain), scope.blockedDomains)) {\n return `Domain blocked by scope: ${domain}`;\n }\n }\n }\n }\n\n // Check blocked resources against target\n if (scope.blockedResources && this.matchesAnyPattern(context.target, scope.blockedResources)) {\n return `Resource blocked by scope: ${context.target}`;\n }\n\n // Check allowed domains (if specified, target must match)\n if (scope.allowedDomains && context.networkAccess) {\n for (const domain of context.networkAccess) {\n if (!this.matchesAnyPattern(this.extractDomain(domain), scope.allowedDomains)) {\n return `Domain not in allowed list: ${domain}`;\n }\n }\n }\n\n return null;\n }\n\n private checkLimits(context: PDLSSContext): string | null {\n const limits = this.policy.limits;\n if (!limits) return null;\n\n // Transaction amount check\n if (limits.maxTransactionAmount !== undefined && context.transactionValue !== undefined) {\n if (context.transactionValue > limits.maxTransactionAmount) {\n return `Transaction value ${context.transactionValue} exceeds limit ${limits.maxTransactionAmount}`;\n }\n }\n\n // Rate limit check\n if (limits.maxRequestsPerHour !== undefined) {\n const key = context.purpose;\n const now = Date.now();\n const entry = this.requestCounts.get(key);\n const hourMs = 3600000;\n\n if (!entry || now - entry.windowStart > hourMs) {\n this.requestCounts.set(key, { count: 1, windowStart: now });\n } else {\n entry.count++;\n if (entry.count > limits.maxRequestsPerHour) {\n return `Rate limit exceeded: ${entry.count}/${limits.maxRequestsPerHour} requests per hour`;\n }\n }\n }\n\n return null;\n }\n\n private checkRiskThresholds(context: PDLSSContext): VerificationDecision | null {\n if (!this.policy.riskThresholds) return null;\n\n const riskScore = this.calculateRiskScore(context);\n const thresholds = this.policy.riskThresholds;\n\n if (riskScore >= thresholds.autoBlock.min) {\n return { recommendation: 'DENY', reason: `Risk score ${riskScore} exceeds block threshold` };\n }\n\n if (riskScore >= thresholds.requireApproval.min) {\n return { recommendation: 'MANUAL_REVIEW', reason: `Risk score ${riskScore} requires approval` };\n }\n\n return null;\n }\n\n private calculateRiskScore(context: PDLSSContext): number {\n let score = 0;\n\n // Explicit risk factors take priority (highest severity wins)\n if (context.riskFactors?.length) {\n const severityScores: Record<string, number> = {\n low: 10,\n medium: 40,\n high: 70,\n critical: 90,\n };\n\n for (const factor of context.riskFactors) {\n const factorScore = severityScores[factor.severity] || 0;\n if (factorScore > score) score = factorScore;\n }\n return score;\n }\n\n // Auto-detect risk from high-risk shell commands\n if (context.purpose === 'shell.exec' && context.target) {\n const targetLower = context.target.toLowerCase();\n for (const cmd of HIGH_RISK_COMMANDS) {\n if (targetLower.startsWith(cmd) || targetLower.includes(` ${cmd} `) || targetLower.includes(` ${cmd}`)) {\n score = Math.max(score, 80);\n break;\n }\n }\n }\n\n // Auto-detect risk from sensitive file paths (score 50 = review range)\n if ((context.purpose === 'file.read' || context.purpose === 'file.write' || context.purpose === 'file.delete') && context.target) {\n const targetLower = context.target.toLowerCase();\n for (const sensitivePath of SENSITIVE_PATHS) {\n if (targetLower.includes(sensitivePath.toLowerCase())) {\n score = Math.max(score, 50);\n break;\n }\n }\n }\n\n return score;\n }\n\n /**\n * Extract domain from a URL-like string (strips protocol and path).\n */\n private extractDomain(value: string): string {\n let domain = value;\n // Strip protocol\n const protoIndex = domain.indexOf('://');\n if (protoIndex !== -1) domain = domain.slice(protoIndex + 3);\n // Strip path\n const slashIndex = domain.indexOf('/');\n if (slashIndex !== -1) domain = domain.slice(0, slashIndex);\n // Strip port\n const colonIndex = domain.indexOf(':');\n if (colonIndex !== -1) domain = domain.slice(0, colonIndex);\n return domain;\n }\n\n /**\n * Check if a value matches any of the given glob patterns.\n */\n private matchesAnyPattern(value: string, patterns: string[]): boolean {\n return patterns.some((pattern) => this.matchGlob(value, pattern));\n }\n\n /**\n * Simple glob pattern matching.\n * Supports * (matches any characters including / and spaces) and ? (single char).\n * Uses the same approach as the backend's matchGlobPattern.\n */\n private matchGlob(value: string, pattern: string): boolean {\n // Exact match\n if (pattern === value) return true;\n\n // Convert glob to regex\n const regexStr = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special regex chars (except * and ?)\n .replace(/\\*/g, '.*') // * matches anything\n .replace(/\\?/g, '.'); // ? matches single char\n\n try {\n return new RegExp(`^${regexStr}$`, 'i').test(value);\n } catch {\n return false;\n }\n }\n}\n\n/**\n * Convenience: find a matching purpose rule.\n */\nexport function findPurposeRule(policy: LocalPolicy, purposeId: string): LocalPurposeRule | undefined {\n return policy.purposes.find((p) => p.id === purposeId);\n}\n","/**\n * AstraSync Gateway - Types for gateway modes, local evaluation, and adapter interface.\n */\n\n// ========================================================================\n// Gateway Configuration\n// ========================================================================\n\nexport type GatewayMode = 'online' | 'local' | 'hybrid';\n\n/**\n * Posture controls whether the gateway actively blocks or just monitors.\n * - active: Evaluate and enforce decisions (block/allow/review)\n * - passive: Evaluate and log but never block (telemetry-only mode)\n */\nexport type GatewayPosture = 'active' | 'passive';\n\nexport interface AstraSyncGatewayConfig {\n mode: GatewayMode;\n /** Enforcement posture: 'active' blocks actions, 'passive' logs only (default: 'active') */\n posture?: GatewayPosture;\n /** AstraSync API base URL (required for online/hybrid modes) */\n apiBaseUrl?: string;\n /** API key for authenticating with AstraSync (required for online/hybrid modes) */\n apiKey?: string;\n /** Path to local PDLSS policy YAML file (required for local/hybrid modes) */\n policyFile?: string;\n /** Inline policy object (alternative to policyFile) */\n policy?: LocalPolicy;\n /** Sync interval in seconds for hybrid mode (default: 3600) */\n syncInterval?: number;\n /** Cache verification results TTL in seconds (default: 300) */\n cacheTtl?: number;\n /** Enable debug logging */\n debug?: boolean;\n /** Enable trace logging to .astrasync/traces/ (default: false) */\n traceEnabled?: boolean;\n /** Trace log directory (default: .astrasync/traces/) */\n tracePath?: string;\n /** Default access level for unverified requests */\n defaultAccessLevel?: import('../types').AccessLevel;\n /** Minimum trust score for standard access (online/hybrid) */\n minTrustScore?: number;\n /** Minimum trust score for full access (online/hybrid) */\n minTrustScoreForFull?: number;\n /** Custom headers to send with API requests */\n customHeaders?: Record<string, string>;\n /** Counterparty URL for analytics */\n counterpartyUrl?: string;\n /** Counterparty type for analytics */\n counterpartyType?: import('../types').CounterpartyType;\n}\n\n// ========================================================================\n// PDLSS Context (Agent-side action context)\n// ========================================================================\n\nexport interface PDLSSContext {\n /** Purpose category (e.g. email.send, shell.exec, file.read) */\n purpose: string;\n /** Specific action within purpose */\n action: string;\n /** Target resource, recipient, or counterparty */\n target: string;\n /** Types of data access (read, write, delete) */\n dataAccess?: string[];\n /** Network domains/IPs being accessed */\n networkAccess?: string[];\n /** Resource type (customer, order, file, directory, process) */\n resourceType?: string;\n /** Risk factors for this action */\n riskFactors?: RiskFactor[];\n /** Transaction value (if financial) */\n transactionValue?: number;\n /** Currency for transaction */\n currency?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\nexport interface RiskFactor {\n type: 'financial' | 'data_sensitivity' | 'privilege_escalation' | 'network_scope' | 'destructive';\n severity: 'low' | 'medium' | 'high' | 'critical';\n detail: string;\n}\n\n// ========================================================================\n// Verification Decision\n// ========================================================================\n\nexport interface VerificationDecision {\n recommendation: 'ALLOW' | 'DENY' | 'MANUAL_REVIEW';\n reason: string;\n trustScore?: number;\n tokenGuidance?: import('../types').TokenGuidance;\n sessionId?: string;\n /** PDLSS dimensions that were evaluated */\n evaluatedDimensions?: {\n purpose: boolean;\n scope: boolean;\n limits: boolean;\n riskThresholds: boolean;\n };\n}\n\n// ========================================================================\n// Local Policy Types (YAML format)\n// ========================================================================\n\nexport interface LocalPolicy {\n version: string;\n name: string;\n description?: string;\n purposes: LocalPurposeRule[];\n scope?: LocalScope;\n limits?: LocalLimits;\n riskThresholds?: LocalRiskThresholds;\n selfInstantiation?: LocalSelfInstantiation;\n}\n\nexport interface LocalPurposeRule {\n id: string;\n allowed: boolean;\n targets?: string[];\n blockedPatterns?: string[];\n requiresApproval?: boolean;\n}\n\nexport interface LocalScope {\n allowedDomains?: string[];\n blockedDomains?: string[];\n blockedResources?: string[];\n}\n\nexport interface LocalLimits {\n maxTransactionAmount?: number;\n maxRequestsPerHour?: number;\n currency?: string;\n}\n\nexport interface LocalRiskThresholds {\n autoAllow: { min: number; max: number };\n requireApproval: { min: number; max: number };\n autoBlock: { min: number; max: number };\n}\n\nexport interface LocalSelfInstantiation {\n /** Whether sub-agent spawning is allowed */\n allowed: boolean;\n /** Maximum depth of sub-agent chain */\n maxDepth?: number;\n}\n\n// ========================================================================\n// Risk Scoring Defaults (cherry-picked from trust-harness-core)\n// ========================================================================\n\n/** Base risk scores per action category */\nexport const BASE_RISK_SCORES: Record<string, number> = {\n 'file.read': 10,\n 'file.write': 40,\n 'file.delete': 70,\n 'shell.exec': 50,\n 'network.fetch': 60,\n 'network.request': 60,\n 'email.send': 45,\n 'email.read': 15,\n 'calendar.create': 20,\n 'calendar.modify': 30,\n 'database.query': 25,\n 'database.write': 55,\n 'payment.execute': 80,\n 'sub_agent.spawn': 65,\n 'code.execute': 45,\n};\n\n/** Shell commands that significantly increase risk score */\nexport const HIGH_RISK_COMMANDS = [\n 'rm', 'rmdir', 'dd', 'mkfs', 'chmod', 'chown',\n 'sudo', 'su', 'curl', 'wget', 'nc', 'netcat',\n 'ssh', 'scp', 'rsync', 'git push', 'npm publish',\n 'docker', 'kubectl',\n];\n\n/** File paths that indicate sensitive data access */\nexport const SENSITIVE_PATHS = [\n '.ssh', '.aws', '.gnupg', '.env', 'credentials',\n 'secrets', 'password', '.git/config', '/etc', '/var', '/root',\n 'id_rsa', '.npmrc', '.pypirc',\n];\n\n// ========================================================================\n// Trace Event Types\n// ========================================================================\n\nexport interface TraceEvent {\n id: string;\n timestamp: Date;\n type: 'evaluation' | 'decision' | 'error' | 'mode_switch';\n context?: PDLSSContext;\n decision?: VerificationDecision;\n metadata?: Record<string, unknown>;\n}\n\n// ========================================================================\n// Adapter Interface Types\n// ========================================================================\n\nexport interface AdapterConfig {\n /** The gateway instance (handles mode routing) */\n gateway: unknown; // Typed as AstraSyncGateway at usage site to avoid circular deps\n /** Platform-specific configuration */\n adapterOptions: Record<string, unknown>;\n}\n\nexport interface AgentAction {\n /** Raw action data from the platform */\n raw: unknown;\n /** Platform identifier (e.g. 'openclaw-cli', 'cursor', 'browser') */\n platform: string;\n /** Timestamp of the action */\n timestamp: Date;\n}\n\nexport interface InterceptResult {\n /** Whether the action was intercepted */\n intercepted: boolean;\n /** Extracted PDLSS context (if intercepted) */\n context?: PDLSSContext;\n /** Reason for not intercepting (if not intercepted) */\n skipReason?: string;\n}\n\n// ========================================================================\n// Sync Queue Types (Hybrid mode)\n// ========================================================================\n\nexport interface SyncQueueEntry {\n id: string;\n context: PDLSSContext;\n decision: VerificationDecision;\n timestamp: Date;\n retryCount: number;\n status: 'pending' | 'synced' | 'failed';\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/gateway/types.ts
|
|
2
|
+
var HIGH_RISK_COMMANDS = [
|
|
3
|
+
"rm",
|
|
4
|
+
"rmdir",
|
|
5
|
+
"dd",
|
|
6
|
+
"mkfs",
|
|
7
|
+
"chmod",
|
|
8
|
+
"chown",
|
|
9
|
+
"sudo",
|
|
10
|
+
"su",
|
|
11
|
+
"curl",
|
|
12
|
+
"wget",
|
|
13
|
+
"nc",
|
|
14
|
+
"netcat",
|
|
15
|
+
"ssh",
|
|
16
|
+
"scp",
|
|
17
|
+
"rsync",
|
|
18
|
+
"git push",
|
|
19
|
+
"npm publish",
|
|
20
|
+
"docker",
|
|
21
|
+
"kubectl"
|
|
22
|
+
];
|
|
23
|
+
var SENSITIVE_PATHS = [
|
|
24
|
+
".ssh",
|
|
25
|
+
".aws",
|
|
26
|
+
".gnupg",
|
|
27
|
+
".env",
|
|
28
|
+
"credentials",
|
|
29
|
+
"secrets",
|
|
30
|
+
"password",
|
|
31
|
+
".git/config",
|
|
32
|
+
"/etc",
|
|
33
|
+
"/var",
|
|
34
|
+
"/root",
|
|
35
|
+
"id_rsa",
|
|
36
|
+
".npmrc",
|
|
37
|
+
".pypirc"
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// src/local-evaluator/evaluator.ts
|
|
41
|
+
var LocalEvaluator = class {
|
|
42
|
+
constructor(policy) {
|
|
43
|
+
this.requestCounts = /* @__PURE__ */ new Map();
|
|
44
|
+
this.policy = policy;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Update the policy (e.g. after hot-reload or sync).
|
|
48
|
+
*/
|
|
49
|
+
updatePolicy(policy) {
|
|
50
|
+
this.policy = policy;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Evaluate an action context against the loaded policy.
|
|
54
|
+
*/
|
|
55
|
+
evaluate(context) {
|
|
56
|
+
const purposeRule = this.policy.purposes.find((p) => p.id === context.purpose);
|
|
57
|
+
if (!purposeRule) {
|
|
58
|
+
return { recommendation: "DENY", reason: "Purpose not in policy" };
|
|
59
|
+
}
|
|
60
|
+
if (!purposeRule.allowed) {
|
|
61
|
+
return { recommendation: "DENY", reason: "Purpose explicitly blocked" };
|
|
62
|
+
}
|
|
63
|
+
if (purposeRule.targets && !this.matchesAnyPattern(context.target, purposeRule.targets)) {
|
|
64
|
+
return { recommendation: "DENY", reason: "Target not in allowed list" };
|
|
65
|
+
}
|
|
66
|
+
if (purposeRule.blockedPatterns && this.matchesAnyPattern(context.target, purposeRule.blockedPatterns)) {
|
|
67
|
+
return { recommendation: "DENY", reason: "Target matches blocked pattern" };
|
|
68
|
+
}
|
|
69
|
+
const scopeBlock = this.checkScopeBlock(context);
|
|
70
|
+
if (scopeBlock) {
|
|
71
|
+
return { recommendation: "DENY", reason: scopeBlock };
|
|
72
|
+
}
|
|
73
|
+
const limitViolation = this.checkLimits(context);
|
|
74
|
+
if (limitViolation) {
|
|
75
|
+
return { recommendation: "DENY", reason: limitViolation };
|
|
76
|
+
}
|
|
77
|
+
if (context.purpose === "sub_agent.spawn" && this.policy.selfInstantiation) {
|
|
78
|
+
if (!this.policy.selfInstantiation.allowed) {
|
|
79
|
+
return { recommendation: "DENY", reason: "Sub-agent spawning is not allowed" };
|
|
80
|
+
}
|
|
81
|
+
const depth = context.metadata?.subAgentDepth || 0;
|
|
82
|
+
if (this.policy.selfInstantiation.maxDepth !== void 0 && depth >= this.policy.selfInstantiation.maxDepth) {
|
|
83
|
+
return { recommendation: "DENY", reason: `Sub-agent depth ${depth} exceeds max depth ${this.policy.selfInstantiation.maxDepth}` };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (purposeRule.requiresApproval) {
|
|
87
|
+
return { recommendation: "MANUAL_REVIEW", reason: "Purpose requires approval" };
|
|
88
|
+
}
|
|
89
|
+
const riskDecision = this.checkRiskThresholds(context);
|
|
90
|
+
if (riskDecision) {
|
|
91
|
+
return riskDecision;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
recommendation: "ALLOW",
|
|
95
|
+
reason: "All PDLSS checks passed",
|
|
96
|
+
evaluatedDimensions: {
|
|
97
|
+
purpose: true,
|
|
98
|
+
scope: !!this.policy.scope,
|
|
99
|
+
limits: !!this.policy.limits,
|
|
100
|
+
riskThresholds: !!this.policy.riskThresholds
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
checkScopeBlock(context) {
|
|
105
|
+
const scope = this.policy.scope;
|
|
106
|
+
if (!scope) return null;
|
|
107
|
+
if (scope.blockedDomains) {
|
|
108
|
+
const targetDomain = this.extractDomain(context.target);
|
|
109
|
+
if (this.matchesAnyPattern(targetDomain, scope.blockedDomains)) {
|
|
110
|
+
return `Target blocked by scope: ${context.target}`;
|
|
111
|
+
}
|
|
112
|
+
if (context.networkAccess) {
|
|
113
|
+
for (const domain of context.networkAccess) {
|
|
114
|
+
if (this.matchesAnyPattern(this.extractDomain(domain), scope.blockedDomains)) {
|
|
115
|
+
return `Domain blocked by scope: ${domain}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (scope.blockedResources && this.matchesAnyPattern(context.target, scope.blockedResources)) {
|
|
121
|
+
return `Resource blocked by scope: ${context.target}`;
|
|
122
|
+
}
|
|
123
|
+
if (scope.allowedDomains && context.networkAccess) {
|
|
124
|
+
for (const domain of context.networkAccess) {
|
|
125
|
+
if (!this.matchesAnyPattern(this.extractDomain(domain), scope.allowedDomains)) {
|
|
126
|
+
return `Domain not in allowed list: ${domain}`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
checkLimits(context) {
|
|
133
|
+
const limits = this.policy.limits;
|
|
134
|
+
if (!limits) return null;
|
|
135
|
+
if (limits.maxTransactionAmount !== void 0 && context.transactionValue !== void 0) {
|
|
136
|
+
if (context.transactionValue > limits.maxTransactionAmount) {
|
|
137
|
+
return `Transaction value ${context.transactionValue} exceeds limit ${limits.maxTransactionAmount}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (limits.maxRequestsPerHour !== void 0) {
|
|
141
|
+
const key = context.purpose;
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const entry = this.requestCounts.get(key);
|
|
144
|
+
const hourMs = 36e5;
|
|
145
|
+
if (!entry || now - entry.windowStart > hourMs) {
|
|
146
|
+
this.requestCounts.set(key, { count: 1, windowStart: now });
|
|
147
|
+
} else {
|
|
148
|
+
entry.count++;
|
|
149
|
+
if (entry.count > limits.maxRequestsPerHour) {
|
|
150
|
+
return `Rate limit exceeded: ${entry.count}/${limits.maxRequestsPerHour} requests per hour`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
checkRiskThresholds(context) {
|
|
157
|
+
if (!this.policy.riskThresholds) return null;
|
|
158
|
+
const riskScore = this.calculateRiskScore(context);
|
|
159
|
+
const thresholds = this.policy.riskThresholds;
|
|
160
|
+
if (riskScore >= thresholds.autoBlock.min) {
|
|
161
|
+
return { recommendation: "DENY", reason: `Risk score ${riskScore} exceeds block threshold` };
|
|
162
|
+
}
|
|
163
|
+
if (riskScore >= thresholds.requireApproval.min) {
|
|
164
|
+
return { recommendation: "MANUAL_REVIEW", reason: `Risk score ${riskScore} requires approval` };
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
calculateRiskScore(context) {
|
|
169
|
+
let score = 0;
|
|
170
|
+
if (context.riskFactors?.length) {
|
|
171
|
+
const severityScores = {
|
|
172
|
+
low: 10,
|
|
173
|
+
medium: 40,
|
|
174
|
+
high: 70,
|
|
175
|
+
critical: 90
|
|
176
|
+
};
|
|
177
|
+
for (const factor of context.riskFactors) {
|
|
178
|
+
const factorScore = severityScores[factor.severity] || 0;
|
|
179
|
+
if (factorScore > score) score = factorScore;
|
|
180
|
+
}
|
|
181
|
+
return score;
|
|
182
|
+
}
|
|
183
|
+
if (context.purpose === "shell.exec" && context.target) {
|
|
184
|
+
const targetLower = context.target.toLowerCase();
|
|
185
|
+
for (const cmd of HIGH_RISK_COMMANDS) {
|
|
186
|
+
if (targetLower.startsWith(cmd) || targetLower.includes(` ${cmd} `) || targetLower.includes(` ${cmd}`)) {
|
|
187
|
+
score = Math.max(score, 80);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
193
|
+
const targetLower = context.target.toLowerCase();
|
|
194
|
+
for (const sensitivePath of SENSITIVE_PATHS) {
|
|
195
|
+
if (targetLower.includes(sensitivePath.toLowerCase())) {
|
|
196
|
+
score = Math.max(score, 50);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return score;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Extract domain from a URL-like string (strips protocol and path).
|
|
205
|
+
*/
|
|
206
|
+
extractDomain(value) {
|
|
207
|
+
let domain = value;
|
|
208
|
+
const protoIndex = domain.indexOf("://");
|
|
209
|
+
if (protoIndex !== -1) domain = domain.slice(protoIndex + 3);
|
|
210
|
+
const slashIndex = domain.indexOf("/");
|
|
211
|
+
if (slashIndex !== -1) domain = domain.slice(0, slashIndex);
|
|
212
|
+
const colonIndex = domain.indexOf(":");
|
|
213
|
+
if (colonIndex !== -1) domain = domain.slice(0, colonIndex);
|
|
214
|
+
return domain;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Check if a value matches any of the given glob patterns.
|
|
218
|
+
*/
|
|
219
|
+
matchesAnyPattern(value, patterns) {
|
|
220
|
+
return patterns.some((pattern) => this.matchGlob(value, pattern));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Simple glob pattern matching.
|
|
224
|
+
* Supports * (matches any characters including / and spaces) and ? (single char).
|
|
225
|
+
* Uses the same approach as the backend's matchGlobPattern.
|
|
226
|
+
*/
|
|
227
|
+
matchGlob(value, pattern) {
|
|
228
|
+
if (pattern === value) return true;
|
|
229
|
+
const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
230
|
+
try {
|
|
231
|
+
return new RegExp(`^${regexStr}$`, "i").test(value);
|
|
232
|
+
} catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
function findPurposeRule(policy, purposeId) {
|
|
238
|
+
return policy.purposes.find((p) => p.id === purposeId);
|
|
239
|
+
}
|
|
240
|
+
export {
|
|
241
|
+
LocalEvaluator,
|
|
242
|
+
findPurposeRule
|
|
243
|
+
};
|
|
244
|
+
//# sourceMappingURL=evaluator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/gateway/types.ts","../../src/local-evaluator/evaluator.ts"],"sourcesContent":["/**\n * AstraSync Gateway - Types for gateway modes, local evaluation, and adapter interface.\n */\n\n// ========================================================================\n// Gateway Configuration\n// ========================================================================\n\nexport type GatewayMode = 'online' | 'local' | 'hybrid';\n\n/**\n * Posture controls whether the gateway actively blocks or just monitors.\n * - active: Evaluate and enforce decisions (block/allow/review)\n * - passive: Evaluate and log but never block (telemetry-only mode)\n */\nexport type GatewayPosture = 'active' | 'passive';\n\nexport interface AstraSyncGatewayConfig {\n mode: GatewayMode;\n /** Enforcement posture: 'active' blocks actions, 'passive' logs only (default: 'active') */\n posture?: GatewayPosture;\n /** AstraSync API base URL (required for online/hybrid modes) */\n apiBaseUrl?: string;\n /** API key for authenticating with AstraSync (required for online/hybrid modes) */\n apiKey?: string;\n /** Path to local PDLSS policy YAML file (required for local/hybrid modes) */\n policyFile?: string;\n /** Inline policy object (alternative to policyFile) */\n policy?: LocalPolicy;\n /** Sync interval in seconds for hybrid mode (default: 3600) */\n syncInterval?: number;\n /** Cache verification results TTL in seconds (default: 300) */\n cacheTtl?: number;\n /** Enable debug logging */\n debug?: boolean;\n /** Enable trace logging to .astrasync/traces/ (default: false) */\n traceEnabled?: boolean;\n /** Trace log directory (default: .astrasync/traces/) */\n tracePath?: string;\n /** Default access level for unverified requests */\n defaultAccessLevel?: import('../types').AccessLevel;\n /** Minimum trust score for standard access (online/hybrid) */\n minTrustScore?: number;\n /** Minimum trust score for full access (online/hybrid) */\n minTrustScoreForFull?: number;\n /** Custom headers to send with API requests */\n customHeaders?: Record<string, string>;\n /** Counterparty URL for analytics */\n counterpartyUrl?: string;\n /** Counterparty type for analytics */\n counterpartyType?: import('../types').CounterpartyType;\n}\n\n// ========================================================================\n// PDLSS Context (Agent-side action context)\n// ========================================================================\n\nexport interface PDLSSContext {\n /** Purpose category (e.g. email.send, shell.exec, file.read) */\n purpose: string;\n /** Specific action within purpose */\n action: string;\n /** Target resource, recipient, or counterparty */\n target: string;\n /** Types of data access (read, write, delete) */\n dataAccess?: string[];\n /** Network domains/IPs being accessed */\n networkAccess?: string[];\n /** Resource type (customer, order, file, directory, process) */\n resourceType?: string;\n /** Risk factors for this action */\n riskFactors?: RiskFactor[];\n /** Transaction value (if financial) */\n transactionValue?: number;\n /** Currency for transaction */\n currency?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\nexport interface RiskFactor {\n type: 'financial' | 'data_sensitivity' | 'privilege_escalation' | 'network_scope' | 'destructive';\n severity: 'low' | 'medium' | 'high' | 'critical';\n detail: string;\n}\n\n// ========================================================================\n// Verification Decision\n// ========================================================================\n\nexport interface VerificationDecision {\n recommendation: 'ALLOW' | 'DENY' | 'MANUAL_REVIEW';\n reason: string;\n trustScore?: number;\n tokenGuidance?: import('../types').TokenGuidance;\n sessionId?: string;\n /** PDLSS dimensions that were evaluated */\n evaluatedDimensions?: {\n purpose: boolean;\n scope: boolean;\n limits: boolean;\n riskThresholds: boolean;\n };\n}\n\n// ========================================================================\n// Local Policy Types (YAML format)\n// ========================================================================\n\nexport interface LocalPolicy {\n version: string;\n name: string;\n description?: string;\n purposes: LocalPurposeRule[];\n scope?: LocalScope;\n limits?: LocalLimits;\n riskThresholds?: LocalRiskThresholds;\n selfInstantiation?: LocalSelfInstantiation;\n}\n\nexport interface LocalPurposeRule {\n id: string;\n allowed: boolean;\n targets?: string[];\n blockedPatterns?: string[];\n requiresApproval?: boolean;\n}\n\nexport interface LocalScope {\n allowedDomains?: string[];\n blockedDomains?: string[];\n blockedResources?: string[];\n}\n\nexport interface LocalLimits {\n maxTransactionAmount?: number;\n maxRequestsPerHour?: number;\n currency?: string;\n}\n\nexport interface LocalRiskThresholds {\n autoAllow: { min: number; max: number };\n requireApproval: { min: number; max: number };\n autoBlock: { min: number; max: number };\n}\n\nexport interface LocalSelfInstantiation {\n /** Whether sub-agent spawning is allowed */\n allowed: boolean;\n /** Maximum depth of sub-agent chain */\n maxDepth?: number;\n}\n\n// ========================================================================\n// Risk Scoring Defaults (cherry-picked from trust-harness-core)\n// ========================================================================\n\n/** Base risk scores per action category */\nexport const BASE_RISK_SCORES: Record<string, number> = {\n 'file.read': 10,\n 'file.write': 40,\n 'file.delete': 70,\n 'shell.exec': 50,\n 'network.fetch': 60,\n 'network.request': 60,\n 'email.send': 45,\n 'email.read': 15,\n 'calendar.create': 20,\n 'calendar.modify': 30,\n 'database.query': 25,\n 'database.write': 55,\n 'payment.execute': 80,\n 'sub_agent.spawn': 65,\n 'code.execute': 45,\n};\n\n/** Shell commands that significantly increase risk score */\nexport const HIGH_RISK_COMMANDS = [\n 'rm', 'rmdir', 'dd', 'mkfs', 'chmod', 'chown',\n 'sudo', 'su', 'curl', 'wget', 'nc', 'netcat',\n 'ssh', 'scp', 'rsync', 'git push', 'npm publish',\n 'docker', 'kubectl',\n];\n\n/** File paths that indicate sensitive data access */\nexport const SENSITIVE_PATHS = [\n '.ssh', '.aws', '.gnupg', '.env', 'credentials',\n 'secrets', 'password', '.git/config', '/etc', '/var', '/root',\n 'id_rsa', '.npmrc', '.pypirc',\n];\n\n// ========================================================================\n// Trace Event Types\n// ========================================================================\n\nexport interface TraceEvent {\n id: string;\n timestamp: Date;\n type: 'evaluation' | 'decision' | 'error' | 'mode_switch';\n context?: PDLSSContext;\n decision?: VerificationDecision;\n metadata?: Record<string, unknown>;\n}\n\n// ========================================================================\n// Adapter Interface Types\n// ========================================================================\n\nexport interface AdapterConfig {\n /** The gateway instance (handles mode routing) */\n gateway: unknown; // Typed as AstraSyncGateway at usage site to avoid circular deps\n /** Platform-specific configuration */\n adapterOptions: Record<string, unknown>;\n}\n\nexport interface AgentAction {\n /** Raw action data from the platform */\n raw: unknown;\n /** Platform identifier (e.g. 'openclaw-cli', 'cursor', 'browser') */\n platform: string;\n /** Timestamp of the action */\n timestamp: Date;\n}\n\nexport interface InterceptResult {\n /** Whether the action was intercepted */\n intercepted: boolean;\n /** Extracted PDLSS context (if intercepted) */\n context?: PDLSSContext;\n /** Reason for not intercepting (if not intercepted) */\n skipReason?: string;\n}\n\n// ========================================================================\n// Sync Queue Types (Hybrid mode)\n// ========================================================================\n\nexport interface SyncQueueEntry {\n id: string;\n context: PDLSSContext;\n decision: VerificationDecision;\n timestamp: Date;\n retryCount: number;\n status: 'pending' | 'synced' | 'failed';\n}\n","/**\n * Local PDLSS Evaluator\n *\n * Evaluates agent actions against a local PDLSS policy.\n * Same logic as the cloud evaluator but runs in-process with no I/O.\n *\n * Evaluation order:\n * 1. Purpose: find matching rule, check if allowed\n * 2. Purpose: check allowed targets (if specified)\n * 3. Purpose: check blocked patterns (if specified)\n * 4. Scope: check global blocked resources/domains\n * 5. Limits: check transaction/rate limits\n * 6. Risk thresholds + approval requirements\n */\n\nimport type { LocalPolicy, PDLSSContext, VerificationDecision, LocalPurposeRule } from '../gateway/types';\nimport { BASE_RISK_SCORES, HIGH_RISK_COMMANDS, SENSITIVE_PATHS } from '../gateway/types';\n\nexport class LocalEvaluator {\n private policy: LocalPolicy;\n private requestCounts: Map<string, { count: number; windowStart: number }> = new Map();\n\n constructor(policy: LocalPolicy) {\n this.policy = policy;\n }\n\n /**\n * Update the policy (e.g. after hot-reload or sync).\n */\n updatePolicy(policy: LocalPolicy): void {\n this.policy = policy;\n }\n\n /**\n * Evaluate an action context against the loaded policy.\n */\n evaluate(context: PDLSSContext): VerificationDecision {\n // 1. Purpose: find matching rule\n const purposeRule = this.policy.purposes.find((p) => p.id === context.purpose);\n if (!purposeRule) {\n return { recommendation: 'DENY', reason: 'Purpose not in policy' };\n }\n if (!purposeRule.allowed) {\n return { recommendation: 'DENY', reason: 'Purpose explicitly blocked' };\n }\n\n // 2. Purpose: check allowed targets\n if (purposeRule.targets && !this.matchesAnyPattern(context.target, purposeRule.targets)) {\n return { recommendation: 'DENY', reason: 'Target not in allowed list' };\n }\n\n // 3. Purpose: check blocked patterns\n if (purposeRule.blockedPatterns && this.matchesAnyPattern(context.target, purposeRule.blockedPatterns)) {\n return { recommendation: 'DENY', reason: 'Target matches blocked pattern' };\n }\n\n // 4. Scope: check global blocked resources/domains\n const scopeBlock = this.checkScopeBlock(context);\n if (scopeBlock) {\n return { recommendation: 'DENY', reason: scopeBlock };\n }\n\n // 5. Limits: check transaction and rate limits\n const limitViolation = this.checkLimits(context);\n if (limitViolation) {\n return { recommendation: 'DENY', reason: limitViolation };\n }\n\n // 6. Self-instantiation: check sub-agent spawning rules\n if (context.purpose === 'sub_agent.spawn' && this.policy.selfInstantiation) {\n if (!this.policy.selfInstantiation.allowed) {\n return { recommendation: 'DENY', reason: 'Sub-agent spawning is not allowed' };\n }\n const depth = (context.metadata?.subAgentDepth as number) || 0;\n if (this.policy.selfInstantiation.maxDepth !== undefined && depth >= this.policy.selfInstantiation.maxDepth) {\n return { recommendation: 'DENY', reason: `Sub-agent depth ${depth} exceeds max depth ${this.policy.selfInstantiation.maxDepth}` };\n }\n }\n\n // 7. Risk thresholds + approval requirements\n if (purposeRule.requiresApproval) {\n return { recommendation: 'MANUAL_REVIEW', reason: 'Purpose requires approval' };\n }\n\n const riskDecision = this.checkRiskThresholds(context);\n if (riskDecision) {\n return riskDecision;\n }\n\n return {\n recommendation: 'ALLOW',\n reason: 'All PDLSS checks passed',\n evaluatedDimensions: {\n purpose: true,\n scope: !!this.policy.scope,\n limits: !!this.policy.limits,\n riskThresholds: !!this.policy.riskThresholds,\n },\n };\n }\n\n private checkScopeBlock(context: PDLSSContext): string | null {\n const scope = this.policy.scope;\n if (!scope) return null;\n\n // Check blocked domains against target and network access\n if (scope.blockedDomains) {\n const targetDomain = this.extractDomain(context.target);\n if (this.matchesAnyPattern(targetDomain, scope.blockedDomains)) {\n return `Target blocked by scope: ${context.target}`;\n }\n if (context.networkAccess) {\n for (const domain of context.networkAccess) {\n if (this.matchesAnyPattern(this.extractDomain(domain), scope.blockedDomains)) {\n return `Domain blocked by scope: ${domain}`;\n }\n }\n }\n }\n\n // Check blocked resources against target\n if (scope.blockedResources && this.matchesAnyPattern(context.target, scope.blockedResources)) {\n return `Resource blocked by scope: ${context.target}`;\n }\n\n // Check allowed domains (if specified, target must match)\n if (scope.allowedDomains && context.networkAccess) {\n for (const domain of context.networkAccess) {\n if (!this.matchesAnyPattern(this.extractDomain(domain), scope.allowedDomains)) {\n return `Domain not in allowed list: ${domain}`;\n }\n }\n }\n\n return null;\n }\n\n private checkLimits(context: PDLSSContext): string | null {\n const limits = this.policy.limits;\n if (!limits) return null;\n\n // Transaction amount check\n if (limits.maxTransactionAmount !== undefined && context.transactionValue !== undefined) {\n if (context.transactionValue > limits.maxTransactionAmount) {\n return `Transaction value ${context.transactionValue} exceeds limit ${limits.maxTransactionAmount}`;\n }\n }\n\n // Rate limit check\n if (limits.maxRequestsPerHour !== undefined) {\n const key = context.purpose;\n const now = Date.now();\n const entry = this.requestCounts.get(key);\n const hourMs = 3600000;\n\n if (!entry || now - entry.windowStart > hourMs) {\n this.requestCounts.set(key, { count: 1, windowStart: now });\n } else {\n entry.count++;\n if (entry.count > limits.maxRequestsPerHour) {\n return `Rate limit exceeded: ${entry.count}/${limits.maxRequestsPerHour} requests per hour`;\n }\n }\n }\n\n return null;\n }\n\n private checkRiskThresholds(context: PDLSSContext): VerificationDecision | null {\n if (!this.policy.riskThresholds) return null;\n\n const riskScore = this.calculateRiskScore(context);\n const thresholds = this.policy.riskThresholds;\n\n if (riskScore >= thresholds.autoBlock.min) {\n return { recommendation: 'DENY', reason: `Risk score ${riskScore} exceeds block threshold` };\n }\n\n if (riskScore >= thresholds.requireApproval.min) {\n return { recommendation: 'MANUAL_REVIEW', reason: `Risk score ${riskScore} requires approval` };\n }\n\n return null;\n }\n\n private calculateRiskScore(context: PDLSSContext): number {\n let score = 0;\n\n // Explicit risk factors take priority (highest severity wins)\n if (context.riskFactors?.length) {\n const severityScores: Record<string, number> = {\n low: 10,\n medium: 40,\n high: 70,\n critical: 90,\n };\n\n for (const factor of context.riskFactors) {\n const factorScore = severityScores[factor.severity] || 0;\n if (factorScore > score) score = factorScore;\n }\n return score;\n }\n\n // Auto-detect risk from high-risk shell commands\n if (context.purpose === 'shell.exec' && context.target) {\n const targetLower = context.target.toLowerCase();\n for (const cmd of HIGH_RISK_COMMANDS) {\n if (targetLower.startsWith(cmd) || targetLower.includes(` ${cmd} `) || targetLower.includes(` ${cmd}`)) {\n score = Math.max(score, 80);\n break;\n }\n }\n }\n\n // Auto-detect risk from sensitive file paths (score 50 = review range)\n if ((context.purpose === 'file.read' || context.purpose === 'file.write' || context.purpose === 'file.delete') && context.target) {\n const targetLower = context.target.toLowerCase();\n for (const sensitivePath of SENSITIVE_PATHS) {\n if (targetLower.includes(sensitivePath.toLowerCase())) {\n score = Math.max(score, 50);\n break;\n }\n }\n }\n\n return score;\n }\n\n /**\n * Extract domain from a URL-like string (strips protocol and path).\n */\n private extractDomain(value: string): string {\n let domain = value;\n // Strip protocol\n const protoIndex = domain.indexOf('://');\n if (protoIndex !== -1) domain = domain.slice(protoIndex + 3);\n // Strip path\n const slashIndex = domain.indexOf('/');\n if (slashIndex !== -1) domain = domain.slice(0, slashIndex);\n // Strip port\n const colonIndex = domain.indexOf(':');\n if (colonIndex !== -1) domain = domain.slice(0, colonIndex);\n return domain;\n }\n\n /**\n * Check if a value matches any of the given glob patterns.\n */\n private matchesAnyPattern(value: string, patterns: string[]): boolean {\n return patterns.some((pattern) => this.matchGlob(value, pattern));\n }\n\n /**\n * Simple glob pattern matching.\n * Supports * (matches any characters including / and spaces) and ? (single char).\n * Uses the same approach as the backend's matchGlobPattern.\n */\n private matchGlob(value: string, pattern: string): boolean {\n // Exact match\n if (pattern === value) return true;\n\n // Convert glob to regex\n const regexStr = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special regex chars (except * and ?)\n .replace(/\\*/g, '.*') // * matches anything\n .replace(/\\?/g, '.'); // ? matches single char\n\n try {\n return new RegExp(`^${regexStr}$`, 'i').test(value);\n } catch {\n return false;\n }\n }\n}\n\n/**\n * Convenience: find a matching purpose rule.\n */\nexport function findPurposeRule(policy: LocalPolicy, purposeId: string): LocalPurposeRule | undefined {\n return policy.purposes.find((p) => p.id === purposeId);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as next_server from 'next/server';
|
|
2
2
|
import { NextRequest } from 'next/server';
|
|
3
|
-
import { N as NextJsMiddlewareOptions } from './types-
|
|
3
|
+
import { N as NextJsMiddlewareOptions } from './types-Bf8pML07.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Create Next.js middleware for agent verification
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as next_server from 'next/server';
|
|
2
2
|
import { NextRequest } from 'next/server';
|
|
3
|
-
import { N as NextJsMiddlewareOptions } from './types-
|
|
3
|
+
import { N as NextJsMiddlewareOptions } from './types-Bf8pML07.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Create Next.js middleware for agent verification
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AccessLevel, i as TrustLevel, S as SDKOptions, b as VerificationResult } from './types-
|
|
1
|
+
import { a as AccessLevel, i as TrustLevel, S as SDKOptions, b as VerificationResult } from './types-Bf8pML07.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* AstraSync Universal Verification Gateway - Access Level Definitions
|
|
@@ -109,6 +109,8 @@ declare class VerificationGatewayClient {
|
|
|
109
109
|
isSubAgentRequest?: boolean;
|
|
110
110
|
parentAgentId?: string;
|
|
111
111
|
subAgentDepth?: number;
|
|
112
|
+
counterpartyUrl?: string;
|
|
113
|
+
counterpartyType?: string;
|
|
112
114
|
}): Promise<VerificationResult>;
|
|
113
115
|
/**
|
|
114
116
|
* Quick verification - just check if credentials are valid
|