@angular-helpers/security 21.4.1 → 21.4.3

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 CHANGED
@@ -98,7 +98,7 @@ Security package for Angular applications that prevents common attacks like ReDo
98
98
  ## 📦 Installation
99
99
 
100
100
  ```bash
101
- npm install @angular-helpers/security
101
+ pnpm add @angular-helpers/security
102
102
  ```
103
103
 
104
104
  ## 🚀 Basic Usage
@@ -286,10 +286,10 @@ class RegexSecurityService {
286
286
  });
287
287
  this.workers.clear();
288
288
  }
289
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: RegexSecurityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
290
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: RegexSecurityService });
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 });
291
291
  }
292
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: RegexSecurityService, decorators: [{
292
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RegexSecurityService, decorators: [{
293
293
  type: Injectable
294
294
  }] });
295
295
  /**
@@ -529,10 +529,10 @@ class WebCryptoService {
529
529
  hmacHashName(algorithm) {
530
530
  return algorithm.replace('HMAC-', '');
531
531
  }
532
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebCryptoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
533
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebCryptoService });
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 });
534
534
  }
535
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebCryptoService, decorators: [{
535
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: WebCryptoService, decorators: [{
536
536
  type: Injectable
537
537
  }] });
538
538
 
@@ -720,10 +720,10 @@ class SecureStorageService {
720
720
  base64ToBytes(base64) {
721
721
  return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
722
722
  }
723
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SecureStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
724
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SecureStorageService });
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 });
725
725
  }
726
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SecureStorageService, decorators: [{
726
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureStorageService, decorators: [{
727
727
  type: Injectable
728
728
  }], ctorParameters: () => [] });
729
729
 
@@ -922,6 +922,7 @@ const DEFAULT_ALLOWED_TAGS = [
922
922
  const DEFAULT_ALLOWED_ATTRIBUTES = {
923
923
  a: ['href'],
924
924
  };
925
+ const URL_ATTRIBUTES = new Set(['href', 'src', 'action', 'formaction', 'data', 'poster']);
925
926
  /**
926
927
  * Sanitizes an HTML string by allowlist. Browser-only: requires DOMParser.
927
928
  *
@@ -980,7 +981,7 @@ function sanitizeElementAttributes(element, tagName, allowedAttributes) {
980
981
  attrsToRemove.push(attr.name);
981
982
  continue;
982
983
  }
983
- if (attr.name === 'href' && sanitizeUrlString(attr.value) === null) {
984
+ if (URL_ATTRIBUTES.has(attr.name) && sanitizeUrlString(attr.value) === null) {
984
985
  attrsToRemove.push(attr.name);
985
986
  }
986
987
  }
@@ -1068,10 +1069,10 @@ class InputSanitizerService {
1068
1069
  return null;
1069
1070
  }
1070
1071
  }
1071
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InputSanitizerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1072
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InputSanitizerService });
1072
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: InputSanitizerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1073
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: InputSanitizerService });
1073
1074
  }
1074
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InputSanitizerService, decorators: [{
1075
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: InputSanitizerService, decorators: [{
1075
1076
  type: Injectable
1076
1077
  }], ctorParameters: () => [] });
1077
1078
 
@@ -1103,10 +1104,10 @@ class PasswordStrengthService {
1103
1104
  assess(password) {
1104
1105
  return assessPasswordStrength(password);
1105
1106
  }
1106
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PasswordStrengthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1107
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PasswordStrengthService });
1107
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PasswordStrengthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1108
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PasswordStrengthService });
1108
1109
  }
1109
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PasswordStrengthService, decorators: [{
1110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PasswordStrengthService, decorators: [{
1110
1111
  type: Injectable
1111
1112
  }] });
1112
1113
 
@@ -1218,10 +1219,10 @@ class JwtService {
1218
1219
  }
1219
1220
  return payload[name] ?? null;
1220
1221
  }
1221
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: JwtService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1222
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: JwtService });
1222
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: JwtService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1223
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: JwtService });
1223
1224
  }
1224
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: JwtService, decorators: [{
1225
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: JwtService, decorators: [{
1225
1226
  type: Injectable
1226
1227
  }] });
1227
1228
  function base64UrlDecode(input) {
@@ -1364,10 +1365,10 @@ class SensitiveClipboardService {
1364
1365
  // ignore
1365
1366
  }
1366
1367
  }
1367
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SensitiveClipboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1368
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SensitiveClipboardService });
1368
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SensitiveClipboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1369
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SensitiveClipboardService });
1369
1370
  }
1370
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SensitiveClipboardService, decorators: [{
1371
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SensitiveClipboardService, decorators: [{
1371
1372
  type: Injectable
1372
1373
  }], ctorParameters: () => [] });
1373
1374
 
@@ -1440,11 +1441,14 @@ class HibpService {
1440
1441
  const match = findSuffixMatch(body, suffix);
1441
1442
  return match > 0 ? { leaked: true, count: match } : { leaked: false, count: 0 };
1442
1443
  }
1443
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: HibpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1444
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: HibpService });
1444
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HibpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1445
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HibpService, providedIn: 'root' });
1445
1446
  }
1446
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: HibpService, decorators: [{
1447
- type: Injectable
1447
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HibpService, decorators: [{
1448
+ type: Injectable,
1449
+ args: [{
1450
+ providedIn: 'root',
1451
+ }]
1448
1452
  }], ctorParameters: () => [] });
1449
1453
  function findSuffixMatch(body, suffix) {
1450
1454
  const lines = body.split(/\r?\n/);
@@ -1509,13 +1513,23 @@ class RateLimiterService {
1509
1513
  this.configure(key, policy);
1510
1514
  }
1511
1515
  }
1512
- this.destroyRef.onDestroy(() => this.buckets.clear());
1516
+ this.destroyRef.onDestroy(() => {
1517
+ for (const bucket of this.buckets.values()) {
1518
+ if (bucket.timerId)
1519
+ clearTimeout(bucket.timerId);
1520
+ }
1521
+ this.buckets.clear();
1522
+ });
1513
1523
  }
1514
1524
  /**
1515
1525
  * Registers or updates the policy for `key`. Re-configuring an existing key resets its state.
1516
1526
  */
1517
1527
  configure(key, policy) {
1518
1528
  validatePolicy(policy);
1529
+ const existing = this.buckets.get(key);
1530
+ if (existing && existing.timerId) {
1531
+ clearTimeout(existing.timerId);
1532
+ }
1519
1533
  const now = Date.now();
1520
1534
  const initialRemaining = policy.type === 'token-bucket' ? policy.capacity : policy.max;
1521
1535
  this.buckets.set(key, {
@@ -1550,6 +1564,7 @@ class RateLimiterService {
1550
1564
  }
1551
1565
  bucket.tokens -= tokens;
1552
1566
  bucket.remaining.set(Math.floor(bucket.tokens));
1567
+ this.scheduleAutoRefill(key, bucket);
1553
1568
  return;
1554
1569
  }
1555
1570
  // sliding-window
@@ -1563,6 +1578,7 @@ class RateLimiterService {
1563
1578
  for (let i = 0; i < tokens; i++)
1564
1579
  bucket.timestamps.push(now);
1565
1580
  bucket.remaining.set(bucket.policy.max - bucket.timestamps.length);
1581
+ this.scheduleAutoRefill(key, bucket);
1566
1582
  }
1567
1583
  /**
1568
1584
  * Reactive signal indicating whether a single unit can be consumed from `key` right now.
@@ -1591,6 +1607,10 @@ class RateLimiterService {
1591
1607
  const bucket = this.buckets.get(key);
1592
1608
  if (!bucket)
1593
1609
  return;
1610
+ if (bucket.timerId) {
1611
+ clearTimeout(bucket.timerId);
1612
+ bucket.timerId = undefined;
1613
+ }
1594
1614
  bucket.timestamps = [];
1595
1615
  if (bucket.policy.type === 'token-bucket') {
1596
1616
  bucket.tokens = bucket.policy.capacity;
@@ -1612,10 +1632,45 @@ class RateLimiterService {
1612
1632
  bucket.lastRefillAt = now;
1613
1633
  bucket.remaining.set(Math.floor(bucket.tokens));
1614
1634
  }
1615
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: RateLimiterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1616
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: RateLimiterService });
1635
+ scheduleAutoRefill(key, bucket) {
1636
+ if (bucket.timerId) {
1637
+ clearTimeout(bucket.timerId);
1638
+ bucket.timerId = undefined;
1639
+ }
1640
+ const now = Date.now();
1641
+ if (bucket.policy.type === 'token-bucket') {
1642
+ if (bucket.tokens >= bucket.policy.capacity) {
1643
+ return;
1644
+ }
1645
+ const currentFloor = Math.floor(bucket.tokens);
1646
+ const nextInteger = currentFloor + 1;
1647
+ const deficit = nextInteger - bucket.tokens;
1648
+ const timeToNextTokenMs = Math.max(10, Math.ceil((deficit / bucket.policy.refillPerSecond) * 1000));
1649
+ bucket.timerId = setTimeout(() => {
1650
+ const tNow = Date.now();
1651
+ this.refillTokenBucket(bucket, tNow);
1652
+ this.scheduleAutoRefill(key, bucket);
1653
+ }, timeToNextTokenMs);
1654
+ }
1655
+ else {
1656
+ // sliding-window
1657
+ const windowStart = now - bucket.policy.windowMs;
1658
+ bucket.timestamps = bucket.timestamps.filter((t) => t > windowStart);
1659
+ bucket.remaining.set(bucket.policy.max - bucket.timestamps.length);
1660
+ if (bucket.timestamps.length === 0) {
1661
+ return;
1662
+ }
1663
+ const oldest = bucket.timestamps[0];
1664
+ const timeToExpiryMs = Math.max(10, oldest + bucket.policy.windowMs - now);
1665
+ bucket.timerId = setTimeout(() => {
1666
+ this.scheduleAutoRefill(key, bucket);
1667
+ }, timeToExpiryMs);
1668
+ }
1669
+ }
1670
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RateLimiterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1671
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RateLimiterService });
1617
1672
  }
1618
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: RateLimiterService, decorators: [{
1673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: RateLimiterService, decorators: [{
1619
1674
  type: Injectable
1620
1675
  }], ctorParameters: () => [] });
1621
1676
  function validatePolicy(policy) {
@@ -1694,10 +1749,10 @@ class CsrfService {
1694
1749
  get nativeStorage() {
1695
1750
  return this.storageTarget === 'session' ? sessionStorage : localStorage;
1696
1751
  }
1697
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CsrfService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1698
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CsrfService });
1752
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: CsrfService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1753
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: CsrfService });
1699
1754
  }
1700
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CsrfService, decorators: [{
1755
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: CsrfService, decorators: [{
1701
1756
  type: Injectable
1702
1757
  }], ctorParameters: () => [] });
1703
1758
  const DEFAULT_HEADER_NAME = 'X-CSRF-Token';
@@ -1833,10 +1888,10 @@ class SessionIdleService {
1833
1888
  this.config = null;
1834
1889
  });
1835
1890
  }
1836
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SessionIdleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1837
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SessionIdleService, providedIn: 'root' });
1891
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SessionIdleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1892
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SessionIdleService, providedIn: 'root' });
1838
1893
  }
1839
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SessionIdleService, decorators: [{
1894
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SessionIdleService, decorators: [{
1840
1895
  type: Injectable,
1841
1896
  args: [{
1842
1897
  providedIn: 'root',
@@ -1936,10 +1991,10 @@ class SecureMessageService {
1936
1991
  this._messages$.next(message);
1937
1992
  });
1938
1993
  }
1939
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SecureMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1940
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SecureMessageService, providedIn: 'root' });
1994
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1995
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureMessageService, providedIn: 'root' });
1941
1996
  }
1942
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SecureMessageService, decorators: [{
1997
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SecureMessageService, decorators: [{
1943
1998
  type: Injectable,
1944
1999
  args: [{
1945
2000
  providedIn: 'root',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular-helpers/security",
3
- "version": "21.4.1",
3
+ "version": "21.4.3",
4
4
  "description": "Angular security helpers for preventing ReDoS and other security vulnerabilities",
5
5
  "keywords": [
6
6
  "angular",
@@ -71,4 +71,4 @@
71
71
  },
72
72
  "sideEffects": false,
73
73
  "type": "module"
74
- }
74
+ }
@@ -675,6 +675,7 @@ declare class RateLimiterService {
675
675
  */
676
676
  reset(key: string): void;
677
677
  private refillTokenBucket;
678
+ private scheduleAutoRefill;
678
679
  static ɵfac: i0.ɵɵFactoryDeclaration<RateLimiterService, never>;
679
680
  static ɵprov: i0.ɵɵInjectableDeclaration<RateLimiterService>;
680
681
  }