guardrail-ship 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 (119) hide show
  1. package/dist/index.d.ts +7 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +7 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/mock-implementation.d.ts +1 -0
  6. package/dist/mock-implementation.d.ts.map +1 -0
  7. package/dist/mock-implementation.js +2 -0
  8. package/dist/mock-implementation.js.map +1 -0
  9. package/dist/mockproof/__tests__/import-graph-scanner.test.d.ts +5 -0
  10. package/dist/mockproof/__tests__/import-graph-scanner.test.d.ts.map +1 -0
  11. package/dist/mockproof/__tests__/import-graph-scanner.test.js +92 -0
  12. package/dist/mockproof/__tests__/import-graph-scanner.test.js.map +1 -0
  13. package/dist/mockproof/import-graph-scanner.d.ts +93 -0
  14. package/dist/mockproof/import-graph-scanner.d.ts.map +1 -0
  15. package/dist/mockproof/import-graph-scanner.js +411 -0
  16. package/dist/mockproof/import-graph-scanner.js.map +1 -0
  17. package/dist/mockproof/index.d.ts +10 -0
  18. package/dist/mockproof/index.d.ts.map +1 -0
  19. package/dist/mockproof/index.js +10 -0
  20. package/dist/mockproof/index.js.map +1 -0
  21. package/dist/reality-mode/auth-enforcer.d.ts +13 -0
  22. package/dist/reality-mode/auth-enforcer.d.ts.map +1 -0
  23. package/dist/reality-mode/auth-enforcer.js +90 -0
  24. package/dist/reality-mode/auth-enforcer.js.map +1 -0
  25. package/dist/reality-mode/explorer/critical-flows.d.ts +71 -0
  26. package/dist/reality-mode/explorer/critical-flows.d.ts.map +1 -0
  27. package/dist/reality-mode/explorer/critical-flows.js +463 -0
  28. package/dist/reality-mode/explorer/critical-flows.js.map +1 -0
  29. package/dist/reality-mode/explorer/flow-parser.d.ts +52 -0
  30. package/dist/reality-mode/explorer/flow-parser.d.ts.map +1 -0
  31. package/dist/reality-mode/explorer/flow-parser.js +250 -0
  32. package/dist/reality-mode/explorer/flow-parser.js.map +1 -0
  33. package/dist/reality-mode/explorer/index.d.ts +11 -0
  34. package/dist/reality-mode/explorer/index.d.ts.map +1 -0
  35. package/dist/reality-mode/explorer/index.js +11 -0
  36. package/dist/reality-mode/explorer/index.js.map +1 -0
  37. package/dist/reality-mode/explorer/runtime-explorer.d.ts +35 -0
  38. package/dist/reality-mode/explorer/runtime-explorer.d.ts.map +1 -0
  39. package/dist/reality-mode/explorer/runtime-explorer.js +688 -0
  40. package/dist/reality-mode/explorer/runtime-explorer.js.map +1 -0
  41. package/dist/reality-mode/explorer/surface-discovery.d.ts +60 -0
  42. package/dist/reality-mode/explorer/surface-discovery.d.ts.map +1 -0
  43. package/dist/reality-mode/explorer/surface-discovery.js +357 -0
  44. package/dist/reality-mode/explorer/surface-discovery.js.map +1 -0
  45. package/dist/reality-mode/explorer/types.d.ts +275 -0
  46. package/dist/reality-mode/explorer/types.d.ts.map +1 -0
  47. package/dist/reality-mode/explorer/types.js +8 -0
  48. package/dist/reality-mode/explorer/types.js.map +1 -0
  49. package/dist/reality-mode/fake-success-detector.d.ts +10 -0
  50. package/dist/reality-mode/fake-success-detector.d.ts.map +1 -0
  51. package/dist/reality-mode/fake-success-detector.js +76 -0
  52. package/dist/reality-mode/fake-success-detector.js.map +1 -0
  53. package/dist/reality-mode/index.d.ts +14 -0
  54. package/dist/reality-mode/index.d.ts.map +1 -0
  55. package/dist/reality-mode/index.js +14 -0
  56. package/dist/reality-mode/index.js.map +1 -0
  57. package/dist/reality-mode/reality-scanner.d.ts +48 -0
  58. package/dist/reality-mode/reality-scanner.d.ts.map +1 -0
  59. package/dist/reality-mode/reality-scanner.js +516 -0
  60. package/dist/reality-mode/reality-scanner.js.map +1 -0
  61. package/dist/reality-mode/report-generator.d.ts +11 -0
  62. package/dist/reality-mode/report-generator.d.ts.map +1 -0
  63. package/dist/reality-mode/report-generator.js +233 -0
  64. package/dist/reality-mode/report-generator.js.map +1 -0
  65. package/dist/reality-mode/traffic-classifier.d.ts +14 -0
  66. package/dist/reality-mode/traffic-classifier.d.ts.map +1 -0
  67. package/dist/reality-mode/traffic-classifier.js +131 -0
  68. package/dist/reality-mode/traffic-classifier.js.map +1 -0
  69. package/dist/reality-mode/types.d.ts +90 -0
  70. package/dist/reality-mode/types.d.ts.map +1 -0
  71. package/dist/reality-mode/types.js +2 -0
  72. package/dist/reality-mode/types.js.map +1 -0
  73. package/dist/ship-badge/__tests__/ship-badge-generator.test.d.ts +5 -0
  74. package/dist/ship-badge/__tests__/ship-badge-generator.test.d.ts.map +1 -0
  75. package/dist/ship-badge/__tests__/ship-badge-generator.test.js +146 -0
  76. package/dist/ship-badge/__tests__/ship-badge-generator.test.js.map +1 -0
  77. package/dist/ship-badge/index.d.ts +9 -0
  78. package/dist/ship-badge/index.d.ts.map +1 -0
  79. package/dist/ship-badge/index.js +9 -0
  80. package/dist/ship-badge/index.js.map +1 -0
  81. package/dist/ship-badge/ship-badge-generator.d.ts +136 -0
  82. package/dist/ship-badge/ship-badge-generator.d.ts.map +1 -0
  83. package/dist/ship-badge/ship-badge-generator.js +681 -0
  84. package/dist/ship-badge/ship-badge-generator.js.map +1 -0
  85. package/package.json +20 -0
  86. package/src/index.ts +7 -0
  87. package/src/mock-implementation.ts +0 -0
  88. package/src/mockproof/__tests__/import-graph-scanner.test.ts +115 -0
  89. package/src/mockproof/import-graph-scanner.d.ts +93 -0
  90. package/src/mockproof/import-graph-scanner.d.ts.map +1 -0
  91. package/src/mockproof/import-graph-scanner.js +482 -0
  92. package/src/mockproof/import-graph-scanner.ts +540 -0
  93. package/src/mockproof/index.ts +18 -0
  94. package/src/reality-mode/auth-enforcer.ts +97 -0
  95. package/src/reality-mode/explorer/critical-flows.ts +504 -0
  96. package/src/reality-mode/explorer/flow-parser.ts +293 -0
  97. package/src/reality-mode/explorer/index.ts +22 -0
  98. package/src/reality-mode/explorer/runtime-explorer.ts +715 -0
  99. package/src/reality-mode/explorer/surface-discovery.ts +498 -0
  100. package/src/reality-mode/explorer/templates/example-flows/auth-flow.yaml +41 -0
  101. package/src/reality-mode/explorer/templates/example-flows/checkout-flow.yaml +66 -0
  102. package/src/reality-mode/explorer/templates/example-flows/contact-form.yaml +43 -0
  103. package/src/reality-mode/explorer/templates/github-action.yml +132 -0
  104. package/src/reality-mode/explorer/types.ts +356 -0
  105. package/src/reality-mode/fake-success-detector.ts +89 -0
  106. package/src/reality-mode/index.ts +19 -0
  107. package/src/reality-mode/reality-scanner.d.ts +123 -0
  108. package/src/reality-mode/reality-scanner.d.ts.map +1 -0
  109. package/src/reality-mode/reality-scanner.js +526 -0
  110. package/src/reality-mode/reality-scanner.ts +576 -0
  111. package/src/reality-mode/report-generator.ts +253 -0
  112. package/src/reality-mode/traffic-classifier.ts +169 -0
  113. package/src/reality-mode/types.ts +95 -0
  114. package/src/ship-badge/__tests__/ship-badge-generator.test.ts +162 -0
  115. package/src/ship-badge/index.ts +16 -0
  116. package/src/ship-badge/ship-badge-generator.d.ts +136 -0
  117. package/src/ship-badge/ship-badge-generator.d.ts.map +1 -0
  118. package/src/ship-badge/ship-badge-generator.js +779 -0
  119. package/src/ship-badge/ship-badge-generator.ts +873 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reality-scanner.d.ts","sourceRoot":"","sources":["reality-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,526 @@
1
+ "use strict";
2
+ /**
3
+ * Reality Mode - Runtime Fake Detection
4
+ *
5
+ * "Stop shipping pretend features. Guardrail runs your app and catches the lies."
6
+ *
7
+ * This module spins up the app, intercepts network calls, clicks through the UI,
8
+ * and detects:
9
+ * - Calls to localhost, jsonplaceholder, staging domains, ngrok
10
+ * - Routes returning demo/placeholder responses
11
+ * - Silent fallback success patterns
12
+ * - Mock billing and fake invoice IDs
13
+ *
14
+ * The killer feature: a "flight recorder" replay showing exactly what happened.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.realityScanner =
18
+ exports.RealityScanner =
19
+ exports.DEFAULT_FAKE_PATTERNS =
20
+ void 0;
21
+ const FAKE_DOMAIN_PATTERNS = [
22
+ /localhost:\d+/i,
23
+ /127\.0\.0\.1:\d+/i,
24
+ /jsonplaceholder\.typicode\.com/i,
25
+ /reqres\.in/i,
26
+ /mockapi\.io/i,
27
+ /mocky\.io/i,
28
+ /httpbin\.org/i,
29
+ /\.ngrok\.io/i,
30
+ /\.ngrok-free\.app/i,
31
+ /staging\./i,
32
+ /\.local\//i,
33
+ /\.test\//i,
34
+ /api\.example\.com/i,
35
+ /fake\.api/i,
36
+ /demo\.api/i,
37
+ ];
38
+ const FAKE_RESPONSE_PATTERNS = [
39
+ { pattern: /inv_demo_/i, name: "Demo invoice ID" },
40
+ { pattern: /user_demo_/i, name: "Demo user ID" },
41
+ { pattern: /cus_demo_/i, name: "Demo customer ID" },
42
+ { pattern: /sub_demo_/i, name: "Demo subscription ID" },
43
+ { pattern: /sk_test_/i, name: "Test Stripe key" },
44
+ { pattern: /pk_test_/i, name: "Test Stripe public key" },
45
+ { pattern: /"success":\s*true.*"demo"/i, name: "Demo success response" },
46
+ { pattern: /lorem\s+ipsum/i, name: "Lorem ipsum placeholder" },
47
+ { pattern: /john\.doe|jane\.doe/i, name: "Placeholder name" },
48
+ { pattern: /user@example\.com/i, name: "Placeholder email" },
49
+ { pattern: /placeholder\.(com|jpg|png)/i, name: "Placeholder domain/image" },
50
+ {
51
+ pattern: /"id":\s*("demo"|"test"|"fake"|1234567890)/i,
52
+ name: "Fake ID pattern",
53
+ },
54
+ { pattern: /"status":\s*"simulated"/i, name: "Simulated status" },
55
+ { pattern: /"mock":\s*true/i, name: "Mock flag enabled" },
56
+ { pattern: /"isDemo":\s*true/i, name: "Demo mode flag" },
57
+ ];
58
+ const SILENT_FALLBACK_PATTERNS = [
59
+ {
60
+ pattern: /"error":\s*null.*"data":\s*\[\]/i,
61
+ name: "Empty success on error",
62
+ },
63
+ {
64
+ pattern: /catch.*return\s*\{\s*success:\s*true/i,
65
+ name: "Success on catch",
66
+ },
67
+ { pattern: /"fallback":\s*true/i, name: "Fallback flag" },
68
+ ];
69
+ exports.DEFAULT_FAKE_PATTERNS = [
70
+ // Fake domain patterns
71
+ {
72
+ id: "fake-api-domain",
73
+ name: "Fake API Domain",
74
+ description: "Request to a mock/staging/localhost API domain",
75
+ severity: "critical",
76
+ detect: (item) => {
77
+ if (item.type !== "request") return false;
78
+ return FAKE_DOMAIN_PATTERNS.some((p) => p.test(item.url));
79
+ },
80
+ },
81
+ // Demo response patterns
82
+ {
83
+ id: "demo-response-data",
84
+ name: "Demo Response Data",
85
+ description: "Response contains demo/placeholder data",
86
+ severity: "critical",
87
+ detect: (item) => {
88
+ if (item.type !== "response" || !item.body) return false;
89
+ return FAKE_RESPONSE_PATTERNS.some(({ pattern }) =>
90
+ pattern.test(item.body),
91
+ );
92
+ },
93
+ },
94
+ // Silent fallback
95
+ {
96
+ id: "silent-fallback-success",
97
+ name: "Silent Fallback Success",
98
+ description:
99
+ "Code silently returns success on error (catch returns default)",
100
+ severity: "warning",
101
+ detect: (item) => {
102
+ if (item.type !== "response" || !item.body) return false;
103
+ return SILENT_FALLBACK_PATTERNS.some(({ pattern }) =>
104
+ pattern.test(item.body),
105
+ );
106
+ },
107
+ },
108
+ // HTTP status checks
109
+ {
110
+ id: "mock-status-code",
111
+ name: "Mock Status Code",
112
+ description: "Response with unusual status indicating mock (418, 999)",
113
+ severity: "warning",
114
+ detect: (item) => {
115
+ if (item.type !== "response") return false;
116
+ return [418, 999, 0].includes(item.status);
117
+ },
118
+ },
119
+ // Test keys in production
120
+ {
121
+ id: "test-api-keys",
122
+ name: "Test API Keys",
123
+ description: "Test/demo API keys detected in request or response",
124
+ severity: "critical",
125
+ detect: (item) => {
126
+ const content =
127
+ item.type === "request"
128
+ ? JSON.stringify(item.headers) + (item.body || "")
129
+ : item.body || "";
130
+ return /sk_test_|pk_test_|api_key_test|demo_api_key/i.test(content);
131
+ },
132
+ },
133
+ // Billing simulation
134
+ {
135
+ id: "simulated-billing",
136
+ name: "Simulated Billing",
137
+ description: "Billing/payment response appears to be simulated",
138
+ severity: "critical",
139
+ detect: (item) => {
140
+ if (item.type !== "response" || !item.body) return false;
141
+ const billingUrls = /stripe|billing|payment|checkout|subscription/i;
142
+ const isBillingEndpoint = billingUrls.test(item.url);
143
+ const hasDemoData = /demo|test|simulate|fake|mock/i.test(item.body);
144
+ return isBillingEndpoint && hasDemoData;
145
+ },
146
+ },
147
+ ];
148
+ class RealityScanner {
149
+ config;
150
+ replay = [];
151
+ detections = [];
152
+ requests = [];
153
+ responses = [];
154
+ actions = [];
155
+ constructor(config = {}) {
156
+ this.config = {
157
+ timeout: 30000,
158
+ patterns: exports.DEFAULT_FAKE_PATTERNS,
159
+ screenshotOnDetection: true,
160
+ headless: true,
161
+ ...config,
162
+ };
163
+ }
164
+ /**
165
+ * Generate Playwright test code for Reality Mode scanning
166
+ */
167
+ generatePlaywrightTest(config) {
168
+ const { baseUrl, clickPaths, outputDir } = config;
169
+ return `/**
170
+ * Reality Mode - Auto-generated Playwright Test
171
+ *
172
+ * This test runs your app and intercepts all network calls to detect fake data.
173
+ * Generated by Guardrail Reality Mode.
174
+ */
175
+
176
+ import { test, expect, Page, Request, Response } from '@playwright/test';
177
+ import * as fs from 'fs';
178
+ import * as path from 'path';
179
+
180
+ const FAKE_DOMAIN_PATTERNS = ${JSON.stringify(
181
+ FAKE_DOMAIN_PATTERNS.map((r) => r.source),
182
+ null,
183
+ 2,
184
+ )};
185
+
186
+ const FAKE_RESPONSE_PATTERNS = ${JSON.stringify(FAKE_RESPONSE_PATTERNS, null, 2)};
187
+
188
+ interface Detection {
189
+ type: 'fake-domain' | 'demo-data' | 'simulated-billing' | 'test-keys';
190
+ url: string;
191
+ evidence: string;
192
+ timestamp: number;
193
+ }
194
+
195
+ interface ReplayStep {
196
+ timestamp: number;
197
+ type: 'request' | 'response' | 'action';
198
+ data: any;
199
+ detections: Detection[];
200
+ }
201
+
202
+ test.describe('Reality Mode Scan', () => {
203
+ let detections: Detection[] = [];
204
+ let replay: ReplayStep[] = [];
205
+
206
+ test.beforeEach(async ({ page }) => {
207
+ detections = [];
208
+ replay = [];
209
+
210
+ // Intercept all network requests
211
+ page.on('request', (request: Request) => {
212
+ const url = request.url();
213
+ const step: ReplayStep = {
214
+ timestamp: Date.now(),
215
+ type: 'request',
216
+ data: { url, method: request.method() },
217
+ detections: []
218
+ };
219
+
220
+ // Check for fake domains
221
+ for (const pattern of FAKE_DOMAIN_PATTERNS) {
222
+ if (new RegExp(pattern, 'i').test(url)) {
223
+ const detection: Detection = {
224
+ type: 'fake-domain',
225
+ url,
226
+ evidence: \`URL matches fake domain pattern: \${pattern}\`,
227
+ timestamp: Date.now()
228
+ };
229
+ step.detections.push(detection);
230
+ detections.push(detection);
231
+ }
232
+ }
233
+
234
+ replay.push(step);
235
+ });
236
+
237
+ page.on('response', async (response: Response) => {
238
+ const url = response.url();
239
+ let body = '';
240
+
241
+ try {
242
+ body = await response.text();
243
+ } catch (e) {
244
+ // Some responses can't be read
245
+ }
246
+
247
+ const step: ReplayStep = {
248
+ timestamp: Date.now(),
249
+ type: 'response',
250
+ data: { url, status: response.status(), bodyPreview: body.slice(0, 500) },
251
+ detections: []
252
+ };
253
+
254
+ // Check for demo data patterns
255
+ for (const { pattern, name } of FAKE_RESPONSE_PATTERNS) {
256
+ if (new RegExp(pattern.source || pattern, 'i').test(body)) {
257
+ const detection: Detection = {
258
+ type: 'demo-data',
259
+ url,
260
+ evidence: \`Response contains \${name}\`,
261
+ timestamp: Date.now()
262
+ };
263
+ step.detections.push(detection);
264
+ detections.push(detection);
265
+ }
266
+ }
267
+
268
+ replay.push(step);
269
+ });
270
+ });
271
+
272
+ test('should detect fake data in production flow', async ({ page }) => {
273
+ // Navigate to the app
274
+ await page.goto('${baseUrl}');
275
+
276
+ replay.push({
277
+ timestamp: Date.now(),
278
+ type: 'action',
279
+ data: { type: 'navigation', url: '${baseUrl}' },
280
+ detections: []
281
+ });
282
+
283
+ // Execute click paths
284
+ const clickPaths = ${JSON.stringify(clickPaths, null, 4)};
285
+
286
+ for (const path of clickPaths) {
287
+ for (const selector of path) {
288
+ try {
289
+ await page.waitForSelector(selector, { timeout: 5000 });
290
+ await page.click(selector);
291
+
292
+ replay.push({
293
+ timestamp: Date.now(),
294
+ type: 'action',
295
+ data: { type: 'click', selector },
296
+ detections: []
297
+ });
298
+
299
+ // Wait for network activity to settle
300
+ await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => {});
301
+ } catch (e) {
302
+ // Selector not found, skip
303
+ }
304
+ }
305
+ }
306
+
307
+ // Save results
308
+ const result = {
309
+ verdict: detections.length > 0 ? 'fake' : 'real',
310
+ score: Math.max(0, 100 - detections.length * 10),
311
+ detections,
312
+ replay,
313
+ summary: {
314
+ totalRequests: replay.filter(r => r.type === 'request').length,
315
+ fakeRequests: detections.filter(d => d.type === 'fake-domain').length,
316
+ totalActions: replay.filter(r => r.type === 'action').length,
317
+ criticalIssues: detections.length
318
+ },
319
+ timestamp: new Date().toISOString()
320
+ };
321
+
322
+ // Write results
323
+ const outputDir = '${outputDir.replace(/\\/g, "\\\\")}';
324
+ await fs.promises.mkdir(outputDir, { recursive: true });
325
+ await fs.promises.writeFile(
326
+ path.join(outputDir, 'reality-mode-result.json'),
327
+ JSON.stringify(result, null, 2)
328
+ );
329
+
330
+ // Generate human-readable report
331
+ const report = generateReport(result);
332
+ await fs.promises.writeFile(
333
+ path.join(outputDir, 'reality-mode-report.txt'),
334
+ report
335
+ );
336
+
337
+ // Fail if fake data detected
338
+ expect(detections.length, \`Found \${detections.length} fake data issues\`).toBe(0);
339
+ });
340
+ });
341
+
342
+ function generateReport(result: any): string {
343
+ const lines: string[] = [];
344
+
345
+ lines.push('╔══════════════════════════════════════════════════════════════╗');
346
+ lines.push('║ 🔍 Reality Mode Scan Results 🔍 ║');
347
+ lines.push('╚══════════════════════════════════════════════════════════════╝');
348
+ lines.push('');
349
+
350
+ if (result.verdict === 'real') {
351
+ lines.push('✅ VERDICT: REAL - No fake data detected!');
352
+ lines.push('');
353
+ lines.push(' Your app is shipping real features.');
354
+ } else {
355
+ lines.push('❌ VERDICT: FAKE - Fake data detected!');
356
+ lines.push('');
357
+ lines.push(\` Found \${result.detections.length} issues that indicate fake/mock data.\`);
358
+ lines.push('');
359
+ lines.push('─'.repeat(64));
360
+ lines.push('');
361
+ lines.push('DETECTIONS:');
362
+ lines.push('');
363
+
364
+ for (const detection of result.detections) {
365
+ lines.push(\` ❌ \${detection.type.toUpperCase()}\`);
366
+ lines.push(\` URL: \${detection.url}\`);
367
+ lines.push(\` Evidence: \${detection.evidence}\`);
368
+ lines.push('');
369
+ }
370
+ }
371
+
372
+ lines.push('─'.repeat(64));
373
+ lines.push(\`Score: \${result.score}/100\`);
374
+ lines.push(\`Total requests intercepted: \${result.summary.totalRequests}\`);
375
+ lines.push(\`Actions performed: \${result.summary.totalActions}\`);
376
+ lines.push(\`Generated: \${result.timestamp}\`);
377
+
378
+ return lines.join('\\n');
379
+ }
380
+ `;
381
+ }
382
+ /**
383
+ * Analyze intercepted network traffic for fake patterns
384
+ */
385
+ analyzeTraffic(requests, responses) {
386
+ const detections = [];
387
+ const patterns = this.config.patterns || exports.DEFAULT_FAKE_PATTERNS;
388
+ for (const request of requests) {
389
+ for (const pattern of patterns) {
390
+ if (pattern.detect(request)) {
391
+ detections.push({
392
+ pattern,
393
+ request,
394
+ timestamp: request.timestamp,
395
+ evidence: this.getEvidence(request, pattern),
396
+ });
397
+ }
398
+ }
399
+ }
400
+ for (const response of responses) {
401
+ for (const pattern of patterns) {
402
+ if (pattern.detect(response)) {
403
+ detections.push({
404
+ pattern,
405
+ response,
406
+ timestamp: response.timestamp,
407
+ evidence: this.getEvidence(response, pattern),
408
+ });
409
+ }
410
+ }
411
+ }
412
+ return detections;
413
+ }
414
+ /**
415
+ * Generate evidence description for a detection
416
+ */
417
+ getEvidence(item, pattern) {
418
+ if (item.type === "request") {
419
+ if (pattern.id === "fake-api-domain") {
420
+ const matched = FAKE_DOMAIN_PATTERNS.find((p) => p.test(item.url));
421
+ return `Request URL "${item.url}" matches fake domain pattern`;
422
+ }
423
+ return `Request to ${item.url} flagged by ${pattern.name}`;
424
+ } else {
425
+ if (pattern.id === "demo-response-data" && item.body) {
426
+ const matched = FAKE_RESPONSE_PATTERNS.find(({ pattern }) =>
427
+ pattern.test(item.body),
428
+ );
429
+ return `Response contains ${matched?.name || "demo data"}`;
430
+ }
431
+ return `Response from ${item.url} flagged by ${pattern.name}`;
432
+ }
433
+ }
434
+ /**
435
+ * Generate a human-readable Reality Mode report
436
+ */
437
+ generateReport(result) {
438
+ const lines = [];
439
+ lines.push(
440
+ "╔══════════════════════════════════════════════════════════════╗",
441
+ );
442
+ lines.push(
443
+ "║ 🔍 Reality Mode Scan Results 🔍 ║",
444
+ );
445
+ lines.push(
446
+ "╚══════════════════════════════════════════════════════════════╝",
447
+ );
448
+ lines.push("");
449
+ const verdictEmoji =
450
+ result.verdict === "real"
451
+ ? "✅"
452
+ : result.verdict === "fake"
453
+ ? "❌"
454
+ : "⚠️";
455
+ const verdictText = result.verdict.toUpperCase();
456
+ lines.push(`${verdictEmoji} VERDICT: ${verdictText}`);
457
+ lines.push(` Reality Score: ${result.score}/100`);
458
+ lines.push("");
459
+ if (result.verdict === "real") {
460
+ lines.push(" 🎉 Congratulations! Your app is shipping real features.");
461
+ lines.push(
462
+ " No fake data, mock APIs, or placeholder content detected.",
463
+ );
464
+ } else {
465
+ lines.push(` Found ${result.summary.criticalIssues} critical issues`);
466
+ lines.push(` Found ${result.summary.warnings} warnings`);
467
+ lines.push("");
468
+ lines.push("─".repeat(64));
469
+ lines.push("");
470
+ lines.push("DETECTIONS:");
471
+ lines.push("");
472
+ for (const detection of result.detections) {
473
+ const icon = detection.pattern.severity === "critical" ? "🚨" : "⚠️";
474
+ lines.push(`${icon} ${detection.pattern.name}`);
475
+ lines.push(` ${detection.pattern.description}`);
476
+ lines.push(` Evidence: ${detection.evidence}`);
477
+ if (detection.request) {
478
+ lines.push(` URL: ${detection.request.url}`);
479
+ }
480
+ if (detection.response) {
481
+ lines.push(` URL: ${detection.response.url}`);
482
+ }
483
+ lines.push("");
484
+ }
485
+ }
486
+ lines.push("─".repeat(64));
487
+ lines.push("");
488
+ lines.push("SUMMARY:");
489
+ lines.push(
490
+ ` Total requests intercepted: ${result.summary.totalRequests}`,
491
+ );
492
+ lines.push(` Fake/mock requests: ${result.summary.fakeRequests}`);
493
+ lines.push(` User actions performed: ${result.summary.totalActions}`);
494
+ lines.push(` Scan duration: ${result.duration}ms`);
495
+ lines.push("");
496
+ lines.push(`Generated: ${result.timestamp}`);
497
+ return lines.join("\n");
498
+ }
499
+ /**
500
+ * Generate common click paths for testing
501
+ */
502
+ generateDefaultClickPaths() {
503
+ return [
504
+ // Auth flow
505
+ ['[data-testid="login-button"]', '[data-testid="signup-button"]'],
506
+ // Navigation
507
+ ["nav a", "header a", '[role="navigation"] a'],
508
+ // Main actions
509
+ [
510
+ 'button[type="submit"]',
511
+ ".cta-button",
512
+ '[data-testid="primary-action"]',
513
+ ],
514
+ // Dashboard/billing
515
+ ['[href*="dashboard"]', '[href*="billing"]', '[href*="settings"]'],
516
+ // Checkout flow
517
+ [
518
+ '[data-testid="add-to-cart"]',
519
+ '[data-testid="checkout"]',
520
+ '[data-testid="pay"]',
521
+ ],
522
+ ];
523
+ }
524
+ }
525
+ exports.RealityScanner = RealityScanner;
526
+ exports.realityScanner = new RealityScanner();