@fuzdev/fuz_app 0.146.1 → 0.147.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/CLAUDE.md +1 -1
- package/dist/auth/bootstrap_account.d.ts +2 -1
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +17 -6
- package/dist/auth/keyring.d.ts +35 -38
- package/dist/auth/keyring.d.ts.map +1 -1
- package/dist/auth/keyring.js +43 -50
- package/dist/auth/password_argon2.d.ts.map +1 -1
- package/dist/auth/password_argon2.js +11 -8
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +5 -2
- package/dist/db/CLAUDE.md +18 -16
- package/dist/db/cell_queries.d.ts +3 -2
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +3 -2
- package/dist/db/fact_disk_storage.d.ts +15 -15
- package/dist/db/fact_disk_storage.d.ts.map +1 -1
- package/dist/db/fact_disk_storage.js +22 -28
- package/dist/db/fact_queries.d.ts +2 -3
- package/dist/db/fact_queries.d.ts.map +1 -1
- package/dist/db/fact_queries.js +2 -3
- package/dist/db/fact_store.d.ts +20 -18
- package/dist/db/fact_store.d.ts.map +1 -1
- package/dist/db/fact_store.js +30 -44
- package/dist/db/file_fact_url.d.ts +19 -4
- package/dist/db/file_fact_url.d.ts.map +1 -1
- package/dist/db/file_fact_url.js +19 -4
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +2 -2
- package/dist/server/serve_fact_route.d.ts +9 -5
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +15 -7
- package/dist/testing/CLAUDE.md +19 -2
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +2 -2
- package/dist/testing/cross_backend/bootstrap_backend.d.ts +11 -1
- package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -1
- package/dist/testing/cross_backend/bootstrap_backend.js +2 -1
- package/dist/testing/cross_backend/fact_serving.d.ts +5 -0
- package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
- package/dist/testing/cross_backend/fact_serving.js +25 -1
- package/dist/testing/cross_backend/session_cookie_codec.d.ts +19 -0
- package/dist/testing/cross_backend/session_cookie_codec.d.ts.map +1 -0
- package/dist/testing/cross_backend/session_cookie_codec.js +128 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +7 -3
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +13 -8
- package/package.json +6 -6
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -154,7 +154,7 @@ Key module-scope values:
|
|
|
154
154
|
|
|
155
155
|
- `stub_password_deps` — `PasswordHashDeps` hashing via `stub_hash_${password}` and verifying by equality. Deterministic, no Argon2 cost — use for every test not specifically exercising password hashing.
|
|
156
156
|
- `create_loopback_app_server_options()` — the loopback `create_app_server` options an in-process test server takes (`localhost` origins, the loopback proxy trusted, an empty env schema), fresh per call; `create_test_app` and `create_test_app_for_bootstrap` spread it under their own defaults (`rate_limiters: 'disabled_for_testing'`, `await_pending_effects: true`).
|
|
157
|
-
- `TEST_COOKIE_SECRET` — 64-hex-char deterministic cookie secret. Produces a valid `Keyring` via `
|
|
157
|
+
- `TEST_COOKIE_SECRET` — 64-hex-char deterministic cookie secret. Produces a valid `Keyring` via `create_keyring`. Never used in production — the stub guard plus fixed value is the contract.
|
|
158
158
|
- `fallback_db_factory` — module-level auth-only default factory (`create_default_db_factory`) `create_test_app_server` uses when no `db` is passed, reusing its driver's WASM cache. When `migration_namespaces` is supplied, a memoized sibling factory (keyed by namespace set) migrating `[auth_migration_ns, ...migration_namespaces]` is used instead — same shared WASM, extra tables.
|
|
159
159
|
|
|
160
160
|
Two helpers share the "insert account + actor + roles + API token + session +
|
|
@@ -970,7 +970,9 @@ source of truth for wire-shape conformance.
|
|
|
970
970
|
hardening headers and bodyless masked 404s); like cells it stays off the
|
|
971
971
|
declared surface and is `true` on every spine that mounts the serve routes +
|
|
972
972
|
the `_testing_put_fact` seeder (whose optional `holder` records a
|
|
973
|
-
`fact_holder` row, so a non-admin can reference what it seeds
|
|
973
|
+
`fact_holder` row, so a non-admin can reference what it seeds, and whose
|
|
974
|
+
optional `external_url` writes a bare external row naming that URL, so the
|
|
975
|
+
suite can pin that a URL naming another hash's file is refused) — it also
|
|
974
976
|
gates `describe_cell_ref_admission_cross_tests`. `ready` gates `describe_ready_cross_tests`
|
|
975
977
|
(anonymous `GET /ready` → `200 {ready: true}` on a clean spine bootstrap);
|
|
976
978
|
like cells/sse the `/ready` deploy gate stays off the declared surface, `true`
|
|
@@ -1917,6 +1919,21 @@ spine bootstrap (auth + cell + fact) and is regenerated +
|
|
|
1917
1919
|
drift-guarded by `src/test/cross_backend/spine_expected_schema.db.test.ts`
|
|
1918
1920
|
(`UPDATE_SCHEMA_READY=1`).
|
|
1919
1921
|
|
|
1922
|
+
### Session cookie codec parity — `cross_backend/session_cookie_codec.ts`
|
|
1923
|
+
|
|
1924
|
+
`describe_session_cookie_codec_cross_tests({setup_test, cookie_name,
|
|
1925
|
+
cookie_keys, rpc_path?})` — both spines accept exactly one spelling of a signed
|
|
1926
|
+
session cookie. The suite holds the backend's `SECRET_FUZ_COOKIE_KEYS`
|
|
1927
|
+
(`cookie_keys`), signs `token:expires_at` itself, and probes `account_verify`
|
|
1928
|
+
with: a signature whose final character carries non-zero trailing bits (the
|
|
1929
|
+
same MAC under `atob`), an unpadded signature, and correctly signed
|
|
1930
|
+
`expires_at` values `+N`, ` N`, `N.0` and one past `u64::MAX` — each refused
|
|
1931
|
+
with `authentication_required`. Controls: re-signing the keeper cookie's
|
|
1932
|
+
payload reproduces it byte for byte and it authenticates, and a leading-zero
|
|
1933
|
+
`0N` authenticates (the `u64` bound is on the value, not the digit count).
|
|
1934
|
+
Ungated; cross-process only. fuz_app's own wiring is
|
|
1935
|
+
`src/test/cross_backend/session_cookie_codec.cross.test.ts`.
|
|
1936
|
+
|
|
1920
1937
|
### Testing-backdoor credential gate — `cross_backend/testing_backdoor.ts`
|
|
1921
1938
|
|
|
1922
1939
|
`describe_testing_backdoor_cross_tests({setup_test, rpc_path?, ws?})` —
|
|
@@ -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;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAGjC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAIpD,OAAO,
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
4
4
|
import { DEFAULT_TEST_PASSWORD } from './test_credentials.js';
|
|
5
5
|
import { ROLE_KEEPER } from '../auth/role_schema.js';
|
|
6
|
-
import {
|
|
6
|
+
import { create_keyring } from '../auth/keyring.js';
|
|
7
7
|
import { generate_api_token } from '../auth/api_token.js';
|
|
8
8
|
import { query_create_account_with_actor } from '../auth/account_queries.js';
|
|
9
9
|
import { query_create_role_grant } from '../auth/role_grant_queries.js';
|
|
@@ -182,7 +182,7 @@ const _build_test_backend = async (options) => {
|
|
|
182
182
|
throw new Error('test app setup: pass either `db` (caller owns migrations) or `migration_namespaces` ' +
|
|
183
183
|
'(harness migrates), not both');
|
|
184
184
|
}
|
|
185
|
-
const keyring_result =
|
|
185
|
+
const keyring_result = create_keyring(TEST_COOKIE_SECRET);
|
|
186
186
|
if (!keyring_result.ok) {
|
|
187
187
|
throw new Error(`Test keyring failed: ${keyring_result.errors.join(', ')}`);
|
|
188
188
|
}
|
|
@@ -25,6 +25,16 @@ import '../assert_dev_env.js';
|
|
|
25
25
|
*/
|
|
26
26
|
import type { BackendConfig } from './backend_config.js';
|
|
27
27
|
import type { BootstrappedBackendHandle } from './setup.js';
|
|
28
|
+
/** Options for `bootstrap_backend`. */
|
|
29
|
+
export interface BootstrapBackendOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Runs after the binary is up and before the keeper bootstrap — the one
|
|
32
|
+
* window in a backend's life where `POST {bootstrap_path}` still answers on
|
|
33
|
+
* the token, so probes of the bootstrap route itself go here. A throw tears
|
|
34
|
+
* the backend down like a failed bootstrap.
|
|
35
|
+
*/
|
|
36
|
+
readonly before_bootstrap?: (config: BackendConfig) => Promise<void>;
|
|
37
|
+
}
|
|
28
38
|
/**
|
|
29
39
|
* Spawn the test binary described by `config`, bootstrap a keeper, and
|
|
30
40
|
* return the enriched handle.
|
|
@@ -37,5 +47,5 @@ import type { BootstrappedBackendHandle } from './setup.js';
|
|
|
37
47
|
* Mirrors the composition `default_cross_process_setup`'s caller would
|
|
38
48
|
* otherwise hand-roll in every consumer's `globalSetup`.
|
|
39
49
|
*/
|
|
40
|
-
export declare const bootstrap_backend: (config: BackendConfig) => Promise<BootstrappedBackendHandle>;
|
|
50
|
+
export declare const bootstrap_backend: (config: BackendConfig, options?: BootstrapBackendOptions) => Promise<BootstrappedBackendHandle>;
|
|
41
51
|
//# sourceMappingURL=bootstrap_backend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/bootstrap_backend.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,aAAa,
|
|
1
|
+
{"version":3,"file":"bootstrap_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/bootstrap_backend.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,uCAAuC;AACvC,MAAM,WAAW,uBAAuB;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,aAAa,EACrB,UAAU,uBAAuB,KAC/B,OAAO,CAAC,yBAAyB,CAyBnC,CAAC"}
|
|
@@ -14,9 +14,10 @@ import { create_fetch_transport } from '../transports/fetch_transport.js';
|
|
|
14
14
|
* Mirrors the composition `default_cross_process_setup`'s caller would
|
|
15
15
|
* otherwise hand-roll in every consumer's `globalSetup`.
|
|
16
16
|
*/
|
|
17
|
-
export const bootstrap_backend = async (config) => {
|
|
17
|
+
export const bootstrap_backend = async (config, options) => {
|
|
18
18
|
const handle = await spawn_backend(config);
|
|
19
19
|
try {
|
|
20
|
+
await options?.before_bootstrap?.(config);
|
|
20
21
|
const keeper_transport = create_fetch_transport({ base_url: config.base_url });
|
|
21
22
|
const keeper = await bootstrap({ transport: keeper_transport, config });
|
|
22
23
|
return {
|
|
@@ -15,6 +15,11 @@ export interface CrossPutFactInput {
|
|
|
15
15
|
readonly content_type?: string;
|
|
16
16
|
/** The actor the put is on behalf of — recorded as a holder. Omit for a system put. */
|
|
17
17
|
readonly holder?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Store an external row naming this URL, verbatim and with no bytes, instead
|
|
20
|
+
* of an embedded one.
|
|
21
|
+
*/
|
|
22
|
+
readonly external_url?: string;
|
|
18
23
|
}
|
|
19
24
|
/**
|
|
20
25
|
* Seed an embedded fact over the daemon channel via `_testing_put_fact`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fact_serving.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/fact_serving.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"fact_serving.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/fact_serving.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmE9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElG;;;;;GAKG;AACH,MAAM,WAAW,2BAA4B,SAAQ,uCAAuC;IAC3F,QAAQ,CAAC,sBAAsB,CAAC,EAAE,SAAS,CAAC;CAC5C;AAgBD,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,uFAAuF;IACvF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAC1B,SAAS,IAAI,CAAC,WAAW,EAAE,iBAAiB,GAAG,6BAA6B,CAAC,EAC7E,UAAU,MAAM,EAChB,OAAO,iBAAiB,KACtB,OAAO,CAAC,MAAM,CAaV,CAAC;AAgDR,eAAO,MAAM,iCAAiC,GAAI,SAAS,2BAA2B,KAAG,IAsTxF,CAAC"}
|
|
@@ -21,6 +21,10 @@ import '../assert_dev_env.js';
|
|
|
21
21
|
* private, max-age=300`, whatever its content type;
|
|
22
22
|
* - **bare-hash admin-only** — `GET /api/facts/:hash` is admin (keeper) only:
|
|
23
23
|
* non-admin → 403, anonymous → 401;
|
|
24
|
+
* - **URL bound to hash** — a row whose `external_url` is a well-formed
|
|
25
|
+
* `file:` URL naming another hash's file is a masked 404 on both routes,
|
|
26
|
+
* refused before any disk read (each spine's unit suite proves the bytes
|
|
27
|
+
* behind such a URL never serve);
|
|
24
28
|
* - **multi-actor fallthrough** — a multi-actor caller resolves to a null
|
|
25
29
|
* (anonymous) context on the (`acting`-less) cell-scoped route, so it can't
|
|
26
30
|
* read its own *private* fact there (admitted only by public cells). Opt-in
|
|
@@ -32,7 +36,8 @@ import '../assert_dev_env.js';
|
|
|
32
36
|
* exactly as for an anonymous caller, while a public cell still serves.
|
|
33
37
|
*
|
|
34
38
|
* Facts are seeded **embedded** via `_testing_put_fact` (the cross-process
|
|
35
|
-
* driver has no DB handle)
|
|
39
|
+
* driver has no DB handle), or as a bare external row through its
|
|
40
|
+
* `external_url`; the referencing cell via the `cell_create` RPC
|
|
36
41
|
* (the hash, a whole string value in `data`, is derived into `cell.refs`). A
|
|
37
42
|
* non-admin owner's cell may reference only a fact it holds, so a fact a
|
|
38
43
|
* non-admin references is put with that owner as its `holder` (the keeper is
|
|
@@ -193,6 +198,25 @@ export const describe_fact_serving_cross_tests = (options) => {
|
|
|
193
198
|
const edge_no_bytes = await fact_get(t, cell_fact_path(unrelated_cell, UNRELATED_HASH), admin_headers);
|
|
194
199
|
assert_masked_404(edge_no_bytes, 'edge to an absent fact');
|
|
195
200
|
});
|
|
201
|
+
test_if(capabilities.fact_serving, "an external_url naming another fact's file is a masked 404 on both routes", async () => {
|
|
202
|
+
const fixture = await setup_test();
|
|
203
|
+
const owner = await fixture.create_account({ username: 'fact_url_owner' });
|
|
204
|
+
// a row written past the store: well-formed `file:` URL, but the
|
|
205
|
+
// address of another hash, so serving it would hand out that fact's
|
|
206
|
+
// bytes under this one's name
|
|
207
|
+
const other = 'ab' + 'cd'.repeat(31);
|
|
208
|
+
const hash = await cross_put_fact(fixture, rpc_path, {
|
|
209
|
+
content: 'row-with-a-foreign-url',
|
|
210
|
+
content_type: 'text/plain',
|
|
211
|
+
holder: owner.actor.id,
|
|
212
|
+
external_url: `file:${other.slice(0, 2)}/${other.slice(2)}`
|
|
213
|
+
});
|
|
214
|
+
const t = fixture.fresh_transport();
|
|
215
|
+
const cell_id = await create_cell_with_ref(t, owner.create_session_headers(), hash, 'public');
|
|
216
|
+
const anon = fixture.fresh_transport({ origin: null });
|
|
217
|
+
assert_masked_404(await fact_get(anon, cell_fact_path(cell_id, hash), {}), 'foreign-url fact via a public cell');
|
|
218
|
+
assert_masked_404(await fact_get(t, `/api/facts/${hash}`, fixture.create_session_headers()), 'foreign-url fact by bare hash, as admin');
|
|
219
|
+
});
|
|
196
220
|
test_if(capabilities.fact_serving, 'bare-hash route is admin-only', async () => {
|
|
197
221
|
const fixture = await setup_test();
|
|
198
222
|
const non_admin = await fixture.create_account({ username: 'fact_non_admin' });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { SetupTest } from './setup.js';
|
|
3
|
+
/** Options for the session cookie codec parity suite. */
|
|
4
|
+
export interface SessionCookieCodecCrossTestOptions {
|
|
5
|
+
/** Per-test fixture producer (cross-process). */
|
|
6
|
+
readonly setup_test: SetupTest;
|
|
7
|
+
/** The spine's session cookie name (`handle.config.cookie_name`). */
|
|
8
|
+
readonly cookie_name: string;
|
|
9
|
+
/**
|
|
10
|
+
* The backend's `SECRET_FUZ_COOKIE_KEYS` value
|
|
11
|
+
* (`handle.config.env.SECRET_FUZ_COOKIE_KEYS`) — the suite signs with its
|
|
12
|
+
* primary key.
|
|
13
|
+
*/
|
|
14
|
+
readonly cookie_keys: string;
|
|
15
|
+
/** JSON-RPC endpoint path. Default `SPINE_RPC_PATH`. */
|
|
16
|
+
readonly rpc_path?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const describe_session_cookie_codec_cross_tests: (options: SessionCookieCodecCrossTestOptions) => void;
|
|
19
|
+
//# sourceMappingURL=session_cookie_codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session_cookie_codec.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/session_cookie_codec.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkC9B,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD,yDAAyD;AACzD,MAAM,WAAW,kCAAkC;IAClD,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AA2BD,eAAO,MAAM,yCAAyC,GACrD,SAAS,kCAAkC,KACzC,IAsGF,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-backend parity suite for the **signed session cookie codec** over real
|
|
4
|
+
* HTTP: which spellings of a correctly signed cookie each spine accepts.
|
|
5
|
+
*
|
|
6
|
+
* The cookie is `${token}:${expires_at}.${signature}`, the signature the padded
|
|
7
|
+
* standard base64 of an HMAC-SHA256 MAC. Both spines must accept exactly one
|
|
8
|
+
* spelling of a given signed payload, or a cookie that one spine refuses would
|
|
9
|
+
* authenticate on the other. The suite holds the test keyring, so it signs
|
|
10
|
+
* payloads itself and sends variants a lenient decoder would take:
|
|
11
|
+
*
|
|
12
|
+
* - **signature spelling** — the last data character bumped so the trailing
|
|
13
|
+
* bits are non-zero (`atob` decodes the same MAC), and the `=` padding
|
|
14
|
+
* dropped. Both must be refused.
|
|
15
|
+
* - **`expires_at` spelling** — `+N`, ` N`, `N.0`, and an all-digit value past
|
|
16
|
+
* `u64::MAX` (the Rust spine parses it as `u64`), each signed with the real
|
|
17
|
+
* key. All must be refused. A leading-zero `0N` reads as `N` on both, so it
|
|
18
|
+
* still authenticates — the bound is on the value, not the digit count.
|
|
19
|
+
*
|
|
20
|
+
* The positive controls run first: re-signing the keeper cookie's payload
|
|
21
|
+
* reproduces the cookie byte for byte (the suite's keyring is the backend's),
|
|
22
|
+
* and that cookie authenticates. Authentication is probed with the
|
|
23
|
+
* `account_verify` RPC action. The per-impl unit tests cover each codec
|
|
24
|
+
* directly; this suite is what catches the two drifting apart.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { describe, test, assert } from 'vitest';
|
|
29
|
+
import { create_keyring } from '../../auth/keyring.js';
|
|
30
|
+
import { ERROR_AUTHENTICATION_REQUIRED } from '../../http/error_schemas.js';
|
|
31
|
+
import { cross_rpc_call, error_reason } from './cell_cross_helpers.js';
|
|
32
|
+
import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
33
|
+
/** The base64 alphabet, in index order. */
|
|
34
|
+
const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
35
|
+
/** `u64::MAX + 1`, the smallest all-digit `expires_at` the Rust spine cannot parse. */
|
|
36
|
+
const U64_MAX_PLUS_ONE = '18446744073709551616';
|
|
37
|
+
/** Split a signed cookie value on its last `.` into payload and signature. */
|
|
38
|
+
const split_signed = (signed) => {
|
|
39
|
+
const dot = signed.lastIndexOf('.');
|
|
40
|
+
assert.ok(dot !== -1, `signed cookie value has no '.' separator: ${signed}`);
|
|
41
|
+
return { payload: signed.slice(0, dot), signature: signed.slice(dot + 1) };
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Bump the signature's last data character (index 42, which carries the MAC's
|
|
45
|
+
* final 4 bits and 2 zero padding bits) to the next base64 character. That sets
|
|
46
|
+
* a padding bit, which `atob` ignores, so the decoded MAC is unchanged.
|
|
47
|
+
*/
|
|
48
|
+
const bump_trailing_bits = (signature) => {
|
|
49
|
+
const last_data = signature[42];
|
|
50
|
+
const index = BASE64_ALPHABET.indexOf(last_data);
|
|
51
|
+
assert.ok(index !== -1 && index % 4 === 0, `unexpected final signature character: ${last_data}`);
|
|
52
|
+
return signature.slice(0, 42) + BASE64_ALPHABET[index + 1] + signature.slice(43);
|
|
53
|
+
};
|
|
54
|
+
export const describe_session_cookie_codec_cross_tests = (options) => {
|
|
55
|
+
const { setup_test, cookie_name, cookie_keys } = options;
|
|
56
|
+
const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
|
|
57
|
+
const keyring_result = create_keyring(cookie_keys);
|
|
58
|
+
if (!keyring_result.ok) {
|
|
59
|
+
throw new Error(`cookie_keys is not a valid keyring: ${keyring_result.errors.join('; ')}`);
|
|
60
|
+
}
|
|
61
|
+
const keyring = keyring_result.keyring;
|
|
62
|
+
/** The keeper's signed cookie value, with its verified `token` and `expires_at`. */
|
|
63
|
+
const keeper_cookie = async (fixture) => {
|
|
64
|
+
const header = fixture.create_session_headers().cookie;
|
|
65
|
+
assert.ok(header, 'the keeper session headers carry a cookie');
|
|
66
|
+
const prefix = `${cookie_name}=`;
|
|
67
|
+
assert.ok(header.startsWith(prefix), `the keeper cookie is named '${cookie_name}'`);
|
|
68
|
+
const signed = header.slice(prefix.length);
|
|
69
|
+
const verified = await keyring.verify(signed);
|
|
70
|
+
assert.ok(verified, 'the keeper cookie verifies under cookie_keys');
|
|
71
|
+
const sep = verified.value.lastIndexOf(':');
|
|
72
|
+
assert.ok(sep !== -1, 'the keeper cookie payload is `token:expires_at`');
|
|
73
|
+
return {
|
|
74
|
+
signed,
|
|
75
|
+
token: verified.value.slice(0, sep),
|
|
76
|
+
expires_at: verified.value.slice(sep + 1)
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
const verify_with = (fixture, signed) => cross_rpc_call(fixture.fresh_transport(), rpc_path, 'account_verify', undefined, {
|
|
80
|
+
cookie: `${cookie_name}=${signed}`
|
|
81
|
+
});
|
|
82
|
+
const assert_unauthenticated = async (fixture, signed, label) => {
|
|
83
|
+
const result = await verify_with(fixture, signed);
|
|
84
|
+
assert.strictEqual(result.ok, false, `${label}: must not authenticate`);
|
|
85
|
+
assert.strictEqual(error_reason(result), ERROR_AUTHENTICATION_REQUIRED, label);
|
|
86
|
+
};
|
|
87
|
+
describe('session cookie codec parity', () => {
|
|
88
|
+
test('controls: re-signing the keeper payload reproduces its cookie, which authenticates', async () => {
|
|
89
|
+
const fixture = await setup_test();
|
|
90
|
+
const { signed, token, expires_at } = await keeper_cookie(fixture);
|
|
91
|
+
assert.strictEqual(await keyring.sign(`${token}:${expires_at}`), signed, 'the suite keyring signs as the backend does');
|
|
92
|
+
const result = await verify_with(fixture, signed);
|
|
93
|
+
assert.ok(result.ok, `the keeper cookie authenticates: ${JSON.stringify(result.error)}`);
|
|
94
|
+
});
|
|
95
|
+
test('a signature with non-zero trailing bits is refused', async () => {
|
|
96
|
+
const fixture = await setup_test();
|
|
97
|
+
const { payload, signature } = split_signed((await keeper_cookie(fixture)).signed);
|
|
98
|
+
await assert_unauthenticated(fixture, `${payload}.${bump_trailing_bits(signature)}`, 'non-zero trailing bits');
|
|
99
|
+
});
|
|
100
|
+
test('an unpadded signature is refused', async () => {
|
|
101
|
+
const fixture = await setup_test();
|
|
102
|
+
const { payload, signature } = split_signed((await keeper_cookie(fixture)).signed);
|
|
103
|
+
assert.ok(signature.endsWith('='), 'the canonical signature is padded');
|
|
104
|
+
await assert_unauthenticated(fixture, `${payload}.${signature.slice(0, -1)}`, 'unpadded');
|
|
105
|
+
});
|
|
106
|
+
test('a non-canonical expires_at is refused even when signed', async () => {
|
|
107
|
+
const fixture = await setup_test();
|
|
108
|
+
const { token, expires_at } = await keeper_cookie(fixture);
|
|
109
|
+
const variants = [
|
|
110
|
+
['plus sign', `+${expires_at}`],
|
|
111
|
+
['leading space', ` ${expires_at}`],
|
|
112
|
+
['decimal', `${expires_at}.0`],
|
|
113
|
+
['past u64::MAX', U64_MAX_PLUS_ONE]
|
|
114
|
+
];
|
|
115
|
+
for (const [label, value] of variants) {
|
|
116
|
+
const signed = await keyring.sign(`${token}:${value}`);
|
|
117
|
+
await assert_unauthenticated(fixture, signed, `expires_at ${label} (${JSON.stringify(value)})`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
test('a leading-zero expires_at reads as its value and authenticates', async () => {
|
|
121
|
+
const fixture = await setup_test();
|
|
122
|
+
const { token, expires_at } = await keeper_cookie(fixture);
|
|
123
|
+
const signed = await keyring.sign(`${token}:0${expires_at}`);
|
|
124
|
+
const result = await verify_with(fixture, signed);
|
|
125
|
+
assert.ok(result.ok, `a leading zero authenticates: ${JSON.stringify(result.error)}`);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
};
|
|
@@ -265,9 +265,12 @@ export declare const create_testing_drain_effects_action: (audit: AuditEmitter)
|
|
|
265
265
|
* the put is on behalf of — recorded in `fact_holder` (deduplicated puts
|
|
266
266
|
* included), as a real upload records its uploader, so the suites can drive
|
|
267
267
|
* cell ref admission; absent, the put is a system put with no holder. The
|
|
268
|
-
* referencing cell is seeded separately via the `cell_create` RPC. Embedded
|
|
269
|
-
* authz assertions (cell-scoped admit, cross-owner-no-leak,
|
|
270
|
-
* admin-only); external / X-Accel parity stays covered by
|
|
268
|
+
* referencing cell is seeded separately via the `cell_create` RPC. Embedded is
|
|
269
|
+
* enough for the authz assertions (cell-scoped admit, cross-owner-no-leak,
|
|
270
|
+
* 404-mask, bare-hash admin-only); external / X-Accel parity stays covered by
|
|
271
|
+
* the forge's own gate. `external_url`, when given, writes the row the way a
|
|
272
|
+
* writer past the store could — no bytes, that URL verbatim, unvalidated — so
|
|
273
|
+
* the suite can prove a serve refuses a URL that is not its hash's own address.
|
|
271
274
|
*
|
|
272
275
|
* `auth` gates on the daemon-token credential, matching `_testing_reset` — the
|
|
273
276
|
* action does a direct `fact` insert the production wire never exposes. The
|
|
@@ -287,6 +290,7 @@ export declare const testing_put_fact_action_spec: {
|
|
|
287
290
|
content: z.ZodString;
|
|
288
291
|
content_type: z.ZodOptional<z.ZodString>;
|
|
289
292
|
holder: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
293
|
+
external_url: z.ZodOptional<z.ZodString>;
|
|
290
294
|
}, z.core.$strict>;
|
|
291
295
|
readonly output: z.ZodObject<{
|
|
292
296
|
hash: z.ZodString & z.ZodType<import("@fuzdev/fuz_util/hash_schemas.js").FactHash, string, z.core.$ZodTypeInternals<import("@fuzdev/fuz_util/hash_schemas.js").FactHash, string>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAOzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAsBzC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCQ,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;CAWA,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;CAwBC,CAAC;AAE/C;;;;;;;;GAQG;AACH,eAAO,MAAM,mCAAmC,GAAI,OAAO,YAAY,KAAG,SAIvE,CAAC;AAEJ
|
|
1
|
+
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -267,9 +267,12 @@ export const create_testing_drain_effects_action = (audit) => rpc_action(testing
|
|
|
267
267
|
* the put is on behalf of — recorded in `fact_holder` (deduplicated puts
|
|
268
268
|
* included), as a real upload records its uploader, so the suites can drive
|
|
269
269
|
* cell ref admission; absent, the put is a system put with no holder. The
|
|
270
|
-
* referencing cell is seeded separately via the `cell_create` RPC. Embedded
|
|
271
|
-
* authz assertions (cell-scoped admit, cross-owner-no-leak,
|
|
272
|
-
* admin-only); external / X-Accel parity stays covered by
|
|
270
|
+
* referencing cell is seeded separately via the `cell_create` RPC. Embedded is
|
|
271
|
+
* enough for the authz assertions (cell-scoped admit, cross-owner-no-leak,
|
|
272
|
+
* 404-mask, bare-hash admin-only); external / X-Accel parity stays covered by
|
|
273
|
+
* the forge's own gate. `external_url`, when given, writes the row the way a
|
|
274
|
+
* writer past the store could — no bytes, that URL verbatim, unvalidated — so
|
|
275
|
+
* the suite can prove a serve refuses a URL that is not its hash's own address.
|
|
273
276
|
*
|
|
274
277
|
* `auth` gates on the daemon-token credential, matching `_testing_reset` — the
|
|
275
278
|
* action does a direct `fact` insert the production wire never exposes. The
|
|
@@ -284,12 +287,14 @@ export const testing_put_fact_action_spec = {
|
|
|
284
287
|
input: z.strictObject({
|
|
285
288
|
content: z.string(),
|
|
286
289
|
content_type: z.string().optional(),
|
|
287
|
-
holder: Uuid.optional()
|
|
290
|
+
holder: Uuid.optional(),
|
|
291
|
+
external_url: z.string().optional()
|
|
288
292
|
}),
|
|
289
293
|
output: z.strictObject({ hash: FactHashSchema }),
|
|
290
294
|
async: true,
|
|
291
|
-
description: 'Test-binary only — seed
|
|
292
|
-
'
|
|
295
|
+
description: 'Test-binary only — seed a fact row for the blake3 of the UTF-8 content (embedded, or with ' +
|
|
296
|
+
'external_url an external row naming that URL) and return its hash, so the cross-process ' +
|
|
297
|
+
'fact-serving suite can store facts without a DB handle.'
|
|
293
298
|
};
|
|
294
299
|
/** The wire method of each notification `_testing_flood_notifications` queues. */
|
|
295
300
|
export const TESTING_FLOOD_NOTIFICATION_METHOD = '_testing_flood';
|
|
@@ -719,8 +724,8 @@ export const create_testing_actions = (deps, options) => {
|
|
|
719
724
|
const hash = fact_hash_bytes(bytes);
|
|
720
725
|
await query_put_fact({ db: ctx.db }, {
|
|
721
726
|
hash,
|
|
722
|
-
bytes,
|
|
723
|
-
external_url: null,
|
|
727
|
+
bytes: input.external_url === undefined ? bytes : null,
|
|
728
|
+
external_url: input.external_url ?? null,
|
|
724
729
|
content_type: input.content_type ?? null,
|
|
725
730
|
size: bytes.length
|
|
726
731
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.147.0",
|
|
4
4
|
"description": "fullstack app library",
|
|
5
5
|
"glyph": "🗝",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@fuzdev/blake3-wasm": ">=0.2.0",
|
|
36
36
|
"@fuzdev/fuz_css": ">=0.70.0",
|
|
37
37
|
"@fuzdev/fuz_schema": "^0.5.0",
|
|
38
|
-
"@fuzdev/fuz_util": ">=0.
|
|
38
|
+
"@fuzdev/fuz_util": ">=0.78.0",
|
|
39
39
|
"@fuzdev/pglet-wasm": ">=0.1.2",
|
|
40
40
|
"@hono/bun": ">=1",
|
|
41
41
|
"@hono/deno": ">=1",
|
|
@@ -92,10 +92,10 @@
|
|
|
92
92
|
"@electric-sql/pglite": "^0.5.8",
|
|
93
93
|
"@fuzdev/blake3-wasm": "^0.2.0",
|
|
94
94
|
"@fuzdev/fuz_code": "^0.54.0",
|
|
95
|
-
"@fuzdev/fuz_css": "^0.
|
|
95
|
+
"@fuzdev/fuz_css": "^0.71.0",
|
|
96
96
|
"@fuzdev/fuz_schema": "^0.5.0",
|
|
97
|
-
"@fuzdev/fuz_ui": "^0.
|
|
98
|
-
"@fuzdev/fuz_util": "^0.
|
|
97
|
+
"@fuzdev/fuz_ui": "^0.220.0",
|
|
98
|
+
"@fuzdev/fuz_util": "^0.78.0",
|
|
99
99
|
"@fuzdev/gro": "^0.220.0",
|
|
100
100
|
"@fuzdev/mdz": "^0.6.0",
|
|
101
101
|
"@fuzdev/pglet-wasm": "^0.1.2",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"pg": "^8.20.0",
|
|
124
124
|
"svelte": "^5.57.2",
|
|
125
125
|
"svelte-check": "^4.7.6",
|
|
126
|
-
"svelte-docinfo": "^0.
|
|
126
|
+
"svelte-docinfo": "^0.16.0",
|
|
127
127
|
"svelte2tsx": "^0.7.52",
|
|
128
128
|
"tslib": "^2.8.1",
|
|
129
129
|
"typescript": "^6.0.3",
|