@ultimat3/http 22.0.0 → 22.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/http",
3
- "version": "22.0.0",
3
+ "version": "22.1.0",
4
4
  "description": "Owned request lifecycle over Bun.serve: router, ordered pipeline, problem+json errors",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,9 +31,9 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/core": "22.0.0",
35
- "@ultimat3/i18n": "22.0.0",
36
- "@ultimat3/schema": "22.0.0",
37
- "@ultimat3/time": "22.0.0"
34
+ "@ultimat3/core": "22.1.0",
35
+ "@ultimat3/i18n": "22.1.0",
36
+ "@ultimat3/schema": "22.1.0",
37
+ "@ultimat3/time": "22.1.0"
38
38
  }
39
39
  }
package/src/csrf.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  // went through. `setRedirect` exists so those form posts work without JS, which makes them a
6
6
  // first-class surface here rather than a legacy one.
7
7
 
8
- import { classifyAddress } from '@ultimat3/core';
8
+ import { classifyAddress, proveSameOrigin } from '@ultimat3/core';
9
9
  import type { CorsConfig } from './cors';
10
10
  import { originListed } from './cors';
11
11
  import { HttpError } from './errors';
@@ -26,9 +26,6 @@ export const DEFAULT_CSRF: CsrfConfig = { mode: 'origin' };
26
26
  /** Methods with no side effects, per RFC 9110. A CSRF check on these is a check on nothing. */
27
27
  const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS', 'TRACE']);
28
28
 
29
- /** The complete `Sec-Fetch-Site` vocabulary. Anything else was written by a non-browser. */
30
- const KNOWN_SITES = new Set(['same-origin', 'same-site', 'cross-site', 'none']);
31
-
32
29
  export interface CsrfCheckInput {
33
30
  readonly method: string;
34
31
  /** The origin this app was reached on — scheme from `ctx.https`, host from the request URL. */
@@ -60,29 +57,21 @@ export const checkCsrf = (input: CsrfCheckInput): CsrfVerdict => {
60
57
  if (input.anonymous) return { ok: true };
61
58
  if (input.hasAuthorizationHeader) return { ok: true };
62
59
 
63
- const site = input.secFetchSite;
64
- if (site === 'same-origin' || site === 'none') return { ok: true };
65
- if (input.origin === input.selfOrigin) return { ok: true };
60
+ // The rule itself is core's, shared with the sync node's upgrade: one answer to "did this
61
+ // come from this app?" on every surface an ambient credential reaches.
62
+ //
66
63
  // `originListed`, never `allowedOrigin`: that one answers the RESPONSE header, and for
67
64
  // `origins: ['*'], credentials: false` — the only wildcard `assertCorsConfig` admits — its
68
65
  // answer is `'*'`, which is not null and so read as "this origin is one we allow". A
69
66
  // credentialed cross-site POST from evil.test was therefore accepted by the check that exists
70
67
  // to refuse exactly it. An exact match is the only allowance a write may be built on.
71
- if (originListed(input.cors, input.origin)) return { ok: true };
72
- // Only the four values a browser can send are quoted back. Anything else is a client that
73
- // wrote the header itself, and echoing what it wrote is how a rejected value reaches the log
74
- // store and the response body — the same defect the error-map stage's log line had.
75
- if (site !== null) {
76
- const known = KNOWN_SITES.has(site) ? site : 'a value no browser sends';
77
- return { ok: false, reason: `the request reported sec-fetch-site: ${known}` };
78
- }
79
- return {
80
- ok: false,
81
- reason:
82
- input.origin === null
83
- ? 'the request carried neither sec-fetch-site nor origin, so it cannot be shown to be same-origin'
84
- : 'the origin it declares is not this app and is not listed in http.cors.origins',
85
- };
68
+ return proveSameOrigin({
69
+ selfOrigins: [input.selfOrigin],
70
+ origin: input.origin,
71
+ secFetchSite: input.secFetchSite,
72
+ listed: (origin) => originListed(input.cors, origin),
73
+ listName: 'http.cors.origins',
74
+ });
86
75
  };
87
76
 
88
77
  /** The origin a browser compares against — the PUBLIC one, so a TLS-terminating proxy agrees. */
package/src/error-map.ts CHANGED
@@ -425,6 +425,11 @@ export const ERROR_STATUS = {
425
425
  X_REALTIME_UNINSTALLED: 500,
426
426
  // Boot-time: a realtime config and its environment that disagree refuse before any request.
427
427
  X_REALTIME_TOPOLOGY: 500,
428
+ // The replicator's own Postgres connection failed TLS. A role that never answers a request, so the
429
+ // row keeps the table closed at 500, beside `X_REALTIME_TOPOLOGY`.
430
+ X_REPLICATION_TLS: 500,
431
+ // A browser page on a foreign origin asked for a socket — refused, not unauthenticated: 403.
432
+ X_SOCKET_ORIGIN_REFUSED: 403,
428
433
  X_RECORD_KEY_MISSING: 500,
429
434
  X_RECORD_REJECTED: 500,
430
435
  X_SYNC_UNCONFIGURED: 500,