ai-lens 0.8.44 → 0.8.46
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/.commithash +1 -1
- package/cli/hooks.js +10 -3
- package/cli/status.js +2 -1
- package/client/redact.js +77 -4
- package/client/sender.js +1 -1
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
8f21797
|
package/cli/hooks.js
CHANGED
|
@@ -115,9 +115,16 @@ export function captureCommand(useTilde = false, rawPath = false, customPath = n
|
|
|
115
115
|
? '~/.ai-lens/client/capture.js'
|
|
116
116
|
: CAPTURE_PATH.replace(/\\/g, '/');
|
|
117
117
|
const pathPart = (customPath != null || rawPath) ? capturePath : shellEscape(capturePath);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
if (nodePath === '/usr/bin/env node') return `/usr/bin/env node ${pathPart}`;
|
|
119
|
+
// Claude Code on Windows executes hooks via cmd.exe. A quoted executable path
|
|
120
|
+
// at the start of the line (e.g. "C:/Program Files/node.exe" args) is not
|
|
121
|
+
// recognised as a command by cmd.exe. Use bare `node` for Claude Code hooks
|
|
122
|
+
// (rawPath=true) when the path contains spaces; Cursor hooks use PowerShell
|
|
123
|
+
// where quoting works fine.
|
|
124
|
+
if (process.platform === 'win32' && rawPath && nodePath.includes(' ')) {
|
|
125
|
+
return `node ${pathPart}`;
|
|
126
|
+
}
|
|
127
|
+
return `${shellEscape(nodePath.replace(/\\/g, '/'))} ${pathPart}`;
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
// Cursor on Windows executes hooks via PowerShell, which treats a quoted path like
|
package/cli/status.js
CHANGED
|
@@ -323,11 +323,12 @@ function checkSystem() {
|
|
|
323
323
|
const arch = osArch() || 'unknown';
|
|
324
324
|
const nodeVersion = process.version || 'unknown';
|
|
325
325
|
const platform = process.platform === 'darwin' ? 'macOS' : process.platform;
|
|
326
|
+
const cwd = process.cwd();
|
|
326
327
|
const summary = `${platform} ${osVersion} ${arch}, Node ${nodeVersion}`;
|
|
327
328
|
return {
|
|
328
329
|
ok: true,
|
|
329
330
|
summary,
|
|
330
|
-
detail: `Platform: ${platform}\nOS version: ${osVersion}\nArchitecture: ${arch}\nNode: ${nodeVersion}`,
|
|
331
|
+
detail: `Platform: ${platform}\nOS version: ${osVersion}\nArchitecture: ${arch}\nNode: ${nodeVersion}\nCWD: ${cwd}`,
|
|
331
332
|
};
|
|
332
333
|
}
|
|
333
334
|
|
package/client/redact.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Secret redaction utility (client-side copy).
|
|
3
|
+
* ⚠️ KEEP IN SYNC with server/utils/redact.js — see test/server/redact.test.js sync test.
|
|
3
4
|
* Replaces tokens, passwords, API keys and other secrets with [REDACTED:TYPE] tags.
|
|
4
5
|
* Standalone — no imports, safe to run on developer machines.
|
|
5
6
|
*/
|
|
@@ -15,15 +16,63 @@ const PATTERNS = [
|
|
|
15
16
|
// GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_)
|
|
16
17
|
{ type: 'GITHUB_TOKEN', re: /gh[pousr]_[A-Za-z0-9_]{36,}/g },
|
|
17
18
|
|
|
19
|
+
// GitLab tokens (glpat-, gldt-, glrt-, glcbt-, glsoat-)
|
|
20
|
+
{
|
|
21
|
+
type: 'GITLAB_TOKEN',
|
|
22
|
+
re: /gl(?:pat|dt|rt|cbt|soat)-[A-Za-z0-9_-]{20,}/g,
|
|
23
|
+
replacer: (m) => {
|
|
24
|
+
const prefix = m.slice(0, m.indexOf('-'));
|
|
25
|
+
const typeMap = { glpat: 'GITLAB_PAT', gldt: 'GITLAB_DEPLOY_TOKEN', glrt: 'GITLAB_RUNNER_TOKEN', glcbt: 'GITLAB_CLUSTER_TOKEN', glsoat: 'GITLAB_OAUTH_TOKEN' };
|
|
26
|
+
return `[REDACTED:${typeMap[prefix] || 'GITLAB_TOKEN'}]`;
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
|
|
18
30
|
// Anthropic API key
|
|
19
31
|
{ type: 'ANTHROPIC_KEY', re: /sk-ant-[A-Za-z0-9_-]{20,}/g },
|
|
20
32
|
|
|
21
33
|
// OpenAI project key
|
|
22
34
|
{ type: 'OPENAI_KEY', re: /sk-proj-[A-Za-z0-9_-]{20,}/g },
|
|
23
35
|
|
|
36
|
+
// Google API key (AIzaSy...)
|
|
37
|
+
{ type: 'GOOGLE_API_KEY', re: /AIza[A-Za-z0-9_-]{35}/g },
|
|
38
|
+
|
|
39
|
+
// Google OAuth access token (ya29....)
|
|
40
|
+
{ type: 'GOOGLE_OAUTH', re: /ya29\.[A-Za-z0-9_-]{50,}/g },
|
|
41
|
+
|
|
42
|
+
// GCP service account email (project-id@project-id.iam.gserviceaccount.com)
|
|
43
|
+
{ type: 'GCP_SERVICE_ACCOUNT', re: /[a-z0-9-]+@[a-z0-9-]+\.iam\.gserviceaccount\.com/g },
|
|
44
|
+
|
|
45
|
+
// x-api-key header value
|
|
46
|
+
{
|
|
47
|
+
type: 'API_KEY_HEADER',
|
|
48
|
+
re: /(["']?x-api-key["']?\s*[:=]\s*["']?)[^\s"',)}\]]+/gi,
|
|
49
|
+
replacer: (m, prefix) => `${prefix}[REDACTED:API_KEY_HEADER]`,
|
|
50
|
+
},
|
|
51
|
+
|
|
24
52
|
// Generic sk- key (must come after sk-ant- and sk-proj-)
|
|
25
53
|
{ type: 'API_KEY', re: /sk-[A-Za-z0-9_-]{32,}/g },
|
|
26
54
|
|
|
55
|
+
// Stripe secret key (live only — test keys intentionally not redacted)
|
|
56
|
+
{ type: 'STRIPE_SECRET', re: /sk_live_[A-Za-z0-9]{24,}/g },
|
|
57
|
+
|
|
58
|
+
// Stripe restricted key
|
|
59
|
+
{ type: 'STRIPE_RESTRICTED', re: /rk_live_[A-Za-z0-9]{24,}/g },
|
|
60
|
+
|
|
61
|
+
// npm token
|
|
62
|
+
{ type: 'NPM_TOKEN', re: /npm_[A-Za-z0-9]{36,}/g },
|
|
63
|
+
|
|
64
|
+
// PyPI token
|
|
65
|
+
{ type: 'PYPI_TOKEN', re: /pypi-[A-Za-z0-9_-]{16,}/g },
|
|
66
|
+
|
|
67
|
+
// SendGrid API key (SG.{22}.{43})
|
|
68
|
+
{ type: 'SENDGRID_KEY', re: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g },
|
|
69
|
+
|
|
70
|
+
// Twilio API key (SK + 32 lowercase hex chars)
|
|
71
|
+
{ type: 'TWILIO_KEY', re: /SK[a-f0-9]{32}/g },
|
|
72
|
+
|
|
73
|
+
// Telegram bot token (8-10 digit bot ID : 35-char secret)
|
|
74
|
+
{ type: 'TELEGRAM_BOT_TOKEN', re: /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/g },
|
|
75
|
+
|
|
27
76
|
// Slack tokens (xox{b,p,a,o,s}-)
|
|
28
77
|
{ type: 'SLACK_TOKEN', re: /xo[xabposr]{1,2}-[A-Za-z0-9-]{10,}/g },
|
|
29
78
|
|
|
@@ -36,22 +85,46 @@ const PATTERNS = [
|
|
|
36
85
|
// PEM private keys — full block or truncated (just the header + base64 content)
|
|
37
86
|
{ type: 'PRIVATE_KEY', re: /-----BEGIN[A-Z ]*PRIVATE KEY-----[A-Za-z0-9+/=\s\\.]*(?:-----END[A-Z ]*PRIVATE KEY-----)?/g },
|
|
38
87
|
|
|
88
|
+
// Meilisearch master/API key (mc_ + 32+ hex chars)
|
|
89
|
+
{ type: 'MEILISEARCH_KEY', re: /\bmc_[0-9a-f]{32,}\b/g },
|
|
90
|
+
|
|
39
91
|
// Connection string password (://user:password@host) — redacts password only
|
|
40
92
|
{ type: 'CONNECTION_STRING', re: /:\/\/([^:@\s]+):([^@\s]{3,})@/g, replacer: (m, user, _pw) => `://${user}:[REDACTED:CONNECTION_STRING]@` },
|
|
41
93
|
|
|
42
|
-
//
|
|
43
|
-
|
|
94
|
+
// MySQL/mysqldump CLI -p<password> syntax (mysql -u user -pSECRET dbname)
|
|
95
|
+
{ type: 'MYSQL_PASSWORD', re: /(\bmysql\w*\b[^\n]*\s-p)(\S{8,})/g, replacer: (m, prefix, _pw) => `${prefix}[REDACTED:MYSQL_PASSWORD]` },
|
|
96
|
+
|
|
97
|
+
// Environment variables with PASSWORD/PASSWD keyword — lower 6-char minimum
|
|
98
|
+
// Catches BASIC_AUTH_PASSWORD=236716, db_password=secret, etc.
|
|
99
|
+
{
|
|
100
|
+
type: 'ENV_VAR',
|
|
101
|
+
re: /([A-Za-z_]{0,50}(?:PASSWORD|PASSWD)[A-Za-z_]{0,50}\s*=\s*["']?)([^\s"';\\\`|>{}\[\]]{6,})/gi,
|
|
102
|
+
replacer: (m, prefix, _val) => `${prefix}[REDACTED:ENV_VAR]`,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// Environment variables with other secret keywords — standard 8-char minimum
|
|
106
|
+
// Catches aws_secret_access_key=..., AI_LENS_AUTH_TOKEN=..., RUSTAT_KEY=..., etc.
|
|
107
|
+
// Case-insensitive to catch lowercase config files. _KEY added for API key vars.
|
|
44
108
|
// Skips template refs like ${VAR} since value excludes { } chars.
|
|
45
109
|
{
|
|
46
110
|
type: 'ENV_VAR',
|
|
47
|
-
re: /([A-
|
|
111
|
+
re: /([A-Za-z_]{0,50}(?:SECRET|TOKEN|API_KEY|APIKEY|_KEY)[A-Za-z_]{0,50}\s*=\s*["']?)([^\s"';\\\`|>{}\[\]]{8,})/gi,
|
|
48
112
|
replacer: (m, prefix, _val) => `${prefix}[REDACTED:ENV_VAR]`,
|
|
49
113
|
},
|
|
50
114
|
|
|
51
115
|
// Key-value pairs: password=..., token: ..., etc.
|
|
116
|
+
// Uses \w* prefix/suffix to match compound names like aws_secret_access_key
|
|
117
|
+
{
|
|
118
|
+
type: 'KEY_VALUE',
|
|
119
|
+
re: /(\b\w{0,30}(?:password|passwd|token|secret|api_key|apikey|api_secret|access_token|auth_token|private_key|client_secret|credential|authorization|session_key|encryption_key|signing_key|refresh_token|id_token)\w{0,30}["']?\s*[:=]\s*["']?)([^\s"',;\[\]{}]{8,})/gi,
|
|
120
|
+
replacer: (m, prefix, _val) => `${prefix}[REDACTED:KEY_VALUE]`,
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// Russian key-value pairs: пароль=..., секрет: ..., токен: ..., ключ: ...
|
|
124
|
+
// Separate pattern because \b does not work with Cyrillic (\w is ASCII-only)
|
|
52
125
|
{
|
|
53
126
|
type: 'KEY_VALUE',
|
|
54
|
-
re: /(\
|
|
127
|
+
re: /((?:^|[\s"',:={}([\-])(?:пароль|парол[а-я]*|секрет[а-я]*|токен[а-я]*|ключ[а-я]*)["']?\s*[:=\-—–]?\s*["']?)([^\s"',;\[\]{}]{6,})/gi,
|
|
55
128
|
replacer: (m, prefix, _val) => `${prefix}[REDACTED:KEY_VALUE]`,
|
|
56
129
|
},
|
|
57
130
|
];
|
package/client/sender.js
CHANGED
|
@@ -60,7 +60,7 @@ export function rotateLog(logPath = LOG_PATH, maxAgeDays = LOG_MAX_AGE_DAYS) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export const MAX_QUEUE_SIZE = 10_000;
|
|
63
|
-
export const MAX_CHUNK_BYTES =
|
|
63
|
+
export const MAX_CHUNK_BYTES = 50 * 1024; // 50 KB per POST — small chunks avoid ECONNRESET on corporate proxies/TLS inspection
|
|
64
64
|
export const LOCK_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
65
65
|
|
|
66
66
|
// =============================================================================
|