@rogatio/cli 6.16.2 → 6.16.3
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 +231 -45
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -2301,6 +2301,7 @@ import { isAbsolute as isAbsolute3 } from "node:path";
|
|
|
2301
2301
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
2302
2302
|
import { existsSync as existsSync2 } from "node:fs";
|
|
2303
2303
|
import { join as join6 } from "node:path";
|
|
2304
|
+
import { lookup as dnsLookup2 } from "node:dns/promises";
|
|
2304
2305
|
import {
|
|
2305
2306
|
createServer as createServer2
|
|
2306
2307
|
} from "node:http";
|
|
@@ -25131,6 +25132,133 @@ function forwardRequestHeaders(headers, host, contentLength) {
|
|
|
25131
25132
|
}
|
|
25132
25133
|
return out;
|
|
25133
25134
|
}
|
|
25135
|
+
var defaultResolver2 = {
|
|
25136
|
+
async lookup(hostname, signal) {
|
|
25137
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
25138
|
+
const addresses = await dnsLookup2(hostname, { all: true });
|
|
25139
|
+
return addresses.map((address) => ({
|
|
25140
|
+
address: address.address,
|
|
25141
|
+
family: address.family === 6 ? 6 : 4
|
|
25142
|
+
}));
|
|
25143
|
+
}
|
|
25144
|
+
};
|
|
25145
|
+
var defaultDial = (port, address) => connect(port, address);
|
|
25146
|
+
function localOriginsFromProject(project) {
|
|
25147
|
+
if (project === null || typeof project !== "object") return [];
|
|
25148
|
+
const policy = project.requestBodyPolicy;
|
|
25149
|
+
if (policy === null || typeof policy !== "object") return [];
|
|
25150
|
+
const origins = policy.localOrigins;
|
|
25151
|
+
if (!Array.isArray(origins)) return [];
|
|
25152
|
+
return origins.filter(
|
|
25153
|
+
(origin) => typeof origin === "string"
|
|
25154
|
+
);
|
|
25155
|
+
}
|
|
25156
|
+
function effectiveLocalOrigins(policy) {
|
|
25157
|
+
if (policy === null) return /* @__PURE__ */ new Set();
|
|
25158
|
+
const raw = policy.localOrigins ?? localOriginsFromProject(policy.project);
|
|
25159
|
+
const origins = /* @__PURE__ */ new Set();
|
|
25160
|
+
for (const origin of raw) {
|
|
25161
|
+
const normalized = normalizeSiteOrigin(origin);
|
|
25162
|
+
if (normalized !== null) origins.add(normalized);
|
|
25163
|
+
}
|
|
25164
|
+
return origins;
|
|
25165
|
+
}
|
|
25166
|
+
function compareAddresses(left, right) {
|
|
25167
|
+
return left.family - right.family || (left.address < right.address ? -1 : left.address > right.address ? 1 : 0);
|
|
25168
|
+
}
|
|
25169
|
+
function selectPinnedAddress(addresses, allowNonPublic) {
|
|
25170
|
+
const publicOnly = validateResolvedAddresses(
|
|
25171
|
+
addresses,
|
|
25172
|
+
RUNTIME_LIMITS.maxDnsAddresses
|
|
25173
|
+
);
|
|
25174
|
+
if (publicOnly !== null) return publicOnly[0] ?? null;
|
|
25175
|
+
if (!allowNonPublic) return null;
|
|
25176
|
+
if (addresses.length === 0 || addresses.length > RUNTIME_LIMITS.maxDnsAddresses) {
|
|
25177
|
+
return null;
|
|
25178
|
+
}
|
|
25179
|
+
const checked = [];
|
|
25180
|
+
for (const address of addresses) {
|
|
25181
|
+
if (address.family !== 4 && address.family !== 6) return null;
|
|
25182
|
+
const classification = classifyAddress(address.address);
|
|
25183
|
+
if (classification === "invalid" || classification === "public") {
|
|
25184
|
+
return null;
|
|
25185
|
+
}
|
|
25186
|
+
checked.push({ address: address.address, family: address.family });
|
|
25187
|
+
}
|
|
25188
|
+
checked.sort(compareAddresses);
|
|
25189
|
+
return checked[0] ?? null;
|
|
25190
|
+
}
|
|
25191
|
+
async function pinHostname(hostname, origin, deps) {
|
|
25192
|
+
if (hostname.length === 0) return null;
|
|
25193
|
+
let resolved;
|
|
25194
|
+
try {
|
|
25195
|
+
resolved = await deps.resolver.lookup(hostname);
|
|
25196
|
+
} catch {
|
|
25197
|
+
return null;
|
|
25198
|
+
}
|
|
25199
|
+
const normalized = normalizeSiteOrigin(origin);
|
|
25200
|
+
const allowNonPublic = normalized !== null && deps.localOrigins.has(normalized);
|
|
25201
|
+
return selectPinnedAddress(resolved, allowNonPublic);
|
|
25202
|
+
}
|
|
25203
|
+
function httpUpstreamPort(target) {
|
|
25204
|
+
if (target.protocol !== "http:") return null;
|
|
25205
|
+
if (target.username.length > 0 || target.password.length > 0) return null;
|
|
25206
|
+
if (target.port.length === 0 || target.port === "80") return 80;
|
|
25207
|
+
return null;
|
|
25208
|
+
}
|
|
25209
|
+
function formatUpstreamRequest(method, path2, headers) {
|
|
25210
|
+
const lines = [];
|
|
25211
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
25212
|
+
if (name.includes("\r") || name.includes("\n") || value.includes("\r") || value.includes("\n")) {
|
|
25213
|
+
return null;
|
|
25214
|
+
}
|
|
25215
|
+
lines.push(`${name}: ${value}`);
|
|
25216
|
+
}
|
|
25217
|
+
return `${method} ${path2} HTTP/1.1\r
|
|
25218
|
+
${lines.join("\r\n")}\r
|
|
25219
|
+
\r
|
|
25220
|
+
`;
|
|
25221
|
+
}
|
|
25222
|
+
function httpsOrigin(host) {
|
|
25223
|
+
const authority = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
25224
|
+
try {
|
|
25225
|
+
return new URL(`https://${authority}`).origin;
|
|
25226
|
+
} catch {
|
|
25227
|
+
return null;
|
|
25228
|
+
}
|
|
25229
|
+
}
|
|
25230
|
+
function parseConnectAuthority(authority) {
|
|
25231
|
+
if (authority.length === 0) return null;
|
|
25232
|
+
let host;
|
|
25233
|
+
let portText;
|
|
25234
|
+
if (authority.startsWith("[")) {
|
|
25235
|
+
const end = authority.indexOf("]");
|
|
25236
|
+
if (end <= 1) return null;
|
|
25237
|
+
host = authority.slice(1, end);
|
|
25238
|
+
const rest = authority.slice(end + 1);
|
|
25239
|
+
if (!rest.startsWith(":")) return null;
|
|
25240
|
+
portText = rest.slice(1);
|
|
25241
|
+
} else {
|
|
25242
|
+
const colon = authority.lastIndexOf(":");
|
|
25243
|
+
if (colon <= 0) return null;
|
|
25244
|
+
host = authority.slice(0, colon);
|
|
25245
|
+
portText = authority.slice(colon + 1);
|
|
25246
|
+
}
|
|
25247
|
+
if (host.length === 0 || !/^\d+$/.test(portText)) return null;
|
|
25248
|
+
const port = Number(portText);
|
|
25249
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
|
|
25250
|
+
return { host, port };
|
|
25251
|
+
}
|
|
25252
|
+
function endConnect(socket, status, reason) {
|
|
25253
|
+
if (socket.destroyed || socket.writableEnded) return;
|
|
25254
|
+
socket.end(
|
|
25255
|
+
`HTTP/1.1 ${status} ${reason}\r
|
|
25256
|
+
Content-Length: 0\r
|
|
25257
|
+
Connection: close\r
|
|
25258
|
+
\r
|
|
25259
|
+
`
|
|
25260
|
+
);
|
|
25261
|
+
}
|
|
25134
25262
|
function pipeSockets(a, b) {
|
|
25135
25263
|
a.pipe(b);
|
|
25136
25264
|
b.pipe(a);
|
|
@@ -25143,11 +25271,16 @@ function pipeSockets(a, b) {
|
|
|
25143
25271
|
a.on("close", () => b.destroy());
|
|
25144
25272
|
b.on("close", () => a.destroy());
|
|
25145
25273
|
}
|
|
25146
|
-
async function upstreamHttp(method, target, headers, body) {
|
|
25147
|
-
const port =
|
|
25274
|
+
async function upstreamHttp(method, target, headers, body, deps) {
|
|
25275
|
+
const port = httpUpstreamPort(target);
|
|
25276
|
+
if (port === null) return { ok: false, status: 403 };
|
|
25148
25277
|
const path2 = `${target.pathname}${target.search}`;
|
|
25278
|
+
const head = formatUpstreamRequest(method, path2, headers);
|
|
25279
|
+
if (head === null) return { ok: false, status: 403 };
|
|
25280
|
+
const pinned = await pinHostname(target.hostname, target.origin, deps);
|
|
25281
|
+
if (pinned === null) return { ok: false, status: 403 };
|
|
25149
25282
|
return new Promise((resolve12, reject) => {
|
|
25150
|
-
const socket =
|
|
25283
|
+
const socket = deps.dial(port, pinned.address);
|
|
25151
25284
|
let buf = Buffer.alloc(0);
|
|
25152
25285
|
let settled = false;
|
|
25153
25286
|
const fail2 = (err) => {
|
|
@@ -25160,11 +25293,7 @@ async function upstreamHttp(method, target, headers, body) {
|
|
|
25160
25293
|
socket.on("timeout", () => fail2(new Error("upstream timeout")));
|
|
25161
25294
|
socket.on("error", (err) => fail2(err));
|
|
25162
25295
|
socket.on("connect", () => {
|
|
25163
|
-
|
|
25164
|
-
socket.write(`${method} ${path2} HTTP/1.1\r
|
|
25165
|
-
${lines}\r
|
|
25166
|
-
\r
|
|
25167
|
-
`);
|
|
25296
|
+
socket.write(head);
|
|
25168
25297
|
if (body.byteLength > 0) socket.write(body);
|
|
25169
25298
|
});
|
|
25170
25299
|
socket.on("data", (chunk) => {
|
|
@@ -25172,9 +25301,9 @@ ${lines}\r
|
|
|
25172
25301
|
buf = Buffer.concat([buf, piece]);
|
|
25173
25302
|
const sep = buf.indexOf("\r\n\r\n");
|
|
25174
25303
|
if (sep < 0) return;
|
|
25175
|
-
const
|
|
25304
|
+
const head2 = buf.subarray(0, sep).toString("latin1");
|
|
25176
25305
|
const statusMatch = /^HTTP\/1\.[01] (\d+)(?: (.*))?/.exec(
|
|
25177
|
-
|
|
25306
|
+
head2.split("\r\n")[0] ?? ""
|
|
25178
25307
|
);
|
|
25179
25308
|
if (!statusMatch) {
|
|
25180
25309
|
fail2(new Error("bad upstream status"));
|
|
@@ -25183,7 +25312,7 @@ ${lines}\r
|
|
|
25183
25312
|
const status = Number(statusMatch[1]);
|
|
25184
25313
|
const statusMessage = statusMatch[2]?.trim() ?? "";
|
|
25185
25314
|
const headerMap = {};
|
|
25186
|
-
for (const line of
|
|
25315
|
+
for (const line of head2.split("\r\n").slice(1)) {
|
|
25187
25316
|
const i = line.indexOf(":");
|
|
25188
25317
|
if (i <= 0) continue;
|
|
25189
25318
|
const name = line.slice(0, i).trim().toLowerCase();
|
|
@@ -25203,6 +25332,7 @@ ${lines}\r
|
|
|
25203
25332
|
settled = true;
|
|
25204
25333
|
socket.destroy();
|
|
25205
25334
|
resolve12({
|
|
25335
|
+
ok: true,
|
|
25206
25336
|
status,
|
|
25207
25337
|
statusMessage,
|
|
25208
25338
|
headers: headerMap,
|
|
@@ -25218,16 +25348,16 @@ ${lines}\r
|
|
|
25218
25348
|
fail2(new Error("upstream closed early"));
|
|
25219
25349
|
return;
|
|
25220
25350
|
}
|
|
25221
|
-
const
|
|
25351
|
+
const head2 = buf.subarray(0, sep).toString("latin1");
|
|
25222
25352
|
const statusMatch = /^HTTP\/1\.[01] (\d+)(?: (.*))?/.exec(
|
|
25223
|
-
|
|
25353
|
+
head2.split("\r\n")[0] ?? ""
|
|
25224
25354
|
);
|
|
25225
25355
|
if (!statusMatch) {
|
|
25226
25356
|
fail2(new Error("bad upstream status"));
|
|
25227
25357
|
return;
|
|
25228
25358
|
}
|
|
25229
25359
|
const headerMap = {};
|
|
25230
|
-
for (const line of
|
|
25360
|
+
for (const line of head2.split("\r\n").slice(1)) {
|
|
25231
25361
|
const i = line.indexOf(":");
|
|
25232
25362
|
if (i <= 0) continue;
|
|
25233
25363
|
headerMap[line.slice(0, i).trim().toLowerCase()] = line.slice(i + 1).trim();
|
|
@@ -25242,6 +25372,7 @@ ${lines}\r
|
|
|
25242
25372
|
}
|
|
25243
25373
|
settled = true;
|
|
25244
25374
|
resolve12({
|
|
25375
|
+
ok: true,
|
|
25245
25376
|
status: Number(statusMatch[1]),
|
|
25246
25377
|
statusMessage: statusMatch[2]?.trim() ?? "",
|
|
25247
25378
|
headers: headerMap,
|
|
@@ -25317,6 +25448,8 @@ function authorizeOp(policy, op, url, method, headers) {
|
|
|
25317
25448
|
async function startInterceptProxy(options = {}) {
|
|
25318
25449
|
let policy = options.policy ?? null;
|
|
25319
25450
|
const serveMock = options.serveMock;
|
|
25451
|
+
const resolver = options.resolver ?? defaultResolver2;
|
|
25452
|
+
const dial = options.dial ?? defaultDial;
|
|
25320
25453
|
let activeMocks = 0;
|
|
25321
25454
|
const mockAborts = /* @__PURE__ */ new Set();
|
|
25322
25455
|
let stopped = false;
|
|
@@ -25411,8 +25544,24 @@ async function startInterceptProxy(options = {}) {
|
|
|
25411
25544
|
method,
|
|
25412
25545
|
target,
|
|
25413
25546
|
forwardHeaders,
|
|
25414
|
-
requestBody
|
|
25547
|
+
requestBody,
|
|
25548
|
+
{
|
|
25549
|
+
resolver,
|
|
25550
|
+
dial,
|
|
25551
|
+
localOrigins: effectiveLocalOrigins(active)
|
|
25552
|
+
}
|
|
25415
25553
|
);
|
|
25554
|
+
if (!upstream.ok) {
|
|
25555
|
+
if (!res.headersSent) {
|
|
25556
|
+
res.writeHead(upstream.status, {
|
|
25557
|
+
"cache-control": "no-store",
|
|
25558
|
+
"content-length": "0",
|
|
25559
|
+
connection: "close"
|
|
25560
|
+
});
|
|
25561
|
+
res.end();
|
|
25562
|
+
}
|
|
25563
|
+
return;
|
|
25564
|
+
}
|
|
25416
25565
|
let responseBody = upstream.body;
|
|
25417
25566
|
if (responseOp !== null && active !== null) {
|
|
25418
25567
|
if (responseBody.byteLength > RUNTIME_LIMITS.maxResponseBodyBytes) {
|
|
@@ -25608,25 +25757,52 @@ async function startInterceptProxy(options = {}) {
|
|
|
25608
25757
|
);
|
|
25609
25758
|
}
|
|
25610
25759
|
function handleConnect(req, clientSocket, head) {
|
|
25611
|
-
|
|
25612
|
-
|
|
25613
|
-
|
|
25614
|
-
|
|
25615
|
-
|
|
25616
|
-
|
|
25760
|
+
void tunnelConnect(req, clientSocket, head);
|
|
25761
|
+
}
|
|
25762
|
+
async function tunnelConnect(req, clientSocket, head) {
|
|
25763
|
+
let upstream;
|
|
25764
|
+
clientSocket.on("error", () => upstream?.destroy());
|
|
25765
|
+
const parsed = parseConnectAuthority(req.url ?? "");
|
|
25766
|
+
if (parsed === null) {
|
|
25767
|
+
endConnect(clientSocket, 400, "Bad Request");
|
|
25768
|
+
return;
|
|
25769
|
+
}
|
|
25770
|
+
if (parsed.port !== 443) {
|
|
25771
|
+
endConnect(clientSocket, 403, "Forbidden");
|
|
25772
|
+
return;
|
|
25773
|
+
}
|
|
25774
|
+
const pinned = await pinHostname(
|
|
25775
|
+
parsed.host,
|
|
25776
|
+
httpsOrigin(parsed.host) ?? "",
|
|
25777
|
+
{
|
|
25778
|
+
resolver,
|
|
25779
|
+
dial,
|
|
25780
|
+
localOrigins: effectiveLocalOrigins(policy)
|
|
25781
|
+
}
|
|
25782
|
+
);
|
|
25783
|
+
if (pinned === null || clientSocket.destroyed) {
|
|
25784
|
+
endConnect(clientSocket, 403, "Forbidden");
|
|
25785
|
+
return;
|
|
25786
|
+
}
|
|
25787
|
+
let upstreamSocket;
|
|
25788
|
+
try {
|
|
25789
|
+
upstreamSocket = dial(443, pinned.address);
|
|
25790
|
+
} catch {
|
|
25791
|
+
endConnect(clientSocket, 502, "Bad Gateway");
|
|
25617
25792
|
return;
|
|
25618
25793
|
}
|
|
25619
|
-
|
|
25620
|
-
|
|
25794
|
+
upstream = upstreamSocket;
|
|
25795
|
+
upstreamSocket.on("connect", () => {
|
|
25621
25796
|
clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
25622
|
-
if (head.byteLength > 0)
|
|
25623
|
-
pipeSockets(clientSocket,
|
|
25797
|
+
if (head.byteLength > 0) upstreamSocket.write(head);
|
|
25798
|
+
pipeSockets(clientSocket, upstreamSocket);
|
|
25624
25799
|
});
|
|
25625
|
-
|
|
25626
|
-
clientSocket.
|
|
25627
|
-
|
|
25800
|
+
upstreamSocket.on("error", () => {
|
|
25801
|
+
if (!clientSocket.destroyed && !clientSocket.writableEnded) {
|
|
25802
|
+
endConnect(clientSocket, 502, "Bad Gateway");
|
|
25803
|
+
}
|
|
25804
|
+
upstreamSocket.destroy();
|
|
25628
25805
|
});
|
|
25629
|
-
clientSocket.on("error", () => upstream.destroy());
|
|
25630
25806
|
}
|
|
25631
25807
|
const endpoint = await new Promise((resolve12, reject) => {
|
|
25632
25808
|
server.once("error", reject);
|
|
@@ -25677,7 +25853,10 @@ async function startInterception(activation, policyDigest, extensionId, pacRoute
|
|
|
25677
25853
|
const sessionId = `session-${activation.startedAt}-${Math.random().toString(36).slice(2)}`;
|
|
25678
25854
|
let proxy;
|
|
25679
25855
|
try {
|
|
25680
|
-
const started = await registeredProvider.start(
|
|
25856
|
+
const started = await registeredProvider.start({
|
|
25857
|
+
...activation,
|
|
25858
|
+
localOrigins: targetPolicy.localOrigins
|
|
25859
|
+
});
|
|
25681
25860
|
if (started && typeof started === "object" && typeof started.host === "string" && typeof started.port === "number") {
|
|
25682
25861
|
proxy = started;
|
|
25683
25862
|
} else if (activation.proxy) {
|
|
@@ -27414,6 +27593,25 @@ function createNativeHost(options) {
|
|
|
27414
27593
|
...options.clock ? { clock: options.clock } : {}
|
|
27415
27594
|
});
|
|
27416
27595
|
let interceptProxy = null;
|
|
27596
|
+
function policyForProxy(activation) {
|
|
27597
|
+
const policy = controller.getActivePolicy();
|
|
27598
|
+
if (!policy) return null;
|
|
27599
|
+
const fromProject = localOriginsFromProject(policy.project);
|
|
27600
|
+
const fromSession = activation.localOrigins ?? [];
|
|
27601
|
+
const localOrigins = [];
|
|
27602
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27603
|
+
for (const origin of [...fromProject, ...fromSession]) {
|
|
27604
|
+
if (seen.has(origin)) continue;
|
|
27605
|
+
seen.add(origin);
|
|
27606
|
+
localOrigins.push(origin);
|
|
27607
|
+
}
|
|
27608
|
+
return {
|
|
27609
|
+
project: policy.project,
|
|
27610
|
+
operations: policy.operations,
|
|
27611
|
+
presetDigest: policy.presetDigest,
|
|
27612
|
+
localOrigins
|
|
27613
|
+
};
|
|
27614
|
+
}
|
|
27417
27615
|
let outboundWrite = null;
|
|
27418
27616
|
let hostRequestCounter = 0;
|
|
27419
27617
|
const pendingHost = /* @__PURE__ */ new Map();
|
|
@@ -27482,18 +27680,13 @@ function createNativeHost(options) {
|
|
|
27482
27680
|
} catch {
|
|
27483
27681
|
}
|
|
27484
27682
|
},
|
|
27485
|
-
async startTlsProxy() {
|
|
27683
|
+
async startTlsProxy(activation) {
|
|
27486
27684
|
if (interceptProxy) {
|
|
27487
27685
|
await interceptProxy.stop();
|
|
27488
27686
|
interceptProxy = null;
|
|
27489
27687
|
}
|
|
27490
|
-
const policy = controller.getActivePolicy();
|
|
27491
27688
|
interceptProxy = await startInterceptProxy({
|
|
27492
|
-
policy:
|
|
27493
|
-
project: policy.project,
|
|
27494
|
-
operations: policy.operations,
|
|
27495
|
-
presetDigest: policy.presetDigest
|
|
27496
|
-
} : null,
|
|
27689
|
+
policy: policyForProxy(activation),
|
|
27497
27690
|
serveMock: (request) => controller.serveMock(request.token, {
|
|
27498
27691
|
method: request.method,
|
|
27499
27692
|
signal: request.signal
|
|
@@ -27516,14 +27709,7 @@ function createNativeHost(options) {
|
|
|
27516
27709
|
async start(activation) {
|
|
27517
27710
|
const routes = pendingRoutes.length > 0 ? pendingRoutes : activation.pacRoutes;
|
|
27518
27711
|
const endpoint = await platformProvider.start(activation, routes);
|
|
27519
|
-
|
|
27520
|
-
interceptProxy?.setPolicy(
|
|
27521
|
-
policy ? {
|
|
27522
|
-
project: policy.project,
|
|
27523
|
-
operations: policy.operations,
|
|
27524
|
-
presetDigest: policy.presetDigest
|
|
27525
|
-
} : null
|
|
27526
|
-
);
|
|
27712
|
+
interceptProxy?.setPolicy(policyForProxy(activation));
|
|
27527
27713
|
return endpoint;
|
|
27528
27714
|
},
|
|
27529
27715
|
async stop() {
|
package/package.json
CHANGED