@fuzdev/fuz_app 0.30.0 → 0.32.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 +630 -0
- package/dist/actions/action_rpc.d.ts +29 -0
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +42 -6
- package/dist/actions/action_types.d.ts +2 -2
- package/dist/actions/cancel.d.ts +12 -13
- package/dist/actions/cancel.d.ts.map +1 -1
- package/dist/actions/cancel.js +10 -13
- package/dist/actions/heartbeat.d.ts +8 -13
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -8
- package/dist/actions/register_action_ws.d.ts +3 -3
- package/dist/actions/register_action_ws.js +2 -2
- package/dist/actions/register_ws_endpoint.d.ts +4 -4
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +3 -3
- package/dist/actions/rpc_client.d.ts +29 -0
- package/dist/actions/rpc_client.d.ts.map +1 -1
- package/dist/actions/rpc_client.js +31 -0
- package/dist/actions/socket.svelte.d.ts +16 -16
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +15 -15
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +945 -0
- package/dist/auth/account_action_specs.d.ts +216 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -0
- package/dist/auth/account_action_specs.js +159 -0
- package/dist/auth/account_actions.d.ts +51 -0
- package/dist/auth/account_actions.d.ts.map +1 -0
- package/dist/auth/account_actions.js +119 -0
- package/dist/auth/account_queries.d.ts +6 -2
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +40 -4
- package/dist/auth/account_routes.d.ts +94 -16
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +108 -180
- package/dist/auth/account_schema.d.ts +85 -30
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +40 -8
- package/dist/auth/admin_action_specs.d.ts +674 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -0
- package/dist/auth/admin_action_specs.js +287 -0
- package/dist/auth/admin_actions.d.ts +69 -0
- package/dist/auth/admin_actions.d.ts.map +1 -0
- package/dist/auth/admin_actions.js +256 -0
- package/dist/auth/admin_rpc_actions.d.ts +49 -0
- package/dist/auth/admin_rpc_actions.d.ts.map +1 -0
- package/dist/auth/admin_rpc_actions.js +32 -0
- package/dist/auth/api_token.d.ts +10 -0
- package/dist/auth/api_token.d.ts.map +1 -1
- package/dist/auth/api_token.js +9 -0
- package/dist/auth/api_token_queries.d.ts +3 -3
- package/dist/auth/api_token_queries.js +3 -3
- package/dist/auth/app_settings_schema.d.ts +4 -3
- package/dist/auth/app_settings_schema.d.ts.map +1 -1
- package/dist/auth/app_settings_schema.js +2 -1
- package/dist/auth/audit_log_routes.d.ts +14 -6
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +22 -79
- package/dist/auth/audit_log_schema.d.ts +100 -29
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +83 -11
- package/dist/auth/bootstrap_routes.d.ts +14 -0
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +10 -3
- package/dist/auth/cleanup.d.ts +63 -0
- package/dist/auth/cleanup.d.ts.map +1 -0
- package/dist/auth/cleanup.js +80 -0
- package/dist/auth/invite_schema.d.ts +11 -10
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +4 -3
- package/dist/auth/migrations.d.ts +6 -0
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +28 -0
- package/dist/auth/permit_offer_action_specs.d.ts +364 -0
- package/dist/auth/permit_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/permit_offer_action_specs.js +216 -0
- package/dist/auth/permit_offer_actions.d.ts +96 -0
- package/dist/auth/permit_offer_actions.d.ts.map +1 -0
- package/dist/auth/permit_offer_actions.js +428 -0
- package/dist/auth/permit_offer_notifications.d.ts +361 -0
- package/dist/auth/permit_offer_notifications.d.ts.map +1 -0
- package/dist/auth/permit_offer_notifications.js +179 -0
- package/dist/auth/permit_offer_queries.d.ts +165 -0
- package/dist/auth/permit_offer_queries.d.ts.map +1 -0
- package/dist/auth/permit_offer_queries.js +390 -0
- package/dist/auth/permit_offer_schema.d.ts +103 -0
- package/dist/auth/permit_offer_schema.d.ts.map +1 -0
- package/dist/auth/permit_offer_schema.js +142 -0
- package/dist/auth/permit_queries.d.ts +77 -14
- package/dist/auth/permit_queries.d.ts.map +1 -1
- package/dist/auth/permit_queries.js +119 -24
- package/dist/auth/session_queries.d.ts +4 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +4 -2
- package/dist/auth/signup_routes.d.ts +13 -0
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +14 -7
- package/dist/http/CLAUDE.md +584 -0
- package/dist/http/pending_effects.d.ts +29 -0
- package/dist/http/pending_effects.d.ts.map +1 -0
- package/dist/http/pending_effects.js +31 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +4 -3
- package/dist/rate_limiter.d.ts +30 -0
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +25 -2
- package/dist/realtime/sse_auth_guard.d.ts +2 -0
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +5 -3
- package/dist/server/app_server.d.ts +13 -2
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +12 -1
- package/dist/testing/CLAUDE.md +668 -1
- package/dist/testing/admin_integration.d.ts +10 -7
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +382 -482
- package/dist/testing/app_server.d.ts +7 -6
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/attack_surface.d.ts +9 -3
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +4 -4
- package/dist/testing/audit_completeness.d.ts +11 -0
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +169 -134
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +4 -33
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +2 -0
- package/dist/testing/entities.d.ts +35 -13
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +17 -0
- package/dist/testing/integration.d.ts +10 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +352 -340
- package/dist/testing/integration_helpers.d.ts +16 -5
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +24 -4
- package/dist/testing/rate_limiting.d.ts +7 -0
- package/dist/testing/rate_limiting.d.ts.map +1 -1
- package/dist/testing/rate_limiting.js +41 -10
- package/dist/testing/rpc_helpers.d.ts +153 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +184 -8
- package/dist/testing/sse_round_trip.d.ts +8 -0
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +10 -3
- package/dist/testing/standard.d.ts +9 -1
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +6 -2
- package/dist/testing/stubs.d.ts +10 -2
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +17 -2
- package/dist/testing/surface_invariants.d.ts +7 -3
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +5 -4
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +9 -38
- package/dist/ui/AccountSessions.svelte +8 -4
- package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAccounts.svelte +61 -33
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAuditLog.svelte +3 -2
- package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -1
- package/dist/ui/AdminInvites.svelte +3 -2
- package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +14 -9
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/AdminPermitHistory.svelte +3 -2
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSessions.svelte +29 -25
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +363 -0
- package/dist/ui/OpenSignupToggle.svelte +6 -3
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
- package/dist/ui/PermitOfferForm.svelte +141 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts +14 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/PermitOfferHistory.svelte +109 -0
- package/dist/ui/PermitOfferHistory.svelte.d.ts +11 -0
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/PermitOfferInbox.svelte +121 -0
- package/dist/ui/PermitOfferInbox.svelte.d.ts +12 -0
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/account_sessions_state.svelte.d.ts +53 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +39 -16
- package/dist/ui/admin_accounts_state.svelte.d.ts +118 -2
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +99 -23
- package/dist/ui/admin_invites_state.svelte.d.ts +47 -1
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.js +38 -26
- package/dist/ui/admin_rpc_adapters.d.ts +94 -0
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -0
- package/dist/ui/admin_rpc_adapters.js +100 -0
- package/dist/ui/admin_sessions_state.svelte.d.ts +26 -0
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_sessions_state.svelte.js +35 -21
- package/dist/ui/app_settings_state.svelte.d.ts +39 -0
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.js +34 -18
- package/dist/ui/audit_log_state.svelte.d.ts +40 -3
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +36 -42
- package/dist/ui/auth_state.svelte.d.ts +4 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +4 -1
- package/dist/ui/permit_offers_state.svelte.d.ts +125 -0
- package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/permit_offers_state.svelte.js +197 -0
- package/package.json +3 -3
- package/dist/auth/admin_routes.d.ts +0 -29
- package/dist/auth/admin_routes.d.ts.map +0 -1
- package/dist/auth/admin_routes.js +0 -226
- package/dist/auth/app_settings_routes.d.ts +0 -27
- package/dist/auth/app_settings_routes.d.ts.map +0 -1
- package/dist/auth/app_settings_routes.js +0 -66
- package/dist/auth/invite_routes.d.ts +0 -18
- package/dist/auth/invite_routes.d.ts.map +0 -1
- package/dist/auth/invite_routes.js +0 -129
|
@@ -18,6 +18,7 @@ import type { Db, DbType } from '../db/db.js';
|
|
|
18
18
|
import type { PasswordHashDeps } from '../auth/password.js';
|
|
19
19
|
import { type SessionOptions } from '../auth/session_cookie.js';
|
|
20
20
|
import type { AuditLogEvent } from '../auth/audit_log_schema.js';
|
|
21
|
+
import type { Uuid } from '../uuid.js';
|
|
21
22
|
import type { AppBackend } from '../server/app_backend.js';
|
|
22
23
|
import { type AppServerOptions, type AppServerContext } from '../server/app_server.js';
|
|
23
24
|
import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
|
|
@@ -52,11 +53,11 @@ export interface BootstrapTestAccountOptions {
|
|
|
52
53
|
*/
|
|
53
54
|
export declare const bootstrap_test_account: (options: BootstrapTestAccountOptions) => Promise<{
|
|
54
55
|
account: {
|
|
55
|
-
id:
|
|
56
|
+
id: Uuid;
|
|
56
57
|
username: string;
|
|
57
58
|
};
|
|
58
59
|
actor: {
|
|
59
|
-
id:
|
|
60
|
+
id: Uuid;
|
|
60
61
|
};
|
|
61
62
|
api_token: string;
|
|
62
63
|
session_cookie: string;
|
|
@@ -67,12 +68,12 @@ export declare const bootstrap_test_account: (options: BootstrapTestAccountOptio
|
|
|
67
68
|
export interface TestAppServer extends AppBackend {
|
|
68
69
|
/** The bootstrapped account. */
|
|
69
70
|
account: {
|
|
70
|
-
id:
|
|
71
|
+
id: Uuid;
|
|
71
72
|
username: string;
|
|
72
73
|
};
|
|
73
74
|
/** The actor linked to the account. */
|
|
74
75
|
actor: {
|
|
75
|
-
id:
|
|
76
|
+
id: Uuid;
|
|
76
77
|
};
|
|
77
78
|
/** Raw API token for Bearer auth. */
|
|
78
79
|
api_token: string;
|
|
@@ -125,11 +126,11 @@ export interface CreateTestAppOptions extends TestAppServerOptions {
|
|
|
125
126
|
*/
|
|
126
127
|
export interface TestAccount {
|
|
127
128
|
account: {
|
|
128
|
-
id:
|
|
129
|
+
id: Uuid;
|
|
129
130
|
username: string;
|
|
130
131
|
};
|
|
131
132
|
actor: {
|
|
132
|
-
id:
|
|
133
|
+
id: Uuid;
|
|
133
134
|
};
|
|
134
135
|
/** Signed session cookie value. */
|
|
135
136
|
session_cookie: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAK/B,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAE1E,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AAU1D,OAAO,EAA8B,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAG3F,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAEN,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,UAAU,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAUrD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,gBAIhC,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AASjD;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C,EAAE,EAAE,EAAE,CAAC;IACP,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GAClC,SAAS,2BAA2B,KAClC,OAAO,CAAC;IACV,OAAO,EAAE;QAAC,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,8 +36,14 @@ export interface StandardAttackSurfaceOptions {
|
|
|
36
36
|
api_path_prefix?: string;
|
|
37
37
|
/** Security policy configuration. Omit for sensible defaults. */
|
|
38
38
|
security_policy?: SurfaceSecurityPolicyOptions;
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Error schema tightness assertion config. Defaults to
|
|
41
|
+
* `DEFAULT_ERROR_SCHEMA_TIGHTNESS` (ignores 401/403/429,
|
|
42
|
+
* `min_specificity: 'enum'`). Pass a narrower config to extend the
|
|
43
|
+
* allowlist or tighten the threshold; pass `null` to skip the assertion
|
|
44
|
+
* and keep the audit log informational-only.
|
|
45
|
+
*/
|
|
46
|
+
error_schema_tightness?: ErrorSchemaTightnessOptions | null;
|
|
41
47
|
}
|
|
42
48
|
/**
|
|
43
49
|
* Run the standard attack surface test suite.
|
|
@@ -49,7 +55,7 @@ export interface StandardAttackSurfaceOptions {
|
|
|
49
55
|
* 4. Middleware stack — every API route has the full middleware chain
|
|
50
56
|
* 5. Surface invariants — structural assertions (error schemas, descriptions, duplicates, consistency)
|
|
51
57
|
* 6. Security policy — rate limiting on sensitive routes, no unexpected public mutations, method conventions
|
|
52
|
-
* 7. Error schema tightness
|
|
58
|
+
* 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`)
|
|
53
59
|
* 8. Adversarial auth — unauthenticated/wrong-role/correct-auth enforcement
|
|
54
60
|
* 9. Adversarial input — input body and params validation
|
|
55
61
|
* 10. Adversarial 404 — stub 404 handlers, validate response bodies against declared schemas
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,
|
|
1
|
+
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAMN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAoBjC,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsClF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IAkH3E,CAAC;AAIF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,gHAAgH;IAChH,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IAoEF,CAAC"}
|
|
@@ -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, } from './surface_invariants.js';
|
|
18
|
+
import { assert_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness, DEFAULT_ERROR_SCHEMA_TIGHTNESS, } 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';
|
|
@@ -167,7 +167,7 @@ export const describe_adversarial_auth = (options) => {
|
|
|
167
167
|
* 4. Middleware stack — every API route has the full middleware chain
|
|
168
168
|
* 5. Surface invariants — structural assertions (error schemas, descriptions, duplicates, consistency)
|
|
169
169
|
* 6. Security policy — rate limiting on sensitive routes, no unexpected public mutations, method conventions
|
|
170
|
-
* 7. Error schema tightness
|
|
170
|
+
* 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`)
|
|
171
171
|
* 8. Adversarial auth — unauthenticated/wrong-role/correct-auth enforcement
|
|
172
172
|
* 9. Adversarial input — input body and params validation
|
|
173
173
|
* 10. Adversarial 404 — stub 404 handlers, validate response bodies against declared schemas
|
|
@@ -178,7 +178,7 @@ export const describe_adversarial_auth = (options) => {
|
|
|
178
178
|
* @param options - the test configuration
|
|
179
179
|
*/
|
|
180
180
|
export const describe_standard_attack_surface_tests = (options) => {
|
|
181
|
-
const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy, error_schema_tightness, } = options;
|
|
181
|
+
const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy, error_schema_tightness = DEFAULT_ERROR_SCHEMA_TIGHTNESS, } = options;
|
|
182
182
|
const built = build();
|
|
183
183
|
const { surface } = built;
|
|
184
184
|
describe('attack surface snapshot', () => {
|
|
@@ -202,7 +202,7 @@ export const describe_standard_attack_surface_tests = (options) => {
|
|
|
202
202
|
test('security policy', () => {
|
|
203
203
|
assert_surface_security_policy(surface, security_policy);
|
|
204
204
|
});
|
|
205
|
-
test('error schema tightness
|
|
205
|
+
test('error schema tightness', () => {
|
|
206
206
|
const entries = audit_error_schema_tightness(surface);
|
|
207
207
|
const generic = entries.filter((e) => e.specificity === 'generic');
|
|
208
208
|
const literal = entries.filter((e) => e.specificity === 'literal');
|
|
@@ -3,6 +3,7 @@ import type { SessionOptions } from '../auth/session_cookie.js';
|
|
|
3
3
|
import type { AppServerContext, AppServerOptions } from '../server/app_server.js';
|
|
4
4
|
import type { RouteSpec } from '../http/route_spec.js';
|
|
5
5
|
import { type DbFactory } from './db.js';
|
|
6
|
+
import type { RpcEndpointSpec } from '../http/surface.js';
|
|
6
7
|
/**
|
|
7
8
|
* Configuration for `describe_audit_completeness_tests`.
|
|
8
9
|
*/
|
|
@@ -11,6 +12,16 @@ export interface AuditCompletenessTestOptions {
|
|
|
11
12
|
session_options: SessionOptions<string>;
|
|
12
13
|
/** Route spec factory — same one used in production. */
|
|
13
14
|
create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
|
|
15
|
+
/**
|
|
16
|
+
* RPC endpoint specs — the source `RpcAction` arrays. Required; the
|
|
17
|
+
* admin permit flow is RPC-only and the suite hard-fails without it.
|
|
18
|
+
*
|
|
19
|
+
* Accepts either an array (eager) or a factory
|
|
20
|
+
* `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
|
|
21
|
+
* is required when action handlers must close over the per-test
|
|
22
|
+
* `ctx.app_settings` / `ctx.deps` (e.g. exercising `app_settings_update`).
|
|
23
|
+
*/
|
|
24
|
+
rpc_endpoints: Array<RpcEndpointSpec> | ((ctx: AppServerContext) => Array<RpcEndpointSpec>);
|
|
14
25
|
/** Optional overrides for `AppServerOptions`. */
|
|
15
26
|
app_options?: Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs'>>;
|
|
16
27
|
/** Database factories to run tests against. Default: pglite only. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAMrD,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAOjB,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAsBxD;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;;;;OAQG;IACH,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAC5F,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAoDD;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IA2ezF,CAAC"}
|
|
@@ -20,11 +20,13 @@ import { create_test_app } from './app_server.js';
|
|
|
20
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
import { query_accept_offer } from '../auth/permit_offer_queries.js';
|
|
24
|
+
import { rpc_call, require_rpc_endpoint_path } from './rpc_helpers.js';
|
|
25
|
+
import { create_stub_app_server_context } from './stubs.js';
|
|
26
|
+
import { permit_offer_create_action_spec, permit_revoke_action_spec, } from '../auth/permit_offer_action_specs.js';
|
|
27
|
+
import { admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, app_settings_update_action_spec, invite_create_action_spec, invite_delete_action_spec, } from '../auth/admin_action_specs.js';
|
|
28
|
+
import { account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
|
|
29
|
+
import { query_actor_by_account } from '../auth/account_queries.js';
|
|
28
30
|
/** Query audit log events from the database. */
|
|
29
31
|
const query_audit_events = async (db) => {
|
|
30
32
|
return db.query('SELECT event_type, seq FROM audit_log ORDER BY seq');
|
|
@@ -39,7 +41,10 @@ const build_options = (options, db) => ({
|
|
|
39
41
|
create_route_specs: options.create_route_specs,
|
|
40
42
|
db,
|
|
41
43
|
roles: [ROLE_KEEPER, ROLE_ADMIN],
|
|
42
|
-
app_options:
|
|
44
|
+
app_options: {
|
|
45
|
+
rpc_endpoints: options.rpc_endpoints,
|
|
46
|
+
...options.app_options,
|
|
47
|
+
},
|
|
43
48
|
});
|
|
44
49
|
/** Headers for unauthenticated JSON requests (login, signup). */
|
|
45
50
|
const UNAUTHENTICATED_JSON_HEADERS = {
|
|
@@ -52,15 +57,6 @@ const json_session_headers = (test_app, extra) => test_app.create_session_header
|
|
|
52
57
|
'content-type': 'application/json',
|
|
53
58
|
...extra,
|
|
54
59
|
});
|
|
55
|
-
/**
|
|
56
|
-
* Find an account-scoped parameterized route (e.g. `/tokens/:id/revoke`).
|
|
57
|
-
*
|
|
58
|
-
* Matches routes with a `:id` or `:param` segment that are NOT admin role-gated.
|
|
59
|
-
*/
|
|
60
|
-
const find_account_parameterized_route = (specs, segment, suffix, method) => specs.find((s) => s.method === method &&
|
|
61
|
-
s.path.includes(`/${segment}/`) &&
|
|
62
|
-
s.path.endsWith(suffix) &&
|
|
63
|
-
s.auth.type !== 'role');
|
|
64
60
|
/**
|
|
65
61
|
* Composable audit log completeness test suite.
|
|
66
62
|
*
|
|
@@ -71,6 +67,19 @@ const find_account_parameterized_route = (specs, segment, suffix, method) => spe
|
|
|
71
67
|
* @param options - session config, route factory, and optional overrides
|
|
72
68
|
*/
|
|
73
69
|
export const describe_audit_completeness_tests = (options) => {
|
|
70
|
+
// Hard-fail early so consumers see a clear setup error instead of a
|
|
71
|
+
// confusing test failure when `rpc_endpoints` is missing. For the
|
|
72
|
+
// factory form we invoke the factory once here with a stub ctx purely
|
|
73
|
+
// to extract the endpoint path (a stable string like `/api/rpc`); the
|
|
74
|
+
// resulting actions array is discarded. `create_app_server` invokes
|
|
75
|
+
// the factory a second time inside each test with its real ctx, and
|
|
76
|
+
// those are the handlers that actually serve requests. Safe as long
|
|
77
|
+
// as the factory is pure — the stock helpers (e.g.
|
|
78
|
+
// `create_admin_rpc_actions`) are.
|
|
79
|
+
const rpc_endpoints_for_setup = typeof options.rpc_endpoints === 'function'
|
|
80
|
+
? options.rpc_endpoints(create_stub_app_server_context(options.session_options))
|
|
81
|
+
: options.rpc_endpoints;
|
|
82
|
+
const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
|
|
74
83
|
const init_schema = async (db) => {
|
|
75
84
|
await run_migrations(db, [AUTH_MIGRATION_NS]);
|
|
76
85
|
};
|
|
@@ -125,37 +134,39 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
125
134
|
});
|
|
126
135
|
test('token create produces token_create event', async () => {
|
|
127
136
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
method:
|
|
132
|
-
|
|
133
|
-
|
|
137
|
+
const res = await rpc_call({
|
|
138
|
+
app: test_app.app,
|
|
139
|
+
path: rpc_path,
|
|
140
|
+
method: account_token_create_action_spec.method,
|
|
141
|
+
params: { name: 'audit-test' },
|
|
142
|
+
headers: test_app.create_session_headers(),
|
|
134
143
|
});
|
|
135
|
-
assert.
|
|
144
|
+
assert.ok(res.ok, `account_token_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
136
145
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
137
|
-
assert_has_event(events, 'token_create', '
|
|
146
|
+
assert_has_event(events, 'token_create', 'account_token_create RPC');
|
|
138
147
|
});
|
|
139
148
|
test('token revoke produces token_revoke event', async () => {
|
|
140
149
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
141
150
|
// get a token ID to revoke
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
const list_res = await rpc_call({
|
|
152
|
+
app: test_app.app,
|
|
153
|
+
path: rpc_path,
|
|
154
|
+
method: account_token_list_action_spec.method,
|
|
145
155
|
headers: test_app.create_session_headers(),
|
|
146
156
|
});
|
|
147
|
-
|
|
157
|
+
assert.ok(list_res.ok, 'account_token_list should succeed');
|
|
158
|
+
const { tokens } = list_res.result;
|
|
148
159
|
assert.ok(tokens.length > 0, 'Expected at least one token');
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
const res = await rpc_call({
|
|
161
|
+
app: test_app.app,
|
|
162
|
+
path: rpc_path,
|
|
163
|
+
method: account_token_revoke_action_spec.method,
|
|
164
|
+
params: { token_id: tokens[0].id },
|
|
154
165
|
headers: test_app.create_session_headers(),
|
|
155
166
|
});
|
|
156
|
-
assert.
|
|
167
|
+
assert.ok(res.ok, `account_token_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
157
168
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
158
|
-
assert_has_event(events, 'token_revoke', '
|
|
169
|
+
assert_has_event(events, 'token_revoke', 'account_token_revoke RPC');
|
|
159
170
|
});
|
|
160
171
|
test('session revoke produces session_revoke event', async () => {
|
|
161
172
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
@@ -171,36 +182,38 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
171
182
|
}),
|
|
172
183
|
});
|
|
173
184
|
// get session IDs (newest first)
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
185
|
+
const list_res = await rpc_call({
|
|
186
|
+
app: test_app.app,
|
|
187
|
+
path: rpc_path,
|
|
188
|
+
method: account_session_list_action_spec.method,
|
|
177
189
|
headers: test_app.create_session_headers(),
|
|
178
190
|
});
|
|
179
|
-
|
|
191
|
+
assert.ok(list_res.ok, 'account_session_list should succeed');
|
|
192
|
+
const { sessions } = list_res.result;
|
|
180
193
|
assert.ok(sessions.length >= 2, 'Expected at least 2 sessions');
|
|
181
|
-
const route = find_account_parameterized_route(test_app.route_specs, 'sessions', '/revoke', 'POST');
|
|
182
|
-
assert.ok(route, 'Expected POST /sessions/:id/revoke route');
|
|
183
194
|
// revoke the second session (not the one used for auth)
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
195
|
+
const res = await rpc_call({
|
|
196
|
+
app: test_app.app,
|
|
197
|
+
path: rpc_path,
|
|
198
|
+
method: account_session_revoke_action_spec.method,
|
|
199
|
+
params: { session_id: sessions[1].id },
|
|
187
200
|
headers: test_app.create_session_headers(),
|
|
188
201
|
});
|
|
189
|
-
assert.
|
|
202
|
+
assert.ok(res.ok, `account_session_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
190
203
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
191
|
-
assert_has_event(events, 'session_revoke', '
|
|
204
|
+
assert_has_event(events, 'session_revoke', 'account_session_revoke RPC');
|
|
192
205
|
});
|
|
193
206
|
test('session revoke-all produces session_revoke_all event', async () => {
|
|
194
207
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
method:
|
|
208
|
+
const res = await rpc_call({
|
|
209
|
+
app: test_app.app,
|
|
210
|
+
path: rpc_path,
|
|
211
|
+
method: account_session_revoke_all_action_spec.method,
|
|
199
212
|
headers: test_app.create_session_headers(),
|
|
200
213
|
});
|
|
201
|
-
assert.
|
|
214
|
+
assert.ok(res.ok, `account_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
202
215
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
203
|
-
assert_has_event(events, 'session_revoke_all', '
|
|
216
|
+
assert_has_event(events, 'session_revoke_all', 'account_session_revoke_all RPC');
|
|
204
217
|
});
|
|
205
218
|
test('password change produces password_change event', async () => {
|
|
206
219
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
@@ -221,137 +234,144 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
221
234
|
});
|
|
222
235
|
// --- Admin routes ---
|
|
223
236
|
describe('admin mutation audit events', () => {
|
|
224
|
-
test('
|
|
237
|
+
test('admin offer (RPC) + accept produces permit_offer_create and permit_grant events', async () => {
|
|
225
238
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
226
|
-
const route = find_admin_route(test_app.route_specs, '/permits/grant', 'POST');
|
|
227
|
-
assert.ok(route, 'Expected admin POST /permits/grant route');
|
|
228
239
|
const target = await test_app.create_account({ username: 'audit_target' });
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
240
|
+
const offer_res = await rpc_call({
|
|
241
|
+
app: test_app.app,
|
|
242
|
+
path: rpc_path,
|
|
243
|
+
method: permit_offer_create_action_spec.method,
|
|
244
|
+
params: { to_account_id: target.account.id, role: ROLE_ADMIN },
|
|
245
|
+
headers: test_app.create_session_headers(),
|
|
234
246
|
});
|
|
235
|
-
assert.
|
|
236
|
-
const
|
|
237
|
-
|
|
247
|
+
assert.ok(offer_res.ok, `permit_offer_create failed: ${offer_res.ok ? '' : JSON.stringify(offer_res.error)}`);
|
|
248
|
+
const { offer } = offer_res.result;
|
|
249
|
+
// Admin offer emits `permit_offer_create` only — the permit doesn't
|
|
250
|
+
// exist yet. Drive the accept to confirm `permit_grant` fires on the
|
|
251
|
+
// downstream consent transition.
|
|
252
|
+
const events_after_offer = await query_audit_events(test_app.backend.deps.db);
|
|
253
|
+
assert_has_event(events_after_offer, 'permit_offer_create', 'permit_offer_create RPC');
|
|
254
|
+
await get_db().transaction(async (tx) => {
|
|
255
|
+
await query_accept_offer({ db: tx }, { offer_id: offer.id, to_account_id: target.account.id, ip: null });
|
|
256
|
+
});
|
|
257
|
+
const events_after_accept = await query_audit_events(test_app.backend.deps.db);
|
|
258
|
+
assert_has_event(events_after_accept, 'permit_grant', 'offer accept');
|
|
238
259
|
});
|
|
239
|
-
test('permit revoke produces permit_revoke event', async () => {
|
|
260
|
+
test('permit revoke (RPC) produces permit_revoke event', async () => {
|
|
240
261
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
241
|
-
const grant_route = find_admin_route(test_app.route_specs, '/permits/grant', 'POST');
|
|
242
|
-
const revoke_route = test_app.route_specs.find((s) => s.method === 'POST' &&
|
|
243
|
-
s.path.includes('/permits/') &&
|
|
244
|
-
s.path.endsWith('/revoke') &&
|
|
245
|
-
s.auth.type === 'role');
|
|
246
|
-
assert.ok(grant_route, 'Expected admin POST /permits/grant route');
|
|
247
|
-
assert.ok(revoke_route, 'Expected admin POST /permits/:permit_id/revoke route');
|
|
248
262
|
const target = await test_app.create_account({ username: 'audit_revoke_target' });
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
263
|
+
const target_actor = await query_actor_by_account({ db: get_db() }, target.account.id);
|
|
264
|
+
assert.ok(target_actor);
|
|
265
|
+
// Offer + accept to materialize a permit we can revoke.
|
|
266
|
+
const offer_res = await rpc_call({
|
|
267
|
+
app: test_app.app,
|
|
268
|
+
path: rpc_path,
|
|
269
|
+
method: permit_offer_create_action_spec.method,
|
|
270
|
+
params: { to_account_id: target.account.id, role: ROLE_ADMIN },
|
|
271
|
+
headers: test_app.create_session_headers(),
|
|
255
272
|
});
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
273
|
+
assert.ok(offer_res.ok, `permit_offer_create failed: ${offer_res.ok ? '' : JSON.stringify(offer_res.error)}`);
|
|
274
|
+
const { offer } = offer_res.result;
|
|
275
|
+
const accept_result = await get_db().transaction(async (tx) => {
|
|
276
|
+
return query_accept_offer({ db: tx }, { offer_id: offer.id, to_account_id: target.account.id, ip: null });
|
|
277
|
+
});
|
|
278
|
+
// Revoke via RPC.
|
|
279
|
+
const revoke_res = await rpc_call({
|
|
280
|
+
app: test_app.app,
|
|
281
|
+
path: rpc_path,
|
|
282
|
+
method: permit_revoke_action_spec.method,
|
|
283
|
+
params: { actor_id: target_actor.id, permit_id: accept_result.permit.id },
|
|
263
284
|
headers: test_app.create_session_headers(),
|
|
264
285
|
});
|
|
265
|
-
assert.
|
|
286
|
+
assert.ok(revoke_res.ok, `permit_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
|
|
266
287
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
267
|
-
assert_has_event(events, 'permit_revoke', '
|
|
288
|
+
assert_has_event(events, 'permit_revoke', 'permit_revoke RPC');
|
|
268
289
|
});
|
|
269
290
|
test('admin session revoke-all produces session_revoke_all event', async () => {
|
|
270
291
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
271
|
-
const route = find_admin_route(test_app.route_specs, '/sessions/revoke-all', 'POST');
|
|
272
|
-
assert.ok(route, 'Expected admin POST /sessions/revoke-all route');
|
|
273
292
|
const target = await test_app.create_account({ username: 'audit_sessions_target' });
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
293
|
+
const res = await rpc_call({
|
|
294
|
+
app: test_app.app,
|
|
295
|
+
path: rpc_path,
|
|
296
|
+
method: admin_session_revoke_all_action_spec.method,
|
|
297
|
+
params: { account_id: target.account.id },
|
|
277
298
|
headers: test_app.create_session_headers(),
|
|
278
299
|
});
|
|
279
|
-
assert.
|
|
300
|
+
assert.ok(res.ok, `admin_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
280
301
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
281
302
|
// admin session revoke-all also produces session_revoke_all
|
|
282
|
-
assert_has_event(events, 'session_revoke_all', '
|
|
303
|
+
assert_has_event(events, 'session_revoke_all', 'admin_session_revoke_all RPC');
|
|
283
304
|
});
|
|
284
305
|
test('admin token revoke-all produces token_revoke_all event', async () => {
|
|
285
306
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
286
|
-
const route = find_admin_route(test_app.route_specs, '/tokens/revoke-all', 'POST');
|
|
287
|
-
assert.ok(route, 'Expected admin POST /tokens/revoke-all route');
|
|
288
307
|
const target = await test_app.create_account({ username: 'audit_tokens_target' });
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
308
|
+
const res = await rpc_call({
|
|
309
|
+
app: test_app.app,
|
|
310
|
+
path: rpc_path,
|
|
311
|
+
method: admin_token_revoke_all_action_spec.method,
|
|
312
|
+
params: { account_id: target.account.id },
|
|
292
313
|
headers: test_app.create_session_headers(),
|
|
293
314
|
});
|
|
294
|
-
assert.
|
|
315
|
+
assert.ok(res.ok, `admin_token_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
295
316
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
296
|
-
assert_has_event(events, 'token_revoke_all', '
|
|
317
|
+
assert_has_event(events, 'token_revoke_all', 'admin_token_revoke_all RPC');
|
|
297
318
|
});
|
|
298
319
|
});
|
|
299
|
-
// --- Invite
|
|
320
|
+
// --- Invite RPC actions ---
|
|
300
321
|
describe('invite mutation audit events', () => {
|
|
301
322
|
test('invite create and delete produce audit events', async () => {
|
|
302
323
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
method: 'POST',
|
|
310
|
-
headers: json_session_headers(test_app),
|
|
311
|
-
body: JSON.stringify({ username: 'invited_user' }),
|
|
324
|
+
const create_res = await rpc_call({
|
|
325
|
+
app: test_app.app,
|
|
326
|
+
path: rpc_path,
|
|
327
|
+
method: invite_create_action_spec.method,
|
|
328
|
+
params: { username: 'invited_user' },
|
|
329
|
+
headers: test_app.create_session_headers(),
|
|
312
330
|
});
|
|
313
|
-
assert.
|
|
314
|
-
const { invite } =
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
method:
|
|
331
|
+
assert.ok(create_res.ok, `invite_create failed: ${create_res.ok ? '' : JSON.stringify(create_res.error)}`);
|
|
332
|
+
const { invite } = create_res.result;
|
|
333
|
+
const delete_res = await rpc_call({
|
|
334
|
+
app: test_app.app,
|
|
335
|
+
path: rpc_path,
|
|
336
|
+
method: invite_delete_action_spec.method,
|
|
337
|
+
params: { invite_id: invite.id },
|
|
319
338
|
headers: test_app.create_session_headers(),
|
|
320
339
|
});
|
|
321
|
-
assert.
|
|
340
|
+
assert.ok(delete_res.ok, `invite_delete failed: ${delete_res.ok ? '' : JSON.stringify(delete_res.error)}`);
|
|
322
341
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
323
|
-
assert_has_event(events, 'invite_create', '
|
|
324
|
-
assert_has_event(events, 'invite_delete', '
|
|
342
|
+
assert_has_event(events, 'invite_create', 'invite_create RPC');
|
|
343
|
+
assert_has_event(events, 'invite_delete', 'invite_delete RPC');
|
|
325
344
|
});
|
|
326
345
|
});
|
|
327
|
-
// --- App settings
|
|
346
|
+
// --- App settings RPC action ---
|
|
328
347
|
describe('app settings mutation audit events', () => {
|
|
329
348
|
test('settings update produces app_settings_update event', async () => {
|
|
330
349
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
method:
|
|
335
|
-
|
|
336
|
-
|
|
350
|
+
const res = await rpc_call({
|
|
351
|
+
app: test_app.app,
|
|
352
|
+
path: rpc_path,
|
|
353
|
+
method: app_settings_update_action_spec.method,
|
|
354
|
+
params: { open_signup: true },
|
|
355
|
+
headers: test_app.create_session_headers(),
|
|
337
356
|
});
|
|
338
|
-
assert.
|
|
357
|
+
assert.ok(res.ok, `app_settings_update failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
339
358
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
340
|
-
assert_has_event(events, 'app_settings_update', '
|
|
359
|
+
assert_has_event(events, 'app_settings_update', 'app_settings_update RPC');
|
|
341
360
|
});
|
|
342
361
|
});
|
|
343
362
|
// --- Signup route ---
|
|
344
363
|
describe('signup audit events', () => {
|
|
345
364
|
test('signup produces signup event', async () => {
|
|
346
365
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
347
|
-
// enable open signup
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
method:
|
|
352
|
-
|
|
353
|
-
|
|
366
|
+
// enable open signup via RPC
|
|
367
|
+
const settings_res = await rpc_call({
|
|
368
|
+
app: test_app.app,
|
|
369
|
+
path: rpc_path,
|
|
370
|
+
method: app_settings_update_action_spec.method,
|
|
371
|
+
params: { open_signup: true },
|
|
372
|
+
headers: test_app.create_session_headers(),
|
|
354
373
|
});
|
|
374
|
+
assert.ok(settings_res.ok, `app_settings_update failed: ${settings_res.ok ? '' : JSON.stringify(settings_res.error)}`);
|
|
355
375
|
// signup
|
|
356
376
|
const signup_route = find_auth_route(test_app.route_specs, '/signup', 'POST');
|
|
357
377
|
assert.ok(signup_route, 'Expected POST /signup route');
|
|
@@ -384,6 +404,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
384
404
|
'token_create',
|
|
385
405
|
'token_revoke',
|
|
386
406
|
'token_revoke_all',
|
|
407
|
+
'permit_offer_create',
|
|
387
408
|
'permit_grant',
|
|
388
409
|
'permit_revoke',
|
|
389
410
|
'invite_create',
|
|
@@ -393,6 +414,20 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
393
414
|
/** Event types excluded with justification. */
|
|
394
415
|
const EXCLUDED_EVENT_TYPES = new Set([
|
|
395
416
|
'bootstrap', // requires filesystem token — tested in bootstrap_account.db.test.ts
|
|
417
|
+
// The remaining `permit_offer_*` events fire only via the RPC
|
|
418
|
+
// endpoint or via downstream effects of `permit_revoke`. Direct
|
|
419
|
+
// coverage lives in `permit_offer_queries.db.test.ts`,
|
|
420
|
+
// `permit_offer_actions.db.test.ts`,
|
|
421
|
+
// `permit_offer_actions.notifications.db.test.ts`, and
|
|
422
|
+
// `permit_offer_actions.notifications.revoke.db.test.ts`.
|
|
423
|
+
// `permit_offer_expire` fires from the cleanup sweep
|
|
424
|
+
// (`cleanup_expired_permit_offers` in `auth/cleanup.ts`) —
|
|
425
|
+
// covered in `cleanup.db.test.ts`.
|
|
426
|
+
'permit_offer_accept',
|
|
427
|
+
'permit_offer_decline',
|
|
428
|
+
'permit_offer_retract',
|
|
429
|
+
'permit_offer_expire',
|
|
430
|
+
'permit_offer_supersede',
|
|
396
431
|
]);
|
|
397
432
|
test('all audit event types are covered or explicitly excluded', () => {
|
|
398
433
|
const all_covered = new Set([...COVERED_EVENT_TYPES, ...EXCLUDED_EVENT_TYPES]);
|