@chdlabs/my-shield 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.d.ts CHANGED
@@ -47,12 +47,87 @@ export interface AdminOptions {
47
47
  enabled?: boolean;
48
48
  }
49
49
 
50
+ export interface BehavioralVerdict {
51
+ isHuman: boolean;
52
+ probability: number;
53
+ score: number;
54
+ latencyMs: number;
55
+ ip?: string;
56
+ mode?: 'block' | 'audit' | 'header-only';
57
+ reason?: string;
58
+ }
59
+
60
+ export interface BehavioralGuardOptions {
61
+ threshold?: number;
62
+ mode?: 'block' | 'audit' | 'header-only';
63
+ headerName?: string;
64
+ replayWindowMs?: number;
65
+ allowFallbackOnError?: boolean;
66
+ autoBlockIP?: boolean;
67
+ onBotDetected?: (req: any, res: any, meta: any) => void;
68
+ onVerdict?: (verdict: BehavioralVerdict, req: any, res: any) => void;
69
+ }
70
+
71
+ export interface BiometricsOptions {
72
+ modelWeights?: {
73
+ thresholds: number[];
74
+ weights: number[];
75
+ bias: number;
76
+ windowLength?: number;
77
+ numFeatures?: number;
78
+ } | null;
79
+ }
80
+
81
+ export interface BehavioralVerificationResult {
82
+ score: number;
83
+ probability: number;
84
+ isHuman: boolean;
85
+ latencyMs: number;
86
+ }
87
+
88
+ export interface FileStorageOptions {
89
+ type?: 'file';
90
+ directory?: string;
91
+ filename?: string;
92
+ debounceMs?: number;
93
+ }
94
+
95
+ export interface CallbackStorageOptions {
96
+ onLoad?: () => Promise<Array<{ ip: string; reason: string; metadata?: any; expiresAt?: number | null }>>;
97
+ onBlock?: (ip: string, reason: string, metadata?: any, expiresAt?: number | null) => Promise<void>;
98
+ onUnblock?: (ip: string) => Promise<void>;
99
+ }
100
+
101
+ export type StorageOption = 'memory' | 'file' | FileStorageOptions | CallbackStorageOptions | BaseStorageAdapter;
102
+
103
+ export interface IPResolverOptions {
104
+ trustProxy?: boolean | string | string[];
105
+ protectGateways?: boolean;
106
+ protectedSubnets?: string[];
107
+ }
108
+
109
+ export interface OtpGateOptions {
110
+ adminEmail?: string;
111
+ jwtSecret?: string;
112
+ secret?: string;
113
+ cookieName?: string;
114
+ codeTtlMs?: number;
115
+ sessionTtlMs?: number;
116
+ maxAttempts?: number;
117
+ sendOtp?: (email: string, code: string) => Promise<void>;
118
+ }
119
+
50
120
  export interface ShieldOptions {
51
121
  whitelist?: string[];
122
+ trustProxy?: boolean | string | string[];
123
+ protectGateways?: boolean;
124
+ protectedSubnets?: string[];
125
+ storage?: StorageOption;
52
126
  bloom?: BloomOptions;
53
127
  lru?: LRUOptions;
54
128
  honeypot?: HoneypotOptions;
55
129
  intelligence?: IntelligenceOptions;
130
+ biometrics?: BiometricsOptions;
56
131
  admin?: AdminOptions;
57
132
  blockStatusCode?: number;
58
133
  logger?: {
@@ -73,15 +148,100 @@ export interface AdminRouterOptions {
73
148
  authMiddleware?: RequestHandler | RequestHandler[];
74
149
  }
75
150
 
151
+ export class BaseStorageAdapter {
152
+ shield: any;
153
+ init(shield: any): Promise<void>;
154
+ loadBlockedIPs(): Promise<Array<{ ip: string; reason: string; metadata?: any; expiresAt?: number | null }>>;
155
+ saveBlockedIP(ip: string, reason: string, metadata?: any, expiresAt?: number | null): Promise<void>;
156
+ removeBlockedIP(ip: string): Promise<void>;
157
+ close(): Promise<void>;
158
+ }
159
+
160
+ export class MemoryAdapter extends BaseStorageAdapter {}
161
+ export class FileStorageAdapter extends BaseStorageAdapter {
162
+ constructor(options?: FileStorageOptions & { logger?: any });
163
+ flushSync(): void;
164
+ }
165
+ export class CallbackStorageAdapter extends BaseStorageAdapter {
166
+ constructor(options?: CallbackStorageOptions & { logger?: any });
167
+ }
168
+
169
+ export function createStorageAdapter(storageOption?: StorageOption, logger?: any): BaseStorageAdapter;
170
+
171
+ export class IPResolver {
172
+ constructor(options?: IPResolverOptions);
173
+ normalize(rawIp: string): string;
174
+ isValid(ip: string): boolean;
175
+ isPrivateOrLoopback(ip: string): boolean;
176
+ isProtectedGateway(ip: string): boolean;
177
+ resolve(req: any): string;
178
+ }
179
+
180
+ export class OTPGate {
181
+ constructor(options?: OtpGateOptions & { logger?: any });
182
+ generateCode(): string;
183
+ createSessionToken(email: string): string;
184
+ verifySessionToken(token: string): { valid: boolean; email?: string };
185
+ middleware(): RequestHandler;
186
+ }
187
+
188
+ export function createOtpGate(options?: OtpGateOptions & { logger?: any }): RequestHandler;
189
+
190
+ export interface MouseTelemetryCollectorOptions {
191
+ windowLength?: number;
192
+ sampleIntervalMs?: number;
193
+ maxBufferSize?: number;
194
+ maxIdleMs?: number;
195
+ minPoints?: number;
196
+ flushOnConsumption?: boolean;
197
+ }
198
+
199
+ export interface ShieldClientOptions extends MouseTelemetryCollectorOptions {
200
+ autoInjectTripwire?: boolean;
201
+ }
202
+
203
+ export interface InstrumentFetchOptions {
204
+ headerName?: string;
205
+ endpoints?: string[];
206
+ }
207
+
208
+ export class MouseTelemetryCollector {
209
+ windowLength: number;
210
+ sampleIntervalMs: number;
211
+ maxBufferSize: number;
212
+ constructor(options?: MouseTelemetryCollectorOptions);
213
+ start(): void;
214
+ stop(): void;
215
+ getPayload(): { velocity: number[]; isTrusted: boolean; timestamp: number } | null;
216
+ }
217
+
218
+ export class ShieldClient {
219
+ collector: MouseTelemetryCollector;
220
+ constructor(options?: ShieldClientOptions);
221
+ start(): void;
222
+ stop(): void;
223
+ getPayload(): { velocity: number[]; isTrusted: boolean; timestamp: number } | null;
224
+ instrumentFetch(options?: InstrumentFetchOptions): void;
225
+ instrumentAxios(axiosInstance: any, options?: InstrumentFetchOptions): void;
226
+ }
227
+
76
228
  export class Shield extends EventEmitter {
229
+ ipResolver: IPResolver;
230
+ storageAdapter: BaseStorageAdapter;
231
+
77
232
  constructor(options?: ShieldOptions);
78
233
 
234
+ init(): Promise<this>;
79
235
  middleware(): RequestHandler;
80
236
  honeypot(): RequestHandler;
81
237
  notFoundCollector(): RequestHandler;
82
238
  adminRouter(options?: AdminRouterOptions): Router;
83
239
  costThrottle(options?: CostThrottleOptions): RequestHandler;
84
240
  keyLeakDetector(): RequestHandler;
241
+ behavioralGuard(options?: BehavioralGuardOptions): RequestHandler;
242
+ strictBotGuard(options?: BehavioralGuardOptions): RequestHandler;
243
+ otpGate(options?: OtpGateOptions): RequestHandler;
244
+ verifyBiometrics(velocity: number[] | Float64Array): BehavioralVerificationResult;
85
245
 
86
246
  blockIP(ip: string, reason?: string, metadata?: Record<string, any>, customTtlMs?: number | null): void;
87
247
  unblockIP(ip: string): void;
@@ -99,5 +259,26 @@ export class Shield extends EventEmitter {
99
259
  shutdown(): Promise<void>;
100
260
  }
101
261
 
262
+ export class MiniRocket {
263
+ windowLength: number;
264
+ targetFeatures: number;
265
+ thresholds: Float32Array | null;
266
+ constructor(windowLength?: number, numFeatures?: number);
267
+ loadThresholds(thresholdsArray: number[] | Float32Array): void;
268
+ fit(trainingWindows: Array<number[] | Float64Array>): void;
269
+ transform(series: number[] | Float64Array): Float32Array;
270
+ }
271
+
272
+ export class DualRidgeClassifier {
273
+ alpha: number;
274
+ weights: Float32Array | null;
275
+ bias: number;
276
+ constructor(alpha?: number);
277
+ loadWeights(weightsArray: number[] | Float32Array, bias?: number): void;
278
+ fit(X: Array<Float32Array | number[]>, y: number[]): void;
279
+ predict(features: Float32Array | number[]): { score: number; probability: number; isHuman: boolean };
280
+ }
281
+
282
+ export function injectTripwire(): void;
102
283
  export function createShield(options?: ShieldOptions): Shield;
103
284
  export default createShield;
package/src/index.js CHANGED
@@ -17,7 +17,13 @@ import { NotFoundCollector } from './middleware/notFoundCollector.js';
17
17
  import { ShieldGuard } from './middleware/shieldGuard.js';
18
18
  import { CostThrottle } from './middleware/costThrottle.js';
19
19
  import { KeyLeakDetector } from './middleware/keyLeakDetector.js';
20
+ import { BehavioralShieldService } from './middleware/behavioralGuard.js';
21
+ import { MiniRocket } from './biometrics/minirocket.js';
22
+ import { DualRidgeClassifier } from './biometrics/ridge.js';
20
23
  import { createAdminRouter } from './admin/adminRouter.js';
24
+ import { IPResolver } from './core/ipResolver.js';
25
+ import { createStorageAdapter } from './storage/index.js';
26
+ import { createOtpGate, OTPGate } from './admin/otpGate.js';
21
27
 
22
28
  export class Shield extends EventEmitter {
23
29
  /**
@@ -122,9 +128,58 @@ export class Shield extends EventEmitter {
122
128
  logger: this.logger
123
129
  });
124
130
 
125
- // 8. Admin Dashboard Configuration (Default: false)
131
+ // 8. Behavioral Biometrics Subsystem (MiniRocket + Dual Ridge)
132
+ const biometricsOpts = options.biometrics || {};
133
+ this.behavioralShieldService = new BehavioralShieldService({
134
+ modelWeights: biometricsOpts.modelWeights || null,
135
+ shield: this,
136
+ logger: this.logger
137
+ });
138
+
139
+ // 9. Admin Dashboard Configuration (Default: false)
126
140
  const adminOpts = options.admin || {};
127
141
  this.adminEnabled = Boolean(adminOpts.enabled);
142
+
143
+ // 10. Enterprise Client IP Resolution & Gateway Protection
144
+ this.ipResolver = new IPResolver({
145
+ trustProxy: options.trustProxy,
146
+ protectGateways: options.protectGateways !== false,
147
+ protectedSubnets: options.protectedSubnets || []
148
+ });
149
+
150
+ // 11. Pluggable Storage Adapter (Memory, File, or Database Callbacks)
151
+ this.storageAdapter = createStorageAdapter(options.storage, this.logger);
152
+ }
153
+
154
+ /**
155
+ * Asynchronous initialization: hydrates unexpired threats from persistent storage
156
+ * into Bloom filter and LRU cache.
157
+ * @returns {Promise<this>}
158
+ */
159
+ async init() {
160
+ await this.storageAdapter.init(this);
161
+ try {
162
+ const blockedList = await this.storageAdapter.loadBlockedIPs();
163
+ if (Array.isArray(blockedList)) {
164
+ const now = Date.now();
165
+ for (const item of blockedList) {
166
+ if (item && item.ip) {
167
+ const cleanIp = this.ipResolver ? this.ipResolver.normalize(item.ip) : item.ip.trim();
168
+ if (!this.whitelist.has(cleanIp) && !this.ipResolver.isProtectedGateway(cleanIp)) {
169
+ this.bloom.add(cleanIp);
170
+ const ttl = item.expiresAt ? Math.max(1, item.expiresAt - now) : null;
171
+ this.lru.set(cleanIp, { reason: item.reason || 'restored_threat', metadata: item.metadata || {} }, ttl);
172
+ }
173
+ }
174
+ }
175
+ if (blockedList.length > 0) {
176
+ this.logger.info?.(`[SHIELD] Hydrated ${blockedList.length} persistent threats into Bloom & LRU.`);
177
+ }
178
+ }
179
+ } catch (err) {
180
+ this.logger.error?.('[SHIELD:STORAGE] Error during startup threat hydration:', err.message);
181
+ }
182
+ return this;
128
183
  }
129
184
 
130
185
  /**
@@ -171,6 +226,49 @@ export class Shield extends EventEmitter {
171
226
  return this.keyLeakDetectorInstance.middleware();
172
227
  }
173
228
 
229
+ /**
230
+ * Route-level Behavioral Biometrics Express Middleware
231
+ * Validates mouse kinematics against MiniRocket model
232
+ * @param {Object} [opts]
233
+ */
234
+ behavioralGuard(opts) {
235
+ return this.behavioralShieldService.middleware(opts);
236
+ }
237
+
238
+ /**
239
+ * Route-level strict behavioral bot defense decorator
240
+ * Designed specifically for mission-critical routes like /login, /checkout, /register
241
+ * Automatically rejects bots with 403 and blacklists their IP in Shield L1.
242
+ * @param {Object} [opts]
243
+ */
244
+ strictBotGuard(opts = {}) {
245
+ return this.behavioralGuard({
246
+ mode: 'block',
247
+ autoBlockIP: true,
248
+ threshold: 0.50,
249
+ ...opts
250
+ });
251
+ }
252
+
253
+ /**
254
+ * Shield Standalone OTP Authentication Gate Middleware
255
+ * @param {Object} [options]
256
+ */
257
+ otpGate(options = {}) {
258
+ return createOtpGate({
259
+ logger: this.logger,
260
+ ...options
261
+ });
262
+ }
263
+
264
+ /**
265
+ * Verify kinematics velocity series against MiniRocket model
266
+ * @param {number[]|Float64Array} velocityArray
267
+ */
268
+ verifyBiometrics(velocityArray) {
269
+ return this.behavioralShieldService.verify(velocityArray);
270
+ }
271
+
174
272
  /**
175
273
  * Block an IP address immediately
176
274
  * @param {string} ip
@@ -180,16 +278,28 @@ export class Shield extends EventEmitter {
180
278
  */
181
279
  blockIP(ip, reason = 'manual_block', metadata = {}, customTtlMs = null) {
182
280
  if (!ip || typeof ip !== 'string') return;
183
- const cleanIp = ip.trim();
281
+ const cleanIp = this.ipResolver ? this.ipResolver.normalize(ip) : ip.trim();
184
282
 
185
- // Whitelisted IPs can never be blocked
283
+ // Whitelisted IPs and protected gateways can never be blocked
186
284
  if (this.whitelist.has(cleanIp)) return;
285
+ if (this.ipResolver && this.ipResolver.isProtectedGateway(cleanIp)) {
286
+ this.logger.warn?.(`[SHIELD] Block attempted on protected gateway/loopback (${cleanIp}). Block prevented.`);
287
+ return;
288
+ }
187
289
 
188
290
  // 1. Add to Bloom filter (for sub-0.1ms pass-through check)
189
291
  this.bloom.add(cleanIp);
190
292
 
191
293
  // 2. Add to LRU confirmed threat cache (with TTL)
192
294
  this.lru.set(cleanIp, { reason, metadata }, customTtlMs);
295
+
296
+ // 3. Persist to storage adapter
297
+ const expiresAt = customTtlMs ? Date.now() + customTtlMs : (this.lru.ttlMs ? Date.now() + this.lru.ttlMs : null);
298
+ try {
299
+ this.storageAdapter?.saveBlockedIP(cleanIp, reason, metadata, expiresAt);
300
+ } catch (err) {
301
+ this.logger.error?.('[SHIELD:STORAGE] Error saving blocked IP to storage:', err.message);
302
+ }
193
303
  }
194
304
 
195
305
  /**
@@ -199,8 +309,14 @@ export class Shield extends EventEmitter {
199
309
  */
200
310
  unblockIP(ip) {
201
311
  if (!ip || typeof ip !== 'string') return;
202
- const cleanIp = ip.trim();
312
+ const cleanIp = this.ipResolver ? this.ipResolver.normalize(ip) : ip.trim();
203
313
  this.lru.delete(cleanIp);
314
+
315
+ try {
316
+ this.storageAdapter?.removeBlockedIP(cleanIp);
317
+ } catch (err) {
318
+ this.logger.error?.('[SHIELD:STORAGE] Error removing blocked IP from storage:', err.message);
319
+ }
204
320
  }
205
321
 
206
322
  /**
@@ -300,8 +416,9 @@ export class Shield extends EventEmitter {
300
416
  * Graceful shutdown: flush in-memory state to 404_intelligence.json
301
417
  */
302
418
  async shutdown() {
303
- this.logger.info?.('[SHIELD] Graceful shutdown initiated. Flushing intelligence to disk...');
419
+ this.logger.info?.('[SHIELD] Graceful shutdown initiated. Flushing state to disk...');
304
420
  await this.filePersistence.stop();
421
+ await this.storageAdapter?.close?.();
305
422
  this.notFoundHandler.destroy();
306
423
  this.costThrottleInstance.destroy();
307
424
  this.keyLeakDetectorInstance.destroy();
@@ -318,4 +435,20 @@ export function createShield(options = {}) {
318
435
  return new Shield(options);
319
436
  }
320
437
 
438
+ export { MiniRocket } from './biometrics/minirocket.js';
439
+ export { DualRidgeClassifier } from './biometrics/ridge.js';
440
+ export { BehavioralShieldService } from './middleware/behavioralGuard.js';
441
+ export { IPResolver } from './core/ipResolver.js';
442
+ export { OTPGate, createOtpGate } from './admin/otpGate.js';
443
+ export {
444
+ BaseStorageAdapter,
445
+ MemoryAdapter,
446
+ FileStorageAdapter,
447
+ CallbackStorageAdapter,
448
+ createStorageAdapter
449
+ } from './storage/index.js';
450
+ export { ShieldClient } from './client/shieldClient.js';
451
+ export { MouseTelemetryCollector } from './client/collector.js';
452
+ export { injectTripwire } from './client/index.js';
453
+
321
454
  export default createShield;