@rogatio/cli 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/index.js +389 -421
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
// packages/cli/src/index.ts
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
|
-
import { dirname as
|
|
6
|
-
import { fileURLToPath as
|
|
5
|
+
import { dirname as dirname4, resolve as resolve6 } from "node:path";
|
|
6
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7
7
|
|
|
8
8
|
// packages/cli/src/commands/edit.ts
|
|
9
|
-
import {
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
|
|
12
12
|
// packages/cli/src/server/http.ts
|
|
@@ -923,7 +923,6 @@ async function writeProject(path, data) {
|
|
|
923
923
|
}
|
|
924
924
|
|
|
925
925
|
// packages/cli/src/commands/edit.ts
|
|
926
|
-
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
927
926
|
async function editCommand(args, options = {}) {
|
|
928
927
|
const customLaunchBrowser = options.launchBrowser;
|
|
929
928
|
const positionalArgs = [];
|
|
@@ -1183,50 +1182,47 @@ function generateEditorHtml(apiBase, csrfToken, filePath) {
|
|
|
1183
1182
|
}
|
|
1184
1183
|
|
|
1185
1184
|
// packages/cli/src/commands/runtime.ts
|
|
1186
|
-
import { dirname as
|
|
1185
|
+
import { dirname as dirname3, isAbsolute, join as join3, relative, resolve as resolve3, sep as sep2 } from "node:path";
|
|
1187
1186
|
import { compileProject as compileProject2 } from "@rogatio/compiler";
|
|
1188
1187
|
|
|
1189
1188
|
// packages/runtime/dist/node/index.js
|
|
1190
1189
|
import { HTTP_METHODS as HTTP_METHODS2 } from "@rogatio/schema";
|
|
1190
|
+
import { hasControl } from "@rogatio/schema";
|
|
1191
1191
|
import { isIP as isIP2 } from "node:net";
|
|
1192
|
-
import { normalizeSiteOrigin } from "@rogatio/schema";
|
|
1192
|
+
import { hasControl as hasControl2, normalizeSiteOrigin } from "@rogatio/schema";
|
|
1193
1193
|
import { normalizeSiteOrigin as normalizeSiteOrigin2 } from "@rogatio/schema";
|
|
1194
1194
|
import {
|
|
1195
1195
|
compileUrlRegex as compileUrlRegex2,
|
|
1196
|
-
normalizeSiteOrigin as normalizeSiteOrigin3
|
|
1197
|
-
} from "@rogatio/schema";
|
|
1198
|
-
import { mkdir as mkdir2, readFile as readFile3, rename as rename2, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
1199
|
-
import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, join as join2, relative as relative2 } from "node:path";
|
|
1200
|
-
import { generateKeyPairSync } from "node:crypto";
|
|
1201
|
-
import {
|
|
1202
|
-
compileUrlRegex as compileUrlRegex22,
|
|
1203
1196
|
HTTP_METHODS as HTTP_METHODS22,
|
|
1197
|
+
hasControl as hasControl3,
|
|
1204
1198
|
LIMITS,
|
|
1205
|
-
normalizeSiteOrigin as
|
|
1199
|
+
normalizeSiteOrigin as normalizeSiteOrigin3,
|
|
1206
1200
|
RESOURCE_TYPES as RESOURCE_TYPES2
|
|
1207
1201
|
} from "@rogatio/schema";
|
|
1208
1202
|
import { createHash as createHash2 } from "node:crypto";
|
|
1203
|
+
import { formatSha256 } from "@rogatio/schema";
|
|
1209
1204
|
import { LIMITS as LIMITS2 } from "@rogatio/schema";
|
|
1210
1205
|
import { LIMITS as LIMITS3 } from "@rogatio/schema";
|
|
1206
|
+
import {
|
|
1207
|
+
compileUrlRegex as compileUrlRegex22,
|
|
1208
|
+
normalizeSiteOrigin as normalizeSiteOrigin4
|
|
1209
|
+
} from "@rogatio/schema";
|
|
1211
1210
|
import { createServer as createServer2 } from "node:http";
|
|
1212
|
-
import { isAbsolute as
|
|
1211
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
1213
1212
|
import { createHash as createHash3, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
1213
|
+
import { isSha256Digest } from "@rogatio/schema";
|
|
1214
1214
|
import { randomBytes as randomBytes22 } from "node:crypto";
|
|
1215
|
-
import { readFile as
|
|
1216
|
-
import { isAbsolute as
|
|
1215
|
+
import { readFile as readFile3, realpath as realpath2, stat } from "node:fs/promises";
|
|
1216
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "node:path";
|
|
1217
|
+
import { mkdir as mkdir2, readFile as readFile22, rename as rename2, rm, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1218
|
+
import { basename as basename3, dirname as dirname2, isAbsolute as isAbsolute4, join as join2, relative as relative3 } from "node:path";
|
|
1219
|
+
import { generateKeyPairSync as generateKeyPairSync2 } from "node:crypto";
|
|
1217
1220
|
function runtimeError(code) {
|
|
1218
1221
|
return { code };
|
|
1219
1222
|
}
|
|
1220
1223
|
function failure(code) {
|
|
1221
1224
|
return { ok: false, error: runtimeError(code) };
|
|
1222
1225
|
}
|
|
1223
|
-
function hasControl(value) {
|
|
1224
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
1225
|
-
const code = value.charCodeAt(index);
|
|
1226
|
-
if (code <= 31 || code === 127) return true;
|
|
1227
|
-
}
|
|
1228
|
-
return false;
|
|
1229
|
-
}
|
|
1230
1226
|
function normalizeLogicalPath(value) {
|
|
1231
1227
|
if (typeof value !== "string" || value.length === 0) return null;
|
|
1232
1228
|
if (value.includes("\\") || value.includes("%") || hasControl(value)) {
|
|
@@ -1311,13 +1307,6 @@ function hasOwn(value, key) {
|
|
|
1311
1307
|
return Object.hasOwn(value, key);
|
|
1312
1308
|
}
|
|
1313
1309
|
var ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
1314
|
-
function hasControl2(value) {
|
|
1315
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
1316
|
-
const code = value.charCodeAt(index);
|
|
1317
|
-
if (code <= 32 || code === 127) return true;
|
|
1318
|
-
}
|
|
1319
|
-
return false;
|
|
1320
|
-
}
|
|
1321
1310
|
function hasValidPercentEncoding(value) {
|
|
1322
1311
|
for (let index = 0; index < value.length; index += 1) {
|
|
1323
1312
|
if (value[index] !== "%") continue;
|
|
@@ -1484,7 +1473,7 @@ var RUNTIME_LIMITS = Object.freeze({
|
|
|
1484
1473
|
maxRegexDeadlineMs: 250,
|
|
1485
1474
|
maxLocalOrigins: 32
|
|
1486
1475
|
});
|
|
1487
|
-
var
|
|
1476
|
+
var ENVELOPE_MAX_BYTES = 64 * 1024;
|
|
1488
1477
|
var registeredProvider = null;
|
|
1489
1478
|
var currentSession = null;
|
|
1490
1479
|
async function startInterception(activation, policyDigest, extensionId, pacOrigins, targetPolicy) {
|
|
@@ -1617,377 +1606,81 @@ function createNativeRuntimeController(options = {}) {
|
|
|
1617
1606
|
function getCurrentSession2() {
|
|
1618
1607
|
return null;
|
|
1619
1608
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
|
1627
|
-
CgKCAQEA
|
|
1628
|
-
-----END CERTIFICATE-----
|
|
1629
|
-
`;
|
|
1630
|
-
const keyPem = `-----BEGIN PRIVATE KEY-----
|
|
1631
|
-
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD
|
|
1632
|
-
-----END PRIVATE KEY-----
|
|
1633
|
-
`;
|
|
1634
|
-
return { certPem, keyPem };
|
|
1609
|
+
var NATIVE_FRAME_MAX_BYTES = 64 * 1024;
|
|
1610
|
+
var NATIVE_POLICY_MAX_BYTES = 256 * 1024;
|
|
1611
|
+
function stringValue(value) {
|
|
1612
|
+
const encoded = JSON.stringify(value);
|
|
1613
|
+
if (encoded === void 0) throw new Error("invalid string");
|
|
1614
|
+
return encoded;
|
|
1635
1615
|
}
|
|
1636
|
-
function
|
|
1637
|
-
return
|
|
1616
|
+
function arrayValue(values) {
|
|
1617
|
+
return `[${values.map(stringValue).join(",")}]`;
|
|
1638
1618
|
}
|
|
1639
|
-
function
|
|
1640
|
-
|
|
1619
|
+
function matcherValue(operation) {
|
|
1620
|
+
const matcher = operation.matcher;
|
|
1621
|
+
const method = matcher.method === void 0 ? "" : `,"method":${stringValue(matcher.method)}`;
|
|
1622
|
+
return `{"kind":"matcher","groupId":${stringValue(operation.groupId)},"ruleId":${stringValue(operation.ruleId)},"matcher":{"urlRegex":{"source":${stringValue(matcher.urlRegex.source)},"flags":""},"origins":${arrayValue(matcher.origins)},"resourceTypes":${arrayValue(matcher.resourceTypes)},"priority":${String(matcher.priority)}${method}}}`;
|
|
1641
1623
|
}
|
|
1642
|
-
function
|
|
1643
|
-
|
|
1624
|
+
function limitsValue(limits) {
|
|
1625
|
+
const fields = [
|
|
1626
|
+
["maxPresetBytes", limits.maxPresetBytes],
|
|
1627
|
+
["maxRequestLineBytes", limits.maxRequestLineBytes],
|
|
1628
|
+
["maxHeaderCount", limits.maxHeaderCount],
|
|
1629
|
+
["maxRequestHeaderBytes", limits.maxRequestHeaderBytes],
|
|
1630
|
+
["maxControlBodyBytes", limits.maxControlBodyBytes],
|
|
1631
|
+
["maxResponseHeaderBytes", limits.maxResponseHeaderBytes],
|
|
1632
|
+
["maxResponseBodyBytes", limits.maxResponseBodyBytes],
|
|
1633
|
+
["maxFileBytes", limits.maxFileBytes],
|
|
1634
|
+
["maxConcurrentSessions", limits.maxConcurrentSessions],
|
|
1635
|
+
["maxConcurrentOperations", limits.maxConcurrentOperations],
|
|
1636
|
+
["maxOperationsPerSession", limits.maxOperationsPerSession],
|
|
1637
|
+
["maxDnsAddresses", limits.maxDnsAddresses],
|
|
1638
|
+
["bootstrapLifetimeMs", limits.bootstrapLifetimeMs],
|
|
1639
|
+
["sessionLifetimeMs", limits.sessionLifetimeMs],
|
|
1640
|
+
["connectTimeoutMs", limits.connectTimeoutMs],
|
|
1641
|
+
["responseHeaderTimeoutMs", limits.responseHeaderTimeoutMs],
|
|
1642
|
+
["bodyIdleTimeoutMs", limits.bodyIdleTimeoutMs],
|
|
1643
|
+
["operationTimeoutMs", limits.operationTimeoutMs],
|
|
1644
|
+
["maxRedirects", limits.maxRedirects]
|
|
1645
|
+
];
|
|
1646
|
+
return `{${fields.map(([key, value]) => `${stringValue(key)}:${String(value)}`).join(",")}}`;
|
|
1644
1647
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
maxAllowedOrigins: 64,
|
|
1648
|
-
caKeyBits: 2048,
|
|
1649
|
-
caValidityDays: 3650
|
|
1650
|
-
};
|
|
1651
|
-
var TrustError = class extends Error {
|
|
1652
|
-
code;
|
|
1653
|
-
reasons;
|
|
1654
|
-
constructor(code, message, reasons = []) {
|
|
1655
|
-
super(message);
|
|
1656
|
-
this.name = "TrustError";
|
|
1657
|
-
this.code = code;
|
|
1658
|
-
this.reasons = reasons;
|
|
1659
|
-
}
|
|
1660
|
-
};
|
|
1661
|
-
var ORIGIN_RE = /^chrome-extension:\/\/[a-p]{32}\/?$/;
|
|
1662
|
-
var DEFAULT_CAPABILITIES = {
|
|
1663
|
-
manifest: false,
|
|
1664
|
-
caTrust: false,
|
|
1665
|
-
reasons: ["no-capability-provider"]
|
|
1666
|
-
};
|
|
1667
|
-
function generateNativeMessagingManifest(hostPath, name, allowedOrigins, installRoot) {
|
|
1668
|
-
if (typeof hostPath !== "string" || !isAbsolute2(hostPath)) {
|
|
1669
|
-
throw new TrustError(
|
|
1670
|
-
"trust.invalid-host-path",
|
|
1671
|
-
"host path must be an absolute path"
|
|
1672
|
-
);
|
|
1673
|
-
}
|
|
1674
|
-
const rel = relative2(installRoot, hostPath);
|
|
1675
|
-
if (rel === "" || rel.startsWith("..") || isAbsolute2(rel)) {
|
|
1676
|
-
throw new TrustError(
|
|
1677
|
-
"trust.invalid-host-path",
|
|
1678
|
-
"host path escapes the configured install root"
|
|
1679
|
-
);
|
|
1680
|
-
}
|
|
1681
|
-
if (typeof name !== "string" || name.length === 0) {
|
|
1682
|
-
throw new TrustError("trust.invalid-manifest", "host name is required");
|
|
1683
|
-
}
|
|
1684
|
-
if (!Array.isArray(allowedOrigins)) {
|
|
1685
|
-
throw new TrustError(
|
|
1686
|
-
"trust.invalid-manifest",
|
|
1687
|
-
"allowed_origins must be an array"
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
if (allowedOrigins.length > F16_TRUST_LIMITS.maxAllowedOrigins) {
|
|
1691
|
-
throw new TrustError(
|
|
1692
|
-
"trust.invalid-manifest",
|
|
1693
|
-
"allowed_origins exceeds the configured maximum"
|
|
1694
|
-
);
|
|
1695
|
-
}
|
|
1696
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1697
|
-
for (const origin of allowedOrigins) {
|
|
1698
|
-
if (typeof origin !== "string" || !ORIGIN_RE.test(origin)) {
|
|
1699
|
-
throw new TrustError(
|
|
1700
|
-
"trust.invalid-origin",
|
|
1701
|
-
`invalid allowed origin: ${String(origin)}`
|
|
1702
|
-
);
|
|
1703
|
-
}
|
|
1704
|
-
seen.add(origin.endsWith("/") ? origin : `${origin}/`);
|
|
1705
|
-
}
|
|
1706
|
-
return {
|
|
1707
|
-
name,
|
|
1708
|
-
description: "Rogatio request-body native runtime host",
|
|
1709
|
-
path: hostPath,
|
|
1710
|
-
type: "stdio",
|
|
1711
|
-
allowed_origins: [...seen].sort()
|
|
1712
|
-
};
|
|
1648
|
+
function grantValue(grant) {
|
|
1649
|
+
return `{"groupId":${stringValue(grant.groupId)},"ruleId":${stringValue(grant.ruleId)},"operationId":${stringValue(grant.operationId)},"kind":${stringValue(grant.kind)},"target":${stringValue(grant.target)},"method":${stringValue(grant.method)}}`;
|
|
1713
1650
|
}
|
|
1714
|
-
function
|
|
1715
|
-
|
|
1716
|
-
return { ...DEFAULT_CAPABILITIES };
|
|
1651
|
+
function mockHeaderValue(header2) {
|
|
1652
|
+
return `{"name":${stringValue(header2.name)},"value":${stringValue(header2.value)}}`;
|
|
1717
1653
|
}
|
|
1718
|
-
function
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
}
|
|
1723
|
-
return
|
|
1654
|
+
function mockValue(mock) {
|
|
1655
|
+
const headers = mock.headers === void 0 ? "" : `,"headers":[${mock.headers.map(mockHeaderValue).join(",")}]`;
|
|
1656
|
+
const delay = mock.delayMs === void 0 ? "" : `,"delayMs":${String(mock.delayMs)}`;
|
|
1657
|
+
const body = mock.body !== void 0 ? `,"body":${stringValue(mock.body)}` : "";
|
|
1658
|
+
const file = mock.file !== void 0 ? `,"file":${stringValue(mock.file)}` : "";
|
|
1659
|
+
return `{"ruleId":${stringValue(mock.ruleId)},"status":${String(mock.status)}${headers}${delay}${body}${file}}`;
|
|
1724
1660
|
}
|
|
1725
|
-
function
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1661
|
+
function sortMocks(mocks) {
|
|
1662
|
+
return [...mocks].sort(
|
|
1663
|
+
(left, right) => compareStrings(left.ruleId, right.ruleId)
|
|
1664
|
+
);
|
|
1729
1665
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
await writeFile2(tmp, data, "utf8");
|
|
1735
|
-
await rename2(tmp, path);
|
|
1666
|
+
function compareStrings(left, right) {
|
|
1667
|
+
if (left < right) return -1;
|
|
1668
|
+
if (left > right) return 1;
|
|
1669
|
+
return 0;
|
|
1736
1670
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
return false;
|
|
1742
|
-
}
|
|
1671
|
+
function sortGrants(grants) {
|
|
1672
|
+
return [...grants].sort(
|
|
1673
|
+
(left, right) => compareStrings(left.groupId, right.groupId) || compareStrings(left.ruleId, right.ruleId) || compareStrings(left.operationId, right.operationId) || compareStrings(left.kind, right.kind) || compareStrings(left.target, right.target) || compareStrings(left.method, right.method)
|
|
1674
|
+
);
|
|
1743
1675
|
}
|
|
1744
|
-
function
|
|
1745
|
-
|
|
1676
|
+
function canonicalPresetBytes(preset, grants = preset.grants) {
|
|
1677
|
+
const matchers = preset.matchers.map(matcherValue).join(",");
|
|
1678
|
+
const mocks = preset.mocks === void 0 || preset.mocks.length === 0 ? "" : `,"mocks":[${sortMocks(preset.mocks).map(mockValue).join(",")}]`;
|
|
1679
|
+
const canonical = `{"version":1,"limits":${limitsValue(preset.limits)},"matchers":[${matchers}],"grants":[${sortGrants(grants).map(grantValue).join(",")}]${mocks}}`;
|
|
1680
|
+
return new TextEncoder().encode(canonical);
|
|
1746
1681
|
}
|
|
1747
|
-
function
|
|
1748
|
-
return
|
|
1749
|
-
}
|
|
1750
|
-
function createRequestBodyTrustController(options = {}) {
|
|
1751
|
-
const platform = options.platform ?? process.platform;
|
|
1752
|
-
const hostName = options.hostName ?? "com.rogatio.runtime";
|
|
1753
|
-
const _allowedOrigins = options.allowedOrigins ?? [];
|
|
1754
|
-
const installRoot = options.installRoot ?? defaultTrustInstallRoot(platform);
|
|
1755
|
-
const hostPath = options.hostPath ?? join2(installRoot, "runtime-host");
|
|
1756
|
-
const manifestDir = options.manifestDir ?? installRoot;
|
|
1757
|
-
const caKeyFile = join2(
|
|
1758
|
-
installRoot,
|
|
1759
|
-
options.caKeyFileName ?? ".rogatio-ca.key"
|
|
1760
|
-
);
|
|
1761
|
-
const caPubFile = join2(
|
|
1762
|
-
installRoot,
|
|
1763
|
-
options.caPubFileName ?? ".rogatio-ca.pub"
|
|
1764
|
-
);
|
|
1765
|
-
const detect = options.detectCapabilities ?? detectTrustCapabilities;
|
|
1766
|
-
const caTrustInstaller = options.caTrustInstaller;
|
|
1767
|
-
const caTrustRemover = options.caTrustRemover;
|
|
1768
|
-
const caCertFile = join2(
|
|
1769
|
-
installRoot,
|
|
1770
|
-
options.caCertFileName ?? ".rogatio-ca.crt"
|
|
1771
|
-
);
|
|
1772
|
-
const manifestPath = () => join2(manifestDir, `${hostName}.json`);
|
|
1773
|
-
let installerCalled = false;
|
|
1774
|
-
async function install(extensionId) {
|
|
1775
|
-
if (!extensionId || !/^[a-p]{32}$/.test(extensionId)) {
|
|
1776
|
-
return {
|
|
1777
|
-
ok: false,
|
|
1778
|
-
state: "unsupported",
|
|
1779
|
-
reasons: ["invalid-extension-id"]
|
|
1780
|
-
};
|
|
1781
|
-
}
|
|
1782
|
-
const allowedOrigins = [`chrome-extension://${extensionId}/`];
|
|
1783
|
-
let manifest;
|
|
1784
|
-
try {
|
|
1785
|
-
manifest = generateNativeMessagingManifest(
|
|
1786
|
-
hostPath,
|
|
1787
|
-
hostName,
|
|
1788
|
-
allowedOrigins,
|
|
1789
|
-
installRoot
|
|
1790
|
-
);
|
|
1791
|
-
} catch (error) {
|
|
1792
|
-
return {
|
|
1793
|
-
ok: false,
|
|
1794
|
-
state: "unsupported",
|
|
1795
|
-
reasons: [codeOf(error, "trust.invalid-manifest")]
|
|
1796
|
-
};
|
|
1797
|
-
}
|
|
1798
|
-
const data = JSON.stringify(manifest, null, 2);
|
|
1799
|
-
if (data.length > F16_TRUST_LIMITS.manifestMaxBytes) {
|
|
1800
|
-
return {
|
|
1801
|
-
ok: false,
|
|
1802
|
-
state: "unsupported",
|
|
1803
|
-
reasons: ["manifest-too-large"]
|
|
1804
|
-
};
|
|
1805
|
-
}
|
|
1806
|
-
const caps = await detect();
|
|
1807
|
-
if (!caps.manifest) return unsupportedResult(caps);
|
|
1808
|
-
try {
|
|
1809
|
-
await writeFileAtomic(manifestPath(), data);
|
|
1810
|
-
} catch (error) {
|
|
1811
|
-
return {
|
|
1812
|
-
ok: false,
|
|
1813
|
-
state: "unsupported",
|
|
1814
|
-
reasons: [codeOf(error, "trust.write-failed")]
|
|
1815
|
-
};
|
|
1816
|
-
}
|
|
1817
|
-
return { ok: true, state: "installed" };
|
|
1818
|
-
}
|
|
1819
|
-
async function uninstall() {
|
|
1820
|
-
try {
|
|
1821
|
-
await rm(manifestPath(), { force: true });
|
|
1822
|
-
} catch (error) {
|
|
1823
|
-
return {
|
|
1824
|
-
ok: false,
|
|
1825
|
-
state: "unsupported",
|
|
1826
|
-
reasons: [codeOf(error, "trust.write-failed")]
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
1829
|
-
return { ok: true, state: "uninstalled" };
|
|
1830
|
-
}
|
|
1831
|
-
async function trust() {
|
|
1832
|
-
const caps = await detect();
|
|
1833
|
-
if (!caps.caTrust) return unsupportedResult(caps);
|
|
1834
|
-
try {
|
|
1835
|
-
if (!await existsFile(caKeyFile) || !await existsFile(caCertFile)) {
|
|
1836
|
-
const { privateKey, publicKey } = generateCaKeyPair(
|
|
1837
|
-
F16_TRUST_LIMITS.caKeyBits
|
|
1838
|
-
);
|
|
1839
|
-
const _privateKeyPem = exportPrivateKey(privateKey);
|
|
1840
|
-
const _pubPem = exportPublicKey(publicKey);
|
|
1841
|
-
const certResult = createCertificate(
|
|
1842
|
-
"CN=Rogatio Request-Body CA",
|
|
1843
|
-
privateKey,
|
|
1844
|
-
F16_TRUST_LIMITS.caValidityDays
|
|
1845
|
-
);
|
|
1846
|
-
const certPem = certResult.certPem;
|
|
1847
|
-
const certKeyPem = certResult.keyPem;
|
|
1848
|
-
await writeFileAtomic(caKeyFile, certKeyPem);
|
|
1849
|
-
await writeFileAtomic(caPubFile, certPem);
|
|
1850
|
-
await writeFileAtomic(caCertFile, certPem);
|
|
1851
|
-
}
|
|
1852
|
-
if (caTrustInstaller && !installerCalled) {
|
|
1853
|
-
await caTrustInstaller(await readFile3(caCertFile, "utf8"));
|
|
1854
|
-
installerCalled = true;
|
|
1855
|
-
}
|
|
1856
|
-
} catch (error) {
|
|
1857
|
-
return {
|
|
1858
|
-
ok: false,
|
|
1859
|
-
state: "unsupported",
|
|
1860
|
-
reasons: [codeOf(error, "trust.internal")]
|
|
1861
|
-
};
|
|
1862
|
-
}
|
|
1863
|
-
return { ok: true, state: "trusted" };
|
|
1864
|
-
}
|
|
1865
|
-
async function untrust() {
|
|
1866
|
-
try {
|
|
1867
|
-
if (await existsFile(caKeyFile) && caTrustRemover) {
|
|
1868
|
-
await caTrustRemover();
|
|
1869
|
-
}
|
|
1870
|
-
await rm(caKeyFile, { force: true });
|
|
1871
|
-
await rm(caPubFile, { force: true });
|
|
1872
|
-
await rm(caCertFile, { force: true });
|
|
1873
|
-
installerCalled = false;
|
|
1874
|
-
} catch (error) {
|
|
1875
|
-
return {
|
|
1876
|
-
ok: false,
|
|
1877
|
-
state: "unsupported",
|
|
1878
|
-
reasons: [codeOf(error, "trust.internal")]
|
|
1879
|
-
};
|
|
1880
|
-
}
|
|
1881
|
-
return { ok: true, state: "untrusted" };
|
|
1882
|
-
}
|
|
1883
|
-
async function status() {
|
|
1884
|
-
let installed = false;
|
|
1885
|
-
try {
|
|
1886
|
-
const raw = await readFile3(manifestPath(), "utf8");
|
|
1887
|
-
installed = isWellFormedManifest(JSON.parse(raw));
|
|
1888
|
-
} catch {
|
|
1889
|
-
installed = false;
|
|
1890
|
-
}
|
|
1891
|
-
const trusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
|
|
1892
|
-
const caps = await detect();
|
|
1893
|
-
return {
|
|
1894
|
-
installed,
|
|
1895
|
-
trusted,
|
|
1896
|
-
platform,
|
|
1897
|
-
capabilityReasons: caps.reasons
|
|
1898
|
-
};
|
|
1899
|
-
}
|
|
1900
|
-
return { install, uninstall, trust, untrust, status };
|
|
1901
|
-
}
|
|
1902
|
-
var NATIVE_FRAME_MAX_BYTES = 64 * 1024;
|
|
1903
|
-
var NATIVE_POLICY_MAX_BYTES = 256 * 1024;
|
|
1904
|
-
var PRIVATE_RANGES = [
|
|
1905
|
-
{ start: ipToInt("10.0.0.0"), end: ipToInt("10.255.255.255") },
|
|
1906
|
-
{ start: ipToInt("172.16.0.0"), end: ipToInt("172.31.255.255") },
|
|
1907
|
-
{ start: ipToInt("192.168.0.0"), end: ipToInt("192.168.255.255") },
|
|
1908
|
-
{ start: ipToInt("127.0.0.0"), end: ipToInt("127.255.255.255") },
|
|
1909
|
-
{ start: ipToInt("169.254.0.0"), end: ipToInt("169.254.255.255") },
|
|
1910
|
-
{ start: ipToInt("0.0.0.0"), end: ipToInt("0.255.255.255") },
|
|
1911
|
-
{ start: ipToInt("224.0.0.0"), end: ipToInt("239.255.255.255") },
|
|
1912
|
-
{ start: ipToInt("240.0.0.0"), end: ipToInt("255.255.255.255") }
|
|
1913
|
-
];
|
|
1914
|
-
function ipToInt(ip) {
|
|
1915
|
-
const parts = ip.split(".").map(Number);
|
|
1916
|
-
return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3];
|
|
1917
|
-
}
|
|
1918
|
-
function stringValue(value) {
|
|
1919
|
-
const encoded = JSON.stringify(value);
|
|
1920
|
-
if (encoded === void 0) throw new Error("invalid string");
|
|
1921
|
-
return encoded;
|
|
1922
|
-
}
|
|
1923
|
-
function arrayValue(values) {
|
|
1924
|
-
return `[${values.map(stringValue).join(",")}]`;
|
|
1925
|
-
}
|
|
1926
|
-
function matcherValue(operation) {
|
|
1927
|
-
const matcher = operation.matcher;
|
|
1928
|
-
const method = matcher.method === void 0 ? "" : `,"method":${stringValue(matcher.method)}`;
|
|
1929
|
-
return `{"kind":"matcher","groupId":${stringValue(operation.groupId)},"ruleId":${stringValue(operation.ruleId)},"matcher":{"urlRegex":{"source":${stringValue(matcher.urlRegex.source)},"flags":""},"origins":${arrayValue(matcher.origins)},"resourceTypes":${arrayValue(matcher.resourceTypes)},"priority":${String(matcher.priority)}${method}}}`;
|
|
1930
|
-
}
|
|
1931
|
-
function limitsValue(limits) {
|
|
1932
|
-
const fields = [
|
|
1933
|
-
["maxPresetBytes", limits.maxPresetBytes],
|
|
1934
|
-
["maxRequestLineBytes", limits.maxRequestLineBytes],
|
|
1935
|
-
["maxHeaderCount", limits.maxHeaderCount],
|
|
1936
|
-
["maxRequestHeaderBytes", limits.maxRequestHeaderBytes],
|
|
1937
|
-
["maxControlBodyBytes", limits.maxControlBodyBytes],
|
|
1938
|
-
["maxResponseHeaderBytes", limits.maxResponseHeaderBytes],
|
|
1939
|
-
["maxResponseBodyBytes", limits.maxResponseBodyBytes],
|
|
1940
|
-
["maxFileBytes", limits.maxFileBytes],
|
|
1941
|
-
["maxConcurrentSessions", limits.maxConcurrentSessions],
|
|
1942
|
-
["maxConcurrentOperations", limits.maxConcurrentOperations],
|
|
1943
|
-
["maxOperationsPerSession", limits.maxOperationsPerSession],
|
|
1944
|
-
["maxDnsAddresses", limits.maxDnsAddresses],
|
|
1945
|
-
["bootstrapLifetimeMs", limits.bootstrapLifetimeMs],
|
|
1946
|
-
["sessionLifetimeMs", limits.sessionLifetimeMs],
|
|
1947
|
-
["connectTimeoutMs", limits.connectTimeoutMs],
|
|
1948
|
-
["responseHeaderTimeoutMs", limits.responseHeaderTimeoutMs],
|
|
1949
|
-
["bodyIdleTimeoutMs", limits.bodyIdleTimeoutMs],
|
|
1950
|
-
["operationTimeoutMs", limits.operationTimeoutMs],
|
|
1951
|
-
["maxRedirects", limits.maxRedirects]
|
|
1952
|
-
];
|
|
1953
|
-
return `{${fields.map(([key, value]) => `${stringValue(key)}:${String(value)}`).join(",")}}`;
|
|
1954
|
-
}
|
|
1955
|
-
function grantValue(grant) {
|
|
1956
|
-
return `{"groupId":${stringValue(grant.groupId)},"ruleId":${stringValue(grant.ruleId)},"operationId":${stringValue(grant.operationId)},"kind":${stringValue(grant.kind)},"target":${stringValue(grant.target)},"method":${stringValue(grant.method)}}`;
|
|
1957
|
-
}
|
|
1958
|
-
function mockHeaderValue(header2) {
|
|
1959
|
-
return `{"name":${stringValue(header2.name)},"value":${stringValue(header2.value)}}`;
|
|
1960
|
-
}
|
|
1961
|
-
function mockValue(mock) {
|
|
1962
|
-
const headers = mock.headers === void 0 ? "" : `,"headers":[${mock.headers.map(mockHeaderValue).join(",")}]`;
|
|
1963
|
-
const delay = mock.delayMs === void 0 ? "" : `,"delayMs":${String(mock.delayMs)}`;
|
|
1964
|
-
const body = mock.body !== void 0 ? `,"body":${stringValue(mock.body)}` : "";
|
|
1965
|
-
const file = mock.file !== void 0 ? `,"file":${stringValue(mock.file)}` : "";
|
|
1966
|
-
return `{"ruleId":${stringValue(mock.ruleId)},"status":${String(mock.status)}${headers}${delay}${body}${file}}`;
|
|
1967
|
-
}
|
|
1968
|
-
function sortMocks(mocks) {
|
|
1969
|
-
return [...mocks].sort(
|
|
1970
|
-
(left, right) => compareStrings(left.ruleId, right.ruleId)
|
|
1971
|
-
);
|
|
1972
|
-
}
|
|
1973
|
-
function compareStrings(left, right) {
|
|
1974
|
-
if (left < right) return -1;
|
|
1975
|
-
if (left > right) return 1;
|
|
1976
|
-
return 0;
|
|
1977
|
-
}
|
|
1978
|
-
function sortGrants(grants) {
|
|
1979
|
-
return [...grants].sort(
|
|
1980
|
-
(left, right) => compareStrings(left.groupId, right.groupId) || compareStrings(left.ruleId, right.ruleId) || compareStrings(left.operationId, right.operationId) || compareStrings(left.kind, right.kind) || compareStrings(left.target, right.target) || compareStrings(left.method, right.method)
|
|
1981
|
-
);
|
|
1982
|
-
}
|
|
1983
|
-
function canonicalPresetBytes(preset, grants = preset.grants) {
|
|
1984
|
-
const matchers = preset.matchers.map(matcherValue).join(",");
|
|
1985
|
-
const mocks = preset.mocks === void 0 || preset.mocks.length === 0 ? "" : `,"mocks":[${sortMocks(preset.mocks).map(mockValue).join(",")}]`;
|
|
1986
|
-
const canonical = `{"version":1,"limits":${limitsValue(preset.limits)},"matchers":[${matchers}],"grants":[${sortGrants(grants).map(grantValue).join(",")}]${mocks}}`;
|
|
1987
|
-
return new TextEncoder().encode(canonical);
|
|
1988
|
-
}
|
|
1989
|
-
function digestBytes(bytes) {
|
|
1990
|
-
return `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
|
|
1682
|
+
function digestBytes(bytes) {
|
|
1683
|
+
return formatSha256(createHash2("sha256").update(bytes).digest("hex"));
|
|
1991
1684
|
}
|
|
1992
1685
|
function canonicalDescriptor(value) {
|
|
1993
1686
|
return `{"groupId":${stringValue(value.groupId)},"ruleId":${stringValue(value.ruleId)},"operationId":${stringValue(value.operationId)},"kind":${stringValue(value.kind)},"target":${stringValue(value.target)},"method":${stringValue(value.method)}}`;
|
|
@@ -2003,13 +1696,6 @@ function validId2(value) {
|
|
|
2003
1696
|
function validMethod2(value) {
|
|
2004
1697
|
return typeof value === "string" && HTTP_METHODS22.includes(value);
|
|
2005
1698
|
}
|
|
2006
|
-
function hasControl3(value) {
|
|
2007
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
2008
|
-
const code = value.charCodeAt(index);
|
|
2009
|
-
if (code <= 31 || code === 127) return true;
|
|
2010
|
-
}
|
|
2011
|
-
return false;
|
|
2012
|
-
}
|
|
2013
1699
|
function normalizeMockHeader(value) {
|
|
2014
1700
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
2015
1701
|
return null;
|
|
@@ -2128,14 +1814,14 @@ function normalizeMatcher(value) {
|
|
|
2128
1814
|
return null;
|
|
2129
1815
|
}
|
|
2130
1816
|
const regex = regexValue;
|
|
2131
|
-
if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" ||
|
|
1817
|
+
if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" || compileUrlRegex2(regex.source) === null) {
|
|
2132
1818
|
return null;
|
|
2133
1819
|
}
|
|
2134
1820
|
if (!Array.isArray(matcher.origins) || matcher.origins.length === 0 || matcher.origins.length > LIMITS.maxOriginsPerScope)
|
|
2135
1821
|
return null;
|
|
2136
1822
|
const origins = [];
|
|
2137
1823
|
for (const value2 of matcher.origins) {
|
|
2138
|
-
const normalized =
|
|
1824
|
+
const normalized = normalizeSiteOrigin3(value2);
|
|
2139
1825
|
if (normalized === null || origins.includes(normalized)) return null;
|
|
2140
1826
|
origins.push(normalized);
|
|
2141
1827
|
}
|
|
@@ -2286,6 +1972,20 @@ function normalizeRuntimePreset(value) {
|
|
|
2286
1972
|
});
|
|
2287
1973
|
return { ok: true, value: normalized };
|
|
2288
1974
|
}
|
|
1975
|
+
var PRIVATE_RANGES = [
|
|
1976
|
+
{ start: ipToInt("10.0.0.0"), end: ipToInt("10.255.255.255") },
|
|
1977
|
+
{ start: ipToInt("172.16.0.0"), end: ipToInt("172.31.255.255") },
|
|
1978
|
+
{ start: ipToInt("192.168.0.0"), end: ipToInt("192.168.255.255") },
|
|
1979
|
+
{ start: ipToInt("127.0.0.0"), end: ipToInt("127.255.255.255") },
|
|
1980
|
+
{ start: ipToInt("169.254.0.0"), end: ipToInt("169.254.255.255") },
|
|
1981
|
+
{ start: ipToInt("0.0.0.0"), end: ipToInt("0.255.255.255") },
|
|
1982
|
+
{ start: ipToInt("224.0.0.0"), end: ipToInt("239.255.255.255") },
|
|
1983
|
+
{ start: ipToInt("240.0.0.0"), end: ipToInt("255.255.255.255") }
|
|
1984
|
+
];
|
|
1985
|
+
function ipToInt(ip) {
|
|
1986
|
+
const parts = ip.split(".").map(Number);
|
|
1987
|
+
return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3];
|
|
1988
|
+
}
|
|
2289
1989
|
var TOKEN_BYTES = 32;
|
|
2290
1990
|
var TOKEN_LENGTH = 43;
|
|
2291
1991
|
function createCapabilityState(preset, now) {
|
|
@@ -2317,8 +2017,7 @@ function sameToken(value, expectedDigest) {
|
|
|
2317
2017
|
return actual.length === expectedDigest.length && timingSafeEqual(actual, expectedDigest);
|
|
2318
2018
|
}
|
|
2319
2019
|
function sameDigest(value, expected) {
|
|
2320
|
-
if (
|
|
2321
|
-
return false;
|
|
2020
|
+
if (!isSha256Digest(value) || value.length !== expected.length) return false;
|
|
2322
2021
|
const actual = Buffer.from(value, "ascii");
|
|
2323
2022
|
const wanted = Buffer.from(expected, "ascii");
|
|
2324
2023
|
return timingSafeEqual(actual, wanted);
|
|
@@ -2386,8 +2085,8 @@ function closeCapabilityState(state) {
|
|
|
2386
2085
|
state.bootstrapDigest.fill(0);
|
|
2387
2086
|
}
|
|
2388
2087
|
function withinRoot2(root, candidate) {
|
|
2389
|
-
const rest =
|
|
2390
|
-
return rest.length > 0 && rest !== ".." && !rest.startsWith(`..${sep}`) && !
|
|
2088
|
+
const rest = relative2(root, candidate);
|
|
2089
|
+
return rest.length > 0 && rest !== ".." && !rest.startsWith(`..${sep}`) && !isAbsolute2(rest);
|
|
2391
2090
|
}
|
|
2392
2091
|
async function readMockFile(root, logicalPath) {
|
|
2393
2092
|
const normalized = normalizeLogicalPath(logicalPath);
|
|
@@ -2398,11 +2097,11 @@ async function readMockFile(root, logicalPath) {
|
|
|
2398
2097
|
const actualPath = await realpath2(candidate);
|
|
2399
2098
|
if (!withinRoot2(canonicalRoot, actualPath))
|
|
2400
2099
|
return failure("runtime.file-denied");
|
|
2401
|
-
const metadata = await
|
|
2100
|
+
const metadata = await stat(actualPath);
|
|
2402
2101
|
if (!metadata.isFile()) return failure("runtime.file-denied");
|
|
2403
2102
|
if (metadata.size > RUNTIME_LIMITS.maxFileBytes)
|
|
2404
2103
|
return failure("runtime.size-limit");
|
|
2405
|
-
const bytes = await
|
|
2104
|
+
const bytes = await readFile3(actualPath);
|
|
2406
2105
|
if (bytes.byteLength > RUNTIME_LIMITS.maxFileBytes)
|
|
2407
2106
|
return failure("runtime.size-limit");
|
|
2408
2107
|
new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
@@ -2663,9 +2362,9 @@ function statusForError(code) {
|
|
|
2663
2362
|
var PAIR_PATH = "/v1/pair";
|
|
2664
2363
|
var AUTHORIZE_PATH = "/v1/authorize";
|
|
2665
2364
|
var CONNECTION_PATH = "/v1/connection";
|
|
2666
|
-
var MOCK_PROTOCOL = "
|
|
2365
|
+
var MOCK_PROTOCOL = "v1";
|
|
2667
2366
|
function isTrustedRoot(value) {
|
|
2668
|
-
if (typeof value !== "string" || !
|
|
2367
|
+
if (typeof value !== "string" || !isAbsolute3(value)) return false;
|
|
2669
2368
|
for (let index = 0; index < value.length; index += 1) {
|
|
2670
2369
|
if (value.charCodeAt(index) <= 31 || value.charCodeAt(index) === 127)
|
|
2671
2370
|
return false;
|
|
@@ -2827,7 +2526,7 @@ async function createRuntimeServer(options) {
|
|
|
2827
2526
|
}
|
|
2828
2527
|
sendJson(response, 200, {
|
|
2829
2528
|
ok: true,
|
|
2830
|
-
protocol: "
|
|
2529
|
+
protocol: "v1",
|
|
2831
2530
|
sessionCapability: result.value.sessionCapability,
|
|
2832
2531
|
expiresInMs: result.value.expiresInMs
|
|
2833
2532
|
});
|
|
@@ -2936,6 +2635,277 @@ async function createRuntimeServer(options) {
|
|
|
2936
2635
|
return failure("runtime.local-bind-denied");
|
|
2937
2636
|
}
|
|
2938
2637
|
}
|
|
2638
|
+
function createCertificate(_subjectName, _privateKey, _validityDays) {
|
|
2639
|
+
const certPem = `-----BEGIN CERTIFICATE-----
|
|
2640
|
+
MIIDXTCCAkWgAwIBAgIJAKoK/heBjcOuMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
|
2641
|
+
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
|
2642
|
+
aWRnaXRzIFB0eSBMdGQwHhcNMTkwNTEyMDAwMDAwWhcNMjAwNTEyMDAwMDAwWjBF
|
|
2643
|
+
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
|
2644
|
+
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
|
2645
|
+
CgKCAQEA
|
|
2646
|
+
-----END CERTIFICATE-----
|
|
2647
|
+
`;
|
|
2648
|
+
const keyPem = `-----BEGIN PRIVATE KEY-----
|
|
2649
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD
|
|
2650
|
+
-----END PRIVATE KEY-----
|
|
2651
|
+
`;
|
|
2652
|
+
return { certPem, keyPem };
|
|
2653
|
+
}
|
|
2654
|
+
function generateCaKeyPair(bits = 2048) {
|
|
2655
|
+
return generateKeyPairSync2("rsa", { modulusLength: bits });
|
|
2656
|
+
}
|
|
2657
|
+
var TRUST_LIMITS = {
|
|
2658
|
+
manifestMaxBytes: 4096,
|
|
2659
|
+
maxAllowedOrigins: 64,
|
|
2660
|
+
caKeyBits: 2048,
|
|
2661
|
+
caValidityDays: 3650
|
|
2662
|
+
};
|
|
2663
|
+
var TrustError = class extends Error {
|
|
2664
|
+
code;
|
|
2665
|
+
reasons;
|
|
2666
|
+
constructor(code, message, reasons = []) {
|
|
2667
|
+
super(message);
|
|
2668
|
+
this.name = "TrustError";
|
|
2669
|
+
this.code = code;
|
|
2670
|
+
this.reasons = reasons;
|
|
2671
|
+
}
|
|
2672
|
+
};
|
|
2673
|
+
var ORIGIN_RE = /^chrome-extension:\/\/[a-p]{32}\/?$/;
|
|
2674
|
+
var DEFAULT_CAPABILITIES = {
|
|
2675
|
+
manifest: false,
|
|
2676
|
+
caTrust: false,
|
|
2677
|
+
reasons: ["no-capability-provider"]
|
|
2678
|
+
};
|
|
2679
|
+
function generateNativeMessagingManifest(hostPath, name, allowedOrigins, installRoot) {
|
|
2680
|
+
if (typeof hostPath !== "string" || !isAbsolute4(hostPath)) {
|
|
2681
|
+
throw new TrustError(
|
|
2682
|
+
"trust.invalid-host-path",
|
|
2683
|
+
"host path must be an absolute path"
|
|
2684
|
+
);
|
|
2685
|
+
}
|
|
2686
|
+
const rel = relative3(installRoot, hostPath);
|
|
2687
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute4(rel)) {
|
|
2688
|
+
throw new TrustError(
|
|
2689
|
+
"trust.invalid-host-path",
|
|
2690
|
+
"host path escapes the configured install root"
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
2694
|
+
throw new TrustError("trust.invalid-manifest", "host name is required");
|
|
2695
|
+
}
|
|
2696
|
+
if (!Array.isArray(allowedOrigins)) {
|
|
2697
|
+
throw new TrustError(
|
|
2698
|
+
"trust.invalid-manifest",
|
|
2699
|
+
"allowed_origins must be an array"
|
|
2700
|
+
);
|
|
2701
|
+
}
|
|
2702
|
+
if (allowedOrigins.length > TRUST_LIMITS.maxAllowedOrigins) {
|
|
2703
|
+
throw new TrustError(
|
|
2704
|
+
"trust.invalid-manifest",
|
|
2705
|
+
"allowed_origins exceeds the configured maximum"
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2709
|
+
for (const origin of allowedOrigins) {
|
|
2710
|
+
if (typeof origin !== "string" || !ORIGIN_RE.test(origin)) {
|
|
2711
|
+
throw new TrustError(
|
|
2712
|
+
"trust.invalid-origin",
|
|
2713
|
+
`invalid allowed origin: ${String(origin)}`
|
|
2714
|
+
);
|
|
2715
|
+
}
|
|
2716
|
+
seen.add(origin.endsWith("/") ? origin : `${origin}/`);
|
|
2717
|
+
}
|
|
2718
|
+
return {
|
|
2719
|
+
name,
|
|
2720
|
+
description: "Rogatio request-body native runtime host",
|
|
2721
|
+
path: hostPath,
|
|
2722
|
+
type: "stdio",
|
|
2723
|
+
allowed_origins: [...seen].sort()
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
function detectTrustCapabilities(options = {}) {
|
|
2727
|
+
void options;
|
|
2728
|
+
return { ...DEFAULT_CAPABILITIES };
|
|
2729
|
+
}
|
|
2730
|
+
function defaultTrustInstallRoot(platform) {
|
|
2731
|
+
if (platform === "darwin") return "/Applications/Rogatio";
|
|
2732
|
+
if (platform === "win32") {
|
|
2733
|
+
return join2(process.env.LOCALAPPDATA ?? "", "Rogatio");
|
|
2734
|
+
}
|
|
2735
|
+
return join2(process.env.HOME ?? "", ".local", "share", "rogatio");
|
|
2736
|
+
}
|
|
2737
|
+
function isWellFormedManifest(value) {
|
|
2738
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2739
|
+
const m = value;
|
|
2740
|
+
return typeof m.name === "string" && typeof m.path === "string" && m.type === "stdio" && Array.isArray(m.allowed_origins) && m.allowed_origins.every((o) => typeof o === "string");
|
|
2741
|
+
}
|
|
2742
|
+
async function writeFileAtomic(path, data) {
|
|
2743
|
+
const dir = dirname2(path);
|
|
2744
|
+
await mkdir2(dir, { recursive: true });
|
|
2745
|
+
const tmp = join2(dir, `.${basename3(path)}.${process.pid}.tmp`);
|
|
2746
|
+
await writeFile2(tmp, data, "utf8");
|
|
2747
|
+
await rename2(tmp, path);
|
|
2748
|
+
}
|
|
2749
|
+
async function existsFile(path) {
|
|
2750
|
+
try {
|
|
2751
|
+
return (await stat2(path)).isFile();
|
|
2752
|
+
} catch {
|
|
2753
|
+
return false;
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
function codeOf(error, fallback) {
|
|
2757
|
+
return error instanceof TrustError ? error.code : fallback;
|
|
2758
|
+
}
|
|
2759
|
+
function unsupportedResult(caps) {
|
|
2760
|
+
return { ok: false, state: "unsupported", reasons: caps.reasons };
|
|
2761
|
+
}
|
|
2762
|
+
function createRequestBodyTrustController(options = {}) {
|
|
2763
|
+
const platform = options.platform ?? process.platform;
|
|
2764
|
+
const hostName = options.hostName ?? "com.rogatio.runtime";
|
|
2765
|
+
const installRoot = options.installRoot ?? defaultTrustInstallRoot(platform);
|
|
2766
|
+
const hostPath = options.hostPath ?? join2(installRoot, "runtime-host");
|
|
2767
|
+
const manifestDir = options.manifestDir ?? installRoot;
|
|
2768
|
+
const caKeyFile = join2(
|
|
2769
|
+
installRoot,
|
|
2770
|
+
options.caKeyFileName ?? ".rogatio-ca.key"
|
|
2771
|
+
);
|
|
2772
|
+
const caPubFile = join2(
|
|
2773
|
+
installRoot,
|
|
2774
|
+
options.caPubFileName ?? ".rogatio-ca.pub"
|
|
2775
|
+
);
|
|
2776
|
+
const detect = options.detectCapabilities ?? detectTrustCapabilities;
|
|
2777
|
+
const caTrustInstaller = options.caTrustInstaller;
|
|
2778
|
+
const caTrustRemover = options.caTrustRemover;
|
|
2779
|
+
const caCertFile = join2(
|
|
2780
|
+
installRoot,
|
|
2781
|
+
options.caCertFileName ?? ".rogatio-ca.crt"
|
|
2782
|
+
);
|
|
2783
|
+
const manifestPath = () => join2(manifestDir, `${hostName}.json`);
|
|
2784
|
+
let installerCalled = false;
|
|
2785
|
+
async function install(extensionId) {
|
|
2786
|
+
if (!extensionId || !/^[a-p]{32}$/.test(extensionId)) {
|
|
2787
|
+
return {
|
|
2788
|
+
ok: false,
|
|
2789
|
+
state: "unsupported",
|
|
2790
|
+
reasons: ["invalid-extension-id"]
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
const allowedOrigins = [`chrome-extension://${extensionId}/`];
|
|
2794
|
+
let manifest;
|
|
2795
|
+
try {
|
|
2796
|
+
manifest = generateNativeMessagingManifest(
|
|
2797
|
+
hostPath,
|
|
2798
|
+
hostName,
|
|
2799
|
+
allowedOrigins,
|
|
2800
|
+
installRoot
|
|
2801
|
+
);
|
|
2802
|
+
} catch (error) {
|
|
2803
|
+
return {
|
|
2804
|
+
ok: false,
|
|
2805
|
+
state: "unsupported",
|
|
2806
|
+
reasons: [codeOf(error, "trust.invalid-manifest")]
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
const data = JSON.stringify(manifest, null, 2);
|
|
2810
|
+
if (data.length > TRUST_LIMITS.manifestMaxBytes) {
|
|
2811
|
+
return {
|
|
2812
|
+
ok: false,
|
|
2813
|
+
state: "unsupported",
|
|
2814
|
+
reasons: ["manifest-too-large"]
|
|
2815
|
+
};
|
|
2816
|
+
}
|
|
2817
|
+
const caps = await detect();
|
|
2818
|
+
if (!caps.manifest) return unsupportedResult(caps);
|
|
2819
|
+
try {
|
|
2820
|
+
await writeFileAtomic(manifestPath(), data);
|
|
2821
|
+
} catch (error) {
|
|
2822
|
+
return {
|
|
2823
|
+
ok: false,
|
|
2824
|
+
state: "unsupported",
|
|
2825
|
+
reasons: [codeOf(error, "trust.write-failed")]
|
|
2826
|
+
};
|
|
2827
|
+
}
|
|
2828
|
+
return { ok: true, state: "installed" };
|
|
2829
|
+
}
|
|
2830
|
+
async function uninstall() {
|
|
2831
|
+
try {
|
|
2832
|
+
await rm(manifestPath(), { force: true });
|
|
2833
|
+
} catch (error) {
|
|
2834
|
+
return {
|
|
2835
|
+
ok: false,
|
|
2836
|
+
state: "unsupported",
|
|
2837
|
+
reasons: [codeOf(error, "trust.write-failed")]
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
return { ok: true, state: "uninstalled" };
|
|
2841
|
+
}
|
|
2842
|
+
async function trust() {
|
|
2843
|
+
const caps = await detect();
|
|
2844
|
+
if (!caps.caTrust) return unsupportedResult(caps);
|
|
2845
|
+
try {
|
|
2846
|
+
if (!await existsFile(caKeyFile) || !await existsFile(caCertFile)) {
|
|
2847
|
+
const { privateKey } = generateCaKeyPair(TRUST_LIMITS.caKeyBits);
|
|
2848
|
+
const certResult = createCertificate(
|
|
2849
|
+
"CN=Rogatio Request-Body CA",
|
|
2850
|
+
privateKey,
|
|
2851
|
+
TRUST_LIMITS.caValidityDays
|
|
2852
|
+
);
|
|
2853
|
+
const certPem = certResult.certPem;
|
|
2854
|
+
const certKeyPem = certResult.keyPem;
|
|
2855
|
+
await writeFileAtomic(caKeyFile, certKeyPem);
|
|
2856
|
+
await writeFileAtomic(caPubFile, certPem);
|
|
2857
|
+
await writeFileAtomic(caCertFile, certPem);
|
|
2858
|
+
}
|
|
2859
|
+
if (caTrustInstaller && !installerCalled) {
|
|
2860
|
+
await caTrustInstaller(await readFile22(caCertFile, "utf8"));
|
|
2861
|
+
installerCalled = true;
|
|
2862
|
+
}
|
|
2863
|
+
} catch (error) {
|
|
2864
|
+
return {
|
|
2865
|
+
ok: false,
|
|
2866
|
+
state: "unsupported",
|
|
2867
|
+
reasons: [codeOf(error, "trust.internal")]
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
return { ok: true, state: "trusted" };
|
|
2871
|
+
}
|
|
2872
|
+
async function untrust() {
|
|
2873
|
+
try {
|
|
2874
|
+
if (await existsFile(caKeyFile) && caTrustRemover) {
|
|
2875
|
+
await caTrustRemover();
|
|
2876
|
+
}
|
|
2877
|
+
await rm(caKeyFile, { force: true });
|
|
2878
|
+
await rm(caPubFile, { force: true });
|
|
2879
|
+
await rm(caCertFile, { force: true });
|
|
2880
|
+
installerCalled = false;
|
|
2881
|
+
} catch (error) {
|
|
2882
|
+
return {
|
|
2883
|
+
ok: false,
|
|
2884
|
+
state: "unsupported",
|
|
2885
|
+
reasons: [codeOf(error, "trust.internal")]
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
return { ok: true, state: "untrusted" };
|
|
2889
|
+
}
|
|
2890
|
+
async function status() {
|
|
2891
|
+
let installed = false;
|
|
2892
|
+
try {
|
|
2893
|
+
const raw = await readFile22(manifestPath(), "utf8");
|
|
2894
|
+
installed = isWellFormedManifest(JSON.parse(raw));
|
|
2895
|
+
} catch {
|
|
2896
|
+
installed = false;
|
|
2897
|
+
}
|
|
2898
|
+
const trusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
|
|
2899
|
+
const caps = await detect();
|
|
2900
|
+
return {
|
|
2901
|
+
installed,
|
|
2902
|
+
trusted,
|
|
2903
|
+
platform,
|
|
2904
|
+
capabilityReasons: caps.reasons
|
|
2905
|
+
};
|
|
2906
|
+
}
|
|
2907
|
+
return { install, uninstall, trust, untrust, status };
|
|
2908
|
+
}
|
|
2939
2909
|
|
|
2940
2910
|
// packages/cli/src/commands/runtime.ts
|
|
2941
2911
|
import { validateProjectDetailed as validateProjectDetailed2 } from "@rogatio/schema";
|
|
@@ -2947,7 +2917,7 @@ function showRuntimeHelp() {
|
|
|
2947
2917
|
rogatio runtime [options] [path]
|
|
2948
2918
|
|
|
2949
2919
|
Native messaging runtime control for response-body and request-body rules, or
|
|
2950
|
-
start the local mock runtime for
|
|
2920
|
+
start the local mock runtime for mock rules.
|
|
2951
2921
|
|
|
2952
2922
|
Native runtime commands:
|
|
2953
2923
|
start Start the runtime (capability-gated; explicit, no auto-start)
|
|
@@ -3224,7 +3194,7 @@ async function runtimeCommand(args, options = {}) {
|
|
|
3224
3194
|
}
|
|
3225
3195
|
return { exitCode: Promise.resolve(1), shutdown: noopShutdown };
|
|
3226
3196
|
}
|
|
3227
|
-
const rootDir = root ?? (inputPath === "-" ? process.cwd() :
|
|
3197
|
+
const rootDir = root ?? (inputPath === "-" ? process.cwd() : dirname3(filePath));
|
|
3228
3198
|
const mocksResult = buildMockConfigs(compileResult.operations, rootDir);
|
|
3229
3199
|
if (!mocksResult.ok) {
|
|
3230
3200
|
console.error(`Error: ${mocksResult.message}`);
|
|
@@ -3614,11 +3584,9 @@ async function testCommand(args, stdinInput, captureOutput = false) {
|
|
|
3614
3584
|
}
|
|
3615
3585
|
|
|
3616
3586
|
// packages/cli/src/commands/verify.ts
|
|
3617
|
-
import {
|
|
3618
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3587
|
+
import { resolve as resolve5 } from "node:path";
|
|
3619
3588
|
import { compileProject as compileProject4 } from "@rogatio/compiler";
|
|
3620
3589
|
import { validateProjectDetailed as validateProjectDetailed4 } from "@rogatio/schema";
|
|
3621
|
-
var __dirname2 = dirname5(fileURLToPath2(import.meta.url));
|
|
3622
3590
|
async function verifyCommandImpl(args, stdinInput, captureOutput) {
|
|
3623
3591
|
let filePath;
|
|
3624
3592
|
let jsonOutput2 = false;
|
|
@@ -3714,10 +3682,10 @@ async function verifyCommand(args, stdinInput, captureOutput = false) {
|
|
|
3714
3682
|
}
|
|
3715
3683
|
|
|
3716
3684
|
// packages/cli/src/index.ts
|
|
3717
|
-
var
|
|
3718
|
-
var isDist =
|
|
3685
|
+
var __dirname = dirname4(fileURLToPath2(import.meta.url));
|
|
3686
|
+
var isDist = __dirname.includes("/dist/") || __dirname.includes("\\dist\\");
|
|
3719
3687
|
var packageJsonPath = resolve6(
|
|
3720
|
-
|
|
3688
|
+
__dirname,
|
|
3721
3689
|
isDist ? "../../package.json" : "../package.json"
|
|
3722
3690
|
);
|
|
3723
3691
|
var packageJson = JSON.parse(
|
|
@@ -3811,7 +3779,7 @@ Commands:
|
|
|
3811
3779
|
test [path] [url...] Run offline dry-run tests against .rogatio.json
|
|
3812
3780
|
verify [path] Validate .rogatio.json file
|
|
3813
3781
|
runtime <start|stop|status|install|trust|untrust|uninstall> Native messaging runtime and request-body trust control
|
|
3814
|
-
runtime [path] Start the mock runtime server (
|
|
3782
|
+
runtime [path] Start the mock runtime server ()
|
|
3815
3783
|
|
|
3816
3784
|
Global Options:
|
|
3817
3785
|
--help, -h Show help
|
|
@@ -3858,7 +3826,7 @@ function showRuntimeHelp2() {
|
|
|
3858
3826
|
rogatio runtime [options] [path]
|
|
3859
3827
|
|
|
3860
3828
|
Native messaging runtime control for response-body and request-body rules, or
|
|
3861
|
-
start the local mock runtime for
|
|
3829
|
+
start the local mock runtime for mock rules.
|
|
3862
3830
|
|
|
3863
3831
|
Native runtime commands:
|
|
3864
3832
|
start Start the runtime (capability-gated; explicit, no auto-start)
|
|
@@ -3917,7 +3885,7 @@ Exit codes:
|
|
|
3917
3885
|
1 Validation/compile/test errors
|
|
3918
3886
|
2 Usage error (invalid arguments, missing input)`);
|
|
3919
3887
|
}
|
|
3920
|
-
if (process.argv[1] !== void 0 && realpathSync.native(
|
|
3888
|
+
if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath2(import.meta.url)) === realpathSync.native(resolve6(process.argv[1]))) {
|
|
3921
3889
|
cli().catch((err) => {
|
|
3922
3890
|
console.error(err);
|
|
3923
3891
|
process.exit(1);
|
package/package.json
CHANGED