@angular-helpers/security 21.3.0 → 21.4.2
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 +95 -1
- package/fesm2022/angular-helpers-security.mjs +280 -35
- package/package.json +10 -4
- package/types/angular-helpers-security.d.ts +86 -2
package/README.md
CHANGED
|
@@ -76,6 +76,19 @@ Security package for Angular applications that prevents common attacks like ReDo
|
|
|
76
76
|
- **Verified auto-clear**: reads back the clipboard before clearing to avoid clobbering unrelated content.
|
|
77
77
|
- **Password-manager semantics**: default 15-second clear, configurable.
|
|
78
78
|
|
|
79
|
+
### **Session Inactivity Monitor**
|
|
80
|
+
|
|
81
|
+
- **NgZone-optimized**: DOM events tracked outside Angular change detection.
|
|
82
|
+
- **Security interop**: Can automatically clear SecureStorage and SensitiveClipboard upon timeout.
|
|
83
|
+
- **Warning states**: Configurable thresholds to warn users before expiration.
|
|
84
|
+
|
|
85
|
+
### **Secure Cross-Window Messaging**
|
|
86
|
+
|
|
87
|
+
- **HMAC-SHA-256 signatures**: Every message is signed; tampered payloads are discarded.
|
|
88
|
+
- **Origin whitelist**: Messages from non-allowed origins are rejected before any crypto work.
|
|
89
|
+
- **Anti-replay protection**: Envelope includes `timestamp + nonce`; messages older than 30s are discarded.
|
|
90
|
+
- **SSR-safe**: No-op on the server — no `window` access.
|
|
91
|
+
|
|
79
92
|
### **Builder Pattern**
|
|
80
93
|
|
|
81
94
|
- **Fluent API**: Intuitively build regular expressions.
|
|
@@ -85,7 +98,7 @@ Security package for Angular applications that prevents common attacks like ReDo
|
|
|
85
98
|
## 📦 Installation
|
|
86
99
|
|
|
87
100
|
```bash
|
|
88
|
-
|
|
101
|
+
pnpm add @angular-helpers/security
|
|
89
102
|
```
|
|
90
103
|
|
|
91
104
|
## 🚀 Basic Usage
|
|
@@ -568,6 +581,87 @@ export class ApiKeyPanel {
|
|
|
568
581
|
The service reads the clipboard before clearing and skips the clear if the content no longer
|
|
569
582
|
matches what was copied — so third-party copies by the user are never overwritten.
|
|
570
583
|
|
|
584
|
+
### **SessionIdleService**
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
import { SessionIdleService } from '@angular-helpers/security';
|
|
588
|
+
|
|
589
|
+
export class AppComponent {
|
|
590
|
+
private sessionIdle = inject(SessionIdleService);
|
|
591
|
+
|
|
592
|
+
ngOnInit() {
|
|
593
|
+
this.sessionIdle.start({
|
|
594
|
+
timeoutMs: 15 * 60 * 1000, // 15 minutes
|
|
595
|
+
warningThresholdMs: 60 * 1000, // 1 minute warning
|
|
596
|
+
autoClearStorage: true,
|
|
597
|
+
autoClearClipboard: true,
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// React to states
|
|
601
|
+
effect(() => {
|
|
602
|
+
if (this.sessionIdle.isWarning()) {
|
|
603
|
+
console.warn(`Session will expire in ${this.sessionIdle.timeRemaining()}ms`);
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
// React to timeout
|
|
608
|
+
this.sessionIdle.onTimeout.subscribe(() => {
|
|
609
|
+
this.authService.logout();
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
The service tracks DOM events (`mousemove`, `keydown`, etc.) outside the Angular Zone to prevent change detection spam. It can automatically clear `SecureStorageService` and `SensitiveClipboardService` when the session times out.
|
|
616
|
+
|
|
617
|
+
### **SecureMessageService**
|
|
618
|
+
|
|
619
|
+
```typescript
|
|
620
|
+
import { SecureMessageService, provideSecureMessage } from '@angular-helpers/security';
|
|
621
|
+
|
|
622
|
+
// app.config.ts
|
|
623
|
+
bootstrapApplication(AppComponent, {
|
|
624
|
+
providers: [provideSecureMessage()],
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// parent-app.component.ts
|
|
628
|
+
export class ParentComponent {
|
|
629
|
+
private channel = inject(SecureMessageService);
|
|
630
|
+
|
|
631
|
+
async ngOnInit() {
|
|
632
|
+
// 1. Generate a shared key (transport it to the iframe via a secure channel)
|
|
633
|
+
const key = await this.channel.generateChannelKey();
|
|
634
|
+
|
|
635
|
+
// 2. Configure: only accept messages from the iframe origin
|
|
636
|
+
this.channel.configure({
|
|
637
|
+
allowedOrigins: ['https://child-app.example.com'],
|
|
638
|
+
signingKey: key,
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// 3. React to incoming messages
|
|
642
|
+
this.channel.messages$<{ type: string; payload: unknown }>().subscribe(({ data, origin }) => {
|
|
643
|
+
console.log('Verified message from', origin, data);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// 4. Or use the Signal for reactive UI
|
|
647
|
+
effect(() => {
|
|
648
|
+
const msg = this.channel.lastMessage()();
|
|
649
|
+
if (msg) console.log('Last message:', msg.data);
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
async sendToChild(iframe: HTMLIFrameElement) {
|
|
654
|
+
await this.channel.send(
|
|
655
|
+
iframe.contentWindow!,
|
|
656
|
+
{ type: 'INIT', payload: { userId: 42 } },
|
|
657
|
+
'https://child-app.example.com', // targetOrigin — never '*'
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
> **Key exchange**: `SecureMessageService` does not perform automatic key negotiation — transport the `CryptoKey` to the other context via a secure out-of-band channel (e.g., derive it from a shared secret using `WebCryptoService.generateHmacKey()` seeded by a passphrase). Once both sides share the key, all message integrity is handled automatically.
|
|
664
|
+
|
|
571
665
|
## 🔧 Advanced Configuration
|
|
572
666
|
|
|
573
667
|
### **Security Options**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, DestroyRef, Injectable, PLATFORM_ID, InjectionToken, signal, computed, makeEnvironmentProviders } from '@angular/core';
|
|
3
|
-
import { isPlatformBrowser } from '@angular/common';
|
|
4
|
-
import { Observable } from 'rxjs';
|
|
2
|
+
import { inject, DestroyRef, Injectable, PLATFORM_ID, InjectionToken, signal, computed, NgZone, Injector, makeEnvironmentProviders } from '@angular/core';
|
|
3
|
+
import { isPlatformBrowser, DOCUMENT } from '@angular/common';
|
|
4
|
+
import { Observable, Subject, fromEvent, merge } from 'rxjs';
|
|
5
|
+
import { throttleTime } from 'rxjs/operators';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Security service for regular expressions that prevents ReDoS
|
|
@@ -285,10 +286,10 @@ class RegexSecurityService {
|
|
|
285
286
|
});
|
|
286
287
|
this.workers.clear();
|
|
287
288
|
}
|
|
288
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
289
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
289
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RegexSecurityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
290
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RegexSecurityService });
|
|
290
291
|
}
|
|
291
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
292
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RegexSecurityService, decorators: [{
|
|
292
293
|
type: Injectable
|
|
293
294
|
}] });
|
|
294
295
|
/**
|
|
@@ -528,10 +529,10 @@ class WebCryptoService {
|
|
|
528
529
|
hmacHashName(algorithm) {
|
|
529
530
|
return algorithm.replace('HMAC-', '');
|
|
530
531
|
}
|
|
531
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
532
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
532
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: WebCryptoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
533
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: WebCryptoService });
|
|
533
534
|
}
|
|
534
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
535
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: WebCryptoService, decorators: [{
|
|
535
536
|
type: Injectable
|
|
536
537
|
}] });
|
|
537
538
|
|
|
@@ -719,10 +720,10 @@ class SecureStorageService {
|
|
|
719
720
|
base64ToBytes(base64) {
|
|
720
721
|
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
|
721
722
|
}
|
|
722
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
723
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
723
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
724
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureStorageService });
|
|
724
725
|
}
|
|
725
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
726
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureStorageService, decorators: [{
|
|
726
727
|
type: Injectable
|
|
727
728
|
}], ctorParameters: () => [] });
|
|
728
729
|
|
|
@@ -1067,10 +1068,10 @@ class InputSanitizerService {
|
|
|
1067
1068
|
return null;
|
|
1068
1069
|
}
|
|
1069
1070
|
}
|
|
1070
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1071
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1071
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: InputSanitizerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1072
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: InputSanitizerService });
|
|
1072
1073
|
}
|
|
1073
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1074
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: InputSanitizerService, decorators: [{
|
|
1074
1075
|
type: Injectable
|
|
1075
1076
|
}], ctorParameters: () => [] });
|
|
1076
1077
|
|
|
@@ -1102,10 +1103,10 @@ class PasswordStrengthService {
|
|
|
1102
1103
|
assess(password) {
|
|
1103
1104
|
return assessPasswordStrength(password);
|
|
1104
1105
|
}
|
|
1105
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1106
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1106
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PasswordStrengthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1107
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PasswordStrengthService });
|
|
1107
1108
|
}
|
|
1108
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1109
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PasswordStrengthService, decorators: [{
|
|
1109
1110
|
type: Injectable
|
|
1110
1111
|
}] });
|
|
1111
1112
|
|
|
@@ -1217,10 +1218,10 @@ class JwtService {
|
|
|
1217
1218
|
}
|
|
1218
1219
|
return payload[name] ?? null;
|
|
1219
1220
|
}
|
|
1220
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1221
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1221
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: JwtService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1222
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: JwtService });
|
|
1222
1223
|
}
|
|
1223
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1224
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: JwtService, decorators: [{
|
|
1224
1225
|
type: Injectable
|
|
1225
1226
|
}] });
|
|
1226
1227
|
function base64UrlDecode(input) {
|
|
@@ -1349,10 +1350,24 @@ class SensitiveClipboardService {
|
|
|
1349
1350
|
return 'read-denied';
|
|
1350
1351
|
}
|
|
1351
1352
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1353
|
+
/**
|
|
1354
|
+
* Forcefully clears the clipboard unconditionally.
|
|
1355
|
+
*/
|
|
1356
|
+
async clear() {
|
|
1357
|
+
if (!this.isSupported())
|
|
1358
|
+
return;
|
|
1359
|
+
this.cancelPendingClear();
|
|
1360
|
+
try {
|
|
1361
|
+
await navigator.clipboard.writeText('');
|
|
1362
|
+
}
|
|
1363
|
+
catch {
|
|
1364
|
+
// ignore
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SensitiveClipboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1368
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SensitiveClipboardService });
|
|
1354
1369
|
}
|
|
1355
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1370
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SensitiveClipboardService, decorators: [{
|
|
1356
1371
|
type: Injectable
|
|
1357
1372
|
}], ctorParameters: () => [] });
|
|
1358
1373
|
|
|
@@ -1425,11 +1440,14 @@ class HibpService {
|
|
|
1425
1440
|
const match = findSuffixMatch(body, suffix);
|
|
1426
1441
|
return match > 0 ? { leaked: true, count: match } : { leaked: false, count: 0 };
|
|
1427
1442
|
}
|
|
1428
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1429
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1443
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HibpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1444
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HibpService, providedIn: 'root' });
|
|
1430
1445
|
}
|
|
1431
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1432
|
-
type: Injectable
|
|
1446
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HibpService, decorators: [{
|
|
1447
|
+
type: Injectable,
|
|
1448
|
+
args: [{
|
|
1449
|
+
providedIn: 'root',
|
|
1450
|
+
}]
|
|
1433
1451
|
}], ctorParameters: () => [] });
|
|
1434
1452
|
function findSuffixMatch(body, suffix) {
|
|
1435
1453
|
const lines = body.split(/\r?\n/);
|
|
@@ -1597,10 +1615,10 @@ class RateLimiterService {
|
|
|
1597
1615
|
bucket.lastRefillAt = now;
|
|
1598
1616
|
bucket.remaining.set(Math.floor(bucket.tokens));
|
|
1599
1617
|
}
|
|
1600
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1601
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1618
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RateLimiterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1619
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RateLimiterService });
|
|
1602
1620
|
}
|
|
1603
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1621
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RateLimiterService, decorators: [{
|
|
1604
1622
|
type: Injectable
|
|
1605
1623
|
}], ctorParameters: () => [] });
|
|
1606
1624
|
function validatePolicy(policy) {
|
|
@@ -1679,10 +1697,10 @@ class CsrfService {
|
|
|
1679
1697
|
get nativeStorage() {
|
|
1680
1698
|
return this.storageTarget === 'session' ? sessionStorage : localStorage;
|
|
1681
1699
|
}
|
|
1682
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1683
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1700
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: CsrfService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1701
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: CsrfService });
|
|
1684
1702
|
}
|
|
1685
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1703
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: CsrfService, decorators: [{
|
|
1686
1704
|
type: Injectable
|
|
1687
1705
|
}], ctorParameters: () => [] });
|
|
1688
1706
|
const DEFAULT_HEADER_NAME = 'X-CSRF-Token';
|
|
@@ -1716,6 +1734,221 @@ function withCsrfHeader(options = {}) {
|
|
|
1716
1734
|
};
|
|
1717
1735
|
}
|
|
1718
1736
|
|
|
1737
|
+
const DEFAULT_EVENTS = ['mousemove', 'keydown', 'mousedown', 'touchstart', 'scroll'];
|
|
1738
|
+
class SessionIdleService {
|
|
1739
|
+
ngZone = inject(NgZone);
|
|
1740
|
+
document = inject(DOCUMENT);
|
|
1741
|
+
injector = inject(Injector);
|
|
1742
|
+
destroyRef = inject(DestroyRef);
|
|
1743
|
+
_isIdle = signal(false, ...(ngDevMode ? [{ debugName: "_isIdle" }] : /* istanbul ignore next */ []));
|
|
1744
|
+
_isWarning = signal(false, ...(ngDevMode ? [{ debugName: "_isWarning" }] : /* istanbul ignore next */ []));
|
|
1745
|
+
_timeRemaining = signal(null, ...(ngDevMode ? [{ debugName: "_timeRemaining" }] : /* istanbul ignore next */ []));
|
|
1746
|
+
_timeoutSubject = new Subject();
|
|
1747
|
+
isIdle = this._isIdle.asReadonly();
|
|
1748
|
+
isWarning = this._isWarning.asReadonly();
|
|
1749
|
+
timeRemaining = this._timeRemaining.asReadonly();
|
|
1750
|
+
onTimeout = this._timeoutSubject.asObservable();
|
|
1751
|
+
config = null;
|
|
1752
|
+
lastActivityTime = 0;
|
|
1753
|
+
timerInterval = null;
|
|
1754
|
+
eventSubscription;
|
|
1755
|
+
constructor() {
|
|
1756
|
+
this.destroyRef.onDestroy(() => this.stop());
|
|
1757
|
+
}
|
|
1758
|
+
start(config) {
|
|
1759
|
+
this.stop();
|
|
1760
|
+
this.config = config;
|
|
1761
|
+
this._isIdle.set(false);
|
|
1762
|
+
this._isWarning.set(false);
|
|
1763
|
+
this._timeRemaining.set(config.timeoutMs);
|
|
1764
|
+
this.lastActivityTime = Date.now();
|
|
1765
|
+
const eventsToTrack = config.events || DEFAULT_EVENTS;
|
|
1766
|
+
this.ngZone.runOutsideAngular(() => {
|
|
1767
|
+
const observables = eventsToTrack.map((ev) => fromEvent(this.document, ev));
|
|
1768
|
+
this.eventSubscription = merge(...observables)
|
|
1769
|
+
.pipe(throttleTime(500))
|
|
1770
|
+
.subscribe(() => {
|
|
1771
|
+
this.lastActivityTime = Date.now();
|
|
1772
|
+
});
|
|
1773
|
+
this.timerInterval = setInterval(() => this.checkIdle(), 1000);
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
stop() {
|
|
1777
|
+
if (this.timerInterval) {
|
|
1778
|
+
clearInterval(this.timerInterval);
|
|
1779
|
+
this.timerInterval = null;
|
|
1780
|
+
}
|
|
1781
|
+
if (this.eventSubscription) {
|
|
1782
|
+
this.eventSubscription.unsubscribe();
|
|
1783
|
+
this.eventSubscription = undefined;
|
|
1784
|
+
}
|
|
1785
|
+
this.config = null;
|
|
1786
|
+
this._timeRemaining.set(null);
|
|
1787
|
+
this._isIdle.set(false);
|
|
1788
|
+
this._isWarning.set(false);
|
|
1789
|
+
}
|
|
1790
|
+
reset() {
|
|
1791
|
+
if (!this.config)
|
|
1792
|
+
return;
|
|
1793
|
+
this.lastActivityTime = Date.now();
|
|
1794
|
+
this._timeRemaining.set(this.config.timeoutMs);
|
|
1795
|
+
this._isIdle.set(false);
|
|
1796
|
+
this._isWarning.set(false);
|
|
1797
|
+
}
|
|
1798
|
+
checkIdle() {
|
|
1799
|
+
if (!this.config || this._isIdle())
|
|
1800
|
+
return;
|
|
1801
|
+
const elapsed = Date.now() - this.lastActivityTime;
|
|
1802
|
+
const remaining = Math.max(0, this.config.timeoutMs - elapsed);
|
|
1803
|
+
if (remaining === 0) {
|
|
1804
|
+
this.triggerTimeout();
|
|
1805
|
+
}
|
|
1806
|
+
else {
|
|
1807
|
+
const warningThreshold = this.config.warningThresholdMs || 0;
|
|
1808
|
+
const shouldBeWarning = remaining <= warningThreshold;
|
|
1809
|
+
this.ngZone.run(() => {
|
|
1810
|
+
this._timeRemaining.set(remaining);
|
|
1811
|
+
if (this._isWarning() !== shouldBeWarning) {
|
|
1812
|
+
this._isWarning.set(shouldBeWarning);
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
triggerTimeout() {
|
|
1818
|
+
const config = this.config;
|
|
1819
|
+
this.stop();
|
|
1820
|
+
// Restore config temporarily to check for autoClear flags
|
|
1821
|
+
this.config = config;
|
|
1822
|
+
this.ngZone.run(() => {
|
|
1823
|
+
this._timeRemaining.set(0);
|
|
1824
|
+
this._isIdle.set(true);
|
|
1825
|
+
this._timeoutSubject.next();
|
|
1826
|
+
if (this.config?.autoClearStorage) {
|
|
1827
|
+
const storage = this.injector.get(SecureStorageService, null);
|
|
1828
|
+
if (storage)
|
|
1829
|
+
storage.clear();
|
|
1830
|
+
}
|
|
1831
|
+
if (this.config?.autoClearClipboard) {
|
|
1832
|
+
const clipboard = this.injector.get(SensitiveClipboardService, null);
|
|
1833
|
+
if (clipboard)
|
|
1834
|
+
clipboard.clear();
|
|
1835
|
+
}
|
|
1836
|
+
this.config = null;
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SessionIdleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1840
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SessionIdleService, providedIn: 'root' });
|
|
1841
|
+
}
|
|
1842
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SessionIdleService, decorators: [{
|
|
1843
|
+
type: Injectable,
|
|
1844
|
+
args: [{
|
|
1845
|
+
providedIn: 'root',
|
|
1846
|
+
}]
|
|
1847
|
+
}], ctorParameters: () => [] });
|
|
1848
|
+
|
|
1849
|
+
const REPLAY_WINDOW_MS = 30_000;
|
|
1850
|
+
class SecureMessageService {
|
|
1851
|
+
ngZone = inject(NgZone);
|
|
1852
|
+
platformId = inject(PLATFORM_ID);
|
|
1853
|
+
document = inject(DOCUMENT);
|
|
1854
|
+
crypto = inject(WebCryptoService);
|
|
1855
|
+
config = null;
|
|
1856
|
+
_lastMessage = signal(null, ...(ngDevMode ? [{ debugName: "_lastMessage" }] : /* istanbul ignore next */ []));
|
|
1857
|
+
_messages$ = new Subject();
|
|
1858
|
+
messageHandler = null;
|
|
1859
|
+
get targetWindow() {
|
|
1860
|
+
return this.document.defaultView;
|
|
1861
|
+
}
|
|
1862
|
+
/** Returns a new HMAC-SHA-256 CryptoKey ready to use in `configure()`. */
|
|
1863
|
+
generateChannelKey() {
|
|
1864
|
+
return this.crypto.generateHmacKey('HMAC-SHA-256');
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Configures the service with a signing key and allowed origins.
|
|
1868
|
+
* Starts listening for incoming messages.
|
|
1869
|
+
*/
|
|
1870
|
+
configure(config) {
|
|
1871
|
+
this.destroy();
|
|
1872
|
+
this.config = config;
|
|
1873
|
+
if (!isPlatformBrowser(this.platformId))
|
|
1874
|
+
return;
|
|
1875
|
+
this.ngZone.runOutsideAngular(() => {
|
|
1876
|
+
this.messageHandler = (event) => this.handleMessage(event);
|
|
1877
|
+
this.targetWindow.addEventListener('message', this.messageHandler);
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Signs and sends a payload to a target window.
|
|
1882
|
+
* @throws if `targetOrigin` is `'*'`
|
|
1883
|
+
*/
|
|
1884
|
+
async send(target, payload, targetOrigin) {
|
|
1885
|
+
if (targetOrigin === '*') {
|
|
1886
|
+
throw new Error('SecureMessageService: targetOrigin must be an explicit origin, not "*".');
|
|
1887
|
+
}
|
|
1888
|
+
if (!this.config) {
|
|
1889
|
+
throw new Error('SecureMessageService: call configure() before send().');
|
|
1890
|
+
}
|
|
1891
|
+
const timestamp = Date.now();
|
|
1892
|
+
const nonce = this.targetWindow.crypto.randomUUID();
|
|
1893
|
+
const body = { payload, timestamp, nonce };
|
|
1894
|
+
const signature = await this.crypto.sign(this.config.signingKey, JSON.stringify(body));
|
|
1895
|
+
const envelope = { __signed: true, ...body, signature };
|
|
1896
|
+
target.postMessage(envelope, targetOrigin);
|
|
1897
|
+
}
|
|
1898
|
+
/** Observable of verified incoming messages. */
|
|
1899
|
+
messages$() {
|
|
1900
|
+
return this._messages$.asObservable();
|
|
1901
|
+
}
|
|
1902
|
+
/** Signal with the last verified incoming message (null before first message). */
|
|
1903
|
+
lastMessage() {
|
|
1904
|
+
return this._lastMessage;
|
|
1905
|
+
}
|
|
1906
|
+
/** Removes the window listener and completes the internal Subject. */
|
|
1907
|
+
destroy() {
|
|
1908
|
+
if (this.messageHandler && isPlatformBrowser(this.platformId)) {
|
|
1909
|
+
this.targetWindow.removeEventListener('message', this.messageHandler);
|
|
1910
|
+
this.messageHandler = null;
|
|
1911
|
+
}
|
|
1912
|
+
this.config = null;
|
|
1913
|
+
}
|
|
1914
|
+
async handleMessage(event) {
|
|
1915
|
+
if (!this.config)
|
|
1916
|
+
return;
|
|
1917
|
+
// 1. Origin whitelist
|
|
1918
|
+
if (!this.config.allowedOrigins.includes(event.origin))
|
|
1919
|
+
return;
|
|
1920
|
+
// 2. Envelope shape
|
|
1921
|
+
const env = event.data;
|
|
1922
|
+
if (env?.__signed !== true)
|
|
1923
|
+
return;
|
|
1924
|
+
const { payload, timestamp, nonce, signature } = env;
|
|
1925
|
+
if (!payload || !timestamp || !nonce || !signature)
|
|
1926
|
+
return;
|
|
1927
|
+
// 3. Replay window
|
|
1928
|
+
if (Date.now() - timestamp > REPLAY_WINDOW_MS)
|
|
1929
|
+
return;
|
|
1930
|
+
// 4. Signature verification
|
|
1931
|
+
const body = { payload, timestamp, nonce };
|
|
1932
|
+
const valid = await this.crypto.verify(this.config.signingKey, JSON.stringify(body), signature);
|
|
1933
|
+
if (!valid)
|
|
1934
|
+
return;
|
|
1935
|
+
// 5. Emit inside NgZone so signals trigger CD
|
|
1936
|
+
this.ngZone.run(() => {
|
|
1937
|
+
const message = { data: payload, origin: event.origin, timestamp };
|
|
1938
|
+
this._lastMessage.set(message);
|
|
1939
|
+
this._messages$.next(message);
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1943
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureMessageService, providedIn: 'root' });
|
|
1944
|
+
}
|
|
1945
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureMessageService, decorators: [{
|
|
1946
|
+
type: Injectable,
|
|
1947
|
+
args: [{
|
|
1948
|
+
providedIn: 'root',
|
|
1949
|
+
}]
|
|
1950
|
+
}] });
|
|
1951
|
+
|
|
1719
1952
|
const defaultSecurityConfig = {
|
|
1720
1953
|
enableRegexSecurity: true,
|
|
1721
1954
|
enableWebCrypto: true,
|
|
@@ -1727,6 +1960,8 @@ const defaultSecurityConfig = {
|
|
|
1727
1960
|
enableHibp: false,
|
|
1728
1961
|
enableRateLimiter: false,
|
|
1729
1962
|
enableCsrf: false,
|
|
1963
|
+
enableSessionIdle: false,
|
|
1964
|
+
enableSecureMessage: false,
|
|
1730
1965
|
defaultTimeout: 5000,
|
|
1731
1966
|
safeMode: false,
|
|
1732
1967
|
};
|
|
@@ -1755,6 +1990,10 @@ function provideSecurity(config = {}) {
|
|
|
1755
1990
|
if (mergedConfig.enableCsrf) {
|
|
1756
1991
|
providers.push(WebCryptoService, CsrfService);
|
|
1757
1992
|
}
|
|
1993
|
+
if (mergedConfig.enableSessionIdle)
|
|
1994
|
+
providers.push(SessionIdleService);
|
|
1995
|
+
if (mergedConfig.enableSecureMessage)
|
|
1996
|
+
providers.push(SecureMessageService);
|
|
1758
1997
|
return makeEnvironmentProviders(providers);
|
|
1759
1998
|
}
|
|
1760
1999
|
function provideRegexSecurity() {
|
|
@@ -1804,9 +2043,15 @@ function provideCsrf(config) {
|
|
|
1804
2043
|
...(config ? [{ provide: CSRF_CONFIG, useValue: config }] : []),
|
|
1805
2044
|
]);
|
|
1806
2045
|
}
|
|
2046
|
+
function provideSessionIdle() {
|
|
2047
|
+
return makeEnvironmentProviders([SessionIdleService]);
|
|
2048
|
+
}
|
|
2049
|
+
function provideSecureMessage() {
|
|
2050
|
+
return makeEnvironmentProviders([WebCryptoService, SecureMessageService]);
|
|
2051
|
+
}
|
|
1807
2052
|
|
|
1808
2053
|
/**
|
|
1809
2054
|
* Generated bundle index. Do not edit.
|
|
1810
2055
|
*/
|
|
1811
2056
|
|
|
1812
|
-
export { CSRF_CONFIG, ClipboardUnsupportedError, CsrfService, DEFAULT_ALLOWED_ATTRIBUTES, DEFAULT_ALLOWED_TAGS, HIBP_CONFIG, HibpService, InputSanitizerService, InvalidJwtError, JwtService, PasswordStrengthService, RATE_LIMITER_CONFIG, RateLimitExceededError, RateLimiterService, RegexSecurityBuilder, RegexSecurityService, SANITIZER_CONFIG, SECURE_STORAGE_CONFIG, SecureStorageService, SensitiveClipboardService, WebCryptoService, assessPasswordStrength, containsScriptInjection, containsSqlInjectionHints, defaultSecurityConfig, isHtmlSafe, isUrlSafe, provideCsrf, provideHibp, provideInputSanitizer, provideJwt, providePasswordStrength, provideRateLimiter, provideRegexSecurity, provideSecureStorage, provideSecurity, provideSensitiveClipboard, provideWebCrypto, sanitizeHtmlString, sanitizeUrlString, withCsrfHeader };
|
|
2057
|
+
export { CSRF_CONFIG, ClipboardUnsupportedError, CsrfService, DEFAULT_ALLOWED_ATTRIBUTES, DEFAULT_ALLOWED_TAGS, HIBP_CONFIG, HibpService, InputSanitizerService, InvalidJwtError, JwtService, PasswordStrengthService, RATE_LIMITER_CONFIG, RateLimitExceededError, RateLimiterService, RegexSecurityBuilder, RegexSecurityService, SANITIZER_CONFIG, SECURE_STORAGE_CONFIG, SecureMessageService, SecureStorageService, SensitiveClipboardService, SessionIdleService, WebCryptoService, assessPasswordStrength, containsScriptInjection, containsSqlInjectionHints, defaultSecurityConfig, isHtmlSafe, isUrlSafe, provideCsrf, provideHibp, provideInputSanitizer, provideJwt, providePasswordStrength, provideRateLimiter, provideRegexSecurity, provideSecureMessage, provideSecureStorage, provideSecurity, provideSensitiveClipboard, provideSessionIdle, provideWebCrypto, sanitizeHtmlString, sanitizeUrlString, withCsrfHeader };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-helpers/security",
|
|
3
|
-
"version": "21.
|
|
3
|
+
"version": "21.4.2",
|
|
4
4
|
"description": "Angular security helpers for preventing ReDoS and other security vulnerabilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
"xss",
|
|
17
17
|
"sanitization",
|
|
18
18
|
"password-strength",
|
|
19
|
-
"secure-storage"
|
|
19
|
+
"secure-storage",
|
|
20
|
+
"session-idle",
|
|
21
|
+
"inactivity",
|
|
22
|
+
"postmessage",
|
|
23
|
+
"iframe",
|
|
24
|
+
"secure-channel"
|
|
20
25
|
],
|
|
21
26
|
"author": "Angular Helpers Team",
|
|
22
27
|
"license": "MIT",
|
|
@@ -64,5 +69,6 @@
|
|
|
64
69
|
"default": "./fesm2022/angular-helpers-security-signal-forms.mjs"
|
|
65
70
|
}
|
|
66
71
|
},
|
|
67
|
-
"sideEffects": false
|
|
68
|
-
|
|
72
|
+
"sideEffects": false,
|
|
73
|
+
"type": "module"
|
|
74
|
+
}
|
|
@@ -537,6 +537,10 @@ declare class SensitiveClipboardService {
|
|
|
537
537
|
*/
|
|
538
538
|
cancelPendingClear(): void;
|
|
539
539
|
private safeClear;
|
|
540
|
+
/**
|
|
541
|
+
* Forcefully clears the clipboard unconditionally.
|
|
542
|
+
*/
|
|
543
|
+
clear(): Promise<void>;
|
|
540
544
|
static ɵfac: i0.ɵɵFactoryDeclaration<SensitiveClipboardService, never>;
|
|
541
545
|
static ɵprov: i0.ɵɵInjectableDeclaration<SensitiveClipboardService>;
|
|
542
546
|
}
|
|
@@ -745,6 +749,82 @@ interface CsrfHeaderOptions {
|
|
|
745
749
|
*/
|
|
746
750
|
declare function withCsrfHeader(options?: CsrfHeaderOptions): HttpInterceptorFn;
|
|
747
751
|
|
|
752
|
+
interface SessionIdleConfig {
|
|
753
|
+
timeoutMs: number;
|
|
754
|
+
warningThresholdMs?: number;
|
|
755
|
+
autoClearStorage?: boolean;
|
|
756
|
+
autoClearClipboard?: boolean;
|
|
757
|
+
events?: string[];
|
|
758
|
+
}
|
|
759
|
+
declare class SessionIdleService {
|
|
760
|
+
private ngZone;
|
|
761
|
+
private document;
|
|
762
|
+
private injector;
|
|
763
|
+
private destroyRef;
|
|
764
|
+
private _isIdle;
|
|
765
|
+
private _isWarning;
|
|
766
|
+
private _timeRemaining;
|
|
767
|
+
private _timeoutSubject;
|
|
768
|
+
readonly isIdle: Signal<boolean>;
|
|
769
|
+
readonly isWarning: Signal<boolean>;
|
|
770
|
+
readonly timeRemaining: Signal<number | null>;
|
|
771
|
+
readonly onTimeout: Observable<void>;
|
|
772
|
+
private config;
|
|
773
|
+
private lastActivityTime;
|
|
774
|
+
private timerInterval;
|
|
775
|
+
private eventSubscription?;
|
|
776
|
+
constructor();
|
|
777
|
+
start(config: SessionIdleConfig): void;
|
|
778
|
+
stop(): void;
|
|
779
|
+
reset(): void;
|
|
780
|
+
private checkIdle;
|
|
781
|
+
private triggerTimeout;
|
|
782
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SessionIdleService, never>;
|
|
783
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SessionIdleService>;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
interface SecureMessageConfig {
|
|
787
|
+
allowedOrigins: string[];
|
|
788
|
+
signingKey: CryptoKey;
|
|
789
|
+
}
|
|
790
|
+
interface SecureMessage<T = unknown> {
|
|
791
|
+
data: T;
|
|
792
|
+
origin: string;
|
|
793
|
+
timestamp: number;
|
|
794
|
+
}
|
|
795
|
+
declare class SecureMessageService {
|
|
796
|
+
private readonly ngZone;
|
|
797
|
+
private readonly platformId;
|
|
798
|
+
private readonly document;
|
|
799
|
+
private readonly crypto;
|
|
800
|
+
private config;
|
|
801
|
+
private _lastMessage;
|
|
802
|
+
private _messages$;
|
|
803
|
+
private messageHandler;
|
|
804
|
+
private get targetWindow();
|
|
805
|
+
/** Returns a new HMAC-SHA-256 CryptoKey ready to use in `configure()`. */
|
|
806
|
+
generateChannelKey(): Promise<CryptoKey>;
|
|
807
|
+
/**
|
|
808
|
+
* Configures the service with a signing key and allowed origins.
|
|
809
|
+
* Starts listening for incoming messages.
|
|
810
|
+
*/
|
|
811
|
+
configure(config: SecureMessageConfig): void;
|
|
812
|
+
/**
|
|
813
|
+
* Signs and sends a payload to a target window.
|
|
814
|
+
* @throws if `targetOrigin` is `'*'`
|
|
815
|
+
*/
|
|
816
|
+
send<T>(target: Window, payload: T, targetOrigin: string): Promise<void>;
|
|
817
|
+
/** Observable of verified incoming messages. */
|
|
818
|
+
messages$<T = unknown>(): Observable<SecureMessage<T>>;
|
|
819
|
+
/** Signal with the last verified incoming message (null before first message). */
|
|
820
|
+
lastMessage<T = unknown>(): Signal<SecureMessage<T> | null>;
|
|
821
|
+
/** Removes the window listener and completes the internal Subject. */
|
|
822
|
+
destroy(): void;
|
|
823
|
+
private handleMessage;
|
|
824
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SecureMessageService, never>;
|
|
825
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SecureMessageService>;
|
|
826
|
+
}
|
|
827
|
+
|
|
748
828
|
interface SecurityConfig {
|
|
749
829
|
enableRegexSecurity?: boolean;
|
|
750
830
|
enableWebCrypto?: boolean;
|
|
@@ -756,6 +836,8 @@ interface SecurityConfig {
|
|
|
756
836
|
enableHibp?: boolean;
|
|
757
837
|
enableRateLimiter?: boolean;
|
|
758
838
|
enableCsrf?: boolean;
|
|
839
|
+
enableSessionIdle?: boolean;
|
|
840
|
+
enableSecureMessage?: boolean;
|
|
759
841
|
defaultTimeout?: number;
|
|
760
842
|
safeMode?: boolean;
|
|
761
843
|
}
|
|
@@ -771,6 +853,8 @@ declare function provideSensitiveClipboard(): EnvironmentProviders;
|
|
|
771
853
|
declare function provideHibp(config?: HibpConfig): EnvironmentProviders;
|
|
772
854
|
declare function provideRateLimiter(config?: RateLimiterConfig): EnvironmentProviders;
|
|
773
855
|
declare function provideCsrf(config?: CsrfConfig): EnvironmentProviders;
|
|
856
|
+
declare function provideSessionIdle(): EnvironmentProviders;
|
|
857
|
+
declare function provideSecureMessage(): EnvironmentProviders;
|
|
774
858
|
|
|
775
|
-
export { CSRF_CONFIG, ClipboardUnsupportedError, CsrfService, DEFAULT_ALLOWED_ATTRIBUTES, DEFAULT_ALLOWED_TAGS, HIBP_CONFIG, HibpService, InputSanitizerService, InvalidJwtError, JwtService, PasswordStrengthService, RATE_LIMITER_CONFIG, RateLimitExceededError, RateLimiterService, RegexSecurityBuilder, RegexSecurityService, SANITIZER_CONFIG, SECURE_STORAGE_CONFIG, SecureStorageService, SensitiveClipboardService, WebCryptoService, assessPasswordStrength, containsScriptInjection, containsSqlInjectionHints, defaultSecurityConfig, isHtmlSafe, isUrlSafe, provideCsrf, provideHibp, provideInputSanitizer, provideJwt, providePasswordStrength, provideRateLimiter, provideRegexSecurity, provideSecureStorage, provideSecurity, provideSensitiveClipboard, provideWebCrypto, sanitizeHtmlString, sanitizeUrlString, withCsrfHeader };
|
|
776
|
-
export type { AesEncryptResult, AesKeyLength, CopyStatus, CsrfConfig, CsrfHeaderOptions, CsrfStorageTarget, HashAlgorithm, HibpConfig, HibpResult, HmacAlgorithm, HtmlSanitizerOptions, HttpMethod, JwtStandardClaims, PasswordAssessment, PasswordLabel, PasswordScore, PasswordStrengthResult, RateLimitPolicy, RateLimiterConfig, RegexBuilderOptions, RegexSecurityConfig, RegexSecurityResult, RegexTestResult, SanitizerConfig, SecureStorageConfig, SecurityConfig, SensitiveCopyOptions, StorageTarget };
|
|
859
|
+
export { CSRF_CONFIG, ClipboardUnsupportedError, CsrfService, DEFAULT_ALLOWED_ATTRIBUTES, DEFAULT_ALLOWED_TAGS, HIBP_CONFIG, HibpService, InputSanitizerService, InvalidJwtError, JwtService, PasswordStrengthService, RATE_LIMITER_CONFIG, RateLimitExceededError, RateLimiterService, RegexSecurityBuilder, RegexSecurityService, SANITIZER_CONFIG, SECURE_STORAGE_CONFIG, SecureMessageService, SecureStorageService, SensitiveClipboardService, SessionIdleService, WebCryptoService, assessPasswordStrength, containsScriptInjection, containsSqlInjectionHints, defaultSecurityConfig, isHtmlSafe, isUrlSafe, provideCsrf, provideHibp, provideInputSanitizer, provideJwt, providePasswordStrength, provideRateLimiter, provideRegexSecurity, provideSecureMessage, provideSecureStorage, provideSecurity, provideSensitiveClipboard, provideSessionIdle, provideWebCrypto, sanitizeHtmlString, sanitizeUrlString, withCsrfHeader };
|
|
860
|
+
export type { AesEncryptResult, AesKeyLength, CopyStatus, CsrfConfig, CsrfHeaderOptions, CsrfStorageTarget, HashAlgorithm, HibpConfig, HibpResult, HmacAlgorithm, HtmlSanitizerOptions, HttpMethod, JwtStandardClaims, PasswordAssessment, PasswordLabel, PasswordScore, PasswordStrengthResult, RateLimitPolicy, RateLimiterConfig, RegexBuilderOptions, RegexSecurityConfig, RegexSecurityResult, RegexTestResult, SanitizerConfig, SecureMessage, SecureMessageConfig, SecureStorageConfig, SecurityConfig, SensitiveCopyOptions, SessionIdleConfig, StorageTarget };
|