@fuzdev/fuz_app 0.95.0 → 0.97.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/account_queries.d.ts +20 -5
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +21 -4
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +6 -7
- package/dist/auth/daemon_token_middleware.d.ts +21 -4
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +61 -10
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +6 -2
- package/dist/auth/request_context.js +2 -2
- package/dist/db/CLAUDE.md +6 -3
- package/dist/db/cell_queries.js +8 -1
- package/dist/db/create_db.d.ts.map +1 -1
- package/dist/db/create_db.js +2 -14
- package/dist/db/db_pg.d.ts +30 -2
- package/dist/db/db_pg.d.ts.map +1 -1
- package/dist/db/db_pg.js +33 -2
- package/dist/db/fact_queries.d.ts.map +1 -1
- package/dist/http/CLAUDE.md +3 -2
- package/dist/http/error_schemas.d.ts +0 -6
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +0 -6
- package/dist/http/origin.d.ts +18 -1
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +17 -0
- package/dist/server/serve_fact_route.d.ts +12 -0
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +40 -0
- package/dist/testing/CLAUDE.md +23 -3
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +4 -1
- package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -1
- package/dist/testing/cross_backend/bootstrap_backend.js +8 -0
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +23 -0
- package/dist/testing/cross_backend/conformance_case.d.ts +40 -0
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_case.js +34 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +58 -0
- package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
- package/dist/testing/cross_backend/fact_serving.js +5 -1
- package/dist/testing/cross_backend/setup.d.ts +17 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +29 -4
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +6 -1
- package/dist/testing/middleware.js +4 -4
- package/package.json +6 -2
|
@@ -46,8 +46,6 @@ export const ERROR_PAYLOAD_TOO_LARGE = 'payload_too_large';
|
|
|
46
46
|
// --- Origin & bearer token verification ---
|
|
47
47
|
/** Request origin not in allowlist. */
|
|
48
48
|
export const ERROR_FORBIDDEN_ORIGIN = 'forbidden_origin';
|
|
49
|
-
/** Bearer token sent with Origin/Referer header (browser context). */
|
|
50
|
-
export const ERROR_BEARER_REJECTED_BROWSER = 'bearer_token_rejected_in_browser_context';
|
|
51
49
|
/** Bearer token failed validation (missing, malformed, or revoked). */
|
|
52
50
|
export const ERROR_INVALID_TOKEN = 'invalid_token';
|
|
53
51
|
/** Token references a deleted account. */
|
|
@@ -86,10 +84,6 @@ export const ERROR_NO_ACTORS_ON_ACCOUNT = 'no_actors_on_account';
|
|
|
86
84
|
*/
|
|
87
85
|
export const ERROR_ACCOUNT_VANISHED = 'account_vanished';
|
|
88
86
|
// --- Keeper / daemon token ---
|
|
89
|
-
/** Daemon token header present but malformed or not matching current/previous token. */
|
|
90
|
-
export const ERROR_INVALID_DAEMON_TOKEN = 'invalid_daemon_token';
|
|
91
|
-
/** Daemon token valid but keeper account not yet resolved (pre-bootstrap). */
|
|
92
|
-
export const ERROR_KEEPER_ACCOUNT_NOT_CONFIGURED = 'keeper_account_not_configured';
|
|
93
87
|
/** Keeper account ID set but account row not found. */
|
|
94
88
|
export const ERROR_KEEPER_ACCOUNT_NOT_FOUND = 'keeper_account_not_found';
|
|
95
89
|
// --- Bootstrap ---
|
package/dist/http/origin.d.ts
CHANGED
|
@@ -9,7 +9,24 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
|
-
import type { Handler } from 'hono';
|
|
12
|
+
import type { Context, Handler } from 'hono';
|
|
13
|
+
/**
|
|
14
|
+
* True when the request looks like it originated from a browser context — it
|
|
15
|
+
* carries an `Origin` or `Referer` header. Browsers attach these
|
|
16
|
+
* automatically on the state-changing surface; loopback / CLI clients don't.
|
|
17
|
+
* Uses `!== undefined` so an empty-string header still counts as browser
|
|
18
|
+
* context. Checks `Referer` too (not just `Origin`) because some browser
|
|
19
|
+
* requests send only `Referer`. Twin of the Rust spine's
|
|
20
|
+
* `bearer_auth::is_browser_context`.
|
|
21
|
+
*
|
|
22
|
+
* The bearer (`auth/bearer_auth.ts`) and daemon-token
|
|
23
|
+
* (`auth/daemon_token_middleware.ts`) middleware both silently discard their
|
|
24
|
+
* credential when this is true: a stolen bearer token can't be replayed from a
|
|
25
|
+
* browser, and the loopback daemon token never legitimately carries an
|
|
26
|
+
* `Origin`. Distinct from `verify_request_source` — that gates browser
|
|
27
|
+
* requests against an allowlist; this only detects browser context.
|
|
28
|
+
*/
|
|
29
|
+
export declare const is_browser_context: (c: Context) => boolean;
|
|
13
30
|
/**
|
|
14
31
|
* Parses FUZ_ALLOWED_ORIGINS env var into regex matchers for request source verification.
|
|
15
32
|
* Origin allowlisting for locally-running services — not the CSRF layer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"origin.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"origin.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAC,OAAO,EAAE,OAAO,EAAC,MAAM,MAAM,CAAC;AAI3C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,GAAI,GAAG,OAAO,KAAG,OAC8B,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAW,MAAM,GAAG,SAAS,KAAG,KAAK,CAAC,MAAM,CAO5E,CAAC;AAEP;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC/B,QAAQ,MAAM,EACd,kBAAkB,aAAa,CAAC,MAAM,CAAC,KACrC,OAAuD,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB,GAChC,kBAAkB,aAAa,CAAC,MAAM,CAAC,KAAG,OAgB1C,CAAC"}
|
package/dist/http/origin.js
CHANGED
|
@@ -11,6 +11,23 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { escape_regexp } from '@fuzdev/fuz_util/regexp.ts';
|
|
13
13
|
import { ERROR_FORBIDDEN_ORIGIN } from './error_schemas.js';
|
|
14
|
+
/**
|
|
15
|
+
* True when the request looks like it originated from a browser context — it
|
|
16
|
+
* carries an `Origin` or `Referer` header. Browsers attach these
|
|
17
|
+
* automatically on the state-changing surface; loopback / CLI clients don't.
|
|
18
|
+
* Uses `!== undefined` so an empty-string header still counts as browser
|
|
19
|
+
* context. Checks `Referer` too (not just `Origin`) because some browser
|
|
20
|
+
* requests send only `Referer`. Twin of the Rust spine's
|
|
21
|
+
* `bearer_auth::is_browser_context`.
|
|
22
|
+
*
|
|
23
|
+
* The bearer (`auth/bearer_auth.ts`) and daemon-token
|
|
24
|
+
* (`auth/daemon_token_middleware.ts`) middleware both silently discard their
|
|
25
|
+
* credential when this is true: a stolen bearer token can't be replayed from a
|
|
26
|
+
* browser, and the loopback daemon token never legitimately carries an
|
|
27
|
+
* `Origin`. Distinct from `verify_request_source` — that gates browser
|
|
28
|
+
* requests against an allowlist; this only detects browser context.
|
|
29
|
+
*/
|
|
30
|
+
export const is_browser_context = (c) => c.req.header('Origin') !== undefined || c.req.header('Referer') !== undefined;
|
|
14
31
|
/**
|
|
15
32
|
* Parses FUZ_ALLOWED_ORIGINS env var into regex matchers for request source verification.
|
|
16
33
|
* Origin allowlisting for locally-running services — not the CSRF layer
|
|
@@ -63,6 +63,18 @@
|
|
|
63
63
|
* is the doc cell, so serving goes view-doc-cell → doc-cell-refs-include-hash
|
|
64
64
|
* → serve.
|
|
65
65
|
*
|
|
66
|
+
* ## Untrusted-content hardening
|
|
67
|
+
*
|
|
68
|
+
* Fact bytes are served with a producer-supplied `content_type` emitted
|
|
69
|
+
* verbatim, inline. To stop a fact stored as (or sniffable to) `text/html`
|
|
70
|
+
* from executing as stored XSS for any reader of a referencing cell, **every**
|
|
71
|
+
* served-fact response carries `X-Content-Type-Options: nosniff` +
|
|
72
|
+
* `Content-Security-Policy: default-src 'none'; sandbox` (the
|
|
73
|
+
* `FACT_SECURITY_HEADERS` set, applied in all three serve branches). The same
|
|
74
|
+
* pair, byte-identical, is set by the Rust twin (`fuz_fact_serving::serve`).
|
|
75
|
+
* `Content-Disposition: attachment` is deliberately **not** added — it would
|
|
76
|
+
* force-download and break legitimate inline image rendering.
|
|
77
|
+
*
|
|
66
78
|
* ## Defense-in-depth
|
|
67
79
|
*
|
|
68
80
|
* The `external_url` regex is re-validated before issuing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAMH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAepD,OAAO,EAAsC,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAE1F,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAkE7C,MAAM,WAAW,+BAA+B;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;CACZ;AA2GD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,+BAA+B,KACtC,SA8CF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,SAgCF,CAAC"}
|
|
@@ -63,6 +63,18 @@
|
|
|
63
63
|
* is the doc cell, so serving goes view-doc-cell → doc-cell-refs-include-hash
|
|
64
64
|
* → serve.
|
|
65
65
|
*
|
|
66
|
+
* ## Untrusted-content hardening
|
|
67
|
+
*
|
|
68
|
+
* Fact bytes are served with a producer-supplied `content_type` emitted
|
|
69
|
+
* verbatim, inline. To stop a fact stored as (or sniffable to) `text/html`
|
|
70
|
+
* from executing as stored XSS for any reader of a referencing cell, **every**
|
|
71
|
+
* served-fact response carries `X-Content-Type-Options: nosniff` +
|
|
72
|
+
* `Content-Security-Policy: default-src 'none'; sandbox` (the
|
|
73
|
+
* `FACT_SECURITY_HEADERS` set, applied in all three serve branches). The same
|
|
74
|
+
* pair, byte-identical, is set by the Rust twin (`fuz_fact_serving::serve`).
|
|
75
|
+
* `Content-Disposition: attachment` is deliberately **not** added — it would
|
|
76
|
+
* force-download and break legitimate inline image rendering.
|
|
77
|
+
*
|
|
66
78
|
* ## Defense-in-depth
|
|
67
79
|
*
|
|
68
80
|
* The `external_url` regex is re-validated before issuing
|
|
@@ -92,6 +104,31 @@ import { can_view_cell } from '../auth/cell_authorize.js';
|
|
|
92
104
|
import { parse_file_fact_url } from '../db/file_fact_url.js';
|
|
93
105
|
/** `Cache-Control` for fact responses — 5 min revocation window. */
|
|
94
106
|
const CACHE_CONTROL = 'private, max-age=300';
|
|
107
|
+
/**
|
|
108
|
+
* `X-Content-Type-Options` for fact responses — block MIME sniffing so an
|
|
109
|
+
* `application/octet-stream` fact can't be sniffed into executable HTML.
|
|
110
|
+
*/
|
|
111
|
+
const X_CONTENT_TYPE_OPTIONS = 'nosniff';
|
|
112
|
+
/**
|
|
113
|
+
* `Content-Security-Policy` for fact responses. Fact bytes carry a
|
|
114
|
+
* producer-supplied `content_type` served verbatim inline, so a fact stored as
|
|
115
|
+
* (or sniffable to) `text/html` would otherwise execute as stored XSS for any
|
|
116
|
+
* reader of a referencing cell. `default-src 'none'; sandbox` neutralizes
|
|
117
|
+
* script execution and sub-resource loads even when a fact is rendered inline;
|
|
118
|
+
* harmless for directly-served images (they load no sub-resources). The same
|
|
119
|
+
* pair, byte-identical, is set by the Rust twin (`fuz_fact_serving::serve`).
|
|
120
|
+
*/
|
|
121
|
+
const CONTENT_SECURITY_POLICY = "default-src 'none'; sandbox";
|
|
122
|
+
/**
|
|
123
|
+
* The untrusted-content hardening headers applied to every served-fact
|
|
124
|
+
* response across all three branches (embedded, `X-Accel-Redirect`,
|
|
125
|
+
* disk-stream). `Content-Disposition: attachment` is deliberately omitted — it
|
|
126
|
+
* would force-download and break legitimate inline image rendering.
|
|
127
|
+
*/
|
|
128
|
+
const FACT_SECURITY_HEADERS = {
|
|
129
|
+
'X-Content-Type-Options': X_CONTENT_TYPE_OPTIONS,
|
|
130
|
+
'Content-Security-Policy': CONTENT_SECURITY_POLICY,
|
|
131
|
+
};
|
|
95
132
|
/**
|
|
96
133
|
* Path-param schema for the bare-hash route. Matching the canonical
|
|
97
134
|
* fact-hash form here pushes malformed-hash 400s through the framework's
|
|
@@ -150,6 +187,7 @@ const serve_fact_bytes = async (c, route, hash, config) => {
|
|
|
150
187
|
'Content-Type': content_type,
|
|
151
188
|
'Content-Length': size,
|
|
152
189
|
'Cache-Control': CACHE_CONTROL,
|
|
190
|
+
...FACT_SECURITY_HEADERS,
|
|
153
191
|
});
|
|
154
192
|
}
|
|
155
193
|
// External — defense-in-depth re-validate the URL before trusting it
|
|
@@ -167,6 +205,7 @@ const serve_fact_bytes = async (c, route, hash, config) => {
|
|
|
167
205
|
'Content-Length': size,
|
|
168
206
|
'Cache-Control': CACHE_CONTROL,
|
|
169
207
|
'X-Accel-Redirect': `${x_accel_redirect_prefix}${shard}/${rest}`,
|
|
208
|
+
...FACT_SECURITY_HEADERS,
|
|
170
209
|
});
|
|
171
210
|
}
|
|
172
211
|
// Dev / tests: stream from disk. `createReadStream` errors land on the
|
|
@@ -178,6 +217,7 @@ const serve_fact_bytes = async (c, route, hash, config) => {
|
|
|
178
217
|
'Content-Type': content_type,
|
|
179
218
|
'Content-Length': size,
|
|
180
219
|
'Cache-Control': CACHE_CONTROL,
|
|
220
|
+
...FACT_SECURITY_HEADERS,
|
|
181
221
|
});
|
|
182
222
|
};
|
|
183
223
|
/**
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -973,9 +973,29 @@ wire-shape check passes green on even when behavior is wrong.
|
|
|
973
973
|
error_reason?, fields?, headers?, equivalence_group?}, note?, xfail?}`. A case is **data** — `method`
|
|
974
974
|
resolves its `input`/`output` from the live registry (RPC) or `RouteSpec`
|
|
975
975
|
(the 6 REST auth routes), so the case never carries a schema. `as` is the
|
|
976
|
-
closed `ConformancePrincipal` enum (`keeper` / `daemon` / `
|
|
977
|
-
`
|
|
978
|
-
`
|
|
976
|
+
closed `ConformancePrincipal` enum (`keeper` / `daemon` / `invalid_daemon` /
|
|
977
|
+
`daemon_browser` / `token` / `bearer_browser` / `anonymous` /
|
|
978
|
+
`fresh_non_admin` / `role_holder` / `wrong_role` / `expired_session`) —
|
|
979
|
+
fixture accessors, never inline credential minting.
|
|
980
|
+
`invalid_daemon` sends a malformed `X-Daemon-Token` alongside the keeper
|
|
981
|
+
session cookie over a no-Origin transport: the middleware soft-fail-discards
|
|
982
|
+
the daemon token (matching the Rust spine's `None`), auth falls through to the
|
|
983
|
+
session leg, and a daemon-gated action then refuses the session credential
|
|
984
|
+
with `credential_type_required` — not a hard invalid-token 401 (the session
|
|
985
|
+
base credential is what makes the credential-type gate, not the auth gate, the
|
|
986
|
+
refusing layer).
|
|
987
|
+
`daemon_browser` sends a _valid_ `X-Daemon-Token` in a browser context
|
|
988
|
+
(default `Origin` present) alongside the keeper session cookie: the middleware
|
|
989
|
+
discards the header-bearing token as browser context (mirroring the bearer
|
|
990
|
+
guard + the Rust spine's `is_browser_context`), so auth falls through to the
|
|
991
|
+
session leg → `credential_type_required`. Unlike `daemon` (no Origin → token
|
|
992
|
+
honored → 400 confirm guard), the 403 proves a valid token is _dropped_, not
|
|
993
|
+
honored, when an `Origin` is present.
|
|
994
|
+
`bearer_browser` is the bearer twin: a _valid_ bearer api-token in a browser
|
|
995
|
+
context (default `Origin` present), fresh jar (no session) — the bearer
|
|
996
|
+
middleware discards it, so an authed action 401s, wire-indistinguishable from
|
|
997
|
+
`anonymous` (pinned by the `browser_bearer_replay` equivalence group). The
|
|
998
|
+
`token` principal is the honored counterpart (suppresses `Origin`).
|
|
979
999
|
`expired_session` is the keeper behind an expired server-side session
|
|
980
1000
|
(`fixture.mint_expired_session()`: a backdated `auth_session` row behind a
|
|
981
1001
|
still-valid signed cookie, so the DB-row expiry gate is what refuses it).
|
|
@@ -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;AA4B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAU9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AA8FD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAU9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AA8FD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAumBzF,CAAC"}
|
|
@@ -445,7 +445,10 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
445
445
|
path: rpc_path,
|
|
446
446
|
spec: account_purge_action_spec,
|
|
447
447
|
params: { account_id: target.account.id, confirm: true },
|
|
448
|
-
// Keeper-gated: daemon-token credential, not a session.
|
|
448
|
+
// Keeper-gated: daemon-token credential, not a session. Suppress the
|
|
449
|
+
// default `origin` header — the daemon-token middleware discards the
|
|
450
|
+
// credential in a browser context (Origin/Referer present).
|
|
451
|
+
suppress_default_origin: true,
|
|
449
452
|
headers: fixture.create_daemon_token_headers(),
|
|
450
453
|
});
|
|
451
454
|
assert.ok(res.ok, `account_purge failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
@@ -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,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIvD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,YAAY,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,aAAa,KACnB,OAAO,CAAC,yBAAyB,
|
|
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,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIvD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,YAAY,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,aAAa,KACnB,OAAO,CAAC,yBAAyB,CAwBnC,CAAC"}
|
|
@@ -22,6 +22,14 @@ export const bootstrap_backend = async (config) => {
|
|
|
22
22
|
return {
|
|
23
23
|
...handle,
|
|
24
24
|
keeper_transport,
|
|
25
|
+
// Origin-free twin carrying the same keeper cookie jar — the
|
|
26
|
+
// daemon-token middleware discards the credential in a browser
|
|
27
|
+
// context, so the `_testing_*` daemon-token calls route through this.
|
|
28
|
+
keeper_daemon_transport: create_fetch_transport({
|
|
29
|
+
base_url: config.base_url,
|
|
30
|
+
initial_cookies: keeper.cookies,
|
|
31
|
+
origin: null,
|
|
32
|
+
}),
|
|
25
33
|
keeper_account: keeper.account,
|
|
26
34
|
keeper_actor: keeper.actor,
|
|
27
35
|
keeper_cookies: keeper.cookies,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_crud.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_crud.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsD9B,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,YAAY,CAAC;AAGzD,eAAO,MAAM,8BAA8B,GAAI,SAAS,wBAAwB,KAAG,
|
|
1
|
+
{"version":3,"file":"cell_crud.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_crud.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsD9B,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,YAAY,CAAC;AAGzD,eAAO,MAAM,8BAA8B,GAAI,SAAS,wBAAwB,KAAG,IA6VlF,CAAC"}
|
|
@@ -83,6 +83,29 @@ export const describe_cell_crud_cross_tests = (options) => {
|
|
|
83
83
|
assert.ok(!gone.ok, 'deleted cell still readable');
|
|
84
84
|
assert.strictEqual(error_reason(gone), 'cell_not_found');
|
|
85
85
|
});
|
|
86
|
+
test_if(capabilities.cell_crud, 'refs are deduped and lexicographically sorted (deterministic, cross-impl-identical)', async () => {
|
|
87
|
+
const fixture = await setup_test();
|
|
88
|
+
const owner = await fixture.create_account({ username: 'cell_refs_owner' });
|
|
89
|
+
const t = fixture.fresh_transport();
|
|
90
|
+
const owner_headers = owner.create_session_headers();
|
|
91
|
+
// Three valid distinct fact hashes that sort a < b < c, embedded in
|
|
92
|
+
// `data` in a deliberately unsorted, nested, duplicated order. Both
|
|
93
|
+
// spines must derive `refs` as the sorted [a, b, c] regardless of
|
|
94
|
+
// traversal order or `HashSet` iteration — the byte-identical
|
|
95
|
+
// cross-backend contract (Rust `extract_refs` ↔ TS `derive_refs`).
|
|
96
|
+
const ref_a = `blake3:${'a'.repeat(64)}`;
|
|
97
|
+
const ref_b = `blake3:${'b'.repeat(64)}`;
|
|
98
|
+
const ref_c = `blake3:${'c'.repeat(64)}`;
|
|
99
|
+
const sorted = [ref_a, ref_b, ref_c];
|
|
100
|
+
const created = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note', cover: ref_c, nested: { attachments: [ref_b, ref_a, ref_b] } } }, owner_headers), CellCreateOutput);
|
|
101
|
+
assert.deepStrictEqual(created.cell.refs, sorted, 'create did not return sorted refs');
|
|
102
|
+
// Read it back — the stored column round-trips sorted too.
|
|
103
|
+
const got = expect_output(await cross_rpc_call(t, rpc_path, 'cell_get', { id: created.cell.id }, owner_headers), CellGetOutput);
|
|
104
|
+
assert.deepStrictEqual(got.cell.refs, sorted, 'get did not return sorted refs');
|
|
105
|
+
// Update reorders the embedded refs — the new column is still sorted.
|
|
106
|
+
const updated = expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: created.cell.id, data: { refs: [ref_b, ref_c, ref_a] } }, owner_headers), CellUpdateOutput);
|
|
107
|
+
assert.deepStrictEqual(updated.cell.refs, sorted, 'update did not return sorted refs');
|
|
108
|
+
});
|
|
86
109
|
test_if(capabilities.cell_crud, 'anon sees public cells only; private is 404', async () => {
|
|
87
110
|
const fixture = await setup_test();
|
|
88
111
|
const owner = await fixture.create_account({ username: 'cell_anon_owner' });
|
|
@@ -33,8 +33,39 @@ import { z } from 'zod';
|
|
|
33
33
|
* - `keeper` — the per-test bootstrapped keeper (holds `ROLE_KEEPER` +
|
|
34
34
|
* `ROLE_ADMIN`), session credential.
|
|
35
35
|
* - `daemon` — the keeper authenticated via the daemon-token header.
|
|
36
|
+
* - `invalid_daemon` — a *malformed/invalid* `X-Daemon-Token` carried
|
|
37
|
+
* alongside the keeper's session cookie, over a non-browser (no-Origin)
|
|
38
|
+
* transport. The middleware soft-fail-discards the invalid daemon token
|
|
39
|
+
* (matching the Rust spine's `None`), so auth falls through to the session
|
|
40
|
+
* leg: the request authenticates as the keeper-via-session and a
|
|
41
|
+
* daemon-gated action then refuses the session credential with
|
|
42
|
+
* `credential_type_required` — not a hard `invalid_daemon_token` 401. The
|
|
43
|
+
* no-Origin transport keeps the daemon token on the invalid-token path
|
|
44
|
+
* rather than the browser-context discard; the session base credential is
|
|
45
|
+
* what makes the credential-type gate (not the auth gate) the refusing
|
|
46
|
+
* layer (without it the discard would 401 anonymous).
|
|
47
|
+
* - `daemon_browser` — a *valid* `X-Daemon-Token` carried in a browser
|
|
48
|
+
* context (default `Origin` present) alongside the keeper's session cookie.
|
|
49
|
+
* Browsers attach `Origin` automatically; the daemon-token middleware
|
|
50
|
+
* discards a header-bearing daemon token as browser context (mirroring the
|
|
51
|
+
* bearer guard and the Rust spine's `is_browser_context`), so the *valid*
|
|
52
|
+
* token is dropped and auth falls through to the session leg → a daemon-gated
|
|
53
|
+
* action then refuses the session credential with `credential_type_required`.
|
|
54
|
+
* Distinct from `invalid_daemon`: here the token is well-formed and current,
|
|
55
|
+
* so a 403 (not a 400 confirm-guard hit like `daemon`) proves the
|
|
56
|
+
* browser-context discard fired — a valid daemon token does NOT authenticate
|
|
57
|
+
* when an `Origin` is present. `Origin` is deliberately NOT suppressed; its
|
|
58
|
+
* presence is the signal under test.
|
|
36
59
|
* - `token` — the keeper authenticated via a bearer api-token (non-browser
|
|
37
60
|
* context; the runner suppresses `Origin` so the token isn't discarded).
|
|
61
|
+
* - `bearer_browser` — a *valid* bearer api-token carried in a browser context
|
|
62
|
+
* (default `Origin` present), fresh jar so NO session rides alongside. The
|
|
63
|
+
* bearer middleware discards the token as browser context (mirroring the
|
|
64
|
+
* daemon guard + the Rust spine's `is_browser_context`), so the request
|
|
65
|
+
* arrives anonymous and an authed action 401s. Proves a stolen bearer cannot
|
|
66
|
+
* be replayed from a browser — wire-indistinguishable from sending no
|
|
67
|
+
* credential (the `token` principal is the honored counterpart: it suppresses
|
|
68
|
+
* `Origin`, so the same token authenticates).
|
|
38
69
|
* - `anonymous` — no credential, fresh cookie jar.
|
|
39
70
|
* - `fresh_non_admin` — a freshly minted account with no roles, session
|
|
40
71
|
* credential (via the production invite → signup → login flow).
|
|
@@ -53,6 +84,9 @@ export declare const ConformancePrincipal: z.ZodEnum<{
|
|
|
53
84
|
keeper: "keeper";
|
|
54
85
|
token: "token";
|
|
55
86
|
daemon: "daemon";
|
|
87
|
+
invalid_daemon: "invalid_daemon";
|
|
88
|
+
daemon_browser: "daemon_browser";
|
|
89
|
+
bearer_browser: "bearer_browser";
|
|
56
90
|
anonymous: "anonymous";
|
|
57
91
|
fresh_non_admin: "fresh_non_admin";
|
|
58
92
|
role_holder: "role_holder";
|
|
@@ -68,6 +102,9 @@ export declare const ConformanceCaseRequest: z.ZodObject<{
|
|
|
68
102
|
keeper: "keeper";
|
|
69
103
|
token: "token";
|
|
70
104
|
daemon: "daemon";
|
|
105
|
+
invalid_daemon: "invalid_daemon";
|
|
106
|
+
daemon_browser: "daemon_browser";
|
|
107
|
+
bearer_browser: "bearer_browser";
|
|
71
108
|
anonymous: "anonymous";
|
|
72
109
|
fresh_non_admin: "fresh_non_admin";
|
|
73
110
|
role_holder: "role_holder";
|
|
@@ -120,6 +157,9 @@ export declare const ConformanceCase: z.ZodObject<{
|
|
|
120
157
|
keeper: "keeper";
|
|
121
158
|
token: "token";
|
|
122
159
|
daemon: "daemon";
|
|
160
|
+
invalid_daemon: "invalid_daemon";
|
|
161
|
+
daemon_browser: "daemon_browser";
|
|
162
|
+
bearer_browser: "bearer_browser";
|
|
123
163
|
anonymous: "anonymous";
|
|
124
164
|
fresh_non_admin: "fresh_non_admin";
|
|
125
165
|
role_holder: "role_holder";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance_case.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_case.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB
|
|
1
|
+
{"version":3,"file":"conformance_case.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_case.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAY/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,6CAA6C;AAC7C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;kBAgBjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B;;;;;;kBAiDrC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEpF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;;;kBAK/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAS1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -33,8 +33,39 @@ import { z } from 'zod';
|
|
|
33
33
|
* - `keeper` — the per-test bootstrapped keeper (holds `ROLE_KEEPER` +
|
|
34
34
|
* `ROLE_ADMIN`), session credential.
|
|
35
35
|
* - `daemon` — the keeper authenticated via the daemon-token header.
|
|
36
|
+
* - `invalid_daemon` — a *malformed/invalid* `X-Daemon-Token` carried
|
|
37
|
+
* alongside the keeper's session cookie, over a non-browser (no-Origin)
|
|
38
|
+
* transport. The middleware soft-fail-discards the invalid daemon token
|
|
39
|
+
* (matching the Rust spine's `None`), so auth falls through to the session
|
|
40
|
+
* leg: the request authenticates as the keeper-via-session and a
|
|
41
|
+
* daemon-gated action then refuses the session credential with
|
|
42
|
+
* `credential_type_required` — not a hard `invalid_daemon_token` 401. The
|
|
43
|
+
* no-Origin transport keeps the daemon token on the invalid-token path
|
|
44
|
+
* rather than the browser-context discard; the session base credential is
|
|
45
|
+
* what makes the credential-type gate (not the auth gate) the refusing
|
|
46
|
+
* layer (without it the discard would 401 anonymous).
|
|
47
|
+
* - `daemon_browser` — a *valid* `X-Daemon-Token` carried in a browser
|
|
48
|
+
* context (default `Origin` present) alongside the keeper's session cookie.
|
|
49
|
+
* Browsers attach `Origin` automatically; the daemon-token middleware
|
|
50
|
+
* discards a header-bearing daemon token as browser context (mirroring the
|
|
51
|
+
* bearer guard and the Rust spine's `is_browser_context`), so the *valid*
|
|
52
|
+
* token is dropped and auth falls through to the session leg → a daemon-gated
|
|
53
|
+
* action then refuses the session credential with `credential_type_required`.
|
|
54
|
+
* Distinct from `invalid_daemon`: here the token is well-formed and current,
|
|
55
|
+
* so a 403 (not a 400 confirm-guard hit like `daemon`) proves the
|
|
56
|
+
* browser-context discard fired — a valid daemon token does NOT authenticate
|
|
57
|
+
* when an `Origin` is present. `Origin` is deliberately NOT suppressed; its
|
|
58
|
+
* presence is the signal under test.
|
|
36
59
|
* - `token` — the keeper authenticated via a bearer api-token (non-browser
|
|
37
60
|
* context; the runner suppresses `Origin` so the token isn't discarded).
|
|
61
|
+
* - `bearer_browser` — a *valid* bearer api-token carried in a browser context
|
|
62
|
+
* (default `Origin` present), fresh jar so NO session rides alongside. The
|
|
63
|
+
* bearer middleware discards the token as browser context (mirroring the
|
|
64
|
+
* daemon guard + the Rust spine's `is_browser_context`), so the request
|
|
65
|
+
* arrives anonymous and an authed action 401s. Proves a stolen bearer cannot
|
|
66
|
+
* be replayed from a browser — wire-indistinguishable from sending no
|
|
67
|
+
* credential (the `token` principal is the honored counterpart: it suppresses
|
|
68
|
+
* `Origin`, so the same token authenticates).
|
|
38
69
|
* - `anonymous` — no credential, fresh cookie jar.
|
|
39
70
|
* - `fresh_non_admin` — a freshly minted account with no roles, session
|
|
40
71
|
* credential (via the production invite → signup → login flow).
|
|
@@ -52,7 +83,10 @@ import { z } from 'zod';
|
|
|
52
83
|
export const ConformancePrincipal = z.enum([
|
|
53
84
|
'keeper',
|
|
54
85
|
'daemon',
|
|
86
|
+
'invalid_daemon',
|
|
87
|
+
'daemon_browser',
|
|
55
88
|
'token',
|
|
89
|
+
'bearer_browser',
|
|
56
90
|
'anonymous',
|
|
57
91
|
'fresh_non_admin',
|
|
58
92
|
'role_holder',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAOjE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAC,KAAK,eAAe,EAA4B,MAAM,uBAAuB,CAAC;AACtF,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,YAAY,CAAC;AAGvD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAyJD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AAgKF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,uBAAuB,KAAG,IAkEnF,CAAC"}
|
|
@@ -25,10 +25,21 @@ import '../assert_dev_env.js';
|
|
|
25
25
|
* @module
|
|
26
26
|
*/
|
|
27
27
|
import { assert, describe, test } from 'vitest';
|
|
28
|
+
import { DAEMON_TOKEN_HEADER } from '../../auth/daemon_token.js';
|
|
28
29
|
import { find_auth_route, rest_auth_route_suffixes, } from '../integration_helpers.js';
|
|
29
30
|
import { find_rpc_action, headers_to_record, rpc_call, resolve_rpc_endpoints_for_setup, } from '../rpc_helpers.js';
|
|
30
31
|
import {} from './conformance_case.js';
|
|
31
32
|
import { xfail_until } from './xfail.js';
|
|
33
|
+
/**
|
|
34
|
+
* A deliberately malformed `X-Daemon-Token` value for the `invalid_daemon`
|
|
35
|
+
* principal. Fails the `DaemonToken` Zod schema (not 43 base64url chars), so
|
|
36
|
+
* the daemon-token middleware soft-fail-discards it rather than authenticating
|
|
37
|
+
* — the request falls through with no identity, surfacing
|
|
38
|
+
* `credential_type_required` on a daemon-gated action (matching the Rust
|
|
39
|
+
* spine's `None`). Sent over a no-Origin transport so it lands on the
|
|
40
|
+
* invalid-token path, not the browser-context discard.
|
|
41
|
+
*/
|
|
42
|
+
const INVALID_DAEMON_TOKEN_VALUE = 'not-a-valid-daemon-token';
|
|
32
43
|
/**
|
|
33
44
|
* Map a `ConformancePrincipal` onto the transport + headers it
|
|
34
45
|
* authenticates with, reading exclusively from the per-test `TestFixture`.
|
|
@@ -51,6 +62,39 @@ const resolve_principal = async (fixture, as, principals) => {
|
|
|
51
62
|
headers: fixture.create_daemon_token_headers(),
|
|
52
63
|
suppress_default_origin: true,
|
|
53
64
|
};
|
|
65
|
+
case 'invalid_daemon':
|
|
66
|
+
// A malformed `X-Daemon-Token` carried ALONGSIDE the keeper's session
|
|
67
|
+
// cookie, over a non-browser (no-Origin) transport. The invalid daemon
|
|
68
|
+
// token is soft-fail-discarded (matching the Rust spine's `None`), so
|
|
69
|
+
// auth falls through to the session leg: the request authenticates as
|
|
70
|
+
// the keeper-via-session (clearing `require_auth`), then the daemon-gated
|
|
71
|
+
// action's credential-type gate refuses the session credential →
|
|
72
|
+
// `403 credential_type_required`. No-Origin keeps the daemon token on
|
|
73
|
+
// the invalid-token path, NOT the browser-context discard. Without the
|
|
74
|
+
// base credential this would 401 (anonymous) — the session is what makes
|
|
75
|
+
// the *credential-type* gate, not the auth gate, the refusing layer.
|
|
76
|
+
return {
|
|
77
|
+
transport: fixture.fresh_transport({ origin: null }),
|
|
78
|
+
headers: fixture.create_session_headers({
|
|
79
|
+
[DAEMON_TOKEN_HEADER]: INVALID_DAEMON_TOKEN_VALUE,
|
|
80
|
+
}),
|
|
81
|
+
suppress_default_origin: true,
|
|
82
|
+
};
|
|
83
|
+
case 'daemon_browser':
|
|
84
|
+
// A VALID `X-Daemon-Token` carried in a browser context (default Origin
|
|
85
|
+
// present) ALONGSIDE the keeper's session cookie. The daemon-token
|
|
86
|
+
// middleware discards a header-bearing daemon token as browser context
|
|
87
|
+
// (mirroring the bearer guard + the Rust spine's `is_browser_context`),
|
|
88
|
+
// so the valid token is dropped and auth falls through to the session
|
|
89
|
+
// leg → keeper-via-session → a daemon-gated action refuses the session
|
|
90
|
+
// credential with `credential_type_required`. Origin is NOT suppressed —
|
|
91
|
+
// its presence is the browser-context signal under test. Unlike the
|
|
92
|
+
// `daemon` principal (no Origin → token honored → reaches the 400 confirm
|
|
93
|
+
// guard), the 403 here proves the valid token was discarded, not honored.
|
|
94
|
+
return {
|
|
95
|
+
transport: fixture.transport,
|
|
96
|
+
headers: fixture.create_session_headers(fixture.create_daemon_token_headers()),
|
|
97
|
+
};
|
|
54
98
|
case 'token':
|
|
55
99
|
// Bearer is discarded in a browser context — empty jar + no Origin.
|
|
56
100
|
return {
|
|
@@ -58,6 +102,20 @@ const resolve_principal = async (fixture, as, principals) => {
|
|
|
58
102
|
headers: fixture.create_bearer_headers(),
|
|
59
103
|
suppress_default_origin: true,
|
|
60
104
|
};
|
|
105
|
+
case 'bearer_browser':
|
|
106
|
+
// A VALID bearer api-token in a browser context (default Origin
|
|
107
|
+
// present), fresh jar so no session rides alongside. The bearer
|
|
108
|
+
// middleware discards a header-bearing token as browser context
|
|
109
|
+
// (mirroring the daemon guard + the Rust spine's `is_browser_context`),
|
|
110
|
+
// so the request arrives anonymous → an authed action 401s. Origin is
|
|
111
|
+
// NOT suppressed (its presence is the browser-context signal) — the
|
|
112
|
+
// inverse of the `token` principal, which suppresses it so the same
|
|
113
|
+
// token is honored. The 401 proves the valid token was discarded, not
|
|
114
|
+
// honored (a non-discarding leg would authenticate → 200).
|
|
115
|
+
return {
|
|
116
|
+
transport: fixture.fresh_transport(),
|
|
117
|
+
headers: fixture.create_bearer_headers(),
|
|
118
|
+
};
|
|
61
119
|
case 'anonymous':
|
|
62
120
|
// Fresh jar so the keeper cookie (cross-process) can't leak in.
|
|
63
121
|
return { transport: fixture.fresh_transport(), headers: {} };
|
|
@@ -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;AAiD9B,OAAO,KAAK,EAAC,wBAAwB,EAAE,SAAS,EAAC,MAAM,YAAY,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,2BAA4B,SAAQ,wBAAwB;IAC5E,QAAQ,CAAC,sBAAsB,CAAC,EAAE,SAAS,CAAC;CAC5C;AA8BD,eAAO,MAAM,iCAAiC,GAAI,SAAS,2BAA2B,KAAG,
|
|
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;AAiD9B,OAAO,KAAK,EAAC,wBAAwB,EAAE,SAAS,EAAC,MAAM,YAAY,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,2BAA4B,SAAQ,wBAAwB;IAC5E,QAAQ,CAAC,sBAAsB,CAAC,EAAE,SAAS,CAAC;CAC5C;AA8BD,eAAO,MAAM,iCAAiC,GAAI,SAAS,2BAA2B,KAAG,IAiOxF,CAAC"}
|
|
@@ -68,7 +68,11 @@ export const describe_fact_serving_cross_tests = (options) => {
|
|
|
68
68
|
const { setup_test, setup_test_multi_actor, capabilities } = options;
|
|
69
69
|
const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
|
|
70
70
|
/** Seed an embedded fact over the daemon channel; returns its `blake3:` hash. */
|
|
71
|
-
const put_fact = async (fixture, content, content_type) => expect_output(await cross_rpc_call(
|
|
71
|
+
const put_fact = async (fixture, content, content_type) => expect_output(await cross_rpc_call(
|
|
72
|
+
// Origin-free transport — the daemon-token middleware discards the
|
|
73
|
+
// credential when an `Origin` / `Referer` is present (browser
|
|
74
|
+
// context), and cross-process `fixture.transport` auto-adds `Origin`.
|
|
75
|
+
fixture.fresh_transport({ origin: null }), rpc_path, '_testing_put_fact', content_type === undefined ? { content } : { content, content_type }, fixture.create_daemon_token_headers()), TestingPutFactOutput).hash;
|
|
72
76
|
/**
|
|
73
77
|
* Create a cell referencing `hash` (`extract_refs` lifts it into `cell.refs`).
|
|
74
78
|
* Pass `acting` when the caller's account holds more than one actor —
|
|
@@ -253,6 +253,11 @@ export interface RpcPathCrossSuiteOptions extends CrossSuiteOptions {
|
|
|
253
253
|
* const bootstrapped: BootstrappedBackendHandle = {
|
|
254
254
|
* ...handle,
|
|
255
255
|
* keeper_transport,
|
|
256
|
+
* keeper_daemon_transport: create_fetch_transport({
|
|
257
|
+
* base_url: config.base_url,
|
|
258
|
+
* initial_cookies: keeper.cookies,
|
|
259
|
+
* origin: null,
|
|
260
|
+
* }),
|
|
256
261
|
* keeper_account: keeper.account,
|
|
257
262
|
* keeper_actor: keeper.actor,
|
|
258
263
|
* keeper_cookies: keeper.cookies,
|
|
@@ -268,6 +273,18 @@ export interface RpcPathCrossSuiteOptions extends CrossSuiteOptions {
|
|
|
268
273
|
export interface BootstrappedBackendHandle extends BackendHandle {
|
|
269
274
|
/** Transport carrying the keeper session cookie + cookie jar. */
|
|
270
275
|
readonly keeper_transport: FetchTransport;
|
|
276
|
+
/**
|
|
277
|
+
* Daemon-token transport for the keeper — same cookie jar as
|
|
278
|
+
* `keeper_transport` but with `origin: null` so it sends no `Origin`
|
|
279
|
+
* header. The daemon-token middleware discards the credential in a
|
|
280
|
+
* browser context (any `Origin` / `Referer`), so the `_testing_*`
|
|
281
|
+
* daemon-token calls (`_testing_reset` / `_testing_schema_snapshot` /
|
|
282
|
+
* `_testing_action_manifest` / `_testing_mint_session`) must route
|
|
283
|
+
* through this Origin-free transport. CSRF/cookie-session calls
|
|
284
|
+
* (`invite_create`, `role_grant_offer_create`) stay on `keeper_transport`,
|
|
285
|
+
* which keeps its default `Origin`.
|
|
286
|
+
*/
|
|
287
|
+
readonly keeper_daemon_transport: FetchTransport;
|
|
271
288
|
/** Keeper account JSON captured from `POST /bootstrap`. */
|
|
272
289
|
readonly keeper_account: {
|
|
273
290
|
readonly id: Uuid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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"}
|