@fuzdev/fuz_app 0.58.0 → 0.60.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 +13 -8
- package/dist/actions/action_codegen.d.ts +1 -1
- package/dist/actions/action_codegen.js +2 -2
- package/dist/actions/action_event_helpers.d.ts +3 -3
- package/dist/actions/action_event_helpers.js +8 -8
- package/dist/actions/action_event_types.d.ts +3 -3
- package/dist/actions/action_event_types.js +3 -3
- package/dist/actions/transports_ws_auth_guard.d.ts +2 -2
- package/dist/actions/transports_ws_auth_guard.js +3 -3
- package/dist/auth/CLAUDE.md +215 -45
- package/dist/auth/account_action_specs.d.ts +9 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +9 -0
- package/dist/auth/actor_lookup_action_specs.d.ts +127 -0
- package/dist/auth/actor_lookup_action_specs.d.ts.map +1 -0
- package/dist/auth/actor_lookup_action_specs.js +93 -0
- package/dist/auth/actor_lookup_actions.d.ts +19 -0
- package/dist/auth/actor_lookup_actions.d.ts.map +1 -0
- package/dist/auth/actor_lookup_actions.js +32 -0
- package/dist/auth/actor_lookup_queries.d.ts +44 -0
- package/dist/auth/actor_lookup_queries.d.ts.map +1 -0
- package/dist/auth/actor_lookup_queries.js +42 -0
- package/dist/auth/actor_search_action_specs.d.ts +166 -0
- package/dist/auth/actor_search_action_specs.d.ts.map +1 -0
- package/dist/auth/actor_search_action_specs.js +139 -0
- package/dist/auth/actor_search_actions.d.ts +31 -0
- package/dist/auth/actor_search_actions.d.ts.map +1 -0
- package/dist/auth/actor_search_actions.js +61 -0
- package/dist/auth/actor_search_queries.d.ts +75 -0
- package/dist/auth/actor_search_queries.d.ts.map +1 -0
- package/dist/auth/actor_search_queries.js +91 -0
- package/dist/auth/admin_action_specs.d.ts +35 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +35 -0
- package/dist/auth/admin_actions.js +2 -2
- package/dist/auth/all_action_spec_registries.d.ts +55 -0
- package/dist/auth/all_action_spec_registries.d.ts.map +1 -0
- package/dist/auth/all_action_spec_registries.js +59 -0
- package/dist/auth/audit_emitter.d.ts +1 -1
- package/dist/auth/audit_emitter.js +2 -2
- package/dist/auth/audit_log_queries.d.ts +1 -1
- package/dist/auth/audit_log_queries.js +3 -3
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.js +1 -1
- package/dist/auth/audit_log_schema.d.ts +5 -5
- package/dist/auth/audit_log_schema.js +7 -7
- package/dist/auth/auth_ddl.d.ts +7 -0
- package/dist/auth/auth_ddl.d.ts.map +1 -1
- package/dist/auth/auth_ddl.js +8 -0
- package/dist/auth/credential_type_schema.d.ts +1 -1
- package/dist/auth/credential_type_schema.js +3 -3
- package/dist/auth/grant_path_schema.d.ts +1 -1
- package/dist/auth/grant_path_schema.js +3 -3
- package/dist/auth/migrations.d.ts +4 -4
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +7 -6
- package/dist/auth/role_grant_offer_action_specs.d.ts +17 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +17 -0
- package/dist/auth/role_grant_offer_actions.js +2 -2
- package/dist/auth/role_grant_offer_notifications.d.ts +2 -2
- package/dist/auth/role_grant_offer_notifications.js +2 -2
- package/dist/auth/role_grant_queries.d.ts +21 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +31 -0
- package/dist/auth/role_schema.d.ts +2 -2
- package/dist/auth/role_schema.js +3 -3
- package/dist/auth/self_service_role_action_specs.d.ts +8 -0
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +8 -0
- package/dist/auth/self_service_role_actions.d.ts +1 -1
- package/dist/auth/self_service_role_actions.js +2 -2
- package/dist/auth/session_cookie.d.ts +1 -1
- package/dist/auth/session_cookie.js +1 -1
- package/dist/auth/session_middleware.d.ts +1 -1
- package/dist/auth/session_middleware.js +5 -5
- package/dist/rate_limiter.d.ts +5 -5
- package/dist/rate_limiter.js +6 -6
- package/dist/realtime/sse_auth_guard.d.ts +3 -3
- package/dist/realtime/sse_auth_guard.js +4 -4
- package/dist/server/app_backend.d.ts +3 -3
- package/dist/server/app_backend.js +4 -4
- package/dist/server/app_server.d.ts +1 -1
- package/dist/server/app_server.js +10 -10
- package/dist/testing/CLAUDE.md +22 -12
- package/dist/testing/admin_integration.js +4 -4
- package/dist/testing/app_server.d.ts +1 -1
- package/dist/testing/app_server.js +2 -2
- package/dist/testing/attack_surface.d.ts +4 -4
- package/dist/testing/attack_surface.js +6 -6
- package/dist/testing/audit_completeness.js +4 -4
- package/dist/testing/data_exposure.d.ts +2 -2
- package/dist/testing/data_exposure.js +7 -7
- package/dist/testing/db.d.ts +8 -8
- package/dist/testing/db.js +11 -11
- package/dist/testing/integration.js +4 -4
- package/dist/testing/integration_helpers.d.ts +6 -6
- package/dist/testing/integration_helpers.js +7 -7
- package/dist/testing/rate_limiting.js +4 -4
- package/dist/testing/round_trip.js +2 -2
- package/dist/testing/rpc_round_trip.js +2 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +23 -2
- package/dist/testing/sse_round_trip.js +2 -2
- package/dist/testing/surface_invariants.d.ts +4 -4
- package/dist/testing/surface_invariants.js +5 -5
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ export declare const get_session_cookie: <T>(c: Context, options: SessionOptions
|
|
|
18
18
|
* `options.max_age` is the single source of truth for cookie lifetime: it
|
|
19
19
|
* drives both the embedded `expires_at` (via `create_session_cookie_value`)
|
|
20
20
|
* and the cookie's HTTP `Max-Age` attribute set here. Falls back to
|
|
21
|
-
* `
|
|
21
|
+
* `session_cookie_options.maxAge` (= `SESSION_AGE_MAX`) when unset.
|
|
22
22
|
* `options.cookie_options` cannot carry `maxAge` (omitted in the type) so
|
|
23
23
|
* the two values can't drift.
|
|
24
24
|
*/
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { getCookie, setCookie, deleteCookie } from 'hono/cookie';
|
|
8
|
-
import {
|
|
8
|
+
import { session_cookie_options, process_session_cookie, create_session_cookie_value, } from './session_cookie.js';
|
|
9
9
|
import { generate_session_token, hash_session_token, AUTH_SESSION_LIFETIME_MS, query_create_session, query_session_enforce_limit, } from './session_queries.js';
|
|
10
10
|
/**
|
|
11
11
|
* Read the session cookie value from a request.
|
|
@@ -19,15 +19,15 @@ export const get_session_cookie = (c, options) => {
|
|
|
19
19
|
* `options.max_age` is the single source of truth for cookie lifetime: it
|
|
20
20
|
* drives both the embedded `expires_at` (via `create_session_cookie_value`)
|
|
21
21
|
* and the cookie's HTTP `Max-Age` attribute set here. Falls back to
|
|
22
|
-
* `
|
|
22
|
+
* `session_cookie_options.maxAge` (= `SESSION_AGE_MAX`) when unset.
|
|
23
23
|
* `options.cookie_options` cannot carry `maxAge` (omitted in the type) so
|
|
24
24
|
* the two values can't drift.
|
|
25
25
|
*/
|
|
26
26
|
export const set_session_cookie = (c, value, options) => {
|
|
27
27
|
const cookie_options = {
|
|
28
|
-
...
|
|
28
|
+
...session_cookie_options,
|
|
29
29
|
...options.cookie_options,
|
|
30
|
-
maxAge: options.max_age ??
|
|
30
|
+
maxAge: options.max_age ?? session_cookie_options.maxAge,
|
|
31
31
|
};
|
|
32
32
|
setCookie(c, options.cookie_name, value, cookie_options);
|
|
33
33
|
};
|
|
@@ -36,7 +36,7 @@ export const set_session_cookie = (c, value, options) => {
|
|
|
36
36
|
*/
|
|
37
37
|
export const clear_session_cookie = (c, options) => {
|
|
38
38
|
const cookie_options = {
|
|
39
|
-
...
|
|
39
|
+
...session_cookie_options,
|
|
40
40
|
...options.cookie_options,
|
|
41
41
|
};
|
|
42
42
|
deleteCookie(c, options.cookie_name, cookie_options);
|
package/dist/rate_limiter.d.ts
CHANGED
|
@@ -43,9 +43,9 @@ export interface RateLimiterOptions {
|
|
|
43
43
|
max_keys?: number | null;
|
|
44
44
|
}
|
|
45
45
|
/** Default options for per-IP login rate limiting: 5 attempts per 15 minutes. */
|
|
46
|
-
export declare const
|
|
46
|
+
export declare const default_login_ip_rate_limit: RateLimiterOptions;
|
|
47
47
|
/** Default options for per-account login rate limiting: 10 attempts per 30 minutes. */
|
|
48
|
-
export declare const
|
|
48
|
+
export declare const default_login_account_rate_limit: RateLimiterOptions;
|
|
49
49
|
/**
|
|
50
50
|
* Default options for per-IP action-dispatcher rate limiting: 600 attempts
|
|
51
51
|
* per 15 minutes. Shared by the HTTP RPC and WebSocket action dispatchers
|
|
@@ -53,7 +53,7 @@ export declare const DEFAULT_LOGIN_ACCOUNT_RATE_LIMIT: RateLimiterOptions;
|
|
|
53
53
|
* scripts and egregious oracle probes, but well above human or normal
|
|
54
54
|
* automation pace. Tighten downstream for stricter deployments.
|
|
55
55
|
*/
|
|
56
|
-
export declare const
|
|
56
|
+
export declare const default_action_ip_rate_limit: RateLimiterOptions;
|
|
57
57
|
/**
|
|
58
58
|
* Default options for per-actor action-dispatcher rate limiting: 1200
|
|
59
59
|
* attempts per 15 minutes. Shared by the HTTP RPC and WebSocket action
|
|
@@ -61,7 +61,7 @@ export declare const DEFAULT_ACTION_IP_RATE_LIMIT: RateLimiterOptions;
|
|
|
61
61
|
* admin workflow; an oracle probing 10k addresses still finishes in
|
|
62
62
|
* ~2 hours, slow enough to surface in audit. Tighten downstream.
|
|
63
63
|
*/
|
|
64
|
-
export declare const
|
|
64
|
+
export declare const default_action_account_rate_limit: RateLimiterOptions;
|
|
65
65
|
/**
|
|
66
66
|
* Result of a rate limit check or record operation.
|
|
67
67
|
*/
|
|
@@ -139,7 +139,7 @@ export declare class RateLimiter {
|
|
|
139
139
|
/**
|
|
140
140
|
* Create a `RateLimiter` with sensible defaults for per-IP login protection.
|
|
141
141
|
*
|
|
142
|
-
* @param options - override individual options; unset fields use `
|
|
142
|
+
* @param options - override individual options; unset fields use `default_login_ip_rate_limit`
|
|
143
143
|
*/
|
|
144
144
|
export declare const create_rate_limiter: (options?: Partial<RateLimiterOptions>) => RateLimiter;
|
|
145
145
|
/**
|
package/dist/rate_limiter.js
CHANGED
|
@@ -17,14 +17,14 @@ import { ERROR_RATE_LIMIT_EXCEEDED } from './http/error_schemas.js';
|
|
|
17
17
|
*/
|
|
18
18
|
export const DEFAULT_RATE_LIMITER_MAX_KEYS = 100_000;
|
|
19
19
|
/** Default options for per-IP login rate limiting: 5 attempts per 15 minutes. */
|
|
20
|
-
export const
|
|
20
|
+
export const default_login_ip_rate_limit = {
|
|
21
21
|
max_attempts: 5,
|
|
22
22
|
window_ms: 15 * 60_000,
|
|
23
23
|
cleanup_interval_ms: 5 * 60_000,
|
|
24
24
|
max_keys: DEFAULT_RATE_LIMITER_MAX_KEYS,
|
|
25
25
|
};
|
|
26
26
|
/** Default options for per-account login rate limiting: 10 attempts per 30 minutes. */
|
|
27
|
-
export const
|
|
27
|
+
export const default_login_account_rate_limit = {
|
|
28
28
|
max_attempts: 10,
|
|
29
29
|
window_ms: 30 * 60_000,
|
|
30
30
|
cleanup_interval_ms: 5 * 60_000,
|
|
@@ -37,7 +37,7 @@ export const DEFAULT_LOGIN_ACCOUNT_RATE_LIMIT = {
|
|
|
37
37
|
* scripts and egregious oracle probes, but well above human or normal
|
|
38
38
|
* automation pace. Tighten downstream for stricter deployments.
|
|
39
39
|
*/
|
|
40
|
-
export const
|
|
40
|
+
export const default_action_ip_rate_limit = {
|
|
41
41
|
max_attempts: 600,
|
|
42
42
|
window_ms: 15 * 60_000,
|
|
43
43
|
cleanup_interval_ms: 5 * 60_000,
|
|
@@ -50,7 +50,7 @@ export const DEFAULT_ACTION_IP_RATE_LIMIT = {
|
|
|
50
50
|
* admin workflow; an oracle probing 10k addresses still finishes in
|
|
51
51
|
* ~2 hours, slow enough to surface in audit. Tighten downstream.
|
|
52
52
|
*/
|
|
53
|
-
export const
|
|
53
|
+
export const default_action_account_rate_limit = {
|
|
54
54
|
max_attempts: 1200,
|
|
55
55
|
window_ms: 15 * 60_000,
|
|
56
56
|
cleanup_interval_ms: 5 * 60_000,
|
|
@@ -203,10 +203,10 @@ export class RateLimiter {
|
|
|
203
203
|
/**
|
|
204
204
|
* Create a `RateLimiter` with sensible defaults for per-IP login protection.
|
|
205
205
|
*
|
|
206
|
-
* @param options - override individual options; unset fields use `
|
|
206
|
+
* @param options - override individual options; unset fields use `default_login_ip_rate_limit`
|
|
207
207
|
*/
|
|
208
208
|
export const create_rate_limiter = (options) => {
|
|
209
|
-
return new RateLimiter({ ...
|
|
209
|
+
return new RateLimiter({ ...default_login_ip_rate_limit, ...options });
|
|
210
210
|
};
|
|
211
211
|
/**
|
|
212
212
|
* Build a 429 rate-limit-exceeded JSON response with `Retry-After` header.
|
|
@@ -27,7 +27,7 @@ export declare const AUDIT_LOG_CHANNEL = "audit_log";
|
|
|
27
27
|
* (matched by the blake3 session hash in `event.metadata.session_id`) — closing
|
|
28
28
|
* all of a user's streams for a single-session revoke would be over-aggressive.
|
|
29
29
|
*/
|
|
30
|
-
export declare const
|
|
30
|
+
export declare const disconnect_event_types: ReadonlySet<string>;
|
|
31
31
|
/**
|
|
32
32
|
* Create an audit event handler that closes SSE streams on auth changes.
|
|
33
33
|
*
|
|
@@ -69,7 +69,7 @@ export interface AuditLogSse {
|
|
|
69
69
|
* One spec per `AUDIT_EVENT_TYPES` entry, all sharing the `AuditLogEventJson` params schema.
|
|
70
70
|
* Pass to `create_app_server`'s `event_specs` for surface generation and DEV validation.
|
|
71
71
|
*/
|
|
72
|
-
export declare const
|
|
72
|
+
export declare const audit_log_event_specs: Array<EventSpec>;
|
|
73
73
|
/**
|
|
74
74
|
* Default max concurrent SSE subscribers per session scope for the audit log.
|
|
75
75
|
*
|
|
@@ -102,7 +102,7 @@ export declare const AUDIT_LOG_SSE_MAX_PER_SCOPE = 10;
|
|
|
102
102
|
* create_audit_log_route_specs({stream: audit_sse});
|
|
103
103
|
*
|
|
104
104
|
* // In create_app_server options:
|
|
105
|
-
* event_specs:
|
|
105
|
+
* event_specs: audit_log_event_specs,
|
|
106
106
|
* ```
|
|
107
107
|
*/
|
|
108
108
|
export declare const create_audit_log_sse: (options: {
|
|
@@ -25,7 +25,7 @@ export const AUDIT_LOG_CHANNEL = 'audit_log';
|
|
|
25
25
|
* (matched by the blake3 session hash in `event.metadata.session_id`) — closing
|
|
26
26
|
* all of a user's streams for a single-session revoke would be over-aggressive.
|
|
27
27
|
*/
|
|
28
|
-
export const
|
|
28
|
+
export const disconnect_event_types = new Set([
|
|
29
29
|
'role_grant_revoke', // role revoked — user lost access
|
|
30
30
|
'session_revoke', // single session revoked — close only that stream
|
|
31
31
|
'session_revoke_all', // all sessions invalidated — user should be kicked
|
|
@@ -51,7 +51,7 @@ export const DISCONNECT_EVENT_TYPES = new Set([
|
|
|
51
51
|
*/
|
|
52
52
|
export const create_sse_auth_guard = (registry, required_role, log) => {
|
|
53
53
|
return (event) => {
|
|
54
|
-
if (!
|
|
54
|
+
if (!disconnect_event_types.has(event.event_type))
|
|
55
55
|
return;
|
|
56
56
|
// Only act on successful revocations. Failed attempts carry
|
|
57
57
|
// attacker-controlled identifiers (e.g., session_revoke with outcome=failure
|
|
@@ -98,7 +98,7 @@ export const create_sse_auth_guard = (registry, required_role, log) => {
|
|
|
98
98
|
* One spec per `AUDIT_EVENT_TYPES` entry, all sharing the `AuditLogEventJson` params schema.
|
|
99
99
|
* Pass to `create_app_server`'s `event_specs` for surface generation and DEV validation.
|
|
100
100
|
*/
|
|
101
|
-
export const
|
|
101
|
+
export const audit_log_event_specs = AUDIT_EVENT_TYPES.map((event_type) => ({
|
|
102
102
|
method: event_type,
|
|
103
103
|
params: AuditLogEventJson,
|
|
104
104
|
description: `Audit log: ${event_type.replaceAll('_', ' ')}`,
|
|
@@ -136,7 +136,7 @@ export const AUDIT_LOG_SSE_MAX_PER_SCOPE = 10;
|
|
|
136
136
|
* create_audit_log_route_specs({stream: audit_sse});
|
|
137
137
|
*
|
|
138
138
|
* // In create_app_server options:
|
|
139
|
-
* event_specs:
|
|
139
|
+
* event_specs: audit_log_event_specs,
|
|
140
140
|
* ```
|
|
141
141
|
*/
|
|
142
142
|
export const create_audit_log_sse = (options) => {
|
|
@@ -66,7 +66,7 @@ export interface CreateAppBackendOptions {
|
|
|
66
66
|
* Audit-log config for consumer event-type extensions. Built once at
|
|
67
67
|
* startup via `create_audit_log_config({extra_events})` and captured
|
|
68
68
|
* inside `AppDeps.audit` so consumer handlers cannot silently fall
|
|
69
|
-
* back to the builtin config. Omit to use `
|
|
69
|
+
* back to the builtin config. Omit to use `builtin_audit_log_config`
|
|
70
70
|
* (no extra events).
|
|
71
71
|
*/
|
|
72
72
|
audit_log_config?: AuditLogConfig;
|
|
@@ -76,7 +76,7 @@ export interface CreateAppBackendOptions {
|
|
|
76
76
|
* (`namespace`, `name`, `sequence`); order is append-only so forward-only
|
|
77
77
|
* guarantees hold per-namespace.
|
|
78
78
|
*
|
|
79
|
-
* Names in `
|
|
79
|
+
* Names in `reserved_migration_namespaces` (currently `['fuz_auth']`) are
|
|
80
80
|
* rejected at startup. Omit for no extra namespaces. This is the only
|
|
81
81
|
* place to splice consumer migrations — DB init belongs to the backend
|
|
82
82
|
* lifecycle, not server assembly.
|
|
@@ -92,7 +92,7 @@ export interface CreateAppBackendOptions {
|
|
|
92
92
|
*
|
|
93
93
|
* @param options - keyring, password deps, optional database URL, and optional `migration_namespaces`
|
|
94
94
|
* @returns app backend with deps, database metadata, and combined migration results
|
|
95
|
-
* @throws Error if `migration_namespaces` contains a namespace in `
|
|
95
|
+
* @throws Error if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`
|
|
96
96
|
*/
|
|
97
97
|
export declare const create_app_backend: (options: CreateAppBackendOptions) => Promise<AppBackend>;
|
|
98
98
|
//# sourceMappingURL=app_backend.d.ts.map
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
14
14
|
import { create_audit_emitter } from '../auth/audit_emitter.js';
|
|
15
15
|
import { run_migrations } from '../db/migrate.js';
|
|
16
|
-
import {
|
|
16
|
+
import { auth_migration_ns, reserved_migration_namespaces } from '../auth/migrations.js';
|
|
17
17
|
import { create_db } from '../db/create_db.js';
|
|
18
18
|
/**
|
|
19
19
|
* Initialize the backend: database + auth migrations + deps.
|
|
@@ -24,7 +24,7 @@ import { create_db } from '../db/create_db.js';
|
|
|
24
24
|
*
|
|
25
25
|
* @param options - keyring, password deps, optional database URL, and optional `migration_namespaces`
|
|
26
26
|
* @returns app backend with deps, database metadata, and combined migration results
|
|
27
|
-
* @throws Error if `migration_namespaces` contains a namespace in `
|
|
27
|
+
* @throws Error if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`
|
|
28
28
|
*/
|
|
29
29
|
export const create_app_backend = async (options) => {
|
|
30
30
|
const { database_url, keyring, password, stat, read_text_file, delete_file } = options;
|
|
@@ -32,13 +32,13 @@ export const create_app_backend = async (options) => {
|
|
|
32
32
|
const { db, close, db_type, db_name } = await create_db(database_url);
|
|
33
33
|
if (options.migration_namespaces?.length) {
|
|
34
34
|
for (const ns of options.migration_namespaces) {
|
|
35
|
-
if (
|
|
35
|
+
if (reserved_migration_namespaces.includes(ns.namespace)) {
|
|
36
36
|
throw new Error(`Migration namespace "${ns.namespace}" is reserved by fuz_app — choose a different namespace`);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
const migration_results = await run_migrations(db, [
|
|
41
|
-
|
|
41
|
+
auth_migration_ns,
|
|
42
42
|
...(options.migration_namespaces ?? []),
|
|
43
43
|
]);
|
|
44
44
|
const audit = create_audit_emitter({
|
|
@@ -136,7 +136,7 @@ export interface AppServerOptions {
|
|
|
136
136
|
* When truthy, creates an `AuditLogSse` instance internally, appends the SSE
|
|
137
137
|
* listener to `backend.deps.audit.on_event_chain` (composing with the
|
|
138
138
|
* consumer's `on_audit_event` callback rather than rebuilding `AppDeps`), and
|
|
139
|
-
* auto-includes `
|
|
139
|
+
* auto-includes `audit_log_event_specs` in the surface. The result is exposed
|
|
140
140
|
* on `AppServerContext` (for route factories) and `AppServer` (for the caller).
|
|
141
141
|
*
|
|
142
142
|
* Pass `true` for defaults (admin role), or `{role: 'custom'}` for a custom role.
|
|
@@ -11,10 +11,10 @@ import { Hono } from 'hono';
|
|
|
11
11
|
import { logger } from 'hono/logger';
|
|
12
12
|
import { bodyLimit } from 'hono/body-limit';
|
|
13
13
|
import { z } from 'zod';
|
|
14
|
-
import {
|
|
15
|
-
import { create_audit_log_sse,
|
|
14
|
+
import { session_cookie_options, } from '../auth/session_cookie.js';
|
|
15
|
+
import { create_audit_log_sse, audit_log_event_specs, } from '../realtime/sse_auth_guard.js';
|
|
16
16
|
import { query_app_settings_load } from '../auth/app_settings_queries.js';
|
|
17
|
-
import { create_rate_limiter,
|
|
17
|
+
import { create_rate_limiter, default_login_account_rate_limit, default_action_account_rate_limit, default_action_ip_rate_limit, } from '../rate_limiter.js';
|
|
18
18
|
// Side-effect import: augments Hono's ContextVariableMap so consumers
|
|
19
19
|
// that import app_server get type-safe c.get('auth_session_id') etc.
|
|
20
20
|
import '../hono_context.js';
|
|
@@ -53,19 +53,19 @@ export const create_app_server = async (options) => {
|
|
|
53
53
|
// Rate limiter defaults (undefined = default, null = disable)
|
|
54
54
|
const ip_rate_limiter = options.ip_rate_limiter === undefined ? create_rate_limiter() : options.ip_rate_limiter;
|
|
55
55
|
const login_account_rate_limiter = options.login_account_rate_limiter === undefined
|
|
56
|
-
? create_rate_limiter(
|
|
56
|
+
? create_rate_limiter(default_login_account_rate_limit)
|
|
57
57
|
: options.login_account_rate_limiter;
|
|
58
58
|
const signup_account_rate_limiter = options.signup_account_rate_limiter === undefined
|
|
59
|
-
? create_rate_limiter(
|
|
59
|
+
? create_rate_limiter(default_login_account_rate_limit)
|
|
60
60
|
: options.signup_account_rate_limiter;
|
|
61
61
|
const bearer_ip_rate_limiter = options.bearer_ip_rate_limiter === undefined
|
|
62
62
|
? create_rate_limiter()
|
|
63
63
|
: options.bearer_ip_rate_limiter;
|
|
64
64
|
const action_ip_rate_limiter = options.action_ip_rate_limiter === undefined
|
|
65
|
-
? create_rate_limiter(
|
|
65
|
+
? create_rate_limiter(default_action_ip_rate_limit)
|
|
66
66
|
: options.action_ip_rate_limiter;
|
|
67
67
|
const action_account_rate_limiter = options.action_account_rate_limiter === undefined
|
|
68
|
-
? create_rate_limiter(
|
|
68
|
+
? create_rate_limiter(default_action_account_rate_limit)
|
|
69
69
|
: options.action_account_rate_limiter;
|
|
70
70
|
// Factory-managed audit SSE — appends a listener to the bound emitter's
|
|
71
71
|
// chain so SSE fan-out runs alongside the consumer's `on_audit_event`
|
|
@@ -156,7 +156,7 @@ export const create_app_server = async (options) => {
|
|
|
156
156
|
: middleware_specs;
|
|
157
157
|
const all_event_specs = [
|
|
158
158
|
...(options.event_specs ?? []),
|
|
159
|
-
...(audit_sse ?
|
|
159
|
+
...(audit_sse ? audit_log_event_specs : []),
|
|
160
160
|
];
|
|
161
161
|
const surface_spec = create_app_surface_spec({
|
|
162
162
|
middleware_specs: surface_middleware,
|
|
@@ -176,11 +176,11 @@ export const create_app_server = async (options) => {
|
|
|
176
176
|
message: 'Session cookie secure=false — cookies sent over HTTP',
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
|
-
if (cookie_opts.sameSite && cookie_opts.sameSite !==
|
|
179
|
+
if (cookie_opts.sameSite && cookie_opts.sameSite !== session_cookie_options.sameSite) {
|
|
180
180
|
config_diagnostics.push({
|
|
181
181
|
level: 'warning',
|
|
182
182
|
category: 'security',
|
|
183
|
-
message: `Session cookie sameSite='${cookie_opts.sameSite}' — weakened from default '${
|
|
183
|
+
message: `Session cookie sameSite='${cookie_opts.sameSite}' — weakened from default '${session_cookie_options.sameSite}'`,
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
186
|
if (cookie_opts.httpOnly === false) {
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -91,7 +91,7 @@ from `app_server.ts` instead.
|
|
|
91
91
|
## Database — `db.ts`
|
|
92
92
|
|
|
93
93
|
Factory builders for parameterized DB tests. Consumer projects pass their
|
|
94
|
-
`init_schema` callback (which calls `run_migrations(db, [
|
|
94
|
+
`init_schema` callback (which calls `run_migrations(db, [auth_migration_ns, ...app_migrations])`);
|
|
95
95
|
factories accept any migration namespace set.
|
|
96
96
|
|
|
97
97
|
| Helper | Role |
|
|
@@ -101,10 +101,10 @@ factories accept any migration namespace set.
|
|
|
101
101
|
| `reset_pglite(db)` | `DROP SCHEMA public CASCADE` + recreate. Reuses a live PGlite instance. |
|
|
102
102
|
| `create_pglite_factory(init_schema)` | In-memory; no external deps; `skip: false`. See WASM caching below. |
|
|
103
103
|
| `create_pg_factory(init_schema, test_url?)` | PostgreSQL; `skip: true` when `test_url` is missing; drops `schema_version` before `init_schema` so migrations re-evaluate against actual tables (prevents stale tracker rows from skipping migrations when DDL changes between test sessions); pool is reused + cleaned up across `create()` calls. |
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `drop_auth_schema(db)` | `DROP TABLE IF EXISTS <table> CASCADE` for every entry in `
|
|
104
|
+
| `auth_truncate_tables` | `['invite', 'api_token', 'auth_session', 'role_grant', 'role_grant_offer', 'actor', 'account']` in FK-safe order. Excludes `audit_log` — unit DB tests don't need to truncate it. |
|
|
105
|
+
| `auth_integration_truncate_tables` | `auth_truncate_tables + ['audit_log']` — for integration suites that exercise the audit path. |
|
|
106
|
+
| `auth_drop_tables` | Full set from `auth_migrations` in drop order; call `drop_auth_schema(db)` at the top of `init_schema` on persistent pg databases that may hold stale DDL from previous fuz_app versions. |
|
|
107
|
+
| `drop_auth_schema(db)` | `DROP TABLE IF EXISTS <table> CASCADE` for every entry in `auth_drop_tables` plus `schema_version`. Safe on fresh DBs. |
|
|
108
108
|
| `create_describe_db(factories, truncate_tables)` | Returns `describe_db(name, fn)` that runs `fn(get_db)` once per factory, inside a `describe` block with shared `beforeAll(create)` + `beforeEach(TRUNCATE)` + `afterAll(close)`. Skipped factories use `describe.skip`. |
|
|
109
109
|
| `log_db_factory_status(factories)` | Console summary of enabled / skipped factories. |
|
|
110
110
|
|
|
@@ -234,13 +234,13 @@ Tightness audit:
|
|
|
234
234
|
classifies every route × status combination as `'literal' | 'enum' | 'generic'`.
|
|
235
235
|
- `assert_error_schema_tightness(surface, options?)` — fails routes below a
|
|
236
236
|
threshold (`min_specificity`, default `'enum'`) with `allowlist` + `ignore_statuses` escape hatches.
|
|
237
|
-
- `
|
|
237
|
+
- `fuz_app_stock_route_tightness_allowlist` — currently empty. Every
|
|
238
238
|
fuz_app-shipped route (account login/password/bootstrap/signup, db
|
|
239
239
|
health/tables/:name/tables/:name/rows/:id) has been tightened in place to
|
|
240
240
|
`z.enum([...])` / `z.literal(...)` against every emit-site code. Kept as a
|
|
241
241
|
forward-compatibility hook for future stock routes that need an interim
|
|
242
242
|
exemption; paths assume the standard `/api/account` + `/api/db` prefixes.
|
|
243
|
-
- `
|
|
243
|
+
- `default_error_schema_tightness` — `{ignore_statuses: [401, 403, 429], allowlist: fuz_app_stock_route_tightness_allowlist}`.
|
|
244
244
|
Applied by `describe_standard_attack_surface_tests` when
|
|
245
245
|
`error_schema_tightness` is omitted; pass an override config or `null` to
|
|
246
246
|
opt out.
|
|
@@ -323,8 +323,8 @@ Walks Zod schemas to generate valid values for adversarial/round-trip tests.
|
|
|
323
323
|
| `check_error_response_fields(body)` | Returns the list of fields outside `KNOWN_SAFE_ERROR_FIELDS` (`error`, `issues`, `required_roles`, `required_credential_types`, `retry_after`, `has_references`, `ok`). |
|
|
324
324
|
| `assert_no_error_info_leakage(body, context)` | Rejects field-name patterns (`stack`, `trace`, `sql`, …) + value patterns (`node_modules`, stack-like `at …`, `.ts:NN`). |
|
|
325
325
|
| `assert_rate_limit_retry_after_header(response, body)` | `Retry-After` numeric header equals `Math.ceil(body.retry_after)`. |
|
|
326
|
-
| `
|
|
327
|
-
| `
|
|
326
|
+
| `sensitive_field_blocklist` | `['password_hash', 'token_hash']` — never in any response body. |
|
|
327
|
+
| `admin_only_field_blocklist` | `['updated_by', 'created_by']` — never in non-admin response bodies. |
|
|
328
328
|
| `collect_json_keys_recursive(value)` | Deep walk; returns `Set<string>` of every key at every nesting depth. |
|
|
329
329
|
| `assert_no_sensitive_fields_in_json(body, blocklist, context)` | Rejects any key in the blocklist at any depth. |
|
|
330
330
|
| `pick_auth_headers(spec, test_app, authed_account, admin_account)` | `RouteAuth` → appropriate test credentials; role `admin` uses `admin_account`, other roles use bootstrapped keeper, `keeper` uses daemon token. |
|
|
@@ -337,7 +337,7 @@ Single-call bundle of 5 top-level groups (10 named tests + every
|
|
|
337
337
|
adversarial case per route):
|
|
338
338
|
|
|
339
339
|
1. **attack surface snapshot** — `matches committed snapshot`, `is deterministic`.
|
|
340
|
-
2. **attack surface structure** — `only expected public routes`, `full middleware stack on API routes`, `surface invariants`, `security policy`, `error schema tightness` (logs counts and asserts against `
|
|
340
|
+
2. **attack surface structure** — `only expected public routes`, `full middleware stack on API routes`, `surface invariants`, `security policy`, `error schema tightness` (logs counts and asserts against `default_error_schema_tightness` by default; pass an override config or `null` via `error_schema_tightness`).
|
|
341
341
|
3. **adversarial HTTP auth enforcement** — `unauthenticated → 401`, `wrong role → 403` × roles, `authenticated without role → 403`, `keeper routes reject session credential → 403`, `correct auth passes guard`.
|
|
342
342
|
4. **adversarial input validation** — delegated to `describe_adversarial_input`.
|
|
343
343
|
5. **adversarial 404 response validation** — delegated to `describe_adversarial_404`.
|
|
@@ -511,8 +511,8 @@ new arrivals (default 1000ms); drops the waiter on timeout so the
|
|
|
511
511
|
Six tests in two top-level groups:
|
|
512
512
|
|
|
513
513
|
1. **schema-level** (3 tests, no DB) — walks JSON Schema representations:
|
|
514
|
-
- `no sensitive fields in any output schema` — `
|
|
515
|
-
- `no admin-only fields in non-admin output schemas` — `
|
|
514
|
+
- `no sensitive fields in any output schema` — `sensitive_field_blocklist`
|
|
515
|
+
- `no admin-only fields in non-admin output schemas` — `admin_only_field_blocklist`
|
|
516
516
|
- `no sensitive fields in any error schema`
|
|
517
517
|
2. **runtime** (3 tests, DB-backed via `create_test_app`):
|
|
518
518
|
- `unauthenticated error responses contain no sensitive fields`
|
|
@@ -699,6 +699,16 @@ deps — no DB needed.
|
|
|
699
699
|
|
|
700
700
|
Options: `{build: () => AppSurfaceSpec, roles: Array<string>}`.
|
|
701
701
|
|
|
702
|
+
**Opt-in bundles need their own per-bundle suite file.** Action bundles
|
|
703
|
+
not folded into `create_standard_rpc_actions` (today `self_service_role_actions`
|
|
704
|
+
and `actor_lookup_actions`) get zero adversarial / round-trip coverage
|
|
705
|
+
from `describe_rpc_attack_surface_tests` + `describe_rpc_round_trip_tests`
|
|
706
|
+
unless the consumer ships a `<module>.rpc_suites.db.test.ts` mounting the
|
|
707
|
+
opt-in factory on the RPC endpoint and calling both suites. See
|
|
708
|
+
`../../test/CLAUDE.md` §Composable Test Suites for the obligation note
|
|
709
|
+
and `actor_lookup_actions.rpc_suites.db.test.ts` /
|
|
710
|
+
`role_grant_offer_actions.rpc_suites.db.test.ts` as templates.
|
|
711
|
+
|
|
702
712
|
## Cross-cutting conventions
|
|
703
713
|
|
|
704
714
|
- **`assert` from vitest, not `expect`.** Project-wide convention
|
|
@@ -28,9 +28,9 @@ import './assert_dev_env.js';
|
|
|
28
28
|
import { describe, test, assert, afterAll } from 'vitest';
|
|
29
29
|
import { ROLE_KEEPER, ROLE_ADMIN } from '../auth/role_schema.js';
|
|
30
30
|
import { GRANT_PATH_ADMIN } from '../auth/grant_path_schema.js';
|
|
31
|
-
import {
|
|
31
|
+
import { auth_migration_ns } from '../auth/migrations.js';
|
|
32
32
|
import { create_test_app } from './app_server.js';
|
|
33
|
-
import { create_pglite_factory, create_describe_db,
|
|
33
|
+
import { create_pglite_factory, create_describe_db, auth_integration_truncate_tables, } from './db.js';
|
|
34
34
|
import { find_auth_route } from './integration_helpers.js';
|
|
35
35
|
import { run_migrations } from '../db/migrate.js';
|
|
36
36
|
import { ErrorCoverageCollector, assert_error_coverage, DEFAULT_INTEGRATION_ERROR_COVERAGE, } from './error_coverage.js';
|
|
@@ -88,10 +88,10 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
88
88
|
const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
|
|
89
89
|
const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
|
|
90
90
|
const init_schema = async (db) => {
|
|
91
|
-
await run_migrations(db, [
|
|
91
|
+
await run_migrations(db, [auth_migration_ns]);
|
|
92
92
|
};
|
|
93
93
|
const factories = options.db_factories ?? [create_pglite_factory(init_schema)];
|
|
94
|
-
const describe_db = create_describe_db(factories,
|
|
94
|
+
const describe_db = create_describe_db(factories, auth_integration_truncate_tables);
|
|
95
95
|
describe_db('standard_admin_integration', (get_db) => {
|
|
96
96
|
const { cookie_name } = options.session_options;
|
|
97
97
|
const { role_specs } = options.roles;
|
|
@@ -120,7 +120,7 @@ export interface TestAppServerOptions {
|
|
|
120
120
|
*
|
|
121
121
|
* Use when the consumer registers extra event types via
|
|
122
122
|
* `create_audit_log_config({extra_events})` — without this, emits for
|
|
123
|
-
* those events fall back to `
|
|
123
|
+
* those events fall back to `builtin_audit_log_config` and log
|
|
124
124
|
* "unknown event_type" warnings.
|
|
125
125
|
*/
|
|
126
126
|
audit_log_config?: AuditLogConfig;
|
|
@@ -10,7 +10,7 @@ import { generate_session_token, hash_session_token, AUTH_SESSION_LIFETIME_MS, q
|
|
|
10
10
|
import { query_create_api_token } from '../auth/api_token_queries.js';
|
|
11
11
|
import { create_session_cookie_value } from '../auth/session_cookie.js';
|
|
12
12
|
import { run_migrations } from '../db/migrate.js';
|
|
13
|
-
import {
|
|
13
|
+
import { auth_migration_ns } from '../auth/migrations.js';
|
|
14
14
|
import { create_audit_emitter } from '../auth/audit_emitter.js';
|
|
15
15
|
import { create_app_server, } from '../server/app_server.js';
|
|
16
16
|
import { generate_daemon_token, DAEMON_TOKEN_HEADER, } from '../auth/daemon_token.js';
|
|
@@ -33,7 +33,7 @@ export const TEST_COOKIE_SECRET = 'a'.repeat(64);
|
|
|
33
33
|
// Shares the WASM instance cache from test_db.ts, avoiding redundant cold starts
|
|
34
34
|
// within the same vitest worker thread. Schema is reset on each create() call.
|
|
35
35
|
const fallback_pglite_factory = create_pglite_factory(async (db) => {
|
|
36
|
-
await run_migrations(db, [
|
|
36
|
+
await run_migrations(db, [auth_migration_ns]);
|
|
37
37
|
});
|
|
38
38
|
/**
|
|
39
39
|
* Bootstrap a test account with credentials.
|
|
@@ -20,7 +20,7 @@ export interface AdversarialTestOptions {
|
|
|
20
20
|
export declare const describe_adversarial_auth: (options: AdversarialTestOptions) => void;
|
|
21
21
|
/**
|
|
22
22
|
* Merge a consumer's `error_schema_tightness` option with
|
|
23
|
-
* `
|
|
23
|
+
* `default_error_schema_tightness` so `allowlist` and `ignore_statuses` are
|
|
24
24
|
* additive rather than replacing.
|
|
25
25
|
*
|
|
26
26
|
* - `undefined` → return the default as-is.
|
|
@@ -52,9 +52,9 @@ export interface StandardAttackSurfaceOptions {
|
|
|
52
52
|
security_policy?: SurfaceSecurityPolicyOptions;
|
|
53
53
|
/**
|
|
54
54
|
* Error schema tightness assertion config. Defaults to
|
|
55
|
-
* `
|
|
55
|
+
* `default_error_schema_tightness` (ignores 401/403/429,
|
|
56
56
|
* `min_specificity: 'enum'`, allowlist seeded with
|
|
57
|
-
* `
|
|
57
|
+
* `fuz_app_stock_route_tightness_allowlist`).
|
|
58
58
|
*
|
|
59
59
|
* Consumer-supplied `allowlist` and `ignore_statuses` are **additive** —
|
|
60
60
|
* the suite merges them underneath the stock defaults, so project-specific
|
|
@@ -74,7 +74,7 @@ export interface StandardAttackSurfaceOptions {
|
|
|
74
74
|
* 4. Middleware stack — every API route has the full middleware chain
|
|
75
75
|
* 5. Surface invariants — structural assertions (error schemas, descriptions, duplicates, consistency)
|
|
76
76
|
* 6. Security policy — rate limiting on sensitive routes, no unexpected public mutations, method conventions
|
|
77
|
-
* 7. Error schema tightness — informational log of generic vs specific error schemas, plus assertion against `
|
|
77
|
+
* 7. Error schema tightness — informational log of generic vs specific error schemas, plus assertion against `default_error_schema_tightness` by default (opt out with `error_schema_tightness: null`)
|
|
78
78
|
* 8. Adversarial auth — unauthenticated/wrong-role/correct-auth enforcement
|
|
79
79
|
* 9. Adversarial input — input body and params validation
|
|
80
80
|
* 10. Adversarial 404 — stub 404 handlers, validate response bodies against declared schemas
|
|
@@ -15,7 +15,7 @@ import './assert_dev_env.js';
|
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
|
17
17
|
import { test, assert, describe } from 'vitest';
|
|
18
|
-
import { assert_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness,
|
|
18
|
+
import { assert_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness, default_error_schema_tightness, fuz_app_stock_route_tightness_allowlist, } from './surface_invariants.js';
|
|
19
19
|
import { describe_adversarial_input } from './adversarial_input.js';
|
|
20
20
|
import { describe_adversarial_404 } from './adversarial_404.js';
|
|
21
21
|
import { create_test_app_from_specs, create_test_request_context, create_auth_test_apps, select_auth_app, resolve_test_path, } from './auth_apps.js';
|
|
@@ -164,7 +164,7 @@ export const describe_adversarial_auth = (options) => {
|
|
|
164
164
|
// --- Standard attack surface test suite ---
|
|
165
165
|
/**
|
|
166
166
|
* Merge a consumer's `error_schema_tightness` option with
|
|
167
|
-
* `
|
|
167
|
+
* `default_error_schema_tightness` so `allowlist` and `ignore_statuses` are
|
|
168
168
|
* additive rather than replacing.
|
|
169
169
|
*
|
|
170
170
|
* - `undefined` → return the default as-is.
|
|
@@ -181,11 +181,11 @@ export const resolve_standard_error_schema_tightness = (consumer) => {
|
|
|
181
181
|
if (consumer === null)
|
|
182
182
|
return null;
|
|
183
183
|
return {
|
|
184
|
-
...
|
|
184
|
+
...default_error_schema_tightness,
|
|
185
185
|
...consumer,
|
|
186
|
-
allowlist: [...
|
|
186
|
+
allowlist: [...fuz_app_stock_route_tightness_allowlist, ...(consumer?.allowlist ?? [])],
|
|
187
187
|
ignore_statuses: [
|
|
188
|
-
...(
|
|
188
|
+
...(default_error_schema_tightness.ignore_statuses ?? []),
|
|
189
189
|
...(consumer?.ignore_statuses ?? []),
|
|
190
190
|
],
|
|
191
191
|
};
|
|
@@ -200,7 +200,7 @@ export const resolve_standard_error_schema_tightness = (consumer) => {
|
|
|
200
200
|
* 4. Middleware stack — every API route has the full middleware chain
|
|
201
201
|
* 5. Surface invariants — structural assertions (error schemas, descriptions, duplicates, consistency)
|
|
202
202
|
* 6. Security policy — rate limiting on sensitive routes, no unexpected public mutations, method conventions
|
|
203
|
-
* 7. Error schema tightness — informational log of generic vs specific error schemas, plus assertion against `
|
|
203
|
+
* 7. Error schema tightness — informational log of generic vs specific error schemas, plus assertion against `default_error_schema_tightness` by default (opt out with `error_schema_tightness: null`)
|
|
204
204
|
* 8. Adversarial auth — unauthenticated/wrong-role/correct-auth enforcement
|
|
205
205
|
* 9. Adversarial input — input body and params validation
|
|
206
206
|
* 10. Adversarial 404 — stub 404 handlers, validate response bodies against declared schemas
|
|
@@ -15,9 +15,9 @@ import './assert_dev_env.js';
|
|
|
15
15
|
import { describe, test, assert } from 'vitest';
|
|
16
16
|
import { ROLE_KEEPER, ROLE_ADMIN } from '../auth/role_schema.js';
|
|
17
17
|
import { AUDIT_EVENT_TYPES } from '../auth/audit_log_schema.js';
|
|
18
|
-
import {
|
|
18
|
+
import { auth_migration_ns } from '../auth/migrations.js';
|
|
19
19
|
import { create_test_app, } from './app_server.js';
|
|
20
|
-
import { create_pglite_factory, create_describe_db,
|
|
20
|
+
import { create_pglite_factory, create_describe_db, auth_integration_truncate_tables, } from './db.js';
|
|
21
21
|
import { find_auth_route } from './integration_helpers.js';
|
|
22
22
|
import { run_migrations } from '../db/migrate.js';
|
|
23
23
|
import { query_accept_offer } from '../auth/role_grant_offer_queries.js';
|
|
@@ -75,10 +75,10 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
75
75
|
const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
|
|
76
76
|
const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
|
|
77
77
|
const init_schema = async (db) => {
|
|
78
|
-
await run_migrations(db, [
|
|
78
|
+
await run_migrations(db, [auth_migration_ns]);
|
|
79
79
|
};
|
|
80
80
|
const factories = options.db_factories ?? [create_pglite_factory(init_schema)];
|
|
81
|
-
const describe_db = create_describe_db(factories,
|
|
81
|
+
const describe_db = create_describe_db(factories, auth_integration_truncate_tables);
|
|
82
82
|
describe_db('audit_log_completeness', (get_db) => {
|
|
83
83
|
// --- Account routes ---
|
|
84
84
|
describe('account mutation audit events', () => {
|
|
@@ -27,9 +27,9 @@ export interface DataExposureTestOptions {
|
|
|
27
27
|
session_options: SessionOptions<string>;
|
|
28
28
|
/** Route spec factory for runtime tests. */
|
|
29
29
|
create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
|
|
30
|
-
/** Fields that must never appear in any response. Default: `
|
|
30
|
+
/** Fields that must never appear in any response. Default: `sensitive_field_blocklist`. */
|
|
31
31
|
sensitive_fields?: ReadonlyArray<string>;
|
|
32
|
-
/** Fields that must not appear in non-admin responses. Default: `
|
|
32
|
+
/** Fields that must not appear in non-admin responses. Default: `admin_only_field_blocklist`. */
|
|
33
33
|
admin_only_fields?: ReadonlyArray<string>;
|
|
34
34
|
/** Optional overrides for `AppServerOptions`. */
|
|
35
35
|
app_options?: Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs'>>;
|