@fuzdev/fuz_app 0.107.0 → 0.108.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/dist/auth/account_route_schema.d.ts +10 -1
- package/dist/auth/account_route_schema.d.ts.map +1 -1
- package/dist/auth/account_route_schema.js +10 -1
- package/dist/auth/account_routes.d.ts +18 -4
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +32 -29
- package/dist/auth/api_token_queries.d.ts +4 -0
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +5 -1
- package/dist/auth/bearer_auth.d.ts +9 -6
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +12 -21
- package/dist/auth/bootstrap_routes.d.ts +12 -2
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +15 -9
- package/dist/auth/middleware.d.ts +0 -3
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +11 -10
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +24 -0
- package/dist/auth/session_queries.d.ts +13 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +14 -3
- package/dist/auth/signup_routes.d.ts +10 -0
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +14 -11
- package/dist/http/error_schemas.d.ts +1 -1
- package/dist/http/error_schemas.js +1 -1
- package/dist/rate_limiter.d.ts +36 -3
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +36 -3
- package/dist/server/app_server.d.ts +27 -10
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +30 -20
- package/dist/server/app_server_context.d.ts +10 -2
- package/dist/server/app_server_context.d.ts.map +1 -1
- package/dist/testing/CLAUDE.md +21 -8
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +6 -4
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +9 -8
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
- package/dist/testing/cross_backend/login_security.js +59 -0
- package/dist/testing/cross_backend/session_cap.d.ts +19 -0
- package/dist/testing/cross_backend/session_cap.d.ts.map +1 -0
- package/dist/testing/cross_backend/session_cap.js +93 -0
- package/dist/testing/middleware.d.ts +2 -5
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +5 -5
- package/dist/testing/rate_limiting.d.ts +10 -16
- package/dist/testing/rate_limiting.d.ts.map +1 -1
- package/dist/testing/rate_limiting.js +11 -95
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +3 -1
- package/dist/testing/testing_rate_limiter.d.ts +7 -1
- package/dist/testing/testing_rate_limiter.d.ts.map +1 -1
- package/dist/testing/testing_rate_limiter.js +7 -1
- package/package.json +1 -1
|
@@ -143,8 +143,19 @@ export const query_session_list_for_account = async (deps, account_id, limit = 5
|
|
|
143
143
|
* both commit above `max_sessions`. A transaction is necessary here but not
|
|
144
144
|
* sufficient. Closing this needs one serialization point per
|
|
145
145
|
* `(account_id, credential_kind)` — a locked parent row or a transaction-scoped
|
|
146
|
-
* advisory lock — taken before count/evict/insert
|
|
147
|
-
*
|
|
146
|
+
* advisory lock — taken before count/evict/insert.
|
|
147
|
+
*
|
|
148
|
+
* Ordering is `created_at DESC, id DESC`. The `id` leg is a **stability**
|
|
149
|
+
* tie-breaker, not a recency one: `created_at` defaults to `NOW()`, the
|
|
150
|
+
* *transaction* timestamp, so two sessions born in one transaction — or in two
|
|
151
|
+
* transactions that started in the same microsecond — tie, and an untied
|
|
152
|
+
* `OFFSET` may keep a different set on two evaluations of the same rows. `id` is
|
|
153
|
+
* the blake3 token hash, so the tie-break is arbitrary but deterministic; it
|
|
154
|
+
* makes the survivors reproducible, it does not make the row just inserted a
|
|
155
|
+
* guaranteed survivor. Only the serialization point above does that.
|
|
156
|
+
*
|
|
157
|
+
* Expired-but-unreaped rows count toward the cap (the predicate is `account_id`
|
|
158
|
+
* alone). Matches the Rust twin `query_session_enforce_limit`.
|
|
148
159
|
*
|
|
149
160
|
* @param deps - query dependencies (must be transaction-scoped)
|
|
150
161
|
* @param account_id - the account to enforce the limit for
|
|
@@ -157,7 +168,7 @@ export const query_session_enforce_limit = async (deps, account_id, max_sessions
|
|
|
157
168
|
WHERE id IN (
|
|
158
169
|
SELECT id FROM auth_session
|
|
159
170
|
WHERE account_id = $1
|
|
160
|
-
ORDER BY created_at DESC
|
|
171
|
+
ORDER BY created_at DESC, id DESC
|
|
161
172
|
OFFSET $2
|
|
162
173
|
) RETURNING id`, [account_id, max_sessions]);
|
|
163
174
|
return rows.length;
|
|
@@ -36,6 +36,16 @@ export declare const DEFAULT_SIGNUP_FAIL_JITTER_MS = 25;
|
|
|
36
36
|
* Per-factory configuration for signup route specs.
|
|
37
37
|
*/
|
|
38
38
|
export interface SignupRouteOptions extends AuthSessionRouteOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Rate limiter for signup attempts, keyed by client IP. Pass `null` to
|
|
41
|
+
* disable. Its own instance, not login's: signup failures bound username
|
|
42
|
+
* *enumeration* (the 403/409 split), not credential guessing, and an
|
|
43
|
+
* open-signup deployment lets any unauthenticated caller spend this budget
|
|
44
|
+
* — which must not cost anyone their login attempts. Never refunded on
|
|
45
|
+
* success (see `RateLimiter.reset`). Mirrors the Rust spine's
|
|
46
|
+
* `signup_ip_rate_limiter`.
|
|
47
|
+
*/
|
|
48
|
+
signup_ip_rate_limiter: RateLimiter | null;
|
|
39
49
|
/** Rate limiter for signup attempts, keyed by submitted username. Pass `null` to disable. */
|
|
40
50
|
signup_account_rate_limiter: RateLimiter | null;
|
|
41
51
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGlD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEnE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAQhD;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAClE,6FAA6F;IAC7F,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAID;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,gBAAgB,EACtB,SAAS,kBAAkB,KACzB,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"signup_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGlD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEnE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAQhD;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAClE;;;;;;;;OAQG;IACH,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C,6FAA6F;IAC7F,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAID;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,gBAAgB,EACtB,SAAS,kBAAkB,KACzB,KAAK,CAAC,SAAS,CAgLjB,CAAC"}
|
|
@@ -55,7 +55,7 @@ const signup_fail_delay = (floor_ms, jitter_ms) => {
|
|
|
55
55
|
*/
|
|
56
56
|
export const create_signup_route_specs = (deps, options) => {
|
|
57
57
|
const { keyring, password } = deps;
|
|
58
|
-
const { session_options,
|
|
58
|
+
const { session_options, signup_ip_rate_limiter, signup_account_rate_limiter, signup_fail_floor_ms = DEFAULT_SIGNUP_FAIL_FLOOR_MS, signup_fail_jitter_ms = DEFAULT_SIGNUP_FAIL_JITTER_MS } = options;
|
|
59
59
|
return [
|
|
60
60
|
{
|
|
61
61
|
...create_signup_route_shape({
|
|
@@ -63,9 +63,9 @@ export const create_signup_route_specs = (deps, options) => {
|
|
|
63
63
|
}),
|
|
64
64
|
handler: async (c, route) => {
|
|
65
65
|
// Per-IP rate limit check (before any work). 429 stays fast.
|
|
66
|
-
const ip =
|
|
67
|
-
if (
|
|
68
|
-
const check =
|
|
66
|
+
const ip = signup_ip_rate_limiter ? get_client_ip(c) : null;
|
|
67
|
+
if (signup_ip_rate_limiter && ip) {
|
|
68
|
+
const check = signup_ip_rate_limiter.check(ip);
|
|
69
69
|
if (!check.allowed) {
|
|
70
70
|
return rate_limit_exceeded_response(c, check.retry_after);
|
|
71
71
|
}
|
|
@@ -157,8 +157,8 @@ export const create_signup_route_specs = (deps, options) => {
|
|
|
157
157
|
}
|
|
158
158
|
catch (e) {
|
|
159
159
|
if (e instanceof NoMatchingInviteError) {
|
|
160
|
-
if (
|
|
161
|
-
|
|
160
|
+
if (signup_ip_rate_limiter && ip)
|
|
161
|
+
signup_ip_rate_limiter.record(ip);
|
|
162
162
|
if (signup_account_rate_limiter)
|
|
163
163
|
signup_account_rate_limiter.record(account_key);
|
|
164
164
|
emit_failure_audit('no_match');
|
|
@@ -167,8 +167,8 @@ export const create_signup_route_specs = (deps, options) => {
|
|
|
167
167
|
}
|
|
168
168
|
// Unique constraint violation: username or email already exists.
|
|
169
169
|
if (is_pg_unique_violation(e)) {
|
|
170
|
-
if (
|
|
171
|
-
|
|
170
|
+
if (signup_ip_rate_limiter && ip)
|
|
171
|
+
signup_ip_rate_limiter.record(ip);
|
|
172
172
|
if (signup_account_rate_limiter)
|
|
173
173
|
signup_account_rate_limiter.record(account_key);
|
|
174
174
|
emit_failure_audit('signup_conflict');
|
|
@@ -185,9 +185,12 @@ export const create_signup_route_specs = (deps, options) => {
|
|
|
185
185
|
emit_failure_audit('internal_error');
|
|
186
186
|
throw e;
|
|
187
187
|
}
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
// Clear the account-grain bucket only — the IP aggregate stands.
|
|
189
|
+
// This is the cheapest version of the spray path `RateLimiter.reset`
|
|
190
|
+
// describes: where login at least costs the attacker a credential,
|
|
191
|
+
// an open-signup deployment let an *unauthenticated* caller zero a
|
|
192
|
+
// shared IP budget by creating throwaway accounts, and the
|
|
193
|
+
// per-account limiter never applies to the attacker's own signups.
|
|
191
194
|
if (signup_account_rate_limiter)
|
|
192
195
|
signup_account_rate_limiter.reset(account_key);
|
|
193
196
|
deps.audit.emit(route, {
|
|
@@ -272,7 +272,7 @@ export type RouteErrorSchemas = Partial<Record<number, z.ZodType>>;
|
|
|
272
272
|
* Rate limit key type — declares what a route or RPC action's rate limiter
|
|
273
273
|
* is keyed on.
|
|
274
274
|
*
|
|
275
|
-
* - `'ip'` — per-IP rate limiting (bootstrap, password change,
|
|
275
|
+
* - `'ip'` — per-IP rate limiting (login, bootstrap, password change, signup)
|
|
276
276
|
* - `'account'` — per-account rate limiting. On REST auth routes the key is
|
|
277
277
|
* the submitted identifier (login). On RPC actions (post-auth) the key is
|
|
278
278
|
* the resolved actor id (`request_context.actor.id`) — separate namespace.
|
|
@@ -261,7 +261,7 @@ export const AccountVanishedError = z.looseObject({
|
|
|
261
261
|
* Rate limit key type — declares what a route or RPC action's rate limiter
|
|
262
262
|
* is keyed on.
|
|
263
263
|
*
|
|
264
|
-
* - `'ip'` — per-IP rate limiting (bootstrap, password change,
|
|
264
|
+
* - `'ip'` — per-IP rate limiting (login, bootstrap, password change, signup)
|
|
265
265
|
* - `'account'` — per-account rate limiting. On REST auth routes the key is
|
|
266
266
|
* the submitted identifier (login). On RPC actions (post-auth) the key is
|
|
267
267
|
* the resolved actor id (`request_context.actor.id`) — separate namespace.
|
package/dist/rate_limiter.d.ts
CHANGED
|
@@ -42,7 +42,18 @@ export interface RateLimiterOptions {
|
|
|
42
42
|
*/
|
|
43
43
|
max_keys?: number | null;
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Default options for per-IP auth rate limiting: 5 attempts per 15 minutes.
|
|
47
|
+
* The default for each of the three per-surface IP limiters (login + password
|
|
48
|
+
* change, signup, bootstrap) and the shared cap the Rust spine's
|
|
49
|
+
* `DEFAULT_LOGIN_IP_RATE_LIMIT` pins.
|
|
50
|
+
*
|
|
51
|
+
* Deliberately **not** widened when the buckets became monotone (a success no
|
|
52
|
+
* longer refunds them — see `RateLimiter.reset`). Widening would have bought
|
|
53
|
+
* NAT'd egress headroom by loosening the one bound that caps credential
|
|
54
|
+
* guessing from a single address; splitting one shared bucket into three
|
|
55
|
+
* per-surface ones buys the same headroom without touching that bound.
|
|
56
|
+
*/
|
|
46
57
|
export declare const default_login_ip_rate_limit: RateLimiterOptions;
|
|
47
58
|
/** Default options for per-account login rate limiting: 10 attempts per 30 minutes. */
|
|
48
59
|
export declare const default_login_account_rate_limit: RateLimiterOptions;
|
|
@@ -86,7 +97,7 @@ export interface RateLimitResult {
|
|
|
86
97
|
* key-enumeration attack at the cost of a slight per-op overhead and the
|
|
87
98
|
* LRU trade-off described on `RateLimiterOptions.max_keys`.
|
|
88
99
|
*
|
|
89
|
-
* Parameters that accept `RateLimiter | null` (e.g. `
|
|
100
|
+
* Parameters that accept `RateLimiter | null` (e.g. `login_ip_rate_limiter`,
|
|
90
101
|
* `login_account_rate_limiter`) silently disable rate limiting when `null`
|
|
91
102
|
* is passed — no checks are performed and all requests are allowed through.
|
|
92
103
|
*/
|
|
@@ -117,7 +128,29 @@ export declare class RateLimiter {
|
|
|
117
128
|
*/
|
|
118
129
|
record(key: string, now?: number): RateLimitResult;
|
|
119
130
|
/**
|
|
120
|
-
* Clear all attempts for `key
|
|
131
|
+
* Clear all attempts for `key`.
|
|
132
|
+
*
|
|
133
|
+
* **Account-grain keys only.** Called after a successful auth to forgive a
|
|
134
|
+
* user's own typos, which is safe because the key *is* the account being
|
|
135
|
+
* attacked — clearing it requires that account's credential and can only
|
|
136
|
+
* widen the budget against an account the caller already holds.
|
|
137
|
+
*
|
|
138
|
+
* Never call this on an **IP-aggregate** key. That bucket is the
|
|
139
|
+
* distributed-spray backstop, and success on one account must not zero the
|
|
140
|
+
* budget shared by every other account reachable from the same IP;
|
|
141
|
+
* otherwise an attacker interleaves their own logins (or, under open signup,
|
|
142
|
+
* throwaway account creations needing no credential at all) with guesses
|
|
143
|
+
* against arbitrary victims and sprays indefinitely from one address.
|
|
144
|
+
* Nothing records on the IP bucket on success on any path, so with no reset
|
|
145
|
+
* it is a pure monotone-within-window failure counter that only time clears.
|
|
146
|
+
*
|
|
147
|
+
* Because they are monotone, the IP buckets are **one instance per auth
|
|
148
|
+
* surface** (`login_ip_rate_limiter`, `signup_ip_rate_limiter`,
|
|
149
|
+
* `bootstrap_ip_rate_limiter`) rather than one shared across all of them.
|
|
150
|
+
* A single instance meant a failure on any surface spent the budget that
|
|
151
|
+
* bounds guessing on every other one, and one caller's exhaustion denied
|
|
152
|
+
* four routes at once — a cost that used to be masked by the reset this
|
|
153
|
+
* doc forbids.
|
|
121
154
|
*
|
|
122
155
|
* @mutates internal map - removes the entry for `key`
|
|
123
156
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate_limiter.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/rate_limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAKpC;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,SAAU,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED
|
|
1
|
+
{"version":3,"file":"rate_limiter.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/rate_limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAKpC;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,SAAU,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,EAAE,kBAKzC,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,gCAAgC,EAAE,kBAK9C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,EAAE,kBAK1C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,EAAE,kBAK/C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,WAAW;;IACvB,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;gBAOzB,OAAO,EAAE,kBAAkB;IAcvC,8BAA8B;IAC9B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAcD;;;;;;;;;;OAUG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,eAAe;IA2B7D;;;;;;OAMG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,eAAe;IA0B9D;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIxB;;;;;OAKG;IACH,OAAO,CAAC,GAAG,GAAE,MAAmB,GAAG,IAAI;IAgBvC;;;;OAIG;IACH,OAAO,IAAI,IAAI;CAMf;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,UAAU,OAAO,CAAC,kBAAkB,CAAC,KAAG,WAE3E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GAAI,GAAG,OAAO,EAAE,aAAa,MAAM,KAAG,QAI7E,CAAC"}
|
package/dist/rate_limiter.js
CHANGED
|
@@ -16,7 +16,18 @@ import { ERROR_RATE_LIMIT_EXCEEDED } from './http/error_schemas.js';
|
|
|
16
16
|
* MB in the worst case.
|
|
17
17
|
*/
|
|
18
18
|
export const DEFAULT_RATE_LIMITER_MAX_KEYS = 100_000;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Default options for per-IP auth rate limiting: 5 attempts per 15 minutes.
|
|
21
|
+
* The default for each of the three per-surface IP limiters (login + password
|
|
22
|
+
* change, signup, bootstrap) and the shared cap the Rust spine's
|
|
23
|
+
* `DEFAULT_LOGIN_IP_RATE_LIMIT` pins.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately **not** widened when the buckets became monotone (a success no
|
|
26
|
+
* longer refunds them — see `RateLimiter.reset`). Widening would have bought
|
|
27
|
+
* NAT'd egress headroom by loosening the one bound that caps credential
|
|
28
|
+
* guessing from a single address; splitting one shared bucket into three
|
|
29
|
+
* per-surface ones buys the same headroom without touching that bound.
|
|
30
|
+
*/
|
|
20
31
|
export const default_login_ip_rate_limit = {
|
|
21
32
|
max_attempts: 5,
|
|
22
33
|
window_ms: 15 * 60_000,
|
|
@@ -69,7 +80,7 @@ export const default_action_account_rate_limit = {
|
|
|
69
80
|
* key-enumeration attack at the cost of a slight per-op overhead and the
|
|
70
81
|
* LRU trade-off described on `RateLimiterOptions.max_keys`.
|
|
71
82
|
*
|
|
72
|
-
* Parameters that accept `RateLimiter | null` (e.g. `
|
|
83
|
+
* Parameters that accept `RateLimiter | null` (e.g. `login_ip_rate_limiter`,
|
|
73
84
|
* `login_account_rate_limiter`) silently disable rate limiting when `null`
|
|
74
85
|
* is passed — no checks are performed and all requests are allowed through.
|
|
75
86
|
*/
|
|
@@ -170,7 +181,29 @@ export class RateLimiter {
|
|
|
170
181
|
return { allowed: false, remaining: 0, retry_after };
|
|
171
182
|
}
|
|
172
183
|
/**
|
|
173
|
-
* Clear all attempts for `key
|
|
184
|
+
* Clear all attempts for `key`.
|
|
185
|
+
*
|
|
186
|
+
* **Account-grain keys only.** Called after a successful auth to forgive a
|
|
187
|
+
* user's own typos, which is safe because the key *is* the account being
|
|
188
|
+
* attacked — clearing it requires that account's credential and can only
|
|
189
|
+
* widen the budget against an account the caller already holds.
|
|
190
|
+
*
|
|
191
|
+
* Never call this on an **IP-aggregate** key. That bucket is the
|
|
192
|
+
* distributed-spray backstop, and success on one account must not zero the
|
|
193
|
+
* budget shared by every other account reachable from the same IP;
|
|
194
|
+
* otherwise an attacker interleaves their own logins (or, under open signup,
|
|
195
|
+
* throwaway account creations needing no credential at all) with guesses
|
|
196
|
+
* against arbitrary victims and sprays indefinitely from one address.
|
|
197
|
+
* Nothing records on the IP bucket on success on any path, so with no reset
|
|
198
|
+
* it is a pure monotone-within-window failure counter that only time clears.
|
|
199
|
+
*
|
|
200
|
+
* Because they are monotone, the IP buckets are **one instance per auth
|
|
201
|
+
* surface** (`login_ip_rate_limiter`, `signup_ip_rate_limiter`,
|
|
202
|
+
* `bootstrap_ip_rate_limiter`) rather than one shared across all of them.
|
|
203
|
+
* A single instance meant a failure on any surface spent the budget that
|
|
204
|
+
* bounds guessing on every other one, and one caller's exhaustion denied
|
|
205
|
+
* four routes at once — a cost that used to be masked by the reset this
|
|
206
|
+
* doc forbids.
|
|
174
207
|
*
|
|
175
208
|
* @mutates internal map - removes the entry for `key`
|
|
176
209
|
*/
|
|
@@ -95,12 +95,35 @@ export interface AppServerOptions {
|
|
|
95
95
|
get_connection_ip: (c: Context) => string | undefined;
|
|
96
96
|
};
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
* Omit or `undefined` to use a default limiter (5 attempts per
|
|
100
|
-
* Pass `null` to explicitly disable rate limiting.
|
|
98
|
+
* Per-IP rate limiter for login + password change — the distributed-spray
|
|
99
|
+
* backstop. Omit or `undefined` to use a default limiter (5 attempts per
|
|
100
|
+
* 15 minutes). Pass `null` to explicitly disable rate limiting.
|
|
101
101
|
* Also available on `AppServerContext` for route factory callbacks.
|
|
102
|
+
*
|
|
103
|
+
* One instance per auth surface, not one shared across all four. These
|
|
104
|
+
* buckets are monotone within their window — a success never refunds them
|
|
105
|
+
* (see `RateLimiter.reset`) — so a shared instance let a failure on any
|
|
106
|
+
* surface spend the budget that bounds guessing on every other one, and
|
|
107
|
+
* let one caller's exhaustion deny four routes at once. Pass the same
|
|
108
|
+
* limiter to two of these fields to opt back into a shared budget.
|
|
102
109
|
*/
|
|
103
|
-
|
|
110
|
+
login_ip_rate_limiter?: RateLimiter | null;
|
|
111
|
+
/**
|
|
112
|
+
* Per-IP rate limiter for signup. Omit or `undefined` to use a default
|
|
113
|
+
* limiter (5 attempts per 15 minutes). Pass `null` to explicitly disable.
|
|
114
|
+
* Separate from `login_ip_rate_limiter` because an open-signup deployment
|
|
115
|
+
* lets any unauthenticated caller spend it. Also available on
|
|
116
|
+
* `AppServerContext` for route factory callbacks.
|
|
117
|
+
*/
|
|
118
|
+
signup_ip_rate_limiter?: RateLimiter | null;
|
|
119
|
+
/**
|
|
120
|
+
* Per-IP rate limiter for bootstrap. Omit or `undefined` to use a default
|
|
121
|
+
* limiter (5 attempts per 15 minutes). Pass `null` to explicitly disable.
|
|
122
|
+
* Separate from `login_ip_rate_limiter` so a fumbled bootstrap token can't
|
|
123
|
+
* spend the operator's login budget. Wired directly into the factory-managed
|
|
124
|
+
* bootstrap route; also on `AppServerContext` for symmetry.
|
|
125
|
+
*/
|
|
126
|
+
bootstrap_ip_rate_limiter?: RateLimiter | null;
|
|
104
127
|
/**
|
|
105
128
|
* Per-account rate limiter for login attempts.
|
|
106
129
|
* Omit or `undefined` to use a default limiter (10 attempts per 30 minutes).
|
|
@@ -115,12 +138,6 @@ export interface AppServerOptions {
|
|
|
115
138
|
* Also available on `AppServerContext` for route factory callbacks.
|
|
116
139
|
*/
|
|
117
140
|
signup_account_rate_limiter?: RateLimiter | null;
|
|
118
|
-
/**
|
|
119
|
-
* Rate limiter for bearer token auth attempts (per-IP).
|
|
120
|
-
* Omit or `undefined` to use a default limiter (5 attempts per 15 minutes).
|
|
121
|
-
* Pass `null` to explicitly disable rate limiting.
|
|
122
|
-
*/
|
|
123
|
-
bearer_ip_rate_limiter?: RateLimiter | null;
|
|
124
141
|
/**
|
|
125
142
|
* Per-IP rate limiter for the action dispatchers (HTTP RPC + WebSocket).
|
|
126
143
|
* Consulted for actions whose spec declares `rate_limit: 'ip'` or `'both'`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAEN,KAAK,cAAc,EAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAGN,KAAK,WAAW,EAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAKN,KAAK,WAAW,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEhF,OAAO,EAEN,KAAK,cAAc,EAEnB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAGN,KAAK,eAAe,EACpB,MAAM,6BAA6B,CAAC;AASrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAKrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,sBAAsB,GACjC,wBAAwB,GAAG,2BAA2B,GAAG,oBAAoB,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC3C,IAAI,EAAE,cAAc,CAAC;IACrB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAChC,2DAA2D;IAC3D,OAAO,EAAE,UAAU,CAAC;IACpB,6CAA6C;IAC7C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,sCAAsC;IACtC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAE/B,6BAA6B;IAC7B,KAAK,EAAE;QACN,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;KACtD,CAAC;IAEF
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -81,16 +81,23 @@ export const create_app_server = async (options) => {
|
|
|
81
81
|
const { deps } = backend;
|
|
82
82
|
const { log } = deps;
|
|
83
83
|
// Rate limiter defaults (undefined = default, null = disable)
|
|
84
|
-
|
|
84
|
+
// One instance per surface — see `AppServerOptions.login_ip_rate_limiter`
|
|
85
|
+
// for why these are not a single shared bucket.
|
|
86
|
+
const login_ip_rate_limiter = options.login_ip_rate_limiter === undefined
|
|
87
|
+
? create_rate_limiter()
|
|
88
|
+
: options.login_ip_rate_limiter;
|
|
89
|
+
const signup_ip_rate_limiter = options.signup_ip_rate_limiter === undefined
|
|
90
|
+
? create_rate_limiter()
|
|
91
|
+
: options.signup_ip_rate_limiter;
|
|
92
|
+
const bootstrap_ip_rate_limiter = options.bootstrap_ip_rate_limiter === undefined
|
|
93
|
+
? create_rate_limiter()
|
|
94
|
+
: options.bootstrap_ip_rate_limiter;
|
|
85
95
|
const login_account_rate_limiter = options.login_account_rate_limiter === undefined
|
|
86
96
|
? create_rate_limiter(default_login_account_rate_limit)
|
|
87
97
|
: options.login_account_rate_limiter;
|
|
88
98
|
const signup_account_rate_limiter = options.signup_account_rate_limiter === undefined
|
|
89
99
|
? create_rate_limiter(default_login_account_rate_limit)
|
|
90
100
|
: options.signup_account_rate_limiter;
|
|
91
|
-
const bearer_ip_rate_limiter = options.bearer_ip_rate_limiter === undefined
|
|
92
|
-
? create_rate_limiter()
|
|
93
|
-
: options.bearer_ip_rate_limiter;
|
|
94
101
|
const action_ip_rate_limiter = options.action_ip_rate_limiter === undefined
|
|
95
102
|
? create_rate_limiter(default_action_ip_rate_limit)
|
|
96
103
|
: options.action_ip_rate_limiter;
|
|
@@ -115,7 +122,6 @@ export const create_app_server = async (options) => {
|
|
|
115
122
|
const auth_middleware = await create_auth_middleware_specs(deps, {
|
|
116
123
|
allowed_origins: options.allowed_origins,
|
|
117
124
|
session_options: options.session_options,
|
|
118
|
-
bearer_ip_rate_limiter,
|
|
119
125
|
daemon_token_state: options.daemon_token_state
|
|
120
126
|
});
|
|
121
127
|
let middleware_specs = [proxy_spec, ...auth_middleware];
|
|
@@ -147,7 +153,9 @@ export const create_app_server = async (options) => {
|
|
|
147
153
|
backend,
|
|
148
154
|
bootstrap_status,
|
|
149
155
|
session_options: options.session_options,
|
|
150
|
-
|
|
156
|
+
login_ip_rate_limiter,
|
|
157
|
+
signup_ip_rate_limiter,
|
|
158
|
+
bootstrap_ip_rate_limiter,
|
|
151
159
|
login_account_rate_limiter,
|
|
152
160
|
signup_account_rate_limiter,
|
|
153
161
|
action_ip_rate_limiter,
|
|
@@ -166,7 +174,7 @@ export const create_app_server = async (options) => {
|
|
|
166
174
|
session_options: options.session_options,
|
|
167
175
|
bootstrap_status,
|
|
168
176
|
on_bootstrap: options.bootstrap.mode === 'live' ? options.bootstrap.on_bootstrap : undefined,
|
|
169
|
-
|
|
177
|
+
bootstrap_ip_rate_limiter
|
|
170
178
|
});
|
|
171
179
|
const prefix = options.bootstrap.route_prefix ?? '/api/account';
|
|
172
180
|
factory_routes.push(...prefix_route_specs(prefix, bootstrap_routes));
|
|
@@ -243,19 +251,21 @@ export const create_app_server = async (options) => {
|
|
|
243
251
|
});
|
|
244
252
|
}
|
|
245
253
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
254
|
+
// One diagnostic per surface — a deployment that disables login's limiter
|
|
255
|
+
// but keeps signup's is a different posture than one that disables both,
|
|
256
|
+
// and a single collapsed warning hid which surface was open.
|
|
257
|
+
for (const [name, limiter] of [
|
|
258
|
+
['login', login_ip_rate_limiter],
|
|
259
|
+
['signup', signup_ip_rate_limiter],
|
|
260
|
+
['bootstrap', bootstrap_ip_rate_limiter]
|
|
261
|
+
]) {
|
|
262
|
+
if (limiter === null) {
|
|
263
|
+
config_diagnostics.push({
|
|
264
|
+
level: 'warning',
|
|
265
|
+
category: 'config',
|
|
266
|
+
message: `${name} IP rate limiter explicitly disabled (null)`
|
|
267
|
+
});
|
|
268
|
+
}
|
|
259
269
|
}
|
|
260
270
|
if (config_diagnostics.length) {
|
|
261
271
|
surface_spec.surface.diagnostics = [...surface_spec.surface.diagnostics, ...config_diagnostics];
|
|
@@ -25,8 +25,16 @@ export interface AppServerContext {
|
|
|
25
25
|
backend: AppBackend;
|
|
26
26
|
bootstrap_status: BootstrapStatus;
|
|
27
27
|
session_options: SessionOptions<string>;
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Per-IP login + password-change rate limiter (from options). `null` when
|
|
30
|
+
* not configured. One instance per auth surface — see
|
|
31
|
+
* `AppServerOptions.login_ip_rate_limiter` for why these aren't shared.
|
|
32
|
+
*/
|
|
33
|
+
login_ip_rate_limiter: RateLimiter | null;
|
|
34
|
+
/** Per-IP signup rate limiter (from options). `null` when not configured. */
|
|
35
|
+
signup_ip_rate_limiter: RateLimiter | null;
|
|
36
|
+
/** Per-IP bootstrap rate limiter (from options). `null` when not configured. */
|
|
37
|
+
bootstrap_ip_rate_limiter: RateLimiter | null;
|
|
30
38
|
/** Per-account login rate limiter (from options). `null` when not configured. */
|
|
31
39
|
login_account_rate_limiter: RateLimiter | null;
|
|
32
40
|
/** Per-account signup rate limiter (from options). `null` when not configured. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server_context.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,gBAAgB,EAAE,eAAe,CAAC;IAClC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,
|
|
1
|
+
{"version":3,"file":"app_server_context.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,gBAAgB,EAAE,eAAe,CAAC;IAClC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,qBAAqB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC1C,6EAA6E;IAC7E,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C,gFAAgF;IAChF,yBAAyB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC9C,iFAAiF;IACjF,0BAA0B,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/C,kFAAkF;IAClF,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,uGAAuG;IACvG,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C,0GAA0G;IAC1G,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD;;;;OAIG;IACH,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;CAC9B"}
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -457,8 +457,8 @@ file that imports from `middleware.ts` gets these mocks globally. Pair with
|
|
|
457
457
|
- `BearerAuthTestOptions`, `BearerAuthTestCase` — test-case table shape for the bearer auth runner.
|
|
458
458
|
- `create_bearer_auth_mocks(tc)` — configures the module-level mocks per test case; returns spy references.
|
|
459
459
|
- `TEST_CLIENT_IP = '127.0.0.1'` — IP set by the proxy stub in `create_bearer_auth_test_app`.
|
|
460
|
-
- `create_bearer_auth_test_app(tc
|
|
461
|
-
- `describe_bearer_auth_cases(suite_name, cases
|
|
460
|
+
- `create_bearer_auth_test_app(tc)` — Hono app with bearer middleware + echo route at `/api/test` returning `{ok, account_id, credential_type, api_token_id, request_context_set}` — the account-grain identity bearer auth writes, plus a flag for tests that pre-populate `REQUEST_CONTEXT_KEY` via `pre_context`.
|
|
461
|
+
- `describe_bearer_auth_cases(suite_name, cases)` — table-driven runner; one `test()` per case; asserts status, error, body fields, `api_token_id`, context preservation.
|
|
462
462
|
- `TEST_MIDDLEWARE_PATH = '/api/test'` — path used by the echo route in the stack factory.
|
|
463
463
|
- `create_test_middleware_stack_app(options?)` — real proxy + origin + bearer middleware for integration-shape testing. Echo route returns `{ok, client_ip, has_context}`.
|
|
464
464
|
|
|
@@ -571,17 +571,19 @@ Options: `{setup_test, surface_source, capabilities, sensitive_fields?, admin_on
|
|
|
571
571
|
|
|
572
572
|
### `rate_limiting.ts` — `describe_rate_limiting_tests`
|
|
573
573
|
|
|
574
|
-
|
|
574
|
+
Two test groups:
|
|
575
575
|
|
|
576
576
|
1. IP rate limiting on login — fires `max_attempts + 1` requests; last should be 429 with `RateLimitError` body + valid `Retry-After` header.
|
|
577
577
|
2. Per-account rate limiting on login — same username exhausts the bucket; a different username is not blocked.
|
|
578
|
-
|
|
578
|
+
|
|
579
|
+
No bearer group — the bearer path carries no rate limiter on either spine
|
|
580
|
+
(`auth/bearer_auth.ts`), which is also why `rpc_endpoints` is optional here.
|
|
579
581
|
|
|
580
582
|
Each group asserts its required route exists with a descriptive message.
|
|
581
583
|
Creates a tight rate limiter (default `max_attempts: 2`, `window_ms: 60_000`)
|
|
582
584
|
per test and disposes it in `finally`.
|
|
583
585
|
|
|
584
|
-
Options: `{session_options, create_route_specs, rpc_endpoints
|
|
586
|
+
Options: `{session_options, create_route_specs, rpc_endpoints?, app_options?, db_factories?, max_attempts?}`.
|
|
585
587
|
Reads inputs directly from the options bag instead of going through the
|
|
586
588
|
`setup_test` fixture protocol — the per-test rate-limiter overrides need a
|
|
587
589
|
fresh `TestApp` per test that the single-fixture model can't carry.
|
|
@@ -1495,7 +1497,7 @@ pay the 250ms denial floor.
|
|
|
1495
1497
|
|
|
1496
1498
|
`describe_login_security_cross_tests({setup_test, login_path?})` — the imperative
|
|
1497
1499
|
suite pinning **login rate limiting + `X-Forwarded-For` client-IP resolution**
|
|
1498
|
-
over real HTTP on both spines
|
|
1500
|
+
over real HTTP on both spines:
|
|
1499
1501
|
|
|
1500
1502
|
- **per-IP limit fires** — `default_login_ip_rate_limit.max_attempts` (5, the
|
|
1501
1503
|
shared cap on both impls) failed logins from one forwarded IP → 401, the next →
|
|
@@ -1505,15 +1507,26 @@ over real HTTP on both spines. Two cases:
|
|
|
1505
1507
|
- **XFF keying** — exhaust one forwarded IP to 429, then a _distinct_ forwarded IP
|
|
1506
1508
|
→ 401 (not 429), proving the limiter keys on the resolved `X-Forwarded-For` IP,
|
|
1507
1509
|
not the loopback TCP peer (a backend ignoring XFF would 429 the fresh IP too).
|
|
1510
|
+
- **a success does not refund the per-IP budget** — the distributed-spray
|
|
1511
|
+
backstop. Failed guesses against distinct usernames from one forwarded IP,
|
|
1512
|
+
interleaved with the attacker's own successful logins, must still 429 within a
|
|
1513
|
+
bounded total-request budget. Both spines used to clear the IP bucket on
|
|
1514
|
+
success; only the account-grain bucket is forgiven now. The **budget bound** is
|
|
1515
|
+
what makes this adversarial — a decrement-instead-of-zero implementation loops
|
|
1516
|
+
to the iteration cap and fails here. Either leg may trip: the aggregate governs
|
|
1517
|
+
the address, so once it fills the attacker's own login is refused too.
|
|
1508
1518
|
|
|
1509
1519
|
**Cross-process only**, on a **dedicated dual-spawn project** (`cross_backend_security`
|
|
1510
1520
|
via `global_setup_login_security.ts`) — the limiters can't be enabled on a backend
|
|
1511
1521
|
the standard suites share (they fire many loopback logins a live limiter would
|
|
1512
1522
|
429), so the standard backends keep every limiter null. Determinism without a
|
|
1513
1523
|
limiter reset: each case uses its own forwarded IP (a fresh bucket) plus its own
|
|
1514
|
-
non-existent username
|
|
1524
|
+
non-existent username; the spray case additionally mints its attacker account
|
|
1525
|
+
over the setup path (loopback, no XFF), so minting can't touch the bucket under
|
|
1526
|
+
test. Zero Rust code change — the stub's `FUZ_LOGIN_RATE_LIMIT_ENABLED`
|
|
1515
1527
|
and `FUZ_TRUSTED_PROXIES` env-gates were already wired; the TS side env-gates the
|
|
1516
|
-
limiters in the binary, and `create_spine_route_specs` honors
|
|
1528
|
+
limiters in the binary, and `create_spine_route_specs` honors
|
|
1529
|
+
`ctx.login_ip_rate_limiter`.
|
|
1517
1530
|
The in-process counterparts already exist (`describe_rate_limiting_tests` plus the
|
|
1518
1531
|
proxy middleware tests), so there's no in-process leg. `npm run test:cross:security`.
|
|
1519
1532
|
Cited property: `docs/security.md` §"Rate Limiting" + §"Trusted Proxy / Client IP".
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -276,10 +276,11 @@ export const create_test_app = async (options) => {
|
|
|
276
276
|
allowed_origins: [/^http:\/\/localhost/],
|
|
277
277
|
proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
|
|
278
278
|
env_schema: z.object({}),
|
|
279
|
-
|
|
279
|
+
login_ip_rate_limiter: null,
|
|
280
|
+
signup_ip_rate_limiter: null,
|
|
281
|
+
bootstrap_ip_rate_limiter: null,
|
|
280
282
|
login_account_rate_limiter: null,
|
|
281
283
|
signup_account_rate_limiter: null,
|
|
282
|
-
bearer_ip_rate_limiter: null,
|
|
283
284
|
await_pending_effects: true,
|
|
284
285
|
daemon_token_state,
|
|
285
286
|
rpc_endpoints: options.rpc_endpoints,
|
|
@@ -402,10 +403,11 @@ export const create_test_app_for_bootstrap = async (options) => {
|
|
|
402
403
|
allowed_origins: [/^http:\/\/localhost/],
|
|
403
404
|
proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
|
|
404
405
|
env_schema: z.object({}),
|
|
405
|
-
|
|
406
|
+
login_ip_rate_limiter: null,
|
|
407
|
+
signup_ip_rate_limiter: null,
|
|
408
|
+
bootstrap_ip_rate_limiter: null,
|
|
406
409
|
login_account_rate_limiter: null,
|
|
407
410
|
signup_account_rate_limiter: null,
|
|
408
|
-
bearer_ip_rate_limiter: null,
|
|
409
411
|
await_pending_effects: true,
|
|
410
412
|
daemon_token_state,
|
|
411
413
|
rpc_endpoints: options.rpc_endpoints,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAItD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAEtF,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI1F,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAe3E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,EAAE,gBAGxB,CAAC;AAEH,+CAA+C;AAC/C,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAC/B,KAAK,gBAAgB,EACrB,UAAU,wBAAwB,KAChC,KAAK,CAAC,eAAe,CAQvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAItD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAEtF,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI1F,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAe3E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,EAAE,gBAGxB,CAAC;AAEH,+CAA+C;AAC/C,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAC/B,KAAK,gBAAgB,EACrB,UAAU,wBAAwB,KAChC,KAAK,CAAC,eAAe,CAQvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,SAAS,CAgC/E,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,KAAG,SAC+B,CAAC;AAE7F;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,QAAO,cAM1C,CAAC"}
|