@fuzdev/fuz_app 0.55.0 → 0.57.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/actions/CLAUDE.md +211 -155
- package/dist/actions/action_bridge.d.ts +8 -5
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +1 -11
- package/dist/actions/action_codegen.d.ts +19 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +20 -14
- package/dist/actions/action_registry.d.ts.map +1 -1
- package/dist/actions/action_registry.js +5 -2
- package/dist/actions/action_rpc.d.ts +110 -44
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +92 -287
- package/dist/actions/action_spec.d.ts +55 -16
- package/dist/actions/action_spec.d.ts.map +1 -1
- package/dist/actions/action_spec.js +16 -11
- package/dist/actions/action_types.d.ts +28 -60
- package/dist/actions/action_types.d.ts.map +1 -1
- package/dist/actions/action_types.js +13 -5
- package/dist/actions/broadcast_api.d.ts +2 -2
- package/dist/actions/broadcast_api.js +2 -2
- package/dist/actions/compile_action_registry.d.ts +50 -0
- package/dist/actions/compile_action_registry.d.ts.map +1 -0
- package/dist/actions/compile_action_registry.js +69 -0
- package/dist/actions/heartbeat.d.ts +8 -4
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -4
- package/dist/actions/perform_action.d.ts +145 -0
- package/dist/actions/perform_action.d.ts.map +1 -0
- package/dist/actions/perform_action.js +258 -0
- package/dist/actions/register_action_ws.d.ts +44 -38
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +101 -159
- package/dist/actions/register_ws_endpoint.d.ts +2 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +32 -10
- package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
- package/dist/actions/transports_ws_auth_guard.js +1 -1
- package/dist/actions/transports_ws_backend.d.ts +1 -1
- package/dist/actions/transports_ws_backend.js +1 -1
- package/dist/auth/CLAUDE.md +673 -442
- package/dist/auth/account_action_specs.d.ts +28 -7
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +7 -7
- package/dist/auth/account_actions.d.ts +8 -14
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +26 -32
- package/dist/auth/account_queries.d.ts +46 -13
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +73 -33
- package/dist/auth/account_routes.d.ts +4 -3
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +58 -33
- package/dist/auth/account_schema.d.ts +46 -54
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +21 -48
- package/dist/auth/admin_action_specs.d.ts +55 -21
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +42 -26
- package/dist/auth/admin_actions.d.ts +14 -21
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +47 -44
- package/dist/auth/audit_emitter.d.ts +160 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -0
- package/dist/auth/audit_emitter.js +83 -0
- package/dist/auth/audit_log_queries.d.ts +17 -87
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +17 -96
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +7 -3
- package/dist/auth/audit_log_schema.d.ts +48 -42
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +56 -43
- package/dist/auth/auth_guard_resolver.d.ts +44 -0
- package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
- package/dist/auth/auth_guard_resolver.js +56 -0
- package/dist/auth/bootstrap_account.d.ts +7 -7
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +7 -7
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +11 -10
- package/dist/auth/cleanup.d.ts +20 -26
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +33 -47
- package/dist/auth/credential_type_schema.d.ts +115 -0
- package/dist/auth/credential_type_schema.d.ts.map +1 -0
- package/dist/auth/credential_type_schema.js +127 -0
- package/dist/auth/daemon_token_middleware.d.ts +1 -1
- package/dist/auth/daemon_token_middleware.js +3 -3
- package/dist/auth/ddl.d.ts +2 -2
- package/dist/auth/ddl.d.ts.map +1 -1
- package/dist/auth/ddl.js +6 -6
- package/dist/auth/deps.d.ts +7 -32
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/grant_path_schema.d.ts +117 -0
- package/dist/auth/grant_path_schema.d.ts.map +1 -0
- package/dist/auth/grant_path_schema.js +137 -0
- package/dist/auth/invite_queries.d.ts +12 -1
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +12 -1
- package/dist/auth/invite_schema.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +5 -2
- package/dist/auth/migrations.d.ts +22 -7
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +64 -25
- package/dist/auth/request_context.d.ts +157 -170
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +224 -268
- package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +130 -100
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_action_specs.js +262 -0
- package/dist/auth/role_grant_offer_actions.d.ts +104 -0
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
- package/dist/auth/{permit_offer_actions.js → role_grant_offer_actions.js} +153 -140
- package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +80 -70
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_notifications.js +182 -0
- package/dist/auth/{permit_offer_queries.d.ts → role_grant_offer_queries.d.ts} +64 -64
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
- package/dist/auth/{permit_offer_queries.js → role_grant_offer_queries.js} +136 -123
- package/dist/auth/role_grant_offer_schema.d.ts +150 -0
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
- package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +55 -36
- package/dist/auth/role_grant_queries.d.ts +231 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_queries.js +320 -0
- package/dist/auth/role_schema.d.ts +150 -40
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +144 -45
- package/dist/auth/scope_kind_schema.d.ts +96 -0
- package/dist/auth/scope_kind_schema.d.ts.map +1 -0
- package/dist/auth/scope_kind_schema.js +94 -0
- package/dist/auth/self_service_role_action_specs.d.ts +4 -1
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +2 -2
- package/dist/auth/self_service_role_actions.d.ts +35 -29
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +58 -48
- package/dist/auth/session_cookie.d.ts +43 -6
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +31 -5
- package/dist/auth/session_middleware.d.ts +37 -3
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +33 -7
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +48 -19
- package/dist/auth/standard_action_specs.d.ts +2 -2
- package/dist/auth/standard_action_specs.js +4 -4
- package/dist/auth/standard_rpc_actions.d.ts +23 -19
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +12 -12
- package/dist/db/migrate.d.ts +1 -1
- package/dist/db/migrate.js +1 -1
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +4 -4
- package/dist/env/load.d.ts +1 -1
- package/dist/env/load.js +1 -1
- package/dist/hono_context.d.ts +27 -45
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +14 -28
- package/dist/http/CLAUDE.md +235 -121
- package/dist/http/auth_shape.d.ts +191 -0
- package/dist/http/auth_shape.d.ts.map +1 -0
- package/dist/http/auth_shape.js +237 -0
- package/dist/http/common_routes.js +3 -3
- package/dist/http/db_routes.d.ts +4 -0
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +44 -7
- package/dist/http/error_schemas.d.ts +72 -39
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +81 -33
- package/dist/http/pending_effects.d.ts +71 -18
- package/dist/http/pending_effects.d.ts.map +1 -1
- package/dist/http/pending_effects.js +87 -18
- package/dist/http/proxy.d.ts +52 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +92 -14
- package/dist/http/route_spec.d.ts +89 -75
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +54 -72
- package/dist/http/schema_helpers.d.ts +3 -14
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +2 -14
- package/dist/http/surface.d.ts +2 -10
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +3 -4
- package/dist/http/surface_query.d.ts +39 -35
- package/dist/http/surface_query.d.ts.map +1 -1
- package/dist/http/surface_query.js +79 -36
- package/dist/primitive_schemas.d.ts +39 -0
- package/dist/primitive_schemas.d.ts.map +1 -0
- package/dist/primitive_schemas.js +40 -0
- package/dist/realtime/sse_auth_guard.d.ts +5 -5
- package/dist/realtime/sse_auth_guard.js +9 -9
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_backend.d.ts +14 -11
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +12 -8
- package/dist/server/app_server.d.ts +7 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +35 -40
- package/dist/server/validate_nginx.d.ts +1 -1
- package/dist/server/validate_nginx.js +1 -1
- package/dist/testing/CLAUDE.md +50 -38
- package/dist/testing/admin_integration.d.ts +5 -6
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +87 -85
- package/dist/testing/app_server.d.ts +11 -14
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +16 -15
- package/dist/testing/assertions.d.ts.map +1 -1
- package/dist/testing/assertions.js +2 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +15 -9
- package/dist/testing/audit_completeness.d.ts +2 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +36 -36
- package/dist/testing/auth_apps.d.ts +5 -4
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +22 -19
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +5 -5
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +4 -4
- package/dist/testing/db_entities.d.ts +22 -0
- package/dist/testing/db_entities.d.ts.map +1 -0
- package/dist/testing/db_entities.js +28 -0
- package/dist/testing/entities.d.ts +8 -7
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +21 -18
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +13 -14
- package/dist/testing/integration_helpers.d.ts +4 -4
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +20 -18
- package/dist/testing/middleware.d.ts +4 -4
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +12 -11
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +40 -24
- package/dist/testing/rpc_round_trip.d.ts +1 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +14 -13
- package/dist/testing/sse_round_trip.d.ts +3 -4
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +7 -11
- package/dist/testing/standard.d.ts +1 -1
- package/dist/testing/stubs.d.ts +25 -0
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +43 -2
- package/dist/testing/surface_invariants.d.ts +14 -6
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +119 -43
- package/dist/testing/ws_round_trip.d.ts +12 -13
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +19 -11
- package/dist/ui/AdminAccounts.svelte +23 -20
- package/dist/ui/AdminOverview.svelte +15 -13
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
- package/dist/ui/BootstrapForm.svelte +1 -1
- package/dist/ui/CLAUDE.md +60 -60
- package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +27 -26
- package/dist/ui/{PermitOfferForm.svelte.d.ts → RoleGrantOfferForm.svelte.d.ts} +7 -7
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
- package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
- package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/SignupForm.svelte +1 -1
- package/dist/ui/SurfaceExplorer.svelte +35 -15
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +2 -3
- package/dist/ui/admin_accounts_state.svelte.d.ts +18 -18
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +16 -16
- package/dist/ui/admin_rpc_adapters.d.ts +20 -20
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +17 -17
- package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
- package/dist/ui/admin_sessions_state.svelte.js +2 -2
- package/dist/ui/audit_log_state.svelte.d.ts +7 -7
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +6 -6
- package/dist/ui/auth_state.svelte.d.ts +3 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +6 -6
- package/dist/ui/format_scope.d.ts +2 -2
- package/dist/ui/format_scope.js +2 -2
- package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +30 -30
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +18 -18
- package/dist/ui/ui_format.js +2 -2
- package/package.json +3 -3
- package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
- package/dist/auth/permit_offer_action_specs.js +0 -258
- package/dist/auth/permit_offer_actions.d.ts +0 -110
- package/dist/auth/permit_offer_actions.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.js +0 -182
- package/dist/auth/permit_offer_queries.d.ts.map +0 -1
- package/dist/auth/permit_offer_schema.d.ts +0 -125
- package/dist/auth/permit_offer_schema.d.ts.map +0 -1
- package/dist/auth/permit_queries.d.ts +0 -222
- package/dist/auth/permit_queries.d.ts.map +0 -1
- package/dist/auth/permit_queries.js +0 -305
- package/dist/auth/require_keeper.d.ts +0 -20
- package/dist/auth/require_keeper.d.ts.map +0 -1
- package/dist/auth/require_keeper.js +0 -35
- package/dist/auth/route_guards.d.ts +0 -27
- package/dist/auth/route_guards.d.ts.map +0 -1
- package/dist/auth/route_guards.js +0 -38
- package/dist/auth/session_lifecycle.d.ts +0 -37
- package/dist/auth/session_lifecycle.d.ts.map +0 -1
- package/dist/auth/session_lifecycle.js +0 -29
- package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
- package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
package/dist/http/proxy.d.ts
CHANGED
|
@@ -54,23 +54,70 @@ export type ParsedProxy = {
|
|
|
54
54
|
* @throws Error on invalid IP, invalid CIDR network, or NaN/negative/over-range prefix
|
|
55
55
|
*/
|
|
56
56
|
export declare const parse_proxy_entry: (entry: string) => ParsedProxy;
|
|
57
|
+
/**
|
|
58
|
+
* Strict IP validity check.
|
|
59
|
+
*
|
|
60
|
+
* Defense in depth around Hono's `hono/utils/ipaddr` helpers, which are
|
|
61
|
+
* lax in two ways:
|
|
62
|
+
*
|
|
63
|
+
* 1. `distinctRemoteAddr` classifies anything-with-a-colon as `'IPv6'`,
|
|
64
|
+
* including `'host:port'`, `'attacker:controlled'`, `'203.0.113.1:8080'`.
|
|
65
|
+
* 2. `convertIPv6ToBinary` silently accepts malformed forms like
|
|
66
|
+
* `'[::1]:8080'` and `'::1\n'`, parsing them as inconsistent binary
|
|
67
|
+
* values that would still serve as distinct rate-limit keys for an
|
|
68
|
+
* attacker rotating the suffix.
|
|
69
|
+
*
|
|
70
|
+
* Strict validation here is two-layered: a character-set pre-filter
|
|
71
|
+
* (`IP_LITERAL_CHARS`), then a round-trip through `convertIPv*ToBinary`
|
|
72
|
+
* to confirm the input parses cleanly. Either layer alone has holes;
|
|
73
|
+
* together they reject every input form we've seen Hono mis-handle.
|
|
74
|
+
*
|
|
75
|
+
* Used as the security primitive for any code path that takes an IP
|
|
76
|
+
* string from an untrusted source (XFF, query params) and uses it as a
|
|
77
|
+
* key (rate limiting, audit subject) or compares it against trusted
|
|
78
|
+
* proxies via CIDR (where the latent throw would otherwise bubble out).
|
|
79
|
+
*
|
|
80
|
+
* @returns the address family on success, `undefined` if the string is
|
|
81
|
+
* not a strictly-valid IP
|
|
82
|
+
*/
|
|
83
|
+
export declare const validate_ip_strict: (ip: string) => "IPv4" | "IPv6" | undefined;
|
|
57
84
|
/**
|
|
58
85
|
* Check whether `ip` matches any entry in the trusted proxy list.
|
|
59
86
|
*
|
|
60
87
|
* Normalizes `ip` before matching (lowercase, IPv4-mapped IPv6 stripped).
|
|
88
|
+
* Uses `validate_ip_strict` to reject malformed input — without strict
|
|
89
|
+
* validation, Hono's lax `distinctRemoteAddr` would let an entry like
|
|
90
|
+
* `'203.0.113.1:8080'` (false-positive `'IPv6'`) reach
|
|
91
|
+
* `convertIPv6ToBinary` in the CIDR-match branch and throw.
|
|
61
92
|
*/
|
|
62
93
|
export declare const is_trusted_ip: (ip: string, proxies: Array<ParsedProxy>) => boolean;
|
|
63
94
|
/**
|
|
64
95
|
* Resolve the real client IP from an `X-Forwarded-For` header value.
|
|
65
96
|
*
|
|
66
|
-
* Walks right-to-left, skipping trusted proxy entries
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
97
|
+
* Walks right-to-left, skipping trusted proxy entries AND any entry
|
|
98
|
+
* that fails strict IP validation (`validate_ip_strict`). The first
|
|
99
|
+
* untrusted, strictly-valid entry is the client IP. If every walked
|
|
100
|
+
* entry is trusted or malformed, returns the leftmost strictly-valid
|
|
101
|
+
* (trusted) entry (likely-misconfigured all-trusted case) or
|
|
102
|
+
* `undefined` (everything was malformed — middleware falls back to
|
|
103
|
+
* the connection IP). All entries are normalized before matching and
|
|
104
|
+
* in the returned value.
|
|
105
|
+
*
|
|
106
|
+
* Skipping malformed entries is the rate-limit-key fix for the
|
|
107
|
+
* "attacker controls XFF and the proxy passes it through" surface —
|
|
108
|
+
* without the skip, an attacker could rotate arbitrary strings (incl.
|
|
109
|
+
* `'attacker:controlled'`, which Hono's lax `distinctRemoteAddr`
|
|
110
|
+
* misclassifies as IPv6) as XFF values to get fresh per-IP rate-limit
|
|
111
|
+
* buckets. Tradeoff: legitimate non-standard proxies that include
|
|
112
|
+
* ports in XFF entries (e.g. `203.0.113.1:8080`) also fail strict
|
|
113
|
+
* validation, so those entries get skipped and the rate-limit bucket
|
|
114
|
+
* collapses to the proxy's connection IP (one bucket for everyone
|
|
115
|
+
* behind that proxy). Standard proxies (nginx, cloud LBs) don't
|
|
116
|
+
* include ports.
|
|
70
117
|
*
|
|
71
118
|
* @param forwarded_for - the `X-Forwarded-For` header value
|
|
72
119
|
* @param proxies - parsed trusted proxy entries
|
|
73
|
-
* @returns the normalized client IP, or `undefined` if the header is empty
|
|
120
|
+
* @returns the normalized client IP, or `undefined` if the header is empty / all entries malformed
|
|
74
121
|
*/
|
|
75
122
|
export declare const resolve_client_ip: (forwarded_for: string, proxies: Array<ParsedProxy>) => string | undefined;
|
|
76
123
|
/**
|
package/dist/http/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAErD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,KAAG,MAQzC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,sFAAsF;IACtF,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,+DAA+D;IAC/D,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GACpB;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,CAAC;AAElF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,WA6CjD,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAErD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,KAAG,MAQzC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,sFAAsF;IACtF,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,+DAA+D;IAC/D,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GACpB;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,CAAC;AAElF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,WA6CjD,CAAC;AA2BF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,kBAAkB,GAAI,IAAI,MAAM,KAAG,MAAM,GAAG,MAAM,GAAG,SAWjE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,GAAI,IAAI,MAAM,EAAE,SAAS,KAAK,CAAC,WAAW,CAAC,KAAG,OAqBvE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,iBAAiB,GAC7B,eAAe,MAAM,EACrB,SAAS,KAAK,CAAC,WAAW,CAAC,KACzB,MAAM,GAAG,SA0BX,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,YAAY,KAAG,iBAyC/D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,YAAY,KAAG,cAInE,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,GAAG,OAAO,KAAG,MAAyC,CAAC"}
|
package/dist/http/proxy.js
CHANGED
|
@@ -91,14 +91,70 @@ const cidr_contains = (ip_binary, network, prefix, total_bits) => {
|
|
|
91
91
|
const shift = BigInt(total_bits - prefix);
|
|
92
92
|
return ip_binary >> shift === network >> shift;
|
|
93
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Allowed character set for a bare IP literal.
|
|
96
|
+
*
|
|
97
|
+
* Covers the union of IPv4 (digits + `.`), IPv6 (hex digits + `:`), and
|
|
98
|
+
* IPv4-mapped IPv6 forms (`::ffff:127.0.0.1`). Anything outside this
|
|
99
|
+
* set — brackets, whitespace, control bytes, letters g-z — disqualifies
|
|
100
|
+
* the input regardless of what Hono's parser does with it.
|
|
101
|
+
*/
|
|
102
|
+
const IP_LITERAL_CHARS = /^[0-9a-fA-F.:]+$/;
|
|
103
|
+
/**
|
|
104
|
+
* Strict IP validity check.
|
|
105
|
+
*
|
|
106
|
+
* Defense in depth around Hono's `hono/utils/ipaddr` helpers, which are
|
|
107
|
+
* lax in two ways:
|
|
108
|
+
*
|
|
109
|
+
* 1. `distinctRemoteAddr` classifies anything-with-a-colon as `'IPv6'`,
|
|
110
|
+
* including `'host:port'`, `'attacker:controlled'`, `'203.0.113.1:8080'`.
|
|
111
|
+
* 2. `convertIPv6ToBinary` silently accepts malformed forms like
|
|
112
|
+
* `'[::1]:8080'` and `'::1\n'`, parsing them as inconsistent binary
|
|
113
|
+
* values that would still serve as distinct rate-limit keys for an
|
|
114
|
+
* attacker rotating the suffix.
|
|
115
|
+
*
|
|
116
|
+
* Strict validation here is two-layered: a character-set pre-filter
|
|
117
|
+
* (`IP_LITERAL_CHARS`), then a round-trip through `convertIPv*ToBinary`
|
|
118
|
+
* to confirm the input parses cleanly. Either layer alone has holes;
|
|
119
|
+
* together they reject every input form we've seen Hono mis-handle.
|
|
120
|
+
*
|
|
121
|
+
* Used as the security primitive for any code path that takes an IP
|
|
122
|
+
* string from an untrusted source (XFF, query params) and uses it as a
|
|
123
|
+
* key (rate limiting, audit subject) or compares it against trusted
|
|
124
|
+
* proxies via CIDR (where the latent throw would otherwise bubble out).
|
|
125
|
+
*
|
|
126
|
+
* @returns the address family on success, `undefined` if the string is
|
|
127
|
+
* not a strictly-valid IP
|
|
128
|
+
*/
|
|
129
|
+
export const validate_ip_strict = (ip) => {
|
|
130
|
+
if (!IP_LITERAL_CHARS.test(ip))
|
|
131
|
+
return undefined;
|
|
132
|
+
const type = distinctRemoteAddr(ip);
|
|
133
|
+
if (!type)
|
|
134
|
+
return undefined;
|
|
135
|
+
try {
|
|
136
|
+
if (type === 'IPv4')
|
|
137
|
+
convertIPv4ToBinary(ip);
|
|
138
|
+
else
|
|
139
|
+
convertIPv6ToBinary(ip);
|
|
140
|
+
return type;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
94
146
|
/**
|
|
95
147
|
* Check whether `ip` matches any entry in the trusted proxy list.
|
|
96
148
|
*
|
|
97
149
|
* Normalizes `ip` before matching (lowercase, IPv4-mapped IPv6 stripped).
|
|
150
|
+
* Uses `validate_ip_strict` to reject malformed input — without strict
|
|
151
|
+
* validation, Hono's lax `distinctRemoteAddr` would let an entry like
|
|
152
|
+
* `'203.0.113.1:8080'` (false-positive `'IPv6'`) reach
|
|
153
|
+
* `convertIPv6ToBinary` in the CIDR-match branch and throw.
|
|
98
154
|
*/
|
|
99
155
|
export const is_trusted_ip = (ip, proxies) => {
|
|
100
156
|
const normalized = normalize_ip(ip);
|
|
101
|
-
const address_type =
|
|
157
|
+
const address_type = validate_ip_strict(normalized);
|
|
102
158
|
if (!address_type)
|
|
103
159
|
return false;
|
|
104
160
|
for (const proxy of proxies) {
|
|
@@ -121,22 +177,33 @@ export const is_trusted_ip = (ip, proxies) => {
|
|
|
121
177
|
}
|
|
122
178
|
return false;
|
|
123
179
|
};
|
|
124
|
-
// NOTE: some non-standard proxies include ports in XFF entries (e.g.
|
|
125
|
-
// 203.0.113.1:8080). The entry fails distinctRemoteAddr and is treated as
|
|
126
|
-
// untrusted (safe default), but rate limiting keys on the port-suffixed
|
|
127
|
-
// string instead of the bare IP. Low risk — nginx and cloud LBs don't
|
|
128
|
-
// include ports.
|
|
129
180
|
/**
|
|
130
181
|
* Resolve the real client IP from an `X-Forwarded-For` header value.
|
|
131
182
|
*
|
|
132
|
-
* Walks right-to-left, skipping trusted proxy entries
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
183
|
+
* Walks right-to-left, skipping trusted proxy entries AND any entry
|
|
184
|
+
* that fails strict IP validation (`validate_ip_strict`). The first
|
|
185
|
+
* untrusted, strictly-valid entry is the client IP. If every walked
|
|
186
|
+
* entry is trusted or malformed, returns the leftmost strictly-valid
|
|
187
|
+
* (trusted) entry (likely-misconfigured all-trusted case) or
|
|
188
|
+
* `undefined` (everything was malformed — middleware falls back to
|
|
189
|
+
* the connection IP). All entries are normalized before matching and
|
|
190
|
+
* in the returned value.
|
|
191
|
+
*
|
|
192
|
+
* Skipping malformed entries is the rate-limit-key fix for the
|
|
193
|
+
* "attacker controls XFF and the proxy passes it through" surface —
|
|
194
|
+
* without the skip, an attacker could rotate arbitrary strings (incl.
|
|
195
|
+
* `'attacker:controlled'`, which Hono's lax `distinctRemoteAddr`
|
|
196
|
+
* misclassifies as IPv6) as XFF values to get fresh per-IP rate-limit
|
|
197
|
+
* buckets. Tradeoff: legitimate non-standard proxies that include
|
|
198
|
+
* ports in XFF entries (e.g. `203.0.113.1:8080`) also fail strict
|
|
199
|
+
* validation, so those entries get skipped and the rate-limit bucket
|
|
200
|
+
* collapses to the proxy's connection IP (one bucket for everyone
|
|
201
|
+
* behind that proxy). Standard proxies (nginx, cloud LBs) don't
|
|
202
|
+
* include ports.
|
|
136
203
|
*
|
|
137
204
|
* @param forwarded_for - the `X-Forwarded-For` header value
|
|
138
205
|
* @param proxies - parsed trusted proxy entries
|
|
139
|
-
* @returns the normalized client IP, or `undefined` if the header is empty
|
|
206
|
+
* @returns the normalized client IP, or `undefined` if the header is empty / all entries malformed
|
|
140
207
|
*/
|
|
141
208
|
export const resolve_client_ip = (forwarded_for, proxies) => {
|
|
142
209
|
const entries = [];
|
|
@@ -147,15 +214,26 @@ export const resolve_client_ip = (forwarded_for, proxies) => {
|
|
|
147
214
|
}
|
|
148
215
|
if (entries.length === 0)
|
|
149
216
|
return undefined;
|
|
150
|
-
// Walk from right to left, skip trusted proxies
|
|
217
|
+
// Walk from right to left, skip trusted proxies and malformed entries.
|
|
218
|
+
// Returning a malformed entry as the client IP would let an attacker
|
|
219
|
+
// who controls XFF poison the per-IP rate-limit key.
|
|
151
220
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
152
221
|
const entry = entries[i];
|
|
222
|
+
if (!validate_ip_strict(entry))
|
|
223
|
+
continue;
|
|
153
224
|
if (!is_trusted_ip(entry, proxies)) {
|
|
154
225
|
return entry;
|
|
155
226
|
}
|
|
156
227
|
}
|
|
157
|
-
//
|
|
158
|
-
|
|
228
|
+
// Every entry was trusted or malformed. Prefer the leftmost
|
|
229
|
+
// strictly-valid (trusted) entry — the misconfiguration warn in
|
|
230
|
+
// the middleware fires on it. If none, fall through to undefined
|
|
231
|
+
// and let the middleware fall back to the connection IP.
|
|
232
|
+
for (const entry of entries) {
|
|
233
|
+
if (validate_ip_strict(entry))
|
|
234
|
+
return entry;
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
159
237
|
};
|
|
160
238
|
/**
|
|
161
239
|
* Create a Hono middleware that resolves the client IP from trusted proxies.
|
|
@@ -18,22 +18,7 @@ import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
|
18
18
|
import type { Db } from '../db/db.js';
|
|
19
19
|
import { type RouteErrorSchemas, type RateLimitKey } from './error_schemas.js';
|
|
20
20
|
import type { MiddlewareSpec } from './middleware_spec.js';
|
|
21
|
-
|
|
22
|
-
* Auth requirement for a route — `none`, `authenticated`, a specific role, or `keeper`.
|
|
23
|
-
*
|
|
24
|
-
* `{type: 'none'}` means the route is open to all clients — including non-browser
|
|
25
|
-
* callers (CLI, API tokens, scripts). No session or auth middleware guards are applied.
|
|
26
|
-
*/
|
|
27
|
-
export type RouteAuth = {
|
|
28
|
-
type: 'none';
|
|
29
|
-
} | {
|
|
30
|
-
type: 'authenticated';
|
|
31
|
-
} | {
|
|
32
|
-
type: 'role';
|
|
33
|
-
role: string;
|
|
34
|
-
} | {
|
|
35
|
-
type: 'keeper';
|
|
36
|
-
};
|
|
21
|
+
import { type RouteAuth } from './auth_shape.js';
|
|
37
22
|
/**
|
|
38
23
|
* Two-phase auth guard set returned by `AuthGuardResolver`.
|
|
39
24
|
*
|
|
@@ -41,7 +26,7 @@ export type RouteAuth = {
|
|
|
41
26
|
* so unauthenticated callers never see route-shape information from
|
|
42
27
|
* input parsing failures. `post_authorization` runs after the
|
|
43
28
|
* authorization phase has populated `RequestContext` — role / keeper
|
|
44
|
-
* checks live here because they read `c.var.request_context.
|
|
29
|
+
* checks live here because they read `c.var.request_context.role_grants`.
|
|
45
30
|
*/
|
|
46
31
|
export interface AuthGuards {
|
|
47
32
|
pre_validation: Array<MiddlewareHandler>;
|
|
@@ -52,17 +37,16 @@ export interface AuthGuards {
|
|
|
52
37
|
*
|
|
53
38
|
* Injected into `apply_route_specs` to decouple route registration
|
|
54
39
|
* from auth-specific middleware. See `fuz_auth_guard_resolver` in
|
|
55
|
-
* `auth/
|
|
40
|
+
* `auth/auth_guard_resolver.ts` for the standard implementation.
|
|
56
41
|
*/
|
|
57
42
|
export type AuthGuardResolver = (auth: RouteAuth) => AuthGuards;
|
|
58
43
|
/**
|
|
59
|
-
* Per-route authorization phase. Runs after
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* the
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* handler.
|
|
44
|
+
* Per-route authorization phase. Runs after pre-validation auth guards
|
|
45
|
+
* AND input validation; resolves the acting actor (when `auth.actor !== 'none'`)
|
|
46
|
+
* by reading `c.var.validated_input.acting` and sets the request context on
|
|
47
|
+
* the Hono context. Per-route order in `apply_route_specs`: params → query
|
|
48
|
+
* → pre-validation auth (401) → input validation (400) → authorization
|
|
49
|
+
* phase → post-authorization auth (403) → handler.
|
|
66
50
|
*
|
|
67
51
|
* Returns a `Response` to short-circuit (resolution failure → 400 / 500),
|
|
68
52
|
* or `void` to continue. The http framework stays auth-agnostic — fuz_app
|
|
@@ -70,37 +54,46 @@ export type AuthGuardResolver = (auth: RouteAuth) => AuthGuards;
|
|
|
70
54
|
* `auth/request_context.ts`.
|
|
71
55
|
*/
|
|
72
56
|
export type AuthorizationHandler = (c: Context, spec: RouteSpec) => Promise<Response | void>;
|
|
73
|
-
/**
|
|
74
|
-
* Predicate that decides whether a route is "acting-aware" — i.e. whether
|
|
75
|
-
* the dispatcher's authorization phase may emit `actor_required` /
|
|
76
|
-
* `actor_not_on_account` (400) or `no_actors_on_account` /
|
|
77
|
-
* `account_vanished` (500) on this spec. When the predicate returns true
|
|
78
|
-
* the merged error schema is widened to accept those shapes so DEV-mode
|
|
79
|
-
* `wrap_output_validation` doesn't reject them.
|
|
80
|
-
*
|
|
81
|
-
* Computed at the call site because the canonical "input declares
|
|
82
|
-
* `acting?: ActingActor`" check lives in `auth/request_context.ts` (it
|
|
83
|
-
* uses reference equality with the canonical `ActingActor` schema). The
|
|
84
|
-
* `http/` framework receives the predicate via this callback so it stays
|
|
85
|
-
* auth-agnostic. See `http/CLAUDE.md` § Three-layer error-schema merge.
|
|
86
|
-
*/
|
|
87
|
-
export type IsActingAware = (spec: Pick<RouteSpec, 'auth' | 'input'>) => boolean;
|
|
88
57
|
/** HTTP methods supported by route specs. */
|
|
89
58
|
export type RouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
90
59
|
/**
|
|
91
60
|
* Per-request deps provided by the framework to route handlers.
|
|
61
|
+
*
|
|
62
|
+
* Audit writes and other rollback-resilient fire-and-forget calls run
|
|
63
|
+
* through `AppDeps.audit.emit(ctx, input)` (see `auth/audit_emitter.ts`),
|
|
64
|
+
* which captures the pool inside its closure — handlers can never
|
|
65
|
+
* accidentally write audits against the request transaction.
|
|
66
|
+
*
|
|
67
|
+
* Routes whose body manages its own transaction (signup, bootstrap)
|
|
68
|
+
* declare `transaction: false` on the spec, which makes `route.db` the
|
|
69
|
+
* pool — they reach for it directly.
|
|
92
70
|
*/
|
|
93
71
|
export interface RouteContext {
|
|
94
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Transaction-scoped when `RouteSpec.transaction` is true (the default
|
|
74
|
+
* for non-GET); pool-level otherwise.
|
|
75
|
+
*/
|
|
95
76
|
db: Db;
|
|
96
|
-
/**
|
|
97
|
-
|
|
98
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Eager fire-and-forget queue — push the in-flight `Promise<void>` for
|
|
79
|
+
* pool writes already running (audit emits, session touch, api-token
|
|
80
|
+
* usage tracking). The flush middleware drains via
|
|
81
|
+
* `flush_pending_effects` after the handler returns.
|
|
82
|
+
*/
|
|
99
83
|
pending_effects: Array<Promise<void>>;
|
|
84
|
+
/**
|
|
85
|
+
* Deferred post-commit thunks — do not push directly; reach for
|
|
86
|
+
* `emit_after_commit(ctx, fn)` from `pending_effects.ts`. The flush
|
|
87
|
+
* middleware invokes each thunk after the handler (and any wrapping
|
|
88
|
+
* `db.transaction`) returns, closing the microtask-ordering window
|
|
89
|
+
* that an eager `Promise.resolve().then(fn)` leaves open inside the
|
|
90
|
+
* transaction.
|
|
91
|
+
*/
|
|
92
|
+
post_commit_effects: Array<() => void | Promise<void>>;
|
|
100
93
|
}
|
|
101
94
|
/**
|
|
102
95
|
* Route handler function — receives the Hono context and a `RouteContext`
|
|
103
|
-
* with per-request deps (db,
|
|
96
|
+
* with per-request deps (db, pending_effects).
|
|
104
97
|
*
|
|
105
98
|
* TypeScript allows fewer params, so handlers that don't need `route`
|
|
106
99
|
* can use `(c) => ...` without changes.
|
|
@@ -164,27 +157,39 @@ export interface RouteSpec {
|
|
|
164
157
|
/**
|
|
165
158
|
* Get validated input from the Hono context.
|
|
166
159
|
*
|
|
167
|
-
* Call after the input validation middleware has run.
|
|
168
|
-
*
|
|
160
|
+
* Call after the input validation middleware has run. Pass the route's
|
|
161
|
+
* `input` Zod schema to infer the typed shape directly:
|
|
162
|
+
*
|
|
163
|
+
* ```ts
|
|
164
|
+
* const input = get_route_input(c, my_route_spec.input);
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* Or pass an explicit type argument when the schema isn't in scope:
|
|
168
|
+
*
|
|
169
|
+
* ```ts
|
|
170
|
+
* const input = get_route_input<MyInput>(c);
|
|
171
|
+
* ```
|
|
169
172
|
*/
|
|
170
|
-
export declare
|
|
173
|
+
export declare function get_route_input<S extends z.ZodType>(c: Context, schema: S): z.infer<S>;
|
|
174
|
+
export declare function get_route_input<T = unknown>(c: Context): T;
|
|
171
175
|
/**
|
|
172
176
|
* Get validated URL path params from the Hono context.
|
|
173
177
|
*
|
|
174
|
-
* Call after the params validation middleware has run.
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* TODO derive `T` from the route spec so the type parameter isn't manually
|
|
178
|
-
* specified — same applies to `get_route_input` / `get_route_query`.
|
|
178
|
+
* Call after the params validation middleware has run. Pass the route's
|
|
179
|
+
* `params` schema to infer the typed shape, or supply an explicit type
|
|
180
|
+
* argument. See `get_route_input` for the two call shapes.
|
|
179
181
|
*/
|
|
180
|
-
export declare
|
|
182
|
+
export declare function get_route_params<S extends z.ZodType>(c: Context, schema: S): z.infer<S>;
|
|
183
|
+
export declare function get_route_params<T = unknown>(c: Context): T;
|
|
181
184
|
/**
|
|
182
185
|
* Get validated URL query params from the Hono context.
|
|
183
186
|
*
|
|
184
|
-
* Call after the query validation middleware has run.
|
|
185
|
-
*
|
|
187
|
+
* Call after the query validation middleware has run. Pass the route's
|
|
188
|
+
* `query` schema to infer the typed shape, or supply an explicit type
|
|
189
|
+
* argument. See `get_route_input` for the two call shapes.
|
|
186
190
|
*/
|
|
187
|
-
export declare
|
|
191
|
+
export declare function get_route_query<S extends z.ZodType>(c: Context, schema: S): z.infer<S>;
|
|
192
|
+
export declare function get_route_query<T = unknown>(c: Context): T;
|
|
188
193
|
/**
|
|
189
194
|
* Apply named middleware specs to a Hono app.
|
|
190
195
|
*
|
|
@@ -202,29 +207,38 @@ export declare const apply_middleware_specs: (app: Hono, specs: Array<Middleware
|
|
|
202
207
|
* and generic errors), and registers the route.
|
|
203
208
|
*
|
|
204
209
|
* Per-route middleware order: params → query → pre-validation auth
|
|
205
|
-
* guards (401) →
|
|
206
|
-
* (403) →
|
|
207
|
-
* body parsing so unauthenticated callers never see
|
|
208
|
-
* information from parse failures.
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
210
|
+
* guards (401) → input validation (400) → authorization phase →
|
|
211
|
+
* post-authorization auth guards (403) → handler. The 401 check runs
|
|
212
|
+
* before any body parsing so unauthenticated callers never see
|
|
213
|
+
* route-shape information from parse failures. Input validation runs
|
|
214
|
+
* before the authorization phase (validate first, authorize after) so
|
|
215
|
+
* the authorization phase reads `c.var.validated_input.acting` as a
|
|
216
|
+
* typed Zod field instead of pre-parsing the raw body. Role /
|
|
217
|
+
* credential-type denials still surface 403 last; trade-off is that
|
|
218
|
+
* authenticated-but-unauthorized callers can distinguish 400
|
|
219
|
+
* (validation) from 403 (authorization), a defense-in-depth concession
|
|
220
|
+
* deemed acceptable because the route surface is public via
|
|
221
|
+
* spec/codegen JSON.
|
|
215
222
|
*
|
|
216
223
|
* Each handler receives a `RouteContext` with:
|
|
217
|
-
* - `db`: transaction-scoped
|
|
218
|
-
* - `
|
|
219
|
-
* - `
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
224
|
+
* - `db`: transaction-scoped when `RouteSpec.transaction` is true; pool-level otherwise
|
|
225
|
+
* - `pending_effects`: eager fire-and-forget pool-write queue
|
|
226
|
+
* - `post_commit_effects`: deferred-thunk queue (push via `emit_after_commit`)
|
|
227
|
+
*
|
|
228
|
+
* Also enforces registry-time invariant 2 from the auth-shape design:
|
|
229
|
+
* `auth.actor !== 'none' ⟺ input or query declares acting?: ActingActor`.
|
|
230
|
+
* REST is bi-located (GETs declare `acting` on `query`, mutations on
|
|
231
|
+
* `input`), so the check passes both slots; the action-dispatcher
|
|
232
|
+
* registries (`compile_action_registry`) share the same helper with
|
|
233
|
+
* `input` only — `ActionSpec` has no `query` shape.
|
|
234
|
+
*
|
|
235
|
+
* @param resolve_auth_guards - maps `RouteAuth` to middleware — use `fuz_auth_guard_resolver` from `auth/auth_guard_resolver.ts`
|
|
236
|
+
* @param authorize - optional authorization phase; runs after input validation
|
|
223
237
|
* @param db - used for transaction wrapping and `RouteContext`
|
|
224
238
|
* @mutates `app`
|
|
225
|
-
* @throws Error if two specs share the same `method` + `path` (each combination must be unique)
|
|
239
|
+
* @throws Error if two specs share the same `method` + `path` (each combination must be unique), or if any spec violates the actor-acting biconditional
|
|
226
240
|
*/
|
|
227
|
-
export declare const apply_route_specs: (app: Hono, specs: Array<RouteSpec>, resolve_auth_guards: AuthGuardResolver, log: Logger, db: Db, authorize?: AuthorizationHandler
|
|
241
|
+
export declare const apply_route_specs: (app: Hono, specs: Array<RouteSpec>, resolve_auth_guards: AuthGuardResolver, log: Logger, db: Db, authorize?: AuthorizationHandler) => void;
|
|
228
242
|
/**
|
|
229
243
|
* Prepend a prefix to all route spec paths.
|
|
230
244
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/route_spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAW,IAAI,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAC;AACpE,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAKjB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"route_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/route_spec.ts"],"names":[],"mappings":"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"}
|