@dynamicu/chromedebug-mcp 2.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/CLAUDE.md +344 -0
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/chrome-extension/README.md +41 -0
- package/chrome-extension/background.js +3917 -0
- package/chrome-extension/chrome-session-manager.js +706 -0
- package/chrome-extension/content.css +181 -0
- package/chrome-extension/content.js +3022 -0
- package/chrome-extension/data-buffer.js +435 -0
- package/chrome-extension/dom-tracker.js +411 -0
- package/chrome-extension/extension-config.js +78 -0
- package/chrome-extension/firebase-client.js +278 -0
- package/chrome-extension/firebase-config.js +32 -0
- package/chrome-extension/firebase-config.module.js +22 -0
- package/chrome-extension/firebase-config.module.template.js +27 -0
- package/chrome-extension/firebase-config.template.js +36 -0
- package/chrome-extension/frame-capture.js +407 -0
- package/chrome-extension/icon128.png +1 -0
- package/chrome-extension/icon16.png +1 -0
- package/chrome-extension/icon48.png +1 -0
- package/chrome-extension/license-helper.js +181 -0
- package/chrome-extension/logger.js +23 -0
- package/chrome-extension/manifest.json +73 -0
- package/chrome-extension/network-tracker.js +510 -0
- package/chrome-extension/offscreen.html +10 -0
- package/chrome-extension/options.html +203 -0
- package/chrome-extension/options.js +282 -0
- package/chrome-extension/pako.min.js +2 -0
- package/chrome-extension/performance-monitor.js +533 -0
- package/chrome-extension/pii-redactor.js +405 -0
- package/chrome-extension/popup.html +532 -0
- package/chrome-extension/popup.js +2446 -0
- package/chrome-extension/upload-manager.js +323 -0
- package/chrome-extension/web-vitals.iife.js +1 -0
- package/config/api-keys.json +11 -0
- package/config/chrome-pilot-config.json +45 -0
- package/package.json +126 -0
- package/scripts/cleanup-processes.js +109 -0
- package/scripts/config-manager.js +280 -0
- package/scripts/generate-extension-config.js +53 -0
- package/scripts/setup-security.js +64 -0
- package/src/capture/architecture.js +426 -0
- package/src/capture/error-handling-tests.md +38 -0
- package/src/capture/error-handling-types.ts +360 -0
- package/src/capture/index.js +508 -0
- package/src/capture/interfaces.js +625 -0
- package/src/capture/memory-manager.js +713 -0
- package/src/capture/types.js +342 -0
- package/src/chrome-controller.js +2658 -0
- package/src/cli.js +19 -0
- package/src/config-loader.js +303 -0
- package/src/database.js +2178 -0
- package/src/firebase-license-manager.js +462 -0
- package/src/firebase-privacy-guard.js +397 -0
- package/src/http-server.js +1516 -0
- package/src/index-direct.js +157 -0
- package/src/index-modular.js +219 -0
- package/src/index-monolithic-backup.js +2230 -0
- package/src/index.js +305 -0
- package/src/legacy/chrome-controller-old.js +1406 -0
- package/src/legacy/index-express.js +625 -0
- package/src/legacy/index-old.js +977 -0
- package/src/legacy/routes.js +260 -0
- package/src/legacy/shared-storage.js +101 -0
- package/src/logger.js +10 -0
- package/src/mcp/handlers/chrome-tool-handler.js +306 -0
- package/src/mcp/handlers/element-tool-handler.js +51 -0
- package/src/mcp/handlers/frame-tool-handler.js +957 -0
- package/src/mcp/handlers/request-handler.js +104 -0
- package/src/mcp/handlers/workflow-tool-handler.js +636 -0
- package/src/mcp/server.js +68 -0
- package/src/mcp/tools/index.js +701 -0
- package/src/middleware/auth.js +371 -0
- package/src/middleware/security.js +267 -0
- package/src/port-discovery.js +258 -0
- package/src/routes/admin.js +182 -0
- package/src/services/browser-daemon.js +494 -0
- package/src/services/chrome-service.js +375 -0
- package/src/services/failover-manager.js +412 -0
- package/src/services/git-safety-service.js +675 -0
- package/src/services/heartbeat-manager.js +200 -0
- package/src/services/http-client.js +195 -0
- package/src/services/process-manager.js +318 -0
- package/src/services/process-tracker.js +574 -0
- package/src/services/profile-manager.js +449 -0
- package/src/services/project-manager.js +415 -0
- package/src/services/session-manager.js +497 -0
- package/src/services/session-registry.js +491 -0
- package/src/services/unified-session-manager.js +678 -0
- package/src/shared-storage-old.js +267 -0
- package/src/standalone-server.js +53 -0
- package/src/utils/extension-path.js +145 -0
- package/src/utils.js +187 -0
- package/src/validation/log-transformer.js +125 -0
- package/src/validation/schemas.js +391 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Privacy Guard
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL SECURITY MODULE - Prevents sensitive recording data from being sent to Firebase
|
|
5
|
+
*
|
|
6
|
+
* This module acts as a gatekeeper to ensure that ONLY licensing-related data
|
|
7
|
+
* is transmitted to Firebase. Any attempt to send recording data will be blocked
|
|
8
|
+
* and logged as a security violation.
|
|
9
|
+
*
|
|
10
|
+
* STRICT PRIVACY RULES:
|
|
11
|
+
* 1. Only whitelisted fields are allowed through
|
|
12
|
+
* 2. Any forbidden field triggers immediate rejection
|
|
13
|
+
* 3. All violations are logged for audit
|
|
14
|
+
* 4. No recording, frame, DOM, or network data ever leaves the local system
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whitelist of allowed fields for Firebase transmission
|
|
19
|
+
* ONLY these fields are permitted in any Firebase operation
|
|
20
|
+
*/
|
|
21
|
+
const ALLOWED_FIELDS = new Set([
|
|
22
|
+
// User identification
|
|
23
|
+
'userId',
|
|
24
|
+
'email',
|
|
25
|
+
'displayName',
|
|
26
|
+
|
|
27
|
+
// License data
|
|
28
|
+
'licenseKey',
|
|
29
|
+
'tier',
|
|
30
|
+
'status',
|
|
31
|
+
'expiresAt',
|
|
32
|
+
'createdAt',
|
|
33
|
+
'updatedAt',
|
|
34
|
+
|
|
35
|
+
// Instance tracking (for anti-sharing)
|
|
36
|
+
'instanceId',
|
|
37
|
+
'deviceName',
|
|
38
|
+
'platform',
|
|
39
|
+
'lastSeen',
|
|
40
|
+
|
|
41
|
+
// Usage metrics (counts only, no content)
|
|
42
|
+
'count',
|
|
43
|
+
'timestamp',
|
|
44
|
+
'date',
|
|
45
|
+
'usageCount',
|
|
46
|
+
'dailyUsage',
|
|
47
|
+
|
|
48
|
+
// Metadata (generic container fields)
|
|
49
|
+
'id',
|
|
50
|
+
'type',
|
|
51
|
+
'version',
|
|
52
|
+
'metadata'
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Forbidden fields that indicate recording/sensitive data
|
|
57
|
+
* NEVER allowed in Firebase operations
|
|
58
|
+
*/
|
|
59
|
+
const FORBIDDEN_FIELDS = new Set([
|
|
60
|
+
// Recording data
|
|
61
|
+
'recordings',
|
|
62
|
+
'frames',
|
|
63
|
+
'frameData',
|
|
64
|
+
'imageData',
|
|
65
|
+
'screenshot',
|
|
66
|
+
'video',
|
|
67
|
+
|
|
68
|
+
// Console and logs
|
|
69
|
+
'consoleLogs',
|
|
70
|
+
'logs',
|
|
71
|
+
'errorLogs',
|
|
72
|
+
'debugLogs',
|
|
73
|
+
|
|
74
|
+
// DOM and page content
|
|
75
|
+
'domSnapshots',
|
|
76
|
+
'dom',
|
|
77
|
+
'html',
|
|
78
|
+
'content',
|
|
79
|
+
'pageContent',
|
|
80
|
+
'domMutations',
|
|
81
|
+
|
|
82
|
+
// Network data
|
|
83
|
+
'networkRequests',
|
|
84
|
+
'requests',
|
|
85
|
+
'responses',
|
|
86
|
+
'headers',
|
|
87
|
+
'body',
|
|
88
|
+
'payload',
|
|
89
|
+
|
|
90
|
+
// Browser state
|
|
91
|
+
'cookies',
|
|
92
|
+
'localStorage',
|
|
93
|
+
'sessionStorage',
|
|
94
|
+
'storage',
|
|
95
|
+
|
|
96
|
+
// User interactions
|
|
97
|
+
'interactions',
|
|
98
|
+
'clicks',
|
|
99
|
+
'inputs',
|
|
100
|
+
'keystrokes',
|
|
101
|
+
|
|
102
|
+
// Code and execution
|
|
103
|
+
'code',
|
|
104
|
+
'script',
|
|
105
|
+
'function',
|
|
106
|
+
'execution',
|
|
107
|
+
'trace',
|
|
108
|
+
'stack',
|
|
109
|
+
|
|
110
|
+
// Sensitive metadata
|
|
111
|
+
'url',
|
|
112
|
+
'path',
|
|
113
|
+
'query',
|
|
114
|
+
'params'
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Privacy violation tracker
|
|
119
|
+
*/
|
|
120
|
+
class PrivacyViolationLogger {
|
|
121
|
+
constructor() {
|
|
122
|
+
this.violations = [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
log(violation) {
|
|
126
|
+
const entry = {
|
|
127
|
+
timestamp: new Date().toISOString(),
|
|
128
|
+
...violation
|
|
129
|
+
};
|
|
130
|
+
this.violations.push(entry);
|
|
131
|
+
console.error('[PRIVACY VIOLATION]', JSON.stringify(entry, null, 2));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getViolations() {
|
|
135
|
+
return this.violations;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
clear() {
|
|
139
|
+
this.violations = [];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const violationLogger = new PrivacyViolationLogger();
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Firebase Privacy Guard Class
|
|
147
|
+
*/
|
|
148
|
+
export class FirebasePrivacyGuard {
|
|
149
|
+
/**
|
|
150
|
+
* Sanitize data before Firebase transmission
|
|
151
|
+
* Removes any non-whitelisted fields and validates against forbidden fields
|
|
152
|
+
*
|
|
153
|
+
* @param {Object} data - Data to sanitize
|
|
154
|
+
* @param {string} context - Context of the operation (for logging)
|
|
155
|
+
* @returns {Object} - Sanitized data with only allowed fields
|
|
156
|
+
* @throws {Error} - If forbidden fields are detected
|
|
157
|
+
*/
|
|
158
|
+
static sanitize(data, context = 'unknown') {
|
|
159
|
+
if (!data || typeof data !== 'object') {
|
|
160
|
+
return data;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Handle arrays
|
|
164
|
+
if (Array.isArray(data)) {
|
|
165
|
+
return data.map(item => this.sanitize(item, context));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const sanitized = {};
|
|
169
|
+
const violations = [];
|
|
170
|
+
|
|
171
|
+
// First pass: check for forbidden fields anywhere in the tree
|
|
172
|
+
const checkForbidden = (obj, path = '') => {
|
|
173
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
174
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
175
|
+
|
|
176
|
+
if (FORBIDDEN_FIELDS.has(key)) {
|
|
177
|
+
violations.push({
|
|
178
|
+
field: fullPath,
|
|
179
|
+
type: 'FORBIDDEN_FIELD',
|
|
180
|
+
context,
|
|
181
|
+
severity: 'CRITICAL'
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check nested objects
|
|
186
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
187
|
+
checkForbidden(value, fullPath);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check arrays
|
|
191
|
+
if (Array.isArray(value)) {
|
|
192
|
+
value.forEach((item, index) => {
|
|
193
|
+
if (item && typeof item === 'object') {
|
|
194
|
+
checkForbidden(item, `${fullPath}[${index}]`);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
checkForbidden(data);
|
|
202
|
+
|
|
203
|
+
// If forbidden fields found, throw immediately
|
|
204
|
+
const criticalViolations = violations.filter(v => v.severity === 'CRITICAL');
|
|
205
|
+
if (criticalViolations.length > 0) {
|
|
206
|
+
violationLogger.log({
|
|
207
|
+
operation: 'sanitize',
|
|
208
|
+
context,
|
|
209
|
+
violations,
|
|
210
|
+
originalFields: Object.keys(data),
|
|
211
|
+
sanitizedFields: []
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
throw new Error(
|
|
215
|
+
`Privacy violation: Forbidden fields detected in Firebase operation: ${
|
|
216
|
+
criticalViolations.map(v => v.field).join(', ')
|
|
217
|
+
}`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Second pass: build sanitized object with only allowed fields
|
|
222
|
+
for (const [key, value] of Object.entries(data)) {
|
|
223
|
+
// Only include whitelisted fields
|
|
224
|
+
if (ALLOWED_FIELDS.has(key)) {
|
|
225
|
+
// Recursively sanitize nested objects
|
|
226
|
+
if (value && typeof value === 'object') {
|
|
227
|
+
sanitized[key] = this.sanitize(value, `${context}.${key}`);
|
|
228
|
+
} else {
|
|
229
|
+
sanitized[key] = value;
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
// Non-whitelisted field (not forbidden, just unknown)
|
|
233
|
+
violations.push({
|
|
234
|
+
field: key,
|
|
235
|
+
type: 'NON_WHITELISTED',
|
|
236
|
+
context,
|
|
237
|
+
severity: 'WARNING'
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Log violations
|
|
243
|
+
if (violations.length > 0) {
|
|
244
|
+
violationLogger.log({
|
|
245
|
+
operation: 'sanitize',
|
|
246
|
+
context,
|
|
247
|
+
violations,
|
|
248
|
+
originalFields: Object.keys(data),
|
|
249
|
+
sanitizedFields: Object.keys(sanitized)
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Throw error if critical violations detected
|
|
253
|
+
const criticalViolations = violations.filter(v => v.severity === 'CRITICAL');
|
|
254
|
+
if (criticalViolations.length > 0) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Privacy violation: Forbidden fields detected in Firebase operation: ${
|
|
257
|
+
criticalViolations.map(v => v.field).join(', ')
|
|
258
|
+
}`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return sanitized;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Validate data before Firebase transmission
|
|
268
|
+
* Does NOT modify data, only validates
|
|
269
|
+
*
|
|
270
|
+
* @param {Object} data - Data to validate
|
|
271
|
+
* @param {string} context - Context of the operation
|
|
272
|
+
* @returns {Object} - Validation result
|
|
273
|
+
*/
|
|
274
|
+
static validate(data, context = 'unknown') {
|
|
275
|
+
if (!data || typeof data !== 'object') {
|
|
276
|
+
return { valid: true, violations: [] };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const violations = [];
|
|
280
|
+
const checkObject = (obj, path = '') => {
|
|
281
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
282
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
283
|
+
|
|
284
|
+
// Check for forbidden fields
|
|
285
|
+
if (FORBIDDEN_FIELDS.has(key)) {
|
|
286
|
+
violations.push({
|
|
287
|
+
field: fullPath,
|
|
288
|
+
type: 'FORBIDDEN_FIELD',
|
|
289
|
+
severity: 'CRITICAL'
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Check nested objects
|
|
294
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
295
|
+
checkObject(value, fullPath);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Check arrays
|
|
299
|
+
if (Array.isArray(value)) {
|
|
300
|
+
value.forEach((item, index) => {
|
|
301
|
+
if (item && typeof item === 'object') {
|
|
302
|
+
checkObject(item, `${fullPath}[${index}]`);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
checkObject(data);
|
|
310
|
+
|
|
311
|
+
if (violations.length > 0) {
|
|
312
|
+
violationLogger.log({
|
|
313
|
+
operation: 'validate',
|
|
314
|
+
context,
|
|
315
|
+
violations
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
valid: violations.filter(v => v.severity === 'CRITICAL').length === 0,
|
|
321
|
+
violations,
|
|
322
|
+
hasCriticalViolations: violations.some(v => v.severity === 'CRITICAL'),
|
|
323
|
+
hasWarnings: violations.some(v => v.severity === 'WARNING')
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Assert that data is safe for Firebase
|
|
329
|
+
* Throws error if validation fails
|
|
330
|
+
*
|
|
331
|
+
* @param {Object} data - Data to check
|
|
332
|
+
* @param {string} context - Context of the operation
|
|
333
|
+
*/
|
|
334
|
+
static assertSafe(data, context = 'unknown') {
|
|
335
|
+
const validation = this.validate(data, context);
|
|
336
|
+
|
|
337
|
+
if (!validation.valid) {
|
|
338
|
+
const criticalFields = validation.violations
|
|
339
|
+
.filter(v => v.severity === 'CRITICAL')
|
|
340
|
+
.map(v => v.field)
|
|
341
|
+
.join(', ');
|
|
342
|
+
|
|
343
|
+
throw new Error(
|
|
344
|
+
`Firebase Privacy Guard: Forbidden data detected in ${context}. ` +
|
|
345
|
+
`Attempted to send: ${criticalFields}. ` +
|
|
346
|
+
`This operation has been blocked for security.`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get privacy violation history
|
|
355
|
+
*/
|
|
356
|
+
static getViolations() {
|
|
357
|
+
return violationLogger.getViolations();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Clear violation history (for testing)
|
|
362
|
+
*/
|
|
363
|
+
static clearViolations() {
|
|
364
|
+
violationLogger.clear();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Check if a field is allowed
|
|
369
|
+
*/
|
|
370
|
+
static isAllowedField(fieldName) {
|
|
371
|
+
return ALLOWED_FIELDS.has(fieldName);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Check if a field is forbidden
|
|
376
|
+
*/
|
|
377
|
+
static isForbiddenField(fieldName) {
|
|
378
|
+
return FORBIDDEN_FIELDS.has(fieldName);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Get list of allowed fields
|
|
383
|
+
*/
|
|
384
|
+
static getAllowedFields() {
|
|
385
|
+
return Array.from(ALLOWED_FIELDS);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Get list of forbidden fields
|
|
390
|
+
*/
|
|
391
|
+
static getForbiddenFields() {
|
|
392
|
+
return Array.from(FORBIDDEN_FIELDS);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Export for testing
|
|
397
|
+
export { ALLOWED_FIELDS, FORBIDDEN_FIELDS, violationLogger };
|