@rogatio/cli 6.4.0 → 6.5.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/node/index.js +600 -12
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { basename as basename3, dirname as dirname8, resolve as resolve8 } from
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7
7
|
|
|
8
8
|
// packages/runtime/dist/node/index.js
|
|
9
|
+
import { isIP } from "node:net";
|
|
9
10
|
import * as fs from "node:fs/promises";
|
|
10
11
|
import * as path from "node:path";
|
|
11
12
|
|
|
@@ -1979,6 +1980,42 @@ function sourceMatches(source, url) {
|
|
|
1979
1980
|
return false;
|
|
1980
1981
|
}
|
|
1981
1982
|
}
|
|
1983
|
+
var PAC_STEER_MARKER = "steer:";
|
|
1984
|
+
function decodePacSteer(entry) {
|
|
1985
|
+
if (!entry.startsWith(PAC_STEER_MARKER)) return null;
|
|
1986
|
+
const value = entry.slice(PAC_STEER_MARKER.length);
|
|
1987
|
+
let url;
|
|
1988
|
+
try {
|
|
1989
|
+
url = new URL(`${value}/`);
|
|
1990
|
+
} catch {
|
|
1991
|
+
return null;
|
|
1992
|
+
}
|
|
1993
|
+
if (url.username !== "" || url.password !== "" || url.search !== "") {
|
|
1994
|
+
return null;
|
|
1995
|
+
}
|
|
1996
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
1997
|
+
const scheme = url.protocol === "https:" ? "https" : "http";
|
|
1998
|
+
const port = url.port === "" ? void 0 : Number(url.port);
|
|
1999
|
+
if (port !== void 0 && (!Number.isInteger(port) || port < 1 || port > 65535)) {
|
|
2000
|
+
return null;
|
|
2001
|
+
}
|
|
2002
|
+
const rebuilt = port === void 0 ? `${scheme}://${url.hostname}` : `${scheme}://${url.hostname}:${port}`;
|
|
2003
|
+
if (rebuilt !== value || !isLiteralSteerHost(url.hostname)) return null;
|
|
2004
|
+
return port === void 0 ? { scheme, host: url.hostname } : { scheme, host: url.hostname, port };
|
|
2005
|
+
}
|
|
2006
|
+
function isLiteralSteerHost(host) {
|
|
2007
|
+
if (host.length === 0 || host.length > 253) return false;
|
|
2008
|
+
if (host.startsWith(".") || host.endsWith(".") || host.includes("..")) {
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
const labels = host.split(".");
|
|
2012
|
+
for (const label2 of labels) {
|
|
2013
|
+
if (label2.length === 0 || label2.length > 63) return false;
|
|
2014
|
+
if (!/^[A-Za-z0-9-]+$/.test(label2)) return false;
|
|
2015
|
+
if (label2.startsWith("-") || label2.endsWith("-")) return false;
|
|
2016
|
+
}
|
|
2017
|
+
return true;
|
|
2018
|
+
}
|
|
1982
2019
|
function sameOrigin(a, b) {
|
|
1983
2020
|
try {
|
|
1984
2021
|
return new URL(a).origin === new URL(b).origin;
|
|
@@ -1998,6 +2035,9 @@ import {
|
|
|
1998
2035
|
} from "node:http";
|
|
1999
2036
|
import { connect } from "node:net";
|
|
2000
2037
|
import { Worker } from "node:worker_threads";
|
|
2038
|
+
import { lookup as dnsLookup } from "node:dns/promises";
|
|
2039
|
+
import { request as httpRequest } from "node:http";
|
|
2040
|
+
import { request as httpsRequest } from "node:https";
|
|
2001
2041
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2002
2042
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
2003
2043
|
import { readFile as readFile2, realpath as realpath2, stat } from "node:fs/promises";
|
|
@@ -20701,6 +20741,131 @@ var require_x509_cjs = __commonJS({
|
|
|
20701
20741
|
exports.idX448 = idX448;
|
|
20702
20742
|
}
|
|
20703
20743
|
});
|
|
20744
|
+
function parseIPv4(value) {
|
|
20745
|
+
const parts = value.split(".");
|
|
20746
|
+
if (parts.length !== 4) return null;
|
|
20747
|
+
const numbers = parts.map((part) => {
|
|
20748
|
+
if (!/^(?:0|[1-9]\d*)$/.test(part)) return -1;
|
|
20749
|
+
const number = Number(part);
|
|
20750
|
+
return Number.isInteger(number) && number >= 0 && number <= 255 ? number : -1;
|
|
20751
|
+
});
|
|
20752
|
+
return numbers.some((number) => number < 0) ? null : [numbers[0], numbers[1], numbers[2], numbers[3]];
|
|
20753
|
+
}
|
|
20754
|
+
function parseIPv6(value) {
|
|
20755
|
+
if (value.includes("%")) return null;
|
|
20756
|
+
const halves = value.split("::");
|
|
20757
|
+
if (halves.length > 2) return null;
|
|
20758
|
+
const left = halves[0] === "" ? [] : halves[0]?.split(":") ?? [];
|
|
20759
|
+
const right = halves.length === 2 && halves[1] !== "" ? halves[1]?.split(":") ?? [] : [];
|
|
20760
|
+
const parts = [...left, ...right];
|
|
20761
|
+
const words = [];
|
|
20762
|
+
for (const part of parts) {
|
|
20763
|
+
if (part.includes(".")) {
|
|
20764
|
+
const ipv4 = parseIPv4(part);
|
|
20765
|
+
if (ipv4 === null) return null;
|
|
20766
|
+
words.push(ipv4[0] << 8 | ipv4[1], ipv4[2] << 8 | ipv4[3]);
|
|
20767
|
+
continue;
|
|
20768
|
+
}
|
|
20769
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(part)) return null;
|
|
20770
|
+
words.push(Number.parseInt(part, 16));
|
|
20771
|
+
}
|
|
20772
|
+
if (halves.length === 1) return words.length === 8 ? words : null;
|
|
20773
|
+
if (words.length >= 8) return null;
|
|
20774
|
+
return [
|
|
20775
|
+
...words.slice(0, left.length),
|
|
20776
|
+
...new Array(8 - words.length).fill(0),
|
|
20777
|
+
...words.slice(left.length)
|
|
20778
|
+
];
|
|
20779
|
+
}
|
|
20780
|
+
function ipv4Number(value) {
|
|
20781
|
+
return ((value[0] * 256 + value[1]) * 256 + value[2]) * 256 + value[3] >>> 0;
|
|
20782
|
+
}
|
|
20783
|
+
function inIPv4Range(value, start, end) {
|
|
20784
|
+
return value >= start && value <= end;
|
|
20785
|
+
}
|
|
20786
|
+
function prefixMatches(value, prefix, bits) {
|
|
20787
|
+
let remaining = bits;
|
|
20788
|
+
for (let index = 0; remaining > 0; index += 1) {
|
|
20789
|
+
const compareBits = Math.min(remaining, 16);
|
|
20790
|
+
const mask = compareBits === 16 ? 65535 : 65535 << 16 - compareBits & 65535;
|
|
20791
|
+
if (((value[index] ?? 0) & mask) !== ((prefix[index] ?? 0) & mask))
|
|
20792
|
+
return false;
|
|
20793
|
+
remaining -= compareBits;
|
|
20794
|
+
}
|
|
20795
|
+
return true;
|
|
20796
|
+
}
|
|
20797
|
+
function mappedIPv4(value) {
|
|
20798
|
+
if (value.slice(0, 5).some((word) => word !== 0) || value[5] !== 65535)
|
|
20799
|
+
return null;
|
|
20800
|
+
return [
|
|
20801
|
+
(value[6] ?? 0) >> 8,
|
|
20802
|
+
(value[6] ?? 0) & 255,
|
|
20803
|
+
(value[7] ?? 0) >> 8,
|
|
20804
|
+
(value[7] ?? 0) & 255
|
|
20805
|
+
];
|
|
20806
|
+
}
|
|
20807
|
+
function classifyIPv4(value) {
|
|
20808
|
+
const number = ipv4Number(value);
|
|
20809
|
+
if (inIPv4Range(number, 0, 16777215)) return "unspecified";
|
|
20810
|
+
if (inIPv4Range(number, 167772160, 184549375) || inIPv4Range(number, 2886729728, 2887778303) || inIPv4Range(number, 3232235520, 3232301055))
|
|
20811
|
+
return "private";
|
|
20812
|
+
if (inIPv4Range(number, 2130706432, 2147483647)) return "loopback";
|
|
20813
|
+
if (inIPv4Range(number, 2851995648, 2852061183)) return "link-local";
|
|
20814
|
+
if (inIPv4Range(number, 1681915904, 1686110207)) return "carrier-grade";
|
|
20815
|
+
if (inIPv4Range(number, 3221225472, 3221225727) || inIPv4Range(number, 3221225984, 3221226239) || inIPv4Range(number, 3227017984, 3227018239))
|
|
20816
|
+
return "reserved";
|
|
20817
|
+
if (inIPv4Range(number, 3323068416, 3323199487)) return "benchmarking";
|
|
20818
|
+
if (inIPv4Range(number, 3325256704, 3325256959) || inIPv4Range(number, 3405803776, 3405804031))
|
|
20819
|
+
return "documentation";
|
|
20820
|
+
if (inIPv4Range(number, 3758096384, 4294967295)) return "multicast";
|
|
20821
|
+
return "public";
|
|
20822
|
+
}
|
|
20823
|
+
function classifyIPv6(value) {
|
|
20824
|
+
const mapped = mappedIPv4(value);
|
|
20825
|
+
if (mapped !== null) {
|
|
20826
|
+
return classifyIPv4(mapped);
|
|
20827
|
+
}
|
|
20828
|
+
if (value.every((word) => word === 0)) return "unspecified";
|
|
20829
|
+
if (value.slice(0, 7).every((word) => word === 0) && value[7] === 1)
|
|
20830
|
+
return "loopback";
|
|
20831
|
+
if (prefixMatches(value, [64512], 7)) return "private";
|
|
20832
|
+
if (prefixMatches(value, [65152], 10)) return "link-local";
|
|
20833
|
+
if (prefixMatches(value, [65280], 8)) return "multicast";
|
|
20834
|
+
if (prefixMatches(value, [8193, 3512], 32)) return "documentation";
|
|
20835
|
+
if (prefixMatches(value, [8193, 0], 32)) return "reserved";
|
|
20836
|
+
if (prefixMatches(value, [8193, 2, 0], 48)) return "benchmarking";
|
|
20837
|
+
if (prefixMatches(value, [8193, 16], 28)) return "reserved";
|
|
20838
|
+
if (prefixMatches(value, [100, 65435, 0, 0, 0, 0], 96))
|
|
20839
|
+
return "reserved";
|
|
20840
|
+
if (prefixMatches(value, [256], 8)) return "reserved";
|
|
20841
|
+
return "public";
|
|
20842
|
+
}
|
|
20843
|
+
function classifyAddress(value) {
|
|
20844
|
+
if (typeof value !== "string") return "invalid";
|
|
20845
|
+
const family = isIP(value);
|
|
20846
|
+
if (family === 4) {
|
|
20847
|
+
const parsed = parseIPv4(value);
|
|
20848
|
+
return parsed === null ? "invalid" : classifyIPv4(parsed);
|
|
20849
|
+
}
|
|
20850
|
+
if (family === 6) {
|
|
20851
|
+
const parsed = parseIPv6(value);
|
|
20852
|
+
return parsed === null ? "invalid" : classifyIPv6(parsed);
|
|
20853
|
+
}
|
|
20854
|
+
return "invalid";
|
|
20855
|
+
}
|
|
20856
|
+
function validateResolvedAddresses(addresses, maxAddresses) {
|
|
20857
|
+
if (addresses.length === 0 || addresses.length > maxAddresses) return null;
|
|
20858
|
+
const checked = [];
|
|
20859
|
+
for (const address of addresses) {
|
|
20860
|
+
if (address.family !== 4 && address.family !== 6 || classifyAddress(address.address) !== "public")
|
|
20861
|
+
return null;
|
|
20862
|
+
checked.push({ address: address.address, family: address.family });
|
|
20863
|
+
}
|
|
20864
|
+
checked.sort(
|
|
20865
|
+
(left, right) => left.family - right.family || (left.address < right.address ? -1 : left.address > right.address ? 1 : 0)
|
|
20866
|
+
);
|
|
20867
|
+
return checked;
|
|
20868
|
+
}
|
|
20704
20869
|
var MAX_RETRIES = 2;
|
|
20705
20870
|
var RETRY_DELAY_BASE = 1e3;
|
|
20706
20871
|
var COMPLETE_TIMEOUT = 6e4;
|
|
@@ -22084,11 +22249,183 @@ async function rewriteRequestBody(input, action, captureContext) {
|
|
|
22084
22249
|
}
|
|
22085
22250
|
};
|
|
22086
22251
|
}
|
|
22252
|
+
var defaultResolver = {
|
|
22253
|
+
async lookup(hostname, signal) {
|
|
22254
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
22255
|
+
const addresses = await dnsLookup(hostname, { all: true });
|
|
22256
|
+
return addresses.map((address) => ({
|
|
22257
|
+
address: address.address,
|
|
22258
|
+
family: address.family
|
|
22259
|
+
}));
|
|
22260
|
+
}
|
|
22261
|
+
};
|
|
22262
|
+
function responseHeaders(response) {
|
|
22263
|
+
const headers = [];
|
|
22264
|
+
for (let index = 0; index < response.rawHeaders.length; index += 2) {
|
|
22265
|
+
const name = response.rawHeaders[index];
|
|
22266
|
+
const value = response.rawHeaders[index + 1];
|
|
22267
|
+
if (name !== void 0 && value !== void 0)
|
|
22268
|
+
headers.push([name.toLowerCase(), value]);
|
|
22269
|
+
}
|
|
22270
|
+
return headers;
|
|
22271
|
+
}
|
|
22272
|
+
var defaultTransport = {
|
|
22273
|
+
request(options) {
|
|
22274
|
+
return new Promise((resolve22, reject) => {
|
|
22275
|
+
const lookup = (_hostname, _options, callback) => {
|
|
22276
|
+
callback(null, options.address, options.address.includes(":") ? 6 : 4);
|
|
22277
|
+
};
|
|
22278
|
+
const requestOptions = {
|
|
22279
|
+
hostname: options.hostname,
|
|
22280
|
+
port: options.port,
|
|
22281
|
+
path: options.path,
|
|
22282
|
+
method: options.method,
|
|
22283
|
+
headers: options.headers,
|
|
22284
|
+
agent: false,
|
|
22285
|
+
lookup,
|
|
22286
|
+
signal: options.signal,
|
|
22287
|
+
setHost: false,
|
|
22288
|
+
autoSelectFamily: false
|
|
22289
|
+
};
|
|
22290
|
+
const request = options.protocol === "https:" ? httpsRequest(
|
|
22291
|
+
{ ...requestOptions, servername: options.servername },
|
|
22292
|
+
(response) => {
|
|
22293
|
+
resolve22({
|
|
22294
|
+
status: response.statusCode ?? 0,
|
|
22295
|
+
headers: responseHeaders(response),
|
|
22296
|
+
body: response
|
|
22297
|
+
});
|
|
22298
|
+
}
|
|
22299
|
+
) : httpRequest(requestOptions, (response) => {
|
|
22300
|
+
resolve22({
|
|
22301
|
+
status: response.statusCode ?? 0,
|
|
22302
|
+
headers: responseHeaders(response),
|
|
22303
|
+
body: response
|
|
22304
|
+
});
|
|
22305
|
+
});
|
|
22306
|
+
request.once("error", reject);
|
|
22307
|
+
request.end();
|
|
22308
|
+
});
|
|
22309
|
+
}
|
|
22310
|
+
};
|
|
22311
|
+
function headerValue(headers, name) {
|
|
22312
|
+
return headers.find(([headerName]) => headerName.toLowerCase() === name)?.[1];
|
|
22313
|
+
}
|
|
22314
|
+
function responseHeaderBytes(headers) {
|
|
22315
|
+
let bytes = 0;
|
|
22316
|
+
for (const [name, value] of headers)
|
|
22317
|
+
bytes += Buffer.byteLength(name) + Buffer.byteLength(value) + 4;
|
|
22318
|
+
return bytes;
|
|
22319
|
+
}
|
|
22320
|
+
function combineSignals(signal, timeoutMs) {
|
|
22321
|
+
const controller = new AbortController();
|
|
22322
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
22323
|
+
const abort = () => controller.abort();
|
|
22324
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
22325
|
+
return {
|
|
22326
|
+
signal: controller.signal,
|
|
22327
|
+
dispose: () => {
|
|
22328
|
+
clearTimeout(timer);
|
|
22329
|
+
signal?.removeEventListener("abort", abort);
|
|
22330
|
+
}
|
|
22331
|
+
};
|
|
22332
|
+
}
|
|
22333
|
+
async function fetchAuthorized(operation, options = {}) {
|
|
22334
|
+
if (operation.kind !== "outbound-http" || operation.method !== "GET" && operation.method !== "HEAD")
|
|
22335
|
+
return failure("runtime.unsupported-method");
|
|
22336
|
+
const target = canonicalizeOutboundTarget(operation.target);
|
|
22337
|
+
if (target === null || target !== operation.target)
|
|
22338
|
+
return failure("runtime.invalid-target");
|
|
22339
|
+
const url = new URL(target);
|
|
22340
|
+
const protocol = url.protocol === "https:" ? "https:" : "http:";
|
|
22341
|
+
const port = protocol === "https:" ? 443 : 80;
|
|
22342
|
+
const timeoutMs = options.operationTimeoutMs ?? RUNTIME_LIMITS.operationTimeoutMs;
|
|
22343
|
+
const combined = combineSignals(
|
|
22344
|
+
options.signal,
|
|
22345
|
+
Math.max(1, Math.min(timeoutMs, RUNTIME_LIMITS.operationTimeoutMs))
|
|
22346
|
+
);
|
|
22347
|
+
try {
|
|
22348
|
+
const resolver = options.resolver ?? defaultResolver;
|
|
22349
|
+
const resolved = await resolver.lookup(url.hostname, combined.signal);
|
|
22350
|
+
const addresses = validateResolvedAddresses(
|
|
22351
|
+
resolved,
|
|
22352
|
+
RUNTIME_LIMITS.maxDnsAddresses
|
|
22353
|
+
);
|
|
22354
|
+
if (addresses === null || addresses.length === 0)
|
|
22355
|
+
return failure("runtime.address-denied");
|
|
22356
|
+
const selected = addresses[0];
|
|
22357
|
+
const transport = options.transport ?? defaultTransport;
|
|
22358
|
+
let response;
|
|
22359
|
+
try {
|
|
22360
|
+
response = await transport.request({
|
|
22361
|
+
protocol,
|
|
22362
|
+
hostname: url.hostname,
|
|
22363
|
+
address: selected.address,
|
|
22364
|
+
port,
|
|
22365
|
+
path: `${url.pathname}${url.search}`,
|
|
22366
|
+
method: operation.method,
|
|
22367
|
+
headers: { host: url.host, "accept-encoding": "identity" },
|
|
22368
|
+
...protocol === "https:" ? { servername: url.hostname } : {},
|
|
22369
|
+
signal: combined.signal
|
|
22370
|
+
});
|
|
22371
|
+
} catch {
|
|
22372
|
+
return combined.signal.aborted ? failure("runtime.timeout") : failure("runtime.internal");
|
|
22373
|
+
}
|
|
22374
|
+
if (combined.signal.aborted) return failure("runtime.timeout");
|
|
22375
|
+
if (response.status >= 300 && response.status <= 399)
|
|
22376
|
+
return failure("runtime.redirect-rejected");
|
|
22377
|
+
if (responseHeaderBytes(response.headers) > RUNTIME_LIMITS.maxResponseHeaderBytes)
|
|
22378
|
+
return failure("runtime.size-limit");
|
|
22379
|
+
const encoding = headerValue(response.headers, "content-encoding");
|
|
22380
|
+
if (encoding !== void 0 && encoding.toLowerCase() !== "identity")
|
|
22381
|
+
return failure("runtime.size-limit");
|
|
22382
|
+
const chunks = [];
|
|
22383
|
+
let total = 0;
|
|
22384
|
+
try {
|
|
22385
|
+
for await (const chunk of response.body) {
|
|
22386
|
+
if (combined.signal.aborted) return failure("runtime.timeout");
|
|
22387
|
+
const bytes = chunk instanceof Uint8Array ? Uint8Array.from(chunk) : Uint8Array.from(chunk);
|
|
22388
|
+
total += bytes.byteLength;
|
|
22389
|
+
if (total > RUNTIME_LIMITS.maxResponseBodyBytes)
|
|
22390
|
+
return failure("runtime.size-limit");
|
|
22391
|
+
chunks.push(bytes);
|
|
22392
|
+
}
|
|
22393
|
+
} catch {
|
|
22394
|
+
return combined.signal.aborted ? failure("runtime.timeout") : failure("runtime.internal");
|
|
22395
|
+
}
|
|
22396
|
+
const body = new Uint8Array(total);
|
|
22397
|
+
let offset = 0;
|
|
22398
|
+
for (const chunk of chunks) {
|
|
22399
|
+
body.set(chunk, offset);
|
|
22400
|
+
offset += chunk.byteLength;
|
|
22401
|
+
}
|
|
22402
|
+
return {
|
|
22403
|
+
ok: true,
|
|
22404
|
+
value: { status: response.status, headers: response.headers, body }
|
|
22405
|
+
};
|
|
22406
|
+
} catch {
|
|
22407
|
+
return combined.signal.aborted ? failure("runtime.timeout") : failure("runtime.dns-failed");
|
|
22408
|
+
} finally {
|
|
22409
|
+
combined.dispose();
|
|
22410
|
+
}
|
|
22411
|
+
}
|
|
22087
22412
|
function supportedContentType2(value) {
|
|
22088
22413
|
if (value === void 0) return false;
|
|
22089
22414
|
const mediaType = value.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
22090
22415
|
return mediaType.startsWith("text/") || mediaType === "application/json" || mediaType === "application/javascript" || mediaType === "text/javascript" || mediaType === "text/css" || mediaType === "application/xml" || mediaType === "text/xml";
|
|
22091
22416
|
}
|
|
22417
|
+
function responseHeader(headers, name) {
|
|
22418
|
+
return headers.find(([key]) => key.toLowerCase() === name)?.[1];
|
|
22419
|
+
}
|
|
22420
|
+
function responseBodyReplacements(action) {
|
|
22421
|
+
if ("replacements" in action && Array.isArray(action.replacements)) {
|
|
22422
|
+
return action.replacements;
|
|
22423
|
+
}
|
|
22424
|
+
return [];
|
|
22425
|
+
}
|
|
22426
|
+
function isReplaceAction(action) {
|
|
22427
|
+
return "mode" in action && action.mode === "replace";
|
|
22428
|
+
}
|
|
22092
22429
|
async function rewriteResponseBody(input, replacements, replaceAction, captureContext) {
|
|
22093
22430
|
if (!supportedContentType2(input.contentType) || input.contentType === void 0)
|
|
22094
22431
|
return failure("runtime.size-limit");
|
|
@@ -22134,6 +22471,78 @@ async function rewriteResponseBody(input, replacements, replaceAction, captureCo
|
|
|
22134
22471
|
}
|
|
22135
22472
|
};
|
|
22136
22473
|
}
|
|
22474
|
+
function replaceResponseBody(body, contentType, captureContext) {
|
|
22475
|
+
const captures = captureContext === void 0 ? [] : matchUrlCaptures(captureContext.urlRegex, captureContext.url);
|
|
22476
|
+
if (captureContext !== void 0 && captures === null) {
|
|
22477
|
+
return failure("runtime.url-capture-mismatch");
|
|
22478
|
+
}
|
|
22479
|
+
const expanded = captureContext === void 0 ? body : substituteUrlCaptures(body, captures ?? []);
|
|
22480
|
+
const encoded = new TextEncoder().encode(expanded);
|
|
22481
|
+
if (encoded.byteLength > RUNTIME_LIMITS.maxResponseBodyBytes)
|
|
22482
|
+
return failure("runtime.size-limit");
|
|
22483
|
+
return {
|
|
22484
|
+
ok: true,
|
|
22485
|
+
value: {
|
|
22486
|
+
body: encoded,
|
|
22487
|
+
contentType: contentType?.split(";", 1)[0]?.trim() ?? "text/plain"
|
|
22488
|
+
}
|
|
22489
|
+
};
|
|
22490
|
+
}
|
|
22491
|
+
async function fetchAndRewriteAuthorizedResponse(operation, action, options = {}) {
|
|
22492
|
+
if (operation.kind !== "outbound-http" || operation.method !== "GET")
|
|
22493
|
+
return failure("runtime.unsupported-method");
|
|
22494
|
+
const response = await fetchAuthorized(operation, options);
|
|
22495
|
+
if (!response.ok) return response;
|
|
22496
|
+
const contentType = responseHeader(response.value.headers, "content-type");
|
|
22497
|
+
const transformed = isReplaceAction(action) ? replaceResponseBody(
|
|
22498
|
+
action.body,
|
|
22499
|
+
contentType,
|
|
22500
|
+
operation.sourceValue === void 0 ? void 0 : { url: operation.target, urlRegex: operation.sourceValue }
|
|
22501
|
+
) : await rewriteResponseBody(
|
|
22502
|
+
{
|
|
22503
|
+
contentType,
|
|
22504
|
+
contentEncoding: responseHeader(
|
|
22505
|
+
response.value.headers,
|
|
22506
|
+
"content-encoding"
|
|
22507
|
+
),
|
|
22508
|
+
body: response.value.body
|
|
22509
|
+
},
|
|
22510
|
+
responseBodyReplacements(action)
|
|
22511
|
+
);
|
|
22512
|
+
if (!transformed.ok) return transformed;
|
|
22513
|
+
return {
|
|
22514
|
+
ok: true,
|
|
22515
|
+
value: {
|
|
22516
|
+
...transformed.value,
|
|
22517
|
+
status: response.value.status,
|
|
22518
|
+
headers: response.value.headers
|
|
22519
|
+
}
|
|
22520
|
+
};
|
|
22521
|
+
}
|
|
22522
|
+
var RESPONSE_BODY_LISTENER_PREFIX = "/.rogatio/body/";
|
|
22523
|
+
function parseResponseBodyRedirect(rawUrl) {
|
|
22524
|
+
const queryAt = rawUrl.indexOf("?");
|
|
22525
|
+
const path2 = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
|
|
22526
|
+
const search = queryAt === -1 ? "" : rawUrl.slice(queryAt);
|
|
22527
|
+
if (!path2.startsWith(RESPONSE_BODY_LISTENER_PREFIX)) return null;
|
|
22528
|
+
const rest = path2.slice(RESPONSE_BODY_LISTENER_PREFIX.length);
|
|
22529
|
+
const slash1 = rest.indexOf("/");
|
|
22530
|
+
if (slash1 <= 0) return null;
|
|
22531
|
+
const slash2 = rest.indexOf("/", slash1 + 1);
|
|
22532
|
+
if (slash2 <= slash1 + 1) return null;
|
|
22533
|
+
let ruleId;
|
|
22534
|
+
let digest;
|
|
22535
|
+
try {
|
|
22536
|
+
ruleId = decodeURIComponent(rest.slice(0, slash1));
|
|
22537
|
+
digest = decodeURIComponent(rest.slice(slash1 + 1, slash2));
|
|
22538
|
+
} catch {
|
|
22539
|
+
return null;
|
|
22540
|
+
}
|
|
22541
|
+
if (ruleId.length === 0 || digest.length === 0) return null;
|
|
22542
|
+
const original = rest.slice(slash2 + 1) + search;
|
|
22543
|
+
if (!/^https?:\/\//i.test(original)) return null;
|
|
22544
|
+
return { ruleId, digest, originalUrl: original };
|
|
22545
|
+
}
|
|
22137
22546
|
function originOf(value) {
|
|
22138
22547
|
const direct = normalizeSiteOrigin(value);
|
|
22139
22548
|
if (direct !== null) return direct;
|
|
@@ -22445,6 +22854,9 @@ function writeResponse(res, status, statusMessage, headers, body) {
|
|
|
22445
22854
|
res.writeHead(status, statusMessage, out);
|
|
22446
22855
|
res.end(body);
|
|
22447
22856
|
}
|
|
22857
|
+
function asPresetDigest(value) {
|
|
22858
|
+
return value.startsWith("sha256:") ? value : null;
|
|
22859
|
+
}
|
|
22448
22860
|
function initiatorFromHeaders(headers, targetUrl) {
|
|
22449
22861
|
const origin = headerValue2(headers, "origin");
|
|
22450
22862
|
if (typeof origin === "string" && /^https?:\/\//i.test(origin)) {
|
|
@@ -22487,6 +22899,10 @@ async function startInterceptProxy(options = {}) {
|
|
|
22487
22899
|
async function handleHttp(req, res) {
|
|
22488
22900
|
try {
|
|
22489
22901
|
const targetUrl = req.url ?? "";
|
|
22902
|
+
if (targetUrl.startsWith("/.rogatio/body/")) {
|
|
22903
|
+
await handleResponseBodyRedirect(req, res, targetUrl);
|
|
22904
|
+
return;
|
|
22905
|
+
}
|
|
22490
22906
|
if (!/^https?:\/\//i.test(targetUrl)) {
|
|
22491
22907
|
res.writeHead(400);
|
|
22492
22908
|
res.end();
|
|
@@ -22602,6 +23018,96 @@ async function startInterceptProxy(options = {}) {
|
|
|
22602
23018
|
}
|
|
22603
23019
|
}
|
|
22604
23020
|
}
|
|
23021
|
+
async function handleResponseBodyRedirect(req, res, rawUrl) {
|
|
23022
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
23023
|
+
if (method !== "GET") {
|
|
23024
|
+
res.writeHead(405);
|
|
23025
|
+
res.end();
|
|
23026
|
+
return;
|
|
23027
|
+
}
|
|
23028
|
+
const parsed = parseResponseBodyRedirect(rawUrl);
|
|
23029
|
+
const active = policy;
|
|
23030
|
+
if (parsed === null || active === null || active.presetDigest === void 0) {
|
|
23031
|
+
res.writeHead(parsed === null ? 400 : 403);
|
|
23032
|
+
res.end();
|
|
23033
|
+
return;
|
|
23034
|
+
}
|
|
23035
|
+
const presetDigest = asPresetDigest(active.presetDigest);
|
|
23036
|
+
if (presetDigest === null || parsed.digest !== presetDigest) {
|
|
23037
|
+
res.writeHead(403);
|
|
23038
|
+
res.end();
|
|
23039
|
+
return;
|
|
23040
|
+
}
|
|
23041
|
+
const operation = active.operations.find(
|
|
23042
|
+
(op) => op.kind === "response-body" && op.ruleId === parsed.ruleId
|
|
23043
|
+
);
|
|
23044
|
+
if (operation === void 0 || !sourceMatches(operation.matcher.source, parsed.originalUrl)) {
|
|
23045
|
+
res.writeHead(404);
|
|
23046
|
+
res.end();
|
|
23047
|
+
return;
|
|
23048
|
+
}
|
|
23049
|
+
if (operation.matcher.method !== void 0 && operation.matcher.method !== "GET") {
|
|
23050
|
+
res.writeHead(405);
|
|
23051
|
+
res.end();
|
|
23052
|
+
return;
|
|
23053
|
+
}
|
|
23054
|
+
const target = canonicalizeOutboundTarget(parsed.originalUrl);
|
|
23055
|
+
if (target === null) {
|
|
23056
|
+
res.writeHead(400);
|
|
23057
|
+
res.end();
|
|
23058
|
+
return;
|
|
23059
|
+
}
|
|
23060
|
+
const allowed = revalidateAuthority(active.project, active.operations, {
|
|
23061
|
+
groupId: operation.groupId,
|
|
23062
|
+
ruleId: operation.ruleId,
|
|
23063
|
+
url: parsed.originalUrl,
|
|
23064
|
+
target,
|
|
23065
|
+
method: "GET",
|
|
23066
|
+
initiator: initiatorFromHeaders(req.headers, parsed.originalUrl) ?? new URL(target).origin,
|
|
23067
|
+
resourceType: operation.matcher.resourceTypes[0]
|
|
23068
|
+
});
|
|
23069
|
+
if (!allowed.allowed) {
|
|
23070
|
+
res.writeHead(403);
|
|
23071
|
+
res.end();
|
|
23072
|
+
return;
|
|
23073
|
+
}
|
|
23074
|
+
const fetched = await fetchAndRewriteAuthorizedResponse(
|
|
23075
|
+
{
|
|
23076
|
+
groupId: operation.groupId,
|
|
23077
|
+
ruleId: operation.ruleId,
|
|
23078
|
+
operationId: operation.ruleId,
|
|
23079
|
+
kind: "outbound-http",
|
|
23080
|
+
target,
|
|
23081
|
+
method: "GET",
|
|
23082
|
+
sourceValue: operation.matcher.source.value,
|
|
23083
|
+
presetDigest
|
|
23084
|
+
},
|
|
23085
|
+
operation.responseBody
|
|
23086
|
+
);
|
|
23087
|
+
if (!fetched.ok) {
|
|
23088
|
+
res.writeHead(502);
|
|
23089
|
+
res.end();
|
|
23090
|
+
return;
|
|
23091
|
+
}
|
|
23092
|
+
const headers = {};
|
|
23093
|
+
for (const [name, value] of fetched.value.headers) {
|
|
23094
|
+
const lower = name.toLowerCase();
|
|
23095
|
+
if (lower === "set-cookie" || lower === "cookie" || lower === "authorization" || lower === "proxy-authorization") {
|
|
23096
|
+
continue;
|
|
23097
|
+
}
|
|
23098
|
+
const existing = headers[lower];
|
|
23099
|
+
if (existing === void 0) headers[lower] = value;
|
|
23100
|
+
else if (Array.isArray(existing)) existing.push(value);
|
|
23101
|
+
else headers[lower] = [existing, value];
|
|
23102
|
+
}
|
|
23103
|
+
writeResponse(
|
|
23104
|
+
res,
|
|
23105
|
+
fetched.value.status,
|
|
23106
|
+
fetched.value.status === 200 ? "OK" : "Upstream",
|
|
23107
|
+
headers,
|
|
23108
|
+
Buffer.from(fetched.value.body)
|
|
23109
|
+
);
|
|
23110
|
+
}
|
|
22605
23111
|
function handleConnect(req, clientSocket, head) {
|
|
22606
23112
|
const authority = req.url ?? "";
|
|
22607
23113
|
const [host, portText] = authority.split(":");
|
|
@@ -22646,12 +23152,72 @@ async function startInterceptProxy(options = {}) {
|
|
|
22646
23152
|
}
|
|
22647
23153
|
};
|
|
22648
23154
|
}
|
|
23155
|
+
function steerKey(origin) {
|
|
23156
|
+
const port = origin.port !== void 0 ? `:${origin.port}` : "";
|
|
23157
|
+
return `steer:${origin.scheme}://${origin.host}${port}`;
|
|
23158
|
+
}
|
|
23159
|
+
function pacRouteFromEntry(entry) {
|
|
23160
|
+
const steer = decodePacSteer(entry);
|
|
23161
|
+
if (steer !== null) return { steer };
|
|
23162
|
+
if (entry.includes("://")) {
|
|
23163
|
+
return isExactHttpUrl(entry) ? { url: entry } : null;
|
|
23164
|
+
}
|
|
23165
|
+
if (entry.length === 0) return null;
|
|
23166
|
+
return { hostname: entry };
|
|
23167
|
+
}
|
|
23168
|
+
function pacCheck(route) {
|
|
23169
|
+
if (typeof route.url === "string" && isExactHttpUrl(route.url)) {
|
|
23170
|
+
return {
|
|
23171
|
+
key: `url:${route.url}`,
|
|
23172
|
+
line: `url === ${JSON.stringify(route.url)}`
|
|
23173
|
+
};
|
|
23174
|
+
}
|
|
23175
|
+
if (route.steer !== void 0) {
|
|
23176
|
+
return {
|
|
23177
|
+
key: steerKey(route.steer),
|
|
23178
|
+
line: steerPredicate(route.steer)
|
|
23179
|
+
};
|
|
23180
|
+
}
|
|
23181
|
+
if (typeof route.hostname === "string" && route.hostname.length > 0 && !route.hostname.includes("://")) {
|
|
23182
|
+
return {
|
|
23183
|
+
key: `host:${route.hostname}`,
|
|
23184
|
+
line: `host === ${JSON.stringify(route.hostname)}`
|
|
23185
|
+
};
|
|
23186
|
+
}
|
|
23187
|
+
return null;
|
|
23188
|
+
}
|
|
23189
|
+
function steerPredicate(origin) {
|
|
23190
|
+
const hostCheck = `host === ${JSON.stringify(origin.host)}`;
|
|
23191
|
+
const base = origin.port !== void 0 ? `${origin.scheme}://${origin.host}:${origin.port}` : `${origin.scheme}://${origin.host}`;
|
|
23192
|
+
if (origin.port !== void 0) {
|
|
23193
|
+
const withSlash = JSON.stringify(`${base}/`);
|
|
23194
|
+
const exact = JSON.stringify(base);
|
|
23195
|
+
const withQuery = JSON.stringify(`${base}?`);
|
|
23196
|
+
return `${hostCheck} && (url.indexOf(${withSlash}) === 0 || url === ${exact} || url.indexOf(${withQuery}) === 0)`;
|
|
23197
|
+
}
|
|
23198
|
+
return `${hostCheck} && url.indexOf(${JSON.stringify(base)}) === 0`;
|
|
23199
|
+
}
|
|
23200
|
+
function isExactHttpUrl(value) {
|
|
23201
|
+
try {
|
|
23202
|
+
const url = new URL(value);
|
|
23203
|
+
return (url.protocol === "http:" || url.protocol === "https:") && url.username === "" && url.password === "" && url.href === value;
|
|
23204
|
+
} catch {
|
|
23205
|
+
return false;
|
|
23206
|
+
}
|
|
23207
|
+
}
|
|
22649
23208
|
function generatePacScript(routes, endpoint, options) {
|
|
22650
23209
|
if (!Array.isArray(routes)) {
|
|
22651
23210
|
throw new Error("runtime.pac-route-limit");
|
|
22652
23211
|
}
|
|
22653
|
-
const
|
|
22654
|
-
const
|
|
23212
|
+
const checksByKey = /* @__PURE__ */ new Map();
|
|
23213
|
+
for (const route of routes) {
|
|
23214
|
+
const check = pacCheck(route);
|
|
23215
|
+
if (check === null) continue;
|
|
23216
|
+
checksByKey.set(check.key, check.line);
|
|
23217
|
+
}
|
|
23218
|
+
const sorted = [...checksByKey.entries()].sort(
|
|
23219
|
+
([left], [right]) => left.localeCompare(right)
|
|
23220
|
+
);
|
|
22655
23221
|
if (sorted.length > MAX_PAC_ROUTES) {
|
|
22656
23222
|
throw new Error("runtime.pac-route-limit");
|
|
22657
23223
|
}
|
|
@@ -22659,7 +23225,7 @@ function generatePacScript(routes, endpoint, options) {
|
|
|
22659
23225
|
const proxy = `${proxyType} ${endpoint.host}:${endpoint.port}`;
|
|
22660
23226
|
const proxyLiteral = JSON.stringify(proxy);
|
|
22661
23227
|
const checks = sorted.map(
|
|
22662
|
-
(
|
|
23228
|
+
([, predicate]) => ` if (${predicate}) {
|
|
22663
23229
|
return ${proxyLiteral};
|
|
22664
23230
|
}`
|
|
22665
23231
|
);
|
|
@@ -22752,6 +23318,7 @@ function unsupportedReasons(capabilities) {
|
|
|
22752
23318
|
function createPlatformInterceptionProvider(adapter) {
|
|
22753
23319
|
let state = "stopped";
|
|
22754
23320
|
let active = false;
|
|
23321
|
+
let pacInstalled = false;
|
|
22755
23322
|
return {
|
|
22756
23323
|
platform: adapter.platform,
|
|
22757
23324
|
detect() {
|
|
@@ -22763,6 +23330,17 @@ function createPlatformInterceptionProvider(adapter) {
|
|
|
22763
23330
|
};
|
|
22764
23331
|
},
|
|
22765
23332
|
async start(activation, pacRoutes) {
|
|
23333
|
+
const decoded = pacRoutes.flatMap((entry) => {
|
|
23334
|
+
const route = pacRouteFromEntry(entry);
|
|
23335
|
+
return route === null ? [] : [route];
|
|
23336
|
+
});
|
|
23337
|
+
if (pacRoutes.length === 0) {
|
|
23338
|
+
const endpoint2 = await adapter.startTlsProxy(activation);
|
|
23339
|
+
active = true;
|
|
23340
|
+
pacInstalled = false;
|
|
23341
|
+
state = "running";
|
|
23342
|
+
return endpoint2;
|
|
23343
|
+
}
|
|
22766
23344
|
const capabilities = adapter.detect();
|
|
22767
23345
|
const reasons = unsupportedReasons(capabilities);
|
|
22768
23346
|
if (!capabilities.supported || reasons.length > 0) {
|
|
@@ -22774,18 +23352,17 @@ function createPlatformInterceptionProvider(adapter) {
|
|
|
22774
23352
|
throw new Error("runtime.platform-unsupported");
|
|
22775
23353
|
}
|
|
22776
23354
|
const endpoint = await adapter.startTlsProxy(activation);
|
|
22777
|
-
const pac = generatePacScript(
|
|
22778
|
-
pacRoutes.map((hostname) => ({ hostname })),
|
|
22779
|
-
endpoint
|
|
22780
|
-
);
|
|
23355
|
+
const pac = generatePacScript(decoded, endpoint);
|
|
22781
23356
|
try {
|
|
22782
23357
|
await adapter.installPac(pac);
|
|
22783
23358
|
active = true;
|
|
23359
|
+
pacInstalled = true;
|
|
22784
23360
|
state = "running";
|
|
22785
23361
|
return endpoint;
|
|
22786
23362
|
} catch (error) {
|
|
22787
23363
|
await adapter.stopTlsProxy();
|
|
22788
23364
|
active = false;
|
|
23365
|
+
pacInstalled = false;
|
|
22789
23366
|
state = "stopped";
|
|
22790
23367
|
throw error;
|
|
22791
23368
|
}
|
|
@@ -22795,9 +23372,10 @@ function createPlatformInterceptionProvider(adapter) {
|
|
|
22795
23372
|
state = state === "unsupported" ? "unsupported" : "stopped";
|
|
22796
23373
|
return;
|
|
22797
23374
|
}
|
|
22798
|
-
await adapter.removePac();
|
|
23375
|
+
if (pacInstalled) await adapter.removePac();
|
|
22799
23376
|
await adapter.stopTlsProxy();
|
|
22800
23377
|
active = false;
|
|
23378
|
+
pacInstalled = false;
|
|
22801
23379
|
state = "stopped";
|
|
22802
23380
|
},
|
|
22803
23381
|
status() {
|
|
@@ -23397,7 +23975,7 @@ function createNativeRuntimeController(options) {
|
|
|
23397
23975
|
return aiConfigInForce;
|
|
23398
23976
|
}
|
|
23399
23977
|
async function bindInterception(current, sessionConfig) {
|
|
23400
|
-
if (sessionConfig.pacRoutes.length === 0) {
|
|
23978
|
+
if (sessionConfig.pacRoutes.length === 0 && sessionConfig.contentListener !== true) {
|
|
23401
23979
|
return {
|
|
23402
23980
|
activation: { ...current, pacRoutes: [] },
|
|
23403
23981
|
session: null,
|
|
@@ -23606,7 +24184,8 @@ function createNativeRuntimeController(options) {
|
|
|
23606
24184
|
preset = normalized.value;
|
|
23607
24185
|
activePolicy = {
|
|
23608
24186
|
project: schemaResult.data,
|
|
23609
|
-
operations: compileResult.operations
|
|
24187
|
+
operations: compileResult.operations,
|
|
24188
|
+
presetDigest: preset.digest
|
|
23610
24189
|
};
|
|
23611
24190
|
const startResult = await this.start();
|
|
23612
24191
|
if (startResult.state !== "running") {
|
|
@@ -23654,6 +24233,7 @@ function createNativeRuntimeController(options) {
|
|
|
23654
24233
|
policyDigest: typeof meta.policyDigest === "string" ? meta.policyDigest : "",
|
|
23655
24234
|
extensionId: typeof meta.extensionId === "string" ? meta.extensionId : "",
|
|
23656
24235
|
pacRoutes: Array.isArray(meta.pacRoutes) ? meta.pacRoutes : [],
|
|
24236
|
+
contentListener: meta.contentListener === true,
|
|
23657
24237
|
targetPolicy: {
|
|
23658
24238
|
public: meta.targetPolicy?.public === true || meta.targetPolicy?.publicAllowed === true,
|
|
23659
24239
|
localOrigins: Array.isArray(meta.targetPolicy?.localOrigins) ? meta.targetPolicy.localOrigins : []
|
|
@@ -24507,7 +25087,11 @@ function createNativeHost(options) {
|
|
|
24507
25087
|
}
|
|
24508
25088
|
const policy = controller.getActivePolicy();
|
|
24509
25089
|
interceptProxy = await startInterceptProxy({
|
|
24510
|
-
policy: policy ? {
|
|
25090
|
+
policy: policy ? {
|
|
25091
|
+
project: policy.project,
|
|
25092
|
+
operations: policy.operations,
|
|
25093
|
+
presetDigest: policy.presetDigest
|
|
25094
|
+
} : null
|
|
24511
25095
|
});
|
|
24512
25096
|
return interceptProxy.endpoint;
|
|
24513
25097
|
},
|
|
@@ -24528,7 +25112,11 @@ function createNativeHost(options) {
|
|
|
24528
25112
|
const endpoint = await platformProvider.start(activation, routes);
|
|
24529
25113
|
const policy = controller.getActivePolicy();
|
|
24530
25114
|
interceptProxy?.setPolicy(
|
|
24531
|
-
policy ? {
|
|
25115
|
+
policy ? {
|
|
25116
|
+
project: policy.project,
|
|
25117
|
+
operations: policy.operations,
|
|
25118
|
+
presetDigest: policy.presetDigest
|
|
25119
|
+
} : null
|
|
24532
25120
|
);
|
|
24533
25121
|
return endpoint;
|
|
24534
25122
|
},
|
package/package.json
CHANGED