@fuzdev/fuz_app 0.147.0 → 0.148.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 +6 -4
- package/dist/actions/action_codegen.d.ts +15 -1
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +23 -2
- package/dist/actions/perform_action.js +1 -1
- package/dist/auth/cell_action_specs.d.ts +3 -1
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +5 -5
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +2 -3
- package/dist/auth/cell_data_schema.d.ts +1 -1
- package/dist/auth/cell_data_schema.js +1 -1
- package/dist/auth/cell_field_action_specs.d.ts +4 -3
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_field_action_specs.js +5 -6
- package/dist/auth/cell_field_actions.d.ts.map +1 -1
- package/dist/auth/cell_field_actions.js +8 -1
- package/dist/auth/cell_item_action_specs.d.ts +4 -2
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_item_action_specs.js +5 -5
- package/dist/auth/cell_item_actions.d.ts.map +1 -1
- package/dist/auth/cell_item_actions.js +8 -1
- package/dist/auth/role_grant_offer_action_specs.d.ts +2 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +3 -1
- package/dist/primitive_schemas.d.ts +1 -1
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +2 -1
- package/dist/schema_meta.d.ts +20 -0
- package/dist/schema_meta.d.ts.map +1 -1
- package/dist/schema_meta.js +7 -1
- package/dist/testing/CLAUDE.md +720 -114
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +73 -5
- package/dist/testing/adversarial_input.d.ts +0 -47
- package/dist/testing/adversarial_input.d.ts.map +1 -1
- package/dist/testing/adversarial_input.js +4 -340
- package/dist/testing/adversarial_input_cases.d.ts +64 -0
- package/dist/testing/adversarial_input_cases.d.ts.map +1 -0
- package/dist/testing/adversarial_input_cases.js +348 -0
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +24 -20
- package/dist/testing/audit_drift_guard.d.ts +27 -1
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +50 -0
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +29 -12
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
- package/dist/testing/cross_backend/actor_lookup.js +7 -25
- package/dist/testing/cross_backend/app_settings.d.ts.map +1 -1
- package/dist/testing/cross_backend/app_settings.js +4 -6
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts +51 -0
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/audit_cross_helpers.js +78 -0
- package/dist/testing/cross_backend/backend_config.d.ts +2 -1
- package/dist/testing/cross_backend/backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.d.ts +6 -0
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +26 -44
- package/dist/testing/cross_backend/body_size_smuggling.d.ts +5 -0
- package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size_smuggling.js +7 -14
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts +3 -1
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.d.ts +55 -59
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +20 -23
- package/dist/testing/cross_backend/cargo_binary.d.ts +70 -0
- package/dist/testing/cross_backend/cargo_binary.d.ts.map +1 -0
- package/dist/testing/cross_backend/cargo_binary.js +148 -0
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +57 -1
- package/dist/testing/cross_backend/cell_gated_create.d.ts +0 -60
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +9 -487
- package/dist/testing/cross_backend/cell_grant_role.d.ts +14 -2
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_grant_role.js +22 -12
- package/dist/testing/cross_backend/cell_moderate.d.ts +66 -0
- package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_moderate.js +511 -0
- package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_ref_admission.js +1 -1
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.js +40 -19
- package/dist/testing/cross_backend/conformance_case.d.ts +43 -4
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_case.js +29 -1
- package/dist/testing/cross_backend/conformance_rpc_auth.d.ts +93 -0
- package/dist/testing/cross_backend/conformance_rpc_auth.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_rpc_auth.js +125 -0
- package/dist/testing/cross_backend/conformance_table.d.ts +144 -4
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +297 -65
- package/dist/testing/cross_backend/cookie_attributes.d.ts.map +1 -1
- package/dist/testing/cross_backend/cookie_attributes.js +3 -2
- package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +38 -5
- package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/create_cross_backend_global_setup.js +24 -3
- package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts +5 -5
- package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/credential_deadline.d.ts +10 -6
- package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -1
- package/dist/testing/cross_backend/credential_deadline.js +32 -31
- package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
- package/dist/testing/cross_backend/credential_header_robustness.js +4 -6
- package/dist/testing/cross_backend/{cell_cross_helpers.d.ts → cross_rpc.d.ts} +12 -2
- package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -0
- package/dist/testing/cross_backend/{cell_cross_helpers.js → cross_rpc.js} +31 -7
- package/dist/testing/cross_backend/default_backend_configs.d.ts +56 -61
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +97 -91
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +2 -2
- package/dist/testing/cross_backend/differential.d.ts +126 -0
- package/dist/testing/cross_backend/differential.d.ts.map +1 -0
- package/dist/testing/cross_backend/differential.js +201 -0
- package/dist/testing/cross_backend/differential_normalize.d.ts +150 -0
- package/dist/testing/cross_backend/differential_normalize.d.ts.map +1 -0
- package/dist/testing/cross_backend/differential_normalize.js +153 -0
- package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
- package/dist/testing/cross_backend/fact_serving.js +27 -17
- package/dist/testing/cross_backend/full_spine_mount.d.ts +15 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +55 -1
- package/dist/testing/cross_backend/identity_parity.d.ts.map +1 -1
- package/dist/testing/cross_backend/identity_parity.js +6 -5
- package/dist/testing/cross_backend/in_process_setup.d.ts +12 -2
- package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/in_process_setup.js +10 -7
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
- package/dist/testing/cross_backend/login_security.js +13 -18
- package/dist/testing/cross_backend/origin.js +1 -1
- package/dist/testing/cross_backend/origin_conformance.js +1 -1
- package/dist/testing/cross_backend/origin_scope.d.ts +6 -0
- package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin_scope.js +13 -12
- package/dist/testing/cross_backend/peer_ping_ws.d.ts +2 -5
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.js +14 -24
- package/dist/testing/cross_backend/postgres_database.d.ts +85 -0
- package/dist/testing/cross_backend/postgres_database.d.ts.map +1 -0
- package/dist/testing/cross_backend/postgres_database.js +140 -0
- package/dist/testing/cross_backend/provided_context.d.ts +28 -0
- package/dist/testing/cross_backend/provided_context.d.ts.map +1 -0
- package/dist/testing/cross_backend/provided_context.js +35 -0
- package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/query_shape.js +10 -12
- package/dist/testing/cross_backend/ready.js +2 -2
- package/dist/testing/cross_backend/revocation_drivers.d.ts +4 -3
- package/dist/testing/cross_backend/revocation_drivers.d.ts.map +1 -1
- package/dist/testing/cross_backend/revocation_drivers.js +1 -1
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +3 -2
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts +13 -0
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -0
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +203 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +2 -5
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +203 -111
- package/dist/testing/cross_backend/role_grant_participation.d.ts +13 -0
- package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +32 -21
- package/dist/testing/cross_backend/role_grant_role_grammar.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_role_grammar.js +6 -9
- package/dist/testing/cross_backend/rpc_adversarial.d.ts +38 -0
- package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -0
- package/dist/testing/cross_backend/rpc_adversarial.js +292 -0
- package/dist/testing/cross_backend/rpc_success_fixtures.d.ts +24 -0
- package/dist/testing/cross_backend/rpc_success_fixtures.d.ts.map +1 -0
- package/dist/testing/cross_backend/rpc_success_fixtures.js +124 -0
- package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +101 -0
- package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts.map +1 -0
- package/dist/testing/cross_backend/rust_spine_consumer_surface.js +113 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +10 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +27 -18
- package/dist/testing/cross_backend/session_cap.d.ts +9 -8
- package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
- package/dist/testing/cross_backend/session_cap.js +9 -4
- package/dist/testing/cross_backend/session_cookie_codec.js +1 -1
- package/dist/testing/cross_backend/setup.d.ts +56 -54
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +29 -29
- package/dist/testing/cross_backend/simple_request.d.ts.map +1 -1
- package/dist/testing/cross_backend/simple_request.js +4 -4
- package/dist/testing/cross_backend/spawn_backend.d.ts +59 -10
- package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -1
- package/dist/testing/cross_backend/spawn_backend.js +219 -60
- package/dist/testing/cross_backend/spine_suites.d.ts +231 -0
- package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -0
- package/dist/testing/cross_backend/spine_suites.js +626 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +29 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +29 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/sse_round_trip.js +3 -4
- package/dist/testing/cross_backend/standard.d.ts +18 -1
- package/dist/testing/cross_backend/standard.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.js +2 -0
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +24 -0
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +15 -0
- package/dist/testing/cross_backend/testing_backdoor.d.ts +2 -8
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +7 -10
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +3 -2
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +8 -4
- package/dist/testing/cross_backend/testing_server_bun.js +1 -1
- package/dist/testing/cross_backend/token_lifetime.js +1 -1
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +9 -13
- package/dist/testing/cross_backend/transport_composition.d.ts +26 -2
- package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -1
- package/dist/testing/cross_backend/transport_composition.js +81 -51
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +8 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +17 -10
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +2 -5
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.js +8 -14
- package/dist/testing/cross_backend/ws_connection_cap.d.ts +4 -9
- package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_connection_cap.js +11 -12
- package/dist/testing/cross_backend/ws_endpoint.d.ts +52 -0
- package/dist/testing/cross_backend/ws_endpoint.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_endpoint.js +54 -0
- package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts +2 -5
- package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_inbound_dispatch.js +9 -15
- package/dist/testing/cross_backend/ws_outbound_overflow.d.ts +2 -7
- package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_outbound_overflow.js +5 -9
- package/dist/testing/cross_backend/ws_role_gated.d.ts +4 -5
- package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_role_gated.js +12 -21
- package/dist/testing/cross_backend/ws_round_trip.d.ts +12 -9
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +29 -34
- package/dist/testing/db_entities.d.ts +16 -3
- package/dist/testing/db_entities.d.ts.map +1 -1
- package/dist/testing/db_entities.js +28 -5
- package/dist/testing/integration_helpers.d.ts +61 -2
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +75 -18
- package/dist/testing/round_trip.d.ts +17 -0
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +67 -24
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +13 -138
- package/dist/testing/rpc_envelope_cases.d.ts +111 -0
- package/dist/testing/rpc_envelope_cases.d.ts.map +1 -0
- package/dist/testing/rpc_envelope_cases.js +240 -0
- package/dist/testing/rpc_helpers.d.ts +31 -0
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +35 -14
- package/dist/testing/rpc_round_trip.d.ts +62 -6
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +151 -39
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +22 -2
- package/dist/testing/standard.d.ts +26 -0
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +4 -1
- package/dist/testing/transports/raw_http.d.ts +21 -0
- package/dist/testing/transports/raw_http.d.ts.map +1 -1
- package/dist/testing/transports/raw_http.js +30 -0
- package/package.json +6 -4
- package/dist/testing/cross_backend/actor_search.d.ts +0 -6
- package/dist/testing/cross_backend/actor_search.d.ts.map +0 -1
- package/dist/testing/cross_backend/actor_search.js +0 -90
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW3E,OAAO,
|
|
1
|
+
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW3E,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA4B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAavE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAw+BF,CAAC"}
|
|
@@ -32,10 +32,11 @@ import { DEFAULT_TEST_PASSWORD } from './test_credentials.js';
|
|
|
32
32
|
import { role_grant_offer_and_accept } from './role_grant_helpers.js';
|
|
33
33
|
import { find_auth_route } from './integration_helpers.js';
|
|
34
34
|
import { ErrorCoverageCollector, assert_error_coverage, DEFAULT_INTEGRATION_ERROR_COVERAGE } from './error_coverage.js';
|
|
35
|
-
import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup } from './rpc_helpers.js';
|
|
35
|
+
import { find_rpc_action, rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup } from './rpc_helpers.js';
|
|
36
36
|
import { role_grant_offer_create_action_spec, role_grant_revoke_action_spec } from '../auth/role_grant_offer_action_specs.js';
|
|
37
|
-
import { admin_account_list_action_spec, ADMIN_ACCOUNT_LIST_LIMIT_MAX, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_role_grant_history_action_spec } from '../auth/admin_action_specs.js';
|
|
38
|
-
import { account_token_create_action_spec, account_verify_action_spec } from '../auth/account_action_specs.js';
|
|
37
|
+
import { admin_account_list_action_spec, ADMIN_ACCOUNT_LIST_LIMIT_MAX, app_settings_get_action_spec, app_settings_update_action_spec, AUDIT_LOG_LIST_LIMIT_MAX, invite_create_action_spec, invite_delete_action_spec, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_role_grant_history_action_spec } from '../auth/admin_action_specs.js';
|
|
38
|
+
import { account_session_list_action_spec, account_session_revoke_action_spec, account_token_create_action_spec, account_token_revoke_action_spec, account_verify_action_spec } from '../auth/account_action_specs.js';
|
|
39
|
+
import { assert_audit_event_metadata } from './audit_drift_guard.js';
|
|
39
40
|
import { to_route_content_type } from './schema_generators.js';
|
|
40
41
|
/**
|
|
41
42
|
* How long a failure-audit read waits for the pool-routed row to land.
|
|
@@ -151,6 +152,11 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
151
152
|
headers: fixture.create_session_headers()
|
|
152
153
|
});
|
|
153
154
|
assert.ok(audit_res.ok, 'audit_log_list should succeed');
|
|
155
|
+
for (const event of audit_res.result.events) {
|
|
156
|
+
assert_audit_event_metadata(event, {
|
|
157
|
+
reject_unknown_keys: fixture.reject_unknown_audit_metadata_keys ?? false
|
|
158
|
+
});
|
|
159
|
+
}
|
|
154
160
|
const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
|
|
155
161
|
if (failure || Date.now() >= deadline)
|
|
156
162
|
return failure;
|
|
@@ -632,6 +638,58 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
632
638
|
headers: fixture.create_session_headers()
|
|
633
639
|
});
|
|
634
640
|
assert.ok(token_res.ok, `account_token_create failed: ${token_res.ok ? '' : JSON.stringify(token_res.error)}`);
|
|
641
|
+
/** Call `spec` on the keeper's session, asserting success. */
|
|
642
|
+
const call_ok = async (spec, params, headers = fixture.create_session_headers()) => {
|
|
643
|
+
const res = await rpc_call_for_spec({
|
|
644
|
+
app: { request: fixture.transport },
|
|
645
|
+
path: rpc_path,
|
|
646
|
+
spec,
|
|
647
|
+
params,
|
|
648
|
+
headers
|
|
649
|
+
});
|
|
650
|
+
if (!res.ok)
|
|
651
|
+
assert.fail(`${spec.method} failed: ${JSON.stringify(res.error)}`);
|
|
652
|
+
return res.result;
|
|
653
|
+
};
|
|
654
|
+
// Steps 5a–5d each drive actions a consumer surface may not mount;
|
|
655
|
+
// a step whose actions are missing is skipped, and its event isn't
|
|
656
|
+
// expected.
|
|
657
|
+
const mounted = (...specs) => specs.every((spec) => find_rpc_action(rpc_endpoints_for_setup, spec.method));
|
|
658
|
+
const driven_types = [];
|
|
659
|
+
// 5a. revoke that token (RPC)
|
|
660
|
+
if (mounted(account_token_revoke_action_spec)) {
|
|
661
|
+
const token_revoked = await call_ok(account_token_revoke_action_spec, {
|
|
662
|
+
token_id: token_res.result.id
|
|
663
|
+
});
|
|
664
|
+
assert.strictEqual(token_revoked.revoked, true);
|
|
665
|
+
driven_types.push('token_revoke');
|
|
666
|
+
}
|
|
667
|
+
// 5b. user_two revokes its own session — the last use of that session
|
|
668
|
+
if (mounted(account_session_list_action_spec, account_session_revoke_action_spec)) {
|
|
669
|
+
const user_two_headers = user_two.create_session_headers();
|
|
670
|
+
const { sessions } = await call_ok(account_session_list_action_spec, undefined, user_two_headers);
|
|
671
|
+
assert.ok(sessions[0], 'user_two has a session to revoke');
|
|
672
|
+
const session_revoked = await call_ok(account_session_revoke_action_spec, { session_id: sessions[0].id }, user_two_headers);
|
|
673
|
+
assert.strictEqual(session_revoked.revoked, true);
|
|
674
|
+
driven_types.push('session_revoke');
|
|
675
|
+
}
|
|
676
|
+
// 5c. an invite created and deleted unclaimed
|
|
677
|
+
if (mounted(invite_create_action_spec, invite_delete_action_spec)) {
|
|
678
|
+
const { invite } = await call_ok(invite_create_action_spec, {
|
|
679
|
+
email: 'audit_invitee@example.com',
|
|
680
|
+
username: 'audit_invitee'
|
|
681
|
+
});
|
|
682
|
+
await call_ok(invite_delete_action_spec, { invite_id: invite.id });
|
|
683
|
+
driven_types.push('invite_create', 'invite_delete');
|
|
684
|
+
}
|
|
685
|
+
// 5d. open signup flipped and restored
|
|
686
|
+
if (mounted(app_settings_get_action_spec, app_settings_update_action_spec)) {
|
|
687
|
+
const { settings } = await call_ok(app_settings_get_action_spec, {});
|
|
688
|
+
for (const open_signup of [!settings.open_signup, settings.open_signup]) {
|
|
689
|
+
await call_ok(app_settings_update_action_spec, { open_signup });
|
|
690
|
+
}
|
|
691
|
+
driven_types.push('app_settings_update');
|
|
692
|
+
}
|
|
635
693
|
// 6. password change
|
|
636
694
|
await fixture.transport(password_route.path, {
|
|
637
695
|
method: 'POST',
|
|
@@ -670,14 +728,23 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
670
728
|
app: { request: fixture.transport },
|
|
671
729
|
path: rpc_path,
|
|
672
730
|
spec: audit_log_list_action_spec,
|
|
673
|
-
params: {},
|
|
731
|
+
params: { limit: AUDIT_LOG_LIST_LIMIT_MAX },
|
|
674
732
|
headers: relogin_headers
|
|
675
733
|
});
|
|
676
734
|
assert.ok(audit_res.ok, `audit_log_list failed: ${audit_res.ok ? '' : JSON.stringify(audit_res.error)}`);
|
|
677
735
|
const events = audit_res.result.events;
|
|
736
|
+
// every row's metadata holds to its event type's schema — the
|
|
737
|
+
// wire leaves it a loose record
|
|
738
|
+
for (const event of events) {
|
|
739
|
+
assert_audit_event_metadata(event, {
|
|
740
|
+
reject_unknown_keys: fixture.reject_unknown_audit_metadata_keys ?? false
|
|
741
|
+
});
|
|
742
|
+
}
|
|
678
743
|
// check that each operation produced at least one event.
|
|
679
744
|
// `role_grant_offer_create` fires on the admin RPC; `role_grant_create`
|
|
680
745
|
// fires when the recipient accepts (driven by offer_and_accept).
|
|
746
|
+
// No `signup` is expected: in-process, `create_account` inserts
|
|
747
|
+
// user_two directly, writing no audit row.
|
|
681
748
|
const expected_types = [
|
|
682
749
|
'login',
|
|
683
750
|
'logout',
|
|
@@ -686,7 +753,8 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
686
753
|
'role_grant_create',
|
|
687
754
|
'role_grant_revoke',
|
|
688
755
|
'token_create',
|
|
689
|
-
'password_change'
|
|
756
|
+
'password_change',
|
|
757
|
+
...driven_types
|
|
690
758
|
];
|
|
691
759
|
for (const event_type of expected_types) {
|
|
692
760
|
const found = events.filter((e) => e.event_type === event_type);
|
|
@@ -1,51 +1,5 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { ERROR_INVALID_REQUEST_BODY, ERROR_INVALID_JSON_BODY } from '../http/error_schemas.js';
|
|
4
2
|
import type { AdversarialTestOptions } from './attack_surface.js';
|
|
5
|
-
interface InputTestCase {
|
|
6
|
-
label: string;
|
|
7
|
-
body: unknown;
|
|
8
|
-
expected_error: typeof ERROR_INVALID_REQUEST_BODY | typeof ERROR_INVALID_JSON_BODY;
|
|
9
|
-
}
|
|
10
|
-
interface ParamsTestCase {
|
|
11
|
-
label: string;
|
|
12
|
-
params: Record<string, string>;
|
|
13
|
-
}
|
|
14
|
-
interface QueryTestCase {
|
|
15
|
-
label: string;
|
|
16
|
-
query: Record<string, string>;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Generate adversarial test cases for a route's input schema.
|
|
20
|
-
*
|
|
21
|
-
* Produces focused, non-redundant cases:
|
|
22
|
-
* - Whole-body: send array instead of object, extra unknown key
|
|
23
|
-
* - Missing required fields (without defaults)
|
|
24
|
-
* - One wrong-type value per field
|
|
25
|
-
* - Null for required non-nullable fields
|
|
26
|
-
* - One format violation per constrained field
|
|
27
|
-
*
|
|
28
|
-
* @throws Error if the seed body built by `generate_valid_value` fails
|
|
29
|
-
* `input_schema.safeParse` — surfaces broken generation logic with the
|
|
30
|
-
* Zod issues path rather than producing nonsense adversarial cases.
|
|
31
|
-
*/
|
|
32
|
-
export declare const generate_input_test_cases: (input_schema: z.ZodType) => Array<InputTestCase>;
|
|
33
|
-
/**
|
|
34
|
-
* Generate adversarial test cases for a route's params schema.
|
|
35
|
-
*
|
|
36
|
-
* Params are always strings from URL segments. Only generates cases for
|
|
37
|
-
* format-constrained fields (uuid, pattern) since unconstrained string
|
|
38
|
-
* params accept any string value.
|
|
39
|
-
*/
|
|
40
|
-
export declare const generate_params_test_cases: (params_schema: z.ZodObject) => Array<ParamsTestCase>;
|
|
41
|
-
/**
|
|
42
|
-
* Generate adversarial test cases for a route's query schema.
|
|
43
|
-
*
|
|
44
|
-
* Query params are always strings from the URL. Generates cases for:
|
|
45
|
-
* - Missing required fields
|
|
46
|
-
* - Format violations on constrained fields (uuid, pattern)
|
|
47
|
-
*/
|
|
48
|
-
export declare const generate_query_test_cases: (query_schema: z.ZodObject) => Array<QueryTestCase>;
|
|
49
3
|
/**
|
|
50
4
|
* Generate adversarial input validation test suites.
|
|
51
5
|
*
|
|
@@ -54,5 +8,4 @@ export declare const generate_query_test_cases: (query_schema: z.ZodObject) => A
|
|
|
54
8
|
* middleware is actually exercised.
|
|
55
9
|
*/
|
|
56
10
|
export declare const describe_adversarial_input: (options: AdversarialTestOptions) => void;
|
|
57
|
-
export {};
|
|
58
11
|
//# sourceMappingURL=adversarial_input.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adversarial_input.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/adversarial_input.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"adversarial_input.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/adversarial_input.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA0B7B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AA2BlE;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GAAI,SAAS,sBAAsB,KAAG,IAyN5E,CAAC"}
|
|
@@ -2,353 +2,17 @@ import './assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Adversarial input validation testing for route specs.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* are reached.
|
|
8
|
-
*
|
|
9
|
-
* Tests are focused: one representative wrong-type value per field, one format
|
|
10
|
-
* violation per constrained field, one null and one missing test per required
|
|
11
|
-
* field, plus whole-body structural attacks (non-object body, extra unknown keys).
|
|
5
|
+
* Fires the payloads `adversarial_input_cases.ts` generates against a test
|
|
6
|
+
* app and asserts 400 responses before handlers are reached.
|
|
12
7
|
*
|
|
13
8
|
* @module
|
|
14
9
|
*/
|
|
15
10
|
import { test, assert, describe } from 'vitest';
|
|
16
|
-
import { z } from 'zod';
|
|
17
|
-
import { zod_unwrap_to_object, zod_extract_fields } from '../zod.js';
|
|
18
|
-
import { is_null_schema } from '../http/schema_helpers.js';
|
|
19
11
|
import { filter_routes_with_input, filter_routes_with_params, filter_routes_with_query } from '../http/surface_query.js';
|
|
20
12
|
import { ValidationError, ERROR_INVALID_REQUEST_BODY, ERROR_INVALID_JSON_BODY, ERROR_INVALID_ROUTE_PARAMS, ERROR_INVALID_QUERY_PARAMS } from '../http/error_schemas.js';
|
|
21
13
|
import { create_auth_test_apps, create_route_skip_filter, select_auth_app } from './auth_apps.js';
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
/** One wrong-type value per base type — one representative is sufficient. */
|
|
25
|
-
const wrong_type_for = (base_type) => {
|
|
26
|
-
switch (base_type) {
|
|
27
|
-
case 'string':
|
|
28
|
-
case 'uuid':
|
|
29
|
-
case 'email':
|
|
30
|
-
return { label: 'number instead of string', value: 42 };
|
|
31
|
-
case 'number':
|
|
32
|
-
case 'int':
|
|
33
|
-
return { label: 'string instead of number', value: 'not_a_number' };
|
|
34
|
-
case 'boolean':
|
|
35
|
-
return { label: 'number instead of boolean', value: 42 };
|
|
36
|
-
case 'array':
|
|
37
|
-
return { label: 'string instead of array', value: 'not_an_array' };
|
|
38
|
-
case 'object':
|
|
39
|
-
return { label: 'string instead of object', value: 'not_an_object' };
|
|
40
|
-
case 'enum':
|
|
41
|
-
return { label: 'number instead of enum', value: 42 };
|
|
42
|
-
default:
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
/** Format violation payloads — one per constraint type. */
|
|
47
|
-
const format_violation = (format) => {
|
|
48
|
-
switch (format) {
|
|
49
|
-
case 'uuid':
|
|
50
|
-
return { label: 'malformed uuid', value: 'not-a-uuid' };
|
|
51
|
-
case 'email':
|
|
52
|
-
return { label: 'malformed email', value: 'not-an-email' };
|
|
53
|
-
case 'date-time':
|
|
54
|
-
return { label: 'malformed datetime', value: 'not-a-date' };
|
|
55
|
-
case 'pattern':
|
|
56
|
-
return { label: 'pattern violation', value: "'; DROP TABLE --" };
|
|
57
|
-
default:
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
// --- Input test case generation ---
|
|
62
|
-
/**
|
|
63
|
-
* Generate adversarial test cases for a route's input schema.
|
|
64
|
-
*
|
|
65
|
-
* Produces focused, non-redundant cases:
|
|
66
|
-
* - Whole-body: send array instead of object, extra unknown key
|
|
67
|
-
* - Missing required fields (without defaults)
|
|
68
|
-
* - One wrong-type value per field
|
|
69
|
-
* - Null for required non-nullable fields
|
|
70
|
-
* - One format violation per constrained field
|
|
71
|
-
*
|
|
72
|
-
* @throws Error if the seed body built by `generate_valid_value` fails
|
|
73
|
-
* `input_schema.safeParse` — surfaces broken generation logic with the
|
|
74
|
-
* Zod issues path rather than producing nonsense adversarial cases.
|
|
75
|
-
*/
|
|
76
|
-
export const generate_input_test_cases = (input_schema) => {
|
|
77
|
-
if (is_null_schema(input_schema))
|
|
78
|
-
return [];
|
|
79
|
-
const object_schema = zod_unwrap_to_object(input_schema);
|
|
80
|
-
if (!object_schema)
|
|
81
|
-
return [];
|
|
82
|
-
const fields = zod_extract_fields(object_schema);
|
|
83
|
-
const cases = [];
|
|
84
|
-
// build a base object with valid-ish values — must pass validation itself
|
|
85
|
-
// skip optional fields without defaults to avoid generating invalid nested values
|
|
86
|
-
const base = {};
|
|
87
|
-
for (const field of fields) {
|
|
88
|
-
if (!field.required && !field.has_default)
|
|
89
|
-
continue;
|
|
90
|
-
base[field.name] = generate_valid_value(field, object_schema.shape[field.name]);
|
|
91
|
-
}
|
|
92
|
-
let base_result = input_schema.safeParse(base);
|
|
93
|
-
if (!base_result.success) {
|
|
94
|
-
// Fallback for schemas with a top-level `.refine()` that requires at
|
|
95
|
-
// least one of N optional fields. Fill optional fields until the base
|
|
96
|
-
// satisfies validation; surfaces a clear error if no combination works.
|
|
97
|
-
for (const field of fields) {
|
|
98
|
-
if (field.required || field.has_default || field.name in base)
|
|
99
|
-
continue;
|
|
100
|
-
base[field.name] = generate_valid_value(field, object_schema.shape[field.name]);
|
|
101
|
-
base_result = input_schema.safeParse(base);
|
|
102
|
-
if (base_result.success)
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (!base_result.success) {
|
|
107
|
-
throw new Error(`adversarial_input: generated base object fails validation for schema — ` +
|
|
108
|
-
`fix generate_valid_value for: ${base_result.error.issues
|
|
109
|
-
.map((i) => `${i.path.join('.')}: ${i.message}`)
|
|
110
|
-
.join(', ')}`);
|
|
111
|
-
}
|
|
112
|
-
// whole-body structural: send array instead of object
|
|
113
|
-
cases.push({
|
|
114
|
-
label: 'non-object body (array)',
|
|
115
|
-
body: [1, 2, 3],
|
|
116
|
-
expected_error: ERROR_INVALID_JSON_BODY
|
|
117
|
-
});
|
|
118
|
-
// whole-body structural: extra unknown key (enforces strictObject)
|
|
119
|
-
// only emit if the schema rejects unknown keys (i.e. uses z.strictObject)
|
|
120
|
-
const extra_key_result = input_schema.safeParse({ ...base, __adversarial_extra: 'rejected' });
|
|
121
|
-
if (!extra_key_result.success) {
|
|
122
|
-
cases.push({
|
|
123
|
-
label: 'extra unknown key',
|
|
124
|
-
body: { ...base, __adversarial_extra: 'should_be_rejected' },
|
|
125
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
for (const field of fields) {
|
|
129
|
-
const field_schema = object_schema.shape[field.name];
|
|
130
|
-
// missing required field (skip fields with defaults — Zod fills them)
|
|
131
|
-
if (field.required && !field.has_default) {
|
|
132
|
-
const without = {};
|
|
133
|
-
for (const [k, v] of Object.entries(base)) {
|
|
134
|
-
if (k !== field.name)
|
|
135
|
-
without[k] = v;
|
|
136
|
-
}
|
|
137
|
-
cases.push({
|
|
138
|
-
label: `missing: ${field.name}`,
|
|
139
|
-
body: without,
|
|
140
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
// one wrong-type value
|
|
144
|
-
const wrong = wrong_type_for(field.base_type);
|
|
145
|
-
if (wrong) {
|
|
146
|
-
cases.push({
|
|
147
|
-
label: `wrong type: ${field.name} (${wrong.label})`,
|
|
148
|
-
body: { ...base, [field.name]: wrong.value },
|
|
149
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
// null for required non-nullable fields
|
|
153
|
-
if (field.required && !field.nullable && field.base_type !== 'null') {
|
|
154
|
-
cases.push({
|
|
155
|
-
label: `null: ${field.name}`,
|
|
156
|
-
body: { ...base, [field.name]: null },
|
|
157
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
// format violation for constrained string fields
|
|
161
|
-
const format = detect_format(field_schema);
|
|
162
|
-
if (format) {
|
|
163
|
-
const violation = format_violation(format);
|
|
164
|
-
if (violation) {
|
|
165
|
-
cases.push({
|
|
166
|
-
label: `format: ${field.name} (${violation.label})`,
|
|
167
|
-
body: { ...base, [field.name]: violation.value },
|
|
168
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// --- Boundary cases via JSON Schema introspection ---
|
|
173
|
-
try {
|
|
174
|
-
const json = z.toJSONSchema(field_schema);
|
|
175
|
-
// string length boundaries
|
|
176
|
-
if (field.base_type === 'string' ||
|
|
177
|
-
field.base_type === 'uuid' ||
|
|
178
|
-
field.base_type === 'email') {
|
|
179
|
-
if (typeof json.minLength === 'number' && json.minLength > 0) {
|
|
180
|
-
cases.push({
|
|
181
|
-
label: `empty string: ${field.name}`,
|
|
182
|
-
body: { ...base, [field.name]: '' },
|
|
183
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
if (typeof json.maxLength === 'number') {
|
|
187
|
-
cases.push({
|
|
188
|
-
label: `over maxLength: ${field.name} (${json.maxLength + 1} chars)`,
|
|
189
|
-
body: { ...base, [field.name]: 'x'.repeat(json.maxLength + 1) },
|
|
190
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
// numeric boundaries
|
|
195
|
-
if (field.base_type === 'number' || field.base_type === 'int') {
|
|
196
|
-
if (typeof json.minimum === 'number') {
|
|
197
|
-
cases.push({
|
|
198
|
-
label: `below minimum: ${field.name} (${json.minimum - 1})`,
|
|
199
|
-
body: { ...base, [field.name]: json.minimum - 1 },
|
|
200
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
if (typeof json.maximum === 'number') {
|
|
204
|
-
cases.push({
|
|
205
|
-
label: `above maximum: ${field.name} (${json.maximum + 1})`,
|
|
206
|
-
body: { ...base, [field.name]: json.maximum + 1 },
|
|
207
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
if (typeof json.exclusiveMinimum === 'number') {
|
|
211
|
-
cases.push({
|
|
212
|
-
label: `at exclusive minimum: ${field.name} (${json.exclusiveMinimum})`,
|
|
213
|
-
body: { ...base, [field.name]: json.exclusiveMinimum },
|
|
214
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
if (typeof json.exclusiveMaximum === 'number') {
|
|
218
|
-
cases.push({
|
|
219
|
-
label: `at exclusive maximum: ${field.name} (${json.exclusiveMaximum})`,
|
|
220
|
-
body: { ...base, [field.name]: json.exclusiveMaximum },
|
|
221
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
// 0 and negative for positive-only fields
|
|
225
|
-
if (typeof json.minimum === 'number' && json.minimum > 0) {
|
|
226
|
-
cases.push({
|
|
227
|
-
label: `zero for positive-only: ${field.name}`,
|
|
228
|
-
body: { ...base, [field.name]: 0 },
|
|
229
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
230
|
-
});
|
|
231
|
-
cases.push({
|
|
232
|
-
label: `negative for positive-only: ${field.name}`,
|
|
233
|
-
body: { ...base, [field.name]: -1 },
|
|
234
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
// array length boundaries
|
|
239
|
-
if (field.base_type === 'array') {
|
|
240
|
-
if (typeof json.minItems === 'number' && json.minItems > 0) {
|
|
241
|
-
cases.push({
|
|
242
|
-
label: `empty array for minItems > 0: ${field.name}`,
|
|
243
|
-
body: { ...base, [field.name]: [] },
|
|
244
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
if (typeof json.maxItems === 'number') {
|
|
248
|
-
// generate an array one item over the max (items are null — schema-agnostic)
|
|
249
|
-
cases.push({
|
|
250
|
-
label: `over maxItems: ${field.name} (${json.maxItems + 1} items)`,
|
|
251
|
-
body: { ...base, [field.name]: Array.from({ length: json.maxItems + 1 }, () => null) },
|
|
252
|
-
expected_error: ERROR_INVALID_REQUEST_BODY
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
// schema can't be converted to JSON Schema, skip boundary cases
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return cases;
|
|
262
|
-
};
|
|
263
|
-
// --- Params test case generation ---
|
|
264
|
-
/**
|
|
265
|
-
* Generate adversarial test cases for a route's params schema.
|
|
266
|
-
*
|
|
267
|
-
* Params are always strings from URL segments. Only generates cases for
|
|
268
|
-
* format-constrained fields (uuid, pattern) since unconstrained string
|
|
269
|
-
* params accept any string value.
|
|
270
|
-
*/
|
|
271
|
-
export const generate_params_test_cases = (params_schema) => {
|
|
272
|
-
const fields = zod_extract_fields(params_schema);
|
|
273
|
-
const cases = [];
|
|
274
|
-
// build base params with valid-ish values
|
|
275
|
-
const base_params = {};
|
|
276
|
-
for (const field of fields) {
|
|
277
|
-
const field_schema = params_schema.shape[field.name];
|
|
278
|
-
const format = detect_format(field_schema);
|
|
279
|
-
if (format === 'uuid' || field.base_type === 'uuid') {
|
|
280
|
-
base_params[field.name] = '00000000-0000-0000-0000-000000000000';
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
base_params[field.name] = 'test_value';
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
for (const field of fields) {
|
|
287
|
-
const field_schema = params_schema.shape[field.name];
|
|
288
|
-
const format = detect_format(field_schema);
|
|
289
|
-
if (!format)
|
|
290
|
-
continue; // unconstrained string — any value passes
|
|
291
|
-
const violation = format_violation(format);
|
|
292
|
-
if (violation) {
|
|
293
|
-
cases.push({
|
|
294
|
-
label: `${violation.label}: param ${field.name}`,
|
|
295
|
-
params: { ...base_params, [field.name]: violation.value }
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return cases;
|
|
300
|
-
};
|
|
301
|
-
// --- Query test case generation ---
|
|
302
|
-
/**
|
|
303
|
-
* Generate adversarial test cases for a route's query schema.
|
|
304
|
-
*
|
|
305
|
-
* Query params are always strings from the URL. Generates cases for:
|
|
306
|
-
* - Missing required fields
|
|
307
|
-
* - Format violations on constrained fields (uuid, pattern)
|
|
308
|
-
*/
|
|
309
|
-
export const generate_query_test_cases = (query_schema) => {
|
|
310
|
-
const fields = zod_extract_fields(query_schema);
|
|
311
|
-
const cases = [];
|
|
312
|
-
// build base query with valid-ish values
|
|
313
|
-
const base_query = {};
|
|
314
|
-
for (const field of fields) {
|
|
315
|
-
const field_schema = query_schema.shape[field.name];
|
|
316
|
-
const format = detect_format(field_schema);
|
|
317
|
-
if (format === 'uuid' || field.base_type === 'uuid') {
|
|
318
|
-
base_query[field.name] = '00000000-0000-0000-0000-000000000000';
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
base_query[field.name] = 'test_value';
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
for (const field of fields) {
|
|
325
|
-
// missing required field
|
|
326
|
-
if (field.required && !field.has_default) {
|
|
327
|
-
const without = {};
|
|
328
|
-
for (const [k, v] of Object.entries(base_query)) {
|
|
329
|
-
if (k !== field.name)
|
|
330
|
-
without[k] = v;
|
|
331
|
-
}
|
|
332
|
-
cases.push({
|
|
333
|
-
label: `missing query: ${field.name}`,
|
|
334
|
-
query: without
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
// format violation for constrained string fields
|
|
338
|
-
const field_schema = query_schema.shape[field.name];
|
|
339
|
-
const format = detect_format(field_schema);
|
|
340
|
-
if (!format)
|
|
341
|
-
continue;
|
|
342
|
-
const violation = format_violation(format);
|
|
343
|
-
if (violation) {
|
|
344
|
-
cases.push({
|
|
345
|
-
label: `${violation.label}: query ${field.name}`,
|
|
346
|
-
query: { ...base_query, [field.name]: violation.value }
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return cases;
|
|
351
|
-
};
|
|
14
|
+
import { resolve_valid_path, to_route_content_type } from './schema_generators.js';
|
|
15
|
+
import { generate_input_test_cases, generate_params_test_cases, generate_query_test_cases } from './adversarial_input_cases.js';
|
|
352
16
|
// --- URL helpers ---
|
|
353
17
|
/** Build a URL path with adversarial param values. */
|
|
354
18
|
const build_fuzz_url = (route_path, params) => {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import './assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Schema-walk generation of adversarial inputs — payloads that must fail
|
|
4
|
+
* validation.
|
|
5
|
+
*
|
|
6
|
+
* Focused cases: one representative wrong-type value per field, one format
|
|
7
|
+
* violation per constrained field, one null and one missing case per required
|
|
8
|
+
* field, plus whole-body structural attacks (non-object body, extra unknown
|
|
9
|
+
* keys). Hono-free, so the cross-process suites reuse the generators the
|
|
10
|
+
* in-process `describe_adversarial_input` and `describe_rpc_attack_surface_tests`
|
|
11
|
+
* run.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { ERROR_INVALID_REQUEST_BODY, ERROR_INVALID_JSON_BODY } from '../http/error_schemas.js';
|
|
17
|
+
/** One adversarial body for an input schema, and the error code it owes. */
|
|
18
|
+
export interface InputTestCase {
|
|
19
|
+
label: string;
|
|
20
|
+
body: unknown;
|
|
21
|
+
expected_error: typeof ERROR_INVALID_REQUEST_BODY | typeof ERROR_INVALID_JSON_BODY;
|
|
22
|
+
}
|
|
23
|
+
/** One adversarial params set for a route's params schema. */
|
|
24
|
+
export interface ParamsTestCase {
|
|
25
|
+
label: string;
|
|
26
|
+
params: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** One adversarial query for a route's query schema. */
|
|
29
|
+
export interface QueryTestCase {
|
|
30
|
+
label: string;
|
|
31
|
+
query: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate adversarial test cases for a route's input schema.
|
|
35
|
+
*
|
|
36
|
+
* Produces focused, non-redundant cases:
|
|
37
|
+
* - Whole-body: send array instead of object, extra unknown key
|
|
38
|
+
* - Missing required fields (without defaults)
|
|
39
|
+
* - One wrong-type value per field
|
|
40
|
+
* - Null for required non-nullable fields
|
|
41
|
+
* - One format violation per constrained field
|
|
42
|
+
*
|
|
43
|
+
* @throws Error if the seed body built by `generate_valid_value` fails
|
|
44
|
+
* `input_schema.safeParse` — surfaces broken generation logic with the
|
|
45
|
+
* Zod issues path rather than producing nonsense adversarial cases.
|
|
46
|
+
*/
|
|
47
|
+
export declare const generate_input_test_cases: (input_schema: z.ZodType) => Array<InputTestCase>;
|
|
48
|
+
/**
|
|
49
|
+
* Generate adversarial test cases for a route's params schema.
|
|
50
|
+
*
|
|
51
|
+
* Params are always strings from URL segments. Only generates cases for
|
|
52
|
+
* format-constrained fields (uuid, pattern) since unconstrained string
|
|
53
|
+
* params accept any string value.
|
|
54
|
+
*/
|
|
55
|
+
export declare const generate_params_test_cases: (params_schema: z.ZodObject) => Array<ParamsTestCase>;
|
|
56
|
+
/**
|
|
57
|
+
* Generate adversarial test cases for a route's query schema.
|
|
58
|
+
*
|
|
59
|
+
* Query params are always strings from the URL. Generates cases for:
|
|
60
|
+
* - Missing required fields
|
|
61
|
+
* - Format violations on constrained fields (uuid, pattern)
|
|
62
|
+
*/
|
|
63
|
+
export declare const generate_query_test_cases: (query_schema: z.ZodObject) => Array<QueryTestCase>;
|
|
64
|
+
//# sourceMappingURL=adversarial_input_cases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adversarial_input_cases.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/adversarial_input_cases.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AA8C/F,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,cAAc,EAAE,OAAO,0BAA0B,GAAG,OAAO,uBAAuB,CAAC;CACnF;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAID;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,KAAK,CAAC,aAAa,CAsMtF,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GAAI,eAAe,CAAC,CAAC,SAAS,KAAG,KAAK,CAAC,cAAc,CA+B3F,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAc,CAAC,CAAC,SAAS,KAAG,KAAK,CAAC,aAAa,CA4CxF,CAAC"}
|