@ultimat3/core 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 +2 -2
- package/src/index.ts +5 -0
- package/src/metrics.ts +8 -5
- package/src/same-origin.ts +51 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/core",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.1.0",
|
|
4
4
|
"description": "Ultimate's foundation: errors, context, env, config, clock, ids, logging, telemetry, lifecycle",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"test": "bun test"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@ultimat3/schema": "22.
|
|
43
|
+
"@ultimat3/schema": "22.1.0"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -590,6 +590,11 @@ export { DEFAULT_ROLE, isRole, ROLE_INFO, ROLES, resolveRole } from './roles';
|
|
|
590
590
|
export type { HydrateStrategy, OfflineStrategy, RenderMode } from './route-vocabulary';
|
|
591
591
|
export { HYDRATE_STRATEGIES, OFFLINE_STRATEGIES, RENDER_MODES } from './route-vocabulary';
|
|
592
592
|
export { safeUrl, URL_ATTRIBUTES } from './safe-url';
|
|
593
|
+
export {
|
|
594
|
+
type OriginEvidence,
|
|
595
|
+
type OriginVerdict,
|
|
596
|
+
proveSameOrigin,
|
|
597
|
+
} from './same-origin';
|
|
593
598
|
export {
|
|
594
599
|
defineService,
|
|
595
600
|
installedServices,
|
package/src/metrics.ts
CHANGED
|
@@ -391,11 +391,10 @@ export function counter(name: string, options?: InstrumentOptions): Counter {
|
|
|
391
391
|
export function gauge(name: string, options?: GaugeOptions): Gauge {
|
|
392
392
|
const instrument = declare(name, 'gauge', options ?? {});
|
|
393
393
|
return {
|
|
394
|
-
// The screen runs BEFORE the series is resolved,
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
// back into one expression.
|
|
394
|
+
// The screen runs BEFORE the series is resolved, as in `counter.add`: a refused value must
|
|
395
|
+
// cost nothing, and `seriesFor` MINTS a series — bounded in number, kept for the process's
|
|
396
|
+
// life, able to trip the cardinality ceiling. `a.b += finite(…)` evaluates the reference
|
|
397
|
+
// first, so the two cannot be folded into one expression.
|
|
399
398
|
record(value, attributes = {}): void {
|
|
400
399
|
if (!enabled) return;
|
|
401
400
|
const observed = finite(name, value);
|
|
@@ -443,6 +442,10 @@ function pointsOf(instrument: Instrument): readonly MetricPoint[] {
|
|
|
443
442
|
return [];
|
|
444
443
|
}
|
|
445
444
|
}
|
|
445
|
+
// Declared counter, no sample: 0, not absent (`rate()` needs a series) — until one exists.
|
|
446
|
+
const unsampled =
|
|
447
|
+
enabled && instrument.descriptor.kind === 'counter' && instrument.series.size === 0;
|
|
448
|
+
if (unsampled) return [{ attributes: {}, value: 0 }];
|
|
446
449
|
return [...instrument.series.values()].map((series) =>
|
|
447
450
|
instrument.descriptor.kind === 'histogram'
|
|
448
451
|
? {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Single responsibility: the one rule for "did this request carrying an AMBIENT credential come
|
|
2
|
+
// from this app?" — `@ultimat3/http`'s CSRF check asks it of an unsafe write, and
|
|
3
|
+
// `@ultimat3/realtime`'s sync node of a websocket upgrade. One rule, so a sibling origin cannot be
|
|
4
|
+
// let in by one surface and refused by the other.
|
|
5
|
+
|
|
6
|
+
/** The complete `Sec-Fetch-Site` vocabulary. Anything else was written by a non-browser. */
|
|
7
|
+
const KNOWN_SITES = new Set(['same-origin', 'same-site', 'cross-site', 'none']);
|
|
8
|
+
|
|
9
|
+
export interface OriginEvidence {
|
|
10
|
+
/** The origins this app is reached on. More than one when the scheme is not knowable here. */
|
|
11
|
+
readonly selfOrigins: readonly string[];
|
|
12
|
+
readonly origin: string | null;
|
|
13
|
+
readonly secFetchSite: string | null;
|
|
14
|
+
/** An EXACT allowance for a sibling origin — never a wildcard, never a suffix match. */
|
|
15
|
+
readonly listed: (origin: string) => boolean;
|
|
16
|
+
/** Where the operator adds an origin, named in the refusal: `http.cors.origins`, `SYNC_ORIGINS`. */
|
|
17
|
+
readonly listName: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type OriginVerdict =
|
|
21
|
+
| { readonly ok: true }
|
|
22
|
+
/** Why it was refused, in terms the caller can act on. Never echoes a header verbatim. */
|
|
23
|
+
| { readonly ok: false; readonly reason: string };
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `sec-fetch-site` first because it is the browser's own answer and cannot be set by script;
|
|
27
|
+
* `Origin` second, so an app that lists a sibling origin keeps working. `same-site` is NOT proof:
|
|
28
|
+
* a sibling subdomain is same-site, and a `SameSite=Lax` cookie rides its requests. A request with
|
|
29
|
+
* neither header is refused — "we could not tell" is the case this exists for.
|
|
30
|
+
*/
|
|
31
|
+
export function proveSameOrigin(evidence: OriginEvidence): OriginVerdict {
|
|
32
|
+
const site = evidence.secFetchSite;
|
|
33
|
+
if (site === 'same-origin' || site === 'none') return { ok: true };
|
|
34
|
+
const origin = evidence.origin;
|
|
35
|
+
if (origin !== null && evidence.selfOrigins.includes(origin)) return { ok: true };
|
|
36
|
+
if (origin !== null && evidence.listed(origin)) return { ok: true };
|
|
37
|
+
// Only the four values a browser can send are quoted back. Anything else is a client that
|
|
38
|
+
// wrote the header itself, and echoing what it wrote is how a rejected value reaches the log
|
|
39
|
+
// store and the response body.
|
|
40
|
+
if (site !== null) {
|
|
41
|
+
const known = KNOWN_SITES.has(site) ? site : 'a value no browser sends';
|
|
42
|
+
return { ok: false, reason: `the request reported sec-fetch-site: ${known}` };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
reason:
|
|
47
|
+
origin === null
|
|
48
|
+
? 'the request carried neither sec-fetch-site nor origin, so it cannot be shown to be same-origin'
|
|
49
|
+
: `the origin it declares is not this app and is not listed in ${evidence.listName}`,
|
|
50
|
+
};
|
|
51
|
+
}
|