agent-inspect 6.31.3 → 6.31.5
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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/SECURITY.md +2 -0
- package/docs/GETTING-STARTED.md +2 -0
- package/docs/SAFE-TRACE-SHARING.md +1 -0
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-SJ6FRNOE.mjs → chunk-LKEPABND.mjs} +86 -80
- package/packages/cli/dist/chunk-LKEPABND.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +449 -91
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +357 -14
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-YNEJOPRA.mjs → src-4WYF5RFI.mjs} +3 -3
- package/packages/cli/dist/{src-YNEJOPRA.mjs.map → src-4WYF5RFI.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +97 -30
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.mjs +13 -13
- package/packages/core/dist/checks.cjs +51 -49
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.mjs +5 -5
- package/packages/core/dist/{chunk-PBNBQRXV.mjs → chunk-2KJR6X4V.mjs} +9 -9
- package/packages/core/dist/chunk-2KJR6X4V.mjs.map +1 -0
- package/packages/core/dist/{chunk-26YJSETN.mjs → chunk-7DHSWDKB.mjs} +19 -6
- package/packages/core/dist/chunk-7DHSWDKB.mjs.map +1 -0
- package/packages/core/dist/{chunk-BGF5JDJN.mjs → chunk-INE5IWLN.mjs} +3 -3
- package/packages/core/dist/{chunk-BGF5JDJN.mjs.map → chunk-INE5IWLN.mjs.map} +1 -1
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs → chunk-MTMTXTJL.mjs} +5 -5
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs.map → chunk-MTMTXTJL.mjs.map} +1 -1
- package/packages/core/dist/{chunk-KWBO36PH.mjs → chunk-NY4ILMXK.mjs} +4 -4
- package/packages/core/dist/{chunk-KWBO36PH.mjs.map → chunk-NY4ILMXK.mjs.map} +1 -1
- package/packages/core/dist/{chunk-WN5PT4GV.mjs → chunk-OSGNGRHF.mjs} +3 -3
- package/packages/core/dist/{chunk-WN5PT4GV.mjs.map → chunk-OSGNGRHF.mjs.map} +1 -1
- package/packages/core/dist/{chunk-CBPWLHNQ.mjs → chunk-PKDBFBJ7.mjs} +5 -76
- package/packages/core/dist/chunk-PKDBFBJ7.mjs.map +1 -0
- package/packages/core/dist/{chunk-VU6O5QAH.mjs → chunk-VLVCLAPB.mjs} +83 -5
- package/packages/core/dist/chunk-VLVCLAPB.mjs.map +1 -0
- package/packages/core/dist/exporters.cjs +242 -6
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +143 -9
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/index.cjs +97 -8
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.mjs +6 -6
- package/packages/core/dist/logs.cjs +80 -2
- package/packages/core/dist/logs.cjs.map +1 -1
- package/packages/core/dist/logs.d.cts +2 -0
- package/packages/core/dist/logs.d.ts +2 -0
- package/packages/core/dist/logs.mjs +2 -2
- package/packages/core/dist/readers.cjs +2 -0
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.mjs +2 -2
- package/packages/core/dist/writers.cjs +96 -8
- package/packages/core/dist/writers.cjs.map +1 -1
- package/packages/core/dist/writers.mjs +7 -8
- package/packages/core/dist/writers.mjs.map +1 -1
- package/packages/cli/dist/chunk-SJ6FRNOE.mjs.map +0 -1
- package/packages/core/dist/chunk-26YJSETN.mjs.map +0 -1
- package/packages/core/dist/chunk-CBPWLHNQ.mjs.map +0 -1
- package/packages/core/dist/chunk-PBNBQRXV.mjs.map +0 -1
- package/packages/core/dist/chunk-VU6O5QAH.mjs.map +0 -1
|
@@ -13,6 +13,7 @@ var readline = require('readline');
|
|
|
13
13
|
var url = require('url');
|
|
14
14
|
var module$1 = require('module');
|
|
15
15
|
var commander = require('commander');
|
|
16
|
+
var string_decoder = require('string_decoder');
|
|
16
17
|
var child_process = require('child_process');
|
|
17
18
|
var util = require('util');
|
|
18
19
|
var http = require('http');
|
|
@@ -66,6 +67,83 @@ var init_correlation_metadata = __esm({
|
|
|
66
67
|
];
|
|
67
68
|
}
|
|
68
69
|
});
|
|
70
|
+
|
|
71
|
+
// packages/core/src/safety/sensitive-key.ts
|
|
72
|
+
function keyHasExplicitSeparator(value) {
|
|
73
|
+
return /[_\-.]/.test(value);
|
|
74
|
+
}
|
|
75
|
+
function normalizeSensitiveKey(value) {
|
|
76
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
77
|
+
}
|
|
78
|
+
function isTokenCredentialKey(normalized) {
|
|
79
|
+
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
80
|
+
if (normalized === "token") return true;
|
|
81
|
+
if (normalized.endsWith("tokens")) return false;
|
|
82
|
+
return normalized.endsWith("token");
|
|
83
|
+
}
|
|
84
|
+
function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
|
|
85
|
+
if (!key) return false;
|
|
86
|
+
const normalized = normalizeSensitiveKey(key);
|
|
87
|
+
if (!normalized) return false;
|
|
88
|
+
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
89
|
+
const allowPrefixCompound = keyHasExplicitSeparator(key);
|
|
90
|
+
for (const sensitive of sensitiveKeys) {
|
|
91
|
+
const s = normalizeSensitiveKey(sensitive);
|
|
92
|
+
if (!s) continue;
|
|
93
|
+
if (s === "token") {
|
|
94
|
+
if (isTokenCredentialKey(normalized)) return true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (normalized === s) return true;
|
|
98
|
+
if (normalized.endsWith(`_${s}`)) return true;
|
|
99
|
+
if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
var NON_CREDENTIAL_TOKEN_CONFIG_KEYS, DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
|
|
104
|
+
var init_sensitive_key = __esm({
|
|
105
|
+
"packages/core/src/safety/sensitive-key.ts"() {
|
|
106
|
+
NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
|
|
107
|
+
[
|
|
108
|
+
"tokens",
|
|
109
|
+
"max_tokens",
|
|
110
|
+
"min_tokens",
|
|
111
|
+
"ls_max_tokens",
|
|
112
|
+
"token_count",
|
|
113
|
+
"token_limit",
|
|
114
|
+
"token_budget",
|
|
115
|
+
"input_tokens",
|
|
116
|
+
"output_tokens",
|
|
117
|
+
"total_tokens",
|
|
118
|
+
"cached_tokens",
|
|
119
|
+
"prompt_tokens",
|
|
120
|
+
"completion_tokens"
|
|
121
|
+
].map(normalizeSensitiveKey)
|
|
122
|
+
);
|
|
123
|
+
DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
|
|
124
|
+
"authorization",
|
|
125
|
+
"cookie",
|
|
126
|
+
"token",
|
|
127
|
+
"access_token",
|
|
128
|
+
"accesstoken",
|
|
129
|
+
"auth_token",
|
|
130
|
+
"authtoken",
|
|
131
|
+
"refresh_token",
|
|
132
|
+
"refreshtoken",
|
|
133
|
+
"id_token",
|
|
134
|
+
"idtoken",
|
|
135
|
+
"bearer_token",
|
|
136
|
+
"bearertoken",
|
|
137
|
+
"api_token",
|
|
138
|
+
"apitoken",
|
|
139
|
+
"apikey",
|
|
140
|
+
"api_key",
|
|
141
|
+
"password",
|
|
142
|
+
"secret",
|
|
143
|
+
"email"
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
});
|
|
69
147
|
function isRecord(v) {
|
|
70
148
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
71
149
|
}
|
|
@@ -80,7 +158,7 @@ function compileRules(rules, extraKeys) {
|
|
|
80
158
|
const out = /* @__PURE__ */ new Map();
|
|
81
159
|
const set = (r) => {
|
|
82
160
|
const k = toKey(r.key);
|
|
83
|
-
out.set(k, { ...r, key: k });
|
|
161
|
+
out.set(k, { ...r, key: k, raw: r.raw ?? r.key });
|
|
84
162
|
};
|
|
85
163
|
for (const k of DEFAULT_REDACT_KEYS) {
|
|
86
164
|
set({ key: k, strategy: "full" });
|
|
@@ -107,6 +185,7 @@ function compileRules(rules, extraKeys) {
|
|
|
107
185
|
var DEFAULT_REDACT_KEYS, Redactor;
|
|
108
186
|
var init_redactor = __esm({
|
|
109
187
|
"packages/core/src/logs/redactor.ts"() {
|
|
188
|
+
init_sensitive_key();
|
|
110
189
|
DEFAULT_REDACT_KEYS = [
|
|
111
190
|
"authorization",
|
|
112
191
|
"cookie",
|
|
@@ -121,9 +200,13 @@ var init_redactor = __esm({
|
|
|
121
200
|
constructor(options) {
|
|
122
201
|
this.#rules = compileRules(options?.rules, options?.extraKeys);
|
|
123
202
|
}
|
|
203
|
+
/** Field names this redactor treats as sensitive, for value-level helpers. */
|
|
204
|
+
get sensitiveKeys() {
|
|
205
|
+
return this.#rules.map((rule) => rule.raw ?? rule.key);
|
|
206
|
+
}
|
|
124
207
|
redactValue(key, value) {
|
|
125
208
|
const k = toKey(key);
|
|
126
|
-
const rule = this.#rules.find((r) => r.key === k);
|
|
209
|
+
const rule = this.#rules.find((r) => r.key === k) ?? this.#rules.find((r) => isCredentialSensitiveKey(key, [r.raw ?? r.key]));
|
|
127
210
|
if (!rule) {
|
|
128
211
|
return this.#redactNested(value);
|
|
129
212
|
}
|
|
@@ -178,7 +261,7 @@ function stringContainsHighConfidenceCredential(value) {
|
|
|
178
261
|
var KEY_VALUE_SECRET_PATTERN, HIGH_CONFIDENCE_CREDENTIAL_PATTERNS;
|
|
179
262
|
var init_credential_value_patterns = __esm({
|
|
180
263
|
"packages/core/src/safety/credential-value-patterns.ts"() {
|
|
181
|
-
KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i;
|
|
264
|
+
KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i;
|
|
182
265
|
HIGH_CONFIDENCE_CREDENTIAL_PATTERNS = [
|
|
183
266
|
/^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
|
|
184
267
|
/\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
|
|
@@ -829,6 +912,10 @@ var init_nanoid = __esm({
|
|
|
829
912
|
POOL_SIZE_MULTIPLIER = 128;
|
|
830
913
|
}
|
|
831
914
|
});
|
|
915
|
+
var init_trace_filesystem = __esm({
|
|
916
|
+
"packages/core/src/trace-filesystem.ts"() {
|
|
917
|
+
}
|
|
918
|
+
});
|
|
832
919
|
|
|
833
920
|
// packages/core/src/utils/duration.ts
|
|
834
921
|
function parseDuration(duration) {
|
|
@@ -979,6 +1066,7 @@ function warn(message, error) {
|
|
|
979
1066
|
var DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME, FALLBACK_TRACE_DIR, MAX_NAME_LENGTH;
|
|
980
1067
|
var init_utils = __esm({
|
|
981
1068
|
"packages/core/src/utils.ts"() {
|
|
1069
|
+
init_trace_filesystem();
|
|
982
1070
|
init_duration();
|
|
983
1071
|
DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
984
1072
|
RUNS_DIR_NAME = "runs";
|
|
@@ -1149,6 +1237,7 @@ var init_storage = __esm({
|
|
|
1149
1237
|
init_types();
|
|
1150
1238
|
init_trace_event_safety();
|
|
1151
1239
|
init_read_trace();
|
|
1240
|
+
init_trace_filesystem();
|
|
1152
1241
|
init_utils();
|
|
1153
1242
|
}
|
|
1154
1243
|
});
|
|
@@ -4662,83 +4751,6 @@ var init_programmatic = __esm({
|
|
|
4662
4751
|
}
|
|
4663
4752
|
});
|
|
4664
4753
|
|
|
4665
|
-
// packages/core/src/safety/sensitive-key.ts
|
|
4666
|
-
function keyHasExplicitSeparator(value) {
|
|
4667
|
-
return /[_\-.]/.test(value);
|
|
4668
|
-
}
|
|
4669
|
-
function normalizeSensitiveKey(value) {
|
|
4670
|
-
return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
4671
|
-
}
|
|
4672
|
-
function isTokenCredentialKey(normalized) {
|
|
4673
|
-
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
4674
|
-
if (normalized === "token") return true;
|
|
4675
|
-
if (normalized.endsWith("tokens")) return false;
|
|
4676
|
-
return normalized.endsWith("token");
|
|
4677
|
-
}
|
|
4678
|
-
function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
|
|
4679
|
-
if (!key) return false;
|
|
4680
|
-
const normalized = normalizeSensitiveKey(key);
|
|
4681
|
-
if (!normalized) return false;
|
|
4682
|
-
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
4683
|
-
const allowPrefixCompound = keyHasExplicitSeparator(key);
|
|
4684
|
-
for (const sensitive of sensitiveKeys) {
|
|
4685
|
-
const s = normalizeSensitiveKey(sensitive);
|
|
4686
|
-
if (!s) continue;
|
|
4687
|
-
if (s === "token") {
|
|
4688
|
-
if (isTokenCredentialKey(normalized)) return true;
|
|
4689
|
-
continue;
|
|
4690
|
-
}
|
|
4691
|
-
if (normalized === s) return true;
|
|
4692
|
-
if (normalized.endsWith(`_${s}`)) return true;
|
|
4693
|
-
if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
|
|
4694
|
-
}
|
|
4695
|
-
return false;
|
|
4696
|
-
}
|
|
4697
|
-
var NON_CREDENTIAL_TOKEN_CONFIG_KEYS, DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
|
|
4698
|
-
var init_sensitive_key = __esm({
|
|
4699
|
-
"packages/core/src/safety/sensitive-key.ts"() {
|
|
4700
|
-
NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
|
|
4701
|
-
[
|
|
4702
|
-
"tokens",
|
|
4703
|
-
"max_tokens",
|
|
4704
|
-
"min_tokens",
|
|
4705
|
-
"ls_max_tokens",
|
|
4706
|
-
"token_count",
|
|
4707
|
-
"token_limit",
|
|
4708
|
-
"token_budget",
|
|
4709
|
-
"input_tokens",
|
|
4710
|
-
"output_tokens",
|
|
4711
|
-
"total_tokens",
|
|
4712
|
-
"cached_tokens",
|
|
4713
|
-
"prompt_tokens",
|
|
4714
|
-
"completion_tokens"
|
|
4715
|
-
].map(normalizeSensitiveKey)
|
|
4716
|
-
);
|
|
4717
|
-
DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
|
|
4718
|
-
"authorization",
|
|
4719
|
-
"cookie",
|
|
4720
|
-
"token",
|
|
4721
|
-
"access_token",
|
|
4722
|
-
"accesstoken",
|
|
4723
|
-
"auth_token",
|
|
4724
|
-
"authtoken",
|
|
4725
|
-
"refresh_token",
|
|
4726
|
-
"refreshtoken",
|
|
4727
|
-
"id_token",
|
|
4728
|
-
"idtoken",
|
|
4729
|
-
"bearer_token",
|
|
4730
|
-
"bearertoken",
|
|
4731
|
-
"api_token",
|
|
4732
|
-
"apitoken",
|
|
4733
|
-
"apikey",
|
|
4734
|
-
"api_key",
|
|
4735
|
-
"password",
|
|
4736
|
-
"secret",
|
|
4737
|
-
"email"
|
|
4738
|
-
];
|
|
4739
|
-
}
|
|
4740
|
-
});
|
|
4741
|
-
|
|
4742
4754
|
// packages/core/src/checks/logical-events.ts
|
|
4743
4755
|
function isRecord7(value) {
|
|
4744
4756
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -7350,7 +7362,7 @@ var init_checks2 = __esm({
|
|
|
7350
7362
|
// Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
|
|
7351
7363
|
{
|
|
7352
7364
|
id: "key-value-secret",
|
|
7353
|
-
pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i
|
|
7365
|
+
pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i
|
|
7354
7366
|
}
|
|
7355
7367
|
];
|
|
7356
7368
|
}
|
|
@@ -16153,7 +16165,7 @@ var init_src = __esm({
|
|
|
16153
16165
|
});
|
|
16154
16166
|
|
|
16155
16167
|
// package.json
|
|
16156
|
-
var version = "6.31.
|
|
16168
|
+
var version = "6.31.5";
|
|
16157
16169
|
|
|
16158
16170
|
// packages/cli/src/list.ts
|
|
16159
16171
|
init_advanced();
|
|
@@ -17757,6 +17769,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
17757
17769
|
await readFileOnce(filePath, onLine);
|
|
17758
17770
|
return;
|
|
17759
17771
|
}
|
|
17772
|
+
let decoder = new string_decoder.StringDecoder("utf-8");
|
|
17760
17773
|
pos = st.size;
|
|
17761
17774
|
while (!shouldStop()) {
|
|
17762
17775
|
let next;
|
|
@@ -17770,6 +17783,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
17770
17783
|
pos = 0;
|
|
17771
17784
|
carry = "";
|
|
17772
17785
|
lineNumber = 0;
|
|
17786
|
+
decoder = new string_decoder.StringDecoder("utf-8");
|
|
17773
17787
|
}
|
|
17774
17788
|
if (next.size > pos) {
|
|
17775
17789
|
const fh = await promises.open(filePath, "r");
|
|
@@ -17778,7 +17792,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
17778
17792
|
const buf = Buffer.allocUnsafe(Number(len));
|
|
17779
17793
|
const { bytesRead } = await fh.read(buf, 0, buf.length, pos);
|
|
17780
17794
|
pos += bytesRead;
|
|
17781
|
-
const chunk = carry + buf.
|
|
17795
|
+
const chunk = carry + decoder.write(buf.subarray(0, bytesRead));
|
|
17782
17796
|
const parts = chunk.split(/\r?\n/);
|
|
17783
17797
|
carry = parts.pop() ?? "";
|
|
17784
17798
|
for (const p of parts) {
|
|
@@ -17943,6 +17957,128 @@ var EXPORT_PAYLOAD_VERSION = "0.1.2";
|
|
|
17943
17957
|
|
|
17944
17958
|
// packages/core/src/exporters/redact-export.ts
|
|
17945
17959
|
init_redactor();
|
|
17960
|
+
init_credential_value_patterns();
|
|
17961
|
+
|
|
17962
|
+
// packages/core/src/safety/url-redaction.ts
|
|
17963
|
+
init_sensitive_key();
|
|
17964
|
+
var HTTP_URL = /^https?:\/\/[^\s]+$/i;
|
|
17965
|
+
var CONNECTION_PROTOCOLS = /* @__PURE__ */ new Set([
|
|
17966
|
+
"postgres:",
|
|
17967
|
+
"postgresql:",
|
|
17968
|
+
"mysql:",
|
|
17969
|
+
"mariadb:",
|
|
17970
|
+
"mongodb:",
|
|
17971
|
+
"mongodb+srv:",
|
|
17972
|
+
"redis:",
|
|
17973
|
+
"rediss:",
|
|
17974
|
+
"amqp:",
|
|
17975
|
+
"amqps:"
|
|
17976
|
+
]);
|
|
17977
|
+
var CONNECTION_URI = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
|
|
17978
|
+
var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17979
|
+
var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
|
|
17980
|
+
var NUMERIC_SEGMENT = /^\d{5,}$/;
|
|
17981
|
+
var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
|
|
17982
|
+
function looksLikeHttpUrl(value) {
|
|
17983
|
+
return HTTP_URL.test(value.trim());
|
|
17984
|
+
}
|
|
17985
|
+
function looksLikeConnectionUri(value) {
|
|
17986
|
+
return CONNECTION_URI.test(value.trim());
|
|
17987
|
+
}
|
|
17988
|
+
function looksLikeUrlForRedaction(value) {
|
|
17989
|
+
return looksLikeHttpUrl(value) || looksLikeConnectionUri(value);
|
|
17990
|
+
}
|
|
17991
|
+
function parseRedactableUrl(value) {
|
|
17992
|
+
try {
|
|
17993
|
+
const url = new URL(value.trim());
|
|
17994
|
+
if (url.protocol === "http:" || url.protocol === "https:") return url;
|
|
17995
|
+
if (CONNECTION_PROTOCOLS.has(url.protocol)) return url;
|
|
17996
|
+
return void 0;
|
|
17997
|
+
} catch {
|
|
17998
|
+
return void 0;
|
|
17999
|
+
}
|
|
18000
|
+
}
|
|
18001
|
+
function isHttpProtocol(protocol) {
|
|
18002
|
+
return protocol === "http:" || protocol === "https:";
|
|
18003
|
+
}
|
|
18004
|
+
function isIdentifierSegment(segment) {
|
|
18005
|
+
if (UUID_SEGMENT.test(segment)) return true;
|
|
18006
|
+
if (HEX_SEGMENT.test(segment)) return true;
|
|
18007
|
+
if (NUMERIC_SEGMENT.test(segment)) return true;
|
|
18008
|
+
return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
18009
|
+
}
|
|
18010
|
+
function decodeParam(value) {
|
|
18011
|
+
try {
|
|
18012
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
18013
|
+
} catch {
|
|
18014
|
+
return value;
|
|
18015
|
+
}
|
|
18016
|
+
}
|
|
18017
|
+
function redactParams(raw, options, replacement) {
|
|
18018
|
+
let redacted = false;
|
|
18019
|
+
const parts = raw.split("&").map((part) => {
|
|
18020
|
+
if (part.length === 0) return part;
|
|
18021
|
+
const separator = part.indexOf("=");
|
|
18022
|
+
if (separator < 0) return part;
|
|
18023
|
+
const rawKey = part.slice(0, separator);
|
|
18024
|
+
const rawValue = part.slice(separator + 1);
|
|
18025
|
+
const key = decodeParam(rawKey);
|
|
18026
|
+
const value = decodeParam(rawValue);
|
|
18027
|
+
if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
18028
|
+
redacted = true;
|
|
18029
|
+
return `${rawKey}=${replacement}`;
|
|
18030
|
+
}
|
|
18031
|
+
return part;
|
|
18032
|
+
});
|
|
18033
|
+
return { value: parts.join("&"), redacted };
|
|
18034
|
+
}
|
|
18035
|
+
function redactUrlString(value, options) {
|
|
18036
|
+
const unchanged = {
|
|
18037
|
+
value,
|
|
18038
|
+
credentialRedacted: false,
|
|
18039
|
+
identifiersMasked: false
|
|
18040
|
+
};
|
|
18041
|
+
if (!looksLikeUrlForRedaction(value)) return unchanged;
|
|
18042
|
+
const url = parseRedactableUrl(value);
|
|
18043
|
+
if (!url) return unchanged;
|
|
18044
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
18045
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
18046
|
+
const http = isHttpProtocol(url.protocol);
|
|
18047
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
18048
|
+
let identifiersMasked = false;
|
|
18049
|
+
let pathname = url.pathname;
|
|
18050
|
+
if (http && options.maskPathIds === true) {
|
|
18051
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
18052
|
+
if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
|
|
18053
|
+
return segment;
|
|
18054
|
+
}
|
|
18055
|
+
identifiersMasked = true;
|
|
18056
|
+
return idPlaceholder;
|
|
18057
|
+
}).join("/");
|
|
18058
|
+
}
|
|
18059
|
+
let search = "";
|
|
18060
|
+
if (url.search.length > 1) {
|
|
18061
|
+
const params = redactParams(url.search.slice(1), options, replacement);
|
|
18062
|
+
if (params.redacted) credentialRedacted = true;
|
|
18063
|
+
search = `?${params.value}`;
|
|
18064
|
+
} else if (url.search.length === 1) {
|
|
18065
|
+
search = "?";
|
|
18066
|
+
}
|
|
18067
|
+
let hash = url.hash;
|
|
18068
|
+
if (url.hash.includes("=")) {
|
|
18069
|
+
const params = redactParams(url.hash.slice(1), options, replacement);
|
|
18070
|
+
if (params.redacted) credentialRedacted = true;
|
|
18071
|
+
hash = `#${params.value}`;
|
|
18072
|
+
}
|
|
18073
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
18074
|
+
return {
|
|
18075
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
18076
|
+
credentialRedacted,
|
|
18077
|
+
identifiersMasked
|
|
18078
|
+
};
|
|
18079
|
+
}
|
|
18080
|
+
|
|
18081
|
+
// packages/core/src/exporters/redact-export.ts
|
|
17946
18082
|
init_redaction_profiles();
|
|
17947
18083
|
function isRecord15(value) {
|
|
17948
18084
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -17960,21 +18096,34 @@ function deepClone(value) {
|
|
|
17960
18096
|
}
|
|
17961
18097
|
return out;
|
|
17962
18098
|
}
|
|
17963
|
-
function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
18099
|
+
function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
17964
18100
|
if (depth > 32) {
|
|
17965
18101
|
return { truncated: true, reason: "maxDepth" };
|
|
17966
18102
|
}
|
|
17967
18103
|
const out = {};
|
|
17968
18104
|
for (const [key, value] of Object.entries(record)) {
|
|
17969
|
-
out[key] = boundValue(
|
|
18105
|
+
out[key] = boundValue(
|
|
18106
|
+
value,
|
|
18107
|
+
key,
|
|
18108
|
+
sensitiveKeys,
|
|
18109
|
+
maxMetadataValueLength,
|
|
18110
|
+
maxPreviewLength,
|
|
18111
|
+
seen,
|
|
18112
|
+
depth
|
|
18113
|
+
);
|
|
17970
18114
|
}
|
|
17971
18115
|
return out;
|
|
17972
18116
|
}
|
|
17973
|
-
function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
18117
|
+
function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
17974
18118
|
if (value === null || typeof value !== "object") {
|
|
17975
18119
|
if (typeof value === "string") {
|
|
18120
|
+
const rewritten = redactUrlString(value, {
|
|
18121
|
+
sensitiveKeys,
|
|
18122
|
+
isSensitiveValue: stringContainsHighConfidenceCredential,
|
|
18123
|
+
maskPathIds: true
|
|
18124
|
+
}).value;
|
|
17976
18125
|
return truncateStringForProfile(
|
|
17977
|
-
|
|
18126
|
+
stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
|
|
17978
18127
|
key,
|
|
17979
18128
|
maxMetadataValueLength,
|
|
17980
18129
|
maxPreviewLength
|
|
@@ -17989,6 +18138,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
17989
18138
|
(item, index) => boundValue(
|
|
17990
18139
|
item,
|
|
17991
18140
|
String(index),
|
|
18141
|
+
sensitiveKeys,
|
|
17992
18142
|
maxMetadataValueLength,
|
|
17993
18143
|
maxPreviewLength,
|
|
17994
18144
|
seen,
|
|
@@ -17998,6 +18148,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
17998
18148
|
}
|
|
17999
18149
|
return boundAttributeValues(
|
|
18000
18150
|
value,
|
|
18151
|
+
sensitiveKeys,
|
|
18001
18152
|
maxMetadataValueLength,
|
|
18002
18153
|
maxPreviewLength,
|
|
18003
18154
|
seen,
|
|
@@ -18012,6 +18163,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
18012
18163
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
18013
18164
|
const bounded = boundAttributeValues(
|
|
18014
18165
|
redacted,
|
|
18166
|
+
[...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
|
|
18015
18167
|
maxMetadataValueLength,
|
|
18016
18168
|
maxPreviewLength,
|
|
18017
18169
|
seen,
|
|
@@ -20150,7 +20302,7 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
|
|
|
20150
20302
|
}
|
|
20151
20303
|
|
|
20152
20304
|
// packages/redact/src/key-value-secret.ts
|
|
20153
|
-
var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
|
|
20305
|
+
var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})`;
|
|
20154
20306
|
var KEY_VALUE_SECRET_PATTERN2 = new RegExp(
|
|
20155
20307
|
KEY_VALUE_SECRET_PATTERN_SOURCE,
|
|
20156
20308
|
"i"
|
|
@@ -20160,6 +20312,124 @@ function valueContainsKeyValueSecret(value) {
|
|
|
20160
20312
|
return KEY_VALUE_SECRET_PATTERN2.test(value);
|
|
20161
20313
|
}
|
|
20162
20314
|
|
|
20315
|
+
// packages/redact/src/url-redaction.ts
|
|
20316
|
+
var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
|
|
20317
|
+
var CONNECTION_PROTOCOLS2 = /* @__PURE__ */ new Set([
|
|
20318
|
+
"postgres:",
|
|
20319
|
+
"postgresql:",
|
|
20320
|
+
"mysql:",
|
|
20321
|
+
"mariadb:",
|
|
20322
|
+
"mongodb:",
|
|
20323
|
+
"mongodb+srv:",
|
|
20324
|
+
"redis:",
|
|
20325
|
+
"rediss:",
|
|
20326
|
+
"amqp:",
|
|
20327
|
+
"amqps:"
|
|
20328
|
+
]);
|
|
20329
|
+
var CONNECTION_URI2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
|
|
20330
|
+
var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
20331
|
+
var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
|
|
20332
|
+
var NUMERIC_SEGMENT2 = /^\d{5,}$/;
|
|
20333
|
+
var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
|
|
20334
|
+
function looksLikeHttpUrl2(value) {
|
|
20335
|
+
return HTTP_URL2.test(value.trim());
|
|
20336
|
+
}
|
|
20337
|
+
function looksLikeConnectionUri2(value) {
|
|
20338
|
+
return CONNECTION_URI2.test(value.trim());
|
|
20339
|
+
}
|
|
20340
|
+
function looksLikeUrlForRedaction2(value) {
|
|
20341
|
+
return looksLikeHttpUrl2(value) || looksLikeConnectionUri2(value);
|
|
20342
|
+
}
|
|
20343
|
+
function parseRedactableUrl2(value) {
|
|
20344
|
+
try {
|
|
20345
|
+
const url = new URL(value.trim());
|
|
20346
|
+
if (url.protocol === "http:" || url.protocol === "https:") return url;
|
|
20347
|
+
if (CONNECTION_PROTOCOLS2.has(url.protocol)) return url;
|
|
20348
|
+
return void 0;
|
|
20349
|
+
} catch {
|
|
20350
|
+
return void 0;
|
|
20351
|
+
}
|
|
20352
|
+
}
|
|
20353
|
+
function isHttpProtocol2(protocol) {
|
|
20354
|
+
return protocol === "http:" || protocol === "https:";
|
|
20355
|
+
}
|
|
20356
|
+
function isIdentifierSegment2(segment) {
|
|
20357
|
+
if (UUID_SEGMENT2.test(segment)) return true;
|
|
20358
|
+
if (HEX_SEGMENT2.test(segment)) return true;
|
|
20359
|
+
if (NUMERIC_SEGMENT2.test(segment)) return true;
|
|
20360
|
+
return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
20361
|
+
}
|
|
20362
|
+
function decodeParam2(value) {
|
|
20363
|
+
try {
|
|
20364
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
20365
|
+
} catch {
|
|
20366
|
+
return value;
|
|
20367
|
+
}
|
|
20368
|
+
}
|
|
20369
|
+
function redactParams2(raw, options, replacement) {
|
|
20370
|
+
let redacted = false;
|
|
20371
|
+
const parts = raw.split("&").map((part) => {
|
|
20372
|
+
if (part.length === 0) return part;
|
|
20373
|
+
const separator = part.indexOf("=");
|
|
20374
|
+
if (separator < 0) return part;
|
|
20375
|
+
const rawKey = part.slice(0, separator);
|
|
20376
|
+
const rawValue = part.slice(separator + 1);
|
|
20377
|
+
const key = decodeParam2(rawKey);
|
|
20378
|
+
const value = decodeParam2(rawValue);
|
|
20379
|
+
if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
20380
|
+
redacted = true;
|
|
20381
|
+
return `${rawKey}=${replacement}`;
|
|
20382
|
+
}
|
|
20383
|
+
return part;
|
|
20384
|
+
});
|
|
20385
|
+
return { value: parts.join("&"), redacted };
|
|
20386
|
+
}
|
|
20387
|
+
function redactUrlString2(value, options) {
|
|
20388
|
+
const unchanged = {
|
|
20389
|
+
value,
|
|
20390
|
+
credentialRedacted: false,
|
|
20391
|
+
identifiersMasked: false
|
|
20392
|
+
};
|
|
20393
|
+
if (!looksLikeUrlForRedaction2(value)) return unchanged;
|
|
20394
|
+
const url = parseRedactableUrl2(value);
|
|
20395
|
+
if (!url) return unchanged;
|
|
20396
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
20397
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
20398
|
+
const http = isHttpProtocol2(url.protocol);
|
|
20399
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
20400
|
+
let identifiersMasked = false;
|
|
20401
|
+
let pathname = url.pathname;
|
|
20402
|
+
if (http && options.maskPathIds === true) {
|
|
20403
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
20404
|
+
if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
|
|
20405
|
+
return segment;
|
|
20406
|
+
}
|
|
20407
|
+
identifiersMasked = true;
|
|
20408
|
+
return idPlaceholder;
|
|
20409
|
+
}).join("/");
|
|
20410
|
+
}
|
|
20411
|
+
let search = "";
|
|
20412
|
+
if (url.search.length > 1) {
|
|
20413
|
+
const params = redactParams2(url.search.slice(1), options, replacement);
|
|
20414
|
+
if (params.redacted) credentialRedacted = true;
|
|
20415
|
+
search = `?${params.value}`;
|
|
20416
|
+
} else if (url.search.length === 1) {
|
|
20417
|
+
search = "?";
|
|
20418
|
+
}
|
|
20419
|
+
let hash = url.hash;
|
|
20420
|
+
if (url.hash.includes("=")) {
|
|
20421
|
+
const params = redactParams2(url.hash.slice(1), options, replacement);
|
|
20422
|
+
if (params.redacted) credentialRedacted = true;
|
|
20423
|
+
hash = `#${params.value}`;
|
|
20424
|
+
}
|
|
20425
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
20426
|
+
return {
|
|
20427
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
20428
|
+
credentialRedacted,
|
|
20429
|
+
identifiersMasked
|
|
20430
|
+
};
|
|
20431
|
+
}
|
|
20432
|
+
|
|
20163
20433
|
// packages/redact/src/index.ts
|
|
20164
20434
|
var DEFAULT_REDACT_KEYS2 = [
|
|
20165
20435
|
"authorization",
|
|
@@ -20226,7 +20496,9 @@ function findCompiledKeyRule(key, rules) {
|
|
|
20226
20496
|
const exact = toKey2(key);
|
|
20227
20497
|
const direct = rules.find((candidate) => candidate.key === exact);
|
|
20228
20498
|
if (direct) return direct;
|
|
20229
|
-
return rules.find(
|
|
20499
|
+
return rules.find(
|
|
20500
|
+
(candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
|
|
20501
|
+
);
|
|
20230
20502
|
}
|
|
20231
20503
|
function stableHash2(value) {
|
|
20232
20504
|
const hash = crypto__default.default.createHash("sha256").update(value, "utf8").digest("hex");
|
|
@@ -20425,7 +20697,7 @@ function compileRules2(rules, extraKeys) {
|
|
|
20425
20697
|
const out = /* @__PURE__ */ new Map();
|
|
20426
20698
|
const set = (rule) => {
|
|
20427
20699
|
const key = toKey2(rule.key);
|
|
20428
|
-
out.set(key, { ...rule, key });
|
|
20700
|
+
out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
|
|
20429
20701
|
};
|
|
20430
20702
|
for (const key of DEFAULT_REDACT_KEYS2) {
|
|
20431
20703
|
set({ key, strategy: "full" });
|
|
@@ -20504,6 +20776,7 @@ function createRedactionProfile(profile = "local") {
|
|
|
20504
20776
|
}
|
|
20505
20777
|
var Redactor2 = class {
|
|
20506
20778
|
#rules;
|
|
20779
|
+
#sensitiveKeys;
|
|
20507
20780
|
#detectors;
|
|
20508
20781
|
#profile;
|
|
20509
20782
|
#replacement;
|
|
@@ -20516,6 +20789,7 @@ var Redactor2 = class {
|
|
|
20516
20789
|
...resolved.extraKeys,
|
|
20517
20790
|
...options?.extraKeys ?? []
|
|
20518
20791
|
]);
|
|
20792
|
+
this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
|
|
20519
20793
|
this.#detectors = [
|
|
20520
20794
|
...builtInDetectorsForProfile(this.#profile),
|
|
20521
20795
|
...options?.detectors ?? []
|
|
@@ -20549,6 +20823,14 @@ var Redactor2 = class {
|
|
|
20549
20823
|
#recordFinding(state, finding) {
|
|
20550
20824
|
if (this.#collectFindings) state.findings.push(finding);
|
|
20551
20825
|
}
|
|
20826
|
+
#detect(value, key, path44) {
|
|
20827
|
+
for (const detector of this.#detectors) {
|
|
20828
|
+
for (const detection of detector.detect({ path: path44, key, value })) {
|
|
20829
|
+
if ((detection.action ?? "replace") !== "keep") return { detector, detection };
|
|
20830
|
+
}
|
|
20831
|
+
}
|
|
20832
|
+
return void 0;
|
|
20833
|
+
}
|
|
20552
20834
|
#redactValue(value, key, path44, depth, state) {
|
|
20553
20835
|
if (depth > this.#maxDepth) {
|
|
20554
20836
|
this.#recordFinding(
|
|
@@ -20567,6 +20849,43 @@ var Redactor2 = class {
|
|
|
20567
20849
|
return applyRule(rule, value, this.#replacement);
|
|
20568
20850
|
}
|
|
20569
20851
|
}
|
|
20852
|
+
if (typeof value === "string" && looksLikeUrlForRedaction2(value)) {
|
|
20853
|
+
const url = redactUrlString2(value, {
|
|
20854
|
+
sensitiveKeys: this.#sensitiveKeys,
|
|
20855
|
+
isSensitiveValue: (candidate) => this.#detect(candidate, key, path44) !== void 0,
|
|
20856
|
+
replacement: this.#replacement,
|
|
20857
|
+
maskPathIds: this.#profile !== "local"
|
|
20858
|
+
});
|
|
20859
|
+
if (url.value !== value) {
|
|
20860
|
+
this.#recordFinding(
|
|
20861
|
+
state,
|
|
20862
|
+
makeFinding(
|
|
20863
|
+
path44,
|
|
20864
|
+
url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
|
|
20865
|
+
"replace",
|
|
20866
|
+
"value",
|
|
20867
|
+
url.credentialRedacted ? "error" : "warning"
|
|
20868
|
+
)
|
|
20869
|
+
);
|
|
20870
|
+
const residual = this.#detect(
|
|
20871
|
+
url.value.split(this.#replacement).join(""),
|
|
20872
|
+
key,
|
|
20873
|
+
path44
|
|
20874
|
+
);
|
|
20875
|
+
if (residual === void 0) return url.value;
|
|
20876
|
+
this.#recordFinding(
|
|
20877
|
+
state,
|
|
20878
|
+
makeFinding(
|
|
20879
|
+
path44,
|
|
20880
|
+
residual.detector.id,
|
|
20881
|
+
"replace",
|
|
20882
|
+
residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
|
|
20883
|
+
residual.detection.severity ?? residual.detector.severity ?? "warning"
|
|
20884
|
+
)
|
|
20885
|
+
);
|
|
20886
|
+
return this.#replacement;
|
|
20887
|
+
}
|
|
20888
|
+
}
|
|
20570
20889
|
for (const detector of this.#detectors) {
|
|
20571
20890
|
const detections = detector.detect({ path: path44, key, value });
|
|
20572
20891
|
for (const detection of detections) {
|
|
@@ -28582,7 +28901,7 @@ function envCheck(name, optional = true) {
|
|
|
28582
28901
|
async function traceDirWritable(traceDir) {
|
|
28583
28902
|
const resolved = path33__default.default.resolve(traceDir);
|
|
28584
28903
|
try {
|
|
28585
|
-
await promises.mkdir(resolved, { recursive: true });
|
|
28904
|
+
await promises.mkdir(resolved, { recursive: true, mode: 448 });
|
|
28586
28905
|
await promises.access(resolved, promises.constants.W_OK);
|
|
28587
28906
|
return {
|
|
28588
28907
|
id: "trace-dir-writable",
|
|
@@ -28600,6 +28919,44 @@ async function traceDirWritable(traceDir) {
|
|
|
28600
28919
|
};
|
|
28601
28920
|
}
|
|
28602
28921
|
}
|
|
28922
|
+
async function traceDirPermissions(traceDir) {
|
|
28923
|
+
const resolved = path33__default.default.resolve(traceDir);
|
|
28924
|
+
if (process3__default.default.platform === "win32") {
|
|
28925
|
+
return {
|
|
28926
|
+
id: "trace-dir-permissions",
|
|
28927
|
+
status: "skipped",
|
|
28928
|
+
message: "POSIX confidentiality mode check skipped on Windows.",
|
|
28929
|
+
evidence: resolved
|
|
28930
|
+
};
|
|
28931
|
+
}
|
|
28932
|
+
try {
|
|
28933
|
+
const st = await promises.stat(resolved);
|
|
28934
|
+
const mode = st.mode & 511;
|
|
28935
|
+
const octal = mode.toString(8).padStart(3, "0");
|
|
28936
|
+
if ((mode & 63) !== 0) {
|
|
28937
|
+
return {
|
|
28938
|
+
id: "trace-dir-permissions",
|
|
28939
|
+
status: "warn",
|
|
28940
|
+
message: `Trace directory allows group/other access (mode ${octal}): ${resolved}`,
|
|
28941
|
+
remediation: `Restrict with chmod 700 on the directory and review existing JSONL files with chmod 600. AgentInspect does not rewrite existing modes. See ${DOCS_BASE}/GETTING-STARTED.md`,
|
|
28942
|
+
evidence: `mode=${octal}`
|
|
28943
|
+
};
|
|
28944
|
+
}
|
|
28945
|
+
return {
|
|
28946
|
+
id: "trace-dir-permissions",
|
|
28947
|
+
status: "pass",
|
|
28948
|
+
message: `Trace directory mode is restrictive (${octal}): ${resolved}`,
|
|
28949
|
+
evidence: `mode=${octal}`
|
|
28950
|
+
};
|
|
28951
|
+
} catch (error) {
|
|
28952
|
+
return {
|
|
28953
|
+
id: "trace-dir-permissions",
|
|
28954
|
+
status: "skipped",
|
|
28955
|
+
message: `Could not inspect trace directory permissions: ${resolved}`,
|
|
28956
|
+
evidence: error instanceof Error ? error.message : String(error)
|
|
28957
|
+
};
|
|
28958
|
+
}
|
|
28959
|
+
}
|
|
28603
28960
|
function readPackageVersionNearEntry(entryPath, packageName) {
|
|
28604
28961
|
let dir = path33__default.default.dirname(path33__default.default.resolve(entryPath));
|
|
28605
28962
|
const { root } = path33__default.default.parse(dir);
|
|
@@ -28731,6 +29088,7 @@ async function runDoctorChecks(options = {}) {
|
|
|
28731
29088
|
evidence: version
|
|
28732
29089
|
},
|
|
28733
29090
|
await traceDirWritable(traceDir),
|
|
29091
|
+
await traceDirPermissions(traceDir),
|
|
28734
29092
|
envCheck("AGENT_INSPECT"),
|
|
28735
29093
|
envCheck("AGENT_INSPECT_TRACE_DIR"),
|
|
28736
29094
|
versionMismatchCheck(cwd)
|