@canonmsg/agent-sdk 12.4.1 → 12.6.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/README.md +37 -6
- package/dist/chunks/{chunk-6PKXASSK.js → chunk-5J4YPLNW.js} +394 -1687
- package/dist/chunks/{chunk-NXL4C2R6.js → chunk-DAJ7L7TN.js} +954 -429
- package/dist/chunks/chunk-FVYKLH6I.js +1475 -0
- package/dist/chunks/chunk-HH7KR22P.js +406 -0
- package/dist/chunks/chunk-QK37HAHC.js +118 -0
- package/dist/chunks/{chunk-GWQF4WFT.js → chunk-S7BV74DN.js} +734 -812
- package/dist/chunks/{mls-crypto-LGPCXM3K.js → mls-crypto-4DZT2UFH.js} +2 -1
- package/dist/cli.js +70 -32
- package/dist/drivers/acp.d.ts +56 -0
- package/dist/drivers/codex.d.ts +1 -2
- package/dist/drivers/connector.d.ts +5 -4
- package/dist/drivers/subprocess.d.ts +35 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -3
- package/dist/internal/backend-contracts/connectorRelay.d.ts +34 -0
- package/dist/internal/backend-contracts/endpoint.d.ts +1 -1
- package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +2 -2
- package/dist/internal/backend-contracts/index.d.ts +1 -0
- package/dist/internal/backend-contracts/loggedOutSessions.d.ts +7 -0
- package/dist/internal/backend-contracts/mlsDevCredential.d.ts +3 -0
- package/dist/internal/endpoint/engine.d.ts +8 -1
- package/dist/internal/endpoint/mls-dev-client.d.ts +11 -0
- package/dist/internal/endpoint/mls-dev-text.d.ts +3 -0
- package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +5 -0
- package/dist/internal/endpoint/node.d.ts +1 -0
- package/dist/media.js +4 -2
- package/dist/mls/darwin-arm64/canon-mls-custody +0 -0
- package/dist/mls/linux-arm64/canon-mls-custody +0 -0
- package/dist/mls/linux-x64/canon-mls-custody +0 -0
- package/dist/mls/wasm/canon_mls_wasm.js +363 -0
- package/dist/mls/wasm/canon_mls_wasm_bg.wasm +0 -0
- package/dist/mls-wasm-host.js +258 -0
- package/dist/runner/mls-bundled.d.ts +15 -2
- package/dist/runner/mls-managed.d.ts +10 -2
- package/dist/runner/mls.d.ts +7 -1
- package/dist/runner/relay.d.ts +3 -3
- package/dist/runner/runner.d.ts +18 -7
- package/dist/runner/types.d.ts +12 -4
- package/package.json +1 -1
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_OUTPUT,
|
|
3
|
+
NodeMlsDevCustodyError,
|
|
4
|
+
custodyFailure,
|
|
5
|
+
custodyValue,
|
|
6
|
+
frame,
|
|
7
|
+
requireValue,
|
|
8
|
+
typedNodeMlsDevCustody
|
|
9
|
+
} from "./chunk-FVYKLH6I.js";
|
|
10
|
+
import {
|
|
11
|
+
__name,
|
|
12
|
+
parseMlsDevDeviceContext
|
|
13
|
+
} from "./chunk-S7BV74DN.js";
|
|
14
|
+
|
|
15
|
+
// src/runner/home.ts
|
|
16
|
+
import { randomBytes } from "node:crypto";
|
|
17
|
+
import { closeSync, constants, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync as mkdirSync2, openSync, readSync, renameSync, rmSync, writeFileSync, writeSync } from "node:fs";
|
|
18
|
+
import { lstat, mkdir, realpath } from "node:fs/promises";
|
|
19
|
+
import { dirname as dirname2 } from "node:path";
|
|
20
|
+
|
|
21
|
+
// src/runner/windows-acl.ts
|
|
22
|
+
import { execFileSync } from "node:child_process";
|
|
23
|
+
import { mkdirSync, readdirSync, realpathSync } from "node:fs";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
|
+
var SYSTEM = "S-1-5-18";
|
|
26
|
+
var ADMINISTRATORS = "S-1-5-32-544";
|
|
27
|
+
var TRUSTED_INSTALLER = "S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464";
|
|
28
|
+
var ALIASES = { SY: SYSTEM, BA: ADMINISTRATORS };
|
|
29
|
+
var RIGHTS = {
|
|
30
|
+
GA: 268435456,
|
|
31
|
+
GX: 536870912,
|
|
32
|
+
GW: 1073741824,
|
|
33
|
+
GR: 2147483648,
|
|
34
|
+
SD: 65536,
|
|
35
|
+
RC: 131072,
|
|
36
|
+
WD: 262144,
|
|
37
|
+
WO: 524288,
|
|
38
|
+
FA: 2032127,
|
|
39
|
+
FR: 1179785,
|
|
40
|
+
FW: 1179926,
|
|
41
|
+
FX: 1179808,
|
|
42
|
+
CC: 1,
|
|
43
|
+
DC: 2,
|
|
44
|
+
LC: 4,
|
|
45
|
+
SW: 8,
|
|
46
|
+
RP: 16,
|
|
47
|
+
WP: 32,
|
|
48
|
+
DT: 64,
|
|
49
|
+
LO: 128,
|
|
50
|
+
CR: 256
|
|
51
|
+
};
|
|
52
|
+
var MOVE = 65536 | 262144 | 524288 | 64 | 268435456;
|
|
53
|
+
var WRITE = MOVE | 2 | 4 | 16 | 256 | 1073741824;
|
|
54
|
+
function rights(text) {
|
|
55
|
+
if (/^0x[0-9a-f]+$/i.test(text)) return Number(text);
|
|
56
|
+
let mask = 0;
|
|
57
|
+
for (const code of text.match(/../g) ?? []) mask |= RIGHTS[code] ?? 4294967295;
|
|
58
|
+
return mask;
|
|
59
|
+
}
|
|
60
|
+
__name(rights, "rights");
|
|
61
|
+
function sddlProblem(sddl, self, rule) {
|
|
62
|
+
const parts = /^O:(.+?)G:.+?D:([A-Z_]*)((?:\([^()]*\))*)(?:S:.*)?$/.exec(sddl.trim());
|
|
63
|
+
if (!parts || parts[2].includes("NO_ACCESS_CONTROL")) return "has an access list this cannot read";
|
|
64
|
+
const sid = /* @__PURE__ */ __name((value) => ALIASES[value] ?? (value === "LA" && self.endsWith("-500") ? self : value), "sid");
|
|
65
|
+
const trusted = /* @__PURE__ */ new Set([self, SYSTEM, ADMINISTRATORS, ...rule === "private" ? [] : [TRUSTED_INSTALLER]]);
|
|
66
|
+
if (!trusted.has(sid(parts[1]))) return `is owned by ${sid(parts[1])}`;
|
|
67
|
+
for (const [, type, flags, mask, , , account] of parts[3].matchAll(/\(([A-Z]+);([A-Z]*);([^;]*);([^;]*);([^;]*);([^;)]*)\)/g)) {
|
|
68
|
+
if (type === "D") continue;
|
|
69
|
+
if (type !== "A") return "has an access list this cannot read";
|
|
70
|
+
if (trusted.has(sid(account))) continue;
|
|
71
|
+
if (rule !== "private" && /^(..)*IO/.test(flags)) continue;
|
|
72
|
+
if (rule === "private" || rights(mask) & (rule === "ancestor" ? MOVE : WRITE)) return `gives ${sid(account)} access`;
|
|
73
|
+
}
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
__name(sddlProblem, "sddlProblem");
|
|
77
|
+
var tool = /* @__PURE__ */ __name((...path) => join(process.env.SystemRoot ?? "C:\\Windows", "System32", ...path), "tool");
|
|
78
|
+
var run = /* @__PURE__ */ __name((file, args, env) => execFileSync(
|
|
79
|
+
file,
|
|
80
|
+
args,
|
|
81
|
+
{ encoding: "utf8", windowsHide: true, stdio: ["ignore", "pipe", "pipe"], ...env ? { env } : {} }
|
|
82
|
+
), "run");
|
|
83
|
+
var current;
|
|
84
|
+
function userSid() {
|
|
85
|
+
current ??= /"(S-1-[\d-]+)"\s*$/.exec(run(tool("whoami.exe"), ["/user", "/fo", "csv", "/nh"]).trim())?.[1];
|
|
86
|
+
if (!current) throw new Error("Refusing to start: whoami did not name this user's SID.");
|
|
87
|
+
return current;
|
|
88
|
+
}
|
|
89
|
+
__name(userSid, "userSid");
|
|
90
|
+
function descriptors(paths) {
|
|
91
|
+
const output = run(
|
|
92
|
+
tool("WindowsPowerShell", "v1.0", "powershell.exe"),
|
|
93
|
+
[
|
|
94
|
+
"-NoLogo",
|
|
95
|
+
"-NoProfile",
|
|
96
|
+
"-NonInteractive",
|
|
97
|
+
"-Command",
|
|
98
|
+
'$ErrorActionPreference = "Stop"; foreach ($path in ($env:CANON_ACL_PATHS -split "`n")) { (Get-Acl -LiteralPath $path).Sddl }'
|
|
99
|
+
],
|
|
100
|
+
{ ...process.env, CANON_ACL_PATHS: paths.join("\n") }
|
|
101
|
+
);
|
|
102
|
+
const lines = output.split(/\r?\n/).filter(Boolean);
|
|
103
|
+
if (lines.length !== paths.length) throw new Error(`Refusing to start: could not read the access list of ${paths.join(", ")}.`);
|
|
104
|
+
return lines;
|
|
105
|
+
}
|
|
106
|
+
__name(descriptors, "descriptors");
|
|
107
|
+
function windowsAclProblems(paths) {
|
|
108
|
+
const self = userSid(), found = descriptors(paths.map(([path]) => path));
|
|
109
|
+
return paths.flatMap(([path, rule], index) => {
|
|
110
|
+
const problem = sddlProblem(found[index], self, rule);
|
|
111
|
+
return problem ? [`${path} ${problem}`] : [];
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
__name(windowsAclProblems, "windowsAclProblems");
|
|
115
|
+
function restrict(path, inherit) {
|
|
116
|
+
const grant = inherit ? ":(OI)(CI)F" : ":F";
|
|
117
|
+
run(tool("icacls.exe"), [path, "/inheritance:r", "/grant:r", `*${userSid()}${grant}`, `*${SYSTEM}${grant}`, `*${ADMINISTRATORS}${grant}`, "/Q"]);
|
|
118
|
+
}
|
|
119
|
+
__name(restrict, "restrict");
|
|
120
|
+
var restrictWindowsFile = /* @__PURE__ */ __name((file) => restrict(file, false), "restrictWindowsFile");
|
|
121
|
+
function assertPrivateWindowsFile(file) {
|
|
122
|
+
const [problem] = windowsAclProblems([[file, "private"], [dirname(file), "private"]]);
|
|
123
|
+
if (problem) throw new Error(`Refusing to start: ${problem}. It must be reachable only by this user, SYSTEM and Administrators.`);
|
|
124
|
+
}
|
|
125
|
+
__name(assertPrivateWindowsFile, "assertPrivateWindowsFile");
|
|
126
|
+
function secureWindowsDirectory(directory) {
|
|
127
|
+
mkdirSync(directory, { recursive: true });
|
|
128
|
+
restrict(directory, true);
|
|
129
|
+
const [problem] = windowsAclProblems([[directory, "private"]]);
|
|
130
|
+
if (problem) throw new Error(`Refusing to start: ${problem}. It must be reachable only by this user, SYSTEM and Administrators.`);
|
|
131
|
+
}
|
|
132
|
+
__name(secureWindowsDirectory, "secureWindowsDirectory");
|
|
133
|
+
function assertPrivateWindowsHome(home) {
|
|
134
|
+
const fail = /* @__PURE__ */ __name((problem2) => {
|
|
135
|
+
throw new Error(`Refusing to start: ${problem2}. CANON_HOME must be a local directory only this user, SYSTEM and Administrators can reach, whose parents no one else can replace, and hold nothing anyone else can reach. Remove what you did not put there. To limit one you own: icacls <directory> /inheritance:r /grant:r "%USERNAME%:(OI)(CI)F" *S-1-5-18:(OI)(CI)F *S-1-5-32-544:(OI)(CI)F /T`);
|
|
136
|
+
}, "fail");
|
|
137
|
+
if (!/^[a-z]:\\/i.test(home)) fail(`${home} is not on a local drive`);
|
|
138
|
+
if (mkdirSync(home, { recursive: true }) !== void 0) restrict(home, true);
|
|
139
|
+
const resolved = realpathSync.native(home);
|
|
140
|
+
if (resolved.toLowerCase() !== home.toLowerCase()) fail(`${home} is not canonical (it resolves to ${resolved})`);
|
|
141
|
+
const paths = [[home, "private"], ...readdirSync(home).map((name) => [join(home, name), "private"])];
|
|
142
|
+
for (let path = dirname(home); ; path = dirname(path)) {
|
|
143
|
+
paths.push([path, "ancestor"]);
|
|
144
|
+
if (path === dirname(path)) break;
|
|
145
|
+
}
|
|
146
|
+
const [problem] = windowsAclProblems(paths);
|
|
147
|
+
if (problem) fail(problem);
|
|
148
|
+
}
|
|
149
|
+
__name(assertPrivateWindowsHome, "assertPrivateWindowsHome");
|
|
150
|
+
|
|
151
|
+
// src/runner/home.ts
|
|
152
|
+
var runnerLockName = /* @__PURE__ */ __name((environmentId, agentId) => `runner-${environmentId}-${agentId}`.replace(/[^\w.-]/g, "_"), "runnerLockName");
|
|
153
|
+
function writeDurable(file, text) {
|
|
154
|
+
const directory = dirname2(file);
|
|
155
|
+
const created = mkdirSync2(directory, { recursive: true, mode: 448 });
|
|
156
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
157
|
+
const fd = openSync(temporary, "w", 384);
|
|
158
|
+
try {
|
|
159
|
+
writeFileSync(fd, text);
|
|
160
|
+
fsyncSync(fd);
|
|
161
|
+
} finally {
|
|
162
|
+
closeSync(fd);
|
|
163
|
+
}
|
|
164
|
+
renameSync(temporary, file);
|
|
165
|
+
if (process.platform === "win32") return;
|
|
166
|
+
const last = created ? dirname2(created) : directory;
|
|
167
|
+
for (let path = directory; ; path = dirname2(path)) {
|
|
168
|
+
const dir = openSync(path, "r");
|
|
169
|
+
try {
|
|
170
|
+
fsyncSync(dir);
|
|
171
|
+
} finally {
|
|
172
|
+
closeSync(dir);
|
|
173
|
+
}
|
|
174
|
+
if (path === last) break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
__name(writeDurable, "writeDurable");
|
|
178
|
+
function privateKey(file, bytes = 32) {
|
|
179
|
+
if (process.platform !== "win32") return keyFile(file, bytes);
|
|
180
|
+
if (existsSync(file)) {
|
|
181
|
+
assertPrivateWindowsFile(file);
|
|
182
|
+
return keyFile(file, bytes);
|
|
183
|
+
}
|
|
184
|
+
secureWindowsDirectory(dirname2(file));
|
|
185
|
+
const key = keyFile(file, bytes);
|
|
186
|
+
restrictWindowsFile(file);
|
|
187
|
+
assertPrivateWindowsFile(file);
|
|
188
|
+
return key;
|
|
189
|
+
}
|
|
190
|
+
__name(privateKey, "privateKey");
|
|
191
|
+
function keyFile(file, bytes = 32) {
|
|
192
|
+
const refuse = /* @__PURE__ */ __name(() => {
|
|
193
|
+
throw new Error(`Refusing to start: ${file} must be a regular file of ${bytes} bytes, owned by this user and private to it (0600).`);
|
|
194
|
+
}, "refuse");
|
|
195
|
+
const read = /* @__PURE__ */ __name(() => {
|
|
196
|
+
let fd2;
|
|
197
|
+
try {
|
|
198
|
+
if (process.platform === "win32" && lstatSync(file).isSymbolicLink()) refuse();
|
|
199
|
+
fd2 = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
200
|
+
} catch (error) {
|
|
201
|
+
return error.code === "ENOENT" ? void 0 : refuse();
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const info = fstatSync(fd2);
|
|
205
|
+
const uid = process.getuid?.();
|
|
206
|
+
if (!info.isFile() || info.size !== bytes || uid !== void 0 && (info.uid !== uid || (info.mode & 63) !== 0)) refuse();
|
|
207
|
+
const key = Buffer.alloc(bytes);
|
|
208
|
+
if (readSync(fd2, key, 0, bytes, 0) !== bytes) refuse();
|
|
209
|
+
return key;
|
|
210
|
+
} finally {
|
|
211
|
+
closeSync(fd2);
|
|
212
|
+
}
|
|
213
|
+
}, "read");
|
|
214
|
+
const existing = read();
|
|
215
|
+
if (existing) return existing;
|
|
216
|
+
const directory = dirname2(file);
|
|
217
|
+
mkdirSync2(directory, { recursive: true, mode: 448 });
|
|
218
|
+
const temporary = `${file}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
|
|
219
|
+
const fd = openSync(temporary, "wx", 384);
|
|
220
|
+
try {
|
|
221
|
+
writeSync(fd, randomBytes(bytes));
|
|
222
|
+
fsyncSync(fd);
|
|
223
|
+
} finally {
|
|
224
|
+
closeSync(fd);
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
linkSync(temporary, file);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (error.code !== "EEXIST") throw error;
|
|
230
|
+
} finally {
|
|
231
|
+
rmSync(temporary, { force: true });
|
|
232
|
+
}
|
|
233
|
+
if (process.platform !== "win32") {
|
|
234
|
+
const dir = openSync(directory, "r");
|
|
235
|
+
try {
|
|
236
|
+
fsyncSync(dir);
|
|
237
|
+
} finally {
|
|
238
|
+
closeSync(dir);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return read() ?? refuse();
|
|
242
|
+
}
|
|
243
|
+
__name(keyFile, "keyFile");
|
|
244
|
+
async function assertPrivateHome(home) {
|
|
245
|
+
if (process.platform === "win32") return assertPrivateWindowsHome(home);
|
|
246
|
+
const uid = process.getuid();
|
|
247
|
+
const fail = /* @__PURE__ */ __name((path, problem) => {
|
|
248
|
+
throw new Error(`Refusing to start: ${path} ${problem}. CANON_HOME must be a private 0700 directory whose ancestors only its owner or root can modify.`);
|
|
249
|
+
}, "fail");
|
|
250
|
+
await mkdir(home, { recursive: true, mode: 448 }).catch(() => void 0);
|
|
251
|
+
let resolved;
|
|
252
|
+
try {
|
|
253
|
+
resolved = await realpath(home);
|
|
254
|
+
} catch {
|
|
255
|
+
return fail(home, "does not exist");
|
|
256
|
+
}
|
|
257
|
+
if (resolved !== home) fail(home, `is not canonical (it resolves to ${resolved})`);
|
|
258
|
+
const stat = await lstat(home);
|
|
259
|
+
if (!stat.isDirectory() || stat.uid !== uid) fail(home, "is not a directory owned by this user");
|
|
260
|
+
if (stat.mode & 63) fail(home, `is accessible to other users (mode ${(stat.mode & 511).toString(8)})`);
|
|
261
|
+
for (let path = dirname2(home); ; path = dirname2(path)) {
|
|
262
|
+
const ancestor = await lstat(path);
|
|
263
|
+
if (ancestor.uid !== uid && ancestor.uid !== 0) fail(path, "is owned by another user");
|
|
264
|
+
if (ancestor.mode & 18) fail(path, "is writable by other users");
|
|
265
|
+
if (path === dirname2(path)) return;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
__name(assertPrivateHome, "assertPrivateHome");
|
|
269
|
+
|
|
270
|
+
// src/runner/mls-wasm.ts
|
|
271
|
+
import { spawn } from "node:child_process";
|
|
272
|
+
var WASM_HOST_CONFIGURE = 0;
|
|
273
|
+
var WASM_HOST_PROVISION = 5;
|
|
274
|
+
var lengthPrefixed = /* @__PURE__ */ __name((message) => {
|
|
275
|
+
const length = Buffer.alloc(4);
|
|
276
|
+
length.writeUInt32BE(message.length);
|
|
277
|
+
return Buffer.concat([length, message]);
|
|
278
|
+
}, "lengthPrefixed");
|
|
279
|
+
function wasmMlsCustody(options) {
|
|
280
|
+
const { core, context, keyPath, issuerKeys, receiptKeys, timeoutMs = 3e4 } = options;
|
|
281
|
+
const configuration = frame(WASM_HOST_CONFIGURE, [JSON.stringify({
|
|
282
|
+
context,
|
|
283
|
+
keyPath,
|
|
284
|
+
issuerKeys,
|
|
285
|
+
receiptKeys,
|
|
286
|
+
wasmPath: core.wasmPath,
|
|
287
|
+
wasmSha256: core.wasmSha256,
|
|
288
|
+
gluePath: core.gluePath,
|
|
289
|
+
glueSha256: core.glueSha256
|
|
290
|
+
})]);
|
|
291
|
+
const jobs = /* @__PURE__ */ new Set();
|
|
292
|
+
let closed = false, host;
|
|
293
|
+
function start() {
|
|
294
|
+
const child = spawn(process.execPath, [core.hostPath], {
|
|
295
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
296
|
+
windowsHide: true,
|
|
297
|
+
env: process.platform === "win32" ? { SystemRoot: process.env.SystemRoot } : {}
|
|
298
|
+
});
|
|
299
|
+
let done = /* @__PURE__ */ __name(() => void 0, "done");
|
|
300
|
+
const started = { child, waiting: [], exited: new Promise((resolve) => {
|
|
301
|
+
done = resolve;
|
|
302
|
+
}) };
|
|
303
|
+
let buffered = Buffer.alloc(0);
|
|
304
|
+
child.stdout.on("data", (chunk) => {
|
|
305
|
+
buffered = Buffer.concat([buffered, chunk]);
|
|
306
|
+
while (buffered.length >= 4) {
|
|
307
|
+
const length = buffered.readUInt32BE(0);
|
|
308
|
+
if (length > MAX_OUTPUT) {
|
|
309
|
+
child.kill("SIGKILL");
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (buffered.length < 4 + length) return;
|
|
313
|
+
started.waiting.shift()?.resolve(buffered.subarray(4, 4 + length));
|
|
314
|
+
buffered = buffered.subarray(4 + length);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
const gone = /* @__PURE__ */ __name(() => {
|
|
318
|
+
if (host === started) host = void 0;
|
|
319
|
+
for (const waiter of started.waiting.splice(0)) waiter.reject(new NodeMlsDevCustodyError());
|
|
320
|
+
done();
|
|
321
|
+
}, "gone");
|
|
322
|
+
child.once("error", gone);
|
|
323
|
+
child.once("close", gone);
|
|
324
|
+
child.stdin.on("error", () => void 0);
|
|
325
|
+
started.waiting.push({ resolve() {
|
|
326
|
+
}, reject() {
|
|
327
|
+
} });
|
|
328
|
+
child.stdin.write(lengthPrefixed(configuration));
|
|
329
|
+
return started;
|
|
330
|
+
}
|
|
331
|
+
__name(start, "start");
|
|
332
|
+
function call(message, signal) {
|
|
333
|
+
requireValue(!closed && !signal.aborted);
|
|
334
|
+
const current2 = host ??= start();
|
|
335
|
+
return new Promise((resolve, reject) => {
|
|
336
|
+
const kill = /* @__PURE__ */ __name(() => {
|
|
337
|
+
if (host === current2) host = void 0;
|
|
338
|
+
current2.child.kill("SIGKILL");
|
|
339
|
+
}, "kill");
|
|
340
|
+
signal.addEventListener("abort", kill, { once: true });
|
|
341
|
+
current2.waiting.push({
|
|
342
|
+
resolve(output) {
|
|
343
|
+
signal.removeEventListener("abort", kill);
|
|
344
|
+
resolve(output);
|
|
345
|
+
},
|
|
346
|
+
reject(error) {
|
|
347
|
+
signal.removeEventListener("abort", kill);
|
|
348
|
+
reject(error);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
current2.child.stdin.write(lengthPrefixed(message));
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
__name(call, "call");
|
|
355
|
+
async function job(work) {
|
|
356
|
+
const controller = new AbortController(), timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
357
|
+
jobs.add(controller);
|
|
358
|
+
try {
|
|
359
|
+
requireValue(!closed);
|
|
360
|
+
const cancelled = new Promise((_, reject) => {
|
|
361
|
+
controller.signal.addEventListener("abort", () => reject(new NodeMlsDevCustodyError()), { once: true });
|
|
362
|
+
});
|
|
363
|
+
return await Promise.race([work(controller.signal), cancelled]);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return custodyFailure(error);
|
|
366
|
+
} finally {
|
|
367
|
+
clearTimeout(timer);
|
|
368
|
+
controller.abort();
|
|
369
|
+
jobs.delete(controller);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
__name(job, "job");
|
|
373
|
+
const custody = typedNodeMlsDevCustody(
|
|
374
|
+
job,
|
|
375
|
+
async (operation, values, signal) => custodyValue(await call(frame(operation, values), signal), operation),
|
|
376
|
+
async () => {
|
|
377
|
+
closed = true;
|
|
378
|
+
for (const running of jobs) running.abort();
|
|
379
|
+
const current2 = host;
|
|
380
|
+
if (current2) {
|
|
381
|
+
current2.child.kill("SIGKILL");
|
|
382
|
+
await current2.exited;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
return Object.freeze({ ...custody, provision: /* @__PURE__ */ __name((device) => job(async (signal) => {
|
|
387
|
+
const message = frame(WASM_HOST_PROVISION, [JSON.stringify(parseMlsDevDeviceContext(device))]);
|
|
388
|
+
requireValue(custodyValue(await call(message, signal), WASM_HOST_PROVISION) === null);
|
|
389
|
+
}), "provision") });
|
|
390
|
+
}
|
|
391
|
+
__name(wasmMlsCustody, "wasmMlsCustody");
|
|
392
|
+
|
|
393
|
+
export {
|
|
394
|
+
windowsAclProblems,
|
|
395
|
+
restrictWindowsFile,
|
|
396
|
+
assertPrivateWindowsFile,
|
|
397
|
+
secureWindowsDirectory,
|
|
398
|
+
runnerLockName,
|
|
399
|
+
writeDurable,
|
|
400
|
+
privateKey,
|
|
401
|
+
keyFile,
|
|
402
|
+
assertPrivateHome,
|
|
403
|
+
WASM_HOST_CONFIGURE,
|
|
404
|
+
WASM_HOST_PROVISION,
|
|
405
|
+
wasmMlsCustody
|
|
406
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name,
|
|
3
|
+
decodeCanonicalMlsBase64Url,
|
|
4
|
+
isMlsEnvironmentId,
|
|
5
|
+
mlsEnvironmentOfIssuerKeyVersion,
|
|
6
|
+
mlsEnvironmentOfReceiptKeyVersion,
|
|
7
|
+
parseMlsDevCredentialReceipt,
|
|
8
|
+
parseMlsDevCredentialToken,
|
|
9
|
+
parseMlsDevEnrollmentPrincipal,
|
|
10
|
+
parseMlsDevIssuerKeyVersion,
|
|
11
|
+
parseMlsDevReceiptKeyVersion
|
|
12
|
+
} from "./chunk-S7BV74DN.js";
|
|
13
|
+
|
|
14
|
+
// ../endpoint/src/mls-crypto.ts
|
|
15
|
+
import { ed25519 } from "@noble/curves/ed25519.js";
|
|
16
|
+
|
|
17
|
+
// ../endpoint/src/runtime-api-origins.generated.ts
|
|
18
|
+
var RUNTIME_API_ORIGINS = {
|
|
19
|
+
"canon-dev-v1": "https://dev.api.canonmail.com",
|
|
20
|
+
"canon-prod-v1": "https://api.canonmail.com"
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// ../endpoint/src/mls-crypto.ts
|
|
24
|
+
function mlsDevDeviceHttpConfiguration(environmentId) {
|
|
25
|
+
if (!isMlsEnvironmentId(environmentId)) invalid();
|
|
26
|
+
const origin = RUNTIME_API_ORIGINS[environmentId];
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
enrollmentUrl: `${origin}/mls/enrollment`,
|
|
29
|
+
credentialUrl: `${origin}/mls/credential`,
|
|
30
|
+
reconciliationUrl: `${origin}/mls/reconciliation`,
|
|
31
|
+
keyPackagesUrl: `${origin}/mls/key-packages`,
|
|
32
|
+
trustedOrigins: Object.freeze([origin])
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
__name(mlsDevDeviceHttpConfiguration, "mlsDevDeviceHttpConfiguration");
|
|
36
|
+
function invalid() {
|
|
37
|
+
throw new Error("MLS_DEV_CLIENT_CONFIGURATION_INVALID");
|
|
38
|
+
}
|
|
39
|
+
__name(invalid, "invalid");
|
|
40
|
+
function record(value, keys) {
|
|
41
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) invalid();
|
|
42
|
+
const fields = Object.getOwnPropertyDescriptors(value);
|
|
43
|
+
if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key) || !Object.hasOwn(fields[key], "value"))) invalid();
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
__name(record, "record");
|
|
47
|
+
function primeOrderPoint(value) {
|
|
48
|
+
const point = ed25519.Point.fromBytes(value, false);
|
|
49
|
+
return !point.isSmallOrder() && point.isTorsionFree();
|
|
50
|
+
}
|
|
51
|
+
__name(primeOrderPoint, "primeOrderPoint");
|
|
52
|
+
function issuerKeys(value, environmentId, receipt = false) {
|
|
53
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 8) invalid();
|
|
54
|
+
const seen = /* @__PURE__ */ new Set();
|
|
55
|
+
let environment = environmentId;
|
|
56
|
+
return Object.freeze(Array.from(value, (item) => {
|
|
57
|
+
const key = record(item, ["keyVersion", "signaturePublicKey"]);
|
|
58
|
+
const keyVersion = receipt ? parseMlsDevReceiptKeyVersion(key.keyVersion, environment) : parseMlsDevIssuerKeyVersion(key.keyVersion, environment);
|
|
59
|
+
environment ??= receipt ? mlsEnvironmentOfReceiptKeyVersion(keyVersion) : mlsEnvironmentOfIssuerKeyVersion(keyVersion);
|
|
60
|
+
if (seen.has(keyVersion) || typeof key.signaturePublicKey !== "string" || !primeOrderPoint(decodeCanonicalMlsBase64Url(key.signaturePublicKey, 32))) invalid();
|
|
61
|
+
seen.add(keyVersion);
|
|
62
|
+
return Object.freeze({ keyVersion, signaturePublicKey: key.signaturePublicKey });
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
__name(issuerKeys, "issuerKeys");
|
|
66
|
+
function parseMlsDevClientConfiguration(value) {
|
|
67
|
+
try {
|
|
68
|
+
const listed = !!value && typeof value === "object" && Object.hasOwn(value, "principalIds");
|
|
69
|
+
const input = record(value, ["version", "environmentId", "enabled", ...listed ? ["principalIds"] : [], "issuerKeys", "receiptKeys"]);
|
|
70
|
+
if (input.version !== 1 || !isMlsEnvironmentId(input.environmentId) || input.enabled !== true || listed && (!Array.isArray(input.principalIds) || input.principalIds.length < 1 || input.principalIds.length > 100)) invalid();
|
|
71
|
+
const environmentId = input.environmentId;
|
|
72
|
+
const principalIds = listed ? Array.from(input.principalIds, (id) => parseMlsDevEnrollmentPrincipal({ type: "human", id }).id) : void 0;
|
|
73
|
+
if (principalIds && new Set(principalIds).size !== principalIds.length) invalid();
|
|
74
|
+
return Object.freeze({
|
|
75
|
+
version: 1,
|
|
76
|
+
environmentId,
|
|
77
|
+
enabled: true,
|
|
78
|
+
...principalIds ? { principalIds: Object.freeze(principalIds) } : {},
|
|
79
|
+
issuerKeys: issuerKeys(input.issuerKeys, environmentId),
|
|
80
|
+
receiptKeys: issuerKeys(input.receiptKeys, environmentId, true)
|
|
81
|
+
});
|
|
82
|
+
} catch {
|
|
83
|
+
return invalid();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
__name(parseMlsDevClientConfiguration, "parseMlsDevClientConfiguration");
|
|
87
|
+
function createMlsDevCredentialVerifier(options) {
|
|
88
|
+
let trusted;
|
|
89
|
+
try {
|
|
90
|
+
trusted = new Map(issuerKeys(options.issuerKeys).map((key) => [key.keyVersion, key.signaturePublicKey]));
|
|
91
|
+
} catch {
|
|
92
|
+
return invalid();
|
|
93
|
+
}
|
|
94
|
+
const clock = options.clock ?? Date.now;
|
|
95
|
+
return async (value) => {
|
|
96
|
+
try {
|
|
97
|
+
const receipt = parseMlsDevCredentialReceipt(value);
|
|
98
|
+
const parsed = parseMlsDevCredentialToken(receipt.credential);
|
|
99
|
+
const key = trusted.get(parsed.unsigned.keyVersion);
|
|
100
|
+
const now = clock();
|
|
101
|
+
const { issuedAtMs, expiresAtMs } = parsed.unsigned.claims;
|
|
102
|
+
if (!key || !Number.isSafeInteger(now) || now < issuedAtMs || now >= expiresAtMs) return false;
|
|
103
|
+
const publicKey = decodeCanonicalMlsBase64Url(key, 32);
|
|
104
|
+
const signature = decodeCanonicalMlsBase64Url(parsed.signature, 64);
|
|
105
|
+
if (!primeOrderPoint(publicKey) || !primeOrderPoint(signature.subarray(0, 32))) return false;
|
|
106
|
+
return ed25519.verify(signature, new TextEncoder().encode(parsed.signingInput), publicKey, { zip215: false });
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
__name(createMlsDevCredentialVerifier, "createMlsDevCredentialVerifier");
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
mlsDevDeviceHttpConfiguration,
|
|
116
|
+
parseMlsDevClientConfiguration,
|
|
117
|
+
createMlsDevCredentialVerifier
|
|
118
|
+
};
|