@spektre/veil 0.1.7 → 0.1.10
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/dist/devUI.d.ts +1 -1
- package/dist/index.esm.js +181 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +181 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/devUI.d.ts
CHANGED
package/dist/index.esm.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { createContext, useState, useEffect, useContext } from 'react';
|
|
2
2
|
|
|
3
3
|
const DEV_UI_STORAGE_KEY = 'spektre_dev_enabled';
|
|
4
|
+
let DEV_UI_VERSION = '1.0.0';
|
|
4
5
|
// Inject styles directly into the document
|
|
5
6
|
function injectDevUIStyles() {
|
|
6
7
|
if (document.getElementById('spektre-dev-ui-styles')) {
|
|
@@ -44,6 +45,21 @@ function injectDevUIStyles() {
|
|
|
44
45
|
box-shadow: 0 6px 16px rgba(100, 116, 139, 0.6);
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
.spektre-dev-dot.warning {
|
|
49
|
+
background-color: #ef4444;
|
|
50
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.6);
|
|
51
|
+
animation: spektre-pulse 2s infinite;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@keyframes spektre-pulse {
|
|
55
|
+
0%, 100% {
|
|
56
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.6);
|
|
57
|
+
}
|
|
58
|
+
50% {
|
|
59
|
+
box-shadow: 0 4px 20px rgba(239, 68, 68, 0.8);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
47
63
|
/* Modal Overlay */
|
|
48
64
|
.spektre-dev-modal-overlay {
|
|
49
65
|
position: fixed;
|
|
@@ -134,6 +150,8 @@ function injectDevUIStyles() {
|
|
|
134
150
|
.spektre-dev-modal-body {
|
|
135
151
|
padding: 24px;
|
|
136
152
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
153
|
+
max-height: 60vh;
|
|
154
|
+
overflow-y: auto;
|
|
137
155
|
}
|
|
138
156
|
|
|
139
157
|
.spektre-dev-modal-info {
|
|
@@ -240,6 +258,52 @@ function injectDevUIStyles() {
|
|
|
240
258
|
font-weight: 500;
|
|
241
259
|
}
|
|
242
260
|
|
|
261
|
+
/* Security Scan Results */
|
|
262
|
+
.spektre-security-scan-container {
|
|
263
|
+
margin-top: 16px;
|
|
264
|
+
padding: 16px;
|
|
265
|
+
background-color: rgba(239, 68, 68, 0.1);
|
|
266
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
267
|
+
border-radius: 8px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.spektre-security-scan-title {
|
|
271
|
+
font-size: 14px;
|
|
272
|
+
font-weight: 600;
|
|
273
|
+
color: #fca5a5;
|
|
274
|
+
margin: 0 0 12px 0;
|
|
275
|
+
display: flex;
|
|
276
|
+
align-items: center;
|
|
277
|
+
gap: 8px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.spektre-security-scan-issues {
|
|
281
|
+
display: flex;
|
|
282
|
+
flex-direction: column;
|
|
283
|
+
gap: 8px;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.spektre-security-issue {
|
|
287
|
+
font-size: 13px;
|
|
288
|
+
color: #fed7aa;
|
|
289
|
+
padding: 8px;
|
|
290
|
+
background-color: rgba(239, 68, 68, 0.15);
|
|
291
|
+
border-left: 3px solid #ef4444;
|
|
292
|
+
border-radius: 4px;
|
|
293
|
+
line-height: 1.4;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.spektre-security-scan-clean {
|
|
297
|
+
font-size: 13px;
|
|
298
|
+
color: #86efac;
|
|
299
|
+
padding: 12px;
|
|
300
|
+
background-color: rgba(34, 197, 94, 0.15);
|
|
301
|
+
border: 1px solid rgba(34, 197, 94, 0.3);
|
|
302
|
+
border-radius: 6px;
|
|
303
|
+
text-align: center;
|
|
304
|
+
font-weight: 500;
|
|
305
|
+
}
|
|
306
|
+
|
|
243
307
|
/* Accessibility */
|
|
244
308
|
@media (prefers-reduced-motion: reduce) {
|
|
245
309
|
.spektre-dev-dot,
|
|
@@ -284,7 +348,70 @@ function injectDevUIStyles() {
|
|
|
284
348
|
`;
|
|
285
349
|
document.head.appendChild(styleSheet);
|
|
286
350
|
}
|
|
287
|
-
function
|
|
351
|
+
function runSecurityScan() {
|
|
352
|
+
const issues = [];
|
|
353
|
+
// 1. Check for hardcoded API keys/tokens
|
|
354
|
+
const sensitivePatterns = [
|
|
355
|
+
{ pattern: /api[_-]?key\s*[:=]\s*['"]\S+['"]/gi, label: 'API Key' },
|
|
356
|
+
{ pattern: /token\s*[:=]\s*['"]\S+['"]/gi, label: 'Token' },
|
|
357
|
+
{ pattern: /password\s*[:=]\s*['"]\S+['"]/gi, label: 'Password' },
|
|
358
|
+
{ pattern: /secret\s*[:=]\s*['"]\S+['"]/gi, label: 'Secret' },
|
|
359
|
+
{ pattern: /bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, label: 'Bearer Token' },
|
|
360
|
+
];
|
|
361
|
+
const pageHTML = document.documentElement.outerHTML;
|
|
362
|
+
const scriptTags = Array.from(document.querySelectorAll('script'));
|
|
363
|
+
const allText = pageHTML + scriptTags.map(s => s.textContent).join('\n');
|
|
364
|
+
sensitivePatterns.forEach(({ pattern, label }) => {
|
|
365
|
+
if (pattern.test(allText)) {
|
|
366
|
+
issues.push(`⚠️ Found hardcoded ${label} in page code`);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
// 2. Check for dangerous functions
|
|
370
|
+
const dangerousPatterns = [
|
|
371
|
+
{ pattern: /eval\s*\(/gi, label: 'eval() usage' },
|
|
372
|
+
{ pattern: /innerHTML\s*=/gi, label: 'innerHTML manipulation' },
|
|
373
|
+
{ pattern: /dangerouslySetInnerHTML/gi, label: 'dangerouslySetInnerHTML' },
|
|
374
|
+
{ pattern: /document\.write/gi, label: 'document.write()' },
|
|
375
|
+
{ pattern: /setTimeout\s*\(\s*['"`].*['"`]/gi, label: 'setTimeout with string' },
|
|
376
|
+
];
|
|
377
|
+
dangerousPatterns.forEach(({ pattern, label }) => {
|
|
378
|
+
if (pattern.test(allText)) {
|
|
379
|
+
issues.push(`⚠️ Potential security risk: ${label}`);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
// 3. Check localStorage for sensitive data
|
|
383
|
+
try {
|
|
384
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
385
|
+
const key = localStorage.key(i);
|
|
386
|
+
if (key) {
|
|
387
|
+
const value = localStorage.getItem(key);
|
|
388
|
+
if (value) {
|
|
389
|
+
if (/token|password|secret|key|auth/i.test(key) &&
|
|
390
|
+
value.length > 20) {
|
|
391
|
+
issues.push(`⚠️ Sensitive data in localStorage: "${key}"`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
catch (e) {
|
|
398
|
+
// localStorage access denied, skip
|
|
399
|
+
}
|
|
400
|
+
// 4. Check for missing CSP headers (via console warning detection)
|
|
401
|
+
const scripts = scriptTags.filter(s => s.src && !s.src.includes('spektre'));
|
|
402
|
+
if (scripts.length > 10) {
|
|
403
|
+
issues.push(`⚠️ Many external scripts loaded (${scripts.length})`);
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
hasIssues: issues.length > 0,
|
|
407
|
+
issues,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function initializeDevUI(version) {
|
|
411
|
+
// Set version if provided
|
|
412
|
+
{
|
|
413
|
+
DEV_UI_VERSION = version;
|
|
414
|
+
}
|
|
288
415
|
// Inject styles first
|
|
289
416
|
injectDevUIStyles();
|
|
290
417
|
// Check if Spektre is enabled in localStorage (default to true)
|
|
@@ -296,6 +423,18 @@ function initializeDevUI() {
|
|
|
296
423
|
}
|
|
297
424
|
// Create and inject the orange dot
|
|
298
425
|
createDevDot();
|
|
426
|
+
// Run security scan after a short delay to ensure DOM is ready
|
|
427
|
+
setTimeout(() => {
|
|
428
|
+
const scanResult = runSecurityScan();
|
|
429
|
+
if (scanResult.hasIssues) {
|
|
430
|
+
// Update dot to warning state and auto-open modal
|
|
431
|
+
const dot = document.getElementById('spektre-dev-dot');
|
|
432
|
+
if (dot) {
|
|
433
|
+
dot.classList.add('warning');
|
|
434
|
+
}
|
|
435
|
+
openDevModal(scanResult);
|
|
436
|
+
}
|
|
437
|
+
}, 500);
|
|
299
438
|
}
|
|
300
439
|
function createDevDot() {
|
|
301
440
|
const dot = document.createElement('div');
|
|
@@ -307,7 +446,7 @@ function createDevDot() {
|
|
|
307
446
|
});
|
|
308
447
|
document.body.appendChild(dot);
|
|
309
448
|
}
|
|
310
|
-
function openDevModal() {
|
|
449
|
+
function openDevModal(scanResult) {
|
|
311
450
|
// Check if modal already exists
|
|
312
451
|
if (document.getElementById('spektre-dev-modal')) {
|
|
313
452
|
return;
|
|
@@ -374,6 +513,43 @@ function openDevModal() {
|
|
|
374
513
|
body.appendChild(statusBadge);
|
|
375
514
|
body.appendChild(toggleContainer);
|
|
376
515
|
body.appendChild(reloadMessage);
|
|
516
|
+
// Add security scan results if available
|
|
517
|
+
if (scanResult) {
|
|
518
|
+
const scanContainer = document.createElement('div');
|
|
519
|
+
scanContainer.className = 'spektre-security-scan-container';
|
|
520
|
+
const scanTitle = document.createElement('div');
|
|
521
|
+
scanTitle.className = 'spektre-security-scan-title';
|
|
522
|
+
scanTitle.innerHTML = '🔒 Security Check';
|
|
523
|
+
scanContainer.appendChild(scanTitle);
|
|
524
|
+
if (scanResult.hasIssues) {
|
|
525
|
+
const issuesContainer = document.createElement('div');
|
|
526
|
+
issuesContainer.className = 'spektre-security-scan-issues';
|
|
527
|
+
scanResult.issues.forEach(issue => {
|
|
528
|
+
const issueElement = document.createElement('div');
|
|
529
|
+
issueElement.className = 'spektre-security-issue';
|
|
530
|
+
issueElement.textContent = issue;
|
|
531
|
+
issuesContainer.appendChild(issueElement);
|
|
532
|
+
});
|
|
533
|
+
scanContainer.appendChild(issuesContainer);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
const cleanMessage = document.createElement('div');
|
|
537
|
+
cleanMessage.className = 'spektre-security-scan-clean';
|
|
538
|
+
cleanMessage.textContent = '✓ No security issues detected';
|
|
539
|
+
scanContainer.appendChild(cleanMessage);
|
|
540
|
+
}
|
|
541
|
+
body.appendChild(scanContainer);
|
|
542
|
+
}
|
|
543
|
+
// Add version label at bottom
|
|
544
|
+
const versionLabel = document.createElement('div');
|
|
545
|
+
versionLabel.style.marginTop = '16px';
|
|
546
|
+
versionLabel.style.paddingTop = '12px';
|
|
547
|
+
versionLabel.style.borderTop = '1px solid rgba(71, 85, 105, 0.3)';
|
|
548
|
+
versionLabel.style.fontSize = '12px';
|
|
549
|
+
versionLabel.style.color = '#64748b';
|
|
550
|
+
versionLabel.style.textAlign = 'center';
|
|
551
|
+
versionLabel.textContent = `Spektre v${DEV_UI_VERSION}`;
|
|
552
|
+
body.appendChild(versionLabel);
|
|
377
553
|
modalContent.appendChild(header);
|
|
378
554
|
modalContent.appendChild(body);
|
|
379
555
|
modal.appendChild(modalContent);
|
|
@@ -832,9 +1008,11 @@ const useProtectedFetch = () => {
|
|
|
832
1008
|
return protectedFetch;
|
|
833
1009
|
};
|
|
834
1010
|
|
|
1011
|
+
// Manually set version - update this with each release
|
|
1012
|
+
const packageVersion = '0.1.10';
|
|
835
1013
|
// Check if we're in an iframe (development environment) and initialize dev UI
|
|
836
1014
|
if (typeof window !== 'undefined' && window.self !== window.top) {
|
|
837
|
-
initializeDevUI();
|
|
1015
|
+
initializeDevUI(packageVersion);
|
|
838
1016
|
}
|
|
839
1017
|
|
|
840
1018
|
export { SecurityGate, SpektreContext, SpektreProvider, useProtectedFetch, useSpektre };
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var React = require('react');
|
|
4
4
|
|
|
5
5
|
const DEV_UI_STORAGE_KEY = 'spektre_dev_enabled';
|
|
6
|
+
let DEV_UI_VERSION = '1.0.0';
|
|
6
7
|
// Inject styles directly into the document
|
|
7
8
|
function injectDevUIStyles() {
|
|
8
9
|
if (document.getElementById('spektre-dev-ui-styles')) {
|
|
@@ -46,6 +47,21 @@ function injectDevUIStyles() {
|
|
|
46
47
|
box-shadow: 0 6px 16px rgba(100, 116, 139, 0.6);
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
.spektre-dev-dot.warning {
|
|
51
|
+
background-color: #ef4444;
|
|
52
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.6);
|
|
53
|
+
animation: spektre-pulse 2s infinite;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@keyframes spektre-pulse {
|
|
57
|
+
0%, 100% {
|
|
58
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.6);
|
|
59
|
+
}
|
|
60
|
+
50% {
|
|
61
|
+
box-shadow: 0 4px 20px rgba(239, 68, 68, 0.8);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
/* Modal Overlay */
|
|
50
66
|
.spektre-dev-modal-overlay {
|
|
51
67
|
position: fixed;
|
|
@@ -136,6 +152,8 @@ function injectDevUIStyles() {
|
|
|
136
152
|
.spektre-dev-modal-body {
|
|
137
153
|
padding: 24px;
|
|
138
154
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
155
|
+
max-height: 60vh;
|
|
156
|
+
overflow-y: auto;
|
|
139
157
|
}
|
|
140
158
|
|
|
141
159
|
.spektre-dev-modal-info {
|
|
@@ -242,6 +260,52 @@ function injectDevUIStyles() {
|
|
|
242
260
|
font-weight: 500;
|
|
243
261
|
}
|
|
244
262
|
|
|
263
|
+
/* Security Scan Results */
|
|
264
|
+
.spektre-security-scan-container {
|
|
265
|
+
margin-top: 16px;
|
|
266
|
+
padding: 16px;
|
|
267
|
+
background-color: rgba(239, 68, 68, 0.1);
|
|
268
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
269
|
+
border-radius: 8px;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.spektre-security-scan-title {
|
|
273
|
+
font-size: 14px;
|
|
274
|
+
font-weight: 600;
|
|
275
|
+
color: #fca5a5;
|
|
276
|
+
margin: 0 0 12px 0;
|
|
277
|
+
display: flex;
|
|
278
|
+
align-items: center;
|
|
279
|
+
gap: 8px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.spektre-security-scan-issues {
|
|
283
|
+
display: flex;
|
|
284
|
+
flex-direction: column;
|
|
285
|
+
gap: 8px;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.spektre-security-issue {
|
|
289
|
+
font-size: 13px;
|
|
290
|
+
color: #fed7aa;
|
|
291
|
+
padding: 8px;
|
|
292
|
+
background-color: rgba(239, 68, 68, 0.15);
|
|
293
|
+
border-left: 3px solid #ef4444;
|
|
294
|
+
border-radius: 4px;
|
|
295
|
+
line-height: 1.4;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.spektre-security-scan-clean {
|
|
299
|
+
font-size: 13px;
|
|
300
|
+
color: #86efac;
|
|
301
|
+
padding: 12px;
|
|
302
|
+
background-color: rgba(34, 197, 94, 0.15);
|
|
303
|
+
border: 1px solid rgba(34, 197, 94, 0.3);
|
|
304
|
+
border-radius: 6px;
|
|
305
|
+
text-align: center;
|
|
306
|
+
font-weight: 500;
|
|
307
|
+
}
|
|
308
|
+
|
|
245
309
|
/* Accessibility */
|
|
246
310
|
@media (prefers-reduced-motion: reduce) {
|
|
247
311
|
.spektre-dev-dot,
|
|
@@ -286,7 +350,70 @@ function injectDevUIStyles() {
|
|
|
286
350
|
`;
|
|
287
351
|
document.head.appendChild(styleSheet);
|
|
288
352
|
}
|
|
289
|
-
function
|
|
353
|
+
function runSecurityScan() {
|
|
354
|
+
const issues = [];
|
|
355
|
+
// 1. Check for hardcoded API keys/tokens
|
|
356
|
+
const sensitivePatterns = [
|
|
357
|
+
{ pattern: /api[_-]?key\s*[:=]\s*['"]\S+['"]/gi, label: 'API Key' },
|
|
358
|
+
{ pattern: /token\s*[:=]\s*['"]\S+['"]/gi, label: 'Token' },
|
|
359
|
+
{ pattern: /password\s*[:=]\s*['"]\S+['"]/gi, label: 'Password' },
|
|
360
|
+
{ pattern: /secret\s*[:=]\s*['"]\S+['"]/gi, label: 'Secret' },
|
|
361
|
+
{ pattern: /bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, label: 'Bearer Token' },
|
|
362
|
+
];
|
|
363
|
+
const pageHTML = document.documentElement.outerHTML;
|
|
364
|
+
const scriptTags = Array.from(document.querySelectorAll('script'));
|
|
365
|
+
const allText = pageHTML + scriptTags.map(s => s.textContent).join('\n');
|
|
366
|
+
sensitivePatterns.forEach(({ pattern, label }) => {
|
|
367
|
+
if (pattern.test(allText)) {
|
|
368
|
+
issues.push(`⚠️ Found hardcoded ${label} in page code`);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
// 2. Check for dangerous functions
|
|
372
|
+
const dangerousPatterns = [
|
|
373
|
+
{ pattern: /eval\s*\(/gi, label: 'eval() usage' },
|
|
374
|
+
{ pattern: /innerHTML\s*=/gi, label: 'innerHTML manipulation' },
|
|
375
|
+
{ pattern: /dangerouslySetInnerHTML/gi, label: 'dangerouslySetInnerHTML' },
|
|
376
|
+
{ pattern: /document\.write/gi, label: 'document.write()' },
|
|
377
|
+
{ pattern: /setTimeout\s*\(\s*['"`].*['"`]/gi, label: 'setTimeout with string' },
|
|
378
|
+
];
|
|
379
|
+
dangerousPatterns.forEach(({ pattern, label }) => {
|
|
380
|
+
if (pattern.test(allText)) {
|
|
381
|
+
issues.push(`⚠️ Potential security risk: ${label}`);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
// 3. Check localStorage for sensitive data
|
|
385
|
+
try {
|
|
386
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
387
|
+
const key = localStorage.key(i);
|
|
388
|
+
if (key) {
|
|
389
|
+
const value = localStorage.getItem(key);
|
|
390
|
+
if (value) {
|
|
391
|
+
if (/token|password|secret|key|auth/i.test(key) &&
|
|
392
|
+
value.length > 20) {
|
|
393
|
+
issues.push(`⚠️ Sensitive data in localStorage: "${key}"`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
catch (e) {
|
|
400
|
+
// localStorage access denied, skip
|
|
401
|
+
}
|
|
402
|
+
// 4. Check for missing CSP headers (via console warning detection)
|
|
403
|
+
const scripts = scriptTags.filter(s => s.src && !s.src.includes('spektre'));
|
|
404
|
+
if (scripts.length > 10) {
|
|
405
|
+
issues.push(`⚠️ Many external scripts loaded (${scripts.length})`);
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
hasIssues: issues.length > 0,
|
|
409
|
+
issues,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function initializeDevUI(version) {
|
|
413
|
+
// Set version if provided
|
|
414
|
+
{
|
|
415
|
+
DEV_UI_VERSION = version;
|
|
416
|
+
}
|
|
290
417
|
// Inject styles first
|
|
291
418
|
injectDevUIStyles();
|
|
292
419
|
// Check if Spektre is enabled in localStorage (default to true)
|
|
@@ -298,6 +425,18 @@ function initializeDevUI() {
|
|
|
298
425
|
}
|
|
299
426
|
// Create and inject the orange dot
|
|
300
427
|
createDevDot();
|
|
428
|
+
// Run security scan after a short delay to ensure DOM is ready
|
|
429
|
+
setTimeout(() => {
|
|
430
|
+
const scanResult = runSecurityScan();
|
|
431
|
+
if (scanResult.hasIssues) {
|
|
432
|
+
// Update dot to warning state and auto-open modal
|
|
433
|
+
const dot = document.getElementById('spektre-dev-dot');
|
|
434
|
+
if (dot) {
|
|
435
|
+
dot.classList.add('warning');
|
|
436
|
+
}
|
|
437
|
+
openDevModal(scanResult);
|
|
438
|
+
}
|
|
439
|
+
}, 500);
|
|
301
440
|
}
|
|
302
441
|
function createDevDot() {
|
|
303
442
|
const dot = document.createElement('div');
|
|
@@ -309,7 +448,7 @@ function createDevDot() {
|
|
|
309
448
|
});
|
|
310
449
|
document.body.appendChild(dot);
|
|
311
450
|
}
|
|
312
|
-
function openDevModal() {
|
|
451
|
+
function openDevModal(scanResult) {
|
|
313
452
|
// Check if modal already exists
|
|
314
453
|
if (document.getElementById('spektre-dev-modal')) {
|
|
315
454
|
return;
|
|
@@ -376,6 +515,43 @@ function openDevModal() {
|
|
|
376
515
|
body.appendChild(statusBadge);
|
|
377
516
|
body.appendChild(toggleContainer);
|
|
378
517
|
body.appendChild(reloadMessage);
|
|
518
|
+
// Add security scan results if available
|
|
519
|
+
if (scanResult) {
|
|
520
|
+
const scanContainer = document.createElement('div');
|
|
521
|
+
scanContainer.className = 'spektre-security-scan-container';
|
|
522
|
+
const scanTitle = document.createElement('div');
|
|
523
|
+
scanTitle.className = 'spektre-security-scan-title';
|
|
524
|
+
scanTitle.innerHTML = '🔒 Security Check';
|
|
525
|
+
scanContainer.appendChild(scanTitle);
|
|
526
|
+
if (scanResult.hasIssues) {
|
|
527
|
+
const issuesContainer = document.createElement('div');
|
|
528
|
+
issuesContainer.className = 'spektre-security-scan-issues';
|
|
529
|
+
scanResult.issues.forEach(issue => {
|
|
530
|
+
const issueElement = document.createElement('div');
|
|
531
|
+
issueElement.className = 'spektre-security-issue';
|
|
532
|
+
issueElement.textContent = issue;
|
|
533
|
+
issuesContainer.appendChild(issueElement);
|
|
534
|
+
});
|
|
535
|
+
scanContainer.appendChild(issuesContainer);
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
const cleanMessage = document.createElement('div');
|
|
539
|
+
cleanMessage.className = 'spektre-security-scan-clean';
|
|
540
|
+
cleanMessage.textContent = '✓ No security issues detected';
|
|
541
|
+
scanContainer.appendChild(cleanMessage);
|
|
542
|
+
}
|
|
543
|
+
body.appendChild(scanContainer);
|
|
544
|
+
}
|
|
545
|
+
// Add version label at bottom
|
|
546
|
+
const versionLabel = document.createElement('div');
|
|
547
|
+
versionLabel.style.marginTop = '16px';
|
|
548
|
+
versionLabel.style.paddingTop = '12px';
|
|
549
|
+
versionLabel.style.borderTop = '1px solid rgba(71, 85, 105, 0.3)';
|
|
550
|
+
versionLabel.style.fontSize = '12px';
|
|
551
|
+
versionLabel.style.color = '#64748b';
|
|
552
|
+
versionLabel.style.textAlign = 'center';
|
|
553
|
+
versionLabel.textContent = `Spektre v${DEV_UI_VERSION}`;
|
|
554
|
+
body.appendChild(versionLabel);
|
|
379
555
|
modalContent.appendChild(header);
|
|
380
556
|
modalContent.appendChild(body);
|
|
381
557
|
modal.appendChild(modalContent);
|
|
@@ -834,9 +1010,11 @@ const useProtectedFetch = () => {
|
|
|
834
1010
|
return protectedFetch;
|
|
835
1011
|
};
|
|
836
1012
|
|
|
1013
|
+
// Manually set version - update this with each release
|
|
1014
|
+
const packageVersion = '0.1.10';
|
|
837
1015
|
// Check if we're in an iframe (development environment) and initialize dev UI
|
|
838
1016
|
if (typeof window !== 'undefined' && window.self !== window.top) {
|
|
839
|
-
initializeDevUI();
|
|
1017
|
+
initializeDevUI(packageVersion);
|
|
840
1018
|
}
|
|
841
1019
|
|
|
842
1020
|
exports.SecurityGate = SecurityGate;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|