@holo-js/security 0.3.27 → 0.4.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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  normalizeCorsConfig,
3
3
  normalizeSecurityConfig
4
- } from "./chunk-WRMZRO77.mjs";
4
+ } from "./chunk-D3ZN5MLJ.mjs";
5
5
 
6
6
  // src/contracts.ts
7
7
  var SecurityCsrfError = class extends Error {
@@ -27,6 +27,8 @@ var PendingSecurityLimiterDefinition = class {
27
27
  this.maxAttempts = maxAttempts;
28
28
  this.decaySeconds = decaySeconds;
29
29
  }
30
+ maxAttempts;
31
+ decaySeconds;
30
32
  by(key) {
31
33
  if (typeof key !== "function") {
32
34
  throw new TypeError("[@holo-js/security] Rate limiter key resolvers must be functions.");
@@ -64,20 +66,6 @@ var limit = Object.freeze({
64
66
  );
65
67
  }
66
68
  });
67
- function ip(request, trustedProxy = false) {
68
- if (!trustedProxy) {
69
- return "unknown";
70
- }
71
- const forwarded = request.headers.get("x-forwarded-for")?.split(",", 1)[0]?.trim();
72
- if (forwarded) {
73
- return forwarded;
74
- }
75
- const realIp = request.headers.get("x-real-ip")?.trim();
76
- if (realIp) {
77
- return realIp;
78
- }
79
- return "unknown";
80
- }
81
69
  function normalizeLimiterIntegerInput(value, label) {
82
70
  if (typeof value === "undefined") {
83
71
  throw new TypeError(`[@holo-js/security] ${label} is required.`);
@@ -117,6 +105,7 @@ function defineSecurityRuntimeBindings(bindings) {
117
105
  cors: normalizeCorsConfig(bindings.cors),
118
106
  rateLimitStore: bindings.rateLimitStore,
119
107
  csrfSigningKey: bindings.csrfSigningKey,
108
+ csrfSession: bindings.csrfSession,
120
109
  defaultKeyResolver: bindings.defaultKeyResolver
121
110
  });
122
111
  }
@@ -144,7 +133,6 @@ export {
144
133
  SecurityCsrfError,
145
134
  SecurityRateLimitError,
146
135
  limit,
147
- ip,
148
136
  defineRateLimiter,
149
137
  defineSecurityRuntimeBindings,
150
138
  createMemoryRateLimitStoreConfig,
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import './config.js';
1
+ import './config-BK6q6Yh9.js';
2
2
  import { SecurityClientConfig } from './contracts.js';
3
3
  import '@holo-js/kernel';
4
4
 
package/dist/client.mjs CHANGED
@@ -2,13 +2,13 @@ import {
2
2
  getDefaultSecurityClientConfig,
3
3
  readSecurityClientConfigFromCookies,
4
4
  securityClientConfigInternals
5
- } from "./chunk-PC2YCWDH.mjs";
5
+ } from "./chunk-EINZXLC2.mjs";
6
6
 
7
7
  // src/client.ts
8
8
  function getSecurityClientConfig() {
9
9
  const runtime = globalThis;
10
10
  const config = readSecurityClientConfigFromCookies(runtime.document?.cookie) ?? getDefaultSecurityClientConfig();
11
- return Object.freeze({ csrf: Object.freeze({ field: config.csrf.field, cookie: config.csrf.cookie }) });
11
+ return Object.freeze({ csrf: Object.freeze({ field: config.csrf.field, header: config.csrf.header, cookie: config.csrf.cookie }) });
12
12
  }
13
13
  var securityClientInternals = {
14
14
  getDefaultSecurityClientConfig,
@@ -0,0 +1,145 @@
1
+ import { NormalizedHoloRedisClusterNodeConfig, HoloRedisClusterNodeConfig, NormalizedHoloRedisConfig } from '@holo-js/kernel';
2
+
3
+ type SecurityTrustedProxies = readonly string[] | '*';
4
+
5
+ interface HoloCorsConfig {
6
+ readonly paths?: readonly string[];
7
+ readonly origins?: readonly string[];
8
+ readonly methods?: readonly string[];
9
+ readonly headers?: readonly string[];
10
+ readonly credentials?: boolean;
11
+ readonly maxAge?: number | string;
12
+ readonly statefulDomains?: readonly string[];
13
+ }
14
+ declare module '@holo-js/config' {
15
+ interface HoloConfigRegistry {
16
+ cors: NormalizedHoloCorsConfig;
17
+ security: NormalizedHoloSecurityConfig;
18
+ }
19
+ }
20
+ interface NormalizedHoloCorsConfig {
21
+ readonly paths: readonly string[];
22
+ readonly origins: readonly string[];
23
+ readonly methods: readonly string[];
24
+ readonly headers: readonly string[];
25
+ readonly credentials: boolean;
26
+ readonly maxAge: number;
27
+ readonly statefulDomains: readonly string[];
28
+ }
29
+ type SecurityRateLimitDriver = 'memory' | 'file' | 'redis';
30
+ interface HoloSecurityCsrfConfig {
31
+ readonly enabled?: boolean;
32
+ readonly field?: string;
33
+ readonly header?: string;
34
+ readonly cookie?: string;
35
+ readonly except?: readonly string[];
36
+ }
37
+ interface NormalizedHoloSecurityCsrfConfig {
38
+ readonly enabled: boolean;
39
+ readonly field: string;
40
+ readonly header: string;
41
+ readonly cookie: string;
42
+ readonly except: readonly string[];
43
+ }
44
+ interface SecurityRateLimitContext<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
45
+ readonly request: Request;
46
+ readonly values?: TValues;
47
+ }
48
+ interface SecurityRateLimitKeyResolver<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
49
+ (context: SecurityRateLimitContext<TValues>): string | Promise<string>;
50
+ }
51
+ interface SecurityLimiterConfig<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
52
+ readonly maxAttempts: number | string;
53
+ readonly decaySeconds: number | string;
54
+ readonly key?: SecurityRateLimitKeyResolver<TValues>;
55
+ }
56
+ interface NormalizedSecurityLimiterConfig<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
57
+ readonly name: string;
58
+ readonly maxAttempts: number;
59
+ readonly decaySeconds: number;
60
+ readonly key?: SecurityRateLimitKeyResolver<TValues>;
61
+ }
62
+ interface SecurityRateLimitMemoryConfig {
63
+ readonly driver?: 'memory';
64
+ }
65
+ interface SecurityRateLimitFileConfig {
66
+ readonly path?: string;
67
+ }
68
+ interface SecurityRateLimitRedisConnectionConfig {
69
+ readonly url?: string;
70
+ readonly clusters?: readonly HoloRedisClusterNodeConfig[];
71
+ readonly host?: string;
72
+ readonly port?: number | string;
73
+ readonly password?: string;
74
+ readonly username?: string;
75
+ readonly db?: number | string;
76
+ }
77
+ interface SecurityRateLimitRedisConfig {
78
+ readonly connection?: string;
79
+ readonly prefix?: string;
80
+ }
81
+ interface HoloSecurityRateLimitConfig {
82
+ readonly driver?: SecurityRateLimitDriver;
83
+ readonly memory?: SecurityRateLimitMemoryConfig;
84
+ readonly file?: SecurityRateLimitFileConfig;
85
+ readonly redis?: SecurityRateLimitRedisConfig;
86
+ readonly limiters?: Readonly<Record<string, SecurityLimiterConfig>>;
87
+ }
88
+ interface NormalizedSecurityRateLimitMemoryConfig {
89
+ readonly driver: 'memory';
90
+ }
91
+ interface NormalizedSecurityRateLimitFileConfig {
92
+ readonly path: string;
93
+ }
94
+ interface NormalizedSecurityRateLimitRedisConfig {
95
+ readonly url?: string;
96
+ readonly clusters?: readonly NormalizedHoloRedisClusterNodeConfig[];
97
+ readonly host: string;
98
+ readonly port: number;
99
+ readonly password?: string;
100
+ readonly username?: string;
101
+ readonly db: number;
102
+ readonly connection: string;
103
+ readonly prefix: string;
104
+ }
105
+ interface NormalizedHoloSecurityRateLimitConfig {
106
+ readonly driver: SecurityRateLimitDriver;
107
+ readonly memory: NormalizedSecurityRateLimitMemoryConfig;
108
+ readonly file: NormalizedSecurityRateLimitFileConfig;
109
+ readonly redis: NormalizedSecurityRateLimitRedisConfig;
110
+ readonly limiters: Readonly<Record<string, NormalizedSecurityLimiterConfig>>;
111
+ }
112
+ interface HoloSecurityConfig {
113
+ readonly csrf?: boolean | HoloSecurityCsrfConfig;
114
+ readonly rateLimit?: HoloSecurityRateLimitConfig;
115
+ readonly trustedProxies?: SecurityTrustedProxies;
116
+ readonly maxBodyBytes?: number | string;
117
+ readonly maxUploadBodyBytes?: number | string;
118
+ }
119
+ interface NormalizedHoloSecurityConfig {
120
+ readonly csrf: NormalizedHoloSecurityCsrfConfig;
121
+ readonly rateLimit: NormalizedHoloSecurityRateLimitConfig;
122
+ readonly trustedProxies: SecurityTrustedProxies;
123
+ readonly maxBodyBytes: number;
124
+ readonly maxUploadBodyBytes: number;
125
+ }
126
+ declare const DEFAULT_CORS_PATHS: readonly ["/api/*"];
127
+ declare const DEFAULT_CORS_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
128
+ declare const DEFAULT_CORS_HEADERS: readonly ["Content-Type", "Authorization", "X-CSRF-TOKEN", "X-XSRF-TOKEN", "X-Requested-With"];
129
+ declare const DEFAULT_CORS_MAX_AGE = 7200;
130
+ declare const holoCorsDefaults: Readonly<NormalizedHoloCorsConfig>;
131
+ declare const DEFAULT_SECURITY_CSRF_FIELD = "_token";
132
+ declare const DEFAULT_SECURITY_CSRF_HEADER = "X-CSRF-TOKEN";
133
+ declare const DEFAULT_SECURITY_CSRF_COOKIE = "XSRF-TOKEN";
134
+ declare const DEFAULT_SECURITY_RATE_LIMIT_DRIVER: SecurityRateLimitDriver;
135
+ declare const DEFAULT_SECURITY_RATE_LIMIT_FILE_PATH = "./storage/framework/rate-limits";
136
+ declare const DEFAULT_SECURITY_RATE_LIMIT_REDIS_CONNECTION = "default";
137
+ declare const DEFAULT_SECURITY_RATE_LIMIT_REDIS_PREFIX = "holo:rate-limit:";
138
+ declare const holoSecurityDefaults: Readonly<NormalizedHoloSecurityConfig>;
139
+ declare function normalizeCorsConfig(config?: HoloCorsConfig): NormalizedHoloCorsConfig;
140
+ declare function normalizeSecurityConfig(config?: HoloSecurityConfig, redisConfig?: NormalizedHoloRedisConfig): NormalizedHoloSecurityConfig;
141
+ declare function defineSecurityConfig<TConfig extends HoloSecurityConfig>(config: TConfig): Readonly<TConfig>;
142
+ declare function defineCorsConfig<TConfig extends HoloCorsConfig>(config: TConfig): Readonly<TConfig>;
143
+
144
+ export { DEFAULT_SECURITY_CSRF_HEADER as A, DEFAULT_SECURITY_RATE_LIMIT_DRIVER as B, DEFAULT_SECURITY_RATE_LIMIT_FILE_PATH as C, DEFAULT_CORS_HEADERS as D, DEFAULT_SECURITY_RATE_LIMIT_REDIS_CONNECTION as E, DEFAULT_SECURITY_RATE_LIMIT_REDIS_PREFIX as F, defineCorsConfig as p, defineSecurityConfig as q, holoCorsDefaults as r, holoSecurityDefaults as s, normalizeCorsConfig as t, normalizeSecurityConfig as u, DEFAULT_CORS_MAX_AGE as v, DEFAULT_CORS_METHODS as w, DEFAULT_CORS_PATHS as x, DEFAULT_SECURITY_CSRF_COOKIE as y, DEFAULT_SECURITY_CSRF_FIELD as z };
145
+ export type { NormalizedSecurityRateLimitFileConfig as G, HoloSecurityConfig as H, NormalizedSecurityRateLimitMemoryConfig as I, SecurityRateLimitRedisConnectionConfig as J, NormalizedSecurityRateLimitRedisConfig as N, SecurityRateLimitKeyResolver as S, NormalizedHoloCorsConfig as a, NormalizedSecurityLimiterConfig as b, NormalizedHoloSecurityConfig as c, SecurityLimiterConfig as d, HoloCorsConfig as e, HoloSecurityCsrfConfig as f, HoloSecurityRateLimitConfig as g, NormalizedHoloSecurityCsrfConfig as h, NormalizedHoloSecurityRateLimitConfig as i, SecurityRateLimitContext as j, SecurityRateLimitDriver as k, SecurityRateLimitFileConfig as l, SecurityRateLimitMemoryConfig as m, SecurityRateLimitRedisConfig as n, SecurityTrustedProxies as o };
package/dist/config.d.ts CHANGED
@@ -1,136 +1,2 @@
1
- import { NormalizedHoloRedisClusterNodeConfig, NormalizedHoloRedisConfig, HoloRedisClusterNodeConfig } from '@holo-js/kernel';
2
-
3
- interface HoloCorsConfig {
4
- readonly paths?: readonly string[];
5
- readonly origins?: readonly string[];
6
- readonly methods?: readonly string[];
7
- readonly headers?: readonly string[];
8
- readonly credentials?: boolean;
9
- readonly maxAge?: number | string;
10
- readonly statefulDomains?: readonly string[];
11
- }
12
- declare module '@holo-js/config' {
13
- interface HoloConfigRegistry {
14
- cors: NormalizedHoloCorsConfig;
15
- security: NormalizedHoloSecurityConfig;
16
- }
17
- }
18
- interface NormalizedHoloCorsConfig {
19
- readonly paths: readonly string[];
20
- readonly origins: readonly string[];
21
- readonly methods: readonly string[];
22
- readonly headers: readonly string[];
23
- readonly credentials: boolean;
24
- readonly maxAge: number;
25
- readonly statefulDomains: readonly string[];
26
- }
27
- type SecurityRateLimitDriver = 'memory' | 'file' | 'redis';
28
- interface HoloSecurityCsrfConfig {
29
- readonly enabled?: boolean;
30
- readonly field?: string;
31
- readonly header?: string;
32
- readonly cookie?: string;
33
- readonly except?: readonly string[];
34
- }
35
- interface NormalizedHoloSecurityCsrfConfig {
36
- readonly enabled: boolean;
37
- readonly field: string;
38
- readonly header: string;
39
- readonly cookie: string;
40
- readonly except: readonly string[];
41
- }
42
- interface SecurityRateLimitContext<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
43
- readonly request: Request;
44
- readonly values?: TValues;
45
- }
46
- interface SecurityRateLimitKeyResolver<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
47
- (context: SecurityRateLimitContext<TValues>): string | Promise<string>;
48
- }
49
- interface SecurityLimiterConfig<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
50
- readonly maxAttempts: number | string;
51
- readonly decaySeconds: number | string;
52
- readonly key?: SecurityRateLimitKeyResolver<TValues>;
53
- }
54
- interface NormalizedSecurityLimiterConfig<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined> {
55
- readonly name: string;
56
- readonly maxAttempts: number;
57
- readonly decaySeconds: number;
58
- readonly key?: SecurityRateLimitKeyResolver<TValues>;
59
- }
60
- interface SecurityRateLimitMemoryConfig {
61
- readonly driver?: 'memory';
62
- }
63
- interface SecurityRateLimitFileConfig {
64
- readonly path?: string;
65
- }
66
- interface SecurityRateLimitRedisConnectionConfig {
67
- readonly url?: string;
68
- readonly clusters?: readonly HoloRedisClusterNodeConfig[];
69
- readonly host?: string;
70
- readonly port?: number | string;
71
- readonly password?: string;
72
- readonly username?: string;
73
- readonly db?: number | string;
74
- }
75
- interface SecurityRateLimitRedisConfig {
76
- readonly connection?: string;
77
- readonly prefix?: string;
78
- }
79
- interface HoloSecurityRateLimitConfig {
80
- readonly driver?: SecurityRateLimitDriver;
81
- readonly memory?: SecurityRateLimitMemoryConfig;
82
- readonly file?: SecurityRateLimitFileConfig;
83
- readonly redis?: SecurityRateLimitRedisConfig;
84
- readonly limiters?: Readonly<Record<string, SecurityLimiterConfig>>;
85
- }
86
- interface NormalizedSecurityRateLimitMemoryConfig {
87
- readonly driver: 'memory';
88
- }
89
- interface NormalizedSecurityRateLimitFileConfig {
90
- readonly path: string;
91
- }
92
- interface NormalizedSecurityRateLimitRedisConfig {
93
- readonly url?: string;
94
- readonly clusters?: readonly NormalizedHoloRedisClusterNodeConfig[];
95
- readonly host: string;
96
- readonly port: number;
97
- readonly password?: string;
98
- readonly username?: string;
99
- readonly db: number;
100
- readonly connection: string;
101
- readonly prefix: string;
102
- }
103
- interface NormalizedHoloSecurityRateLimitConfig {
104
- readonly driver: SecurityRateLimitDriver;
105
- readonly memory: NormalizedSecurityRateLimitMemoryConfig;
106
- readonly file: NormalizedSecurityRateLimitFileConfig;
107
- readonly redis: NormalizedSecurityRateLimitRedisConfig;
108
- readonly limiters: Readonly<Record<string, NormalizedSecurityLimiterConfig>>;
109
- }
110
- interface HoloSecurityConfig {
111
- readonly csrf?: boolean | HoloSecurityCsrfConfig;
112
- readonly rateLimit?: HoloSecurityRateLimitConfig;
113
- }
114
- interface NormalizedHoloSecurityConfig {
115
- readonly csrf: NormalizedHoloSecurityCsrfConfig;
116
- readonly rateLimit: NormalizedHoloSecurityRateLimitConfig;
117
- }
118
- declare const DEFAULT_CORS_PATHS: readonly ["/api/*"];
119
- declare const DEFAULT_CORS_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
120
- declare const DEFAULT_CORS_HEADERS: readonly ["Content-Type", "Authorization", "X-CSRF-TOKEN", "X-Requested-With"];
121
- declare const DEFAULT_CORS_MAX_AGE = 7200;
122
- declare const holoCorsDefaults: Readonly<NormalizedHoloCorsConfig>;
123
- declare const DEFAULT_SECURITY_CSRF_FIELD = "_token";
124
- declare const DEFAULT_SECURITY_CSRF_HEADER = "X-CSRF-TOKEN";
125
- declare const DEFAULT_SECURITY_CSRF_COOKIE = "XSRF-TOKEN";
126
- declare const DEFAULT_SECURITY_RATE_LIMIT_DRIVER: SecurityRateLimitDriver;
127
- declare const DEFAULT_SECURITY_RATE_LIMIT_FILE_PATH = "./storage/framework/rate-limits";
128
- declare const DEFAULT_SECURITY_RATE_LIMIT_REDIS_CONNECTION = "default";
129
- declare const DEFAULT_SECURITY_RATE_LIMIT_REDIS_PREFIX = "holo:rate-limit:";
130
- declare const holoSecurityDefaults: Readonly<NormalizedHoloSecurityConfig>;
131
- declare function normalizeCorsConfig(config?: HoloCorsConfig): NormalizedHoloCorsConfig;
132
- declare function normalizeSecurityConfig(config?: HoloSecurityConfig, redisConfig?: NormalizedHoloRedisConfig): NormalizedHoloSecurityConfig;
133
- declare function defineSecurityConfig<TConfig extends HoloSecurityConfig>(config: TConfig): Readonly<TConfig>;
134
- declare function defineCorsConfig<TConfig extends HoloCorsConfig>(config: TConfig): Readonly<TConfig>;
135
-
136
- export { DEFAULT_CORS_HEADERS, DEFAULT_CORS_MAX_AGE, DEFAULT_CORS_METHODS, DEFAULT_CORS_PATHS, DEFAULT_SECURITY_CSRF_COOKIE, DEFAULT_SECURITY_CSRF_FIELD, DEFAULT_SECURITY_CSRF_HEADER, DEFAULT_SECURITY_RATE_LIMIT_DRIVER, DEFAULT_SECURITY_RATE_LIMIT_FILE_PATH, DEFAULT_SECURITY_RATE_LIMIT_REDIS_CONNECTION, DEFAULT_SECURITY_RATE_LIMIT_REDIS_PREFIX, type HoloCorsConfig, type HoloSecurityConfig, type HoloSecurityCsrfConfig, type HoloSecurityRateLimitConfig, type NormalizedHoloCorsConfig, type NormalizedHoloSecurityConfig, type NormalizedHoloSecurityCsrfConfig, type NormalizedHoloSecurityRateLimitConfig, type NormalizedSecurityLimiterConfig, type NormalizedSecurityRateLimitFileConfig, type NormalizedSecurityRateLimitMemoryConfig, type NormalizedSecurityRateLimitRedisConfig, type SecurityLimiterConfig, type SecurityRateLimitContext, type SecurityRateLimitDriver, type SecurityRateLimitFileConfig, type SecurityRateLimitKeyResolver, type SecurityRateLimitMemoryConfig, type SecurityRateLimitRedisConfig, type SecurityRateLimitRedisConnectionConfig, defineCorsConfig, defineSecurityConfig, holoCorsDefaults, holoSecurityDefaults, normalizeCorsConfig, normalizeSecurityConfig };
1
+ import '@holo-js/kernel';
2
+ export { D as DEFAULT_CORS_HEADERS, v as DEFAULT_CORS_MAX_AGE, w as DEFAULT_CORS_METHODS, x as DEFAULT_CORS_PATHS, y as DEFAULT_SECURITY_CSRF_COOKIE, z as DEFAULT_SECURITY_CSRF_FIELD, A as DEFAULT_SECURITY_CSRF_HEADER, B as DEFAULT_SECURITY_RATE_LIMIT_DRIVER, C as DEFAULT_SECURITY_RATE_LIMIT_FILE_PATH, E as DEFAULT_SECURITY_RATE_LIMIT_REDIS_CONNECTION, F as DEFAULT_SECURITY_RATE_LIMIT_REDIS_PREFIX, e as HoloCorsConfig, H as HoloSecurityConfig, f as HoloSecurityCsrfConfig, g as HoloSecurityRateLimitConfig, a as NormalizedHoloCorsConfig, c as NormalizedHoloSecurityConfig, h as NormalizedHoloSecurityCsrfConfig, i as NormalizedHoloSecurityRateLimitConfig, b as NormalizedSecurityLimiterConfig, G as NormalizedSecurityRateLimitFileConfig, I as NormalizedSecurityRateLimitMemoryConfig, N as NormalizedSecurityRateLimitRedisConfig, d as SecurityLimiterConfig, j as SecurityRateLimitContext, k as SecurityRateLimitDriver, l as SecurityRateLimitFileConfig, S as SecurityRateLimitKeyResolver, m as SecurityRateLimitMemoryConfig, n as SecurityRateLimitRedisConfig, J as SecurityRateLimitRedisConnectionConfig, p as defineCorsConfig, q as defineSecurityConfig, r as holoCorsDefaults, s as holoSecurityDefaults, t as normalizeCorsConfig, u as normalizeSecurityConfig } from './config-BK6q6Yh9.js';
package/dist/config.mjs CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  holoSecurityDefaults,
17
17
  normalizeCorsConfig,
18
18
  normalizeSecurityConfig
19
- } from "./chunk-WRMZRO77.mjs";
19
+ } from "./chunk-D3ZN5MLJ.mjs";
20
20
  export {
21
21
  DEFAULT_CORS_HEADERS,
22
22
  DEFAULT_CORS_MAX_AGE,
@@ -1,12 +1,20 @@
1
- import { HoloSecurityConfig, NormalizedHoloSecurityConfig, HoloCorsConfig, NormalizedHoloCorsConfig, SecurityRateLimitFileConfig, SecurityRateLimitMemoryConfig, SecurityRateLimitRedisConfig, SecurityLimiterConfig, SecurityRateLimitKeyResolver } from './config.js';
2
- export { HoloSecurityCsrfConfig, HoloSecurityRateLimitConfig, NormalizedHoloSecurityCsrfConfig, NormalizedHoloSecurityRateLimitConfig, NormalizedSecurityLimiterConfig, SecurityRateLimitContext, SecurityRateLimitDriver } from './config.js';
1
+ import { H as HoloSecurityConfig, c as NormalizedHoloSecurityConfig, e as HoloCorsConfig, a as NormalizedHoloCorsConfig, l as SecurityRateLimitFileConfig, m as SecurityRateLimitMemoryConfig, n as SecurityRateLimitRedisConfig, d as SecurityLimiterConfig, S as SecurityRateLimitKeyResolver } from './config-BK6q6Yh9.js';
2
+ export { f as HoloSecurityCsrfConfig, g as HoloSecurityRateLimitConfig, h as NormalizedHoloSecurityCsrfConfig, i as NormalizedHoloSecurityRateLimitConfig, b as NormalizedSecurityLimiterConfig, j as SecurityRateLimitContext, k as SecurityRateLimitDriver } from './config-BK6q6Yh9.js';
3
3
  import '@holo-js/kernel';
4
4
 
5
+ interface SecurityCsrfSessionAccessor {
6
+ read(request: Request): Promise<string | undefined>;
7
+ write(request: Request, token: string): Promise<{
8
+ readonly token: string;
9
+ readonly cookies: readonly string[];
10
+ }>;
11
+ }
5
12
  interface SecurityRuntimeBindings {
6
13
  readonly config: HoloSecurityConfig | NormalizedHoloSecurityConfig;
7
14
  readonly cors?: HoloCorsConfig | NormalizedHoloCorsConfig;
8
15
  readonly rateLimitStore?: SecurityRateLimitStore;
9
16
  readonly csrfSigningKey?: string;
17
+ readonly csrfSession?: SecurityCsrfSessionAccessor;
10
18
  readonly defaultKeyResolver?: SecurityDefaultRateLimitKeyResolver;
11
19
  }
12
20
  interface SecurityRuntimeFacade {
@@ -14,11 +22,13 @@ interface SecurityRuntimeFacade {
14
22
  readonly cors: NormalizedHoloCorsConfig;
15
23
  readonly rateLimitStore?: SecurityRateLimitStore;
16
24
  readonly csrfSigningKey?: string;
25
+ readonly csrfSession?: SecurityCsrfSessionAccessor;
17
26
  readonly defaultKeyResolver?: SecurityDefaultRateLimitKeyResolver;
18
27
  }
19
28
  interface SecurityClientConfig {
20
29
  readonly csrf: {
21
30
  readonly field: string;
31
+ readonly header: string;
22
32
  readonly cookie: string;
23
33
  };
24
34
  }
@@ -125,13 +135,13 @@ declare const limit: Readonly<{
125
135
  perMinute(maxAttempts: number): PendingSecurityLimiterDefinition<Readonly<Record<string, unknown>> | undefined>;
126
136
  perHour(maxAttempts: number): PendingSecurityLimiterDefinition<Readonly<Record<string, unknown>> | undefined>;
127
137
  }>;
128
- declare function ip(request: Request, trustedProxy?: boolean): string;
129
138
  declare function defineRateLimiter<TValues extends Readonly<Record<string, unknown>> | undefined = Readonly<Record<string, unknown>> | undefined>(definition: SecurityLimiterConfig<TValues>): SecurityLimiterConfig<TValues>;
130
139
  declare function defineSecurityRuntimeBindings(bindings: SecurityRuntimeBindings): Readonly<{
131
140
  config: NormalizedHoloSecurityConfig;
132
141
  cors: NormalizedHoloCorsConfig;
133
142
  rateLimitStore?: SecurityRateLimitStore;
134
143
  csrfSigningKey?: string;
144
+ csrfSession?: SecurityCsrfSessionAccessor;
135
145
  defaultKeyResolver?: SecurityDefaultRateLimitKeyResolver;
136
146
  }>;
137
147
  declare function createMemoryRateLimitStoreConfig(config?: SecurityRateLimitMemoryConfig): Readonly<SecurityRateLimitMemoryConfig>;
@@ -142,4 +152,5 @@ declare const securityInternals: {
142
152
  normalizeLimiterAttempts: typeof normalizeLimiterAttempts;
143
153
  };
144
154
 
145
- export { HoloCorsConfig, HoloSecurityConfig, NormalizedHoloCorsConfig, NormalizedHoloSecurityConfig, type SecurityClearRateLimitOptions, type SecurityClientConfig, type SecurityCorsFacade, SecurityCsrfError, type SecurityCsrfFacade, type SecurityCsrfField, type SecurityCsrfInput, type SecurityDefaultRateLimitKeyResolver, SecurityLimiterConfig, type SecurityProtectOptions, type SecurityRateLimitBucketSnapshot, type SecurityRateLimitCallOptions, SecurityRateLimitError, SecurityRateLimitFileConfig, type SecurityRateLimitHitResult, SecurityRateLimitKeyResolver, SecurityRateLimitMemoryConfig, SecurityRateLimitRedisConfig, type SecurityRateLimitRedisDriverAdapter, type SecurityRateLimitStore, type SecurityRateLimitStoreFactoryOptions, type SecurityRuntimeBindings, type SecurityRuntimeFacade, createFileRateLimitStoreConfig, createMemoryRateLimitStoreConfig, createRedisRateLimitStoreConfig, defineRateLimiter, defineSecurityRuntimeBindings, ip, limit, securityInternals };
155
+ export { HoloCorsConfig, HoloSecurityConfig, NormalizedHoloCorsConfig, NormalizedHoloSecurityConfig, SecurityCsrfError, SecurityLimiterConfig, SecurityRateLimitError, SecurityRateLimitFileConfig, SecurityRateLimitKeyResolver, SecurityRateLimitMemoryConfig, SecurityRateLimitRedisConfig, createFileRateLimitStoreConfig, createMemoryRateLimitStoreConfig, createRedisRateLimitStoreConfig, defineRateLimiter, defineSecurityRuntimeBindings, limit, securityInternals };
156
+ export type { SecurityClearRateLimitOptions, SecurityClientConfig, SecurityCorsFacade, SecurityCsrfFacade, SecurityCsrfField, SecurityCsrfInput, SecurityCsrfSessionAccessor, SecurityDefaultRateLimitKeyResolver, SecurityProtectOptions, SecurityRateLimitBucketSnapshot, SecurityRateLimitCallOptions, SecurityRateLimitHitResult, SecurityRateLimitRedisDriverAdapter, SecurityRateLimitStore, SecurityRateLimitStoreFactoryOptions, SecurityRuntimeBindings, SecurityRuntimeFacade };
@@ -6,11 +6,10 @@ import {
6
6
  createRedisRateLimitStoreConfig,
7
7
  defineRateLimiter,
8
8
  defineSecurityRuntimeBindings,
9
- ip,
10
9
  limit,
11
10
  securityInternals
12
- } from "./chunk-FQ4X7X6L.mjs";
13
- import "./chunk-WRMZRO77.mjs";
11
+ } from "./chunk-U25HSHLJ.mjs";
12
+ import "./chunk-D3ZN5MLJ.mjs";
14
13
  export {
15
14
  SecurityCsrfError,
16
15
  SecurityRateLimitError,
@@ -19,7 +18,6 @@ export {
19
18
  createRedisRateLimitStoreConfig,
20
19
  defineRateLimiter,
21
20
  defineSecurityRuntimeBindings,
22
- ip,
23
21
  limit,
24
22
  securityInternals
25
23
  };
@@ -2,9 +2,6 @@ import { NormalizedSecurityRateLimitRedisConfig } from '../config.js';
2
2
  import { SecurityRateLimitRedisDriverAdapter } from '../contracts.js';
3
3
  import '@holo-js/kernel';
4
4
 
5
- interface SecurityRedisAdapterOptions {
6
- readonly now?: () => Date;
7
- }
8
5
  type RedisClientOptions = {
9
6
  readonly host?: string;
10
7
  readonly port?: number;
@@ -35,16 +32,14 @@ declare function resolveClusterStartupNodes(config: NormalizedSecurityRateLimitR
35
32
  declare function createRedisClusterOptions(config: NormalizedSecurityRateLimitRedisConfig): RedisClusterOptions;
36
33
  declare class RedisSecurityAdapter implements SecurityRateLimitRedisDriverAdapter {
37
34
  private readonly client;
38
- private readonly now;
39
35
  private readonly prefix;
40
- constructor(config: NormalizedSecurityRateLimitRedisConfig, options?: SecurityRedisAdapterOptions);
36
+ constructor(config: NormalizedSecurityRateLimitRedisConfig);
41
37
  private qualifyKey;
42
38
  private qualifyPattern;
43
39
  private normalizeScanResponse;
44
40
  private clearMatchingKeysForClient;
45
41
  private clearMatchingKeysForCluster;
46
42
  private clearMatchingKeys;
47
- private parseOldestScore;
48
43
  private getCommandValue;
49
44
  connect(): Promise<void>;
50
45
  increment(key: string, options: {
@@ -58,7 +53,7 @@ declare class RedisSecurityAdapter implements SecurityRateLimitRedisDriverAdapte
58
53
  clearAll(): Promise<number>;
59
54
  close(): Promise<void>;
60
55
  }
61
- declare function createSecurityRedisAdapter(config: NormalizedSecurityRateLimitRedisConfig, options?: SecurityRedisAdapterOptions): RedisSecurityAdapter;
56
+ declare function createSecurityRedisAdapter(config: NormalizedSecurityRateLimitRedisConfig): RedisSecurityAdapter;
62
57
  declare const securityRedisAdapterInternals: {
63
58
  REDIS_SCAN_COUNT: number;
64
59
  RedisSecurityAdapter: typeof RedisSecurityAdapter;
@@ -72,4 +67,4 @@ declare const securityRedisAdapterInternals: {
72
67
  toRedisSocketPath: typeof toRedisSocketPath;
73
68
  };
74
69
 
75
- export { RedisSecurityAdapter, type SecurityRedisAdapterOptions, createSecurityRedisAdapter, securityRedisAdapterInternals };
70
+ export { RedisSecurityAdapter, createSecurityRedisAdapter, securityRedisAdapterInternals };
@@ -1,5 +1,4 @@
1
1
  // src/drivers/redis-adapter.ts
2
- import { randomUUID } from "crypto";
3
2
  import Redis from "ioredis";
4
3
  var REDIS_SCAN_COUNT = 100;
5
4
  function isRedisClusterClientLike(client) {
@@ -46,7 +45,7 @@ function parseClusterNodeUrl(url, label) {
46
45
  ...parsed.protocol === "rediss:" ? { tls: {} } : {}
47
46
  };
48
47
  } catch (error) {
49
- throw new Error(`[@holo-js/security] ${label} is invalid: ${error instanceof Error ? error.message : String(error)}`);
48
+ throw new Error(`[@holo-js/security] ${label} is invalid: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
50
49
  }
51
50
  }
52
51
  function resolveClusterStartupNodes(config) {
@@ -82,11 +81,9 @@ function createRedisClusterOptions(config) {
82
81
  }
83
82
  var RedisSecurityAdapter = class {
84
83
  client;
85
- now;
86
84
  prefix;
87
- constructor(config, options = {}) {
85
+ constructor(config) {
88
86
  this.prefix = config.prefix;
89
- this.now = options.now ?? (() => /* @__PURE__ */ new Date());
90
87
  const clientOptions = createRedisClientOptions(config);
91
88
  const RedisConstructor = Redis;
92
89
  this.client = typeof config.url === "string" ? new RedisConstructor(config.url, clientOptions) : config.clusters && config.clusters.length > 0 ? new RedisConstructor.Cluster(resolveClusterStartupNodes(config), createRedisClusterOptions(config)) : isRedisConnectionTarget(config.connection) ? new RedisConstructor(config.connection, clientOptions) : new RedisConstructor(clientOptions);
@@ -154,17 +151,6 @@ var RedisSecurityAdapter = class {
154
151
  }
155
152
  return await this.clearMatchingKeysForClient(this.client, pattern);
156
153
  }
157
- parseOldestScore(result) {
158
- if (!Array.isArray(result) || result.length < 2) {
159
- throw new Error("[@holo-js/security] Redis transaction failed to return the oldest rate-limit hit.");
160
- }
161
- const value = result[1];
162
- const score = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
163
- if (!Number.isFinite(score)) {
164
- throw new Error("[@holo-js/security] Redis transaction returned an invalid oldest-hit score.");
165
- }
166
- return score;
167
- }
168
154
  getCommandValue(result, index, operation) {
169
155
  const entry = result[index];
170
156
  if (!entry) {
@@ -183,23 +169,21 @@ var RedisSecurityAdapter = class {
183
169
  await this.client.connect();
184
170
  }
185
171
  async increment(key, options) {
186
- const now = this.now().getTime();
187
- const ttlMs = options.decaySeconds * 1e3;
188
172
  const qualifiedKey = this.qualifyKey(key);
189
- const member = `${now}:${randomUUID()}`;
190
- const result = await this.client.multi().zadd(qualifiedKey, now, member).zremrangebyscore(qualifiedKey, "-inf", now - ttlMs).zcard(qualifiedKey).zrange(qualifiedKey, 0, 0, "WITHSCORES").exec();
173
+ const result = await this.client.multi().set(qualifiedKey, 0, "PX", options.decaySeconds * 1e3, "NX").incr(qualifiedKey).pttl(qualifiedKey).exec();
191
174
  if (!result) {
192
175
  throw new Error("[@holo-js/security] Redis transaction failed for increment.");
193
176
  }
194
- const attemptsValue = this.getCommandValue(result, 2, "attempt count");
195
- if (typeof attemptsValue !== "number") {
177
+ const replies = result;
178
+ const attempts = this.getCommandValue(replies, 1, "attempt count");
179
+ if (typeof attempts !== "number") {
196
180
  throw new Error("[@holo-js/security] Redis transaction returned an invalid attempt count.");
197
181
  }
198
- const attempts = attemptsValue;
199
- const oldestScore = this.parseOldestScore(this.getCommandValue(result, 3, "oldest rate-limit hit"));
200
- await this.client.pexpireat(qualifiedKey, now + ttlMs);
201
- const ttlSeconds = Math.max(0, Math.ceil((oldestScore + ttlMs - now) / 1e3));
202
- return { attempts, ttlSeconds };
182
+ const remainingMs = this.getCommandValue(replies, 2, "window time to live");
183
+ if (typeof remainingMs !== "number" || remainingMs < 0) {
184
+ throw new Error("[@holo-js/security] Redis transaction returned an invalid window time to live.");
185
+ }
186
+ return { attempts, ttlSeconds: Math.ceil(remainingMs / 1e3) };
203
187
  }
204
188
  async del(key) {
205
189
  return this.client.del(this.qualifyKey(key));
@@ -220,8 +204,8 @@ var RedisSecurityAdapter = class {
220
204
  }
221
205
  }
222
206
  };
223
- function createSecurityRedisAdapter(config, options) {
224
- return new RedisSecurityAdapter(config, options);
207
+ function createSecurityRedisAdapter(config) {
208
+ return new RedisSecurityAdapter(config);
225
209
  }
226
210
  var securityRedisAdapterInternals = {
227
211
  REDIS_SCAN_COUNT,