@angular-helpers/security 21.0.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/README.md ADDED
@@ -0,0 +1,380 @@
1
+ [English](README.en.md) | [Español](README.md)
2
+
3
+ # Angular Security Helpers
4
+
5
+ Security package for Angular applications that prevents common attacks like ReDoS (Regular Expression Denial of Service) using Web Workers for safe execution.
6
+
7
+ ## 🛡️ Features
8
+
9
+ ### **ReDoS Prevention**
10
+ - **Web Worker Execution**: Regular expressions are executed in a separate thread.
11
+ - **Configurable Timeout**: Prevents infinite executions.
12
+ - **Complexity Analysis**: Detects dangerous patterns before execution.
13
+ - **Safe Mode**: Only allows patterns verified as safe.
14
+
15
+ ### **Builder Pattern**
16
+ - **Fluent API**: Intuitively build regular expressions.
17
+ - **Method Chaining**: `.pattern().group().quantifier()`
18
+ - **Real-time Validation**: Security analysis during construction.
19
+
20
+ ## 📦 Installation
21
+
22
+ ```bash
23
+ npm install @angular-helpers/security
24
+ ```
25
+
26
+ ## 🚀 Basic Usage
27
+
28
+ ### **Configuration**
29
+
30
+ ```typescript
31
+ import { provideSecurity } from '@angular-helpers/security';
32
+
33
+ bootstrapApplication(AppComponent, {
34
+ providers: [
35
+ provideSecurity({
36
+ enableRegexSecurity: true,
37
+ defaultTimeout: 5000,
38
+ safeMode: false
39
+ })
40
+ ]
41
+ });
42
+ ```
43
+
44
+ ### **Service Injection**
45
+
46
+ ```typescript
47
+ import { RegexSecurityService } from '@angular-helpers/security';
48
+
49
+ @Component({...})
50
+ export class MyComponent {
51
+ constructor(private regexSecurity: RegexSecurityService) {}
52
+ }
53
+ ```
54
+
55
+ ## 📖 Usage Examples
56
+
57
+ ### **1. Basic Regular Expression Test**
58
+
59
+ ```typescript
60
+ async testEmail(email: string): Promise<boolean> {
61
+ const result = await this.regexSecurity.testRegex(
62
+ '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
63
+ email,
64
+ { timeout: 3000 }
65
+ );
66
+
67
+ return result.match;
68
+ }
69
+ ```
70
+
71
+ ### **2. Builder Pattern**
72
+
73
+ ```typescript
74
+ import { RegexSecurityBuilder } from '@angular-helpers/security';
75
+
76
+ // Fluent regular expression construction
77
+ const emailRegex = RegexSecurityBuilder
78
+ .builder()
79
+ .startOfLine()
80
+ .characterSet('a-zA-Z0-9._%+-')
81
+ .quantifier('+')
82
+ .append('@')
83
+ .characterSet('a-zA-Z0-9.-')
84
+ .quantifier('+')
85
+ .append('\\.')
86
+ .characterSet('a-zA-Z')
87
+ .quantifier('{2,}')
88
+ .endOfLine()
89
+ .timeout(5000)
90
+ .safeMode()
91
+ .build();
92
+
93
+ // Direct execution
94
+ const result = await RegexSecurityBuilder
95
+ .builder()
96
+ .pattern('^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$')
97
+ .timeout(3000)
98
+ .execute(email, this.regexSecurity);
99
+ ```
100
+
101
+ ### **3. Security Analysis**
102
+
103
+ ```typescript
104
+ async analyzePattern(pattern: string): Promise<void> {
105
+ const analysis = await this.regexSecurity.analyzePatternSecurity(pattern);
106
+
107
+ if (!analysis.safe) {
108
+ console.warn('⚠️ Pattern not safe:', analysis.warnings);
109
+ console.info('💡 Recommendations:', analysis.recommendations);
110
+
111
+ if (analysis.risk === 'critical') {
112
+ throw new Error('Pattern rejected due to critical security risk');
113
+ }
114
+ }
115
+
116
+ console.log(`✅ Pattern complexity: ${analysis.complexity}`);
117
+ console.log(`🎯 Risk level: ${analysis.risk}`);
118
+ }
119
+ ```
120
+
121
+ ### **4. Form Validation**
122
+
123
+ ```typescript
124
+ @Component({...})
125
+ export class FormValidationComponent {
126
+ constructor(private regexSecurity: RegexSecurityService) {}
127
+
128
+ async validateUsername(username: string): Promise<boolean> {
129
+ const result = await this.regexSecurity.testRegex(
130
+ '^[a-zA-Z0-9_]{3,20}$',
131
+ username,
132
+ { timeout: 1000, safeMode: true }
133
+ );
134
+
135
+ if (result.timeout) {
136
+ throw new Error('Username validation timeout - possible ReDoS attack');
137
+ }
138
+
139
+ if (result.error) {
140
+ console.error('Validation error:', result.error);
141
+ return false;
142
+ }
143
+
144
+ return result.match;
145
+ }
146
+
147
+ async validateComplexInput(input: string): Promise<boolean> {
148
+ // Builder pattern for complex validation
149
+ const result = await RegexSecurityBuilder
150
+ .builder()
151
+ .startOfLine()
152
+ .nonCapturingGroup('[a-zA-Z]') // First letter
153
+ .characterSet('a-zA-Z0-9_') // Allowed characters
154
+ .quantifier('{2,19}') // Between 3 and 20 characters total
155
+ .endOfLine()
156
+ .timeout(2000)
157
+ .execute(input, this.regexSecurity);
158
+
159
+ return result.match;
160
+ }
161
+ }
162
+ ```
163
+
164
+ ## 🔧 Advanced Configuration
165
+
166
+ ### **Security Options**
167
+
168
+ ```typescript
169
+ interface RegexSecurityConfig {
170
+ timeout?: number; // Timeout in ms (default: 5000)
171
+ maxComplexity?: number; // Max complexity (default: 10)
172
+ allowBacktracking?: boolean; // Allow backtracking (default: false)
173
+ safeMode?: boolean; // Safe mode (default: false)
174
+ }
175
+ ```
176
+
177
+ ### **Builder Options**
178
+
179
+ ```typescript
180
+ interface RegexBuilderOptions {
181
+ global?: boolean; // 'g' flag
182
+ ignoreCase?: boolean; // 'i' flag
183
+ multiline?: boolean; // 'm' flag
184
+ dotAll?: boolean; // 's' flag
185
+ unicode?: boolean; // 'u' flag
186
+ sticky?: boolean; // 'y' flag
187
+ }
188
+ ```
189
+
190
+ ## 🛡️ Security Features
191
+
192
+ ### **Dangerous Pattern Detection**
193
+
194
+ The service automatically detects:
195
+
196
+ - **Nested quantifiers**: `**`, `++` (catastrophic backtracking)
197
+ - **Lookaheads/lookbehinds**: `(?=)`, `(?!)`, `(?<=)`, `(?<!)`
198
+ - **Atomic groups**: `(?>)`
199
+ - **Recursive patterns**: Deeply nested groups
200
+ - **Complex quantifiers**: `{n,m}` with high values
201
+ - **Greedy wildcards**: `.*`, `.+` with variable characters
202
+
203
+ ### **Risk Levels**
204
+
205
+ - **🟢 Low**: Simple and safe patterns
206
+ - **🟡 Medium**: Patterns with lookahead/lookbehind
207
+ - **🟠 High**: Patterns with complex quantifiers
208
+ - **🔴 Critical**: Patterns with catastrophic backtracking
209
+
210
+ ### **Attack Prevention**
211
+
212
+ - **Timeout**: Stops execution after the time limit
213
+ - **Web Worker**: Isolates execution from the main thread
214
+ - **Pre-analysis**: Rejects dangerous patterns before execution
215
+ - **Match limit**: Prevents infinite loops
216
+
217
+ ## 📊 Metrics and Monitoring
218
+
219
+ ### **Execution Results**
220
+
221
+ ```typescript
222
+ interface RegexTestResult {
223
+ match: boolean; // If there was a match
224
+ matches?: RegExpMatchArray[]; // All matches found
225
+ groups?: { [key: string]: string }; // Captured groups
226
+ executionTime: number; // Execution time in ms
227
+ timeout: boolean; // If there was a timeout
228
+ error?: string; // Error if one occurred
229
+ }
230
+ ```
231
+
232
+ ### **Security Analysis**
233
+
234
+ ```typescript
235
+ interface RegexSecurityResult {
236
+ safe: boolean; // If the pattern is safe
237
+ complexity: number; // Complexity level (0-∞)
238
+ risk: 'low' | 'medium' | 'high' | 'critical';
239
+ warnings: string[]; // Security warnings
240
+ recommendations: string[]; // Improvement recommendations
241
+ }
242
+ ```
243
+
244
+ ## 🔄 Form Integration
245
+
246
+ ### **Angular Validators**
247
+
248
+ ```typescript
249
+ import { AbstractControl, ValidationErrors } from '@angular/forms';
250
+
251
+ export class SecurityValidators {
252
+ constructor(private regexSecurity: RegexSecurityService) {}
253
+
254
+ async securePattern(pattern: string, config?: RegexSecurityConfig) {
255
+ return async (control: AbstractControl): Promise<ValidationErrors | null> => {
256
+ const value = control.value;
257
+
258
+ if (!value) return null;
259
+
260
+ try {
261
+ const result = await this.regexSecurity.testRegex(pattern, value, config);
262
+
263
+ if (!result.match) {
264
+ return { securePattern: { value, reason: 'Pattern does not match' } };
265
+ }
266
+
267
+ if (result.timeout) {
268
+ return { securePattern: { value, reason: 'Pattern execution timeout' } };
269
+ }
270
+
271
+ return null;
272
+ } catch (error) {
273
+ return { securePattern: { value, reason: error.message } };
274
+ }
275
+ };
276
+ }
277
+ }
278
+ ```
279
+
280
+ ### **Usage in Template Forms**
281
+
282
+ ```typescript
283
+ @Component({...})
284
+ export class SecureFormComponent {
285
+ constructor(
286
+ private regexSecurity: RegexSecurityService,
287
+ private securityValidators: SecurityValidators
288
+ ) {}
289
+
290
+ emailValidator = this.securityValidators.securePattern(
291
+ '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
292
+ { timeout: 3000, safeMode: true }
293
+ );
294
+ }
295
+ ```
296
+
297
+ ## 🚨 Best Practices
298
+
299
+ ### **1. Use Safe Mode in Production**
300
+
301
+ ```typescript
302
+ // In production, always use safeMode
303
+ const config = { safeMode: true, timeout: 3000 };
304
+ ```
305
+
306
+ ### **2. Appropriate Timeout**
307
+
308
+ ```typescript
309
+ // For form validations: 1-3 seconds
310
+ // For text processing: 5-10 seconds
311
+ // Never more than 30 seconds
312
+ ```
313
+
314
+ ### **3. Pre-analysis**
315
+
316
+ ```typescript
317
+ // Always analyze user-provided patterns
318
+ const analysis = await this.regexSecurity.analyzePatternSecurity(pattern);
319
+ if (!analysis.safe) {
320
+ // Consider using a safer alternative pattern
321
+ }
322
+ ```
323
+
324
+ ### **4. Error Handling**
325
+
326
+ ```typescript
327
+ try {
328
+ const result = await this.regexSecurity.testRegex(pattern, text, config);
329
+ // Process result
330
+ } catch (error) {
331
+ // Handle error safely
332
+ console.error('Regex security error:', error);
333
+ // Fallback to a simpler validation
334
+ }
335
+ ```
336
+
337
+ ## 🔍 Debugging
338
+
339
+ ### **Security Logging**
340
+
341
+ The service includes automatic logging:
342
+
343
+ ```typescript
344
+ // Enables detailed logging
345
+ console.log('Regex security initialized');
346
+ console.log('Pattern analysis completed:', analysis);
347
+ console.log('Pattern execution completed:', result);
348
+ ```
349
+
350
+ ### **Performance Monitoring**
351
+
352
+ ```typescript
353
+ // Monitor execution times
354
+ if (result.executionTime > 1000) {
355
+ console.warn('Slow regex pattern:', pattern, result.executionTime + 'ms');
356
+ }
357
+ ```
358
+
359
+ ## 📝 License
360
+
361
+ MIT License - see the LICENSE file for details.
362
+
363
+ ## 🤝 Contributions
364
+
365
+ Contributions are welcome. Please:
366
+
367
+ 1. Create an issue to discuss changes
368
+ 2. Fork the repository
369
+ 3. Create a feature branch
370
+ 4. Send a pull request
371
+
372
+ ## 📚 Additional Resources
373
+
374
+ - [OWASP Regular Expression Denial of Service](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
375
+ - [MDN Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
376
+ - [Angular Security Best Practices](https://angular.io/guide/security)
377
+
378
+ ---
379
+
380
+ **⚠️ Warning**: This package helps prevent ReDoS but does not replace other security practices. Always validate and sanitize user inputs.
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "lib": {
4
+ "entryFile": "src/index.ts"
5
+ },
6
+ "dest": "../../dist/security"
7
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@angular-helpers/security",
3
+ "version": "21.0.0",
4
+ "description": "Angular security helpers for preventing ReDoS and other security vulnerabilities",
5
+ "keywords": [
6
+ "angular",
7
+ "security",
8
+ "regex",
9
+ "redos",
10
+ "prevention",
11
+ "web-worker",
12
+ "builder-pattern"
13
+ ],
14
+ "author": "Angular Helpers Team",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/angular-helpers/security"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/angular-helpers/security/issues"
22
+ },
23
+ "homepage": "https://github.com/angular-helpers/security#readme",
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "build": "ng-packagr -p ng-package.json"
29
+ },
30
+ "peerDependencies": {
31
+ "@angular/common": "^21.0.0",
32
+ "@angular/core": "^21.0.0",
33
+ "@angular-helpers/browser-web-apis": "21.0.0",
34
+ "rxjs": "^7.0.0 || ^8.0.0",
35
+ "tslib": "^2.0.0"
36
+ },
37
+ "dependencies": {
38
+ "tslib": "^2.0.0"
39
+ },
40
+ "ngPackage": {
41
+ "lib": {
42
+ "entryFile": "src/index.ts"
43
+ },
44
+ "dest": "../../dist/security",
45
+ "allowedNonPeerDependencies": [
46
+ "tslib"
47
+ ]
48
+ }
49
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './services/regex-security.service';
@@ -0,0 +1,32 @@
1
+ export interface RegexSecurityConfig {
2
+ timeout?: number; // Timeout en milisegundos (default: 5000)
3
+ maxComplexity?: number; // Máxima complejidad permitida
4
+ allowBacktracking?: boolean; // Permitir backtracking catastrófico
5
+ safeMode?: boolean; // Modo seguro solo con patrones seguros
6
+ }
7
+
8
+ export interface RegexTestResult {
9
+ match: boolean;
10
+ matches?: RegExpMatchArray[];
11
+ groups?: { [key: string]: string };
12
+ executionTime: number;
13
+ timeout: boolean;
14
+ error?: string;
15
+ }
16
+
17
+ export interface RegexSecurityResult {
18
+ safe: boolean;
19
+ complexity: number;
20
+ risk: 'low' | 'medium' | 'high' | 'critical';
21
+ warnings: string[];
22
+ recommendations: string[];
23
+ }
24
+
25
+ export interface RegexBuilderOptions {
26
+ global?: boolean;
27
+ ignoreCase?: boolean;
28
+ multiline?: boolean;
29
+ dotAll?: boolean;
30
+ unicode?: boolean;
31
+ sticky?: boolean;
32
+ }
@@ -0,0 +1,29 @@
1
+ import { makeEnvironmentProviders, EnvironmentProviders } from '@angular/core';
2
+ import { RegexSecurityService } from './services/regex-security.service';
3
+
4
+ export interface SecurityConfig {
5
+ enableRegexSecurity?: boolean;
6
+ defaultTimeout?: number;
7
+ safeMode?: boolean;
8
+ }
9
+
10
+ export const defaultSecurityConfig: SecurityConfig = {
11
+ enableRegexSecurity: true,
12
+ defaultTimeout: 5000,
13
+ safeMode: false
14
+ };
15
+
16
+ export function provideSecurity(config: SecurityConfig = {}): EnvironmentProviders {
17
+ const mergedConfig = { ...defaultSecurityConfig, ...config };
18
+ const providers = [];
19
+
20
+ if (mergedConfig.enableRegexSecurity) {
21
+ providers.push(RegexSecurityService);
22
+ }
23
+
24
+ return makeEnvironmentProviders(providers);
25
+ }
26
+
27
+ export function provideRegexSecurity(): EnvironmentProviders {
28
+ return makeEnvironmentProviders([RegexSecurityService]);
29
+ }
@@ -0,0 +1,487 @@
1
+ import { Injectable, inject, DestroyRef } from '@angular/core';
2
+ import { Observable, Subject } from 'rxjs';
3
+ import { map, catchError, timeout } from 'rxjs/operators';
4
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
+
6
+ export interface RegexSecurityConfig {
7
+ timeout?: number; // Timeout in milliseconds (default: 5000)
8
+ maxComplexity?: number; // Maximum complexity allowed
9
+ allowBacktracking?: boolean; // Allow catastrophic backtracking
10
+ safeMode?: boolean; // Safe mode with secure patterns only
11
+ }
12
+
13
+ export interface RegexTestResult {
14
+ match: boolean;
15
+ matches?: RegExpMatchArray[];
16
+ groups?: { [key: string]: string };
17
+ executionTime: number;
18
+ timeout: boolean;
19
+ error?: string;
20
+ }
21
+
22
+ export interface RegexSecurityResult {
23
+ safe: boolean;
24
+ complexity: number;
25
+ risk: 'low' | 'medium' | 'high' | 'critical';
26
+ warnings: string[];
27
+ recommendations: string[];
28
+ }
29
+
30
+ export interface RegexBuilderOptions {
31
+ global?: boolean;
32
+ ignoreCase?: boolean;
33
+ multiline?: boolean;
34
+ dotAll?: boolean;
35
+ unicode?: boolean;
36
+ sticky?: boolean;
37
+ }
38
+
39
+ /**
40
+ * Security service for regular expressions that prevents ReDoS
41
+ * using Web Workers for safe execution with timeout
42
+ */
43
+ @Injectable()
44
+ export class RegexSecurityService {
45
+ private destroyRef = inject(DestroyRef);
46
+ private workers = new Map<string, Worker>();
47
+
48
+ /**
49
+ * Builder pattern to construct safe regular expressions
50
+ */
51
+ static builder(): RegexSecurityBuilder {
52
+ return new RegexSecurityBuilder();
53
+ }
54
+
55
+ /**
56
+ * Executes a regular expression safely with a timeout
57
+ */
58
+ async testRegex(
59
+ pattern: string,
60
+ text: string,
61
+ config: RegexSecurityConfig = {}
62
+ ): Promise<RegexTestResult> {
63
+ const startTime = performance.now();
64
+ const finalConfig = this.mergeConfig(config);
65
+
66
+ try {
67
+ // First, analyze pattern security
68
+ const securityCheck = await this.analyzePatternSecurity(pattern);
69
+
70
+ if (!securityCheck.safe && !finalConfig.safeMode) {
71
+ return {
72
+ match: false,
73
+ executionTime: performance.now() - startTime,
74
+ timeout: false,
75
+ error: `Pattern rejected: ${securityCheck.warnings.join(', ')}`
76
+ };
77
+ }
78
+
79
+ // Execute in Web Worker with timeout
80
+ const result = await this.executeInWorker(pattern, text, finalConfig);
81
+
82
+ return {
83
+ ...result,
84
+ executionTime: performance.now() - startTime
85
+ };
86
+ } catch (error) {
87
+ return {
88
+ match: false,
89
+ executionTime: performance.now() - startTime,
90
+ timeout: false,
91
+ error: error instanceof Error ? error.message : 'Unknown error'
92
+ };
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Analyzes the security of a regular expression pattern
98
+ */
99
+ async analyzePatternSecurity(pattern: string): Promise<RegexSecurityResult> {
100
+ const warnings: string[] = [];
101
+ const recommendations: string[] = [];
102
+ let complexity = 0;
103
+ let risk: 'low' | 'medium' | 'high' | 'critical' = 'low';
104
+
105
+ // Analysis of dangerous patterns
106
+ const dangerousPatterns = [
107
+ { pattern: /\*\*/, risk: 'high' as const, message: 'Nested quantifiers (catastrophic backtracking)' },
108
+ { pattern: /\+\+/, risk: 'high' as const, message: 'Nested plus quantifiers' },
109
+ { pattern: /\(\?\=/, risk: 'medium' as const, message: 'Lookahead assertions' },
110
+ { pattern: /\(\?\!/, risk: 'medium' as const, message: 'Negative lookahead' },
111
+ { pattern: /\(\?\:/, risk: 'low' as const, message: 'Non-capturing groups' },
112
+ { pattern: /\(\?\</, risk: 'high' as const, message: 'Lookbehind assertions' },
113
+ { pattern: /\(\?\(\?\)/, risk: 'critical' as const, message: 'Recursive patterns' },
114
+ { pattern: /(\{(\d+,)?\d+\})/, risk: 'medium' as const, message: 'Quantified repetition' },
115
+ { pattern: /(\.\*)|(\.+)|(\.\?)/, risk: 'medium' as const, message: 'Greedy quantifiers with dot' },
116
+ { pattern: /(\[.*\*.*\])|(\[.*\+.*\])/, risk: 'medium' as const, message: 'Character classes with quantifiers' }
117
+ ];
118
+
119
+ // Calculate complexity
120
+ complexity = this.calculateComplexity(pattern);
121
+
122
+ // Evaluate dangerous patterns
123
+ for (const dangerous of dangerousPatterns) {
124
+ if (dangerous.pattern.test(pattern)) {
125
+ warnings.push(dangerous.message);
126
+ if (this.getRiskLevel(dangerous.risk) > this.getRiskLevel(risk)) {
127
+ risk = dangerous.risk;
128
+ }
129
+ }
130
+ }
131
+
132
+ // Recommendations based on the analysis
133
+ if (complexity > 10) {
134
+ recommendations.push('Consider simplifying the pattern');
135
+ risk = this.getRiskLevel(risk) > this.getRiskLevel('high') ? risk : 'high';
136
+ }
137
+
138
+ if (pattern.includes('**') || pattern.includes('++')) {
139
+ recommendations.push('Avoid nested quantifiers to prevent catastrophic backtracking');
140
+ }
141
+
142
+ if (pattern.length > 100) {
143
+ recommendations.push('Long patterns are harder to maintain and may impact performance');
144
+ }
145
+
146
+ const safe = risk !== 'critical' && warnings.length === 0;
147
+
148
+ return {
149
+ safe,
150
+ complexity,
151
+ risk,
152
+ warnings,
153
+ recommendations
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Executes the regular expression in a Web Worker
159
+ */
160
+ private async executeInWorker(
161
+ pattern: string,
162
+ text: string,
163
+ config: RegexSecurityConfig
164
+ ): Promise<RegexTestResult> {
165
+ return new Promise((resolve) => {
166
+ const workerName = `regex-worker-${Date.now()}`;
167
+
168
+ try {
169
+ // Create temporary worker
170
+ const workerCode = this.generateWorkerCode();
171
+ const blob = new Blob([workerCode], { type: 'application/javascript' });
172
+ const worker = new Worker(URL.createObjectURL(blob));
173
+
174
+ this.workers.set(workerName, worker);
175
+
176
+ const taskId = `regex_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
177
+
178
+ const task = {
179
+ id: taskId,
180
+ type: 'regex-test',
181
+ data: {
182
+ pattern,
183
+ text,
184
+ timeout: config.timeout || 5000
185
+ }
186
+ };
187
+
188
+ // Timeout for execution
189
+ const timeoutId = setTimeout(() => {
190
+ worker.terminate();
191
+ this.workers.delete(workerName);
192
+ resolve({
193
+ match: false,
194
+ executionTime: 0,
195
+ timeout: true,
196
+ error: 'Execution timeout'
197
+ });
198
+ }, config.timeout || 5000);
199
+
200
+ worker.onmessage = (event) => {
201
+ clearTimeout(timeoutId);
202
+ worker.terminate();
203
+ this.workers.delete(workerName);
204
+
205
+ if (event.data.id === taskId) {
206
+ resolve(event.data.data as RegexTestResult);
207
+ }
208
+ };
209
+
210
+ worker.onerror = (error) => {
211
+ clearTimeout(timeoutId);
212
+ worker.terminate();
213
+ this.workers.delete(workerName);
214
+ resolve({
215
+ match: false,
216
+ executionTime: 0,
217
+ timeout: false,
218
+ error: `Worker error: ${error.message || 'Unknown error'}`
219
+ });
220
+ };
221
+
222
+ worker.postMessage(task);
223
+ } catch (error) {
224
+ resolve({
225
+ match: false,
226
+ executionTime: 0,
227
+ timeout: false,
228
+ error: `Failed to create worker: ${error instanceof Error ? error.message : 'Unknown error'}`
229
+ });
230
+ }
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Generates the Web Worker code
236
+ */
237
+ private generateWorkerCode(): string {
238
+ return `
239
+ self.addEventListener('message', function(event) {
240
+ const task = event.data;
241
+
242
+ if (task.type === 'regex-test') {
243
+ const { pattern, text, timeout } = task.data;
244
+ const startTime = performance.now();
245
+
246
+ try {
247
+ const regex = new RegExp(pattern, 'g');
248
+ const matches = [];
249
+ let match;
250
+
251
+ while ((match = regex.exec(text)) !== null) {
252
+ matches.push([...match]);
253
+
254
+ // Prevention of infinite loops
255
+ if (matches.length > 1000) {
256
+ throw new Error('Too many matches - possible infinite loop');
257
+ }
258
+ }
259
+
260
+ const groups = {};
261
+ if (matches.length > 0) {
262
+ const firstMatch = matches[0];
263
+ for (let i = 1; i < firstMatch.length; i++) {
264
+ groups[\`group\${i}\`] = firstMatch[i];
265
+ }
266
+ }
267
+
268
+ self.postMessage({
269
+ id: task.id,
270
+ type: 'regex-result',
271
+ data: {
272
+ match: matches.length > 0,
273
+ matches,
274
+ groups,
275
+ executionTime: performance.now() - startTime,
276
+ timeout: false
277
+ }
278
+ });
279
+ } catch (error) {
280
+ self.postMessage({
281
+ id: task.id,
282
+ type: 'regex-result',
283
+ data: {
284
+ match: false,
285
+ executionTime: performance.now() - startTime,
286
+ timeout: false,
287
+ error: error.message || 'Execution error'
288
+ }
289
+ });
290
+ }
291
+ }
292
+ });
293
+ `;
294
+ }
295
+
296
+ /**
297
+ * Calculates the complexity of a pattern
298
+ */
299
+ private calculateComplexity(pattern: string): number {
300
+ let complexity = 0;
301
+
302
+ // Nested quantifiers increase complexity
303
+ complexity += (pattern.match(/\*\*/g) || []).length * 5;
304
+ complexity += (pattern.match(/\+\+/g) || []).length * 5;
305
+ complexity += (pattern.match(/\?\?/g) || []).length * 3;
306
+
307
+ // Lookaheads/lookbehinds
308
+ complexity += (pattern.match(/\(\?\=/g) || []).length * 2;
309
+ complexity += (pattern.match(/\(\?\!/g) || []).length * 2;
310
+ complexity += (pattern.match(/\(\?\</g) || []).length * 3;
311
+
312
+ // Nested groups
313
+ const openParens = (pattern.match(/\(/g) || []).length;
314
+ complexity += openParens * 0.5;
315
+
316
+ // Pattern length
317
+ complexity += pattern.length * 0.01;
318
+
319
+ return Math.round(complexity * 100) / 100;
320
+ }
321
+
322
+ /**
323
+ * Gets numeric risk level
324
+ */
325
+ private getRiskLevel(risk: 'low' | 'medium' | 'high' | 'critical'): number {
326
+ const levels = { 'low': 1, 'medium': 2, 'high': 3, 'critical': 4 };
327
+ return levels[risk] || 0;
328
+ }
329
+
330
+ /**
331
+ * Merges configuration with default values
332
+ */
333
+ private mergeConfig(config: RegexSecurityConfig): Required<RegexSecurityConfig> {
334
+ return {
335
+ timeout: config.timeout || 5000,
336
+ maxComplexity: config.maxComplexity || 10,
337
+ allowBacktracking: config.allowBacktracking || false,
338
+ safeMode: config.safeMode || false
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Cleans up resources when the service is destroyed
344
+ */
345
+ ngOnDestroy(): void {
346
+ this.workers.forEach(worker => {
347
+ worker.terminate();
348
+ });
349
+ this.workers.clear();
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Builder pattern to construct safe regular expressions
355
+ */
356
+ export class RegexSecurityBuilder {
357
+ private patternValue: string = '';
358
+ private optionsValue: RegexBuilderOptions = {};
359
+ private securityConfigValue: RegexSecurityConfig = {};
360
+
361
+ /**
362
+ * Defines the base pattern
363
+ */
364
+ pattern(pattern: string): RegexSecurityBuilder {
365
+ this.patternValue = pattern;
366
+ return this;
367
+ }
368
+
369
+ /**
370
+ * Appends text to the current pattern
371
+ */
372
+ append(text: string): RegexSecurityBuilder {
373
+ this.patternValue += text;
374
+ return this;
375
+ }
376
+
377
+ /**
378
+ * Adds a capturing group
379
+ */
380
+ group(content: string, name?: string): RegexSecurityBuilder {
381
+ if (name) {
382
+ this.patternValue += `(?<${name}>${content})`;
383
+ } else {
384
+ this.patternValue += `(${content})`;
385
+ }
386
+ return this;
387
+ }
388
+
389
+ /**
390
+ * Adds a non-capturing group
391
+ */
392
+ nonCapturingGroup(content: string): RegexSecurityBuilder {
393
+ this.patternValue += `(?:${content})`;
394
+ return this;
395
+ }
396
+
397
+ /**
398
+ * Adds an alternative
399
+ */
400
+ or(alternative: string): RegexSecurityBuilder {
401
+ this.patternValue += `|${alternative}`;
402
+ return this;
403
+ }
404
+
405
+ /**
406
+ * Adds a quantifier
407
+ */
408
+ quantifier(quantifier: '*' | '+' | '?' | '{n}' | '{n,}' | '{n,m}'): RegexSecurityBuilder {
409
+ this.patternValue += quantifier;
410
+ return this;
411
+ }
412
+
413
+ /**
414
+ * Adds a character set
415
+ */
416
+ characterSet(chars: string, negate = false): RegexSecurityBuilder {
417
+ this.patternValue += `[${negate ? '^' : ''}${chars}]`;
418
+ return this;
419
+ }
420
+
421
+ /**
422
+ * Adds a start of line anchor
423
+ */
424
+ startOfLine(): RegexSecurityBuilder {
425
+ this.patternValue += '^';
426
+ return this;
427
+ }
428
+
429
+ /**
430
+ * Adds an end of line anchor
431
+ */
432
+ endOfLine(): RegexSecurityBuilder {
433
+ this.patternValue += '$';
434
+ return this;
435
+ }
436
+
437
+ /**
438
+ * Configures regular expression options
439
+ */
440
+ options(options: RegexBuilderOptions): RegexSecurityBuilder {
441
+ this.optionsValue = { ...this.optionsValue, ...options };
442
+ return this;
443
+ }
444
+
445
+ /**
446
+ * Configures security options
447
+ */
448
+ security(config: RegexSecurityConfig): RegexSecurityBuilder {
449
+ this.securityConfigValue = { ...this.securityConfigValue, ...config };
450
+ return this;
451
+ }
452
+
453
+ /**
454
+ * Configures timeout
455
+ */
456
+ timeout(ms: number): RegexSecurityBuilder {
457
+ this.securityConfigValue.timeout = ms;
458
+ return this;
459
+ }
460
+
461
+ /**
462
+ * Activates safe mode
463
+ */
464
+ safeMode(): RegexSecurityBuilder {
465
+ this.securityConfigValue.safeMode = true;
466
+ return this;
467
+ }
468
+
469
+ /**
470
+ * Builds the final regular expression
471
+ */
472
+ build(): { pattern: string; options: RegexBuilderOptions; security: RegexSecurityConfig } {
473
+ return {
474
+ pattern: this.patternValue,
475
+ options: this.optionsValue,
476
+ security: this.securityConfigValue
477
+ };
478
+ }
479
+
480
+ /**
481
+ * Builds and executes the regular expression
482
+ */
483
+ async execute(text: string, service: RegexSecurityService): Promise<RegexTestResult> {
484
+ const { pattern, security } = this.build();
485
+ return service.testRegex(pattern, text, security);
486
+ }
487
+ }