@spektre/veil 1.0.3 → 1.0.4

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/index.esm.js CHANGED
@@ -1,238 +1,7 @@
1
1
  import React, { createContext, useState, useEffect, useContext } from 'react';
2
2
 
3
- // src/analysis/sourceMapAnalyzer.ts
4
- // Reads all code via source mapping and generates security analysis
5
- let cachedSourceMap = null;
6
- let cachedSourceCode = new Map();
7
- /**
8
- * Fetch and parse the source map
9
- */
10
- async function fetchSourceMap(sourceMapUrl) {
11
- if (cachedSourceMap) {
12
- return cachedSourceMap;
13
- }
14
- // Try multiple possible locations for source map
15
- const possibleUrls = [
16
- sourceMapUrl || `${window.location.origin}/index.js.map`,
17
- `${window.location.origin}/dist/index.js.map`,
18
- `${window.location.origin}/dist/index.esm.js.map`,
19
- '/index.js.map',
20
- '/dist/index.js.map',
21
- '/dist/index.esm.js.map',
22
- ];
23
- for (const url of possibleUrls) {
24
- try {
25
- const response = await fetch(url);
26
- if (response.ok) {
27
- cachedSourceMap = await response.json();
28
- console.log('[Spektre] Source map loaded from:', url);
29
- return cachedSourceMap;
30
- }
31
- }
32
- catch (error) {
33
- // Continue to next URL
34
- }
35
- }
36
- console.warn('[Spektre] Could not find source map at any location:', possibleUrls);
37
- return null;
38
- }
39
- /**
40
- * Extract all source code from source map
41
- */
42
- async function extractAllSourceCode(sourceMap) {
43
- if (cachedSourceCode.size > 0) {
44
- return cachedSourceCode;
45
- }
46
- try {
47
- if (!sourceMap || !sourceMap.sourcesContent) {
48
- return cachedSourceCode;
49
- }
50
- sourceMap.sources.forEach((file, index) => {
51
- const content = sourceMap.sourcesContent[index];
52
- if (content) {
53
- cachedSourceCode.set(file, content);
54
- }
55
- });
56
- return cachedSourceCode;
57
- }
58
- catch (error) {
59
- console.warn('[Spektre] Error extracting source code:', error);
60
- return cachedSourceCode;
61
- }
62
- }
63
- /**
64
- * Security analysis patterns to check
65
- */
66
- const SECURITY_PATTERNS = [
67
- {
68
- name: 'eval() usage',
69
- pattern: /\beval\s*\(/g,
70
- severity: 'critical',
71
- message: 'eval() is extremely dangerous and should never be used',
72
- },
73
- {
74
- name: 'innerHTML assignment',
75
- pattern: /\.innerHTML\s*=/g,
76
- severity: 'high',
77
- message: 'Direct innerHTML assignment can lead to XSS vulnerabilities',
78
- },
79
- {
80
- name: 'dangerouslySetInnerHTML',
81
- pattern: /dangerouslySetInnerHTML/g,
82
- severity: 'high',
83
- message: 'dangerouslySetInnerHTML should only be used with sanitized content',
84
- },
85
- {
86
- name: 'document.write()',
87
- pattern: /document\.write\s*\(/g,
88
- severity: 'medium',
89
- message: 'document.write() can overwrite the entire document',
90
- },
91
- {
92
- name: 'setTimeout with string',
93
- pattern: /setTimeout\s*\(\s*['"]/g,
94
- severity: 'high',
95
- message: 'Passing strings to setTimeout is equivalent to eval()',
96
- },
97
- {
98
- name: 'Hardcoded API key',
99
- pattern: /(?:api[_-]?key|token|password|secret)\s*[:=]\s*['"](.*?)['"]/gi,
100
- severity: 'critical',
101
- message: 'Hardcoded credentials found in code',
102
- },
103
- {
104
- name: 'No input validation',
105
- pattern: /innerHTML\s*=\s*(?:userInput|params|query|request)/gi,
106
- severity: 'high',
107
- message: 'Unvalidated user input assigned to DOM',
108
- },
109
- {
110
- name: 'SQL injection risk',
111
- pattern: /query\s*=\s*['"]\s*\+|query\s*=\s*`.*\$/gi,
112
- severity: 'high',
113
- message: 'String concatenation detected in database query',
114
- },
115
- {
116
- name: 'Unsafe regex',
117
- pattern: /RegExp\s*\(\s*['"].*\*.*['"]\s*\)/g,
118
- severity: 'medium',
119
- message: 'Unsafe regex pattern could cause ReDoS attacks',
120
- },
121
- {
122
- name: 'Missing HTTPS',
123
- pattern: /http:\/\/(?!localhost)/gi,
124
- severity: 'high',
125
- message: 'Non-HTTPS URL detected',
126
- },
127
- ];
128
- /**
129
- * Analyze source code for security issues
130
- */
131
- function analyzeSourceCode(sourceCode, fileName) {
132
- const issues = [];
133
- const lines = sourceCode.split('\n');
134
- SECURITY_PATTERNS.forEach(({ name, pattern, severity, message }) => {
135
- let match;
136
- const patternWithIndices = new RegExp(pattern.source, pattern.flags + 'g');
137
- while ((match = patternWithIndices.exec(sourceCode)) !== null) {
138
- // Calculate line number from string position
139
- const lineNumber = sourceCode.substring(0, match.index).split('\n').length;
140
- const lineContent = lines[lineNumber - 1] || '';
141
- const columnNumber = match.index - sourceCode.lastIndexOf('\n', match.index);
142
- // Avoid duplicate issues on same line
143
- const isDuplicate = issues.some(issue => issue.file === fileName &&
144
- issue.line === lineNumber &&
145
- issue.type === name);
146
- if (!isDuplicate) {
147
- issues.push({
148
- type: name,
149
- severity,
150
- file: fileName,
151
- line: lineNumber,
152
- column: columnNumber,
153
- message,
154
- code: lineContent.trim(),
155
- });
156
- }
157
- }
158
- });
159
- return issues;
160
- }
161
- /**
162
- * Generate full security analysis of all source code
163
- */
164
- async function generateSecurityAnalysis(sourceMapUrl) {
165
- const sourceMap = await fetchSourceMap(sourceMapUrl);
166
- if (!sourceMap) {
167
- return {
168
- timestamp: new Date().toISOString(),
169
- totalIssuesFound: 0,
170
- criticalCount: 0,
171
- highCount: 0,
172
- mediumCount: 0,
173
- lowCount: 0,
174
- issues: [],
175
- summary: 'Could not load source map for analysis. Make sure your build includes source maps (sourcemap: true in rollup config).',
176
- };
177
- }
178
- const sourceCode = await extractAllSourceCode(sourceMap);
179
- const allIssues = [];
180
- // Analyze each source file
181
- sourceCode.forEach((code, fileName) => {
182
- const issues = analyzeSourceCode(code, fileName);
183
- allIssues.push(...issues);
184
- });
185
- // Sort by severity
186
- const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
187
- allIssues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
188
- // Count issues by severity
189
- const criticalCount = allIssues.filter(i => i.severity === 'critical').length;
190
- const highCount = allIssues.filter(i => i.severity === 'high').length;
191
- const mediumCount = allIssues.filter(i => i.severity === 'medium').length;
192
- const lowCount = allIssues.filter(i => i.severity === 'low').length;
193
- // Generate summary
194
- const summary = generateSummary(criticalCount, highCount, mediumCount, lowCount);
195
- return {
196
- timestamp: new Date().toISOString(),
197
- totalIssuesFound: allIssues.length,
198
- criticalCount,
199
- highCount,
200
- mediumCount,
201
- lowCount,
202
- issues: allIssues,
203
- summary,
204
- };
205
- }
206
- /**
207
- * Generate human-readable summary
208
- */
209
- function generateSummary(critical, high, medium, low) {
210
- const total = critical + high + medium + low;
211
- if (total === 0) {
212
- return '✅ No security issues detected! Your code looks good.';
213
- }
214
- let summary = `Found ${total} potential security issue${total !== 1 ? 's' : ''}: `;
215
- const parts = [];
216
- if (critical > 0)
217
- parts.push(`${critical} critical`);
218
- if (high > 0)
219
- parts.push(`${high} high`);
220
- if (medium > 0)
221
- parts.push(`${medium} medium`);
222
- if (low > 0)
223
- parts.push(`${low} low`);
224
- summary += parts.join(', ') + '.';
225
- if (critical > 0) {
226
- summary += ' ⚠️ Critical issues must be fixed before deployment.';
227
- }
228
- else if (high > 0) {
229
- summary += ' Please review and fix high-severity issues.';
230
- }
231
- return summary;
232
- }
233
-
234
3
  const DEV_UI_STORAGE_KEY = 'spektre_dev_enabled';
235
- let DEV_UI_VERSION = '1.0.3';
4
+ let DEV_UI_VERSION = '1.0.4';
236
5
  // Inject styles directly into the document
237
6
  function injectDevUIStyles() {
238
7
  if (document.getElementById('spektre-dev-ui-styles')) {
@@ -477,31 +246,6 @@ function injectDevUIStyles() {
477
246
  box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.2);
478
247
  }
479
248
 
480
- /* Analysis Button */
481
- .spektre-analysis-button {
482
- width: 100%;
483
- padding: 12px 16px;
484
- background-color: rgba(249, 115, 22, 0.2);
485
- border: 1px solid rgba(249, 115, 22, 0.5);
486
- color: #fed7aa;
487
- border-radius: 8px;
488
- cursor: pointer;
489
- font-size: 14px;
490
- font-weight: 500;
491
- transition: all 0.2s ease;
492
- margin-bottom: 16px;
493
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
494
- }
495
-
496
- .spektre-analysis-button:hover {
497
- background-color: rgba(249, 115, 22, 0.3);
498
- box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.2);
499
- }
500
-
501
- .spektre-analysis-button:active {
502
- transform: scale(0.98);
503
- }
504
-
505
249
  /* Reload Message */
506
250
  .spektre-dev-modal-reload-msg {
507
251
  margin: 0;
@@ -691,294 +435,6 @@ function runSecurityScan() {
691
435
  issues,
692
436
  };
693
437
  }
694
- async function openAnalysisModal() {
695
- // Check if modal already exists
696
- if (document.getElementById('spektre-analysis-modal')) {
697
- return;
698
- }
699
- // Show loading state
700
- const loadingOverlay = document.createElement('div');
701
- loadingOverlay.style.cssText = `
702
- position: fixed;
703
- top: 0;
704
- left: 0;
705
- right: 0;
706
- bottom: 0;
707
- background-color: rgba(0, 0, 0, 0.7);
708
- display: flex;
709
- align-items: center;
710
- justify-content: center;
711
- z-index: 9999;
712
- `;
713
- const loadingModal = document.createElement('div');
714
- loadingModal.style.cssText = `
715
- background-color: #000000;
716
- border: 1px solid #1e293b;
717
- border-radius: 12px;
718
- padding: 40px;
719
- text-align: center;
720
- color: #ffffff;
721
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
722
- `;
723
- loadingModal.innerHTML = `
724
- <div style="width: 40px; height: 40px; border: 4px solid rgba(71, 85, 105, 0.3); border-top-color: #f97316; border-radius: 50%; margin: 0 auto 16px; animation: spin 1s linear infinite;"></div>
725
- <p style="margin: 0; font-size: 16px; color: #cbd5e1;">Analyzing your code...</p>
726
- `;
727
- loadingOverlay.appendChild(loadingModal);
728
- document.body.appendChild(loadingOverlay);
729
- try {
730
- // Generate analysis
731
- const sourceMapUrl = `${window.location.origin}/index.js.map`;
732
- const analysis = await generateSecurityAnalysis(sourceMapUrl);
733
- // Remove loading modal
734
- loadingOverlay.remove();
735
- // Show analysis modal
736
- createAnalysisModal(analysis);
737
- }
738
- catch (error) {
739
- console.error('[Spektre] Analysis failed:', error);
740
- loadingOverlay.remove();
741
- }
742
- }
743
- function createAnalysisModal(analysis) {
744
- const overlay = document.createElement('div');
745
- overlay.id = 'spektre-analysis-modal';
746
- overlay.style.cssText = `
747
- position: fixed;
748
- top: 0;
749
- left: 0;
750
- right: 0;
751
- bottom: 0;
752
- background-color: rgba(0, 0, 0, 0.7);
753
- display: flex;
754
- align-items: center;
755
- justify-content: center;
756
- z-index: 9999;
757
- animation: spektre-fade-in 0.2s ease;
758
- `;
759
- const modal = document.createElement('div');
760
- modal.style.cssText = `
761
- background-color: #000000;
762
- border: 1px solid #1e293b;
763
- border-radius: 12px;
764
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
765
- width: 90%;
766
- max-width: 700px;
767
- max-height: 80vh;
768
- overflow: hidden;
769
- display: flex;
770
- flex-direction: column;
771
- animation: spektre-slide-up 0.3s ease;
772
- `;
773
- // Header
774
- const header = document.createElement('div');
775
- header.style.cssText = `
776
- display: flex;
777
- justify-content: space-between;
778
- align-items: center;
779
- padding: 20px;
780
- background: linear-gradient(135deg, rgba(249, 115, 22, 0.1) 0%, rgba(0, 0, 0, 0.5) 100%);
781
- border-bottom: 1px solid #1e293b;
782
- `;
783
- const title = document.createElement('h2');
784
- title.style.cssText = `
785
- margin: 0;
786
- font-size: 18px;
787
- font-weight: 600;
788
- color: #ffffff;
789
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
790
- `;
791
- title.textContent = '🔒 Security Analysis Report';
792
- const closeBtn = document.createElement('button');
793
- closeBtn.style.cssText = `
794
- background: none;
795
- border: none;
796
- color: #94a3b8;
797
- font-size: 24px;
798
- cursor: pointer;
799
- padding: 0;
800
- width: 28px;
801
- height: 28px;
802
- display: flex;
803
- align-items: center;
804
- justify-content: center;
805
- transition: color 0.2s ease;
806
- border-radius: 4px;
807
- `;
808
- closeBtn.innerHTML = '✕';
809
- closeBtn.addEventListener('click', () => overlay.remove());
810
- closeBtn.addEventListener('mouseover', () => (closeBtn.style.color = '#f97316'));
811
- closeBtn.addEventListener('mouseout', () => (closeBtn.style.color = '#94a3b8'));
812
- header.appendChild(title);
813
- header.appendChild(closeBtn);
814
- // Body
815
- const body = document.createElement('div');
816
- body.style.cssText = `
817
- padding: 24px;
818
- overflow-y: auto;
819
- flex: 1;
820
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
821
- font-size: 14px;
822
- color: #cbd5e1;
823
- line-height: 1.6;
824
- `;
825
- // Summary
826
- const summaryDiv = document.createElement('div');
827
- summaryDiv.style.cssText = `
828
- background-color: ${analysis.totalIssuesFound === 0 ? 'rgba(34, 197, 94, 0.1)' : 'rgba(239, 68, 68, 0.1)'};
829
- border: 1px solid ${analysis.totalIssuesFound === 0 ? 'rgba(34, 197, 94, 0.3)' : 'rgba(239, 68, 68, 0.3)'};
830
- border-radius: 8px;
831
- padding: 16px;
832
- margin-bottom: 24px;
833
- `;
834
- const summaryText = document.createElement('p');
835
- summaryText.style.cssText = `
836
- margin: 0;
837
- font-size: 15px;
838
- font-weight: 500;
839
- color: ${analysis.totalIssuesFound === 0 ? '#86efac' : '#fed7aa'};
840
- `;
841
- summaryText.textContent = analysis.summary;
842
- summaryDiv.appendChild(summaryText);
843
- body.appendChild(summaryDiv);
844
- // Stats
845
- const statsDiv = document.createElement('div');
846
- statsDiv.style.cssText = `
847
- display: grid;
848
- grid-template-columns: repeat(4, 1fr);
849
- gap: 12px;
850
- margin-bottom: 24px;
851
- `;
852
- const stats = [
853
- { label: 'Critical', count: analysis.criticalCount, color: '#ef4444' },
854
- { label: 'High', count: analysis.highCount, color: '#f97316' },
855
- { label: 'Medium', count: analysis.mediumCount, color: '#eab308' },
856
- { label: 'Low', count: analysis.lowCount, color: '#22c55e' },
857
- ];
858
- stats.forEach(stat => {
859
- const statCard = document.createElement('div');
860
- statCard.style.cssText = `
861
- background-color: rgba(15, 23, 42, 0.8);
862
- border: 1px solid rgba(71, 85, 105, 0.3);
863
- border-radius: 6px;
864
- padding: 12px;
865
- text-align: center;
866
- `;
867
- const count = document.createElement('div');
868
- count.style.cssText = `
869
- font-size: 24px;
870
- font-weight: 600;
871
- color: ${stat.color};
872
- margin-bottom: 4px;
873
- `;
874
- count.textContent = String(stat.count);
875
- const label = document.createElement('div');
876
- label.style.cssText = `
877
- font-size: 12px;
878
- color: #94a3b8;
879
- text-transform: uppercase;
880
- `;
881
- label.textContent = stat.label;
882
- statCard.appendChild(count);
883
- statCard.appendChild(label);
884
- statsDiv.appendChild(statCard);
885
- });
886
- body.appendChild(statsDiv);
887
- // Issues list
888
- if (analysis.issues.length > 0) {
889
- const issuesTitle = document.createElement('h3');
890
- issuesTitle.style.cssText = `
891
- margin: 0 0 16px 0;
892
- font-size: 16px;
893
- font-weight: 600;
894
- color: #ffffff;
895
- `;
896
- issuesTitle.textContent = `${analysis.issues.length} Issue${analysis.issues.length !== 1 ? 's' : ''} Found`;
897
- body.appendChild(issuesTitle);
898
- analysis.issues.forEach((issue) => {
899
- const issueCard = document.createElement('div');
900
- issueCard.style.cssText = `
901
- background-color: rgba(15, 23, 42, 0.6);
902
- border-left: 4px solid ${issue.severity === 'critical'
903
- ? '#ef4444'
904
- : issue.severity === 'high'
905
- ? '#f97316'
906
- : issue.severity === 'medium'
907
- ? '#eab308'
908
- : '#22c55e'};
909
- border-radius: 6px;
910
- padding: 12px;
911
- margin-bottom: 12px;
912
- `;
913
- issueCard.innerHTML = `
914
- <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 8px;">
915
- <div style="font-weight: 600; color: #ffffff;">${escapeHtml(issue.type)}</div>
916
- <div style="background-color: rgba(71, 85, 105, 0.3); padding: 2px 8px; border-radius: 4px; font-size: 12px; color: #94a3b8; text-transform: uppercase;">${issue.severity}</div>
917
- </div>
918
- <div style="font-size: 13px; color: #cbd5e1; margin-bottom: 8px;">${escapeHtml(issue.message)}</div>
919
- <div style="font-size: 12px; color: #64748b; margin-bottom: 8px;">
920
- 📍 ${escapeHtml(issue.file)}:${issue.line}:${issue.column}
921
- </div>
922
- <div style="background-color: rgba(0, 0, 0, 0.3); padding: 8px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #cbd5e1; overflow-x: auto;">
923
- ${escapeHtml(issue.code)}
924
- </div>
925
- `;
926
- body.appendChild(issueCard);
927
- });
928
- }
929
- else {
930
- const noIssues = document.createElement('div');
931
- noIssues.style.cssText = `
932
- text-align: center;
933
- padding: 40px 20px;
934
- color: #86efac;
935
- `;
936
- noIssues.innerHTML = `
937
- <div style="font-size: 48px; margin-bottom: 16px;">✅</div>
938
- <p style="margin: 0; font-size: 16px; font-weight: 500;">No security issues detected!</p>
939
- <p style="margin: 8px 0 0 0; font-size: 14px; color: #64748b;">Your code looks great.</p>
940
- `;
941
- body.appendChild(noIssues);
942
- }
943
- // Footer
944
- const footer = document.createElement('div');
945
- footer.style.cssText = `
946
- padding: 16px 24px;
947
- background-color: #000000;
948
- border-top: 1px solid #1e293b;
949
- font-size: 12px;
950
- color: #64748b;
951
- text-align: center;
952
- `;
953
- footer.textContent = `Generated: ${new Date(analysis.timestamp).toLocaleString()}`;
954
- modal.appendChild(header);
955
- modal.appendChild(body);
956
- modal.appendChild(footer);
957
- overlay.appendChild(modal);
958
- // Close on overlay click
959
- overlay.addEventListener('click', e => {
960
- if (e.target === overlay) {
961
- overlay.remove();
962
- }
963
- });
964
- // Close on escape
965
- document.addEventListener('keydown', e => {
966
- if (e.key === 'Escape' && document.getElementById('spektre-analysis-modal')) {
967
- overlay.remove();
968
- }
969
- }, { once: true });
970
- document.body.appendChild(overlay);
971
- }
972
- function escapeHtml(text) {
973
- const map = {
974
- '&': '&amp;',
975
- '<': '&lt;',
976
- '>': '&gt;',
977
- '"': '&quot;',
978
- "'": '&#039;',
979
- };
980
- return text.replace(/[&<>"']/g, m => map[m]);
981
- }
982
438
  function initializeDevUI(version) {
983
439
  // Set version if provided
984
440
  {
@@ -1086,15 +542,6 @@ function openDevModal(scanResult) {
1086
542
  body.appendChild(infoText);
1087
543
  body.appendChild(statusBadge);
1088
544
  body.appendChild(toggleContainer);
1089
- // Add analysis button
1090
- const analysisButton = document.createElement('button');
1091
- analysisButton.className = 'spektre-analysis-button';
1092
- analysisButton.textContent = '📊 Run Full Code Analysis';
1093
- analysisButton.addEventListener('click', () => {
1094
- modal.remove();
1095
- openAnalysisModal();
1096
- });
1097
- body.appendChild(analysisButton);
1098
545
  body.appendChild(reloadMessage);
1099
546
  // Add security scan results if available
1100
547
  if (scanResult) {
@@ -1594,7 +1041,7 @@ const useProtectedFetch = () => {
1594
1041
  };
1595
1042
 
1596
1043
  // Manually set version - update this with each release
1597
- const packageVersion = '1.0.0';
1044
+ const packageVersion = '1.0.4';
1598
1045
  // Check if we're in an iframe (development environment) and initialize dev UI
1599
1046
  if (typeof window !== 'undefined' && window.self !== window.top) {
1600
1047
  initializeDevUI(packageVersion);
@@ -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
@@ -2,239 +2,8 @@
2
2
 
3
3
  var React = require('react');
4
4
 
5
- // src/analysis/sourceMapAnalyzer.ts
6
- // Reads all code via source mapping and generates security analysis
7
- let cachedSourceMap = null;
8
- let cachedSourceCode = new Map();
9
- /**
10
- * Fetch and parse the source map
11
- */
12
- async function fetchSourceMap(sourceMapUrl) {
13
- if (cachedSourceMap) {
14
- return cachedSourceMap;
15
- }
16
- // Try multiple possible locations for source map
17
- const possibleUrls = [
18
- sourceMapUrl || `${window.location.origin}/index.js.map`,
19
- `${window.location.origin}/dist/index.js.map`,
20
- `${window.location.origin}/dist/index.esm.js.map`,
21
- '/index.js.map',
22
- '/dist/index.js.map',
23
- '/dist/index.esm.js.map',
24
- ];
25
- for (const url of possibleUrls) {
26
- try {
27
- const response = await fetch(url);
28
- if (response.ok) {
29
- cachedSourceMap = await response.json();
30
- console.log('[Spektre] Source map loaded from:', url);
31
- return cachedSourceMap;
32
- }
33
- }
34
- catch (error) {
35
- // Continue to next URL
36
- }
37
- }
38
- console.warn('[Spektre] Could not find source map at any location:', possibleUrls);
39
- return null;
40
- }
41
- /**
42
- * Extract all source code from source map
43
- */
44
- async function extractAllSourceCode(sourceMap) {
45
- if (cachedSourceCode.size > 0) {
46
- return cachedSourceCode;
47
- }
48
- try {
49
- if (!sourceMap || !sourceMap.sourcesContent) {
50
- return cachedSourceCode;
51
- }
52
- sourceMap.sources.forEach((file, index) => {
53
- const content = sourceMap.sourcesContent[index];
54
- if (content) {
55
- cachedSourceCode.set(file, content);
56
- }
57
- });
58
- return cachedSourceCode;
59
- }
60
- catch (error) {
61
- console.warn('[Spektre] Error extracting source code:', error);
62
- return cachedSourceCode;
63
- }
64
- }
65
- /**
66
- * Security analysis patterns to check
67
- */
68
- const SECURITY_PATTERNS = [
69
- {
70
- name: 'eval() usage',
71
- pattern: /\beval\s*\(/g,
72
- severity: 'critical',
73
- message: 'eval() is extremely dangerous and should never be used',
74
- },
75
- {
76
- name: 'innerHTML assignment',
77
- pattern: /\.innerHTML\s*=/g,
78
- severity: 'high',
79
- message: 'Direct innerHTML assignment can lead to XSS vulnerabilities',
80
- },
81
- {
82
- name: 'dangerouslySetInnerHTML',
83
- pattern: /dangerouslySetInnerHTML/g,
84
- severity: 'high',
85
- message: 'dangerouslySetInnerHTML should only be used with sanitized content',
86
- },
87
- {
88
- name: 'document.write()',
89
- pattern: /document\.write\s*\(/g,
90
- severity: 'medium',
91
- message: 'document.write() can overwrite the entire document',
92
- },
93
- {
94
- name: 'setTimeout with string',
95
- pattern: /setTimeout\s*\(\s*['"]/g,
96
- severity: 'high',
97
- message: 'Passing strings to setTimeout is equivalent to eval()',
98
- },
99
- {
100
- name: 'Hardcoded API key',
101
- pattern: /(?:api[_-]?key|token|password|secret)\s*[:=]\s*['"](.*?)['"]/gi,
102
- severity: 'critical',
103
- message: 'Hardcoded credentials found in code',
104
- },
105
- {
106
- name: 'No input validation',
107
- pattern: /innerHTML\s*=\s*(?:userInput|params|query|request)/gi,
108
- severity: 'high',
109
- message: 'Unvalidated user input assigned to DOM',
110
- },
111
- {
112
- name: 'SQL injection risk',
113
- pattern: /query\s*=\s*['"]\s*\+|query\s*=\s*`.*\$/gi,
114
- severity: 'high',
115
- message: 'String concatenation detected in database query',
116
- },
117
- {
118
- name: 'Unsafe regex',
119
- pattern: /RegExp\s*\(\s*['"].*\*.*['"]\s*\)/g,
120
- severity: 'medium',
121
- message: 'Unsafe regex pattern could cause ReDoS attacks',
122
- },
123
- {
124
- name: 'Missing HTTPS',
125
- pattern: /http:\/\/(?!localhost)/gi,
126
- severity: 'high',
127
- message: 'Non-HTTPS URL detected',
128
- },
129
- ];
130
- /**
131
- * Analyze source code for security issues
132
- */
133
- function analyzeSourceCode(sourceCode, fileName) {
134
- const issues = [];
135
- const lines = sourceCode.split('\n');
136
- SECURITY_PATTERNS.forEach(({ name, pattern, severity, message }) => {
137
- let match;
138
- const patternWithIndices = new RegExp(pattern.source, pattern.flags + 'g');
139
- while ((match = patternWithIndices.exec(sourceCode)) !== null) {
140
- // Calculate line number from string position
141
- const lineNumber = sourceCode.substring(0, match.index).split('\n').length;
142
- const lineContent = lines[lineNumber - 1] || '';
143
- const columnNumber = match.index - sourceCode.lastIndexOf('\n', match.index);
144
- // Avoid duplicate issues on same line
145
- const isDuplicate = issues.some(issue => issue.file === fileName &&
146
- issue.line === lineNumber &&
147
- issue.type === name);
148
- if (!isDuplicate) {
149
- issues.push({
150
- type: name,
151
- severity,
152
- file: fileName,
153
- line: lineNumber,
154
- column: columnNumber,
155
- message,
156
- code: lineContent.trim(),
157
- });
158
- }
159
- }
160
- });
161
- return issues;
162
- }
163
- /**
164
- * Generate full security analysis of all source code
165
- */
166
- async function generateSecurityAnalysis(sourceMapUrl) {
167
- const sourceMap = await fetchSourceMap(sourceMapUrl);
168
- if (!sourceMap) {
169
- return {
170
- timestamp: new Date().toISOString(),
171
- totalIssuesFound: 0,
172
- criticalCount: 0,
173
- highCount: 0,
174
- mediumCount: 0,
175
- lowCount: 0,
176
- issues: [],
177
- summary: 'Could not load source map for analysis. Make sure your build includes source maps (sourcemap: true in rollup config).',
178
- };
179
- }
180
- const sourceCode = await extractAllSourceCode(sourceMap);
181
- const allIssues = [];
182
- // Analyze each source file
183
- sourceCode.forEach((code, fileName) => {
184
- const issues = analyzeSourceCode(code, fileName);
185
- allIssues.push(...issues);
186
- });
187
- // Sort by severity
188
- const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
189
- allIssues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
190
- // Count issues by severity
191
- const criticalCount = allIssues.filter(i => i.severity === 'critical').length;
192
- const highCount = allIssues.filter(i => i.severity === 'high').length;
193
- const mediumCount = allIssues.filter(i => i.severity === 'medium').length;
194
- const lowCount = allIssues.filter(i => i.severity === 'low').length;
195
- // Generate summary
196
- const summary = generateSummary(criticalCount, highCount, mediumCount, lowCount);
197
- return {
198
- timestamp: new Date().toISOString(),
199
- totalIssuesFound: allIssues.length,
200
- criticalCount,
201
- highCount,
202
- mediumCount,
203
- lowCount,
204
- issues: allIssues,
205
- summary,
206
- };
207
- }
208
- /**
209
- * Generate human-readable summary
210
- */
211
- function generateSummary(critical, high, medium, low) {
212
- const total = critical + high + medium + low;
213
- if (total === 0) {
214
- return '✅ No security issues detected! Your code looks good.';
215
- }
216
- let summary = `Found ${total} potential security issue${total !== 1 ? 's' : ''}: `;
217
- const parts = [];
218
- if (critical > 0)
219
- parts.push(`${critical} critical`);
220
- if (high > 0)
221
- parts.push(`${high} high`);
222
- if (medium > 0)
223
- parts.push(`${medium} medium`);
224
- if (low > 0)
225
- parts.push(`${low} low`);
226
- summary += parts.join(', ') + '.';
227
- if (critical > 0) {
228
- summary += ' ⚠️ Critical issues must be fixed before deployment.';
229
- }
230
- else if (high > 0) {
231
- summary += ' Please review and fix high-severity issues.';
232
- }
233
- return summary;
234
- }
235
-
236
5
  const DEV_UI_STORAGE_KEY = 'spektre_dev_enabled';
237
- let DEV_UI_VERSION = '1.0.3';
6
+ let DEV_UI_VERSION = '1.0.4';
238
7
  // Inject styles directly into the document
239
8
  function injectDevUIStyles() {
240
9
  if (document.getElementById('spektre-dev-ui-styles')) {
@@ -479,31 +248,6 @@ function injectDevUIStyles() {
479
248
  box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.2);
480
249
  }
481
250
 
482
- /* Analysis Button */
483
- .spektre-analysis-button {
484
- width: 100%;
485
- padding: 12px 16px;
486
- background-color: rgba(249, 115, 22, 0.2);
487
- border: 1px solid rgba(249, 115, 22, 0.5);
488
- color: #fed7aa;
489
- border-radius: 8px;
490
- cursor: pointer;
491
- font-size: 14px;
492
- font-weight: 500;
493
- transition: all 0.2s ease;
494
- margin-bottom: 16px;
495
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
496
- }
497
-
498
- .spektre-analysis-button:hover {
499
- background-color: rgba(249, 115, 22, 0.3);
500
- box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.2);
501
- }
502
-
503
- .spektre-analysis-button:active {
504
- transform: scale(0.98);
505
- }
506
-
507
251
  /* Reload Message */
508
252
  .spektre-dev-modal-reload-msg {
509
253
  margin: 0;
@@ -693,294 +437,6 @@ function runSecurityScan() {
693
437
  issues,
694
438
  };
695
439
  }
696
- async function openAnalysisModal() {
697
- // Check if modal already exists
698
- if (document.getElementById('spektre-analysis-modal')) {
699
- return;
700
- }
701
- // Show loading state
702
- const loadingOverlay = document.createElement('div');
703
- loadingOverlay.style.cssText = `
704
- position: fixed;
705
- top: 0;
706
- left: 0;
707
- right: 0;
708
- bottom: 0;
709
- background-color: rgba(0, 0, 0, 0.7);
710
- display: flex;
711
- align-items: center;
712
- justify-content: center;
713
- z-index: 9999;
714
- `;
715
- const loadingModal = document.createElement('div');
716
- loadingModal.style.cssText = `
717
- background-color: #000000;
718
- border: 1px solid #1e293b;
719
- border-radius: 12px;
720
- padding: 40px;
721
- text-align: center;
722
- color: #ffffff;
723
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
724
- `;
725
- loadingModal.innerHTML = `
726
- <div style="width: 40px; height: 40px; border: 4px solid rgba(71, 85, 105, 0.3); border-top-color: #f97316; border-radius: 50%; margin: 0 auto 16px; animation: spin 1s linear infinite;"></div>
727
- <p style="margin: 0; font-size: 16px; color: #cbd5e1;">Analyzing your code...</p>
728
- `;
729
- loadingOverlay.appendChild(loadingModal);
730
- document.body.appendChild(loadingOverlay);
731
- try {
732
- // Generate analysis
733
- const sourceMapUrl = `${window.location.origin}/index.js.map`;
734
- const analysis = await generateSecurityAnalysis(sourceMapUrl);
735
- // Remove loading modal
736
- loadingOverlay.remove();
737
- // Show analysis modal
738
- createAnalysisModal(analysis);
739
- }
740
- catch (error) {
741
- console.error('[Spektre] Analysis failed:', error);
742
- loadingOverlay.remove();
743
- }
744
- }
745
- function createAnalysisModal(analysis) {
746
- const overlay = document.createElement('div');
747
- overlay.id = 'spektre-analysis-modal';
748
- overlay.style.cssText = `
749
- position: fixed;
750
- top: 0;
751
- left: 0;
752
- right: 0;
753
- bottom: 0;
754
- background-color: rgba(0, 0, 0, 0.7);
755
- display: flex;
756
- align-items: center;
757
- justify-content: center;
758
- z-index: 9999;
759
- animation: spektre-fade-in 0.2s ease;
760
- `;
761
- const modal = document.createElement('div');
762
- modal.style.cssText = `
763
- background-color: #000000;
764
- border: 1px solid #1e293b;
765
- border-radius: 12px;
766
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
767
- width: 90%;
768
- max-width: 700px;
769
- max-height: 80vh;
770
- overflow: hidden;
771
- display: flex;
772
- flex-direction: column;
773
- animation: spektre-slide-up 0.3s ease;
774
- `;
775
- // Header
776
- const header = document.createElement('div');
777
- header.style.cssText = `
778
- display: flex;
779
- justify-content: space-between;
780
- align-items: center;
781
- padding: 20px;
782
- background: linear-gradient(135deg, rgba(249, 115, 22, 0.1) 0%, rgba(0, 0, 0, 0.5) 100%);
783
- border-bottom: 1px solid #1e293b;
784
- `;
785
- const title = document.createElement('h2');
786
- title.style.cssText = `
787
- margin: 0;
788
- font-size: 18px;
789
- font-weight: 600;
790
- color: #ffffff;
791
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
792
- `;
793
- title.textContent = '🔒 Security Analysis Report';
794
- const closeBtn = document.createElement('button');
795
- closeBtn.style.cssText = `
796
- background: none;
797
- border: none;
798
- color: #94a3b8;
799
- font-size: 24px;
800
- cursor: pointer;
801
- padding: 0;
802
- width: 28px;
803
- height: 28px;
804
- display: flex;
805
- align-items: center;
806
- justify-content: center;
807
- transition: color 0.2s ease;
808
- border-radius: 4px;
809
- `;
810
- closeBtn.innerHTML = '✕';
811
- closeBtn.addEventListener('click', () => overlay.remove());
812
- closeBtn.addEventListener('mouseover', () => (closeBtn.style.color = '#f97316'));
813
- closeBtn.addEventListener('mouseout', () => (closeBtn.style.color = '#94a3b8'));
814
- header.appendChild(title);
815
- header.appendChild(closeBtn);
816
- // Body
817
- const body = document.createElement('div');
818
- body.style.cssText = `
819
- padding: 24px;
820
- overflow-y: auto;
821
- flex: 1;
822
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
823
- font-size: 14px;
824
- color: #cbd5e1;
825
- line-height: 1.6;
826
- `;
827
- // Summary
828
- const summaryDiv = document.createElement('div');
829
- summaryDiv.style.cssText = `
830
- background-color: ${analysis.totalIssuesFound === 0 ? 'rgba(34, 197, 94, 0.1)' : 'rgba(239, 68, 68, 0.1)'};
831
- border: 1px solid ${analysis.totalIssuesFound === 0 ? 'rgba(34, 197, 94, 0.3)' : 'rgba(239, 68, 68, 0.3)'};
832
- border-radius: 8px;
833
- padding: 16px;
834
- margin-bottom: 24px;
835
- `;
836
- const summaryText = document.createElement('p');
837
- summaryText.style.cssText = `
838
- margin: 0;
839
- font-size: 15px;
840
- font-weight: 500;
841
- color: ${analysis.totalIssuesFound === 0 ? '#86efac' : '#fed7aa'};
842
- `;
843
- summaryText.textContent = analysis.summary;
844
- summaryDiv.appendChild(summaryText);
845
- body.appendChild(summaryDiv);
846
- // Stats
847
- const statsDiv = document.createElement('div');
848
- statsDiv.style.cssText = `
849
- display: grid;
850
- grid-template-columns: repeat(4, 1fr);
851
- gap: 12px;
852
- margin-bottom: 24px;
853
- `;
854
- const stats = [
855
- { label: 'Critical', count: analysis.criticalCount, color: '#ef4444' },
856
- { label: 'High', count: analysis.highCount, color: '#f97316' },
857
- { label: 'Medium', count: analysis.mediumCount, color: '#eab308' },
858
- { label: 'Low', count: analysis.lowCount, color: '#22c55e' },
859
- ];
860
- stats.forEach(stat => {
861
- const statCard = document.createElement('div');
862
- statCard.style.cssText = `
863
- background-color: rgba(15, 23, 42, 0.8);
864
- border: 1px solid rgba(71, 85, 105, 0.3);
865
- border-radius: 6px;
866
- padding: 12px;
867
- text-align: center;
868
- `;
869
- const count = document.createElement('div');
870
- count.style.cssText = `
871
- font-size: 24px;
872
- font-weight: 600;
873
- color: ${stat.color};
874
- margin-bottom: 4px;
875
- `;
876
- count.textContent = String(stat.count);
877
- const label = document.createElement('div');
878
- label.style.cssText = `
879
- font-size: 12px;
880
- color: #94a3b8;
881
- text-transform: uppercase;
882
- `;
883
- label.textContent = stat.label;
884
- statCard.appendChild(count);
885
- statCard.appendChild(label);
886
- statsDiv.appendChild(statCard);
887
- });
888
- body.appendChild(statsDiv);
889
- // Issues list
890
- if (analysis.issues.length > 0) {
891
- const issuesTitle = document.createElement('h3');
892
- issuesTitle.style.cssText = `
893
- margin: 0 0 16px 0;
894
- font-size: 16px;
895
- font-weight: 600;
896
- color: #ffffff;
897
- `;
898
- issuesTitle.textContent = `${analysis.issues.length} Issue${analysis.issues.length !== 1 ? 's' : ''} Found`;
899
- body.appendChild(issuesTitle);
900
- analysis.issues.forEach((issue) => {
901
- const issueCard = document.createElement('div');
902
- issueCard.style.cssText = `
903
- background-color: rgba(15, 23, 42, 0.6);
904
- border-left: 4px solid ${issue.severity === 'critical'
905
- ? '#ef4444'
906
- : issue.severity === 'high'
907
- ? '#f97316'
908
- : issue.severity === 'medium'
909
- ? '#eab308'
910
- : '#22c55e'};
911
- border-radius: 6px;
912
- padding: 12px;
913
- margin-bottom: 12px;
914
- `;
915
- issueCard.innerHTML = `
916
- <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 8px;">
917
- <div style="font-weight: 600; color: #ffffff;">${escapeHtml(issue.type)}</div>
918
- <div style="background-color: rgba(71, 85, 105, 0.3); padding: 2px 8px; border-radius: 4px; font-size: 12px; color: #94a3b8; text-transform: uppercase;">${issue.severity}</div>
919
- </div>
920
- <div style="font-size: 13px; color: #cbd5e1; margin-bottom: 8px;">${escapeHtml(issue.message)}</div>
921
- <div style="font-size: 12px; color: #64748b; margin-bottom: 8px;">
922
- 📍 ${escapeHtml(issue.file)}:${issue.line}:${issue.column}
923
- </div>
924
- <div style="background-color: rgba(0, 0, 0, 0.3); padding: 8px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #cbd5e1; overflow-x: auto;">
925
- ${escapeHtml(issue.code)}
926
- </div>
927
- `;
928
- body.appendChild(issueCard);
929
- });
930
- }
931
- else {
932
- const noIssues = document.createElement('div');
933
- noIssues.style.cssText = `
934
- text-align: center;
935
- padding: 40px 20px;
936
- color: #86efac;
937
- `;
938
- noIssues.innerHTML = `
939
- <div style="font-size: 48px; margin-bottom: 16px;">✅</div>
940
- <p style="margin: 0; font-size: 16px; font-weight: 500;">No security issues detected!</p>
941
- <p style="margin: 8px 0 0 0; font-size: 14px; color: #64748b;">Your code looks great.</p>
942
- `;
943
- body.appendChild(noIssues);
944
- }
945
- // Footer
946
- const footer = document.createElement('div');
947
- footer.style.cssText = `
948
- padding: 16px 24px;
949
- background-color: #000000;
950
- border-top: 1px solid #1e293b;
951
- font-size: 12px;
952
- color: #64748b;
953
- text-align: center;
954
- `;
955
- footer.textContent = `Generated: ${new Date(analysis.timestamp).toLocaleString()}`;
956
- modal.appendChild(header);
957
- modal.appendChild(body);
958
- modal.appendChild(footer);
959
- overlay.appendChild(modal);
960
- // Close on overlay click
961
- overlay.addEventListener('click', e => {
962
- if (e.target === overlay) {
963
- overlay.remove();
964
- }
965
- });
966
- // Close on escape
967
- document.addEventListener('keydown', e => {
968
- if (e.key === 'Escape' && document.getElementById('spektre-analysis-modal')) {
969
- overlay.remove();
970
- }
971
- }, { once: true });
972
- document.body.appendChild(overlay);
973
- }
974
- function escapeHtml(text) {
975
- const map = {
976
- '&': '&amp;',
977
- '<': '&lt;',
978
- '>': '&gt;',
979
- '"': '&quot;',
980
- "'": '&#039;',
981
- };
982
- return text.replace(/[&<>"']/g, m => map[m]);
983
- }
984
440
  function initializeDevUI(version) {
985
441
  // Set version if provided
986
442
  {
@@ -1088,15 +544,6 @@ function openDevModal(scanResult) {
1088
544
  body.appendChild(infoText);
1089
545
  body.appendChild(statusBadge);
1090
546
  body.appendChild(toggleContainer);
1091
- // Add analysis button
1092
- const analysisButton = document.createElement('button');
1093
- analysisButton.className = 'spektre-analysis-button';
1094
- analysisButton.textContent = '📊 Run Full Code Analysis';
1095
- analysisButton.addEventListener('click', () => {
1096
- modal.remove();
1097
- openAnalysisModal();
1098
- });
1099
- body.appendChild(analysisButton);
1100
547
  body.appendChild(reloadMessage);
1101
548
  // Add security scan results if available
1102
549
  if (scanResult) {
@@ -1596,7 +1043,7 @@ const useProtectedFetch = () => {
1596
1043
  };
1597
1044
 
1598
1045
  // Manually set version - update this with each release
1599
- const packageVersion = '1.0.0';
1046
+ const packageVersion = '1.0.4';
1600
1047
  // Check if we're in an iframe (development environment) and initialize dev UI
1601
1048
  if (typeof window !== 'undefined' && window.self !== window.top) {
1602
1049
  initializeDevUI(packageVersion);
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spektre/veil",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "type": "module",
5
5
  "description": "Security and monitoring wrapper for React apps built with AI tools",
6
6
  "main": "dist/index.js",