@rogatio/cli 6.16.1 → 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 +275 -61
- 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";
|
|
@@ -22681,6 +22682,18 @@ async function readPreviousRogatioCa(caCertFile) {
|
|
|
22681
22682
|
return void 0;
|
|
22682
22683
|
}
|
|
22683
22684
|
}
|
|
22685
|
+
async function assertReadableIfRegular(path2, snapshot2) {
|
|
22686
|
+
if (snapshot2 !== void 0) return;
|
|
22687
|
+
try {
|
|
22688
|
+
const info = await lstat(path2);
|
|
22689
|
+
if (!info.isFile()) return;
|
|
22690
|
+
} catch {
|
|
22691
|
+
return;
|
|
22692
|
+
}
|
|
22693
|
+
throw new TrustError("trust.write-failed", "CA file could not be read", [
|
|
22694
|
+
"trust.write-failed"
|
|
22695
|
+
]);
|
|
22696
|
+
}
|
|
22684
22697
|
async function discardPreviousCa(caPreviousCertFile) {
|
|
22685
22698
|
try {
|
|
22686
22699
|
await rm(caPreviousCertFile, { force: true });
|
|
@@ -22732,6 +22745,9 @@ function createRequestBodyTrustController(options = {}) {
|
|
|
22732
22745
|
let reusedExisting = false;
|
|
22733
22746
|
let previousKeyPem;
|
|
22734
22747
|
let previousPubPem;
|
|
22748
|
+
let replacedKey = false;
|
|
22749
|
+
let replacedPub = false;
|
|
22750
|
+
let replacedCert = false;
|
|
22735
22751
|
const caCertFile = join2(
|
|
22736
22752
|
installRoot,
|
|
22737
22753
|
options.caCertFileName ?? ".rogatio-ca.crt"
|
|
@@ -22838,32 +22854,35 @@ function createRequestBodyTrustController(options = {}) {
|
|
|
22838
22854
|
console.error("[rogatio] previous CA trust anchor was not removed");
|
|
22839
22855
|
}
|
|
22840
22856
|
}
|
|
22841
|
-
async function
|
|
22842
|
-
await rm(caKeyFile, { force: true });
|
|
22843
|
-
await rm(caPubFile, { force: true });
|
|
22844
|
-
await rm(caCertFile, { force: true });
|
|
22857
|
+
async function removeReplacedCaFiles() {
|
|
22858
|
+
if (replacedKey) await rm(caKeyFile, { force: true });
|
|
22859
|
+
if (replacedPub) await rm(caPubFile, { force: true });
|
|
22860
|
+
if (replacedCert) await rm(caCertFile, { force: true });
|
|
22861
|
+
}
|
|
22862
|
+
async function restoreOrDrop(path2, snapshot2, replaced, mode) {
|
|
22863
|
+
if (snapshot2 !== void 0) {
|
|
22864
|
+
await writeFileAtomic(path2, snapshot2, mode);
|
|
22865
|
+
return;
|
|
22866
|
+
}
|
|
22867
|
+
if (replaced) await rm(path2, { force: true });
|
|
22845
22868
|
}
|
|
22846
22869
|
async function rollbackReplacedCa() {
|
|
22847
22870
|
if (reusedExisting) return;
|
|
22848
22871
|
if (!replacingPrevious) {
|
|
22849
|
-
await
|
|
22872
|
+
await removeReplacedCaFiles();
|
|
22850
22873
|
return;
|
|
22851
22874
|
}
|
|
22852
22875
|
const saved = await readPreviousRogatioCa(caPreviousCertFile);
|
|
22853
22876
|
const live = await readPreviousRogatioCa(caCertFile);
|
|
22854
22877
|
const copyKept = rememberedFingerprint !== void 0 && saved?.fingerprint === rememberedFingerprint;
|
|
22855
22878
|
const liveDiffers = live?.fingerprint !== rememberedFingerprint;
|
|
22856
|
-
if (copyKept && liveDiffers) {
|
|
22857
|
-
await
|
|
22879
|
+
if (copyKept && replacedCert && liveDiffers) {
|
|
22880
|
+
await removeReplacedCaFiles();
|
|
22858
22881
|
return;
|
|
22859
22882
|
}
|
|
22860
|
-
if (copyKept && !
|
|
22861
|
-
|
|
22862
|
-
|
|
22863
|
-
}
|
|
22864
|
-
if (previousPubPem !== void 0) {
|
|
22865
|
-
await writeFileAtomic(caPubFile, previousPubPem);
|
|
22866
|
-
}
|
|
22883
|
+
if (copyKept && !replacedCert) {
|
|
22884
|
+
await restoreOrDrop(caKeyFile, previousKeyPem, replacedKey, 384);
|
|
22885
|
+
await restoreOrDrop(caPubFile, previousPubPem, replacedPub);
|
|
22867
22886
|
}
|
|
22868
22887
|
}
|
|
22869
22888
|
async function runCaTrust() {
|
|
@@ -22881,12 +22900,21 @@ function createRequestBodyTrustController(options = {}) {
|
|
|
22881
22900
|
reusedExisting = false;
|
|
22882
22901
|
previousKeyPem = void 0;
|
|
22883
22902
|
previousPubPem = void 0;
|
|
22903
|
+
replacedKey = false;
|
|
22904
|
+
replacedPub = false;
|
|
22905
|
+
replacedCert = false;
|
|
22884
22906
|
const reusable = await reusableCaMaterial();
|
|
22885
22907
|
reusedExisting = reusable;
|
|
22886
22908
|
const liveAnchor = await readPreviousRogatioCa(caCertFile);
|
|
22887
22909
|
const siblingAnchor = await readPreviousRogatioCa(caPreviousCertFile);
|
|
22888
22910
|
let preserved;
|
|
22889
22911
|
if (!reusable) {
|
|
22912
|
+
const rawCert = await readNoFollowRegularFile(caCertFile);
|
|
22913
|
+
previousKeyPem = await readNoFollowRegularFile(caKeyFile);
|
|
22914
|
+
previousPubPem = await readNoFollowRegularFile(caPubFile);
|
|
22915
|
+
await assertReadableIfRegular(caCertFile, rawCert);
|
|
22916
|
+
await assertReadableIfRegular(caKeyFile, previousKeyPem);
|
|
22917
|
+
await assertReadableIfRegular(caPubFile, previousPubPem);
|
|
22890
22918
|
replacingPrevious = liveAnchor !== void 0 || siblingAnchor !== void 0;
|
|
22891
22919
|
const { privateKey } = generateCaKeyPair(TRUST_LIMITS.caKeyBits);
|
|
22892
22920
|
const certResult = await createCertificate(
|
|
@@ -22914,11 +22942,12 @@ function createRequestBodyTrustController(options = {}) {
|
|
|
22914
22942
|
} else if (siblingAnchor) {
|
|
22915
22943
|
await discardPreviousCa(caPreviousCertFile);
|
|
22916
22944
|
}
|
|
22917
|
-
previousKeyPem = await readNoFollowRegularFile(caKeyFile);
|
|
22918
|
-
previousPubPem = await readNoFollowRegularFile(caPubFile);
|
|
22919
22945
|
await writeFileAtomic(caKeyFile, certResult.keyPem, 384);
|
|
22946
|
+
replacedKey = true;
|
|
22920
22947
|
await writeFileAtomic(caPubFile, certResult.certPem);
|
|
22948
|
+
replacedPub = true;
|
|
22921
22949
|
await writeFileAtomic(caCertFile, certResult.certPem);
|
|
22950
|
+
replacedCert = true;
|
|
22922
22951
|
}
|
|
22923
22952
|
let installedNew = false;
|
|
22924
22953
|
if (caTrustInstaller && !installerCalled) {
|
|
@@ -25103,6 +25132,133 @@ function forwardRequestHeaders(headers, host, contentLength) {
|
|
|
25103
25132
|
}
|
|
25104
25133
|
return out;
|
|
25105
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
|
+
}
|
|
25106
25262
|
function pipeSockets(a, b) {
|
|
25107
25263
|
a.pipe(b);
|
|
25108
25264
|
b.pipe(a);
|
|
@@ -25115,11 +25271,16 @@ function pipeSockets(a, b) {
|
|
|
25115
25271
|
a.on("close", () => b.destroy());
|
|
25116
25272
|
b.on("close", () => a.destroy());
|
|
25117
25273
|
}
|
|
25118
|
-
async function upstreamHttp(method, target, headers, body) {
|
|
25119
|
-
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 };
|
|
25120
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 };
|
|
25121
25282
|
return new Promise((resolve12, reject) => {
|
|
25122
|
-
const socket =
|
|
25283
|
+
const socket = deps.dial(port, pinned.address);
|
|
25123
25284
|
let buf = Buffer.alloc(0);
|
|
25124
25285
|
let settled = false;
|
|
25125
25286
|
const fail2 = (err) => {
|
|
@@ -25132,11 +25293,7 @@ async function upstreamHttp(method, target, headers, body) {
|
|
|
25132
25293
|
socket.on("timeout", () => fail2(new Error("upstream timeout")));
|
|
25133
25294
|
socket.on("error", (err) => fail2(err));
|
|
25134
25295
|
socket.on("connect", () => {
|
|
25135
|
-
|
|
25136
|
-
socket.write(`${method} ${path2} HTTP/1.1\r
|
|
25137
|
-
${lines}\r
|
|
25138
|
-
\r
|
|
25139
|
-
`);
|
|
25296
|
+
socket.write(head);
|
|
25140
25297
|
if (body.byteLength > 0) socket.write(body);
|
|
25141
25298
|
});
|
|
25142
25299
|
socket.on("data", (chunk) => {
|
|
@@ -25144,9 +25301,9 @@ ${lines}\r
|
|
|
25144
25301
|
buf = Buffer.concat([buf, piece]);
|
|
25145
25302
|
const sep = buf.indexOf("\r\n\r\n");
|
|
25146
25303
|
if (sep < 0) return;
|
|
25147
|
-
const
|
|
25304
|
+
const head2 = buf.subarray(0, sep).toString("latin1");
|
|
25148
25305
|
const statusMatch = /^HTTP\/1\.[01] (\d+)(?: (.*))?/.exec(
|
|
25149
|
-
|
|
25306
|
+
head2.split("\r\n")[0] ?? ""
|
|
25150
25307
|
);
|
|
25151
25308
|
if (!statusMatch) {
|
|
25152
25309
|
fail2(new Error("bad upstream status"));
|
|
@@ -25155,7 +25312,7 @@ ${lines}\r
|
|
|
25155
25312
|
const status = Number(statusMatch[1]);
|
|
25156
25313
|
const statusMessage = statusMatch[2]?.trim() ?? "";
|
|
25157
25314
|
const headerMap = {};
|
|
25158
|
-
for (const line of
|
|
25315
|
+
for (const line of head2.split("\r\n").slice(1)) {
|
|
25159
25316
|
const i = line.indexOf(":");
|
|
25160
25317
|
if (i <= 0) continue;
|
|
25161
25318
|
const name = line.slice(0, i).trim().toLowerCase();
|
|
@@ -25175,6 +25332,7 @@ ${lines}\r
|
|
|
25175
25332
|
settled = true;
|
|
25176
25333
|
socket.destroy();
|
|
25177
25334
|
resolve12({
|
|
25335
|
+
ok: true,
|
|
25178
25336
|
status,
|
|
25179
25337
|
statusMessage,
|
|
25180
25338
|
headers: headerMap,
|
|
@@ -25190,16 +25348,16 @@ ${lines}\r
|
|
|
25190
25348
|
fail2(new Error("upstream closed early"));
|
|
25191
25349
|
return;
|
|
25192
25350
|
}
|
|
25193
|
-
const
|
|
25351
|
+
const head2 = buf.subarray(0, sep).toString("latin1");
|
|
25194
25352
|
const statusMatch = /^HTTP\/1\.[01] (\d+)(?: (.*))?/.exec(
|
|
25195
|
-
|
|
25353
|
+
head2.split("\r\n")[0] ?? ""
|
|
25196
25354
|
);
|
|
25197
25355
|
if (!statusMatch) {
|
|
25198
25356
|
fail2(new Error("bad upstream status"));
|
|
25199
25357
|
return;
|
|
25200
25358
|
}
|
|
25201
25359
|
const headerMap = {};
|
|
25202
|
-
for (const line of
|
|
25360
|
+
for (const line of head2.split("\r\n").slice(1)) {
|
|
25203
25361
|
const i = line.indexOf(":");
|
|
25204
25362
|
if (i <= 0) continue;
|
|
25205
25363
|
headerMap[line.slice(0, i).trim().toLowerCase()] = line.slice(i + 1).trim();
|
|
@@ -25214,6 +25372,7 @@ ${lines}\r
|
|
|
25214
25372
|
}
|
|
25215
25373
|
settled = true;
|
|
25216
25374
|
resolve12({
|
|
25375
|
+
ok: true,
|
|
25217
25376
|
status: Number(statusMatch[1]),
|
|
25218
25377
|
statusMessage: statusMatch[2]?.trim() ?? "",
|
|
25219
25378
|
headers: headerMap,
|
|
@@ -25289,6 +25448,8 @@ function authorizeOp(policy, op, url, method, headers) {
|
|
|
25289
25448
|
async function startInterceptProxy(options = {}) {
|
|
25290
25449
|
let policy = options.policy ?? null;
|
|
25291
25450
|
const serveMock = options.serveMock;
|
|
25451
|
+
const resolver = options.resolver ?? defaultResolver2;
|
|
25452
|
+
const dial = options.dial ?? defaultDial;
|
|
25292
25453
|
let activeMocks = 0;
|
|
25293
25454
|
const mockAborts = /* @__PURE__ */ new Set();
|
|
25294
25455
|
let stopped = false;
|
|
@@ -25383,8 +25544,24 @@ async function startInterceptProxy(options = {}) {
|
|
|
25383
25544
|
method,
|
|
25384
25545
|
target,
|
|
25385
25546
|
forwardHeaders,
|
|
25386
|
-
requestBody
|
|
25547
|
+
requestBody,
|
|
25548
|
+
{
|
|
25549
|
+
resolver,
|
|
25550
|
+
dial,
|
|
25551
|
+
localOrigins: effectiveLocalOrigins(active)
|
|
25552
|
+
}
|
|
25387
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
|
+
}
|
|
25388
25565
|
let responseBody = upstream.body;
|
|
25389
25566
|
if (responseOp !== null && active !== null) {
|
|
25390
25567
|
if (responseBody.byteLength > RUNTIME_LIMITS.maxResponseBodyBytes) {
|
|
@@ -25580,25 +25757,52 @@ async function startInterceptProxy(options = {}) {
|
|
|
25580
25757
|
);
|
|
25581
25758
|
}
|
|
25582
25759
|
function handleConnect(req, clientSocket, head) {
|
|
25583
|
-
|
|
25584
|
-
|
|
25585
|
-
|
|
25586
|
-
|
|
25587
|
-
|
|
25588
|
-
|
|
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");
|
|
25589
25785
|
return;
|
|
25590
25786
|
}
|
|
25591
|
-
|
|
25592
|
-
|
|
25787
|
+
let upstreamSocket;
|
|
25788
|
+
try {
|
|
25789
|
+
upstreamSocket = dial(443, pinned.address);
|
|
25790
|
+
} catch {
|
|
25791
|
+
endConnect(clientSocket, 502, "Bad Gateway");
|
|
25792
|
+
return;
|
|
25793
|
+
}
|
|
25794
|
+
upstream = upstreamSocket;
|
|
25795
|
+
upstreamSocket.on("connect", () => {
|
|
25593
25796
|
clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
25594
|
-
if (head.byteLength > 0)
|
|
25595
|
-
pipeSockets(clientSocket,
|
|
25797
|
+
if (head.byteLength > 0) upstreamSocket.write(head);
|
|
25798
|
+
pipeSockets(clientSocket, upstreamSocket);
|
|
25596
25799
|
});
|
|
25597
|
-
|
|
25598
|
-
clientSocket.
|
|
25599
|
-
|
|
25800
|
+
upstreamSocket.on("error", () => {
|
|
25801
|
+
if (!clientSocket.destroyed && !clientSocket.writableEnded) {
|
|
25802
|
+
endConnect(clientSocket, 502, "Bad Gateway");
|
|
25803
|
+
}
|
|
25804
|
+
upstreamSocket.destroy();
|
|
25600
25805
|
});
|
|
25601
|
-
clientSocket.on("error", () => upstream.destroy());
|
|
25602
25806
|
}
|
|
25603
25807
|
const endpoint = await new Promise((resolve12, reject) => {
|
|
25604
25808
|
server.once("error", reject);
|
|
@@ -25649,7 +25853,10 @@ async function startInterception(activation, policyDigest, extensionId, pacRoute
|
|
|
25649
25853
|
const sessionId = `session-${activation.startedAt}-${Math.random().toString(36).slice(2)}`;
|
|
25650
25854
|
let proxy;
|
|
25651
25855
|
try {
|
|
25652
|
-
const started = await registeredProvider.start(
|
|
25856
|
+
const started = await registeredProvider.start({
|
|
25857
|
+
...activation,
|
|
25858
|
+
localOrigins: targetPolicy.localOrigins
|
|
25859
|
+
});
|
|
25653
25860
|
if (started && typeof started === "object" && typeof started.host === "string" && typeof started.port === "number") {
|
|
25654
25861
|
proxy = started;
|
|
25655
25862
|
} else if (activation.proxy) {
|
|
@@ -27386,6 +27593,25 @@ function createNativeHost(options) {
|
|
|
27386
27593
|
...options.clock ? { clock: options.clock } : {}
|
|
27387
27594
|
});
|
|
27388
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
|
+
}
|
|
27389
27615
|
let outboundWrite = null;
|
|
27390
27616
|
let hostRequestCounter = 0;
|
|
27391
27617
|
const pendingHost = /* @__PURE__ */ new Map();
|
|
@@ -27454,18 +27680,13 @@ function createNativeHost(options) {
|
|
|
27454
27680
|
} catch {
|
|
27455
27681
|
}
|
|
27456
27682
|
},
|
|
27457
|
-
async startTlsProxy() {
|
|
27683
|
+
async startTlsProxy(activation) {
|
|
27458
27684
|
if (interceptProxy) {
|
|
27459
27685
|
await interceptProxy.stop();
|
|
27460
27686
|
interceptProxy = null;
|
|
27461
27687
|
}
|
|
27462
|
-
const policy = controller.getActivePolicy();
|
|
27463
27688
|
interceptProxy = await startInterceptProxy({
|
|
27464
|
-
policy:
|
|
27465
|
-
project: policy.project,
|
|
27466
|
-
operations: policy.operations,
|
|
27467
|
-
presetDigest: policy.presetDigest
|
|
27468
|
-
} : null,
|
|
27689
|
+
policy: policyForProxy(activation),
|
|
27469
27690
|
serveMock: (request) => controller.serveMock(request.token, {
|
|
27470
27691
|
method: request.method,
|
|
27471
27692
|
signal: request.signal
|
|
@@ -27488,14 +27709,7 @@ function createNativeHost(options) {
|
|
|
27488
27709
|
async start(activation) {
|
|
27489
27710
|
const routes = pendingRoutes.length > 0 ? pendingRoutes : activation.pacRoutes;
|
|
27490
27711
|
const endpoint = await platformProvider.start(activation, routes);
|
|
27491
|
-
|
|
27492
|
-
interceptProxy?.setPolicy(
|
|
27493
|
-
policy ? {
|
|
27494
|
-
project: policy.project,
|
|
27495
|
-
operations: policy.operations,
|
|
27496
|
-
presetDigest: policy.presetDigest
|
|
27497
|
-
} : null
|
|
27498
|
-
);
|
|
27712
|
+
interceptProxy?.setPolicy(policyForProxy(activation));
|
|
27499
27713
|
return endpoint;
|
|
27500
27714
|
},
|
|
27501
27715
|
async stop() {
|
package/package.json
CHANGED