@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.
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAYH;;;GAGG;AACH,MAAM,kBAAkB,GAAwB,IAAI,GAAG,CAAC;IACtD,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,QAAQ;IACR,aAAa;IACb,YAAY;IACZ,cAAc;IACd,eAAe;IACf,YAAY;IACZ,eAAe;IACf,cAAc;IACd,OAAO;IACP,eAAe;IACf,eAAe;IACf,YAAY;IACZ,UAAU;IACV,UAAU;IACV,QAAQ;IACR,aAAa;IACb,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,QAAQ;IACR,aAAa;IACb,cAAc;IACd,WAAW;IACX,WAAW;IACX,mBAAmB;IACnB,uBAAuB;IACvB,mBAAmB;IACnB,qBAAqB;IACrB,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,mBAAmB;IACnB,cAAc;IACd,cAAc;IACd,WAAW;IACX,gBAAgB;IAChB,aAAa;IACb,eAAe;IACf,YAAY;IACZ,gBAAgB;IAChB,aAAa;IACb,YAAY;IACZ,aAAa;IACb,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,uCAAuC,CAAC;AACjE,MAAM,YAAY,GAAG,wBAAwB,CAAC;AAC9C,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAC9C,MAAM,YAAY,GAAG,iCAAiC,CAAC;AACvD,MAAM,SAAS,GAAG,0BAA0B,CAAC;AAC7C,MAAM,SAAS,GAAG,oBAAoB,CAAC;AACvC,MAAM,eAAe,GAAG,qCAAqC,CAAC;AAC9D,MAAM,QAAQ,GACZ,qGAAqG,CAAC;AAExG;;GAEG;AACH,MAAM,uBAAuB,GAAoB;IAC/C,EAAE,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,0BAA0B,EAAE;IAC/D,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE;IAChD,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,EAAE;IAClD,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE;IAC/C,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE;IACzC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE;IACzC,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,iBAAiB,EAAE;IACrD,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,kBAAkB,EAAE;CAChD,CAAC;AAEF,sDAAsD;AACtD,IAAI,cAAc,GAAoB,CAAC,GAAG,uBAAuB,CAAC,CAAC;AAEnE,sEAAsE;AACtE,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,eAAuB,aAAa;IAC3E,IAAI,YAAY,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9D,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,IAAI,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAE3B,oEAAoE;IACpE,+DAA+D;IAC/D,MAAM,GAAG,MAAM,CAAC,OAAO,CACrB,4DAA4D,EAC5D,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACxC,IAAI,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC9C,OAAO,GAAG,GAAG,GAAG,SAAS,GAAG,KAAK,MAAM,KAAK,EAAE,CAAC;QACjD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CACF,CAAC;IAEF,wDAAwD;IACxD,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,cAAc,EAAE,CAAC;QACzC,gEAAgE;QAChE,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,OAAe,QAAQ;IACvE,cAAc,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,cAAc,GAAG,CAAC,GAAG,uBAAuB,CAAC,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,kBAAkB,CAAC;AAC5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slbdn/mcp-tester",
3
- "version": "1.1.0",
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\"",