@heroku/js-blanket 0.0.0 → 1.0.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.
Files changed (82) hide show
  1. package/README.md +4 -1
  2. package/dist/cjs/.tsbuildinfo +1 -0
  3. package/dist/cjs/adapters/logging/generic.js +23 -0
  4. package/dist/cjs/adapters/logging/generic.js.map +1 -0
  5. package/dist/cjs/adapters/logging/generic.test.js +432 -0
  6. package/dist/cjs/adapters/logging/generic.test.js.map +1 -0
  7. package/dist/cjs/core/patterns.js +17 -0
  8. package/dist/cjs/core/patterns.js.map +1 -0
  9. package/dist/cjs/core/presets.js +116 -0
  10. package/dist/cjs/core/presets.js.map +1 -0
  11. package/dist/cjs/core/scrubber.js +260 -0
  12. package/dist/cjs/core/scrubber.js.map +1 -0
  13. package/dist/cjs/core/scrubber.test.js +392 -0
  14. package/dist/cjs/core/scrubber.test.js.map +1 -0
  15. package/dist/cjs/core/types.js +3 -0
  16. package/dist/cjs/core/types.js.map +1 -0
  17. package/dist/cjs/core/types.test.js +326 -0
  18. package/dist/cjs/core/types.test.js.map +1 -0
  19. package/dist/cjs/index.js +16 -0
  20. package/dist/cjs/index.js.map +1 -0
  21. package/dist/cjs/index.test.js +31 -0
  22. package/dist/cjs/index.test.js.map +1 -0
  23. package/dist/cjs/package.json +1 -0
  24. package/dist/esm/.tsbuildinfo +1 -0
  25. package/{src/adapters/logging/generic.ts → dist/esm/adapters/logging/generic.d.ts} +1 -4
  26. package/dist/esm/adapters/logging/generic.js +20 -0
  27. package/dist/esm/adapters/logging/generic.js.map +1 -0
  28. package/dist/esm/adapters/logging/generic.test.d.ts +7 -0
  29. package/dist/esm/adapters/logging/generic.test.js +430 -0
  30. package/dist/esm/adapters/logging/generic.test.js.map +1 -0
  31. package/dist/esm/core/patterns.d.ts +4 -0
  32. package/dist/esm/core/patterns.js +14 -0
  33. package/dist/esm/core/patterns.js.map +1 -0
  34. package/dist/esm/core/presets.d.ts +64 -0
  35. package/{src/core/presets.ts → dist/esm/core/presets.js} +46 -55
  36. package/dist/esm/core/presets.js.map +1 -0
  37. package/dist/esm/core/scrubber.d.ts +131 -0
  38. package/dist/esm/core/scrubber.js +256 -0
  39. package/dist/esm/core/scrubber.js.map +1 -0
  40. package/dist/esm/core/scrubber.test.d.ts +1 -0
  41. package/dist/esm/core/scrubber.test.js +390 -0
  42. package/dist/esm/core/scrubber.test.js.map +1 -0
  43. package/dist/esm/core/types.d.ts +169 -0
  44. package/dist/esm/core/types.js +2 -0
  45. package/dist/esm/core/types.js.map +1 -0
  46. package/dist/esm/core/types.test.d.ts +9 -0
  47. package/dist/esm/core/types.test.js +324 -0
  48. package/dist/esm/core/types.test.js.map +1 -0
  49. package/{src/index.ts → dist/esm/index.d.ts} +0 -3
  50. package/dist/esm/index.js +7 -0
  51. package/dist/esm/index.js.map +1 -0
  52. package/dist/esm/index.test.d.ts +1 -0
  53. package/dist/esm/index.test.js +29 -0
  54. package/dist/esm/index.test.js.map +1 -0
  55. package/package.json +45 -47
  56. package/.c8rc.json +0 -11
  57. package/.editorconfig +0 -11
  58. package/.github/PULL_REQUEST_TEMPLATE.md +0 -41
  59. package/.github/copilot-instructions.md +0 -117
  60. package/.github/workflows/ci.yml +0 -25
  61. package/.husky/pre-commit +0 -1
  62. package/.lintstagedrc.json +0 -4
  63. package/.tool-versions +0 -1
  64. package/CODEOWNERS +0 -8
  65. package/CODE_OF_CONDUCT.md +0 -111
  66. package/CONTRIBUTING.md +0 -123
  67. package/SECURITY.md +0 -8
  68. package/docs/examples/logging-integration.md +0 -736
  69. package/eslint.config.mjs +0 -108
  70. package/prettier.config.mjs +0 -10
  71. package/scripts/test-setup.mjs +0 -24
  72. package/src/adapters/logging/generic.test.ts +0 -531
  73. package/src/core/patterns.ts +0 -22
  74. package/src/core/scrubber.test.ts +0 -465
  75. package/src/core/scrubber.ts +0 -284
  76. package/src/core/types.test.ts +0 -516
  77. package/src/core/types.ts +0 -176
  78. package/src/index.test.ts +0 -41
  79. package/tsconfig.cjs.json +0 -12
  80. package/tsconfig.esm.json +0 -12
  81. package/tsconfig.json +0 -32
  82. package/tsconfig.test.json +0 -9
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PII_PATTERNS = void 0;
4
+ /**
5
+ * Regex patterns for detecting PII in string content
6
+ */
7
+ exports.PII_PATTERNS = [
8
+ // Social Security Numbers (US)
9
+ /\b\d{3}-\d{2}-\d{4}\b/g,
10
+ // Email addresses
11
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
12
+ // Phone numbers (US format)
13
+ /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g,
14
+ // JWT tokens
15
+ /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
16
+ ];
17
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/core/patterns.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,YAAY,GAAG;IAC1B,+BAA+B;IAC/B,wBAAwB;IAExB,kBAAkB;IAClB,sDAAsD;IAEtD,4BAA4B;IAC5B,gCAAgC;IAEhC,aAAa;IACb,2DAA2D;CAC5D,CAAC"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PCI_FIELDS = exports.GDPR_FIELDS = exports.HEROKU_FIELDS = void 0;
4
+ /**
5
+ * Heroku-specific sensitive field patterns
6
+ *
7
+ * Consolidated list of field names and patterns that contain sensitive data in Heroku applications.
8
+ *
9
+ * Use this preset to ensure consistent PII handling across Heroku services.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { HEROKU_FIELDS } from '@heroku/js-blanket/core/presets';
14
+ * import { Scrubber } from '@heroku/js-blanket';
15
+ *
16
+ * const scrubber = new Scrubber({ fields: HEROKU_FIELDS });
17
+ * const result = scrubber.scrub(data);
18
+ * ```
19
+ */
20
+ exports.HEROKU_FIELDS = [
21
+ // Authentication & Sessions
22
+ 'access_token',
23
+ /api[-_]?key/i, // Matches api_key, api-key, apikey (case insensitive)
24
+ 'authenticity_token',
25
+ 'heroku_oauth_token',
26
+ 'heroku_session_nonce',
27
+ 'heroku_user_session',
28
+ 'oauth_token',
29
+ 'sudo_oauth_token',
30
+ 'super_user_session_secret',
31
+ 'user_session_secret',
32
+ 'postgres_session_nonce',
33
+ // Passwords & Secrets
34
+ 'password',
35
+ 'passwd',
36
+ 'old_secret',
37
+ 'secret',
38
+ 'secret_token',
39
+ 'confirm_password',
40
+ 'password_confirmation',
41
+ /client[-_]?secret/i, // Matches client_secret, client-secret, clientsecret
42
+ // Tokens
43
+ 'token',
44
+ 'bouncer.token',
45
+ 'bouncer.refresh_token',
46
+ // Headers (case-insensitive)
47
+ /authorization/i,
48
+ /cookie/i,
49
+ /x-refresh-token/i,
50
+ // SSO & Sessions
51
+ 'www-sso-session',
52
+ // Payment
53
+ 'payment_method',
54
+ // Infrastructure
55
+ 'logplexUrl',
56
+ ];
57
+ /**
58
+ * GDPR-relevant PII field patterns
59
+ *
60
+ * Field names that typically contain personally identifiable information (PII)
61
+ * regulated by GDPR (General Data Protection Regulation).
62
+ *
63
+ * Use this preset when handling EU user data to ensure compliance with GDPR requirements.
64
+ *
65
+ * @see {@link https://gdpr.eu/what-is-gdpr/|GDPR Official Documentation}
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import { GDPR_FIELDS, HEROKU_FIELDS } from '@heroku/js-blanket/core/presets';
70
+ * import { Scrubber } from '@heroku/js-blanket';
71
+ *
72
+ * // Combine multiple presets
73
+ * const scrubber = new Scrubber({
74
+ * fields: [...HEROKU_FIELDS, ...GDPR_FIELDS]
75
+ * });
76
+ * ```
77
+ */
78
+ exports.GDPR_FIELDS = [
79
+ 'email',
80
+ 'phone',
81
+ 'address',
82
+ 'postal_code',
83
+ 'ssn',
84
+ 'tax_id',
85
+ ];
86
+ /**
87
+ * PCI-DSS relevant field patterns
88
+ *
89
+ * Field names that typically contain payment card information regulated by
90
+ * PCI-DSS (Payment Card Industry Data Security Standard).
91
+ *
92
+ * Use this preset when handling payment card data to help maintain PCI-DSS compliance.
93
+ *
94
+ * **Important**: This preset helps reduce exposure of sensitive payment data in logs and
95
+ * error reports, but is not a substitute for full PCI-DSS compliance measures.
96
+ *
97
+ * @see {@link https://www.pcisecuritystandards.org/|PCI Security Standards Council}
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * import { PCI_FIELDS } from '@heroku/js-blanket/core/presets';
102
+ * import { Scrubber } from '@heroku/js-blanket';
103
+ *
104
+ * const scrubber = new Scrubber({
105
+ * fields: PCI_FIELDS,
106
+ * patterns: [/\d{4}-\d{4}-\d{4}-\d{4}/g] // Also scrub card numbers in text
107
+ * });
108
+ * ```
109
+ */
110
+ exports.PCI_FIELDS = [
111
+ 'card_number',
112
+ 'cvv',
113
+ 'credit_card',
114
+ 'payment_method',
115
+ ];
116
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../../src/core/presets.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;GAeG;AACU,QAAA,aAAa,GAAG;IAC3B,4BAA4B;IAC5B,cAAc;IACd,cAAc,EAAE,sDAAsD;IACtE,oBAAoB;IACpB,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,aAAa;IACb,kBAAkB;IAClB,2BAA2B;IAC3B,qBAAqB;IACrB,wBAAwB;IAExB,sBAAsB;IACtB,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,cAAc;IACd,kBAAkB;IAClB,uBAAuB;IACvB,oBAAoB,EAAE,qDAAqD;IAE3E,SAAS;IACT,OAAO;IACP,eAAe;IACf,uBAAuB;IAEvB,6BAA6B;IAC7B,gBAAgB;IAChB,SAAS;IACT,kBAAkB;IAElB,iBAAiB;IACjB,iBAAiB;IAEjB,UAAU;IACV,gBAAgB;IAEhB,iBAAiB;IACjB,YAAY;CACb,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACU,QAAA,WAAW,GAAG;IACzB,OAAO;IACP,OAAO;IACP,SAAS;IACT,aAAa;IACb,KAAK;IACL,QAAQ;CACT,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACU,QAAA,UAAU,GAAG;IACxB,aAAa;IACb,KAAK;IACL,aAAa;IACb,gBAAgB;CACjB,CAAC"}
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Scrubber = void 0;
4
+ /**
5
+ * Core Scrubber - Deep object traversal with PII scrubbing
6
+ *
7
+ * A high-performance, immutable scrubbing engine that removes sensitive data from structured objects.
8
+ * Supports three scrubbing modes:
9
+ * - **Field-based**: Scrubs values based on field names (e.g., 'password', 'apiToken')
10
+ * - **Path-based**: Scrubs values at specific paths (e.g., 'user.email', 'request.headers.authorization')
11
+ * - **Pattern-based**: Scrubs content matching regex patterns (e.g., SSN, credit cards)
12
+ *
13
+ * ### Design Principles
14
+ * - **Immutable**: All operations create new objects, never mutate inputs
15
+ * - **Type-safe**: Preserves TypeScript types through generic constraints
16
+ * - **Circular-safe**: Handles circular references without crashing
17
+ * - **Performance**: <1ms p95 for logging, <10ms p95 for exception handling (544k+ ops/sec)
18
+ *
19
+ * ### Pattern Adoption
20
+ * Patterns adopted from `@heroku/oauth-provider-adapters-for-mcp/src/logging/redaction.ts`:
21
+ * - Deep recursive traversal with circular reference detection
22
+ * - Immutable cloning strategy with fallback for complex objects
23
+ * - Nested path resolution (e.g., 'user.profile.email')
24
+ * - General array path handling (e.g., 'users[0].password')
25
+ * - Type-safe generics preserving input types
26
+ *
27
+ * Enhanced with:
28
+ * - Field-based matching supporting both strings and regular expressions
29
+ * - Pattern-based content scrubbing for SSN, credit cards, etc.
30
+ * - Dual scrubbing: both field/path matching AND content pattern replacement
31
+ *
32
+ * @example Basic Usage
33
+ * ```typescript
34
+ * const scrubber = new Scrubber({
35
+ * fields: ['password', 'apiToken'],
36
+ * replacement: '[REDACTED]'
37
+ * });
38
+ *
39
+ * const result = scrubber.scrub({
40
+ * user: { name: 'John', password: 'secret123' }
41
+ * });
42
+ * // Result: { user: { name: 'John', password: '[REDACTED]' } }
43
+ * ```
44
+ *
45
+ * @example Advanced Usage with All Modes
46
+ * ```typescript
47
+ * const scrubber = new Scrubber({
48
+ * fields: ['password', /api[-_]?key/i], // Regex matches api_key, api-key, apikey
49
+ * paths: ['user.email', 'request.headers.authorization'],
50
+ * patterns: [/\b\d{3}-\d{2}-\d{4}\b/g], // SSN pattern
51
+ * replacement: '[SCRUBBED]'
52
+ * });
53
+ *
54
+ * const result = scrubber.scrub({
55
+ * user: { name: 'John', email: 'john@example.com', password: 'secret' },
56
+ * request: { headers: { authorization: 'Bearer token123' } },
57
+ * message: 'User SSN is 123-45-6789'
58
+ * });
59
+ * ```
60
+ */
61
+ class Scrubber {
62
+ config;
63
+ circularRefs = new WeakSet();
64
+ pathSet;
65
+ /**
66
+ * Creates a new Scrubber instance with the specified configuration
67
+ *
68
+ * @param config - Scrubbing configuration
69
+ * @param config.fields - Field names to scrub (strings or regex patterns)
70
+ * @param config.paths - Dot-notation paths to scrub (e.g., 'user.email', 'items[0].password')
71
+ * @param config.patterns - Regex patterns for content scrubbing (must include global flag for multiple matches)
72
+ * @param config.replacement - Replacement string for scrubbed values (default: '[SCRUBBED]')
73
+ * @param config.recursive - Whether to recursively scrub nested objects (default: true)
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const scrubber = new Scrubber({
78
+ * fields: ['password', /api[-_]?key/i],
79
+ * paths: ['user.email'],
80
+ * patterns: [/\b\d{3}-\d{2}-\d{4}\b/g],
81
+ * replacement: '[REDACTED]'
82
+ * });
83
+ * ```
84
+ */
85
+ constructor(config) {
86
+ this.config = {
87
+ fields: config.fields || [],
88
+ paths: config.paths || [],
89
+ patterns: config.patterns || [],
90
+ replacement: config.replacement || '[SCRUBBED]',
91
+ recursive: config.recursive !== undefined ? config.recursive : true,
92
+ };
93
+ // Pre-compute path set for O(1) lookups
94
+ this.pathSet = new Set(this.config.paths);
95
+ }
96
+ /**
97
+ * Scrubs sensitive data from an object
98
+ *
99
+ * This is the main entry point for the scrubbing engine. It performs three types of scrubbing:
100
+ * 1. **Field-based**: Replaces values of fields matching configured field names/patterns
101
+ * 2. **Path-based**: Replaces values at specific dot-notation paths
102
+ * 3. **Pattern-based**: Replaces content within string values matching regex patterns
103
+ *
104
+ * The operation is immutable - the input object is not modified. A deep clone is created
105
+ * and scrubbed values are replaced in the clone.
106
+ *
107
+ * ### Performance Characteristics
108
+ * - Small objects (typical logs): ~0.003ms p95
109
+ * - Medium objects (typical errors): ~0.034ms p95
110
+ * - Large objects (10KB+): ~1.2ms p95
111
+ * - Throughput: 54,000+ events/sec
112
+ *
113
+ * @template T - The type of the input object (preserved in output)
114
+ * @param obj - The object to scrub
115
+ * @returns A result object containing the scrubbed data, whether scrubbing occurred, and which paths were scrubbed
116
+ *
117
+ * @example Basic scrubbing
118
+ * ```typescript
119
+ * const scrubber = new Scrubber({ fields: ['password'] });
120
+ * const result = scrubber.scrub({ user: 'john', password: 'secret' });
121
+ * // result.data === { user: 'john', password: '[SCRUBBED]' }
122
+ * // result.scrubbed === true
123
+ * // result.scrubbedPaths === ['password']
124
+ * ```
125
+ *
126
+ * @example Type preservation
127
+ * ```typescript
128
+ * interface User { name: string; email: string; password: string; }
129
+ * const scrubber = new Scrubber({ fields: ['password', 'email'] });
130
+ * const user: User = { name: 'John', email: 'john@example.com', password: 'secret' };
131
+ * const result = scrubber.scrub(user);
132
+ * // result.data is still typed as User
133
+ * ```
134
+ */
135
+ scrub(obj) {
136
+ const scrubbedPaths = [];
137
+ const cloned = this.deepClone(obj);
138
+ // Reset circular refs tracker for each scrub operation
139
+ this.circularRefs = new WeakSet();
140
+ const scrubbed = this.scrubObject(cloned, '', scrubbedPaths);
141
+ return {
142
+ data: scrubbed,
143
+ scrubbed: scrubbedPaths.length > 0,
144
+ scrubbedPaths,
145
+ };
146
+ }
147
+ scrubObject(obj, path, paths) {
148
+ // Handle circular references
149
+ if (obj && typeof obj === 'object') {
150
+ if (this.circularRefs.has(obj)) {
151
+ return '[Circular Reference]';
152
+ }
153
+ this.circularRefs.add(obj);
154
+ }
155
+ // Handle primitives
156
+ if (obj === null || typeof obj !== 'object') {
157
+ return this.scrubValue(obj, path, paths);
158
+ }
159
+ // Handle arrays
160
+ if (Array.isArray(obj)) {
161
+ return obj.map((item, index) => {
162
+ const indexStr = index.toString();
163
+ const arrayPath = path ? `${path}[${index}]` : indexStr;
164
+ // Check if this specific array index path should be scrubbed
165
+ if (this.pathSet.has(indexStr) || this.pathSet.has(arrayPath)) {
166
+ paths.push(arrayPath);
167
+ return this.config.replacement;
168
+ }
169
+ // Recursively scrub array items
170
+ return this.scrubObject(item, arrayPath, paths);
171
+ });
172
+ }
173
+ // Handle objects - create new object (immutable approach)
174
+ const result = {};
175
+ for (const [key, value] of Object.entries(obj)) {
176
+ const keyPath = path ? `${path}.${key}` : key;
177
+ // Check if this specific path should be scrubbed
178
+ if (this.pathSet.has(key) || this.pathSet.has(keyPath)) {
179
+ result[key] = this.config.replacement;
180
+ paths.push(keyPath);
181
+ continue;
182
+ }
183
+ // Check if key matches sensitive field pattern
184
+ if (this.isSensitiveField(key)) {
185
+ result[key] = this.config.replacement;
186
+ paths.push(keyPath);
187
+ continue;
188
+ }
189
+ // Recursively scrub value
190
+ result[key] = this.config.recursive
191
+ ? this.scrubObject(value, keyPath, paths)
192
+ : this.scrubValue(value, keyPath, paths);
193
+ }
194
+ return result;
195
+ }
196
+ scrubValue(value, path, paths) {
197
+ if (typeof value !== 'string') {
198
+ return value;
199
+ }
200
+ let scrubbed = value;
201
+ let didScrub = false;
202
+ // Check against patterns (SSN, credit cards, etc.)
203
+ for (const pattern of this.config.patterns) {
204
+ if (pattern.test(scrubbed)) {
205
+ scrubbed = scrubbed.replace(pattern, this.config.replacement);
206
+ didScrub = true;
207
+ }
208
+ }
209
+ if (didScrub) {
210
+ paths.push(path);
211
+ }
212
+ return scrubbed;
213
+ }
214
+ /**
215
+ * Check if a field name matches any configured sensitive field patterns
216
+ */
217
+ isSensitiveField(key) {
218
+ return this.config.fields.some((field) => {
219
+ if (field instanceof RegExp) {
220
+ return field.test(key);
221
+ }
222
+ return key.toLowerCase().includes(field.toLowerCase());
223
+ });
224
+ }
225
+ deepClone(obj) {
226
+ try {
227
+ // Fast path for JSON-serializable objects
228
+ return JSON.parse(JSON.stringify(obj));
229
+ }
230
+ catch {
231
+ // Fallback for objects with circular references
232
+ const seen = new WeakMap();
233
+ function clone(value) {
234
+ if (value === null || typeof value !== 'object') {
235
+ return value;
236
+ }
237
+ if (seen.has(value)) {
238
+ return seen.get(value);
239
+ }
240
+ if (Array.isArray(value)) {
241
+ const arr = [];
242
+ seen.set(value, arr);
243
+ value.forEach((item, i) => {
244
+ arr[i] = clone(item);
245
+ });
246
+ return arr;
247
+ }
248
+ const obj = {};
249
+ seen.set(value, obj);
250
+ Object.keys(value).forEach((key) => {
251
+ obj[key] = clone(value[key]);
252
+ });
253
+ return obj;
254
+ }
255
+ return clone(obj);
256
+ }
257
+ }
258
+ }
259
+ exports.Scrubber = Scrubber;
260
+ //# sourceMappingURL=scrubber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrubber.js","sourceRoot":"","sources":["../../../src/core/scrubber.ts"],"names":[],"mappings":"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"}