@rogatio/cli 6.20.1 → 6.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/node/index.js +218 -180
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ rogatio <command> [options]
|
|
|
45
45
|
| `rogatio runtime <install\|uninstall\|verify>` | Register the native-messaging host with `install` and trust the device-local CA in one **transactional** call; it needs elevated privileges (Linux `sudo`, macOS keychain authorization, Windows Administrator) and rolls back with exit `1` and `trust unsupported: <reasons>` when a capability or elevation is missing. Release users never pass `--extension-id`. That flag is only for development (a local unpacked build without the release key) and for forks. `uninstall` removes the host manifest, the device-local CA files, and the CA trust installation (idempotent). `verify` reports whether manifest, `runtime-host` wrapper, allowed origins, and CA trust are all valid, and fails when `allowed_origins` omits the release ID. Release users never pass `--extension-id` on verify either. |
|
|
46
46
|
| `rogatio runtime host [path]` | Run the consolidated native-messaging host for the project (pair/authorize/body transforms and mock responses over stdio). Normally launched by the browser; run manually only for debugging. `--root <dir>` overrides the confined mock file root. When omitted, a saved device-local root for that project file is used, otherwise the project directory. A stdin project that contains a file mock requires `--root`. |
|
|
47
47
|
|
|
48
|
-
`rogatio edit` includes **Run checks**. It calls `POST /api/doctor` (CSRF-
|
|
48
|
+
`rogatio edit` includes **Run checks**. It calls `POST /e/<session>/api/doctor` (session path, loopback Origin, and `X-CSRF-Token`) and shows editor checks plus the same report as `rogatio doctor` for the open file. A provider configured after the page opened is picked up on the next `rogatio edit`.
|
|
49
49
|
|
|
50
50
|
Global options: `--help, -h` and `--version, -v`. Run `rogatio <command> --help`
|
|
51
51
|
for command-specific usage.
|
package/dist/node/index.js
CHANGED
|
@@ -7,8 +7,6 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
7
7
|
|
|
8
8
|
// packages/runtime/dist/node/index.js
|
|
9
9
|
import { isIP } from "node:net";
|
|
10
|
-
import * as fs from "node:fs/promises";
|
|
11
|
-
import * as path from "node:path";
|
|
12
10
|
|
|
13
11
|
// packages/schema/dist/node/index.js
|
|
14
12
|
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
@@ -2260,6 +2258,8 @@ function sameOrigin(a, b) {
|
|
|
2260
2258
|
}
|
|
2261
2259
|
|
|
2262
2260
|
// packages/runtime/dist/node/index.js
|
|
2261
|
+
import * as fs from "node:fs/promises";
|
|
2262
|
+
import * as path from "node:path";
|
|
2263
2263
|
import { isIP as isIP2 } from "node:net";
|
|
2264
2264
|
import { constants, existsSync } from "node:fs";
|
|
2265
2265
|
import { open, readlink, realpath } from "node:fs/promises";
|
|
@@ -2283,21 +2283,23 @@ import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join as join2
|
|
|
2283
2283
|
import { X509Certificate } from "node:crypto";
|
|
2284
2284
|
import { generateKeyPairSync, webcrypto } from "node:crypto";
|
|
2285
2285
|
import { spawn, spawnSync } from "node:child_process";
|
|
2286
|
-
import { accessSync, constants as
|
|
2287
|
-
import { mkdtemp
|
|
2288
|
-
import { tmpdir } from "node:os";
|
|
2289
|
-
import { dirname as dirname3, join as
|
|
2286
|
+
import { accessSync, constants as constants3 } from "node:fs";
|
|
2287
|
+
import { mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
|
|
2288
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
2289
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
2290
2290
|
import { join as posixJoin } from "node:path/posix";
|
|
2291
|
+
import { constants as constants2 } from "node:fs";
|
|
2292
|
+
import { lstat as lstat2, mkdtemp, open as open3, rm as rm2 } from "node:fs/promises";
|
|
2293
|
+
import { tmpdir } from "node:os";
|
|
2294
|
+
import { join as join3 } from "node:path";
|
|
2291
2295
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
|
|
2292
|
-
import { accessSync as accessSync2, constants as
|
|
2296
|
+
import { accessSync as accessSync2, constants as constants4, mkdirSync, unlinkSync } from "node:fs";
|
|
2293
2297
|
import { dirname as dirname4 } from "node:path";
|
|
2294
2298
|
import { join as posixJoin2 } from "node:path/posix";
|
|
2295
2299
|
import { spawn as spawn3, spawnSync as spawnSync3 } from "node:child_process";
|
|
2296
|
-
import { accessSync as accessSync3, constants as
|
|
2297
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
2298
|
-
import { join as join4 } from "node:path";
|
|
2300
|
+
import { accessSync as accessSync3, constants as constants5 } from "node:fs";
|
|
2299
2301
|
import { join as posixJoin3 } from "node:path/posix";
|
|
2300
|
-
import { constants as
|
|
2302
|
+
import { constants as constants6 } from "node:fs";
|
|
2301
2303
|
import { open as open4, realpath as realpath2 } from "node:fs/promises";
|
|
2302
2304
|
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
2303
2305
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
@@ -21299,67 +21301,23 @@ function createAIClient(config) {
|
|
|
21299
21301
|
}
|
|
21300
21302
|
};
|
|
21301
21303
|
}
|
|
21302
|
-
var
|
|
21303
|
-
"main_frame",
|
|
21304
|
-
"sub_frame",
|
|
21305
|
-
"stylesheet",
|
|
21306
|
-
"script",
|
|
21307
|
-
"image",
|
|
21308
|
-
"font",
|
|
21309
|
-
"object",
|
|
21310
|
-
"xmlhttprequest",
|
|
21311
|
-
"fetch",
|
|
21312
|
-
"media",
|
|
21313
|
-
"websocket",
|
|
21314
|
-
"manifest",
|
|
21315
|
-
"other"
|
|
21316
|
-
];
|
|
21317
|
-
var HTTP_METHODS2 = [
|
|
21318
|
-
"GET",
|
|
21319
|
-
"POST",
|
|
21320
|
-
"PUT",
|
|
21321
|
-
"PATCH",
|
|
21322
|
-
"DELETE",
|
|
21323
|
-
"HEAD",
|
|
21324
|
-
"OPTIONS",
|
|
21325
|
-
"CONNECT",
|
|
21326
|
-
"TRACE"
|
|
21327
|
-
];
|
|
21328
|
-
var FORBIDDEN_REQUEST_HEADERS2 = [
|
|
21329
|
-
"accept-charset",
|
|
21330
|
-
"accept-encoding",
|
|
21331
|
-
"access-control-request-headers",
|
|
21332
|
-
"access-control-request-method",
|
|
21333
|
-
"connection",
|
|
21334
|
-
"content-length",
|
|
21335
|
-
"cookie",
|
|
21336
|
-
"cookie2",
|
|
21337
|
-
"date",
|
|
21338
|
-
"dnt",
|
|
21339
|
-
"expect",
|
|
21340
|
-
"host",
|
|
21341
|
-
"keep-alive",
|
|
21342
|
-
"origin",
|
|
21343
|
-
"referer",
|
|
21344
|
-
"te",
|
|
21345
|
-
"trailer",
|
|
21346
|
-
"transfer-encoding",
|
|
21347
|
-
"upgrade",
|
|
21348
|
-
"via"
|
|
21349
|
-
];
|
|
21350
|
-
var FORBIDDEN_RESPONSE_HEADERS2 = ["set-cookie", "set-cookie2"];
|
|
21304
|
+
var MAX_PROJECTS_PER_PROFILE = 64;
|
|
21351
21305
|
function formatResourceTypes() {
|
|
21352
|
-
return
|
|
21306
|
+
return [...RESOURCE_TYPES].join(", ");
|
|
21353
21307
|
}
|
|
21354
21308
|
function formatHttpMethods() {
|
|
21355
|
-
return
|
|
21309
|
+
return [...HTTP_METHODS].join(", ");
|
|
21356
21310
|
}
|
|
21357
21311
|
function formatForbiddenHeaders() {
|
|
21358
21312
|
return [
|
|
21359
|
-
...
|
|
21360
|
-
...
|
|
21313
|
+
...FORBIDDEN_REQUEST_HEADERS.map((header) => `request: ${header}`),
|
|
21314
|
+
...FORBIDDEN_REQUEST_PREFIXES.map((prefix) => `request prefix: ${prefix}`),
|
|
21315
|
+
...FORBIDDEN_RESPONSE_HEADERS.map((header) => `response: ${header}`)
|
|
21361
21316
|
].join(", ");
|
|
21362
21317
|
}
|
|
21318
|
+
function formatDiagnostics() {
|
|
21319
|
+
return Object.entries(MESSAGES).map(([code, message]) => `- ${code} - ${message}`).join("\n");
|
|
21320
|
+
}
|
|
21363
21321
|
function formatProjectContext(project) {
|
|
21364
21322
|
try {
|
|
21365
21323
|
return JSON.stringify(project, null, 2);
|
|
@@ -21392,9 +21350,10 @@ ${formatResourceTypes()}
|
|
|
21392
21350
|
${formatHttpMethods()}
|
|
21393
21351
|
|
|
21394
21352
|
### Bounds
|
|
21395
|
-
- Max
|
|
21396
|
-
- Max
|
|
21397
|
-
- Max
|
|
21353
|
+
- Max ${MAX_PROJECTS_PER_PROFILE} projects per profile
|
|
21354
|
+
- Max ${LIMITS.maxGroups} groups per project
|
|
21355
|
+
- Max ${LIMITS.maxRulesPerGroup} rules per group
|
|
21356
|
+
- Max ${LIMITS.maxRulesPerProject} rules per project
|
|
21398
21357
|
|
|
21399
21358
|
## Rule Types
|
|
21400
21359
|
|
|
@@ -21416,22 +21375,7 @@ Replace-mode body may use URL captures. Regex-mode replacement strings use captu
|
|
|
21416
21375
|
Replace-mode body may use URL captures. Regex-mode replacement strings use captures from the body pattern, not the source pattern.
|
|
21417
21376
|
|
|
21418
21377
|
## Compiler Diagnostic Codes (stable)
|
|
21419
|
-
|
|
21420
|
-
- schema.unknown-property - The project contains an unknown property
|
|
21421
|
-
- schema.invalid-type - A value has an invalid type
|
|
21422
|
-
- schema.invalid-format - A value has an invalid format
|
|
21423
|
-
- schema.invalid-value - A value is invalid
|
|
21424
|
-
- schema.out-of-range - A value is outside its allowed bounds
|
|
21425
|
-
- schema.invalid-structure - A value has invalid structure
|
|
21426
|
-
- schema.duplicate-id - Duplicate group or rule ID
|
|
21427
|
-
- schema.duplicate-name - Duplicate group or rule name; every group and rule name must be unique in the project
|
|
21428
|
-
- schema.rule-limit - The project contains too many rules
|
|
21429
|
-
- compiler.invariant - The compiler could not normalize the project
|
|
21430
|
-
- compiler.forbidden-header - The header is forbidden for this direction
|
|
21431
|
-
- compiler.header-value-required - A header value is required for set and append
|
|
21432
|
-
- compiler.header-value-unexpected - A header value is not allowed for remove
|
|
21433
|
-
- compiler.invalid-header-direction - Invalid header direction
|
|
21434
|
-
- compiler.invalid-header-operation - Invalid header operation
|
|
21378
|
+
${formatDiagnostics()}
|
|
21435
21379
|
|
|
21436
21380
|
## Current Project
|
|
21437
21381
|
${projectJson}
|
|
@@ -23211,6 +23155,84 @@ function createRequestBodyTrustController(options = {}) {
|
|
|
23211
23155
|
}
|
|
23212
23156
|
return { install, uninstall, status, verify: verify2 };
|
|
23213
23157
|
}
|
|
23158
|
+
var INSTALL_CERT_NAME = "ca.crt";
|
|
23159
|
+
function optionalFlag(flag) {
|
|
23160
|
+
return typeof flag === "number" ? flag : 0;
|
|
23161
|
+
}
|
|
23162
|
+
function exclusiveWriteFlags() {
|
|
23163
|
+
return constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_TRUNC | optionalFlag(constants2.O_NOFOLLOW);
|
|
23164
|
+
}
|
|
23165
|
+
function readFlags() {
|
|
23166
|
+
return constants2.O_RDONLY | optionalFlag(constants2.O_NOFOLLOW) | optionalFlag(constants2.O_NONBLOCK);
|
|
23167
|
+
}
|
|
23168
|
+
function refuseCertificateFile(message) {
|
|
23169
|
+
throw new TrustError("trust.internal", message, [
|
|
23170
|
+
"certificate-file-rejected"
|
|
23171
|
+
]);
|
|
23172
|
+
}
|
|
23173
|
+
async function writeExclusiveCertificate(certPath, pem) {
|
|
23174
|
+
const handle = await open3(certPath, exclusiveWriteFlags(), 384);
|
|
23175
|
+
try {
|
|
23176
|
+
await handle.writeFile(Buffer.from(pem, "utf8"));
|
|
23177
|
+
await handle.chmod(384);
|
|
23178
|
+
} finally {
|
|
23179
|
+
await handle.close();
|
|
23180
|
+
}
|
|
23181
|
+
}
|
|
23182
|
+
async function assertCertificateReady(certPath, pem) {
|
|
23183
|
+
let info;
|
|
23184
|
+
try {
|
|
23185
|
+
info = await lstat2(certPath);
|
|
23186
|
+
} catch {
|
|
23187
|
+
refuseCertificateFile("certificate file is not a regular file");
|
|
23188
|
+
}
|
|
23189
|
+
if (!info.isFile() || info.isSymbolicLink() || info.isFIFO()) {
|
|
23190
|
+
refuseCertificateFile("certificate file is not a regular file");
|
|
23191
|
+
}
|
|
23192
|
+
let handle;
|
|
23193
|
+
try {
|
|
23194
|
+
handle = await open3(certPath, readFlags());
|
|
23195
|
+
const opened = await handle.stat();
|
|
23196
|
+
if (!opened.isFile() || opened.isFIFO() || opened.isSymbolicLink()) {
|
|
23197
|
+
refuseCertificateFile("certificate file is not a regular file");
|
|
23198
|
+
}
|
|
23199
|
+
const bytes = await handle.readFile();
|
|
23200
|
+
if (!bytes.equals(Buffer.from(pem, "utf8"))) {
|
|
23201
|
+
refuseCertificateFile(
|
|
23202
|
+
"certificate file does not match the certificate to install"
|
|
23203
|
+
);
|
|
23204
|
+
}
|
|
23205
|
+
if (!isInstallableCaCertificate(bytes.toString("utf8"))) {
|
|
23206
|
+
throw new TrustError("trust.internal", "invalid-ca-certificate", [
|
|
23207
|
+
"invalid-ca-certificate"
|
|
23208
|
+
]);
|
|
23209
|
+
}
|
|
23210
|
+
} catch (error) {
|
|
23211
|
+
if (error instanceof TrustError) throw error;
|
|
23212
|
+
refuseCertificateFile("certificate file is not a regular file");
|
|
23213
|
+
} finally {
|
|
23214
|
+
await handle?.close().catch(() => void 0);
|
|
23215
|
+
}
|
|
23216
|
+
}
|
|
23217
|
+
async function withInstallCertificateFile(pem, use) {
|
|
23218
|
+
if (!isInstallableCaCertificate(pem)) {
|
|
23219
|
+
throw new TrustError("trust.internal", "invalid-ca-certificate", [
|
|
23220
|
+
"invalid-ca-certificate"
|
|
23221
|
+
]);
|
|
23222
|
+
}
|
|
23223
|
+
const directory = await mkdtemp(join3(tmpdir(), "rogatio-ca-"));
|
|
23224
|
+
try {
|
|
23225
|
+
const certPath = join3(directory, INSTALL_CERT_NAME);
|
|
23226
|
+
await writeExclusiveCertificate(certPath, pem);
|
|
23227
|
+
await assertCertificateReady(certPath, pem);
|
|
23228
|
+
await use(certPath);
|
|
23229
|
+
} finally {
|
|
23230
|
+
try {
|
|
23231
|
+
await rm2(directory, { recursive: true, force: true });
|
|
23232
|
+
} catch {
|
|
23233
|
+
}
|
|
23234
|
+
}
|
|
23235
|
+
}
|
|
23214
23236
|
var PREVIOUS_CA_PEM_MAX_BYTES = 16 * 1024;
|
|
23215
23237
|
function spawnSecurity(args) {
|
|
23216
23238
|
return new Promise((resolve12) => {
|
|
@@ -23249,12 +23271,12 @@ var darwinAdapter = {
|
|
|
23249
23271
|
reasons.push("tooling-missing");
|
|
23250
23272
|
}
|
|
23251
23273
|
try {
|
|
23252
|
-
accessSync(manifestDir,
|
|
23274
|
+
accessSync(manifestDir, constants3.W_OK);
|
|
23253
23275
|
} catch {
|
|
23254
23276
|
reasons.push("manifest-dir-unwritable");
|
|
23255
23277
|
}
|
|
23256
23278
|
try {
|
|
23257
|
-
accessSync(caDir,
|
|
23279
|
+
accessSync(caDir, constants3.W_OK);
|
|
23258
23280
|
} catch {
|
|
23259
23281
|
reasons.push("keychain-unwritable");
|
|
23260
23282
|
}
|
|
@@ -23268,44 +23290,31 @@ var darwinAdapter = {
|
|
|
23268
23290
|
},
|
|
23269
23291
|
async caTrustInstaller(certPem) {
|
|
23270
23292
|
const keychain = this.defaultCaInstallPath();
|
|
23271
|
-
const tmpPath = join3(tmpdir(), `rogatio-ca-${Date.now()}.crt`);
|
|
23272
23293
|
try {
|
|
23273
|
-
|
|
23274
|
-
|
|
23275
|
-
throw new TrustError("trust.internal", "keychain-unwritable", [
|
|
23276
|
-
"keychain-unwritable"
|
|
23277
|
-
]);
|
|
23278
|
-
}
|
|
23279
|
-
const result = await new Promise(
|
|
23280
|
-
(resolve12) => {
|
|
23281
|
-
const child = spawn("security", [
|
|
23294
|
+
await withInstallCertificateFile(certPem, async (certPath) => {
|
|
23295
|
+
const result = await spawnSecurity([
|
|
23282
23296
|
"add-trusted-cert",
|
|
23283
23297
|
"-d",
|
|
23284
23298
|
"-r",
|
|
23285
23299
|
"trustRoot",
|
|
23286
23300
|
"-k",
|
|
23287
23301
|
keychain,
|
|
23288
|
-
|
|
23302
|
+
certPath
|
|
23289
23303
|
]);
|
|
23290
|
-
|
|
23291
|
-
|
|
23292
|
-
|
|
23293
|
-
|
|
23294
|
-
|
|
23295
|
-
|
|
23296
|
-
|
|
23297
|
-
|
|
23298
|
-
|
|
23299
|
-
|
|
23300
|
-
|
|
23301
|
-
|
|
23302
|
-
|
|
23303
|
-
|
|
23304
|
-
let reason = "keychain-unwritable";
|
|
23305
|
-
if (stderr.includes("permission") || stderr.includes("auth")) {
|
|
23306
|
-
reason = "keychain-unwritable";
|
|
23307
|
-
}
|
|
23308
|
-
throw new TrustError("trust.internal", reason, [reason]);
|
|
23304
|
+
if (result.code !== 0) {
|
|
23305
|
+
const stderr = result.stderr.toLowerCase();
|
|
23306
|
+
let reason = "keychain-unwritable";
|
|
23307
|
+
if (stderr.includes("permission") || stderr.includes("auth")) {
|
|
23308
|
+
reason = "keychain-unwritable";
|
|
23309
|
+
}
|
|
23310
|
+
throw new TrustError("trust.internal", reason, [reason]);
|
|
23311
|
+
}
|
|
23312
|
+
});
|
|
23313
|
+
} catch (error) {
|
|
23314
|
+
if (error instanceof TrustError) throw error;
|
|
23315
|
+
throw new TrustError("trust.internal", "keychain-unwritable", [
|
|
23316
|
+
"keychain-unwritable"
|
|
23317
|
+
]);
|
|
23309
23318
|
}
|
|
23310
23319
|
},
|
|
23311
23320
|
async caTrustRemover() {
|
|
@@ -23347,17 +23356,11 @@ var darwinAdapter = {
|
|
|
23347
23356
|
]);
|
|
23348
23357
|
}
|
|
23349
23358
|
const keychain = this.defaultCaInstallPath();
|
|
23350
|
-
const dir = await
|
|
23351
|
-
const tmpPath =
|
|
23359
|
+
const dir = await mkdtemp2(join4(tmpdir2(), "rogatio-ca-"));
|
|
23360
|
+
const tmpPath = join4(dir, "previous.crt");
|
|
23352
23361
|
let failed = false;
|
|
23353
23362
|
try {
|
|
23354
|
-
|
|
23355
|
-
try {
|
|
23356
|
-
await handle.writeFile(certPem, "utf8");
|
|
23357
|
-
await handle.chmod(384);
|
|
23358
|
-
} finally {
|
|
23359
|
-
await handle.close();
|
|
23360
|
-
}
|
|
23363
|
+
await writeExclusiveCertificate(tmpPath, certPem);
|
|
23361
23364
|
const trust = await spawnSecurity(["remove-trusted-cert", "-d", tmpPath]);
|
|
23362
23365
|
if (trust.code !== 0 && !securityMiss(trust.stderr)) failed = true;
|
|
23363
23366
|
const item = await spawnSecurity([
|
|
@@ -23372,7 +23375,7 @@ var darwinAdapter = {
|
|
|
23372
23375
|
failed = true;
|
|
23373
23376
|
} finally {
|
|
23374
23377
|
try {
|
|
23375
|
-
await
|
|
23378
|
+
await rm3(dir, { recursive: true, force: true });
|
|
23376
23379
|
} catch {
|
|
23377
23380
|
}
|
|
23378
23381
|
}
|
|
@@ -23410,7 +23413,7 @@ function exists(path2) {
|
|
|
23410
23413
|
}
|
|
23411
23414
|
function writable(path2) {
|
|
23412
23415
|
try {
|
|
23413
|
-
accessSync2(path2,
|
|
23416
|
+
accessSync2(path2, constants4.W_OK);
|
|
23414
23417
|
return true;
|
|
23415
23418
|
} catch {
|
|
23416
23419
|
return false;
|
|
@@ -23447,7 +23450,7 @@ function p11kitAnchorsDir() {
|
|
|
23447
23450
|
function needsElevation(dir) {
|
|
23448
23451
|
if (writable(dir)) return false;
|
|
23449
23452
|
try {
|
|
23450
|
-
accessSync2(dir,
|
|
23453
|
+
accessSync2(dir, constants4.W_OK);
|
|
23451
23454
|
} catch (err) {
|
|
23452
23455
|
if (nodeCode(err) === "ENOENT" && writable(dirname4(dir))) return false;
|
|
23453
23456
|
}
|
|
@@ -23464,13 +23467,13 @@ function manifestDirFor(browser) {
|
|
|
23464
23467
|
}
|
|
23465
23468
|
function canHostManifest(dir) {
|
|
23466
23469
|
try {
|
|
23467
|
-
accessSync2(dir,
|
|
23470
|
+
accessSync2(dir, constants4.W_OK);
|
|
23468
23471
|
return true;
|
|
23469
23472
|
} catch (err) {
|
|
23470
23473
|
if (nodeCode(err) !== "ENOENT") return false;
|
|
23471
23474
|
}
|
|
23472
23475
|
try {
|
|
23473
|
-
accessSync2(dirname4(dir),
|
|
23476
|
+
accessSync2(dirname4(dir), constants4.W_OK);
|
|
23474
23477
|
return true;
|
|
23475
23478
|
} catch {
|
|
23476
23479
|
return false;
|
|
@@ -23600,7 +23603,7 @@ var linuxAdapter = {
|
|
|
23600
23603
|
},
|
|
23601
23604
|
async caTrustRemover() {
|
|
23602
23605
|
try {
|
|
23603
|
-
|
|
23606
|
+
unlinkSync(DEBIAN_CERT);
|
|
23604
23607
|
} catch {
|
|
23605
23608
|
}
|
|
23606
23609
|
if (!toolOnPath("update-ca-trust")) return;
|
|
@@ -23661,7 +23664,7 @@ var win32Adapter = {
|
|
|
23661
23664
|
reasons.push("tooling-missing");
|
|
23662
23665
|
}
|
|
23663
23666
|
try {
|
|
23664
|
-
accessSync3(manifestDir,
|
|
23667
|
+
accessSync3(manifestDir, constants5.W_OK);
|
|
23665
23668
|
} catch {
|
|
23666
23669
|
reasons.push("manifest-dir-unwritable");
|
|
23667
23670
|
}
|
|
@@ -23674,37 +23677,42 @@ var win32Adapter = {
|
|
|
23674
23677
|
};
|
|
23675
23678
|
},
|
|
23676
23679
|
async caTrustInstaller(certPem) {
|
|
23677
|
-
const tmpPath = join4(tmpdir2(), `rogatio-ca-${Date.now()}.crt`);
|
|
23678
23680
|
try {
|
|
23679
|
-
|
|
23680
|
-
|
|
23681
|
+
await withInstallCertificateFile(certPem, async (certPath) => {
|
|
23682
|
+
const result = await new Promise(
|
|
23683
|
+
(resolve12) => {
|
|
23684
|
+
const child = spawn3("certutil", [
|
|
23685
|
+
"-addstore",
|
|
23686
|
+
"-f",
|
|
23687
|
+
"Root",
|
|
23688
|
+
certPath
|
|
23689
|
+
]);
|
|
23690
|
+
let stderr = "";
|
|
23691
|
+
child.stderr?.on("data", (data) => {
|
|
23692
|
+
stderr += data.toString();
|
|
23693
|
+
});
|
|
23694
|
+
child.on("close", (code) => resolve12({ code: code ?? 1, stderr }));
|
|
23695
|
+
child.on(
|
|
23696
|
+
"error",
|
|
23697
|
+
(err) => resolve12({ code: 1, stderr: err.message })
|
|
23698
|
+
);
|
|
23699
|
+
}
|
|
23700
|
+
);
|
|
23701
|
+
if (result.code !== 0) {
|
|
23702
|
+
const stderr = result.stderr.toLowerCase();
|
|
23703
|
+
let reason = "elevation-required";
|
|
23704
|
+
if (stderr.includes("access denied") || stderr.includes("permission")) {
|
|
23705
|
+
reason = "elevation-required";
|
|
23706
|
+
}
|
|
23707
|
+
throw new TrustError("trust.internal", reason, [reason]);
|
|
23708
|
+
}
|
|
23709
|
+
});
|
|
23710
|
+
} catch (error) {
|
|
23711
|
+
if (error instanceof TrustError) throw error;
|
|
23681
23712
|
throw new TrustError("trust.internal", "elevation-required", [
|
|
23682
23713
|
"elevation-required"
|
|
23683
23714
|
]);
|
|
23684
23715
|
}
|
|
23685
|
-
const result = await new Promise(
|
|
23686
|
-
(resolve12) => {
|
|
23687
|
-
const child = spawn3("certutil", ["-addstore", "-f", "Root", tmpPath]);
|
|
23688
|
-
let stderr = "";
|
|
23689
|
-
child.stderr?.on("data", (data) => {
|
|
23690
|
-
stderr += data.toString();
|
|
23691
|
-
});
|
|
23692
|
-
child.on("close", (code) => resolve12({ code: code ?? 1, stderr }));
|
|
23693
|
-
child.on("error", (err) => resolve12({ code: 1, stderr: err.message }));
|
|
23694
|
-
}
|
|
23695
|
-
);
|
|
23696
|
-
try {
|
|
23697
|
-
unlinkSync3(tmpPath);
|
|
23698
|
-
} catch {
|
|
23699
|
-
}
|
|
23700
|
-
if (result.code !== 0) {
|
|
23701
|
-
const stderr = result.stderr.toLowerCase();
|
|
23702
|
-
let reason = "elevation-required";
|
|
23703
|
-
if (stderr.includes("access denied") || stderr.includes("permission")) {
|
|
23704
|
-
reason = "elevation-required";
|
|
23705
|
-
}
|
|
23706
|
-
throw new TrustError("trust.internal", reason, [reason]);
|
|
23707
|
-
}
|
|
23708
23716
|
},
|
|
23709
23717
|
async caTrustRemover() {
|
|
23710
23718
|
const result = await new Promise(
|
|
@@ -24644,7 +24652,7 @@ async function resolveConfinedRoot(value) {
|
|
|
24644
24652
|
}
|
|
24645
24653
|
let directory;
|
|
24646
24654
|
try {
|
|
24647
|
-
directory = await open4(value,
|
|
24655
|
+
directory = await open4(value, constants6.O_RDONLY | constants6.O_DIRECTORY);
|
|
24648
24656
|
const info = await directory.stat();
|
|
24649
24657
|
if (!info.isDirectory()) return failure("runtime.root-invalid");
|
|
24650
24658
|
return { ok: true, value: await realpath2(value) };
|
|
@@ -29678,7 +29686,7 @@ function createServer3(handler, options = {}) {
|
|
|
29678
29686
|
}
|
|
29679
29687
|
|
|
29680
29688
|
// packages/cli/src/server/routes.ts
|
|
29681
|
-
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
29689
|
+
import { randomBytes as randomBytes4, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
29682
29690
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
29683
29691
|
import { dirname as dirname9, isAbsolute as isAbsolute5, resolve as resolve5 } from "node:path";
|
|
29684
29692
|
|
|
@@ -30134,17 +30142,44 @@ function isAllowedEditorHost(host, boundPort) {
|
|
|
30134
30142
|
return host === `127.0.0.1:${expected}` || host === `localhost:${expected}`;
|
|
30135
30143
|
}
|
|
30136
30144
|
function isAllowedEditorOrigin(origin, boundPort) {
|
|
30137
|
-
if (origin === void 0)
|
|
30138
|
-
|
|
30145
|
+
if (origin === void 0 || !Number.isInteger(boundPort) || boundPort <= 0) {
|
|
30146
|
+
return false;
|
|
30147
|
+
}
|
|
30139
30148
|
const expected = String(boundPort);
|
|
30140
30149
|
return origin === `http://127.0.0.1:${expected}` || origin === `http://localhost:${expected}`;
|
|
30141
30150
|
}
|
|
30142
30151
|
function generateCsrfToken() {
|
|
30143
30152
|
return randomBytes4(16).toString("hex");
|
|
30144
30153
|
}
|
|
30154
|
+
function tokensEqual(provided, expected) {
|
|
30155
|
+
const providedBytes = Buffer.from(provided);
|
|
30156
|
+
const expectedBytes = Buffer.from(expected);
|
|
30157
|
+
if (providedBytes.length !== expectedBytes.length) return false;
|
|
30158
|
+
return timingSafeEqual2(providedBytes, expectedBytes);
|
|
30159
|
+
}
|
|
30160
|
+
function scopedPathname(pathname, sessionToken) {
|
|
30161
|
+
const marker = "/e/";
|
|
30162
|
+
if (!pathname.startsWith(marker)) return void 0;
|
|
30163
|
+
const after = pathname.slice(marker.length);
|
|
30164
|
+
const slash = after.indexOf("/");
|
|
30165
|
+
const candidate = slash === -1 ? after : after.slice(0, slash);
|
|
30166
|
+
if (candidate.length === 0 || !tokensEqual(candidate, sessionToken)) {
|
|
30167
|
+
return void 0;
|
|
30168
|
+
}
|
|
30169
|
+
return slash === -1 ? "/" : after.slice(slash);
|
|
30170
|
+
}
|
|
30171
|
+
function editorSessionPath(token, suffix) {
|
|
30172
|
+
const path2 = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
30173
|
+
return `/e/${token}${path2}`;
|
|
30174
|
+
}
|
|
30145
30175
|
function validateCsrf(req, expectedToken) {
|
|
30146
|
-
const token = req.headers["x-csrf-token"];
|
|
30147
|
-
|
|
30176
|
+
const token = headerValue3(req.headers["x-csrf-token"]);
|
|
30177
|
+
if (token === void 0) return false;
|
|
30178
|
+
return tokensEqual(token, expectedToken);
|
|
30179
|
+
}
|
|
30180
|
+
function emptyNotFound(res) {
|
|
30181
|
+
res.writeHead(404);
|
|
30182
|
+
res.end();
|
|
30148
30183
|
}
|
|
30149
30184
|
var MAX_REQUEST_BODY_BYTES = 8 * 1024 * 1024;
|
|
30150
30185
|
var RequestBodyError = class extends Error {
|
|
@@ -30249,8 +30284,13 @@ function createRoutes(context) {
|
|
|
30249
30284
|
);
|
|
30250
30285
|
return;
|
|
30251
30286
|
}
|
|
30252
|
-
const { pathname } = parseUrl(req);
|
|
30287
|
+
const { pathname: requestPath } = parseUrl(req);
|
|
30253
30288
|
const method = req.method || "GET";
|
|
30289
|
+
const pathname = scopedPathname(requestPath, context.csrfToken);
|
|
30290
|
+
if (pathname === void 0) {
|
|
30291
|
+
emptyNotFound(res);
|
|
30292
|
+
return;
|
|
30293
|
+
}
|
|
30254
30294
|
if (method === "POST") {
|
|
30255
30295
|
const origin = headerValue3(req.headers.origin);
|
|
30256
30296
|
if (!isAllowedEditorOrigin(origin, context.boundPort)) {
|
|
@@ -30271,7 +30311,8 @@ function createRoutes(context) {
|
|
|
30271
30311
|
}
|
|
30272
30312
|
if (pathname === "/editor.html" && method === "GET") {
|
|
30273
30313
|
res.writeHead(200, {
|
|
30274
|
-
"Content-Type": "text/html; charset=utf-8"
|
|
30314
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
30315
|
+
"Referrer-Policy": "no-referrer"
|
|
30275
30316
|
});
|
|
30276
30317
|
res.end(context.editorHtml);
|
|
30277
30318
|
return;
|
|
@@ -31016,18 +31057,13 @@ async function editCommand(args, options = {}) {
|
|
|
31016
31057
|
}
|
|
31017
31058
|
context.boundPort = server.port;
|
|
31018
31059
|
const serverUrl = `http://127.0.0.1:${server.port}`;
|
|
31019
|
-
const editorUrl = `${serverUrl}/editor.html`;
|
|
31060
|
+
const editorUrl = `${serverUrl}${editorSessionPath(csrfToken, "/editor.html")}`;
|
|
31020
31061
|
const {
|
|
31021
31062
|
bundle: editorBundlePath,
|
|
31022
31063
|
css: editorCssPath,
|
|
31023
31064
|
fonts: editorFontsPath
|
|
31024
31065
|
} = editorAssetPaths();
|
|
31025
|
-
context.editorHtml = generateEditorHtml(
|
|
31026
|
-
serverUrl,
|
|
31027
|
-
csrfToken,
|
|
31028
|
-
filePath,
|
|
31029
|
-
!!aiClient
|
|
31030
|
-
);
|
|
31066
|
+
context.editorHtml = generateEditorHtml(filePath, !!aiClient);
|
|
31031
31067
|
context.editorBundlePath = editorBundlePath;
|
|
31032
31068
|
context.editorCssPath = editorCssPath;
|
|
31033
31069
|
context.editorFontsPath = editorFontsPath;
|
|
@@ -31092,7 +31128,7 @@ async function ensureCivilizationProjectName(storage, filePath, data) {
|
|
|
31092
31128
|
await storage.update(filePath, named);
|
|
31093
31129
|
return named;
|
|
31094
31130
|
}
|
|
31095
|
-
function generateEditorHtml(
|
|
31131
|
+
function generateEditorHtml(filePath, aiConfigured) {
|
|
31096
31132
|
const aiAssistHandler = aiConfigured ? `
|
|
31097
31133
|
aiAssist: async function(request) {
|
|
31098
31134
|
const res = await fetch(apiBase + '/api/ai/assist', {
|
|
@@ -31121,7 +31157,7 @@ function generateEditorHtml(apiBase, csrfToken, filePath, aiConfigured) {
|
|
|
31121
31157
|
<meta charset="UTF-8">
|
|
31122
31158
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31123
31159
|
<title>Rogatio Editor</title>
|
|
31124
|
-
<link rel="stylesheet" href="
|
|
31160
|
+
<link rel="stylesheet" href="vendor/editor.css" />
|
|
31125
31161
|
<style>
|
|
31126
31162
|
html, body { margin: 0; min-height: 100%; }
|
|
31127
31163
|
body {
|
|
@@ -31149,14 +31185,16 @@ function generateEditorHtml(apiBase, csrfToken, filePath, aiConfigured) {
|
|
|
31149
31185
|
<div id="editor-doctor" hidden></div>
|
|
31150
31186
|
<div id="editor-root"></div>
|
|
31151
31187
|
<script type="importmap">
|
|
31152
|
-
{ "imports": { "@rogatio/editor": "
|
|
31188
|
+
{ "imports": { "@rogatio/editor": "./vendor/editor.js" } }
|
|
31153
31189
|
</script>
|
|
31154
31190
|
<script type="module">
|
|
31155
31191
|
import { createEditor } from '@rogatio/editor';
|
|
31156
31192
|
|
|
31157
31193
|
const root = document.getElementById('editor-root');
|
|
31158
|
-
const apiBase =
|
|
31159
|
-
|
|
31194
|
+
const apiBase = location.pathname.endsWith('/editor.html')
|
|
31195
|
+
? location.pathname.slice(0, -'/editor.html'.length)
|
|
31196
|
+
: location.pathname;
|
|
31197
|
+
const csrfToken = apiBase.slice(apiBase.lastIndexOf('/') + 1);
|
|
31160
31198
|
const filePath = ${embedJson(filePath)};
|
|
31161
31199
|
const doctorTitles = ${embedJson(editorPageTitles())};
|
|
31162
31200
|
const serverStoppedJson = ${embedJson(serializeEditorDiagnostics(editorServerStoppedReport(filePath)))};
|
|
@@ -33467,7 +33505,7 @@ function errorMessage(error) {
|
|
|
33467
33505
|
}
|
|
33468
33506
|
|
|
33469
33507
|
// packages/cli/src/commands/runtime.ts
|
|
33470
|
-
import { chmod, mkdir as mkdir5, rm as
|
|
33508
|
+
import { chmod, mkdir as mkdir5, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
|
|
33471
33509
|
import { dirname as dirname10, join as join9, resolve as resolve8 } from "node:path";
|
|
33472
33510
|
function generateHostWrapper(rogatioCommand, logPath) {
|
|
33473
33511
|
const parts = rogatioCommand.split(" ");
|
|
@@ -33578,11 +33616,11 @@ Note: reload the extension at chrome://extensions for the native host to be dete
|
|
|
33578
33616
|
case "uninstall": {
|
|
33579
33617
|
const uninstallResult = await controller.uninstall();
|
|
33580
33618
|
try {
|
|
33581
|
-
await
|
|
33619
|
+
await rm4(hostPath, { force: true });
|
|
33582
33620
|
} catch {
|
|
33583
33621
|
}
|
|
33584
33622
|
try {
|
|
33585
|
-
await
|
|
33623
|
+
await rm4(join9(installRoot, "runtime-host.log"), { force: true });
|
|
33586
33624
|
} catch {
|
|
33587
33625
|
}
|
|
33588
33626
|
return reportTrust(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rogatio/cli",
|
|
3
|
-
"version": "6.20.
|
|
3
|
+
"version": "6.20.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Local-first browser request and response rules — editor host, file verification, test runner, and runtime dispatch.",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"ajv": "8.20.0",
|
|
44
|
-
"koffi": "3.3.
|
|
44
|
+
"koffi": "3.3.3"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@rogatio/compiler": "workspace:*",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"@rogatio/requestly-import": "workspace:*",
|
|
51
51
|
"@rogatio/runtime": "workspace:*",
|
|
52
52
|
"@rogatio/schema": "workspace:*",
|
|
53
|
-
"@types/node": "26.6.
|
|
53
|
+
"@types/node": "26.6.5",
|
|
54
54
|
"typescript": "7.0.2",
|
|
55
|
-
"vitest": "5.0.
|
|
55
|
+
"vitest": "5.0.4"
|
|
56
56
|
}
|
|
57
57
|
}
|