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