trusera-sdk 1.1.0 → 1.1.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/index.cjs +79 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +79 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -47,6 +47,7 @@ var TruseraClient = class {
|
|
|
47
47
|
apiKey;
|
|
48
48
|
baseUrl;
|
|
49
49
|
batchSize;
|
|
50
|
+
maxQueueSize;
|
|
50
51
|
flushInterval;
|
|
51
52
|
debug;
|
|
52
53
|
agentId;
|
|
@@ -66,6 +67,7 @@ var TruseraClient = class {
|
|
|
66
67
|
this.baseUrl = options.baseUrl ?? "https://api.trusera.io";
|
|
67
68
|
this.agentId = options.agentId;
|
|
68
69
|
this.batchSize = options.batchSize ?? 100;
|
|
70
|
+
this.maxQueueSize = options.maxQueueSize ?? 1e4;
|
|
69
71
|
this.flushInterval = options.flushInterval ?? 5e3;
|
|
70
72
|
this.debug = options.debug ?? false;
|
|
71
73
|
const envAuto = (typeof process !== "undefined" ? process.env?.TRUSERA_AUTO_REGISTER : void 0) ?? "";
|
|
@@ -111,7 +113,7 @@ var TruseraClient = class {
|
|
|
111
113
|
name,
|
|
112
114
|
framework,
|
|
113
115
|
metadata: {
|
|
114
|
-
sdk_version:
|
|
116
|
+
sdk_version: SDK_VERSION,
|
|
115
117
|
runtime: "node",
|
|
116
118
|
node_version: process.version
|
|
117
119
|
}
|
|
@@ -141,10 +143,14 @@ var TruseraClient = class {
|
|
|
141
143
|
metadata: {
|
|
142
144
|
...event.metadata,
|
|
143
145
|
agent_id: this.agentId,
|
|
144
|
-
sdk_version:
|
|
146
|
+
sdk_version: SDK_VERSION
|
|
145
147
|
}
|
|
146
148
|
};
|
|
147
149
|
this.eventQueue.push(enrichedEvent);
|
|
150
|
+
while (this.eventQueue.length > this.maxQueueSize) {
|
|
151
|
+
this.eventQueue.shift();
|
|
152
|
+
this.log("Event queue overflow, dropping oldest event");
|
|
153
|
+
}
|
|
148
154
|
this.log("Event tracked", { type: event.type, name: event.name, queueSize: this.eventQueue.length });
|
|
149
155
|
if (this.eventQueue.length >= this.batchSize) {
|
|
150
156
|
void this.flush();
|
|
@@ -214,7 +220,7 @@ var TruseraClient = class {
|
|
|
214
220
|
if (typeof process === "undefined") return {};
|
|
215
221
|
return {
|
|
216
222
|
pid: process.pid,
|
|
217
|
-
argv: process.argv?.slice(0,
|
|
223
|
+
argv: process.argv?.slice(0, 2),
|
|
218
224
|
node_version: process.version,
|
|
219
225
|
platform: process.platform,
|
|
220
226
|
arch: process.arch
|
|
@@ -362,13 +368,47 @@ function tryRequire(moduleName) {
|
|
|
362
368
|
return null;
|
|
363
369
|
}
|
|
364
370
|
}
|
|
371
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
372
|
+
"authorization",
|
|
373
|
+
"cookie",
|
|
374
|
+
"set-cookie",
|
|
375
|
+
"x-api-key",
|
|
376
|
+
"x-n8n-api-key",
|
|
377
|
+
"proxy-authorization"
|
|
378
|
+
]);
|
|
379
|
+
function redactHeaders(headers) {
|
|
380
|
+
const redacted = {};
|
|
381
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
382
|
+
redacted[key] = SENSITIVE_HEADERS.has(key.toLowerCase()) ? "[REDACTED]" : value;
|
|
383
|
+
}
|
|
384
|
+
return redacted;
|
|
385
|
+
}
|
|
386
|
+
var MAX_PATTERN_LENGTH = 500;
|
|
387
|
+
var EVIL_REGEX_PATTERNS = /(\.\*){2,}|(\w\+){2,}|\(\[^[^\]]*\]\*\)\*|\(\.\+\)\+/;
|
|
388
|
+
function safeRegExp(pattern) {
|
|
389
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
390
|
+
console.warn(`[Trusera] Exclude pattern too long (${pattern.length} > ${MAX_PATTERN_LENGTH}), skipping`);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
if (EVIL_REGEX_PATTERNS.test(pattern)) {
|
|
394
|
+
console.warn(`[Trusera] Potentially dangerous regex pattern detected, skipping: ${pattern.slice(0, 50)}`);
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
try {
|
|
398
|
+
return new RegExp(pattern);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
console.warn(`[Trusera] Invalid regex pattern, skipping: ${pattern.slice(0, 50)}`);
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
365
404
|
var TruseraInterceptor = class {
|
|
366
405
|
client = null;
|
|
367
406
|
options = {
|
|
368
407
|
enforcement: "log",
|
|
369
408
|
policyUrl: "",
|
|
370
409
|
excludePatterns: [],
|
|
371
|
-
debug: false
|
|
410
|
+
debug: false,
|
|
411
|
+
failClosed: false
|
|
372
412
|
};
|
|
373
413
|
excludeRegexes = [];
|
|
374
414
|
isInstalled = false;
|
|
@@ -405,9 +445,10 @@ var TruseraInterceptor = class {
|
|
|
405
445
|
enforcement: options.enforcement ?? "log",
|
|
406
446
|
policyUrl: options.policyUrl ?? "",
|
|
407
447
|
excludePatterns: options.excludePatterns ?? [],
|
|
408
|
-
debug: options.debug ?? false
|
|
448
|
+
debug: options.debug ?? false,
|
|
449
|
+
failClosed: options.failClosed ?? false
|
|
409
450
|
};
|
|
410
|
-
this.excludeRegexes = this.options.excludePatterns.map((pattern) =>
|
|
451
|
+
this.excludeRegexes = this.options.excludePatterns.map((pattern) => safeRegExp(pattern)).filter((r) => r !== null);
|
|
411
452
|
if (originalFetch === null) {
|
|
412
453
|
originalFetch = globalThis.fetch;
|
|
413
454
|
}
|
|
@@ -454,7 +495,7 @@ var TruseraInterceptor = class {
|
|
|
454
495
|
const event = createEvent(
|
|
455
496
|
"api_call" /* API_CALL */,
|
|
456
497
|
eventName,
|
|
457
|
-
{ method, url, headers },
|
|
498
|
+
{ method, url, headers: redactHeaders(headers) },
|
|
458
499
|
{ interception_mode: this.options.enforcement }
|
|
459
500
|
);
|
|
460
501
|
if (this.options.policyUrl) {
|
|
@@ -729,7 +770,7 @@ var TruseraInterceptor = class {
|
|
|
729
770
|
response.headers.forEach((v, k) => {
|
|
730
771
|
h[k] = v;
|
|
731
772
|
});
|
|
732
|
-
return h;
|
|
773
|
+
return redactHeaders(h);
|
|
733
774
|
})()
|
|
734
775
|
}
|
|
735
776
|
);
|
|
@@ -774,7 +815,7 @@ var TruseraInterceptor = class {
|
|
|
774
815
|
body = "[Binary data]";
|
|
775
816
|
}
|
|
776
817
|
}
|
|
777
|
-
return { headers, body };
|
|
818
|
+
return { headers: redactHeaders(headers), body };
|
|
778
819
|
}
|
|
779
820
|
/**
|
|
780
821
|
* Evaluates request against Cedar policies.
|
|
@@ -801,11 +842,21 @@ var TruseraInterceptor = class {
|
|
|
801
842
|
});
|
|
802
843
|
if (!response.ok) {
|
|
803
844
|
console.error(`[Trusera] Policy evaluation failed: ${response.status}`);
|
|
845
|
+
if (this.options.failClosed && this.options.enforcement === "block") {
|
|
846
|
+
console.warn("[Trusera] FAIL-CLOSED: Policy service returned error, denying request");
|
|
847
|
+
return { decision: "Deny", reasons: [`Policy service error: ${response.status}`] };
|
|
848
|
+
}
|
|
849
|
+
console.warn("[Trusera] FAIL-OPEN: Policy service returned error, allowing request");
|
|
804
850
|
return { decision: "Allow" };
|
|
805
851
|
}
|
|
806
852
|
return await response.json();
|
|
807
853
|
} catch (error) {
|
|
808
854
|
console.error("[Trusera] Policy evaluation error:", error);
|
|
855
|
+
if (this.options.failClosed && this.options.enforcement === "block") {
|
|
856
|
+
console.warn("[Trusera] FAIL-CLOSED: Policy evaluation failed, denying request");
|
|
857
|
+
return { decision: "Deny", reasons: ["Policy evaluation failed (fail-closed mode)"] };
|
|
858
|
+
}
|
|
859
|
+
console.warn("[Trusera] FAIL-OPEN: Policy evaluation failed, allowing request");
|
|
809
860
|
return { decision: "Allow" };
|
|
810
861
|
}
|
|
811
862
|
}
|
|
@@ -1010,6 +1061,24 @@ var CedarEvaluator = class {
|
|
|
1010
1061
|
};
|
|
1011
1062
|
|
|
1012
1063
|
// src/standalone.ts
|
|
1064
|
+
var MAX_PATTERN_LENGTH2 = 500;
|
|
1065
|
+
var EVIL_REGEX_PATTERNS2 = /(\.\*){2,}|(\w\+){2,}|\(\[^[^\]]*\]\*\)\*|\(\.\+\)\+/;
|
|
1066
|
+
function safeRegExp2(pattern) {
|
|
1067
|
+
if (pattern.length > MAX_PATTERN_LENGTH2) {
|
|
1068
|
+
console.warn(`[Trusera Standalone] Exclude pattern too long, skipping`);
|
|
1069
|
+
return null;
|
|
1070
|
+
}
|
|
1071
|
+
if (EVIL_REGEX_PATTERNS2.test(pattern)) {
|
|
1072
|
+
console.warn(`[Trusera Standalone] Potentially dangerous regex pattern, skipping`);
|
|
1073
|
+
return null;
|
|
1074
|
+
}
|
|
1075
|
+
try {
|
|
1076
|
+
return new RegExp(pattern);
|
|
1077
|
+
} catch {
|
|
1078
|
+
console.warn(`[Trusera Standalone] Invalid regex pattern, skipping`);
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1013
1082
|
var activeStandaloneInterceptor = null;
|
|
1014
1083
|
var originalFetch2 = null;
|
|
1015
1084
|
var StandaloneInterceptor = class {
|
|
@@ -1026,9 +1095,7 @@ var StandaloneInterceptor = class {
|
|
|
1026
1095
|
excludePatterns: options.excludePatterns ?? [],
|
|
1027
1096
|
debug: options.debug ?? false
|
|
1028
1097
|
};
|
|
1029
|
-
this.excludeRegexes = this.options.excludePatterns.map(
|
|
1030
|
-
(pattern) => new RegExp(pattern)
|
|
1031
|
-
);
|
|
1098
|
+
this.excludeRegexes = this.options.excludePatterns.map((pattern) => safeRegExp2(pattern)).filter((r) => r !== null);
|
|
1032
1099
|
}
|
|
1033
1100
|
/**
|
|
1034
1101
|
* Installs the standalone interceptor.
|