@spektre/veil 0.1.0 → 0.1.1

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.
@@ -1,5 +1,2 @@
1
1
  export { useSpektre } from './useSpektre';
2
2
  export { useProtectedFetch } from './useProtectedFetch';
3
- export { useVeil } from './useVeil';
4
- export type { UseSpektreReturn } from './useSpektre';
5
- export type { ProtectedFetch } from './useProtectedFetch';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- export { default as VeilProvider } from './components/VeilProvider';
2
- export { useVeil, useSpektre, useProtectedFetch } from './hooks';
3
- export { SpektreContext } from './SpektreContext';
4
- export { SpektreProvider } from './SpektreProvider';
5
1
  export { SecurityGate } from './SecurityGate';
6
- export type { SpektreConfig, SpektreState, ProtectionState, MonitoringState, BlockedRequest, BlockedAttack, TamperingDetails, SecurityGateProps, VeilConfig, VeilContextType, } from './types';
7
- export type { UseSpektreReturn, ProtectedFetch } from './hooks';
2
+ export { SpektreProvider } from './SpektreProvider';
3
+ export { SpektreContext } from './SpektreContext';
4
+ export { useSpektre } from './hooks/useSpektre';
5
+ export { useProtectedFetch } from './hooks/useProtectedFetch';
6
+ export type { SecurityGateProps, SpektreConfig, SpektreState, ProtectionState, MonitoringState, BlockedRequest, BlockedAttack, TamperingDetails, VeilConfig, VeilState, VeilProps, VeilContextType, } from './types';
package/dist/index.esm.js CHANGED
@@ -1,81 +1,8 @@
1
- import React, { createContext, useMemo, useCallback, useContext, useState, useEffect } from 'react';
2
-
3
- const VeilContext = createContext(undefined);
4
- const VeilProvider = ({ config, children }) => {
5
- const isMonitoring = useMemo(() => config.enableMonitoring ?? false, [config.enableMonitoring]);
6
- const logEvent = useCallback((event, data) => {
7
- if (isMonitoring) {
8
- console.log('[Veil]', event, data);
9
- }
10
- }, [isMonitoring]);
11
- const value = useMemo(() => ({
12
- config,
13
- isMonitoring,
14
- logEvent,
15
- }), [config, isMonitoring, logEvent]);
16
- return React.createElement(VeilContext.Provider, { value: value }, children);
17
- };
1
+ import React, { createContext, useState, useEffect, useContext } from 'react';
18
2
 
19
3
  const SpektreContext = createContext(null);
20
4
  SpektreContext.displayName = 'SpektreContext';
21
5
 
22
- const useSpektre = () => {
23
- const context = useContext(SpektreContext);
24
- if (!context) {
25
- throw new Error('useSpektre must be used within a SecurityGate component');
26
- }
27
- const runScan = async () => {
28
- if (typeof window.securityScan === 'function') {
29
- await window.securityScan();
30
- }
31
- };
32
- const getTelemetry = () => {
33
- if (typeof window.getSecurityTelemetry === 'function') {
34
- return window.getSecurityTelemetry();
35
- }
36
- return context.monitoring;
37
- };
38
- const getProtectionStats = () => {
39
- if (typeof window.getProtectionStats === 'function') {
40
- return window.getProtectionStats();
41
- }
42
- return context.protection;
43
- };
44
- return {
45
- isVerified: context.isVerified,
46
- sessionId: context.sessionId,
47
- protectionEnabled: context.protection?.enabled || false,
48
- blockedRequests: context.protection?.blockedRequests || [],
49
- totalBlocked: context.protection?.totalBlocked || 0,
50
- secretExposures: context.monitoring?.secretExposures || 0,
51
- scriptProfiles: context.monitoring?.scriptProfiles || 0,
52
- supplyChainAlerts: context.monitoring?.supplyChainAlerts || 0,
53
- sessionAnomalies: context.monitoring?.sessionAnomalies || 0,
54
- runScan,
55
- getTelemetry,
56
- getProtectionStats,
57
- };
58
- };
59
-
60
- const useProtectedFetch = () => {
61
- const { isVerified } = useSpektre();
62
- const protectedFetch = async (url, options) => {
63
- if (!isVerified) {
64
- throw new Error('Security verification failed. Cannot make fetch requests until verification is complete.');
65
- }
66
- return fetch(url, options);
67
- };
68
- return protectedFetch;
69
- };
70
-
71
- const useVeil = () => {
72
- const context = useContext(VeilContext);
73
- if (context === undefined) {
74
- throw new Error('useVeil must be used within a VeilProvider');
75
- }
76
- return context;
77
- };
78
-
79
6
  const SpektreProvider = ({ value, children }) => {
80
7
  return React.createElement(SpektreContext.Provider, { value: value }, children);
81
8
  };
@@ -89,6 +16,9 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
89
16
  sessionId: null,
90
17
  });
91
18
  useEffect(() => {
19
+ // Set tampering detection marker
20
+ sessionStorage.setItem('spektre_js_enabled', 'true');
21
+ sessionStorage.setItem('spektre_init_time', Date.now().toString());
92
22
  const scriptUrl = `${SUPABASE_BASE_URL}/functions/v1/scanner_client`;
93
23
  const script = document.createElement('script');
94
24
  script.src = `${scriptUrl}?apiKey=${encodeURIComponent(apiKey)}`;
@@ -118,7 +48,7 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
118
48
  script.setAttribute('data-session', String(config.modules.sessionProtection));
119
49
  }
120
50
  if (config.modules.activeProtection !== undefined) {
121
- script.setAttribute('data-active', String(config.modules.activeProtection));
51
+ script.setAttribute('data-protection', String(config.modules.activeProtection));
122
52
  }
123
53
  }
124
54
  script.onload = () => {
@@ -141,6 +71,8 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
141
71
  protection,
142
72
  monitoring,
143
73
  });
74
+ // Start tampering detection after successful load
75
+ startTamperingDetection(config);
144
76
  }
145
77
  catch (err) {
146
78
  setState({
@@ -169,6 +101,7 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
169
101
  if (script.parentNode) {
170
102
  script.parentNode.removeChild(script);
171
103
  }
104
+ stopTamperingDetection();
172
105
  };
173
106
  }, [apiKey, config]);
174
107
  const handleRetry = () => {
@@ -197,70 +130,229 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
197
130
  if (fallback) {
198
131
  return React.createElement(React.Fragment, null, fallback);
199
132
  }
200
- return (React.createElement("div", { style: styles.container },
133
+ return (React.createElement("div", { style: styles.errorContainer },
201
134
  React.createElement("div", { style: styles.content },
202
135
  React.createElement("div", { style: { ...styles.icon, color: '#ef4444' } },
203
136
  React.createElement("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
204
137
  React.createElement("circle", { cx: "12", cy: "12", r: "10" }),
205
138
  React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
206
139
  React.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" }))),
207
- React.createElement("h2", { style: styles.title }, "Security Verification Failed"),
208
- React.createElement("p", { style: styles.message }, state.error || 'Unable to verify application security'),
209
- React.createElement("button", { onClick: handleRetry, style: styles.button }, "Retry"))));
140
+ React.createElement("h2", { style: styles.errorTitle }, "Security Protection Required"),
141
+ React.createElement("p", { style: styles.errorMessage }, "This application requires Spektre security protection to run safely."),
142
+ React.createElement("p", { style: styles.errorDetails }, state.error || 'Unable to verify application security'),
143
+ React.createElement("button", { onClick: handleRetry, style: styles.button }, "Retry Loading"))));
210
144
  }
211
145
  return React.createElement(SpektreProvider, { value: state }, children);
212
146
  };
147
+ // Tampering detection
148
+ let tamperingInterval = null;
149
+ let fetchIntegrityHash = null;
150
+ function startTamperingDetection(config) {
151
+ // Calculate integrity hash of fetch function
152
+ if (typeof window.fetch === 'function') {
153
+ fetchIntegrityHash = hashCode(window.fetch.toString());
154
+ }
155
+ tamperingInterval = window.setInterval(() => {
156
+ // Check 1: Verify JavaScript marker is still present
157
+ const jsEnabled = sessionStorage.getItem('spektre_js_enabled');
158
+ if (!jsEnabled) {
159
+ handleTampering('JavaScript marker removed', config);
160
+ return;
161
+ }
162
+ // Check 2: Verify Spektre script is still in DOM
163
+ const scriptExists = document.querySelector('script[src*="scanner_client"]');
164
+ if (!scriptExists) {
165
+ handleTampering('Security script removed from page', config);
166
+ return;
167
+ }
168
+ // Check 3: Verify fetch function hasn't been tampered with
169
+ if (typeof window.fetch === 'function') {
170
+ const currentHash = hashCode(window.fetch.toString());
171
+ if (fetchIntegrityHash && currentHash !== fetchIntegrityHash) {
172
+ // Fetch function was modified
173
+ const fetchCode = window.fetch.toString();
174
+ // Check if it still has Spektre protection
175
+ if (!fetchCode.includes('Spektre') && !fetchCode.includes('PROTECTION')) {
176
+ handleTampering('Security protection has been disabled', config);
177
+ return;
178
+ }
179
+ }
180
+ }
181
+ else {
182
+ handleTampering('Fetch function removed', config);
183
+ return;
184
+ }
185
+ // Check 4: Verify protection stats function exists
186
+ if (typeof window.getProtectionStats !== 'function') {
187
+ handleTampering('Security monitoring disabled', config);
188
+ return;
189
+ }
190
+ }, 2000); // Check every 2 seconds
191
+ }
192
+ function stopTamperingDetection() {
193
+ if (tamperingInterval) {
194
+ clearInterval(tamperingInterval);
195
+ tamperingInterval = null;
196
+ }
197
+ }
198
+ function handleTampering(reason, config) {
199
+ console.error('[Spektre SecurityGate] TAMPERING DETECTED:', reason);
200
+ // Stop checking to prevent infinite loops
201
+ stopTamperingDetection();
202
+ // Call callback if provided
203
+ if (config?.onTamperingDetected) {
204
+ config.onTamperingDetected(reason);
205
+ }
206
+ // Lock down the application
207
+ const root = document.getElementById('root');
208
+ if (root) {
209
+ root.innerHTML = '';
210
+ }
211
+ document.body.innerHTML = `
212
+ <div style="
213
+ position: fixed;
214
+ top: 0;
215
+ left: 0;
216
+ width: 100%;
217
+ height: 100%;
218
+ background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
219
+ color: white;
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ flex-direction: column;
224
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
225
+ z-index: 999999;
226
+ ">
227
+ <div style="max-width: 500px; text-align: center; padding: 40px;">
228
+ <div style="font-size: 72px; margin-bottom: 24px;">⛔</div>
229
+ <h1 style="font-size: 28px; font-weight: 600; margin-bottom: 16px;">
230
+ Security Violation Detected
231
+ </h1>
232
+ <p style="font-size: 16px; line-height: 1.6; opacity: 0.9; margin-bottom: 24px;">
233
+ This application has been locked due to security tampering.
234
+ </p>
235
+ <div style="
236
+ background: rgba(255, 255, 255, 0.1);
237
+ border-radius: 8px;
238
+ padding: 16px;
239
+ margin-bottom: 24px;
240
+ font-size: 14px;
241
+ font-family: monospace;
242
+ ">
243
+ ${reason}
244
+ </div>
245
+ <button onclick="location.reload()" style="
246
+ padding: 12px 32px;
247
+ font-size: 16px;
248
+ font-weight: 500;
249
+ background: white;
250
+ color: #dc2626;
251
+ border: none;
252
+ border-radius: 8px;
253
+ cursor: pointer;
254
+ transition: transform 0.2s;
255
+ " onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'">
256
+ Reload Application
257
+ </button>
258
+ </div>
259
+ </div>
260
+ `;
261
+ }
262
+ // Simple hash function for integrity checking
263
+ function hashCode(str) {
264
+ let hash = 0;
265
+ for (let i = 0; i < str.length; i++) {
266
+ const char = str.charCodeAt(i);
267
+ hash = ((hash << 5) - hash) + char;
268
+ hash = hash & hash;
269
+ }
270
+ return hash.toString(36);
271
+ }
213
272
  const styles = {
214
273
  container: {
215
274
  display: 'flex',
216
275
  alignItems: 'center',
217
276
  justifyContent: 'center',
218
277
  minHeight: '100vh',
219
- backgroundColor: '#f9fafb',
278
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
279
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
280
+ },
281
+ errorContainer: {
282
+ display: 'flex',
283
+ alignItems: 'center',
284
+ justifyContent: 'center',
285
+ minHeight: '100vh',
286
+ background: 'linear-gradient(135deg, #1f2937 0%, #111827 100%)',
220
287
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
221
288
  },
222
289
  content: {
223
290
  textAlign: 'center',
224
291
  padding: '2rem',
225
- maxWidth: '400px',
292
+ maxWidth: '500px',
293
+ background: 'rgba(255, 255, 255, 0.95)',
294
+ borderRadius: '16px',
295
+ boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3)',
226
296
  },
227
297
  icon: {
228
- color: '#3b82f6',
229
- marginBottom: '1rem',
298
+ color: '#667eea',
299
+ marginBottom: '1.5rem',
230
300
  display: 'flex',
231
301
  justifyContent: 'center',
232
302
  },
233
303
  title: {
234
- fontSize: '1.5rem',
304
+ fontSize: '1.75rem',
235
305
  fontWeight: '600',
236
306
  color: '#111827',
237
- marginBottom: '0.5rem',
307
+ marginBottom: '0.75rem',
238
308
  },
239
309
  message: {
240
310
  fontSize: '1rem',
241
311
  color: '#6b7280',
242
312
  marginBottom: '1.5rem',
243
- lineHeight: '1.5',
313
+ lineHeight: '1.6',
314
+ },
315
+ errorTitle: {
316
+ fontSize: '1.75rem',
317
+ fontWeight: '600',
318
+ color: '#111827',
319
+ marginBottom: '0.75rem',
320
+ },
321
+ errorMessage: {
322
+ fontSize: '1rem',
323
+ color: '#6b7280',
324
+ marginBottom: '1rem',
325
+ lineHeight: '1.6',
326
+ },
327
+ errorDetails: {
328
+ fontSize: '0.875rem',
329
+ color: '#ef4444',
330
+ marginBottom: '1.5rem',
331
+ padding: '0.75rem',
332
+ background: '#fef2f2',
333
+ borderRadius: '8px',
334
+ fontFamily: 'monospace',
244
335
  },
245
336
  spinner: {
246
- width: '32px',
247
- height: '32px',
248
- border: '3px solid #e5e7eb',
249
- borderTopColor: '#3b82f6',
337
+ width: '40px',
338
+ height: '40px',
339
+ border: '4px solid rgba(255, 255, 255, 0.3)',
340
+ borderTopColor: '#667eea',
250
341
  borderRadius: '50%',
251
342
  margin: '0 auto',
252
343
  animation: 'spin 1s linear infinite',
253
344
  },
254
345
  button: {
255
- backgroundColor: '#3b82f6',
346
+ backgroundColor: '#667eea',
256
347
  color: 'white',
257
348
  border: 'none',
258
- borderRadius: '0.5rem',
259
- padding: '0.75rem 1.5rem',
349
+ borderRadius: '8px',
350
+ padding: '0.875rem 2rem',
260
351
  fontSize: '1rem',
261
- fontWeight: '500',
352
+ fontWeight: '600',
262
353
  cursor: 'pointer',
263
- transition: 'background-color 0.2s',
354
+ transition: 'all 0.2s',
355
+ boxShadow: '0 4px 12px rgba(102, 126, 234, 0.4)',
264
356
  },
265
357
  };
266
358
  const styleSheet = document.createElement('style');
@@ -273,5 +365,54 @@ styleSheet.textContent = `
273
365
  `;
274
366
  document.head.appendChild(styleSheet);
275
367
 
276
- export { SecurityGate, SpektreContext, SpektreProvider, VeilProvider, useProtectedFetch, useSpektre, useVeil };
368
+ const useSpektre = () => {
369
+ const context = useContext(SpektreContext);
370
+ if (!context) {
371
+ throw new Error('useSpektre must be used within a SecurityGate component');
372
+ }
373
+ const runScan = async () => {
374
+ if (typeof window.securityScan === 'function') {
375
+ await window.securityScan();
376
+ }
377
+ };
378
+ const getTelemetry = () => {
379
+ if (typeof window.getSecurityTelemetry === 'function') {
380
+ return window.getSecurityTelemetry();
381
+ }
382
+ return context.monitoring;
383
+ };
384
+ const getProtectionStats = () => {
385
+ if (typeof window.getProtectionStats === 'function') {
386
+ return window.getProtectionStats();
387
+ }
388
+ return context.protection;
389
+ };
390
+ return {
391
+ isVerified: context.isVerified,
392
+ sessionId: context.sessionId,
393
+ protectionEnabled: context.protection?.enabled || false,
394
+ blockedRequests: context.protection?.blockedRequests || [],
395
+ totalBlocked: context.protection?.totalBlocked || 0,
396
+ secretExposures: context.monitoring?.secretExposures || 0,
397
+ scriptProfiles: context.monitoring?.scriptProfiles || 0,
398
+ supplyChainAlerts: context.monitoring?.supplyChainAlerts || 0,
399
+ sessionAnomalies: context.monitoring?.sessionAnomalies || 0,
400
+ runScan,
401
+ getTelemetry,
402
+ getProtectionStats,
403
+ };
404
+ };
405
+
406
+ const useProtectedFetch = () => {
407
+ const { isVerified } = useSpektre();
408
+ const protectedFetch = async (url, options) => {
409
+ if (!isVerified) {
410
+ throw new Error('Security verification failed. Cannot make fetch requests until verification is complete.');
411
+ }
412
+ return fetch(url, options);
413
+ };
414
+ return protectedFetch;
415
+ };
416
+
417
+ export { SecurityGate, SpektreContext, SpektreProvider, useProtectedFetch, useSpektre };
277
418
  //# sourceMappingURL=index.esm.js.map
@@ -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,82 +2,9 @@
2
2
 
3
3
  var React = require('react');
4
4
 
5
- const VeilContext = React.createContext(undefined);
6
- const VeilProvider = ({ config, children }) => {
7
- const isMonitoring = React.useMemo(() => config.enableMonitoring ?? false, [config.enableMonitoring]);
8
- const logEvent = React.useCallback((event, data) => {
9
- if (isMonitoring) {
10
- console.log('[Veil]', event, data);
11
- }
12
- }, [isMonitoring]);
13
- const value = React.useMemo(() => ({
14
- config,
15
- isMonitoring,
16
- logEvent,
17
- }), [config, isMonitoring, logEvent]);
18
- return React.createElement(VeilContext.Provider, { value: value }, children);
19
- };
20
-
21
5
  const SpektreContext = React.createContext(null);
22
6
  SpektreContext.displayName = 'SpektreContext';
23
7
 
24
- const useSpektre = () => {
25
- const context = React.useContext(SpektreContext);
26
- if (!context) {
27
- throw new Error('useSpektre must be used within a SecurityGate component');
28
- }
29
- const runScan = async () => {
30
- if (typeof window.securityScan === 'function') {
31
- await window.securityScan();
32
- }
33
- };
34
- const getTelemetry = () => {
35
- if (typeof window.getSecurityTelemetry === 'function') {
36
- return window.getSecurityTelemetry();
37
- }
38
- return context.monitoring;
39
- };
40
- const getProtectionStats = () => {
41
- if (typeof window.getProtectionStats === 'function') {
42
- return window.getProtectionStats();
43
- }
44
- return context.protection;
45
- };
46
- return {
47
- isVerified: context.isVerified,
48
- sessionId: context.sessionId,
49
- protectionEnabled: context.protection?.enabled || false,
50
- blockedRequests: context.protection?.blockedRequests || [],
51
- totalBlocked: context.protection?.totalBlocked || 0,
52
- secretExposures: context.monitoring?.secretExposures || 0,
53
- scriptProfiles: context.monitoring?.scriptProfiles || 0,
54
- supplyChainAlerts: context.monitoring?.supplyChainAlerts || 0,
55
- sessionAnomalies: context.monitoring?.sessionAnomalies || 0,
56
- runScan,
57
- getTelemetry,
58
- getProtectionStats,
59
- };
60
- };
61
-
62
- const useProtectedFetch = () => {
63
- const { isVerified } = useSpektre();
64
- const protectedFetch = async (url, options) => {
65
- if (!isVerified) {
66
- throw new Error('Security verification failed. Cannot make fetch requests until verification is complete.');
67
- }
68
- return fetch(url, options);
69
- };
70
- return protectedFetch;
71
- };
72
-
73
- const useVeil = () => {
74
- const context = React.useContext(VeilContext);
75
- if (context === undefined) {
76
- throw new Error('useVeil must be used within a VeilProvider');
77
- }
78
- return context;
79
- };
80
-
81
8
  const SpektreProvider = ({ value, children }) => {
82
9
  return React.createElement(SpektreContext.Provider, { value: value }, children);
83
10
  };
@@ -91,6 +18,9 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
91
18
  sessionId: null,
92
19
  });
93
20
  React.useEffect(() => {
21
+ // Set tampering detection marker
22
+ sessionStorage.setItem('spektre_js_enabled', 'true');
23
+ sessionStorage.setItem('spektre_init_time', Date.now().toString());
94
24
  const scriptUrl = `${SUPABASE_BASE_URL}/functions/v1/scanner_client`;
95
25
  const script = document.createElement('script');
96
26
  script.src = `${scriptUrl}?apiKey=${encodeURIComponent(apiKey)}`;
@@ -120,7 +50,7 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
120
50
  script.setAttribute('data-session', String(config.modules.sessionProtection));
121
51
  }
122
52
  if (config.modules.activeProtection !== undefined) {
123
- script.setAttribute('data-active', String(config.modules.activeProtection));
53
+ script.setAttribute('data-protection', String(config.modules.activeProtection));
124
54
  }
125
55
  }
126
56
  script.onload = () => {
@@ -143,6 +73,8 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
143
73
  protection,
144
74
  monitoring,
145
75
  });
76
+ // Start tampering detection after successful load
77
+ startTamperingDetection(config);
146
78
  }
147
79
  catch (err) {
148
80
  setState({
@@ -171,6 +103,7 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
171
103
  if (script.parentNode) {
172
104
  script.parentNode.removeChild(script);
173
105
  }
106
+ stopTamperingDetection();
174
107
  };
175
108
  }, [apiKey, config]);
176
109
  const handleRetry = () => {
@@ -199,70 +132,229 @@ const SecurityGate = ({ apiKey, children, config, fallback, loadingComponent, })
199
132
  if (fallback) {
200
133
  return React.createElement(React.Fragment, null, fallback);
201
134
  }
202
- return (React.createElement("div", { style: styles.container },
135
+ return (React.createElement("div", { style: styles.errorContainer },
203
136
  React.createElement("div", { style: styles.content },
204
137
  React.createElement("div", { style: { ...styles.icon, color: '#ef4444' } },
205
138
  React.createElement("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
206
139
  React.createElement("circle", { cx: "12", cy: "12", r: "10" }),
207
140
  React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
208
141
  React.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" }))),
209
- React.createElement("h2", { style: styles.title }, "Security Verification Failed"),
210
- React.createElement("p", { style: styles.message }, state.error || 'Unable to verify application security'),
211
- React.createElement("button", { onClick: handleRetry, style: styles.button }, "Retry"))));
142
+ React.createElement("h2", { style: styles.errorTitle }, "Security Protection Required"),
143
+ React.createElement("p", { style: styles.errorMessage }, "This application requires Spektre security protection to run safely."),
144
+ React.createElement("p", { style: styles.errorDetails }, state.error || 'Unable to verify application security'),
145
+ React.createElement("button", { onClick: handleRetry, style: styles.button }, "Retry Loading"))));
212
146
  }
213
147
  return React.createElement(SpektreProvider, { value: state }, children);
214
148
  };
149
+ // Tampering detection
150
+ let tamperingInterval = null;
151
+ let fetchIntegrityHash = null;
152
+ function startTamperingDetection(config) {
153
+ // Calculate integrity hash of fetch function
154
+ if (typeof window.fetch === 'function') {
155
+ fetchIntegrityHash = hashCode(window.fetch.toString());
156
+ }
157
+ tamperingInterval = window.setInterval(() => {
158
+ // Check 1: Verify JavaScript marker is still present
159
+ const jsEnabled = sessionStorage.getItem('spektre_js_enabled');
160
+ if (!jsEnabled) {
161
+ handleTampering('JavaScript marker removed', config);
162
+ return;
163
+ }
164
+ // Check 2: Verify Spektre script is still in DOM
165
+ const scriptExists = document.querySelector('script[src*="scanner_client"]');
166
+ if (!scriptExists) {
167
+ handleTampering('Security script removed from page', config);
168
+ return;
169
+ }
170
+ // Check 3: Verify fetch function hasn't been tampered with
171
+ if (typeof window.fetch === 'function') {
172
+ const currentHash = hashCode(window.fetch.toString());
173
+ if (fetchIntegrityHash && currentHash !== fetchIntegrityHash) {
174
+ // Fetch function was modified
175
+ const fetchCode = window.fetch.toString();
176
+ // Check if it still has Spektre protection
177
+ if (!fetchCode.includes('Spektre') && !fetchCode.includes('PROTECTION')) {
178
+ handleTampering('Security protection has been disabled', config);
179
+ return;
180
+ }
181
+ }
182
+ }
183
+ else {
184
+ handleTampering('Fetch function removed', config);
185
+ return;
186
+ }
187
+ // Check 4: Verify protection stats function exists
188
+ if (typeof window.getProtectionStats !== 'function') {
189
+ handleTampering('Security monitoring disabled', config);
190
+ return;
191
+ }
192
+ }, 2000); // Check every 2 seconds
193
+ }
194
+ function stopTamperingDetection() {
195
+ if (tamperingInterval) {
196
+ clearInterval(tamperingInterval);
197
+ tamperingInterval = null;
198
+ }
199
+ }
200
+ function handleTampering(reason, config) {
201
+ console.error('[Spektre SecurityGate] TAMPERING DETECTED:', reason);
202
+ // Stop checking to prevent infinite loops
203
+ stopTamperingDetection();
204
+ // Call callback if provided
205
+ if (config?.onTamperingDetected) {
206
+ config.onTamperingDetected(reason);
207
+ }
208
+ // Lock down the application
209
+ const root = document.getElementById('root');
210
+ if (root) {
211
+ root.innerHTML = '';
212
+ }
213
+ document.body.innerHTML = `
214
+ <div style="
215
+ position: fixed;
216
+ top: 0;
217
+ left: 0;
218
+ width: 100%;
219
+ height: 100%;
220
+ background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
221
+ color: white;
222
+ display: flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ flex-direction: column;
226
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
227
+ z-index: 999999;
228
+ ">
229
+ <div style="max-width: 500px; text-align: center; padding: 40px;">
230
+ <div style="font-size: 72px; margin-bottom: 24px;">⛔</div>
231
+ <h1 style="font-size: 28px; font-weight: 600; margin-bottom: 16px;">
232
+ Security Violation Detected
233
+ </h1>
234
+ <p style="font-size: 16px; line-height: 1.6; opacity: 0.9; margin-bottom: 24px;">
235
+ This application has been locked due to security tampering.
236
+ </p>
237
+ <div style="
238
+ background: rgba(255, 255, 255, 0.1);
239
+ border-radius: 8px;
240
+ padding: 16px;
241
+ margin-bottom: 24px;
242
+ font-size: 14px;
243
+ font-family: monospace;
244
+ ">
245
+ ${reason}
246
+ </div>
247
+ <button onclick="location.reload()" style="
248
+ padding: 12px 32px;
249
+ font-size: 16px;
250
+ font-weight: 500;
251
+ background: white;
252
+ color: #dc2626;
253
+ border: none;
254
+ border-radius: 8px;
255
+ cursor: pointer;
256
+ transition: transform 0.2s;
257
+ " onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'">
258
+ Reload Application
259
+ </button>
260
+ </div>
261
+ </div>
262
+ `;
263
+ }
264
+ // Simple hash function for integrity checking
265
+ function hashCode(str) {
266
+ let hash = 0;
267
+ for (let i = 0; i < str.length; i++) {
268
+ const char = str.charCodeAt(i);
269
+ hash = ((hash << 5) - hash) + char;
270
+ hash = hash & hash;
271
+ }
272
+ return hash.toString(36);
273
+ }
215
274
  const styles = {
216
275
  container: {
217
276
  display: 'flex',
218
277
  alignItems: 'center',
219
278
  justifyContent: 'center',
220
279
  minHeight: '100vh',
221
- backgroundColor: '#f9fafb',
280
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
281
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
282
+ },
283
+ errorContainer: {
284
+ display: 'flex',
285
+ alignItems: 'center',
286
+ justifyContent: 'center',
287
+ minHeight: '100vh',
288
+ background: 'linear-gradient(135deg, #1f2937 0%, #111827 100%)',
222
289
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
223
290
  },
224
291
  content: {
225
292
  textAlign: 'center',
226
293
  padding: '2rem',
227
- maxWidth: '400px',
294
+ maxWidth: '500px',
295
+ background: 'rgba(255, 255, 255, 0.95)',
296
+ borderRadius: '16px',
297
+ boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3)',
228
298
  },
229
299
  icon: {
230
- color: '#3b82f6',
231
- marginBottom: '1rem',
300
+ color: '#667eea',
301
+ marginBottom: '1.5rem',
232
302
  display: 'flex',
233
303
  justifyContent: 'center',
234
304
  },
235
305
  title: {
236
- fontSize: '1.5rem',
306
+ fontSize: '1.75rem',
237
307
  fontWeight: '600',
238
308
  color: '#111827',
239
- marginBottom: '0.5rem',
309
+ marginBottom: '0.75rem',
240
310
  },
241
311
  message: {
242
312
  fontSize: '1rem',
243
313
  color: '#6b7280',
244
314
  marginBottom: '1.5rem',
245
- lineHeight: '1.5',
315
+ lineHeight: '1.6',
316
+ },
317
+ errorTitle: {
318
+ fontSize: '1.75rem',
319
+ fontWeight: '600',
320
+ color: '#111827',
321
+ marginBottom: '0.75rem',
322
+ },
323
+ errorMessage: {
324
+ fontSize: '1rem',
325
+ color: '#6b7280',
326
+ marginBottom: '1rem',
327
+ lineHeight: '1.6',
328
+ },
329
+ errorDetails: {
330
+ fontSize: '0.875rem',
331
+ color: '#ef4444',
332
+ marginBottom: '1.5rem',
333
+ padding: '0.75rem',
334
+ background: '#fef2f2',
335
+ borderRadius: '8px',
336
+ fontFamily: 'monospace',
246
337
  },
247
338
  spinner: {
248
- width: '32px',
249
- height: '32px',
250
- border: '3px solid #e5e7eb',
251
- borderTopColor: '#3b82f6',
339
+ width: '40px',
340
+ height: '40px',
341
+ border: '4px solid rgba(255, 255, 255, 0.3)',
342
+ borderTopColor: '#667eea',
252
343
  borderRadius: '50%',
253
344
  margin: '0 auto',
254
345
  animation: 'spin 1s linear infinite',
255
346
  },
256
347
  button: {
257
- backgroundColor: '#3b82f6',
348
+ backgroundColor: '#667eea',
258
349
  color: 'white',
259
350
  border: 'none',
260
- borderRadius: '0.5rem',
261
- padding: '0.75rem 1.5rem',
351
+ borderRadius: '8px',
352
+ padding: '0.875rem 2rem',
262
353
  fontSize: '1rem',
263
- fontWeight: '500',
354
+ fontWeight: '600',
264
355
  cursor: 'pointer',
265
- transition: 'background-color 0.2s',
356
+ transition: 'all 0.2s',
357
+ boxShadow: '0 4px 12px rgba(102, 126, 234, 0.4)',
266
358
  },
267
359
  };
268
360
  const styleSheet = document.createElement('style');
@@ -275,11 +367,58 @@ styleSheet.textContent = `
275
367
  `;
276
368
  document.head.appendChild(styleSheet);
277
369
 
370
+ const useSpektre = () => {
371
+ const context = React.useContext(SpektreContext);
372
+ if (!context) {
373
+ throw new Error('useSpektre must be used within a SecurityGate component');
374
+ }
375
+ const runScan = async () => {
376
+ if (typeof window.securityScan === 'function') {
377
+ await window.securityScan();
378
+ }
379
+ };
380
+ const getTelemetry = () => {
381
+ if (typeof window.getSecurityTelemetry === 'function') {
382
+ return window.getSecurityTelemetry();
383
+ }
384
+ return context.monitoring;
385
+ };
386
+ const getProtectionStats = () => {
387
+ if (typeof window.getProtectionStats === 'function') {
388
+ return window.getProtectionStats();
389
+ }
390
+ return context.protection;
391
+ };
392
+ return {
393
+ isVerified: context.isVerified,
394
+ sessionId: context.sessionId,
395
+ protectionEnabled: context.protection?.enabled || false,
396
+ blockedRequests: context.protection?.blockedRequests || [],
397
+ totalBlocked: context.protection?.totalBlocked || 0,
398
+ secretExposures: context.monitoring?.secretExposures || 0,
399
+ scriptProfiles: context.monitoring?.scriptProfiles || 0,
400
+ supplyChainAlerts: context.monitoring?.supplyChainAlerts || 0,
401
+ sessionAnomalies: context.monitoring?.sessionAnomalies || 0,
402
+ runScan,
403
+ getTelemetry,
404
+ getProtectionStats,
405
+ };
406
+ };
407
+
408
+ const useProtectedFetch = () => {
409
+ const { isVerified } = useSpektre();
410
+ const protectedFetch = async (url, options) => {
411
+ if (!isVerified) {
412
+ throw new Error('Security verification failed. Cannot make fetch requests until verification is complete.');
413
+ }
414
+ return fetch(url, options);
415
+ };
416
+ return protectedFetch;
417
+ };
418
+
278
419
  exports.SecurityGate = SecurityGate;
279
420
  exports.SpektreContext = SpektreContext;
280
421
  exports.SpektreProvider = SpektreProvider;
281
- exports.VeilProvider = VeilProvider;
282
422
  exports.useProtectedFetch = useProtectedFetch;
283
423
  exports.useSpektre = useSpektre;
284
- exports.useVeil = useVeil;
285
424
  //# sourceMappingURL=index.js.map
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/dist/types.d.ts CHANGED
@@ -1,8 +1,6 @@
1
- import { ReactNode } from 'react';
2
1
  export interface SpektreConfig {
3
- apiKey: string;
4
2
  endpoint?: string;
5
- environment?: 'dev' | 'staging' | 'prod';
3
+ environment?: string;
6
4
  enabledEnvironments?: string[];
7
5
  modules?: {
8
6
  performanceMonitoring?: boolean;
@@ -12,7 +10,7 @@ export interface SpektreConfig {
12
10
  sessionProtection?: boolean;
13
11
  activeProtection?: boolean;
14
12
  };
15
- onTamperingDetected?: (details: TamperingDetails) => void;
13
+ onTamperingDetected?: (reason: string) => void;
16
14
  onSecurityFailed?: (error: string) => void;
17
15
  onAttackBlocked?: (attack: BlockedAttack) => void;
18
16
  }
@@ -24,11 +22,22 @@ export interface SpektreState {
24
22
  protection?: ProtectionState;
25
23
  monitoring?: MonitoringState;
26
24
  }
25
+ export interface VeilContextType {
26
+ isVerified: boolean;
27
+ isLoading: boolean;
28
+ error: string | null;
29
+ sessionId: string | null;
30
+ protection?: ProtectionState;
31
+ monitoring?: MonitoringState;
32
+ }
27
33
  export interface ProtectionState {
28
34
  enabled: boolean;
29
35
  blockedRequests: BlockedRequest[];
30
36
  totalBlocked: number;
31
37
  }
38
+ export type VeilConfig = SpektreConfig;
39
+ export type VeilState = SpektreState;
40
+ export type VeilProps = SecurityGateProps;
32
41
  export interface MonitoringState {
33
42
  secretExposures: number;
34
43
  scriptProfiles: number;
@@ -39,15 +48,14 @@ export interface BlockedRequest {
39
48
  url: string;
40
49
  reason: string;
41
50
  timestamp: number;
42
- severity: 'low' | 'medium' | 'high' | 'critical';
51
+ severity?: 'low' | 'medium' | 'high' | 'critical';
43
52
  }
44
53
  export interface BlockedAttack {
45
- url: string;
46
- reason: string;
47
- timestamp: number;
48
- severity: 'low' | 'medium' | 'high' | 'critical';
49
54
  type: string;
55
+ severity: string;
50
56
  details: string;
57
+ timestamp: number;
58
+ url?: string;
51
59
  }
52
60
  export interface TamperingDetails {
53
61
  type: string;
@@ -56,19 +64,8 @@ export interface TamperingDetails {
56
64
  }
57
65
  export interface SecurityGateProps {
58
66
  apiKey: string;
59
- children: ReactNode;
60
- config?: Partial<SpektreConfig>;
61
- fallback?: ReactNode;
62
- loadingComponent?: ReactNode;
63
- }
64
- export interface VeilConfig {
65
- enableMonitoring?: boolean;
66
- enableSecurityChecks?: boolean;
67
- apiKey?: string;
68
- environment?: 'development' | 'production';
69
- }
70
- export interface VeilContextType {
71
- config: VeilConfig;
72
- isMonitoring: boolean;
73
- logEvent: (event: string, data?: Record<string, unknown>) => void;
67
+ children: React.ReactNode;
68
+ config?: SpektreConfig;
69
+ fallback?: React.ReactNode;
70
+ loadingComponent?: React.ReactNode;
74
71
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@spektre/veil",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
+ "type": "module",
4
5
  "description": "Security and monitoring wrapper for React apps built with AI tools",
5
6
  "main": "dist/index.js",
6
7
  "module": "dist/index.esm.js",
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import type { VeilConfig, VeilContextType } from '../types';
3
- export declare const VeilContext: React.Context<VeilContextType | undefined>;
4
- interface VeilProviderProps {
5
- config: VeilConfig;
6
- children: React.ReactNode;
7
- }
8
- declare const VeilProvider: React.FC<VeilProviderProps>;
9
- export default VeilProvider;
@@ -1,2 +0,0 @@
1
- import type { VeilContextType } from '../types';
2
- export declare const useVeil: () => VeilContextType;