@rogatio/cli 5.20.1 → 6.0.1
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/editor/index.js +114 -152
- package/dist/node/index.js +551 -282
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -186,7 +186,6 @@ var LIMITS = Object.freeze({
|
|
|
186
186
|
maxGroups: 64,
|
|
187
187
|
maxRulesPerGroup: 256,
|
|
188
188
|
maxRulesPerProject: 4096,
|
|
189
|
-
maxOriginsPerScope: 32,
|
|
190
189
|
maxIdLength: 64,
|
|
191
190
|
maxLabelLength: 100,
|
|
192
191
|
maxDescriptionLength: 1e3,
|
|
@@ -242,16 +241,7 @@ function normalizeSiteOrigin(value) {
|
|
|
242
241
|
function isSiteOrigin(value) {
|
|
243
242
|
return typeof value === "string" && normalizeSiteOrigin(value) !== null;
|
|
244
243
|
}
|
|
245
|
-
|
|
246
|
-
if (typeof value !== "string" || value.length === 0 || value.length > LIMITS.maxUrlRegexLength)
|
|
247
|
-
return null;
|
|
248
|
-
try {
|
|
249
|
-
return new RegExp(value);
|
|
250
|
-
} catch {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
var PROJECT_VERSION = 1;
|
|
244
|
+
var PROJECT_VERSION = 2;
|
|
255
245
|
var RESOURCE_TYPES = Object.freeze([
|
|
256
246
|
"main_frame",
|
|
257
247
|
"sub_frame",
|
|
@@ -280,6 +270,296 @@ var HTTP_METHODS = Object.freeze([
|
|
|
280
270
|
"CONNECT",
|
|
281
271
|
"TRACE"
|
|
282
272
|
]);
|
|
273
|
+
function isPlainObject(value) {
|
|
274
|
+
const proto = Object.getPrototypeOf(value);
|
|
275
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && (proto === Object.prototype || proto === null);
|
|
276
|
+
}
|
|
277
|
+
function ownString(value) {
|
|
278
|
+
return typeof value === "string" ? value : null;
|
|
279
|
+
}
|
|
280
|
+
function ownStringArray(value) {
|
|
281
|
+
if (!Array.isArray(value)) return null;
|
|
282
|
+
const out = [];
|
|
283
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
284
|
+
if (!Object.hasOwn(value, i)) return null;
|
|
285
|
+
const entry = value[i];
|
|
286
|
+
if (typeof entry !== "string") return null;
|
|
287
|
+
out.push(entry);
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
var INVALID_INPUT = "migration.invalid-input";
|
|
292
|
+
var MigrationError = class extends Error {
|
|
293
|
+
constructor() {
|
|
294
|
+
super(INVALID_INPUT);
|
|
295
|
+
this.name = "MigrationError";
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
function regexProvenInsideOrigins(urlRegex, effectiveOrigins) {
|
|
299
|
+
if (effectiveOrigins.size === 0) return false;
|
|
300
|
+
if (urlRegex.includes("|")) return false;
|
|
301
|
+
if (!urlRegex.startsWith("^")) return false;
|
|
302
|
+
let index = 1;
|
|
303
|
+
let schemes;
|
|
304
|
+
if (urlRegex.startsWith("https?://", index)) {
|
|
305
|
+
schemes = ["http", "https"];
|
|
306
|
+
index += "https?://".length;
|
|
307
|
+
} else if (urlRegex.startsWith("https://", index)) {
|
|
308
|
+
schemes = ["https"];
|
|
309
|
+
index += "https://".length;
|
|
310
|
+
} else if (urlRegex.startsWith("http://", index)) {
|
|
311
|
+
schemes = ["http"];
|
|
312
|
+
index += "http://".length;
|
|
313
|
+
} else {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
let host = "";
|
|
317
|
+
while (index < urlRegex.length) {
|
|
318
|
+
const char = urlRegex[index];
|
|
319
|
+
if (char === "\\") {
|
|
320
|
+
if (urlRegex[index + 1] !== ".") return false;
|
|
321
|
+
host += ".";
|
|
322
|
+
index += 2;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (char === "/" || char === ":" || char === "$") break;
|
|
326
|
+
if (!/^[A-Za-z0-9-]$/.test(char ?? "")) return false;
|
|
327
|
+
host += char;
|
|
328
|
+
index += 1;
|
|
329
|
+
}
|
|
330
|
+
if (!isLiteralHost(host)) return false;
|
|
331
|
+
let port = null;
|
|
332
|
+
if (urlRegex[index] === "/") {
|
|
333
|
+
} else if (urlRegex[index] === "$") {
|
|
334
|
+
} else if (urlRegex[index] === ":") {
|
|
335
|
+
index += 1;
|
|
336
|
+
const start = index;
|
|
337
|
+
while (index < urlRegex.length && (urlRegex[index] ?? "") >= "0" && (urlRegex[index] ?? "") <= "9") {
|
|
338
|
+
index += 1;
|
|
339
|
+
}
|
|
340
|
+
if (index === start) return false;
|
|
341
|
+
port = urlRegex.slice(start, index);
|
|
342
|
+
const portNumber = Number(port);
|
|
343
|
+
if (!Number.isInteger(portNumber) || portNumber < 1 || portNumber > 65535) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
if (urlRegex[index] !== "/" && urlRegex[index] !== "$") return false;
|
|
347
|
+
} else {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
for (const scheme of schemes) {
|
|
351
|
+
const origin = normalizeSiteOrigin(
|
|
352
|
+
port === null ? `${scheme}://${host}` : `${scheme}://${host}:${port}`
|
|
353
|
+
);
|
|
354
|
+
if (origin === null || !effectiveOrigins.has(origin)) return false;
|
|
355
|
+
}
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
function isLiteralHost(host) {
|
|
359
|
+
if (host.length === 0 || host.startsWith(".") || host.endsWith(".") || host.includes("..")) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
if (/^(?:\d{1,3}\.){3}\d{1,3}$/.test(host)) {
|
|
363
|
+
const parts = host.split(".");
|
|
364
|
+
for (const part of parts) {
|
|
365
|
+
const value = Number(part);
|
|
366
|
+
if (!Number.isInteger(value) || value < 0 || value > 255) return false;
|
|
367
|
+
}
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
return /^(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]+$/.test(host);
|
|
371
|
+
}
|
|
372
|
+
function effectiveOriginSet(groupOrigins, ruleOrigins) {
|
|
373
|
+
const set = /* @__PURE__ */ new Set();
|
|
374
|
+
for (const raw of [...groupOrigins, ...ruleOrigins]) {
|
|
375
|
+
const normalized = normalizeSiteOrigin(raw);
|
|
376
|
+
if (normalized !== null) set.add(normalized);
|
|
377
|
+
}
|
|
378
|
+
return set;
|
|
379
|
+
}
|
|
380
|
+
function migrateRule(raw, groupOrigins, groupIndex, ruleIndex, notices) {
|
|
381
|
+
if (!isPlainObject(raw)) return null;
|
|
382
|
+
const id2 = ownString(raw.id);
|
|
383
|
+
const name = ownString(raw.name);
|
|
384
|
+
const urlRegex = ownString(raw.urlRegex);
|
|
385
|
+
if (id2 === null || name === null || urlRegex === null) return null;
|
|
386
|
+
const ruleOrigins = ownStringArray(raw.origins);
|
|
387
|
+
if (ruleOrigins === null) return null;
|
|
388
|
+
const resourceTypes = raw.resourceTypes;
|
|
389
|
+
if (!Array.isArray(resourceTypes) || resourceTypes.length === 0) return null;
|
|
390
|
+
const priority = raw.priority;
|
|
391
|
+
if (typeof priority !== "number" || !Number.isInteger(priority)) return null;
|
|
392
|
+
const source = {
|
|
393
|
+
key: "url",
|
|
394
|
+
operator: "regex",
|
|
395
|
+
value: urlRegex
|
|
396
|
+
};
|
|
397
|
+
const effective = effectiveOriginSet(groupOrigins, ruleOrigins);
|
|
398
|
+
const path2 = `/groups/${groupIndex}/rules/${ruleIndex}`;
|
|
399
|
+
if (!regexProvenInsideOrigins(urlRegex, effective)) {
|
|
400
|
+
notices.push({
|
|
401
|
+
code: "migration.possible-scope-widen",
|
|
402
|
+
path: path2,
|
|
403
|
+
message: "dropping origins may widen this rule; source keeps the previous urlRegex bytes"
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const rule = {
|
|
407
|
+
id: id2,
|
|
408
|
+
name,
|
|
409
|
+
source,
|
|
410
|
+
resourceTypes,
|
|
411
|
+
priority
|
|
412
|
+
};
|
|
413
|
+
for (const key of [
|
|
414
|
+
"method",
|
|
415
|
+
"type",
|
|
416
|
+
"redirect",
|
|
417
|
+
"action",
|
|
418
|
+
"headerDirection",
|
|
419
|
+
"headerOperation",
|
|
420
|
+
"headerName",
|
|
421
|
+
"headerValue",
|
|
422
|
+
"responseBody",
|
|
423
|
+
"requestBody",
|
|
424
|
+
"redactSensitiveInLogs"
|
|
425
|
+
]) {
|
|
426
|
+
if (Object.hasOwn(raw, key)) {
|
|
427
|
+
const value = raw[key];
|
|
428
|
+
rule[key] = value === void 0 ? void 0 : structuredClone(value);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return rule;
|
|
432
|
+
}
|
|
433
|
+
function assertAcyclic(value, seen) {
|
|
434
|
+
if (value === null || typeof value !== "object") return;
|
|
435
|
+
if (seen.has(value)) throw new MigrationError();
|
|
436
|
+
seen.add(value);
|
|
437
|
+
if (Object.getOwnPropertySymbols(value).length > 0)
|
|
438
|
+
throw new MigrationError();
|
|
439
|
+
if (Array.isArray(value)) {
|
|
440
|
+
const lengthDesc = Object.getOwnPropertyDescriptor(value, "length");
|
|
441
|
+
if (lengthDesc === void 0 || !("value" in lengthDesc) || typeof lengthDesc.value !== "number" || !Number.isSafeInteger(lengthDesc.value) || lengthDesc.value < 0) {
|
|
442
|
+
throw new MigrationError();
|
|
443
|
+
}
|
|
444
|
+
const length = lengthDesc.value;
|
|
445
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
446
|
+
if (key === "length") continue;
|
|
447
|
+
if (!/^(?:0|[1-9]\d*)$/.test(key) || Number(key) >= length) {
|
|
448
|
+
throw new MigrationError();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (let index = 0; index < length; index += 1) {
|
|
452
|
+
const desc = Object.getOwnPropertyDescriptor(value, String(index));
|
|
453
|
+
if (desc === void 0 || !("value" in desc) || desc.get || desc.set) {
|
|
454
|
+
throw new MigrationError();
|
|
455
|
+
}
|
|
456
|
+
assertAcyclic(desc.value, seen);
|
|
457
|
+
}
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const prototype = Object.getPrototypeOf(value);
|
|
461
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
462
|
+
throw new MigrationError();
|
|
463
|
+
}
|
|
464
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
465
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
466
|
+
throw new MigrationError();
|
|
467
|
+
}
|
|
468
|
+
const desc = Object.getOwnPropertyDescriptor(value, key);
|
|
469
|
+
if (desc === void 0 || !("value" in desc) || desc.get || desc.set || !desc.enumerable) {
|
|
470
|
+
throw new MigrationError();
|
|
471
|
+
}
|
|
472
|
+
assertAcyclic(desc.value, seen);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function migrateV1Project(input) {
|
|
476
|
+
try {
|
|
477
|
+
assertAcyclic(input, /* @__PURE__ */ new WeakSet());
|
|
478
|
+
return migrateOwned(input);
|
|
479
|
+
} catch {
|
|
480
|
+
return { ok: false, error: INVALID_INPUT };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function migrateOwned(input) {
|
|
484
|
+
if (!isPlainObject(input)) {
|
|
485
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
486
|
+
}
|
|
487
|
+
if (Object.getOwnPropertySymbols(input).length > 0) {
|
|
488
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
489
|
+
}
|
|
490
|
+
const version = input.version;
|
|
491
|
+
if (version !== 1) {
|
|
492
|
+
return { ok: false, error: "migration.not-v1" };
|
|
493
|
+
}
|
|
494
|
+
const name = ownString(input.name);
|
|
495
|
+
if (name === null) {
|
|
496
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
497
|
+
}
|
|
498
|
+
const groupsRaw = input.groups;
|
|
499
|
+
if (!Array.isArray(groupsRaw)) {
|
|
500
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
501
|
+
}
|
|
502
|
+
const notices = [];
|
|
503
|
+
const groups = [];
|
|
504
|
+
for (let gi = 0; gi < groupsRaw.length; gi += 1) {
|
|
505
|
+
if (!Object.hasOwn(groupsRaw, gi)) {
|
|
506
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
507
|
+
}
|
|
508
|
+
const groupRaw = groupsRaw[gi];
|
|
509
|
+
if (!isPlainObject(groupRaw)) {
|
|
510
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
511
|
+
}
|
|
512
|
+
const groupId = ownString(groupRaw.id);
|
|
513
|
+
const groupName = ownString(groupRaw.name);
|
|
514
|
+
const groupOrigins = ownStringArray(groupRaw.origins);
|
|
515
|
+
const rulesRaw = groupRaw.rules;
|
|
516
|
+
if (groupId === null || groupName === null || groupOrigins === null || !Array.isArray(rulesRaw)) {
|
|
517
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
518
|
+
}
|
|
519
|
+
const rules = [];
|
|
520
|
+
for (let ri = 0; ri < rulesRaw.length; ri += 1) {
|
|
521
|
+
if (!Object.hasOwn(rulesRaw, ri)) {
|
|
522
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
523
|
+
}
|
|
524
|
+
const migrated = migrateRule(rulesRaw[ri], groupOrigins, gi, ri, notices);
|
|
525
|
+
if (migrated === null) {
|
|
526
|
+
return { ok: false, error: "migration.invalid-rule" };
|
|
527
|
+
}
|
|
528
|
+
rules.push(migrated);
|
|
529
|
+
}
|
|
530
|
+
groups.push({ id: groupId, name: groupName, rules });
|
|
531
|
+
}
|
|
532
|
+
notices.push({
|
|
533
|
+
code: "migration.initiator-policy",
|
|
534
|
+
path: "/",
|
|
535
|
+
message: "initiator must now be a present http(s) origin; the old origins allowlist no longer applies"
|
|
536
|
+
});
|
|
537
|
+
const project = {
|
|
538
|
+
version: PROJECT_VERSION,
|
|
539
|
+
name,
|
|
540
|
+
groups
|
|
541
|
+
};
|
|
542
|
+
if (Object.hasOwn(input, "description")) {
|
|
543
|
+
const description = ownString(input.description);
|
|
544
|
+
if (description === null) {
|
|
545
|
+
return { ok: false, error: "migration.invalid-project" };
|
|
546
|
+
}
|
|
547
|
+
project.description = description;
|
|
548
|
+
}
|
|
549
|
+
if (Object.hasOwn(input, "requestBodyPolicy")) {
|
|
550
|
+
project.requestBodyPolicy = input.requestBodyPolicy;
|
|
551
|
+
}
|
|
552
|
+
return { ok: true, project, notices };
|
|
553
|
+
}
|
|
554
|
+
function compileUrlRegex(value) {
|
|
555
|
+
if (typeof value !== "string" || value.length === 0 || value.length > LIMITS.maxUrlRegexLength)
|
|
556
|
+
return null;
|
|
557
|
+
try {
|
|
558
|
+
return new RegExp(value);
|
|
559
|
+
} catch {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
283
563
|
var label = {
|
|
284
564
|
type: "string",
|
|
285
565
|
minLength: 1,
|
|
@@ -294,7 +574,7 @@ var id = {
|
|
|
294
574
|
};
|
|
295
575
|
var projectSchemaDefinition = {
|
|
296
576
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
297
|
-
$id: "https://rogatio.dev/schema/project-
|
|
577
|
+
$id: "https://rogatio.dev/schema/project-v2.json",
|
|
298
578
|
type: "object",
|
|
299
579
|
additionalProperties: false,
|
|
300
580
|
required: ["version", "name", "groups"],
|
|
@@ -313,19 +593,28 @@ var projectSchemaDefinition = {
|
|
|
313
593
|
requestBodyPolicy: { $ref: "#/$defs/requestBodyPolicyConfig" }
|
|
314
594
|
},
|
|
315
595
|
$defs: {
|
|
596
|
+
sourceCondition: {
|
|
597
|
+
type: "object",
|
|
598
|
+
additionalProperties: false,
|
|
599
|
+
required: ["key", "operator", "value"],
|
|
600
|
+
properties: {
|
|
601
|
+
key: { type: "string", enum: ["url", "host"] },
|
|
602
|
+
operator: { type: "string", const: "regex" },
|
|
603
|
+
value: {
|
|
604
|
+
type: "string",
|
|
605
|
+
minLength: 1,
|
|
606
|
+
maxLength: LIMITS.maxUrlRegexLength,
|
|
607
|
+
format: "rogatio-url-regex"
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
},
|
|
316
611
|
group: {
|
|
317
612
|
type: "object",
|
|
318
613
|
additionalProperties: false,
|
|
319
|
-
required: ["id", "name", "
|
|
614
|
+
required: ["id", "name", "rules"],
|
|
320
615
|
properties: {
|
|
321
616
|
id,
|
|
322
617
|
name: label,
|
|
323
|
-
origins: {
|
|
324
|
-
type: "array",
|
|
325
|
-
maxItems: LIMITS.maxOriginsPerScope,
|
|
326
|
-
uniqueItems: true,
|
|
327
|
-
items: { type: "string", format: "rogatio-origin" }
|
|
328
|
-
},
|
|
329
618
|
rules: {
|
|
330
619
|
type: "array",
|
|
331
620
|
maxItems: LIMITS.maxRulesPerGroup,
|
|
@@ -336,29 +625,11 @@ var projectSchemaDefinition = {
|
|
|
336
625
|
rule: {
|
|
337
626
|
type: "object",
|
|
338
627
|
additionalProperties: false,
|
|
339
|
-
required: [
|
|
340
|
-
"id",
|
|
341
|
-
"name",
|
|
342
|
-
"urlRegex",
|
|
343
|
-
"origins",
|
|
344
|
-
"resourceTypes",
|
|
345
|
-
"priority"
|
|
346
|
-
],
|
|
628
|
+
required: ["id", "name", "source", "resourceTypes", "priority"],
|
|
347
629
|
properties: {
|
|
348
630
|
id,
|
|
349
631
|
name: label,
|
|
350
|
-
|
|
351
|
-
type: "string",
|
|
352
|
-
minLength: 1,
|
|
353
|
-
maxLength: LIMITS.maxUrlRegexLength,
|
|
354
|
-
format: "rogatio-url-regex"
|
|
355
|
-
},
|
|
356
|
-
origins: {
|
|
357
|
-
type: "array",
|
|
358
|
-
maxItems: LIMITS.maxOriginsPerScope,
|
|
359
|
-
uniqueItems: true,
|
|
360
|
-
items: { type: "string", format: "rogatio-origin" }
|
|
361
|
-
},
|
|
632
|
+
source: { $ref: "#/$defs/sourceCondition" },
|
|
362
633
|
resourceTypes: {
|
|
363
634
|
type: "array",
|
|
364
635
|
minItems: 1,
|
|
@@ -855,7 +1126,7 @@ function semanticIssues(project) {
|
|
|
855
1126
|
const destinationPath = `${rulePath}/redirect/destination`;
|
|
856
1127
|
for (const issue of validateRedirectDestination(
|
|
857
1128
|
destination,
|
|
858
|
-
rule.
|
|
1129
|
+
rule.source.value
|
|
859
1130
|
)) {
|
|
860
1131
|
issues.push({
|
|
861
1132
|
instancePath: destinationPath,
|
|
@@ -867,29 +1138,12 @@ function semanticIssues(project) {
|
|
|
867
1138
|
issues.push(
|
|
868
1139
|
...captureValidationIssues(
|
|
869
1140
|
destination,
|
|
870
|
-
rule.
|
|
1141
|
+
rule.source.value,
|
|
871
1142
|
destinationPath
|
|
872
1143
|
)
|
|
873
1144
|
);
|
|
874
1145
|
}
|
|
875
1146
|
}
|
|
876
|
-
const effectiveOrigins = /* @__PURE__ */ new Set();
|
|
877
|
-
for (let originIndex = 0; originIndex < group.origins.length; originIndex += 1) {
|
|
878
|
-
const origin = normalizeSiteOrigin(group.origins[originIndex]);
|
|
879
|
-
if (origin !== null) effectiveOrigins.add(origin);
|
|
880
|
-
}
|
|
881
|
-
for (let originIndex = 0; originIndex < rule.origins.length; originIndex += 1) {
|
|
882
|
-
const origin = normalizeSiteOrigin(rule.origins[originIndex]);
|
|
883
|
-
if (origin !== null) effectiveOrigins.add(origin);
|
|
884
|
-
}
|
|
885
|
-
if (effectiveOrigins.size === 0) {
|
|
886
|
-
issues.push({
|
|
887
|
-
instancePath: `${rulePath}/origins`,
|
|
888
|
-
keyword: "effectiveOrigin",
|
|
889
|
-
message: "must combine with group origins to contain at least one origin",
|
|
890
|
-
params: {}
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
1147
|
const action = rule.action;
|
|
894
1148
|
if (action && "type" in action && action.type === "query") {
|
|
895
1149
|
const seenNames = /* @__PURE__ */ new Set();
|
|
@@ -920,7 +1174,7 @@ function semanticIssues(project) {
|
|
|
920
1174
|
issues.push(
|
|
921
1175
|
...captureValidationIssues(
|
|
922
1176
|
param.value,
|
|
923
|
-
rule.
|
|
1177
|
+
rule.source.value,
|
|
924
1178
|
`${rulePath}/action/params/${p}/value`
|
|
925
1179
|
)
|
|
926
1180
|
);
|
|
@@ -952,7 +1206,7 @@ function semanticIssues(project) {
|
|
|
952
1206
|
issues.push(
|
|
953
1207
|
...captureValidationIssues(
|
|
954
1208
|
rule.headerValue,
|
|
955
|
-
rule.
|
|
1209
|
+
rule.source.value,
|
|
956
1210
|
`${rulePath}/headerValue`
|
|
957
1211
|
)
|
|
958
1212
|
);
|
|
@@ -994,7 +1248,7 @@ function semanticIssues(project) {
|
|
|
994
1248
|
issues.push(
|
|
995
1249
|
...captureValidationIssues(
|
|
996
1250
|
action2.body,
|
|
997
|
-
rule.
|
|
1251
|
+
rule.source.value,
|
|
998
1252
|
`${actionPath}/body`
|
|
999
1253
|
)
|
|
1000
1254
|
);
|
|
@@ -1075,7 +1329,7 @@ function semanticIssues(project) {
|
|
|
1075
1329
|
issues.push(
|
|
1076
1330
|
...captureValidationIssues(
|
|
1077
1331
|
action2.body,
|
|
1078
|
-
rule.
|
|
1332
|
+
rule.source.value,
|
|
1079
1333
|
`${actionPath}/body`
|
|
1080
1334
|
)
|
|
1081
1335
|
);
|
|
@@ -1348,18 +1602,6 @@ function validateProjectDetailed(value) {
|
|
|
1348
1602
|
return errors.length > 0 ? { valid: false, errors } : { valid: true, data: value };
|
|
1349
1603
|
}
|
|
1350
1604
|
|
|
1351
|
-
// packages/runtime/dist/node/index.js
|
|
1352
|
-
import { isIP as isIP2 } from "node:net";
|
|
1353
|
-
import { Buffer as Buffer2 } from "node:buffer";
|
|
1354
|
-
import { existsSync } from "node:fs";
|
|
1355
|
-
import { createServer as createServer2 } from "node:http";
|
|
1356
|
-
import { join as join3 } from "node:path";
|
|
1357
|
-
import {
|
|
1358
|
-
createServer
|
|
1359
|
-
} from "node:http";
|
|
1360
|
-
import { connect } from "node:net";
|
|
1361
|
-
import { Worker } from "node:worker_threads";
|
|
1362
|
-
|
|
1363
1605
|
// packages/compiler/dist/node/index.js
|
|
1364
1606
|
var ISSUE_CODES = {
|
|
1365
1607
|
required: "schema.required",
|
|
@@ -1378,7 +1620,6 @@ var ISSUE_CODES = {
|
|
|
1378
1620
|
uniqueItems: "schema.out-of-range",
|
|
1379
1621
|
ownProperties: "schema.invalid-structure",
|
|
1380
1622
|
uniqueId: "schema.duplicate-id",
|
|
1381
|
-
effectiveOrigin: "schema.no-effective-origin",
|
|
1382
1623
|
maxRulesPerProject: "schema.rule-limit"
|
|
1383
1624
|
};
|
|
1384
1625
|
var SAFE_PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -1406,7 +1647,6 @@ var MESSAGES = {
|
|
|
1406
1647
|
"schema.out-of-range": "The project contains a value outside its allowed bounds.",
|
|
1407
1648
|
"schema.invalid-structure": "The project contains invalid structure.",
|
|
1408
1649
|
"schema.duplicate-id": "Project and rule IDs must be unique.",
|
|
1409
|
-
"schema.no-effective-origin": "Each rule must have at least one effective origin.",
|
|
1410
1650
|
"schema.rule-limit": "The project contains too many rules.",
|
|
1411
1651
|
"compiler.invariant": "The compiler could not normalize validated project data.",
|
|
1412
1652
|
"compiler.forbidden-header": "The header name is forbidden for this direction.",
|
|
@@ -1472,11 +1712,6 @@ function invariantDiagnostic(path2 = "") {
|
|
|
1472
1712
|
params: {}
|
|
1473
1713
|
};
|
|
1474
1714
|
}
|
|
1475
|
-
function compareCodeUnits2(left, right) {
|
|
1476
|
-
if (left < right) return -1;
|
|
1477
|
-
if (left > right) return 1;
|
|
1478
|
-
return 0;
|
|
1479
|
-
}
|
|
1480
1715
|
function snapshotOwnData2(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
1481
1716
|
if (value === null || typeof value !== "object") {
|
|
1482
1717
|
return { valid: true, value };
|
|
@@ -1537,33 +1772,6 @@ function snapshotOwnData2(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
1537
1772
|
ancestors.delete(value);
|
|
1538
1773
|
}
|
|
1539
1774
|
}
|
|
1540
|
-
var CompilerInvariantError = class extends Error {
|
|
1541
|
-
constructor(path2) {
|
|
1542
|
-
super("invalid normalized origin");
|
|
1543
|
-
this.path = path2;
|
|
1544
|
-
this.name = "CompilerInvariantError";
|
|
1545
|
-
}
|
|
1546
|
-
path;
|
|
1547
|
-
};
|
|
1548
|
-
function normalizeOrigins(group, rule, groupIndex, ruleIndex) {
|
|
1549
|
-
const origins = /* @__PURE__ */ new Set();
|
|
1550
|
-
const sourceArrays = [group.origins, rule.origins];
|
|
1551
|
-
for (let sourceIndex = 0; sourceIndex < sourceArrays.length; sourceIndex += 1) {
|
|
1552
|
-
const source = sourceArrays[sourceIndex];
|
|
1553
|
-
for (let originIndex = 0; originIndex < source.length; originIndex += 1) {
|
|
1554
|
-
const normalized = normalizeSiteOrigin(source[originIndex]);
|
|
1555
|
-
if (normalized === null) {
|
|
1556
|
-
const path2 = sourceIndex === 0 ? `/groups/${groupIndex}/origins/${originIndex}` : `/groups/${groupIndex}/rules/${ruleIndex}/origins/${originIndex}`;
|
|
1557
|
-
throw new CompilerInvariantError(path2);
|
|
1558
|
-
}
|
|
1559
|
-
origins.add(normalized);
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
const normalizedOrigins = [];
|
|
1563
|
-
for (const origin of origins) normalizedOrigins.push(origin);
|
|
1564
|
-
normalizedOrigins.sort(compareCodeUnits2);
|
|
1565
|
-
return normalizedOrigins;
|
|
1566
|
-
}
|
|
1567
1775
|
function canonicalResourceTypes(resourceTypes) {
|
|
1568
1776
|
const ordered = [];
|
|
1569
1777
|
for (let canonicalIndex = 0; canonicalIndex < RESOURCE_TYPES.length; canonicalIndex += 1) {
|
|
@@ -1577,10 +1785,13 @@ function canonicalResourceTypes(resourceTypes) {
|
|
|
1577
1785
|
function resolveRedactSensitiveInLogs(rule) {
|
|
1578
1786
|
return rule.redactSensitiveInLogs === true;
|
|
1579
1787
|
}
|
|
1580
|
-
function compileMatcher(
|
|
1788
|
+
function compileMatcher(rule) {
|
|
1581
1789
|
const matcher = {
|
|
1582
|
-
|
|
1583
|
-
|
|
1790
|
+
source: {
|
|
1791
|
+
key: rule.source.key,
|
|
1792
|
+
operator: "regex",
|
|
1793
|
+
value: rule.source.value
|
|
1794
|
+
},
|
|
1584
1795
|
resourceTypes: canonicalResourceTypes(rule.resourceTypes),
|
|
1585
1796
|
priority: rule.priority
|
|
1586
1797
|
};
|
|
@@ -1593,7 +1804,7 @@ function compileOperations(project) {
|
|
|
1593
1804
|
const group = project.groups[groupIndex];
|
|
1594
1805
|
for (let ruleIndex = 0; ruleIndex < group.rules.length; ruleIndex += 1) {
|
|
1595
1806
|
const rule = group.rules[ruleIndex];
|
|
1596
|
-
const matcher = compileMatcher(
|
|
1807
|
+
const matcher = compileMatcher(rule);
|
|
1597
1808
|
const redactSensitiveInLogs = resolveRedactSensitiveInLogs(rule);
|
|
1598
1809
|
if (rule.type === "redirect") {
|
|
1599
1810
|
const operation = {
|
|
@@ -1706,17 +1917,46 @@ function compileProject(value) {
|
|
|
1706
1917
|
operations: compileOperations(validation.data),
|
|
1707
1918
|
diagnostics: []
|
|
1708
1919
|
};
|
|
1709
|
-
} catch
|
|
1710
|
-
const path2 = error instanceof CompilerInvariantError ? error.path : "";
|
|
1920
|
+
} catch {
|
|
1711
1921
|
return {
|
|
1712
1922
|
ok: false,
|
|
1713
1923
|
operations: [],
|
|
1714
|
-
diagnostics: [invariantDiagnostic(
|
|
1924
|
+
diagnostics: [invariantDiagnostic()]
|
|
1715
1925
|
};
|
|
1716
1926
|
}
|
|
1717
1927
|
}
|
|
1928
|
+
function sourceMatches(source, url) {
|
|
1929
|
+
const regex = compileUrlRegex(source.value);
|
|
1930
|
+
if (regex === null) return false;
|
|
1931
|
+
if (source.key === "url") {
|
|
1932
|
+
return regex.test(url);
|
|
1933
|
+
}
|
|
1934
|
+
try {
|
|
1935
|
+
const hostname = new URL(url).hostname;
|
|
1936
|
+
return regex.test(hostname);
|
|
1937
|
+
} catch {
|
|
1938
|
+
return false;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
function sameOrigin(a, b) {
|
|
1942
|
+
try {
|
|
1943
|
+
return new URL(a).origin === new URL(b).origin;
|
|
1944
|
+
} catch {
|
|
1945
|
+
return false;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1718
1948
|
|
|
1719
1949
|
// packages/runtime/dist/node/index.js
|
|
1950
|
+
import { isIP as isIP2 } from "node:net";
|
|
1951
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
1952
|
+
import { existsSync } from "node:fs";
|
|
1953
|
+
import { createServer as createServer2 } from "node:http";
|
|
1954
|
+
import { join as join3 } from "node:path";
|
|
1955
|
+
import {
|
|
1956
|
+
createServer
|
|
1957
|
+
} from "node:http";
|
|
1958
|
+
import { connect } from "node:net";
|
|
1959
|
+
import { Worker } from "node:worker_threads";
|
|
1720
1960
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
1721
1961
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1722
1962
|
import { readFile as readFile2, realpath as realpath2, stat } from "node:fs/promises";
|
|
@@ -20687,19 +20927,15 @@ function buildSystemPrompt(project) {
|
|
|
20687
20927
|
const projectJson = formatProjectContext(project);
|
|
20688
20928
|
return `You are an expert Rogatio rule author. Rogatio is a local-first browser request/response rule engine.
|
|
20689
20929
|
|
|
20690
|
-
## Schema
|
|
20930
|
+
## Schema Constraints (version 2)
|
|
20691
20931
|
|
|
20692
|
-
###
|
|
20693
|
-
|
|
20694
|
-
-
|
|
20695
|
-
-
|
|
20696
|
-
-
|
|
20932
|
+
### Source condition
|
|
20933
|
+
Each rule matches with \`source: { key, operator, value }\`.
|
|
20934
|
+
- \`key\`: \`"url"\` tests the full request URL; \`"host"\` tests the hostname only.
|
|
20935
|
+
- \`operator\`: must be the constant \`"regex"\`.
|
|
20936
|
+
- \`value\`: non-empty ECMAScript regex, max 2048 characters, case-sensitive, no flags.
|
|
20697
20937
|
|
|
20698
|
-
|
|
20699
|
-
- Only explicit http:// and https:// origins with hostname
|
|
20700
|
-
- Optional port (e.g., https://example.com:8080)
|
|
20701
|
-
- No wildcards, credentials, paths, query strings, or fragments
|
|
20702
|
-
- Effective origins are union of project, group, and rule origins
|
|
20938
|
+
There are no group or rule \`origins\` fields and no \`urlRegex\` field.
|
|
20703
20939
|
|
|
20704
20940
|
### Forbidden Headers
|
|
20705
20941
|
Cannot set/remove these headers:
|
|
@@ -20715,14 +20951,13 @@ ${formatHttpMethods()}
|
|
|
20715
20951
|
- Max 64 projects per profile
|
|
20716
20952
|
- Max 100 groups per project
|
|
20717
20953
|
- Max 500 rules per project
|
|
20718
|
-
- Max 10 origins per group/rule
|
|
20719
20954
|
|
|
20720
20955
|
## Rule Types
|
|
20721
20956
|
|
|
20722
|
-
Each rule has: id, name,
|
|
20957
|
+
Each rule has: id, name, source, resourceTypes[], priority, method?, action
|
|
20723
20958
|
|
|
20724
20959
|
### redirect
|
|
20725
|
-
Action: destination string. Use $1 through $9 for URL
|
|
20960
|
+
Action: destination string. Use $1 through $9 for URL source captures when \`source.key\` is \`"url"\`. $$ is a literal dollar sign. Existing redirect \\\\1 references remain compatible.
|
|
20726
20961
|
|
|
20727
20962
|
### query
|
|
20728
20963
|
Action: query parameters with name, operation, and value. Set values may use URL captures.
|
|
@@ -20731,15 +20966,14 @@ Action: query parameters with name, operation, and value. Set values may use URL
|
|
|
20731
20966
|
Action: header direction, operation, name, and value. Set and append values may use URL captures.
|
|
20732
20967
|
|
|
20733
20968
|
### response-body
|
|
20734
|
-
Replace-mode body may use URL captures. Regex-mode replacement strings use captures from the body pattern, not the
|
|
20969
|
+
Replace-mode body may use URL captures. Regex-mode replacement strings use captures from the body pattern, not the source pattern.
|
|
20735
20970
|
|
|
20736
20971
|
### request-body
|
|
20737
|
-
Replace-mode body may use URL captures. Regex-mode replacement strings use captures from the body pattern, not the
|
|
20972
|
+
Replace-mode body may use URL captures. Regex-mode replacement strings use captures from the body pattern, not the source pattern.
|
|
20738
20973
|
|
|
20739
20974
|
## Compiler Diagnostic Codes (stable)
|
|
20740
20975
|
- schema.invalid-regex - Invalid regex syntax
|
|
20741
20976
|
- schema.duplicate-id - Duplicate group/rule ID
|
|
20742
|
-
- schema.invalid-origin - Invalid origin format
|
|
20743
20977
|
- schema.invalid-resource-type - Unknown resource type
|
|
20744
20978
|
- schema.invalid-method - Unknown HTTP method
|
|
20745
20979
|
- schema.regex-too-long - Regex exceeds 2048 chars
|
|
@@ -20753,12 +20987,17 @@ ${projectJson}
|
|
|
20753
20987
|
Return ONLY valid JSON matching this TypeScript interface:
|
|
20754
20988
|
|
|
20755
20989
|
\`\`\`typescript
|
|
20990
|
+
interface SourceProposal {
|
|
20991
|
+
key: "url" | "host";
|
|
20992
|
+
operator: "regex";
|
|
20993
|
+
value: string;
|
|
20994
|
+
}
|
|
20995
|
+
|
|
20756
20996
|
interface RuleProposal {
|
|
20757
20997
|
kind: "redirect" | "query" | "header" | "response-body" | "request-body";
|
|
20758
20998
|
groupId: string;
|
|
20759
20999
|
name: string;
|
|
20760
|
-
|
|
20761
|
-
origins?: string[];
|
|
21000
|
+
source: SourceProposal;
|
|
20762
21001
|
resourceTypes?: string[];
|
|
20763
21002
|
priority?: number;
|
|
20764
21003
|
method?: string;
|
|
@@ -20775,13 +21014,13 @@ interface AIProposal {
|
|
|
20775
21014
|
|
|
20776
21015
|
1. Propose MINIMAL changes - only what the user asked for
|
|
20777
21016
|
2. Use existing groups when possible; create new only if needed
|
|
20778
|
-
3. All regex must be valid ECMAScript
|
|
20779
|
-
4.
|
|
21017
|
+
3. All source.value regex must be valid ECMAScript
|
|
21018
|
+
4. source.operator must be exactly "regex"
|
|
20780
21019
|
5. Resource types must be from the allowed list
|
|
20781
21020
|
6. Methods must be from the allowed list
|
|
20782
21021
|
7. Priority: higher = more specific (100 default)
|
|
20783
21022
|
8. Action object must match the rule kind schema exactly
|
|
20784
|
-
9. Use $1\u2013$9 only when
|
|
21023
|
+
9. Use $1\u2013$9 only when source defines those capture groups; use $$ for a literal dollar sign
|
|
20785
21024
|
10. Before proposing, mentally validate against dry-run: would this rule match the intended URLs?
|
|
20786
21025
|
11. Explanation should be 1-2 sentences describing what the rules do
|
|
20787
21026
|
`;
|
|
@@ -20790,7 +21029,7 @@ var MAX_FIX_ATTEMPTS = 3;
|
|
|
20790
21029
|
function isRecord(value) {
|
|
20791
21030
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20792
21031
|
}
|
|
20793
|
-
function
|
|
21032
|
+
function ownString2(record, ...keys) {
|
|
20794
21033
|
for (const key of keys) {
|
|
20795
21034
|
if (!Object.hasOwn(record, key)) continue;
|
|
20796
21035
|
const value = record[key];
|
|
@@ -20802,8 +21041,11 @@ function ruleFromProposal(proposal, ruleId) {
|
|
|
20802
21041
|
const rule = {
|
|
20803
21042
|
id: ruleId,
|
|
20804
21043
|
name: proposal.name,
|
|
20805
|
-
|
|
20806
|
-
|
|
21044
|
+
source: {
|
|
21045
|
+
key: proposal.source.key,
|
|
21046
|
+
operator: "regex",
|
|
21047
|
+
value: proposal.source.value
|
|
21048
|
+
},
|
|
20807
21049
|
resourceTypes: proposal.resourceTypes ? [...proposal.resourceTypes] : ["main_frame"],
|
|
20808
21050
|
priority: proposal.priority ?? 100
|
|
20809
21051
|
};
|
|
@@ -20822,18 +21064,18 @@ function ruleFromProposal(proposal, ruleId) {
|
|
|
20822
21064
|
case "header": {
|
|
20823
21065
|
rule.type = "header";
|
|
20824
21066
|
if (isRecord(proposal.action)) {
|
|
20825
|
-
const direction =
|
|
21067
|
+
const direction = ownString2(
|
|
20826
21068
|
proposal.action,
|
|
20827
21069
|
"headerDirection",
|
|
20828
21070
|
"direction"
|
|
20829
21071
|
);
|
|
20830
|
-
const operation =
|
|
21072
|
+
const operation = ownString2(
|
|
20831
21073
|
proposal.action,
|
|
20832
21074
|
"headerOperation",
|
|
20833
21075
|
"operation"
|
|
20834
21076
|
);
|
|
20835
|
-
const name =
|
|
20836
|
-
const value =
|
|
21077
|
+
const name = ownString2(proposal.action, "headerName", "name");
|
|
21078
|
+
const value = ownString2(proposal.action, "headerValue", "value");
|
|
20837
21079
|
if (direction !== void 0) rule.headerDirection = direction;
|
|
20838
21080
|
if (operation !== void 0) rule.headerOperation = operation;
|
|
20839
21081
|
if (name !== void 0) rule.headerName = name;
|
|
@@ -20859,7 +21101,7 @@ function ruleFromProposal(proposal, ruleId) {
|
|
|
20859
21101
|
function mergeProposalIntoProject(project, proposal) {
|
|
20860
21102
|
const groups = Array.isArray(project.groups) ? project.groups.map((group) => {
|
|
20861
21103
|
if (!isRecord(group)) {
|
|
20862
|
-
return { id: "invalid", name: "invalid",
|
|
21104
|
+
return { id: "invalid", name: "invalid", rules: [] };
|
|
20863
21105
|
}
|
|
20864
21106
|
return {
|
|
20865
21107
|
...group,
|
|
@@ -20875,7 +21117,6 @@ function mergeProposalIntoProject(project, proposal) {
|
|
|
20875
21117
|
group = {
|
|
20876
21118
|
id: ruleProposal.groupId,
|
|
20877
21119
|
name: "AI Group",
|
|
20878
|
-
origins: [],
|
|
20879
21120
|
rules: []
|
|
20880
21121
|
};
|
|
20881
21122
|
groups.push(group);
|
|
@@ -20923,18 +21164,29 @@ function stringArray(value) {
|
|
|
20923
21164
|
);
|
|
20924
21165
|
return items.length === value.length ? items : void 0;
|
|
20925
21166
|
}
|
|
21167
|
+
function parseSourceProposal(value) {
|
|
21168
|
+
if (!isRecord(value)) return null;
|
|
21169
|
+
if (value.key !== "url" && value.key !== "host") return null;
|
|
21170
|
+
if (value.operator !== "regex") return null;
|
|
21171
|
+
if (typeof value.value !== "string" || value.value.length === 0) return null;
|
|
21172
|
+
return {
|
|
21173
|
+
key: value.key,
|
|
21174
|
+
operator: "regex",
|
|
21175
|
+
value: value.value
|
|
21176
|
+
};
|
|
21177
|
+
}
|
|
20926
21178
|
function parseProposalRule(value) {
|
|
20927
21179
|
if (!isRecord(value)) return null;
|
|
20928
21180
|
const kind = value.kind;
|
|
20929
|
-
|
|
21181
|
+
const source = parseSourceProposal(value.source);
|
|
21182
|
+
if (typeof kind !== "string" || !RULE_KINDS.has(kind) || typeof value.groupId !== "string" || typeof value.name !== "string" || source === null || !Object.hasOwn(value, "action")) {
|
|
20930
21183
|
return null;
|
|
20931
21184
|
}
|
|
20932
21185
|
return {
|
|
20933
21186
|
kind,
|
|
20934
21187
|
groupId: value.groupId,
|
|
20935
21188
|
name: value.name,
|
|
20936
|
-
|
|
20937
|
-
origins: stringArray(value.origins),
|
|
21189
|
+
source,
|
|
20938
21190
|
resourceTypes: stringArray(value.resourceTypes),
|
|
20939
21191
|
priority: typeof value.priority === "number" ? value.priority : void 0,
|
|
20940
21192
|
method: typeof value.method === "string" ? value.method : void 0,
|
|
@@ -20944,7 +21196,7 @@ function parseProposalRule(value) {
|
|
|
20944
21196
|
function repairProposalIntoProject(project, diagnostics, proposal) {
|
|
20945
21197
|
const groups = Array.isArray(project.groups) ? project.groups.map((group) => {
|
|
20946
21198
|
if (!isRecord(group)) {
|
|
20947
|
-
return { id: "invalid", name: "invalid",
|
|
21199
|
+
return { id: "invalid", name: "invalid", rules: [] };
|
|
20948
21200
|
}
|
|
20949
21201
|
return {
|
|
20950
21202
|
...group,
|
|
@@ -20985,7 +21237,6 @@ function repairProposalIntoProject(project, diagnostics, proposal) {
|
|
|
20985
21237
|
group = {
|
|
20986
21238
|
id: ruleProposal.groupId,
|
|
20987
21239
|
name: "AI Group",
|
|
20988
|
-
origins: [],
|
|
20989
21240
|
rules: []
|
|
20990
21241
|
};
|
|
20991
21242
|
groups.push(group);
|
|
@@ -21334,14 +21585,6 @@ function canonicalizeOutboundTarget(value) {
|
|
|
21334
21585
|
if (canonical.includes("#") || canonical.includes("@")) return null;
|
|
21335
21586
|
return canonical;
|
|
21336
21587
|
}
|
|
21337
|
-
function targetOrigin(value) {
|
|
21338
|
-
const origin = normalizeSiteOrigin(new URL(value).origin);
|
|
21339
|
-
return origin;
|
|
21340
|
-
}
|
|
21341
|
-
function isOriginAllowed(target, origins) {
|
|
21342
|
-
const origin = targetOrigin(target);
|
|
21343
|
-
return origin !== null && origins.includes(origin);
|
|
21344
|
-
}
|
|
21345
21588
|
function validMethod(value) {
|
|
21346
21589
|
return typeof value === "string" && HTTP_METHODS.includes(value);
|
|
21347
21590
|
}
|
|
@@ -21373,9 +21616,12 @@ function canonicalTarget(kind, target) {
|
|
|
21373
21616
|
function matchesGrant(descriptor, grant) {
|
|
21374
21617
|
return descriptor.groupId === grant.groupId && descriptor.ruleId === grant.ruleId && descriptor.operationId === grant.operationId && descriptor.kind === grant.kind && descriptor.target === grant.target && descriptor.method === grant.method;
|
|
21375
21618
|
}
|
|
21376
|
-
function authorizeExact(preset, value) {
|
|
21619
|
+
function authorizeExact(preset, value, requestUrl) {
|
|
21377
21620
|
const descriptor = exactDescriptor(value);
|
|
21378
21621
|
if (descriptor === null) return failure("runtime.authorization-denied");
|
|
21622
|
+
if (typeof requestUrl !== "string" || requestUrl.length === 0) {
|
|
21623
|
+
return failure("runtime.authorization-denied");
|
|
21624
|
+
}
|
|
21379
21625
|
const target = canonicalTarget(descriptor.kind, descriptor.target);
|
|
21380
21626
|
if (target === null) return failure("runtime.authorization-denied");
|
|
21381
21627
|
for (const grant of preset.grants) {
|
|
@@ -21383,7 +21629,10 @@ function authorizeExact(preset, value) {
|
|
|
21383
21629
|
const matcher = preset.matchers.find(
|
|
21384
21630
|
(candidate) => candidate.groupId === grant.groupId && candidate.ruleId === grant.ruleId
|
|
21385
21631
|
);
|
|
21386
|
-
if (matcher === void 0
|
|
21632
|
+
if (matcher === void 0) {
|
|
21633
|
+
return failure("runtime.authorization-denied");
|
|
21634
|
+
}
|
|
21635
|
+
if (grant.kind === "outbound-http" && !sameOrigin(grant.target, requestUrl)) {
|
|
21387
21636
|
return failure("runtime.authorization-denied");
|
|
21388
21637
|
}
|
|
21389
21638
|
return {
|
|
@@ -21395,7 +21644,7 @@ function authorizeExact(preset, value) {
|
|
|
21395
21644
|
kind: grant.kind,
|
|
21396
21645
|
target: grant.target,
|
|
21397
21646
|
method: grant.method,
|
|
21398
|
-
|
|
21647
|
+
sourceValue: matcher.matcher.source.value,
|
|
21399
21648
|
presetDigest: preset.digest
|
|
21400
21649
|
})
|
|
21401
21650
|
};
|
|
@@ -21432,7 +21681,7 @@ var RUNTIME_LIMITS = Object.freeze({
|
|
|
21432
21681
|
});
|
|
21433
21682
|
var PROTOCOL = "v1";
|
|
21434
21683
|
var ENVELOPE_MAX_BYTES = 64 * 1024;
|
|
21435
|
-
var
|
|
21684
|
+
var MAX_PAC_ROUTES = 256;
|
|
21436
21685
|
var FORBIDDEN_BODY_KEYS = /* @__PURE__ */ new Set(["body", "requestBody", "responseBody"]);
|
|
21437
21686
|
var MOCK_BODY_KEY = "mockBody";
|
|
21438
21687
|
var MOCK_BODY_ALLOWED_TYPES = /* @__PURE__ */ new Set(["mock.response"]);
|
|
@@ -21842,12 +22091,18 @@ function originOf(value) {
|
|
|
21842
22091
|
return null;
|
|
21843
22092
|
}
|
|
21844
22093
|
}
|
|
22094
|
+
function isHttpOrigin(origin) {
|
|
22095
|
+
return origin.startsWith("http://") || origin.startsWith("https://");
|
|
22096
|
+
}
|
|
21845
22097
|
function projectHasRule(project, groupId, ruleId) {
|
|
21846
22098
|
if (!Array.isArray(project.groups)) return false;
|
|
21847
22099
|
const group = project.groups.find((candidate) => candidate.id === groupId);
|
|
21848
22100
|
if (group === void 0 || !Array.isArray(group.rules)) return false;
|
|
21849
22101
|
return group.rules.some((rule) => rule.id === ruleId);
|
|
21850
22102
|
}
|
|
22103
|
+
function isBodyOperation(operation) {
|
|
22104
|
+
return operation.kind === "request-body" || operation.kind === "response-body";
|
|
22105
|
+
}
|
|
21851
22106
|
function revalidateAuthority(project, operations, request) {
|
|
21852
22107
|
const projectSnap = snapshotOwnData3(project);
|
|
21853
22108
|
if (!projectSnap.valid) return { allowed: false, reason: "project-invalid" };
|
|
@@ -21869,17 +22124,21 @@ function revalidateAuthority(project, operations, request) {
|
|
|
21869
22124
|
return { allowed: false, reason: "project-inconsistent" };
|
|
21870
22125
|
}
|
|
21871
22126
|
const matcher = operation.matcher;
|
|
21872
|
-
|
|
21873
|
-
if (regex === null || !regex.test(request.url)) {
|
|
22127
|
+
if (!sourceMatches(matcher.source, request.url)) {
|
|
21874
22128
|
return { allowed: false, reason: "url-mismatch" };
|
|
21875
22129
|
}
|
|
21876
|
-
const
|
|
21877
|
-
|
|
22130
|
+
const requestOrigin = originOf(request.url);
|
|
22131
|
+
const targetUrl = request.target ?? request.url;
|
|
22132
|
+
const targetOrigin = originOf(targetUrl);
|
|
22133
|
+
if (requestOrigin === null || targetOrigin === null || !sameOrigin(request.url, targetUrl)) {
|
|
21878
22134
|
return { allowed: false, reason: "target-unauthorized" };
|
|
21879
22135
|
}
|
|
21880
|
-
if (
|
|
22136
|
+
if (isBodyOperation(operation)) {
|
|
22137
|
+
if (request.initiator === void 0) {
|
|
22138
|
+
return { allowed: false, reason: "initiator-unauthorized" };
|
|
22139
|
+
}
|
|
21881
22140
|
const initiatorOrigin = originOf(request.initiator);
|
|
21882
|
-
if (initiatorOrigin === null || !
|
|
22141
|
+
if (initiatorOrigin === null || !isHttpOrigin(initiatorOrigin)) {
|
|
21883
22142
|
return { allowed: false, reason: "initiator-unauthorized" };
|
|
21884
22143
|
}
|
|
21885
22144
|
}
|
|
@@ -21902,21 +22161,11 @@ function revalidateAuthority(project, operations, request) {
|
|
|
21902
22161
|
operation
|
|
21903
22162
|
};
|
|
21904
22163
|
}
|
|
21905
|
-
function originOf2(url) {
|
|
21906
|
-
try {
|
|
21907
|
-
return normalizeSiteOrigin(new URL(url).origin);
|
|
21908
|
-
} catch {
|
|
21909
|
-
return null;
|
|
21910
|
-
}
|
|
21911
|
-
}
|
|
21912
22164
|
function isBodyOp(op) {
|
|
21913
22165
|
return op.kind === "request-body" || op.kind === "response-body";
|
|
21914
22166
|
}
|
|
21915
22167
|
function matcherMatches(op, url, method) {
|
|
21916
|
-
|
|
21917
|
-
if (regex === null || !regex.test(url)) return false;
|
|
21918
|
-
const origin = originOf2(url);
|
|
21919
|
-
if (origin === null || !op.matcher.origins.includes(origin)) return false;
|
|
22168
|
+
if (!sourceMatches(op.matcher.source, url)) return false;
|
|
21920
22169
|
if (op.matcher.method !== void 0 && op.matcher.method !== method.toUpperCase()) {
|
|
21921
22170
|
return false;
|
|
21922
22171
|
}
|
|
@@ -22144,13 +22393,33 @@ function writeResponse(res, status, statusMessage, headers, body) {
|
|
|
22144
22393
|
res.writeHead(status, statusMessage, out);
|
|
22145
22394
|
res.end(body);
|
|
22146
22395
|
}
|
|
22147
|
-
function
|
|
22396
|
+
function initiatorFromHeaders(headers, targetUrl) {
|
|
22397
|
+
const origin = headerValue2(headers, "origin");
|
|
22398
|
+
if (typeof origin === "string" && /^https?:\/\//i.test(origin)) {
|
|
22399
|
+
return origin.endsWith("/") ? origin.slice(0, -1) : origin;
|
|
22400
|
+
}
|
|
22401
|
+
const referer = headerValue2(headers, "referer");
|
|
22402
|
+
if (typeof referer === "string") {
|
|
22403
|
+
try {
|
|
22404
|
+
return new URL(referer).origin;
|
|
22405
|
+
} catch {
|
|
22406
|
+
}
|
|
22407
|
+
}
|
|
22408
|
+
try {
|
|
22409
|
+
return new URL(targetUrl).origin;
|
|
22410
|
+
} catch {
|
|
22411
|
+
return void 0;
|
|
22412
|
+
}
|
|
22413
|
+
}
|
|
22414
|
+
function authorizeOp(policy, op, url, method, headers) {
|
|
22148
22415
|
const resourceType = op.matcher.resourceTypes[0];
|
|
22416
|
+
const initiator = initiatorFromHeaders(headers, url);
|
|
22149
22417
|
const decision = revalidateAuthority(policy.project, policy.operations, {
|
|
22150
22418
|
groupId: op.groupId,
|
|
22151
22419
|
ruleId: op.ruleId,
|
|
22152
22420
|
url,
|
|
22153
22421
|
method,
|
|
22422
|
+
...initiator !== void 0 ? { initiator } : {},
|
|
22154
22423
|
...resourceType !== void 0 ? { resourceType } : {}
|
|
22155
22424
|
});
|
|
22156
22425
|
return decision.allowed;
|
|
@@ -22212,7 +22481,7 @@ async function startInterceptProxy(options = {}) {
|
|
|
22212
22481
|
}
|
|
22213
22482
|
let requestBody = bodyResult.body;
|
|
22214
22483
|
if (needsRequestBody && requestOp !== null && active !== null) {
|
|
22215
|
-
if (!authorizeOp(active, requestOp, targetUrl, method)) {
|
|
22484
|
+
if (!authorizeOp(active, requestOp, targetUrl, method, req.headers)) {
|
|
22216
22485
|
} else {
|
|
22217
22486
|
const rewritten = await rewriteRequestBody(
|
|
22218
22487
|
{
|
|
@@ -22223,7 +22492,7 @@ async function startInterceptProxy(options = {}) {
|
|
|
22223
22492
|
requestOp.requestBody,
|
|
22224
22493
|
{
|
|
22225
22494
|
url: targetUrl,
|
|
22226
|
-
urlRegex: requestOp.matcher.
|
|
22495
|
+
urlRegex: requestOp.matcher.source.value
|
|
22227
22496
|
}
|
|
22228
22497
|
);
|
|
22229
22498
|
if (rewritten.ok) {
|
|
@@ -22245,7 +22514,7 @@ async function startInterceptProxy(options = {}) {
|
|
|
22245
22514
|
let responseBody = upstream.body;
|
|
22246
22515
|
if (responseOp !== null && active !== null) {
|
|
22247
22516
|
if (responseBody.byteLength > RUNTIME_LIMITS.maxResponseBodyBytes) {
|
|
22248
|
-
} else if (!authorizeOp(active, responseOp, targetUrl, method)) {
|
|
22517
|
+
} else if (!authorizeOp(active, responseOp, targetUrl, method, req.headers)) {
|
|
22249
22518
|
} else {
|
|
22250
22519
|
const action = responseOp.responseBody;
|
|
22251
22520
|
const isReplace = "mode" in action && action.mode === "replace";
|
|
@@ -22259,7 +22528,7 @@ async function startInterceptProxy(options = {}) {
|
|
|
22259
22528
|
isReplace ? action : void 0,
|
|
22260
22529
|
{
|
|
22261
22530
|
url: targetUrl,
|
|
22262
|
-
urlRegex: responseOp.matcher.
|
|
22531
|
+
urlRegex: responseOp.matcher.source.value
|
|
22263
22532
|
}
|
|
22264
22533
|
);
|
|
22265
22534
|
if (rewritten.ok) {
|
|
@@ -22325,33 +22594,29 @@ async function startInterceptProxy(options = {}) {
|
|
|
22325
22594
|
}
|
|
22326
22595
|
};
|
|
22327
22596
|
}
|
|
22328
|
-
function generatePacScript(
|
|
22329
|
-
if (!Array.isArray(
|
|
22330
|
-
throw new Error("pac
|
|
22331
|
-
}
|
|
22332
|
-
const valid = [];
|
|
22333
|
-
for (const origin of origins) {
|
|
22334
|
-
const normalized = normalizeSiteOrigin(origin);
|
|
22335
|
-
if (normalized !== null) valid.push(normalized);
|
|
22597
|
+
function generatePacScript(routes, endpoint, options) {
|
|
22598
|
+
if (!Array.isArray(routes)) {
|
|
22599
|
+
throw new Error("runtime.pac-route-limit");
|
|
22336
22600
|
}
|
|
22337
|
-
const unique =
|
|
22338
|
-
|
|
22339
|
-
|
|
22601
|
+
const unique = routes.map((route) => route.hostname).filter((hostname) => typeof hostname === "string" && hostname.length > 0);
|
|
22602
|
+
const sorted = [...new Set(unique)].sort();
|
|
22603
|
+
if (sorted.length > MAX_PAC_ROUTES) {
|
|
22604
|
+
throw new Error("runtime.pac-route-limit");
|
|
22340
22605
|
}
|
|
22341
22606
|
const proxyType = options?.proxyType ?? "PROXY";
|
|
22342
22607
|
const proxy = `${proxyType} ${endpoint.host}:${endpoint.port}`;
|
|
22343
|
-
const originsLiteral = JSON.stringify(unique);
|
|
22344
22608
|
const proxyLiteral = JSON.stringify(proxy);
|
|
22609
|
+
const checks = sorted.map(
|
|
22610
|
+
(hostname) => ` if (host === ${JSON.stringify(hostname)}) {
|
|
22611
|
+
return ${proxyLiteral};
|
|
22612
|
+
}`
|
|
22613
|
+
);
|
|
22345
22614
|
return [
|
|
22346
22615
|
"function FindProxyForURL(url, host) {",
|
|
22347
|
-
|
|
22348
|
-
" var origin = null;",
|
|
22349
|
-
" try { origin = new URL(url).origin; } catch (e) { origin = null; }",
|
|
22350
|
-
" if (origin !== null && origins.indexOf(origin) !== -1) {",
|
|
22351
|
-
` return ${proxyLiteral};`,
|
|
22352
|
-
" }",
|
|
22616
|
+
...checks,
|
|
22353
22617
|
" return 'DIRECT';",
|
|
22354
|
-
"}"
|
|
22618
|
+
"}",
|
|
22619
|
+
""
|
|
22355
22620
|
].join("\n");
|
|
22356
22621
|
}
|
|
22357
22622
|
var registeredProvider = null;
|
|
@@ -22362,7 +22627,7 @@ function registerInterceptionProvider(provider) {
|
|
|
22362
22627
|
function getCurrentSession() {
|
|
22363
22628
|
return currentSession;
|
|
22364
22629
|
}
|
|
22365
|
-
async function startInterception(activation, policyDigest, extensionId,
|
|
22630
|
+
async function startInterception(activation, policyDigest, extensionId, pacRoutes, targetPolicy) {
|
|
22366
22631
|
if (registeredProvider === null) {
|
|
22367
22632
|
return { kind: "unsupported", reasons: ["no-interception-provider"] };
|
|
22368
22633
|
}
|
|
@@ -22396,10 +22661,10 @@ async function startInterception(activation, policyDigest, extensionId, pacOrigi
|
|
|
22396
22661
|
currentSession = {
|
|
22397
22662
|
sessionId,
|
|
22398
22663
|
provider: registeredProvider,
|
|
22399
|
-
activation: { ...activation, proxy,
|
|
22664
|
+
activation: { ...activation, proxy, pacRoutes },
|
|
22400
22665
|
policyDigest,
|
|
22401
22666
|
extensionId,
|
|
22402
|
-
|
|
22667
|
+
pacRoutes,
|
|
22403
22668
|
targetPolicy,
|
|
22404
22669
|
startedAt: activation.startedAt
|
|
22405
22670
|
};
|
|
@@ -22445,7 +22710,7 @@ function createPlatformInterceptionProvider(adapter) {
|
|
|
22445
22710
|
reasons
|
|
22446
22711
|
};
|
|
22447
22712
|
},
|
|
22448
|
-
async start(activation,
|
|
22713
|
+
async start(activation, pacRoutes) {
|
|
22449
22714
|
const capabilities = adapter.detect();
|
|
22450
22715
|
const reasons = unsupportedReasons(capabilities);
|
|
22451
22716
|
if (!capabilities.supported || reasons.length > 0) {
|
|
@@ -22457,7 +22722,10 @@ function createPlatformInterceptionProvider(adapter) {
|
|
|
22457
22722
|
throw new Error("runtime.platform-unsupported");
|
|
22458
22723
|
}
|
|
22459
22724
|
const endpoint = await adapter.startTlsProxy(activation);
|
|
22460
|
-
const pac = generatePacScript(
|
|
22725
|
+
const pac = generatePacScript(
|
|
22726
|
+
pacRoutes.map((hostname) => ({ hostname })),
|
|
22727
|
+
endpoint
|
|
22728
|
+
);
|
|
22461
22729
|
try {
|
|
22462
22730
|
await adapter.installPac(pac);
|
|
22463
22731
|
active = true;
|
|
@@ -22655,8 +22923,9 @@ function arrayValue(values) {
|
|
|
22655
22923
|
}
|
|
22656
22924
|
function matcherValue(operation) {
|
|
22657
22925
|
const matcher = operation.matcher;
|
|
22926
|
+
const source = matcher.source;
|
|
22658
22927
|
const method = matcher.method === void 0 ? "" : `,"method":${stringValue(matcher.method)}`;
|
|
22659
|
-
return `{"kind":"matcher","groupId":${stringValue(operation.groupId)},"ruleId":${stringValue(operation.ruleId)},"matcher":{"
|
|
22928
|
+
return `{"kind":"matcher","groupId":${stringValue(operation.groupId)},"ruleId":${stringValue(operation.ruleId)},"matcher":{"source":{"key":${stringValue(source.key)},"operator":${stringValue(source.operator)},"value":${stringValue(source.value)}},"resourceTypes":${arrayValue(matcher.resourceTypes)},"priority":${String(matcher.priority)}${method}}}`;
|
|
22660
22929
|
}
|
|
22661
22930
|
function limitsValue(limits) {
|
|
22662
22931
|
const fields = [
|
|
@@ -22799,12 +23068,13 @@ function validResourceType(value) {
|
|
|
22799
23068
|
return typeof value === "string" && RESOURCE_TYPES.includes(value);
|
|
22800
23069
|
}
|
|
22801
23070
|
function freezeMatcher(operation) {
|
|
23071
|
+
const source = Object.freeze({
|
|
23072
|
+
key: operation.matcher.source.key,
|
|
23073
|
+
operator: operation.matcher.source.operator,
|
|
23074
|
+
value: operation.matcher.source.value
|
|
23075
|
+
});
|
|
22802
23076
|
const matcher = Object.freeze({
|
|
22803
|
-
|
|
22804
|
-
source: operation.matcher.urlRegex.source,
|
|
22805
|
-
flags: ""
|
|
22806
|
-
}),
|
|
22807
|
-
origins: Object.freeze([...operation.matcher.origins]),
|
|
23077
|
+
source,
|
|
22808
23078
|
resourceTypes: Object.freeze([...operation.matcher.resourceTypes]),
|
|
22809
23079
|
priority: operation.matcher.priority,
|
|
22810
23080
|
...operation.matcher.method === void 0 ? {} : { method: operation.matcher.method }
|
|
@@ -22834,14 +23104,8 @@ function normalizeMatcher(value) {
|
|
|
22834
23104
|
if (record.kind !== "matcher" || !validId2(record.groupId) || !validId2(record.ruleId))
|
|
22835
23105
|
return null;
|
|
22836
23106
|
const matcherValue2 = record.matcher;
|
|
22837
|
-
if (matcherValue2 === null || typeof matcherValue2 !== "object" || Array.isArray(matcherValue2) || !exactKeys(matcherValue2, [
|
|
22838
|
-
"
|
|
22839
|
-
"origins",
|
|
22840
|
-
"resourceTypes",
|
|
22841
|
-
"priority"
|
|
22842
|
-
]) && !exactKeys(matcherValue2, [
|
|
22843
|
-
"urlRegex",
|
|
22844
|
-
"origins",
|
|
23107
|
+
if (matcherValue2 === null || typeof matcherValue2 !== "object" || Array.isArray(matcherValue2) || !exactKeys(matcherValue2, ["source", "resourceTypes", "priority"]) && !exactKeys(matcherValue2, [
|
|
23108
|
+
"source",
|
|
22845
23109
|
"resourceTypes",
|
|
22846
23110
|
"priority",
|
|
22847
23111
|
"method"
|
|
@@ -22849,22 +23113,14 @@ function normalizeMatcher(value) {
|
|
|
22849
23113
|
return null;
|
|
22850
23114
|
}
|
|
22851
23115
|
const matcher = matcherValue2;
|
|
22852
|
-
const
|
|
22853
|
-
if (
|
|
23116
|
+
const sourceValue = matcher.source;
|
|
23117
|
+
if (sourceValue === null || typeof sourceValue !== "object" || Array.isArray(sourceValue) || !exactKeys(sourceValue, ["key", "operator", "value"])) {
|
|
22854
23118
|
return null;
|
|
22855
23119
|
}
|
|
22856
|
-
const
|
|
22857
|
-
if (
|
|
23120
|
+
const source = sourceValue;
|
|
23121
|
+
if (source.key !== "url" && source.key !== "host" || source.operator !== "regex" || typeof source.value !== "string" || source.value.length === 0 || source.value.length > LIMITS.maxUrlRegexLength || compileUrlRegex(source.value) === null) {
|
|
22858
23122
|
return null;
|
|
22859
23123
|
}
|
|
22860
|
-
if (!Array.isArray(matcher.origins) || matcher.origins.length === 0 || matcher.origins.length > LIMITS.maxOriginsPerScope)
|
|
22861
|
-
return null;
|
|
22862
|
-
const origins = [];
|
|
22863
|
-
for (const value2 of matcher.origins) {
|
|
22864
|
-
const normalized = normalizeSiteOrigin(value2);
|
|
22865
|
-
if (normalized === null || origins.includes(normalized)) return null;
|
|
22866
|
-
origins.push(normalized);
|
|
22867
|
-
}
|
|
22868
23124
|
if (!Array.isArray(matcher.resourceTypes) || matcher.resourceTypes.length === 0 || matcher.resourceTypes.length > LIMITS.maxResourceTypesPerRule)
|
|
22869
23125
|
return null;
|
|
22870
23126
|
const resourceTypes = [];
|
|
@@ -22893,8 +23149,11 @@ function normalizeMatcher(value) {
|
|
|
22893
23149
|
name: resolveMatcherName(record.ruleId, record.name),
|
|
22894
23150
|
redactSensitiveInLogs,
|
|
22895
23151
|
matcher: {
|
|
22896
|
-
|
|
22897
|
-
|
|
23152
|
+
source: {
|
|
23153
|
+
key: source.key,
|
|
23154
|
+
operator: "regex",
|
|
23155
|
+
value: source.value
|
|
23156
|
+
},
|
|
22898
23157
|
resourceTypes,
|
|
22899
23158
|
priority: matcher.priority,
|
|
22900
23159
|
...method === void 0 ? {} : { method }
|
|
@@ -22937,8 +23196,7 @@ function makeGrant(value, matcherById) {
|
|
|
22937
23196
|
if (record.kind === "outbound-http") {
|
|
22938
23197
|
if (record.method !== "GET" && record.method !== "HEAD") return null;
|
|
22939
23198
|
target = canonicalizeOutboundTarget(record.target);
|
|
22940
|
-
if (target === null
|
|
22941
|
-
return null;
|
|
23199
|
+
if (target === null) return null;
|
|
22942
23200
|
} else {
|
|
22943
23201
|
target = normalizeLogicalPath(record.target);
|
|
22944
23202
|
}
|
|
@@ -23060,11 +23318,11 @@ function createNativeRuntimeController(options) {
|
|
|
23060
23318
|
aiClient = createAIClient(options.aiProviderConfig);
|
|
23061
23319
|
}
|
|
23062
23320
|
async function bindInterception(current, sessionConfig) {
|
|
23063
|
-
if (sessionConfig.
|
|
23321
|
+
if (sessionConfig.pacRoutes.length === 0) {
|
|
23064
23322
|
return {
|
|
23065
|
-
activation: { ...current,
|
|
23323
|
+
activation: { ...current, pacRoutes: [] },
|
|
23066
23324
|
session: null,
|
|
23067
|
-
interception: { active: false, reasons: ["no-pac-
|
|
23325
|
+
interception: { active: false, reasons: ["no-pac-routes"] }
|
|
23068
23326
|
};
|
|
23069
23327
|
}
|
|
23070
23328
|
if (hasActiveSession()) {
|
|
@@ -23082,7 +23340,7 @@ function createNativeRuntimeController(options) {
|
|
|
23082
23340
|
current,
|
|
23083
23341
|
sessionConfig.policyDigest,
|
|
23084
23342
|
sessionConfig.extensionId,
|
|
23085
|
-
sessionConfig.
|
|
23343
|
+
sessionConfig.pacRoutes,
|
|
23086
23344
|
{
|
|
23087
23345
|
public: sessionConfig.targetPolicy.public,
|
|
23088
23346
|
localOrigins: sessionConfig.targetPolicy.localOrigins
|
|
@@ -23098,7 +23356,7 @@ function createNativeRuntimeController(options) {
|
|
|
23098
23356
|
const session = getCurrentSession();
|
|
23099
23357
|
const next = {
|
|
23100
23358
|
...current,
|
|
23101
|
-
|
|
23359
|
+
pacRoutes: sessionConfig.pacRoutes,
|
|
23102
23360
|
proxy: result.proxy
|
|
23103
23361
|
};
|
|
23104
23362
|
return {
|
|
@@ -23135,7 +23393,7 @@ function createNativeRuntimeController(options) {
|
|
|
23135
23393
|
state: "running",
|
|
23136
23394
|
startedAt,
|
|
23137
23395
|
presetDigest: preset.digest,
|
|
23138
|
-
|
|
23396
|
+
pacRoutes: sessionConfig?.pacRoutes ?? []
|
|
23139
23397
|
};
|
|
23140
23398
|
let session = null;
|
|
23141
23399
|
if (sessionConfig) {
|
|
@@ -23316,7 +23574,7 @@ function createNativeRuntimeController(options) {
|
|
|
23316
23574
|
const sessionConfig = {
|
|
23317
23575
|
policyDigest: typeof meta.policyDigest === "string" ? meta.policyDigest : "",
|
|
23318
23576
|
extensionId: typeof meta.extensionId === "string" ? meta.extensionId : "",
|
|
23319
|
-
|
|
23577
|
+
pacRoutes: Array.isArray(meta.pacRoutes) ? meta.pacRoutes : [],
|
|
23320
23578
|
targetPolicy: {
|
|
23321
23579
|
public: meta.targetPolicy?.public === true || meta.targetPolicy?.publicAllowed === true,
|
|
23322
23580
|
localOrigins: Array.isArray(meta.targetPolicy?.localOrigins) ? meta.targetPolicy.localOrigins : []
|
|
@@ -23333,7 +23591,7 @@ function createNativeRuntimeController(options) {
|
|
|
23333
23591
|
ok: bound.interception.active,
|
|
23334
23592
|
interception: bound.interception,
|
|
23335
23593
|
...activation.proxy !== void 0 ? { proxy: activation.proxy } : {},
|
|
23336
|
-
|
|
23594
|
+
pacRoutes: activation.pacRoutes
|
|
23337
23595
|
}
|
|
23338
23596
|
};
|
|
23339
23597
|
}
|
|
@@ -23406,7 +23664,9 @@ function createNativeRuntimeController(options) {
|
|
|
23406
23664
|
};
|
|
23407
23665
|
}
|
|
23408
23666
|
if (!preset) throw new Error("runtime not started");
|
|
23409
|
-
const
|
|
23667
|
+
const descriptorRecord = typeof meta.descriptor === "object" && meta.descriptor !== null ? meta.descriptor : {};
|
|
23668
|
+
const requestUrl = typeof meta.requestUrl === "string" && meta.requestUrl.length > 0 ? meta.requestUrl : typeof descriptorRecord.target === "string" ? descriptorRecord.target : "";
|
|
23669
|
+
const result = authorizeExact(preset, meta.descriptor, requestUrl);
|
|
23410
23670
|
op.value();
|
|
23411
23671
|
if (!result.ok) {
|
|
23412
23672
|
return {
|
|
@@ -24162,13 +24422,13 @@ function createNativeHost(options) {
|
|
|
24162
24422
|
}
|
|
24163
24423
|
};
|
|
24164
24424
|
const platformProvider = createPlatformInterceptionProvider(platformAdapter);
|
|
24165
|
-
let
|
|
24425
|
+
let pendingRoutes = [];
|
|
24166
24426
|
registerInterceptionProvider({
|
|
24167
24427
|
platform: platformProvider.platform,
|
|
24168
24428
|
detect: () => platformProvider.detect(),
|
|
24169
24429
|
async start(activation) {
|
|
24170
|
-
const
|
|
24171
|
-
const endpoint = await platformProvider.start(activation,
|
|
24430
|
+
const routes = pendingRoutes.length > 0 ? pendingRoutes : activation.pacRoutes;
|
|
24431
|
+
const endpoint = await platformProvider.start(activation, routes);
|
|
24172
24432
|
const policy = controller.getActivePolicy();
|
|
24173
24433
|
interceptProxy?.setPolicy(
|
|
24174
24434
|
policy ? { project: policy.project, operations: policy.operations } : null
|
|
@@ -24220,8 +24480,8 @@ function createNativeHost(options) {
|
|
|
24220
24480
|
}
|
|
24221
24481
|
}
|
|
24222
24482
|
if (envelope.type === "runtime.start") {
|
|
24223
|
-
const
|
|
24224
|
-
|
|
24483
|
+
const routes = envelope.metadata.pacRoutes;
|
|
24484
|
+
pendingRoutes = Array.isArray(routes) ? routes.filter((o) => typeof o === "string") : [];
|
|
24225
24485
|
}
|
|
24226
24486
|
console.error(
|
|
24227
24487
|
"[rogatio-host] received envelope:",
|
|
@@ -25209,6 +25469,10 @@ function buildDimension(state, detail) {
|
|
|
25209
25469
|
detail
|
|
25210
25470
|
};
|
|
25211
25471
|
}
|
|
25472
|
+
function sourceDetail(source, matched) {
|
|
25473
|
+
const subject = source.key === "host" ? "hostname" : "url";
|
|
25474
|
+
return matched ? `${subject} matched /${source.value}/ (${source.key})` : `${subject} did not match /${source.value}/ (${source.key})`;
|
|
25475
|
+
}
|
|
25212
25476
|
function emptySummary() {
|
|
25213
25477
|
return {
|
|
25214
25478
|
caseCount: 0,
|
|
@@ -25310,15 +25574,6 @@ function dryRunProject(operations, cases, options) {
|
|
|
25310
25574
|
errors.push(result.error);
|
|
25311
25575
|
}
|
|
25312
25576
|
}
|
|
25313
|
-
const regexCache = /* @__PURE__ */ new Map();
|
|
25314
|
-
const getRegex = (source) => {
|
|
25315
|
-
let cached = regexCache.get(source);
|
|
25316
|
-
if (cached === void 0) {
|
|
25317
|
-
cached = compileUrlRegex(source);
|
|
25318
|
-
regexCache.set(source, cached);
|
|
25319
|
-
}
|
|
25320
|
-
return cached;
|
|
25321
|
-
};
|
|
25322
25577
|
const results = [];
|
|
25323
25578
|
let matchedUrlCount = 0;
|
|
25324
25579
|
let matchedRuleTotal = 0;
|
|
@@ -25332,21 +25587,16 @@ function dryRunProject(operations, cases, options) {
|
|
|
25332
25587
|
const rules = [];
|
|
25333
25588
|
for (const op of operations) {
|
|
25334
25589
|
const matcher = op.matcher;
|
|
25335
|
-
const
|
|
25336
|
-
|
|
25337
|
-
|
|
25338
|
-
parsed.value.origin
|
|
25590
|
+
const sourceState = sourceMatches(
|
|
25591
|
+
matcher.source,
|
|
25592
|
+
testCase.url
|
|
25339
25593
|
) ? "matched" : "unmatched";
|
|
25340
25594
|
const methodState = testCase.method === void 0 ? "not-applicable" : matcher.method === void 0 || matcher.method === testCase.method ? "matched" : "unmatched";
|
|
25341
25595
|
const resourceState = testCase.resourceType === void 0 ? "not-applicable" : matcher.resourceTypes.length === 0 || matcher.resourceTypes.includes(testCase.resourceType) ? "matched" : "unmatched";
|
|
25342
|
-
const matched =
|
|
25343
|
-
const
|
|
25344
|
-
|
|
25345
|
-
|
|
25346
|
-
);
|
|
25347
|
-
const originDim = buildDimension(
|
|
25348
|
-
originState,
|
|
25349
|
-
originState === "matched" ? `origin ${parsed.value.origin} in [${matcher.origins.join(", ")}]` : `origin ${parsed.value.origin} not in [${matcher.origins.join(", ")}]`
|
|
25596
|
+
const matched = sourceState === "matched" && methodState !== "unmatched" && resourceState !== "unmatched";
|
|
25597
|
+
const sourceDim = buildDimension(
|
|
25598
|
+
sourceState,
|
|
25599
|
+
sourceDetail(matcher.source, sourceState === "matched")
|
|
25350
25600
|
);
|
|
25351
25601
|
const methodDim = buildDimension(
|
|
25352
25602
|
methodState,
|
|
@@ -25366,8 +25616,7 @@ function dryRunProject(operations, cases, options) {
|
|
|
25366
25616
|
groupId: op.groupId,
|
|
25367
25617
|
ruleId: op.ruleId,
|
|
25368
25618
|
matched,
|
|
25369
|
-
|
|
25370
|
-
effectiveOrigin: originDim,
|
|
25619
|
+
source: sourceDim,
|
|
25371
25620
|
method: methodDim,
|
|
25372
25621
|
resourceType: resourceDim,
|
|
25373
25622
|
actionPreview
|
|
@@ -26106,7 +26355,7 @@ var ProjectFileError = class extends ProjectStorageError {
|
|
|
26106
26355
|
}
|
|
26107
26356
|
};
|
|
26108
26357
|
function emptyProjectDocument() {
|
|
26109
|
-
return { version:
|
|
26358
|
+
return { version: 2, name: randomCivilizationProjectName(), groups: [] };
|
|
26110
26359
|
}
|
|
26111
26360
|
var PROJECT_NAME_LEADS = Object.freeze([
|
|
26112
26361
|
"Apollo",
|
|
@@ -26201,6 +26450,18 @@ async function pathExists(path2) {
|
|
|
26201
26450
|
);
|
|
26202
26451
|
}
|
|
26203
26452
|
}
|
|
26453
|
+
function migrateLoadedProject(parsed) {
|
|
26454
|
+
if (parsed.version === 2) return parsed;
|
|
26455
|
+
if (parsed.version !== 1) return parsed;
|
|
26456
|
+
const migrated = migrateV1Project(parsed);
|
|
26457
|
+
if (!migrated.ok) return parsed;
|
|
26458
|
+
for (const notice of migrated.notices) {
|
|
26459
|
+
console.error(
|
|
26460
|
+
`[rogatio] ${notice.code}: ${notice.message} (${notice.path})`
|
|
26461
|
+
);
|
|
26462
|
+
}
|
|
26463
|
+
return migrated.project;
|
|
26464
|
+
}
|
|
26204
26465
|
async function readDocument(id2) {
|
|
26205
26466
|
try {
|
|
26206
26467
|
const content = await readFile5(id2, "utf-8");
|
|
@@ -26212,7 +26473,7 @@ async function readDocument(id2) {
|
|
|
26212
26473
|
"Project file must contain a JSON object"
|
|
26213
26474
|
);
|
|
26214
26475
|
}
|
|
26215
|
-
return parsed;
|
|
26476
|
+
return migrateLoadedProject(parsed);
|
|
26216
26477
|
} catch (e) {
|
|
26217
26478
|
if (e instanceof ProjectFileError) throw e;
|
|
26218
26479
|
if (e.code === "ENOENT") {
|
|
@@ -27088,8 +27349,18 @@ function usageError(message) {
|
|
|
27088
27349
|
return `Error: ${message}
|
|
27089
27350
|
`;
|
|
27090
27351
|
}
|
|
27352
|
+
function captureSubject(matcher, url) {
|
|
27353
|
+
if (matcher.source.key === "url") return url;
|
|
27354
|
+
try {
|
|
27355
|
+
return new URL(url).hostname;
|
|
27356
|
+
} catch {
|
|
27357
|
+
return null;
|
|
27358
|
+
}
|
|
27359
|
+
}
|
|
27091
27360
|
function previewAction(operation, url) {
|
|
27092
|
-
const
|
|
27361
|
+
const subject = captureSubject(operation.matcher, url);
|
|
27362
|
+
if (subject === null) return null;
|
|
27363
|
+
const captures = matchUrlCaptures(operation.matcher.source.value, subject);
|
|
27093
27364
|
if (captures === null) return null;
|
|
27094
27365
|
const expand = (value) => substituteUrlCaptures(value, captures);
|
|
27095
27366
|
if (operation.kind === "redirect") {
|
|
@@ -27396,9 +27667,7 @@ URL: ${urlResult.url}
|
|
|
27396
27667
|
for (const rule of urlResult.rules) {
|
|
27397
27668
|
output += ` ${rule.groupId}/${rule.ruleId}: ${rule.matched ? "MATCHED" : "NOT MATCHED"}
|
|
27398
27669
|
`;
|
|
27399
|
-
output += `
|
|
27400
|
-
`;
|
|
27401
|
-
output += ` effectiveOrigin: ${rule.effectiveOrigin.state} - ${rule.effectiveOrigin.detail}
|
|
27670
|
+
output += ` source: ${rule.source.state} - ${rule.source.detail}
|
|
27402
27671
|
`;
|
|
27403
27672
|
output += ` method: ${rule.method.state} - ${rule.method.detail}
|
|
27404
27673
|
`;
|