@rogatio/cli 6.21.0 → 6.21.2
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/README.md +1 -1
- package/dist/node/index.js +153 -100
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ rogatio <command> [options]
|
|
|
42
42
|
| `rogatio import requestly <export.json>` | Migrate a Requestly export into `.rogatio.json`. `--out <path>` chooses the file (default `.rogatio.json`); `--merge` appends onto an existing project; `--json` prints the report. |
|
|
43
43
|
| `rogatio test [path] [url...]` | Run offline dry-run tests. Any filename is accepted; the default is `.rogatio.json` in the current directory. |
|
|
44
44
|
| `rogatio ai <setup\|ls\|show\|delete\|test>` | AI provider configuration. `setup` interactive; `ls` list; `show` redacted; `delete` remove; `test` connection. |
|
|
45
|
-
| `rogatio runtime <install\|uninstall\|verify>` | Register the native-messaging host with `install` and trust the device-local CA in one **transactional** call; it needs elevated privileges (Linux `sudo`, macOS keychain authorization, Windows Administrator) and rolls back with exit `1` and `trust unsupported: <reasons>` when a capability or elevation is missing. Release users never pass `--extension-id`. That flag is only for development (a local unpacked build without the release key) and for forks. `uninstall` removes the host manifest, the device-local CA files, and the CA trust installation (idempotent). `verify` reports whether manifest, `runtime-host` wrapper, allowed origins, and CA trust are all valid, and fails when `allowed_origins` omits the release ID. Release users never pass `--extension-id` on verify either. |
|
|
45
|
+
| `rogatio runtime <install\|uninstall\|verify>` | Register the native-messaging host with `install` and trust the device-local CA in one **transactional** call; it needs elevated privileges (Linux `sudo`, macOS keychain authorization, Windows Administrator) and rolls back with exit `1` and `trust unsupported: <reasons>` when a capability or elevation is missing. Release users never pass `--extension-id`. That flag is only for development (a local unpacked build without the release key) and for forks. `uninstall` removes the host manifest, the device-local CA files, and the CA trust installation (idempotent). If the trust-store refresh fails, it exits `1` and does not print `runtime uninstall complete`. `verify` reports whether manifest, `runtime-host` wrapper, allowed origins, and CA trust are all valid, and fails when `allowed_origins` omits the release ID. Release users never pass `--extension-id` on verify either. |
|
|
46
46
|
| `rogatio runtime host [path]` | Run the consolidated native-messaging host for the project (pair/authorize/body transforms and mock responses over stdio). Normally launched by the browser; run manually only for debugging. `--root <dir>` overrides the confined mock file root. When omitted, a saved device-local root for that project file is used, otherwise the project directory. A stdin project that contains a file mock requires `--root`. |
|
|
47
47
|
|
|
48
48
|
`rogatio edit` includes **Run checks**. It calls `POST /e/<session>/api/doctor` (session path, loopback Origin, and `X-CSRF-Token`) and shows editor checks plus the same report as `rogatio doctor` for the open file. A provider configured after the page opened is picked up on the next `rogatio edit`.
|
package/dist/node/index.js
CHANGED
|
@@ -2306,7 +2306,7 @@ import { lstat as lstat3, mkdtemp, open as open4, rm as rm2 } from "node:fs/prom
|
|
|
2306
2306
|
import { tmpdir } from "node:os";
|
|
2307
2307
|
import { join as join3 } from "node:path";
|
|
2308
2308
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
|
|
2309
|
-
import { accessSync as accessSync2, constants as constants5, mkdirSync
|
|
2309
|
+
import { accessSync as accessSync2, constants as constants5, mkdirSync } from "node:fs";
|
|
2310
2310
|
import { dirname as dirname4 } from "node:path";
|
|
2311
2311
|
import { join as posixJoin2 } from "node:path/posix";
|
|
2312
2312
|
import { spawn as spawn3, spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -2318,18 +2318,19 @@ import { isAbsolute as isAbsolute3 } from "node:path";
|
|
|
2318
2318
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
2319
2319
|
import { existsSync as existsSync2 } from "node:fs";
|
|
2320
2320
|
import { join as join6 } from "node:path";
|
|
2321
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2321
2322
|
import { lookup as dnsLookup2 } from "node:dns/promises";
|
|
2322
2323
|
import {
|
|
2323
2324
|
createServer as createServer2
|
|
2324
2325
|
} from "node:http";
|
|
2325
2326
|
import { connect } from "node:net";
|
|
2326
2327
|
import { Worker } from "node:worker_threads";
|
|
2328
|
+
import { randomBytes } from "node:crypto";
|
|
2327
2329
|
import { lookup as dnsLookup } from "node:dns/promises";
|
|
2328
2330
|
import { request as httpRequest2 } from "node:http";
|
|
2329
2331
|
import { request as httpsRequest } from "node:https";
|
|
2330
|
-
import { createHash as createHash2, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2332
|
+
import { createHash as createHash2, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
2331
2333
|
import { spawn as spawn4 } from "node:child_process";
|
|
2332
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
2333
2334
|
import { createHash as createHash3 } from "node:crypto";
|
|
2334
2335
|
import { createHash as createHash4 } from "node:crypto";
|
|
2335
2336
|
var __create = Object.create;
|
|
@@ -23705,9 +23706,10 @@ var linuxAdapter = {
|
|
|
23705
23706
|
throw new TrustError("trust.internal", reason, [reason]);
|
|
23706
23707
|
},
|
|
23707
23708
|
async caTrustRemover() {
|
|
23708
|
-
|
|
23709
|
-
|
|
23710
|
-
|
|
23709
|
+
if (caBackend() === "debian") {
|
|
23710
|
+
await sudoOrThrow(["rm", "-f", DEBIAN_CERT]);
|
|
23711
|
+
await sudoOrThrow(["update-ca-certificates"]);
|
|
23712
|
+
return;
|
|
23711
23713
|
}
|
|
23712
23714
|
if (!toolOnPath("update-ca-trust")) return;
|
|
23713
23715
|
const present = P11_CERTS.filter((path2) => exists(path2));
|
|
@@ -25024,6 +25026,74 @@ async function sourceMatchesWithinDeadline(source, url) {
|
|
|
25024
25026
|
function matchUrlCapturesWithinDeadline(urlRegex, url) {
|
|
25025
25027
|
return runRegexExec(url, urlRegex, RUNTIME_LIMITS.maxRegexDeadlineMs);
|
|
25026
25028
|
}
|
|
25029
|
+
async function readMockFile(root, logicalPath, signal) {
|
|
25030
|
+
return readConfinedPathBytes(root, logicalPath, signal);
|
|
25031
|
+
}
|
|
25032
|
+
function mintToken() {
|
|
25033
|
+
return randomBytes(32).toString("hex");
|
|
25034
|
+
}
|
|
25035
|
+
function headerName(name) {
|
|
25036
|
+
return name.toLowerCase();
|
|
25037
|
+
}
|
|
25038
|
+
function waitForDelay(delayMs, signal) {
|
|
25039
|
+
if (signal?.aborted) return Promise.resolve(failure("runtime.timeout"));
|
|
25040
|
+
if (delayMs === void 0 || delayMs <= 0) {
|
|
25041
|
+
return Promise.resolve({ ok: true, value: void 0 });
|
|
25042
|
+
}
|
|
25043
|
+
return new Promise((resolve12) => {
|
|
25044
|
+
const onAbort = () => {
|
|
25045
|
+
clearTimeout(timer);
|
|
25046
|
+
resolve12(failure("runtime.timeout"));
|
|
25047
|
+
};
|
|
25048
|
+
const timer = setTimeout(() => {
|
|
25049
|
+
signal?.removeEventListener("abort", onAbort);
|
|
25050
|
+
resolve12({ ok: true, value: void 0 });
|
|
25051
|
+
}, delayMs);
|
|
25052
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
25053
|
+
});
|
|
25054
|
+
}
|
|
25055
|
+
async function renderMockResponse(options) {
|
|
25056
|
+
const { mock, fileRoot, method, signal } = options;
|
|
25057
|
+
const waited = await waitForDelay(mock.delayMs, signal);
|
|
25058
|
+
if (!waited.ok) return waited;
|
|
25059
|
+
let fullBytes;
|
|
25060
|
+
if (mock.body !== void 0) {
|
|
25061
|
+
fullBytes = new TextEncoder().encode(mock.body);
|
|
25062
|
+
} else if (mock.file !== void 0) {
|
|
25063
|
+
if (fileRoot === void 0) return failure("runtime.file-denied");
|
|
25064
|
+
if (!isConfinedFileSupported()) {
|
|
25065
|
+
return failure("runtime.platform-unsupported");
|
|
25066
|
+
}
|
|
25067
|
+
const read2 = await readMockFile(fileRoot, mock.file, signal);
|
|
25068
|
+
if (!read2.ok) return read2;
|
|
25069
|
+
fullBytes = read2.value;
|
|
25070
|
+
} else {
|
|
25071
|
+
return failure("runtime.file-denied");
|
|
25072
|
+
}
|
|
25073
|
+
const headers = [];
|
|
25074
|
+
let hasContentType = false;
|
|
25075
|
+
let hasCacheControl = false;
|
|
25076
|
+
for (const header of mock.headers ?? []) {
|
|
25077
|
+
const name = headerName(header.name);
|
|
25078
|
+
if (name === "content-length") continue;
|
|
25079
|
+
if (name === "content-type") hasContentType = true;
|
|
25080
|
+
if (name === "cache-control") hasCacheControl = true;
|
|
25081
|
+
headers.push([header.name, header.value]);
|
|
25082
|
+
}
|
|
25083
|
+
if (!hasContentType) {
|
|
25084
|
+
headers.push(["Content-Type", "application/octet-stream"]);
|
|
25085
|
+
}
|
|
25086
|
+
headers.push(["Content-Length", String(fullBytes.byteLength)]);
|
|
25087
|
+
if (!hasCacheControl) headers.push(["Cache-Control", "no-store"]);
|
|
25088
|
+
return {
|
|
25089
|
+
ok: true,
|
|
25090
|
+
value: {
|
|
25091
|
+
status: mock.status,
|
|
25092
|
+
headers,
|
|
25093
|
+
bodyBytes: method === "HEAD" ? new Uint8Array() : fullBytes
|
|
25094
|
+
}
|
|
25095
|
+
};
|
|
25096
|
+
}
|
|
25027
25097
|
var MOCK_LISTENER_PREFIX = "/.rogatio/mock/";
|
|
25028
25098
|
function isSingleSegment(value) {
|
|
25029
25099
|
return value.length > 0 && value.length <= 2048 && value !== "." && value !== ".." && !value.includes("/") && !value.includes("\\") && !value.includes("?") && !hasControl(value);
|
|
@@ -25402,6 +25472,9 @@ async function fetchAndRewriteAuthorizedResponse(operation, action, options = {}
|
|
|
25402
25472
|
};
|
|
25403
25473
|
}
|
|
25404
25474
|
var RESPONSE_BODY_LISTENER_PREFIX = "/.rogatio/body/";
|
|
25475
|
+
function isSingleSegment2(value) {
|
|
25476
|
+
return value.length > 0 && value.length <= 2048 && value !== "." && value !== ".." && !value.includes("/") && !value.includes("\\") && !value.includes("?") && !hasControl(value);
|
|
25477
|
+
}
|
|
25405
25478
|
function parseResponseBodyRedirect(rawUrl) {
|
|
25406
25479
|
const queryAt = rawUrl.indexOf("?");
|
|
25407
25480
|
const path2 = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
|
|
@@ -25412,18 +25485,24 @@ function parseResponseBodyRedirect(rawUrl) {
|
|
|
25412
25485
|
if (slash1 <= 0) return null;
|
|
25413
25486
|
const slash2 = rest.indexOf("/", slash1 + 1);
|
|
25414
25487
|
if (slash2 <= slash1 + 1) return null;
|
|
25488
|
+
const slash3 = rest.indexOf("/", slash2 + 1);
|
|
25489
|
+
if (slash3 <= slash2 + 1) return null;
|
|
25490
|
+
let token;
|
|
25415
25491
|
let ruleId;
|
|
25416
25492
|
let digest;
|
|
25417
25493
|
try {
|
|
25418
|
-
|
|
25419
|
-
|
|
25494
|
+
token = decodeURIComponent(rest.slice(0, slash1));
|
|
25495
|
+
ruleId = decodeURIComponent(rest.slice(slash1 + 1, slash2));
|
|
25496
|
+
digest = decodeURIComponent(rest.slice(slash2 + 1, slash3));
|
|
25420
25497
|
} catch {
|
|
25421
25498
|
return null;
|
|
25422
25499
|
}
|
|
25423
|
-
if (
|
|
25424
|
-
|
|
25500
|
+
if (!isSingleSegment2(token) || !isSingleSegment2(ruleId) || !isSingleSegment2(digest)) {
|
|
25501
|
+
return null;
|
|
25502
|
+
}
|
|
25503
|
+
const original = rest.slice(slash3 + 1) + search;
|
|
25425
25504
|
if (!/^https?:\/\//i.test(original)) return null;
|
|
25426
|
-
return { ruleId, digest, originalUrl: original };
|
|
25505
|
+
return { token, ruleId, digest, originalUrl: original };
|
|
25427
25506
|
}
|
|
25428
25507
|
function originOf(value) {
|
|
25429
25508
|
const direct = normalizeSiteOrigin(value);
|
|
@@ -25492,8 +25571,12 @@ async function revalidateAuthority(project, operations, request) {
|
|
|
25492
25571
|
}
|
|
25493
25572
|
}
|
|
25494
25573
|
if (matcher.resourceTypes.length > 0) {
|
|
25495
|
-
const
|
|
25496
|
-
if (
|
|
25574
|
+
const supplied = request.resourceType;
|
|
25575
|
+
if (supplied === void 0) {
|
|
25576
|
+
if (operation.kind !== "response-body") {
|
|
25577
|
+
return { allowed: false, reason: "resource-type-unauthorized" };
|
|
25578
|
+
}
|
|
25579
|
+
} else if (typeof supplied !== "string" || !matcher.resourceTypes.includes(supplied)) {
|
|
25497
25580
|
return { allowed: false, reason: "resource-type-unauthorized" };
|
|
25498
25581
|
}
|
|
25499
25582
|
}
|
|
@@ -25504,6 +25587,24 @@ async function revalidateAuthority(project, operations, request) {
|
|
|
25504
25587
|
operation
|
|
25505
25588
|
};
|
|
25506
25589
|
}
|
|
25590
|
+
var PROXY_DOCUMENT_CSP = "sandbox; default-src 'none'; script-src 'none'; connect-src 'none'; form-action 'none'; frame-src 'none'";
|
|
25591
|
+
var DROPPED_DOCUMENT_HEADERS = /* @__PURE__ */ new Set([
|
|
25592
|
+
"content-security-policy",
|
|
25593
|
+
"content-security-policy-report-only",
|
|
25594
|
+
"x-content-type-options"
|
|
25595
|
+
]);
|
|
25596
|
+
function sameToken(supplied, expected) {
|
|
25597
|
+
const left = Buffer.from(supplied);
|
|
25598
|
+
const right = Buffer.from(expected);
|
|
25599
|
+
if (left.byteLength !== right.byteLength) return false;
|
|
25600
|
+
return timingSafeEqual(left, right);
|
|
25601
|
+
}
|
|
25602
|
+
function proxyDocumentHeaders() {
|
|
25603
|
+
return [
|
|
25604
|
+
["content-security-policy", PROXY_DOCUMENT_CSP],
|
|
25605
|
+
["x-content-type-options", "nosniff"]
|
|
25606
|
+
];
|
|
25607
|
+
}
|
|
25507
25608
|
function isBodyOp(op) {
|
|
25508
25609
|
return op.kind === "request-body" || op.kind === "response-body";
|
|
25509
25610
|
}
|
|
@@ -25906,6 +26007,8 @@ async function authorizeOp(policy, op, url, method, headers) {
|
|
|
25906
26007
|
return decision.allowed;
|
|
25907
26008
|
}
|
|
25908
26009
|
async function startInterceptProxy(options = {}) {
|
|
26010
|
+
const responseBodyToken = mintToken();
|
|
26011
|
+
const outbound = options.outbound;
|
|
25909
26012
|
let policy = options.policy ?? null;
|
|
25910
26013
|
const serveMock = options.serveMock;
|
|
25911
26014
|
const resolver = options.resolver ?? defaultResolver2;
|
|
@@ -26126,10 +26229,11 @@ async function startInterceptProxy(options = {}) {
|
|
|
26126
26229
|
endEmpty(res, status);
|
|
26127
26230
|
return;
|
|
26128
26231
|
}
|
|
26129
|
-
|
|
26130
|
-
|
|
26131
|
-
rendered.value.headers.map(([name, value]) => [name, value])
|
|
26232
|
+
const mockHeaders = rendered.value.headers.flatMap(
|
|
26233
|
+
([name, value]) => DROPPED_DOCUMENT_HEADERS.has(name.toLowerCase()) ? [] : [[name, value]]
|
|
26132
26234
|
);
|
|
26235
|
+
mockHeaders.push(...proxyDocumentHeaders());
|
|
26236
|
+
res.writeHead(rendered.value.status, mockHeaders);
|
|
26133
26237
|
res.end(Buffer.from(rendered.value.bodyBytes));
|
|
26134
26238
|
} catch {
|
|
26135
26239
|
endEmpty(res, 502);
|
|
@@ -26146,9 +26250,19 @@ async function startInterceptProxy(options = {}) {
|
|
|
26146
26250
|
return;
|
|
26147
26251
|
}
|
|
26148
26252
|
const parsed = parseResponseBodyRedirect(rawUrl);
|
|
26253
|
+
if (parsed === null) {
|
|
26254
|
+
res.writeHead(400);
|
|
26255
|
+
res.end();
|
|
26256
|
+
return;
|
|
26257
|
+
}
|
|
26258
|
+
if (!sameToken(parsed.token, responseBodyToken)) {
|
|
26259
|
+
res.writeHead(403);
|
|
26260
|
+
res.end();
|
|
26261
|
+
return;
|
|
26262
|
+
}
|
|
26149
26263
|
const active = policy;
|
|
26150
|
-
if (
|
|
26151
|
-
res.writeHead(
|
|
26264
|
+
if (active === null || active.presetDigest === void 0) {
|
|
26265
|
+
res.writeHead(403);
|
|
26152
26266
|
res.end();
|
|
26153
26267
|
return;
|
|
26154
26268
|
}
|
|
@@ -26189,8 +26303,7 @@ async function startInterceptProxy(options = {}) {
|
|
|
26189
26303
|
url: parsed.originalUrl,
|
|
26190
26304
|
target,
|
|
26191
26305
|
method: "GET",
|
|
26192
|
-
initiator: initiatorFromHeaders(req.headers, parsed.originalUrl) ?? new URL(target).origin
|
|
26193
|
-
resourceType: operation.matcher.resourceTypes[0]
|
|
26306
|
+
initiator: initiatorFromHeaders(req.headers, parsed.originalUrl) ?? new URL(target).origin
|
|
26194
26307
|
}
|
|
26195
26308
|
);
|
|
26196
26309
|
if (!allowed.allowed) {
|
|
@@ -26209,7 +26322,8 @@ async function startInterceptProxy(options = {}) {
|
|
|
26209
26322
|
sourceValue: operation.matcher.source.value,
|
|
26210
26323
|
presetDigest
|
|
26211
26324
|
},
|
|
26212
|
-
operation.responseBody
|
|
26325
|
+
operation.responseBody,
|
|
26326
|
+
outbound ?? {}
|
|
26213
26327
|
);
|
|
26214
26328
|
if (!fetched.ok) {
|
|
26215
26329
|
res.writeHead(502);
|
|
@@ -26219,7 +26333,7 @@ async function startInterceptProxy(options = {}) {
|
|
|
26219
26333
|
const headers = {};
|
|
26220
26334
|
for (const [name, value] of fetched.value.headers) {
|
|
26221
26335
|
const lower = name.toLowerCase();
|
|
26222
|
-
if (lower === "set-cookie" || lower === "cookie" || lower === "authorization" || lower === "proxy-authorization") {
|
|
26336
|
+
if (lower === "set-cookie" || lower === "cookie" || lower === "authorization" || lower === "proxy-authorization" || DROPPED_DOCUMENT_HEADERS.has(lower)) {
|
|
26223
26337
|
continue;
|
|
26224
26338
|
}
|
|
26225
26339
|
const existing = headers[lower];
|
|
@@ -26227,6 +26341,9 @@ async function startInterceptProxy(options = {}) {
|
|
|
26227
26341
|
else if (Array.isArray(existing)) existing.push(value);
|
|
26228
26342
|
else headers[lower] = [existing, value];
|
|
26229
26343
|
}
|
|
26344
|
+
for (const [name, value] of proxyDocumentHeaders()) {
|
|
26345
|
+
headers[name] = value;
|
|
26346
|
+
}
|
|
26230
26347
|
writeResponse(
|
|
26231
26348
|
res,
|
|
26232
26349
|
fetched.value.status,
|
|
@@ -26296,6 +26413,7 @@ async function startInterceptProxy(options = {}) {
|
|
|
26296
26413
|
});
|
|
26297
26414
|
return {
|
|
26298
26415
|
endpoint,
|
|
26416
|
+
responseBodyToken,
|
|
26299
26417
|
setPolicy(next) {
|
|
26300
26418
|
policy = next;
|
|
26301
26419
|
},
|
|
@@ -26462,7 +26580,7 @@ function createPlatformInterceptionProvider(adapter) {
|
|
|
26462
26580
|
var TOKEN_BYTES = 32;
|
|
26463
26581
|
var TOKEN_LENGTH = 43;
|
|
26464
26582
|
function createCapabilityState(preset, now) {
|
|
26465
|
-
const bootstrap =
|
|
26583
|
+
const bootstrap = randomBytes2(TOKEN_BYTES).toString("base64url");
|
|
26466
26584
|
return {
|
|
26467
26585
|
presetDigest: preset.digest,
|
|
26468
26586
|
bootstrap,
|
|
@@ -26485,15 +26603,15 @@ function isToken(value) {
|
|
|
26485
26603
|
return false;
|
|
26486
26604
|
}
|
|
26487
26605
|
}
|
|
26488
|
-
function
|
|
26606
|
+
function sameToken2(value, expectedDigest) {
|
|
26489
26607
|
const actual = digestToken(value);
|
|
26490
|
-
return actual.length === expectedDigest.length &&
|
|
26608
|
+
return actual.length === expectedDigest.length && timingSafeEqual2(actual, expectedDigest);
|
|
26491
26609
|
}
|
|
26492
26610
|
function sameDigest(value, expected) {
|
|
26493
26611
|
if (!isSha256Digest(value) || value.length !== expected.length) return false;
|
|
26494
26612
|
const actual = Buffer.from(value, "ascii");
|
|
26495
26613
|
const wanted = Buffer.from(expected, "ascii");
|
|
26496
|
-
return
|
|
26614
|
+
return timingSafeEqual2(actual, wanted);
|
|
26497
26615
|
}
|
|
26498
26616
|
function cleanSessions(state, now) {
|
|
26499
26617
|
for (let index = state.sessions.length - 1; index >= 0; index -= 1) {
|
|
@@ -26503,14 +26621,14 @@ function cleanSessions(state, now) {
|
|
|
26503
26621
|
}
|
|
26504
26622
|
function pairCapability(state, capability, digest, now) {
|
|
26505
26623
|
cleanSessions(state, now);
|
|
26506
|
-
if (state.closed || state.consumed || !isToken(capability) || typeof digest !== "string" || !sameDigest(digest, state.presetDigest) || now >= state.bootstrapExpiresAt || !
|
|
26624
|
+
if (state.closed || state.consumed || !isToken(capability) || typeof digest !== "string" || !sameDigest(digest, state.presetDigest) || now >= state.bootstrapExpiresAt || !sameToken2(capability, state.bootstrapDigest)) {
|
|
26507
26625
|
return failure("runtime.pairing-denied");
|
|
26508
26626
|
}
|
|
26509
26627
|
if (state.sessions.length >= RUNTIME_LIMITS.maxConcurrentSessions) {
|
|
26510
26628
|
return failure("runtime.overloaded");
|
|
26511
26629
|
}
|
|
26512
26630
|
state.consumed = true;
|
|
26513
|
-
const sessionCapability =
|
|
26631
|
+
const sessionCapability = randomBytes2(TOKEN_BYTES).toString("base64url");
|
|
26514
26632
|
const expiresAt = now + RUNTIME_LIMITS.sessionLifetimeMs;
|
|
26515
26633
|
state.sessions.push({
|
|
26516
26634
|
digest: Buffer.from(state.presetDigest, "ascii"),
|
|
@@ -26533,7 +26651,7 @@ function findSession(state, capability, digest, now) {
|
|
|
26533
26651
|
if (!sameDigest(digest, state.presetDigest)) return null;
|
|
26534
26652
|
const candidate = digestToken(capability);
|
|
26535
26653
|
for (const session of state.sessions) {
|
|
26536
|
-
if (candidate.length === session.tokenDigest.length &&
|
|
26654
|
+
if (candidate.length === session.tokenDigest.length && timingSafeEqual2(candidate, session.tokenDigest) && sameDigest(digest, session.digest.toString("ascii"))) {
|
|
26537
26655
|
return session;
|
|
26538
26656
|
}
|
|
26539
26657
|
}
|
|
@@ -26649,74 +26767,6 @@ async function pickDirectory(run = runDialog, platform = process.platform) {
|
|
|
26649
26767
|
}
|
|
26650
26768
|
return { ok: false, code: "runtime.picker-unavailable" };
|
|
26651
26769
|
}
|
|
26652
|
-
async function readMockFile(root, logicalPath, signal) {
|
|
26653
|
-
return readConfinedPathBytes(root, logicalPath, signal);
|
|
26654
|
-
}
|
|
26655
|
-
function mintToken() {
|
|
26656
|
-
return randomBytes2(32).toString("hex");
|
|
26657
|
-
}
|
|
26658
|
-
function headerName(name) {
|
|
26659
|
-
return name.toLowerCase();
|
|
26660
|
-
}
|
|
26661
|
-
function waitForDelay(delayMs, signal) {
|
|
26662
|
-
if (signal?.aborted) return Promise.resolve(failure("runtime.timeout"));
|
|
26663
|
-
if (delayMs === void 0 || delayMs <= 0) {
|
|
26664
|
-
return Promise.resolve({ ok: true, value: void 0 });
|
|
26665
|
-
}
|
|
26666
|
-
return new Promise((resolve12) => {
|
|
26667
|
-
const onAbort = () => {
|
|
26668
|
-
clearTimeout(timer);
|
|
26669
|
-
resolve12(failure("runtime.timeout"));
|
|
26670
|
-
};
|
|
26671
|
-
const timer = setTimeout(() => {
|
|
26672
|
-
signal?.removeEventListener("abort", onAbort);
|
|
26673
|
-
resolve12({ ok: true, value: void 0 });
|
|
26674
|
-
}, delayMs);
|
|
26675
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
26676
|
-
});
|
|
26677
|
-
}
|
|
26678
|
-
async function renderMockResponse(options) {
|
|
26679
|
-
const { mock, fileRoot, method, signal } = options;
|
|
26680
|
-
const waited = await waitForDelay(mock.delayMs, signal);
|
|
26681
|
-
if (!waited.ok) return waited;
|
|
26682
|
-
let fullBytes;
|
|
26683
|
-
if (mock.body !== void 0) {
|
|
26684
|
-
fullBytes = new TextEncoder().encode(mock.body);
|
|
26685
|
-
} else if (mock.file !== void 0) {
|
|
26686
|
-
if (fileRoot === void 0) return failure("runtime.file-denied");
|
|
26687
|
-
if (!isConfinedFileSupported()) {
|
|
26688
|
-
return failure("runtime.platform-unsupported");
|
|
26689
|
-
}
|
|
26690
|
-
const read2 = await readMockFile(fileRoot, mock.file, signal);
|
|
26691
|
-
if (!read2.ok) return read2;
|
|
26692
|
-
fullBytes = read2.value;
|
|
26693
|
-
} else {
|
|
26694
|
-
return failure("runtime.file-denied");
|
|
26695
|
-
}
|
|
26696
|
-
const headers = [];
|
|
26697
|
-
let hasContentType = false;
|
|
26698
|
-
let hasCacheControl = false;
|
|
26699
|
-
for (const header of mock.headers ?? []) {
|
|
26700
|
-
const name = headerName(header.name);
|
|
26701
|
-
if (name === "content-length") continue;
|
|
26702
|
-
if (name === "content-type") hasContentType = true;
|
|
26703
|
-
if (name === "cache-control") hasCacheControl = true;
|
|
26704
|
-
headers.push([header.name, header.value]);
|
|
26705
|
-
}
|
|
26706
|
-
if (!hasContentType) {
|
|
26707
|
-
headers.push(["Content-Type", "application/octet-stream"]);
|
|
26708
|
-
}
|
|
26709
|
-
headers.push(["Content-Length", String(fullBytes.byteLength)]);
|
|
26710
|
-
if (!hasCacheControl) headers.push(["Cache-Control", "no-store"]);
|
|
26711
|
-
return {
|
|
26712
|
-
ok: true,
|
|
26713
|
-
value: {
|
|
26714
|
-
status: mock.status,
|
|
26715
|
-
headers,
|
|
26716
|
-
bodyBytes: method === "HEAD" ? new Uint8Array() : fullBytes
|
|
26717
|
-
}
|
|
26718
|
-
};
|
|
26719
|
-
}
|
|
26720
26770
|
var MAX_MOCK_FILE_ERRORS = 64;
|
|
26721
26771
|
var FILE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
26722
26772
|
"runtime.file-denied",
|
|
@@ -28171,7 +28221,10 @@ function createNativeHost(options) {
|
|
|
28171
28221
|
signal: request.signal
|
|
28172
28222
|
})
|
|
28173
28223
|
});
|
|
28174
|
-
return
|
|
28224
|
+
return {
|
|
28225
|
+
...interceptProxy.endpoint,
|
|
28226
|
+
responseBodyToken: interceptProxy.responseBodyToken
|
|
28227
|
+
};
|
|
28175
28228
|
},
|
|
28176
28229
|
async stopTlsProxy() {
|
|
28177
28230
|
if (interceptProxy) {
|
|
@@ -29792,7 +29845,7 @@ function createServer3(handler, options = {}) {
|
|
|
29792
29845
|
}
|
|
29793
29846
|
|
|
29794
29847
|
// packages/cli/src/server/routes.ts
|
|
29795
|
-
import { randomBytes as randomBytes4, timingSafeEqual as
|
|
29848
|
+
import { randomBytes as randomBytes4, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
|
|
29796
29849
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
29797
29850
|
import { dirname as dirname9, isAbsolute as isAbsolute5, resolve as resolve5 } from "node:path";
|
|
29798
29851
|
|
|
@@ -30261,7 +30314,7 @@ function tokensEqual(provided, expected) {
|
|
|
30261
30314
|
const providedBytes = Buffer.from(provided);
|
|
30262
30315
|
const expectedBytes = Buffer.from(expected);
|
|
30263
30316
|
if (providedBytes.length !== expectedBytes.length) return false;
|
|
30264
|
-
return
|
|
30317
|
+
return timingSafeEqual3(providedBytes, expectedBytes);
|
|
30265
30318
|
}
|
|
30266
30319
|
function scopedPathname(pathname, sessionToken) {
|
|
30267
30320
|
const marker = "/e/";
|
package/package.json
CHANGED