@slbdn/mcp-tester 1.1.0 → 1.2.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/CHANGELOG.md +16 -0
- package/README.md +140 -6
- package/dist/client/MCPClient.d.ts.map +1 -1
- package/dist/client/MCPClient.js +33 -22
- package/dist/client/MCPClient.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/errors.d.ts +43 -5
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +86 -7
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/logger.d.ts +51 -24
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +103 -9
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/masking.d.ts +66 -0
- package/dist/utils/masking.d.ts.map +1 -0
- package/dist/utils/masking.js +171 -0
- package/dist/utils/masking.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret masking utility for preventing sensitive data from appearing in logs.
|
|
3
|
+
*
|
|
4
|
+
* Automatically detects and masks common secret patterns like API keys,
|
|
5
|
+
* tokens, passwords, and connection strings. Also masks values of known
|
|
6
|
+
* sensitive environment variable keys.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { maskSecrets, addSecretPattern } from '@slbdn/mcp-tester';
|
|
11
|
+
*
|
|
12
|
+
* // Built-in patterns are applied automatically
|
|
13
|
+
* maskSecrets('API key: sk-abc123def456ghi789');
|
|
14
|
+
* // → 'API key: sk-ab...789'
|
|
15
|
+
*
|
|
16
|
+
* // Add custom patterns
|
|
17
|
+
* addSecretPattern(/my-secret-key-[a-zA-Z0-9]+/g, 'MyOrg API key');
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Environment variable keys that commonly contain secrets.
|
|
22
|
+
* Values for these keys will be masked in log output.
|
|
23
|
+
*/
|
|
24
|
+
const SENSITIVE_ENV_KEYS = new Set([
|
|
25
|
+
'API_KEY',
|
|
26
|
+
'API_SECRET',
|
|
27
|
+
'SECRET_KEY',
|
|
28
|
+
'SECRET',
|
|
29
|
+
'PRIVATE_KEY',
|
|
30
|
+
'ACCESS_KEY',
|
|
31
|
+
'ACCESS_TOKEN',
|
|
32
|
+
'ACCESS_SECRET',
|
|
33
|
+
'AUTH_TOKEN',
|
|
34
|
+
'AUTHORIZATION',
|
|
35
|
+
'BEARER_TOKEN',
|
|
36
|
+
'TOKEN',
|
|
37
|
+
'REFRESH_TOKEN',
|
|
38
|
+
'SESSION_TOKEN',
|
|
39
|
+
'SESSION_ID',
|
|
40
|
+
'ID_TOKEN',
|
|
41
|
+
'PASSWORD',
|
|
42
|
+
'PASSWD',
|
|
43
|
+
'DB_PASSWORD',
|
|
44
|
+
'DATABASE_PASSWORD',
|
|
45
|
+
'REDIS_PASSWORD',
|
|
46
|
+
'MONGO_PASSWORD',
|
|
47
|
+
'DATABASE_URL',
|
|
48
|
+
'DB_URL',
|
|
49
|
+
'MONGODB_URI',
|
|
50
|
+
'POSTGRES_URL',
|
|
51
|
+
'MYSQL_URL',
|
|
52
|
+
'REDIS_URL',
|
|
53
|
+
'AWS_ACCESS_KEY_ID',
|
|
54
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
55
|
+
'AWS_SESSION_TOKEN',
|
|
56
|
+
'AZURE_CLIENT_SECRET',
|
|
57
|
+
'GCP_API_KEY',
|
|
58
|
+
'GOOGLE_API_KEY',
|
|
59
|
+
'OPENAI_API_KEY',
|
|
60
|
+
'ANTHROPIC_API_KEY',
|
|
61
|
+
'GITHUB_TOKEN',
|
|
62
|
+
'GITLAB_TOKEN',
|
|
63
|
+
'NPM_TOKEN',
|
|
64
|
+
'NPM_AUTH_TOKEN',
|
|
65
|
+
'SLACK_TOKEN',
|
|
66
|
+
'SLACK_WEBHOOK',
|
|
67
|
+
'SENTRY_DSN',
|
|
68
|
+
'ENCRYPTION_KEY',
|
|
69
|
+
'SIGNING_KEY',
|
|
70
|
+
'JWT_SECRET',
|
|
71
|
+
'CSRF_SECRET',
|
|
72
|
+
'APP_SECRET',
|
|
73
|
+
]);
|
|
74
|
+
const OPENAI_KEY_REGEX = /\bsk-(?:proj|ant)-[a-zA-Z0-9_-]{10,}/g;
|
|
75
|
+
const SK_KEY_REGEX = /\bsk-[a-zA-Z0-9]{12,}/g;
|
|
76
|
+
const AWS_KEY_REGEX = /\bAKIA[A-Z0-9]{16}\b/g;
|
|
77
|
+
const BEARER_REGEX = /\bBearer\s+[a-zA-Z0-9._-]{8,}/gi;
|
|
78
|
+
const JWT_REGEX = /\beyJ[a-zA-Z0-9_-]{10,}/g;
|
|
79
|
+
const HEX_REGEX = /\b[a-f0-9]{40,}\b/g;
|
|
80
|
+
const URL_CREDS_REGEX = /:[a-zA-Z0-9._-]{4,}@[a-zA-Z0-9.-]+/g;
|
|
81
|
+
const KV_REGEX = /\b(?:password|passwd|secret|token|key|auth|credential)s?\s*[:=]\s*["']?[a-zA-Z0-9._/@+-]{8,}["']?/gi;
|
|
82
|
+
/**
|
|
83
|
+
* Built-in secret patterns that are automatically masked.
|
|
84
|
+
*/
|
|
85
|
+
const DEFAULT_SECRET_PATTERNS = [
|
|
86
|
+
{ pattern: OPENAI_KEY_REGEX, name: 'OpenAI/Anthropic API key' },
|
|
87
|
+
{ pattern: SK_KEY_REGEX, name: 'API key (sk-)' },
|
|
88
|
+
{ pattern: AWS_KEY_REGEX, name: 'AWS access key' },
|
|
89
|
+
{ pattern: BEARER_REGEX, name: 'Bearer token' },
|
|
90
|
+
{ pattern: JWT_REGEX, name: 'JWT token' },
|
|
91
|
+
{ pattern: HEX_REGEX, name: 'Hex token' },
|
|
92
|
+
{ pattern: URL_CREDS_REGEX, name: 'URL credentials' },
|
|
93
|
+
{ pattern: KV_REGEX, name: 'Key-value secret' },
|
|
94
|
+
];
|
|
95
|
+
/** Active secret patterns (built-in + user-added). */
|
|
96
|
+
let activePatterns = [...DEFAULT_SECRET_PATTERNS];
|
|
97
|
+
/** Number of characters to keep visible at start/end when masking. */
|
|
98
|
+
const VISIBLE_CHARS = 3;
|
|
99
|
+
/**
|
|
100
|
+
* Mask a string value, keeping the first few and last few characters visible
|
|
101
|
+
* and replacing the rest with `...`.
|
|
102
|
+
*
|
|
103
|
+
* @param value - The secret value to mask
|
|
104
|
+
* @param visibleChars - How many characters to keep visible at start and end
|
|
105
|
+
* @returns Masked string like "sk-ab...789"
|
|
106
|
+
*/
|
|
107
|
+
export function maskValue(value, visibleChars = VISIBLE_CHARS) {
|
|
108
|
+
if (visibleChars <= 0 || value.length <= visibleChars * 2 + 3) {
|
|
109
|
+
return '***';
|
|
110
|
+
}
|
|
111
|
+
const start = value.slice(0, visibleChars);
|
|
112
|
+
const end = value.slice(-visibleChars);
|
|
113
|
+
return `${start}...${end}`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Mask secrets in a string by applying all active secret patterns
|
|
117
|
+
* and masking values of known sensitive environment variable keys.
|
|
118
|
+
*
|
|
119
|
+
* @param input - The value to mask (any type — converted to string)
|
|
120
|
+
* @returns String with secrets replaced by masked versions
|
|
121
|
+
*/
|
|
122
|
+
export function maskSecrets(input) {
|
|
123
|
+
if (input === null || input === undefined) {
|
|
124
|
+
return String(input);
|
|
125
|
+
}
|
|
126
|
+
let result = String(input);
|
|
127
|
+
// 1. First mask values of known sensitive environment variable keys
|
|
128
|
+
// Matches: KEY=value, KEY="value", KEY: value, KEY: "value"
|
|
129
|
+
result = result.replace(/\b([A-Z_][A-Z0-9_]{2,})(\s*[:=]\s*)(["']?)([^\s"']{4,})\3/g, (_match, key, separator, quote, _value) => {
|
|
130
|
+
if (SENSITIVE_ENV_KEYS.has(key.toUpperCase())) {
|
|
131
|
+
return `${key}${separator}${quote}***${quote}`;
|
|
132
|
+
}
|
|
133
|
+
return _match;
|
|
134
|
+
});
|
|
135
|
+
// 2. Then apply built-in and user-added secret patterns
|
|
136
|
+
for (const { pattern } of activePatterns) {
|
|
137
|
+
// Create fresh regex to avoid lastIndex issues from global flag
|
|
138
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
139
|
+
result = result.replace(regex, (match) => maskValue(match));
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Add a custom secret pattern to the masking rules.
|
|
145
|
+
*
|
|
146
|
+
* @param pattern - Regex pattern to match secrets
|
|
147
|
+
* @param name - Human-readable name for the pattern
|
|
148
|
+
*/
|
|
149
|
+
export function addSecretPattern(pattern, name = 'custom') {
|
|
150
|
+
activePatterns.push({ pattern, name });
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reset all secret patterns to the built-in defaults.
|
|
154
|
+
* Removes any patterns added via `addSecretPattern()`.
|
|
155
|
+
*/
|
|
156
|
+
export function resetSecretPatterns() {
|
|
157
|
+
activePatterns = [...DEFAULT_SECRET_PATTERNS];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get the list of currently active secret patterns.
|
|
161
|
+
*/
|
|
162
|
+
export function getSecretPatterns() {
|
|
163
|
+
return [...activePatterns];
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get the set of sensitive environment variable key names.
|
|
167
|
+
*/
|
|
168
|
+
export function getSensitiveEnvKeys() {
|
|
169
|
+
return SENSITIVE_ENV_KEYS;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=masking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"masking.js","sourceRoot":"","sources":["../../src/utils/masking.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slbdn/mcp-tester",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Minimal MCP client implementation for CI/CD testing of MCP servers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "tsc",
|
|
32
|
+
"dev": "tsc --watch",
|
|
32
33
|
"docs": "typedoc",
|
|
33
34
|
"test": "jest",
|
|
34
35
|
"test:watch": "jest --watch",
|
|
35
36
|
"test:coverage": "jest --coverage",
|
|
37
|
+
"benchmark": "npx tsx src/__tests__/benchmarks.ts 2>/dev/null",
|
|
36
38
|
"lint": "eslint src --ext .ts",
|
|
37
39
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
38
40
|
"format:check": "prettier --check \"src/**/*.ts\"",
|