@ai-ecoverse/slicc-kernel 1.136.2 → 1.136.4

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 CHANGED
@@ -327,7 +327,7 @@ On top of preview1 a WASI program gets:
327
327
  A kernel without the imports answers `ENOSYS`, and the libc reports `SI_USER` with pid and uid 0, as before. **SIGCHLD:** with `SIGCHLD` set to `SIG_IGN` (by the program, or inherited), or with `SA_NOCLDWAIT` in the flags `slicc.sigaction_set` gets for `SIGCHLD`, an exited child is reaped at once and never stays a zombie: `waitpid` for it is `ECHILD`, and `wait` blocks until every child has exited, then fails with `ECHILD`, as on Linux. `SIGCHLD` is still sent. An explicit `SIG_DFL` turns it back off. A child that a process waits on for `exec`, or that `kernel.run` and the run calls collect, is never reaped that way.
328
328
  - **Pipes** have no file type for WASI and WASIX programs' `fstat` (`st_mode` type 0), because WASI has no FIFO file type. A socket type, as kernels 1.47.3 to 1.48.1 gave them, made Ruby treat a `popen` pipe as a socket (`close_write` then never ends the child's input). The kernel's `slicc_fs.fd_mode` answers `S_IFIFO | 0600` (Linux numbering) for a pipe, and wasix-sysroot 2025.9.30-21 and later take the type from it, so programs built on it see a FIFO (`S_ISFIFO`); older libcs keep only the permission bits. Until then Python's asyncio refuses a pipe for a subprocess (`Pipe transport is only for pipes, sockets and character devices`). They are read and written as pipes; socket calls on them fail with `ENOTSOCK`, and `isatty()` is false. Emscripten programs, bash included, see a FIFO. The same holds for any kernel descriptor that is neither seekable nor a terminal or device, such as captured output.
329
329
  - **Sockets**: the WASIX socket calls (`sock_open`, `sock_pair` for an `AF_UNIX` stream or datagram pair, `bind`, `listen`, `connect`, `accept`, options, local and peer names) are the kernel's loopback sockets, and preview1's `sock_recv` / `sock_send` / `sock_shutdown` work on them and on inherited ones. Writing to a socket whose peer is gone fails with `EPIPE`, through `fd_write` as through `sock_send`, as if `MSG_NOSIGNAL` were set. A write to a pipe, FIFO or terminal whose reader is gone follows the program's `SIGPIPE` disposition, as the kernel knows it (#810): at the default it ends the program with status 141; ignored (also an ignore inherited across `exec` and spawn, as bash's `trap '' PIPE` passes on), the write fails with `EPIPE`; with a handler, the handler runs on the thread that wrote and then the write fails with `EPIPE` (#820); blocked, `SIGPIPE` stays pending and the write fails with `EPIPE`. An Emscripten program's write to a pipe, and its `send` without `MSG_NOSIGNAL`, follow the same rules, except that the kernel doesn't see an Emscripten program's signal mask, so a blocked `SIGPIPE` there still ends it. A WASIX program's libc reads an inherited ignore back through `slicc.sigaction_inherited` (wasix-sysroot -32 and later; see below). An Emscripten program's shim reads it from `Module.sliccKernel.sigInherited()` (#965, homescoop#973). That's a mask with bit n set for signal n (not bit n-1, as the WASIX import has it) holding only inherited `SIG_IGN`s, never `SIGKILL` or `SIGSTOP`. The shim seeds its `sigaction` table from it in a constructor that runs before any user constructor. The kernel puts the function on `Module.sliccKernel` before the glue runs constructors, ahead of the rest of `sliccKernel`. On a kernel without the function it is undefined, and the shim keeps the defaults. HTTP and HTTPS leave through the realm proxy that `https_proxy` names, as for any other program; `resolve` answers `/etc/hosts` names and literal addresses, and the [uplink](#uplink)'s names. `sock_open(SOCK_DGRAM)` on `AF_INET`/`AF_INET6` gives a [datagram socket](#datagram-sockets-udp); `sock_send_to` and `sock_recv_from` carry its addresses, and `RECV_DATA_TRUNCATED` is set in `ro_flags` when a datagram did not fit.
330
- - **Diagnostics**: a trap ends the program with 134 and its message on stderr; with `SLICC_WASM_BACKTRACE=1` the wasm frames follow, named from the module's name section, or from a sidecar for a module shipped without one (`<module>.names` beside it, or the same path in a `<package>-names` package). `SLICC_WASI_STATS=1` makes a program print its calls, counted and timed, as it ends.
330
+ - **Diagnostics**: a trap ends the program with 134 and its message on stderr; a program that crashes its worker otherwise (a JS stack overflow, `RangeError: Maximum call stack size exceeded`, or any other uncaught error) ends with 70, and its message goes to the process's own fd 2 too, so `2>` catches it (#1237), or to the session's stderr when it has no fd 2 to write; with `SLICC_WASM_BACKTRACE=1` the wasm frames follow, named from the module's name section, or from a sidecar for a module shipped without one (`<module>.names` beside it, or the same path in a `<package>-names` package). `SLICC_WASI_STATS=1` makes a program print its calls, counted and timed, as it ends.
331
331
 
332
332
  ### Program imports
333
333
 
@@ -505,10 +505,20 @@ The module's default export (or its `main` export) is called with a context and
505
505
  - `AUTH LOGIN`: the password line (the second line, or the first after `AUTH LOGIN <user>`) is decoded, substituted (`auth-login`) and re-encoded; the user line goes out as written.
506
506
  - Every other line goes out as written. After the first `AUTH`, or once `MAIL`, `RCPT`, `DATA` or `BDAT` is seen, so is the rest of the session. A line still unfinished after 2 s, or longer than 4 KiB, is sent as written, and so is the rest.
507
507
  - **pg:** a session that starts with a protocol 3 `StartupMessage` (after `sslmode`'s `SSLRequest` upgrade, or with direct TLS) is followed message by message. The only position is the **`PasswordMessage` answering `AuthenticationCleartextPassword` (`R` 3)**, as the program read it from the server. The password is substituted (`password-cleartext`) and the message is framed again. **SCRAM-SHA-256** (`R` 10, the pg 14+ default) works with an unmodified client:
508
- - **What the server is offered:** in a session that can substitute, the kernel offers the program only `SCRAM-SHA-256`, never `-PLUS`. The program's client computes its proof from the placeholder. Dropping `-PLUS` costs nothing here: the kernel terminates and verifies the TLS itself, so channel binding would only bind to the kernel's own session. A client that **requires** channel binding (libpq's `channel_binding=require`) refuses the connection and never gets the value, so leave it at `prefer` or `disable` for sealed SCRAM.
508
+ - **What the program is offered:** the server's mechanisms. `SCRAM-SHA-256-PLUS` is offered only when the kernel can bind to the server: a verified session whose leaf certificate's signature hash is SHA-256, SHA-384 or SHA-512, with MD5 and SHA-1 counting as SHA-256 (RFC 5929 §4.1). With any other hash (RSA-PSS or Ed25519, say), `-PLUS` is dropped. So libpq's default `channel_binding=prefer`, `require`, and node-postgres' `enableChannelBinding` all work.
509
+ - **Channel binding (`tls-server-end-point`):** the kernel changes only what the server would refuse.
510
+ - `n,,` goes out as written.
511
+ - `y,,` goes out as written to a server that didn't offer `-PLUS`. Otherwise it becomes `SCRAM-SHA-256-PLUS` with `p=tls-server-end-point,,` (or `n,,` when the kernel can't bind), because pg refuses `y,,` from a client after offering `-PLUS`.
512
+ - `p=tls-server-end-point,,` keeps its header. Its `c=` binding data becomes the hash of the leaf the kernel verified, when the program's differs.
513
+ - Any other `p=` type is changed to the kernel's choice.
514
+
515
+ So a program that asks for binding is bound to the real server. A program using its own password in such a session still authenticates whenever nothing had to change: `n,,`, `y,,` without `-PLUS`, or a right `p=`.
516
+ - **The vault:** substitution with `-PLUS` (a program's `p=tls-server-end-point`) needs slicc-vault ≥ 1.15.1. An older vault refuses `p=` with `EINVAL` and fails closed. A program's `p=tls-unique` or `p=tls-exporter` fails in a substituting session either way: the vault refuses it with a placeholder (`EINVAL`), and with the program's own password, the kernel's rewrite to `tls-server-end-point` invalidates its proof. PostgreSQL supports only `tls-server-end-point` anyway.
517
+ - **Two auth messages:** the vault and the check against the placeholder see the program's own messages (P). The server sees the kernel's (S), with its header and `c=`. The real proof is computed over S, and the server's signature is verified over S before the program gets the placeholder's signature over P.
509
518
  - **The match:** the kernel sends the three SCRAM messages and that proof to the vault (`scram-sha-256`, with no placeholders), and the vault names the one bound placeholder whose proof matches.
510
519
  - **The exchange:** the kernel recomputes the proof from the value and sends that instead. It checks the server's final signature against the value **before** it hands the program the signature its own placeholder-based check expects. A server whose signature doesn't verify, or that answers with an error, ends the session (`EPROTO`) without passing it on. The salted password and the derived keys are wiped after the exchange.
511
- - **Requirements:** only the plain GS2 header (`n,,`) and the bare RFC 5802 messages; at most 100000 iterations.
520
+ - **Requirements:** a GS2 header without an authorization identity (`n,,`, `y,,` or `p=<type>,,`), whose `c=` starts with it, and the bare RFC 5802 messages; at most 100000 iterations.
521
+ - **A stale vault value:** on a verified session, a value the server no longer accepts makes its final signature fail to verify. The program sees `EPROTO`, not pg's `28P01`. This is by design: the kernel never passes on a signature it couldn't check.
512
522
 
513
523
  Anything else goes out as written: the startup, an MD5 (`R` 5) answer, every later message, and a frame over 16 KiB. A server whose `pg_hba` asks for MD5 gets the placeholder, and refuses.
514
524
  - **Vault anchors** (§3.19.6) are for private-CA services such as managed pg and corporate APIs.
@@ -1266,6 +1276,16 @@ Not part of the run yet: the rest of bios's setup (the `rest` shard, the upgrade
1266
1276
 
1267
1277
  The WASIX fixtures of `test/integration/epoll.test.mjs` (#674), under `test/integration/fixtures/`, are committed with their sources, build scripts and built wasm.
1268
1278
 
1279
+ ### `pg-fixture` with `plus`
1280
+
1281
+ `test/integration/pg-fixture.mjs`, `pgFixture(t, cert, password, { plus: true })`: pg-gateway's SCRAM-SHA-256 server, which offers `SCRAM-SHA-256-PLUS` too and refuses as PostgreSQL's `auth-scram.c` does (jsh#531):
1282
+ - `y,,` after it offered `-PLUS`: "SCRAM channel binding negotiation error";
1283
+ - `p=` with `SCRAM-SHA-256`, and `SCRAM-SHA-256-PLUS` without `p=`;
1284
+ - a channel-binding type other than `tls-server-end-point`;
1285
+ - a `c=` other than base64 of the GS2 header plus, for `-PLUS`, the SHA-256 of its leaf.
1286
+
1287
+ The refusals and the client-final messages are recorded in `seen.plus`. The real-server check is slicc-jsh's, against PostgreSQL over SSL.
1288
+
1269
1289
  ### `heldcwd`
1270
1290
 
1271
1291
  `test/integration/fixtures/heldcwd`: a Rust wasm32-wasip1 program, built by `build.sh` with rustup's wasm32-wasip1 target, for the [working directory](#the-working-directory) rows (#1164).
@@ -7098,7 +7098,7 @@ function satisfies(version, range2) {
7098
7098
  }
7099
7099
 
7100
7100
  // src/version.ts
7101
- var KERNEL_VERSION = true ? "1.136.2" : "0.0.0-dev";
7101
+ var KERNEL_VERSION = true ? "1.136.4" : "0.0.0-dev";
7102
7102
  var OS_RELEASE = "6.0.0-slicc";
7103
7103
  function procVersion() {
7104
7104
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -18560,6 +18560,66 @@ async function anchorFor(engine, chain2, host, found) {
18560
18560
  return void 0;
18561
18561
  }
18562
18562
 
18563
+ // src/kernel/net/sealed-binding.ts
18564
+ var HASH_OF_SIGNATURE = {
18565
+ "1.2.840.113549.1.1.4": "SHA-256",
18566
+ "1.2.840.113549.1.1.5": "SHA-256",
18567
+ "1.2.840.10045.4.1": "SHA-256",
18568
+ "1.2.840.10040.4.3": "SHA-256",
18569
+ "1.2.840.113549.1.1.11": "SHA-256",
18570
+ "1.2.840.10045.4.3.2": "SHA-256",
18571
+ "1.2.840.113549.1.1.12": "SHA-384",
18572
+ "1.2.840.10045.4.3.3": "SHA-384",
18573
+ "1.2.840.113549.1.1.13": "SHA-512",
18574
+ "1.2.840.10045.4.3.4": "SHA-512"
18575
+ };
18576
+ function tlv3(der2, at) {
18577
+ const tag = der2[at];
18578
+ let len = der2[at + 1];
18579
+ if (tag === void 0 || len === void 0) return void 0;
18580
+ let start2 = at + 2;
18581
+ if (len & 128) {
18582
+ const n = len & 127;
18583
+ if (n === 0 || n > 4) return void 0;
18584
+ len = 0;
18585
+ for (let i = 0; i < n; i++) len = len * 256 + (der2[start2 + i] ?? 0);
18586
+ start2 += n;
18587
+ }
18588
+ const end = start2 + len;
18589
+ return end <= der2.length ? { tag, start: start2, end } : void 0;
18590
+ }
18591
+ function oidText(bytes) {
18592
+ const first = bytes[0] ?? 0;
18593
+ const parts2 = [Math.floor(first / 40), first % 40];
18594
+ let value2 = 0;
18595
+ for (const b of bytes.subarray(1)) {
18596
+ value2 = value2 * 128 + (b & 127);
18597
+ if ((b & 128) === 0) {
18598
+ parts2.push(value2);
18599
+ value2 = 0;
18600
+ }
18601
+ }
18602
+ return parts2.join(".");
18603
+ }
18604
+ function signatureOid(cert) {
18605
+ const outer = tlv3(cert, 0);
18606
+ if (outer?.tag !== 48) return void 0;
18607
+ const tbs = tlv3(cert, outer.start);
18608
+ if (tbs?.tag !== 48) return void 0;
18609
+ const algorithm = tlv3(cert, tbs.end);
18610
+ if (algorithm?.tag !== 48) return void 0;
18611
+ const oid2 = tlv3(cert, algorithm.start);
18612
+ if (oid2?.tag !== 6) return void 0;
18613
+ return oidText(cert.subarray(oid2.start, oid2.end));
18614
+ }
18615
+ async function endPointBinding(cert) {
18616
+ if (!cert) return void 0;
18617
+ const oid2 = signatureOid(cert);
18618
+ const hash = oid2 === void 0 ? void 0 : HASH_OF_SIGNATURE[oid2];
18619
+ if (!hash) return void 0;
18620
+ return new Uint8Array(await crypto.subtle.digest(hash, cert));
18621
+ }
18622
+
18563
18623
  // node_modules/@ai-ecoverse/slicc-scrub/dist/index.js
18564
18624
  var FRAGMENT = 16;
18565
18625
  var MIN_VALUE = 8;
@@ -19295,25 +19355,35 @@ function base64Of(bytes) {
19295
19355
  for (const b of bytes) bin += String.fromCharCode(b);
19296
19356
  return btoa(bin);
19297
19357
  }
19298
- function clientFirstBare(message3) {
19299
- const m = /^n,,(n=[^,]*,r=[^,]+)$/.exec(message3);
19300
- return m ? m[1] : void 0;
19358
+ function clientFirstOf(message3) {
19359
+ const m = /^((?:n|y|p=[A-Za-z0-9.-]+),,)(n=[^,]*,r=[^,]+)$/.exec(message3);
19360
+ return m ? { header: m[1], bare: m[2] } : void 0;
19361
+ }
19362
+ var latinBytes = (text) => Uint8Array.from(text, (c) => c.charCodeAt(0));
19363
+ function channelOf(header3, binding) {
19364
+ const head = latinBytes(header3);
19365
+ const all = new Uint8Array(head.length + (binding?.length ?? 0));
19366
+ all.set(head);
19367
+ if (binding) all.set(binding, head.length);
19368
+ return base64Of(all);
19301
19369
  }
19302
19370
  var SERVER_FIRST = /^r=([^,]+),s=([^,]+),i=(\d{1,6})$/;
19303
- var CLIENT_FINAL = /^(c=biws,r=([^,]+)),p=([^,]+)$/;
19371
+ var CLIENT_FINAL = /^(c=([A-Za-z0-9+/]+={0,2}),r=([^,]+)),p=([^,]+)$/;
19304
19372
  var SCRAM_MAX = 1024;
19305
19373
  function exchangeOf(clientFirst, serverFirst, clientFinal) {
19306
- const bare = clientFirstBare(clientFirst);
19374
+ const first = clientFirstOf(clientFirst);
19375
+ const bare = first?.bare;
19307
19376
  const sf = SERVER_FIRST.exec(serverFirst);
19308
19377
  const fin = CLIENT_FINAL.exec(clientFinal);
19309
19378
  if (!bare || !sf || !fin || [bare, serverFirst, clientFinal].some((t) => t.length > SCRAM_MAX))
19310
19379
  return void 0;
19380
+ const channel = base64Bytes(fin[2]);
19311
19381
  const cnonce = bare.slice(bare.indexOf(",r=") + 3);
19312
19382
  const nonce = sf[1];
19313
19383
  const salt = base64Bytes(sf[2]);
19314
19384
  const iterations = Number(sf[3]);
19315
- const proof = base64Bytes(fin[3]);
19316
- const ok4 = fin[2] === nonce && nonce.startsWith(cnonce) && iterations >= 1 && iterations <= 1e5 && salt !== void 0 && proof?.length === 32;
19385
+ const proof = base64Bytes(fin[4]);
19386
+ const ok4 = channel !== void 0 && latin(channel).startsWith(first.header) && fin[3] === nonce && nonce.startsWith(cnonce) && iterations >= 1 && iterations <= 1e5 && salt !== void 0 && proof?.length === 32;
19317
19387
  if (!ok4) return void 0;
19318
19388
  return {
19319
19389
  exchange: {
@@ -19323,9 +19393,16 @@ function exchangeOf(clientFirst, serverFirst, clientFinal) {
19323
19393
  salt,
19324
19394
  iterations
19325
19395
  },
19326
- proof
19396
+ proof,
19397
+ channel,
19398
+ nonce
19327
19399
  };
19328
19400
  }
19401
+ function latin(bytes) {
19402
+ let out = "";
19403
+ for (const b of bytes) out += String.fromCharCode(b);
19404
+ return out;
19405
+ }
19329
19406
  async function hmac(key, data) {
19330
19407
  const k = await crypto.subtle.importKey(
19331
19408
  "raw",
@@ -19392,16 +19469,23 @@ function sniffPg(start2) {
19392
19469
  const length2 = int32(bytes, 0);
19393
19470
  return length2 >= 8 && length2 <= PG_MESSAGE_MAX && int32(bytes, 4) === PROTOCOL_3 ? "yes" : "no";
19394
19471
  }
19472
+ var PLAIN = "SCRAM-SHA-256";
19395
19473
  var PLUS = "SCRAM-SHA-256-PLUS";
19474
+ var END_POINT = "p=tls-server-end-point,,";
19396
19475
  var SASL = 10;
19397
19476
  var SASL_CONTINUE = 11;
19398
19477
  var SASL_FINAL = 12;
19399
- function latin(bytes) {
19478
+ function latin2(bytes) {
19400
19479
  let out = "";
19401
19480
  for (const b of bytes) out += String.fromCharCode(b);
19402
19481
  return out;
19403
19482
  }
19404
19483
  var PgServer = class {
19484
+ binding;
19485
+ plus = false;
19486
+ constructor(binding) {
19487
+ this.binding = binding;
19488
+ }
19405
19489
  buffer = new Uint8Array(0);
19406
19490
  skip = 0;
19407
19491
  cleartext = false;
@@ -19415,20 +19499,22 @@ var PgServer = class {
19415
19499
  if (this.expect && code !== SASL_FINAL) this.failed = true;
19416
19500
  this.cleartext = message3.length === 9 && code === CLEARTEXT;
19417
19501
  if (code === SASL) return this.mechanisms(message3);
19418
- else if (code === SASL_CONTINUE) this.serverFirst = latin(message3.subarray(9));
19502
+ else if (code === SASL_CONTINUE) this.serverFirst = latin2(message3.subarray(9));
19419
19503
  else if (code === SASL_FINAL) this.final(message3);
19420
19504
  return message3;
19421
19505
  }
19422
19506
  mechanisms(message3) {
19423
19507
  this.sasl = true;
19424
- const names2 = latin(message3.subarray(9)).split("\0").filter((n) => n !== "" && n !== PLUS);
19425
- return frame3(82, `${latin(message3.subarray(5, 9))}${names2.map((n) => `${n}\0`).join("")}\0`);
19508
+ const offered = latin2(message3.subarray(9)).split("\0").filter((n) => n !== "");
19509
+ this.plus = offered.includes(PLUS);
19510
+ const names2 = offered.filter((n) => n !== PLUS || this.binding !== void 0);
19511
+ return frame3(82, `${latin2(message3.subarray(5, 9))}${names2.map((n) => `${n}\0`).join("")}\0`);
19426
19512
  }
19427
19513
  final(message3) {
19428
19514
  const expect = this.expect;
19429
19515
  if (!expect) return;
19430
19516
  this.expect = void 0;
19431
- const data = latin(message3.subarray(9));
19517
+ const data = latin2(message3.subarray(9));
19432
19518
  if (data !== expect.real || expect.fake.length !== expect.real.length) {
19433
19519
  this.failed = true;
19434
19520
  return;
@@ -19503,9 +19589,9 @@ function pgLength(bytes, started) {
19503
19589
  const length2 = int32(bytes, started ? 1 : 0);
19504
19590
  return started ? 1 + length2 : length2;
19505
19591
  }
19506
- var MECHANISMS = /* @__PURE__ */ new Set(["SCRAM-SHA-256"]);
19592
+ var MECHANISMS = /* @__PURE__ */ new Set([PLAIN, PLUS]);
19507
19593
  async function cleartext(message3, asks) {
19508
- const body = latin(message3.subarray(5));
19594
+ const body = latin2(message3.subarray(5));
19509
19595
  if (!body.endsWith("\0") || body.indexOf("\0") !== body.length - 1)
19510
19596
  return { bytes: message3, done: true };
19511
19597
  const password = body.slice(0, -1);
@@ -19517,14 +19603,30 @@ async function cleartext(message3, asks) {
19517
19603
  return { bytes: frame3(112, `${replaced(password, values)}\0`), values, done: true };
19518
19604
  }
19519
19605
  function initial(message3) {
19520
- const body = latin(message3.subarray(5));
19606
+ const body = latin2(message3.subarray(5));
19521
19607
  const nul = body.indexOf("\0");
19522
- if (nul < 0 || !MECHANISMS.has(body.slice(0, nul)) || body.length < nul + 5) return void 0;
19608
+ const mechanism = body.slice(0, nul);
19609
+ if (nul < 0 || !MECHANISMS.has(mechanism) || body.length < nul + 5) return void 0;
19523
19610
  const data = message3.subarray(5 + nul + 5);
19524
- return int32(message3, 5 + nul + 1) === data.length ? latin(data) : void 0;
19611
+ return int32(message3, 5 + nul + 1) === data.length ? { mechanism, data: latin2(data) } : void 0;
19612
+ }
19613
+ function int32Text(n) {
19614
+ return String.fromCharCode(n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255);
19615
+ }
19616
+ function serverHeader(header3, server) {
19617
+ if (header3 === "n,,") return header3;
19618
+ if (header3 === "y,," && !server.plus) return header3;
19619
+ if (header3 === END_POINT && server.binding) return header3;
19620
+ return server.plus && server.binding ? END_POINT : "n,,";
19621
+ }
19622
+ function rewritten(first, header3) {
19623
+ const parsed2 = clientFirstOf(first.data);
19624
+ if (!parsed2 || parsed2.header === header3) return first;
19625
+ return { mechanism: header3 === END_POINT ? PLUS : PLAIN, data: `${header3}${parsed2.bare}` };
19525
19626
  }
19526
19627
  var PgClient = class {
19527
19628
  clientFirst;
19629
+ header = "n,,";
19528
19630
  server;
19529
19631
  asks;
19530
19632
  constructor(server, asks) {
@@ -19539,17 +19641,28 @@ var PgClient = class {
19539
19641
  return cleartext(message3, this.asks);
19540
19642
  }
19541
19643
  if (server.sasl && this.clientFirst === void 0) {
19542
- this.clientFirst = initial(message3);
19543
- return { bytes: message3, done: this.clientFirst === void 0 };
19644
+ const first = initial(message3);
19645
+ const parsed2 = first && clientFirstOf(first.data);
19646
+ if (!first || !parsed2) return { bytes: message3, done: true };
19647
+ this.clientFirst = first.data;
19648
+ this.header = serverHeader(parsed2.header, server);
19649
+ const sent = rewritten(first, this.header);
19650
+ if (sent === first) return { bytes: message3, done: false };
19651
+ return {
19652
+ bytes: frame3(112, `${sent.mechanism}\0${int32Text(sent.data.length)}${sent.data}`),
19653
+ done: false
19654
+ };
19544
19655
  }
19545
19656
  if (this.clientFirst !== void 0 && server.serverFirst !== void 0)
19546
19657
  return this.final(message3, this.clientFirst, server.serverFirst);
19547
19658
  return { bytes: message3, done: true };
19548
19659
  }
19549
19660
  async final(message3, first, serverFirst) {
19550
- const got = exchangeOf(first, serverFirst, latin(message3.subarray(5)));
19661
+ const got = exchangeOf(first, serverFirst, latin2(message3.subarray(5)));
19551
19662
  if (!got) return { bytes: message3, done: true };
19552
19663
  const x = got.exchange;
19664
+ const binding = this.header === END_POINT ? this.server.binding : void 0;
19665
+ const serverFinal = `c=${channelOf(this.header, binding)},r=${got.nonce}`;
19553
19666
  const values = usable(
19554
19667
  await this.asks.scram({
19555
19668
  clientFirstBare: x.clientFirstBare,
@@ -19559,16 +19672,19 @@ var PgClient = class {
19559
19672
  })
19560
19673
  );
19561
19674
  const [entry, ...more] = Object.entries(values);
19562
- if (!entry || more.length > 0) return { bytes: message3, done: true };
19675
+ const own = { bytes: frame3(112, `${serverFinal},p=${base64Of(got.proof)}`), done: true };
19676
+ if (!entry || more.length > 0)
19677
+ return serverFinal === x.clientFinalBare ? { bytes: message3, done: true } : own;
19563
19678
  const [placeholder, value2] = entry;
19564
19679
  const fake = await scramKeys(placeholder, x);
19565
- if (!sameBytes(fake.proof, got.proof)) return { bytes: message3, done: true };
19566
- const real = await scramKeys(saslprep(value2), x);
19680
+ if (!sameBytes(fake.proof, got.proof))
19681
+ return serverFinal === x.clientFinalBare ? { bytes: message3, done: true } : own;
19682
+ const real = await scramKeys(saslprep(value2), { ...x, clientFinalBare: serverFinal });
19567
19683
  this.server.expect = {
19568
19684
  real: `v=${base64Of(real.serverSignature)}`,
19569
19685
  fake: `v=${base64Of(fake.serverSignature)}`
19570
19686
  };
19571
- const bytes = frame3(112, `${x.clientFinalBare},p=${base64Of(real.proof)}`);
19687
+ const bytes = frame3(112, `${serverFinal},p=${base64Of(real.proof)}`);
19572
19688
  for (const k of [real.proof, real.serverSignature, fake.proof, fake.serverSignature]) k.fill(0);
19573
19689
  return { bytes, values, done: true };
19574
19690
  }
@@ -19967,7 +20083,7 @@ function idle() {
19967
20083
  return new Promise((resolve2) => setTimeout(() => resolve2(IDLE2), SCRUB_IDLE_MS));
19968
20084
  }
19969
20085
  function sealedConn(conn, s) {
19970
- const pg = new PgServer();
20086
+ const pg = new PgServer(s.binding);
19971
20087
  const scope = s.broker.sessions.scope(s.pid, s.host);
19972
20088
  const writer = substitutes(s) ? new SessionWriter(conn, s, pg, scope) : void 0;
19973
20089
  const observed = {
@@ -20258,6 +20374,8 @@ function secure(prepared, seal) {
20258
20374
  const session = sealing({ ...seal, engine }, raw2, made2.info, prepared.options.servername);
20259
20375
  if (!session) return made2;
20260
20376
  made2.info.substitutes = substitutes(session);
20377
+ if (made2.info.substitutes && session.verified)
20378
+ session.binding = await endPointBinding(made2.info.peerCertificates[0]);
20261
20379
  return { conn: sealedConn(made2.conn, session), info: made2.info };
20262
20380
  } finally {
20263
20381
  prepared.own?.release();
@@ -22291,6 +22409,18 @@ var WasmProcess = class {
22291
22409
  throw err;
22292
22410
  }
22293
22411
  }
22412
+ async complain(line) {
22413
+ const stderr = this.fds.has(2) ? this.fds.get(2).file : void 0;
22414
+ if (!stderr?.write || stderr.sealed || stderr.clipboard) return false;
22415
+ const bytes = new TextEncoder().encode(`${line}
22416
+ `);
22417
+ const timeout = new Promise((r) => setTimeout(() => r(0), 1e3));
22418
+ const sent = await Promise.race([stderr.write(bytes, AbortSignal.timeout(1e3)), timeout]).then(
22419
+ (n) => n,
22420
+ () => 0
22421
+ );
22422
+ return sent > 0;
22423
+ }
22294
22424
  async clipboardComplaint(err) {
22295
22425
  const name2 = this.options.clipboardName;
22296
22426
  if (name2 === void 0 || !(err instanceof KernelError) || err.detail === void 0) return;
@@ -23766,6 +23896,29 @@ function outOfMemory(opts, data) {
23766
23896
  }
23767
23897
  return true;
23768
23898
  }
23899
+ function workerError(event) {
23900
+ return String(event.message ?? "worker error");
23901
+ }
23902
+ var CrashSlot = class {
23903
+ status;
23904
+ reported = false;
23905
+ report;
23906
+ constructor(report) {
23907
+ this.report = report;
23908
+ }
23909
+ take(status, message3, finish) {
23910
+ if (this.status) return;
23911
+ this.status = status;
23912
+ const done5 = () => {
23913
+ this.reported = true;
23914
+ finish(...status);
23915
+ };
23916
+ void this.report(message3).then(done5, done5);
23917
+ }
23918
+ };
23919
+ async function crashReport(process, onError, message3) {
23920
+ if (!await process.complain(message3)) onError?.(message3);
23921
+ }
23769
23922
  function spawnWasmProcess(opts) {
23770
23923
  const { promise: ready2, resolve: started } = readiness(opts.program.abi);
23771
23924
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
@@ -23816,9 +23969,11 @@ function spawnWasmProcess(opts) {
23816
23969
  const jsThreads = jsThreadsOf(opts, process, token, dispatch2, clocks, header3);
23817
23970
  const { promise: exited, resolve: settle3 } = Promise.withResolvers();
23818
23971
  let done5 = false;
23972
+ const crash = new CrashSlot((message3) => crashReport(process, opts.onError, message3));
23819
23973
  let endedBy;
23820
23974
  const finish = (code, sig) => {
23821
23975
  if (done5) return;
23976
+ if (crash.status && !crash.reported) return;
23822
23977
  done5 = true;
23823
23978
  started(true);
23824
23979
  clocks.freeze();
@@ -23847,8 +24002,9 @@ function spawnWasmProcess(opts) {
23847
24002
  } else if (data?.type === WASM_PROCESS_EXIT) {
23848
24003
  finish(...dying.status(data.code));
23849
24004
  } else if (data?.type === WASM_PROCESS_ERROR) {
23850
- if (!dying.ending) opts.onError?.(String(data.message));
23851
- finish(...dying.status(data.code));
24005
+ const status = dying.status(data.code);
24006
+ if (dying.ending) finish(...status);
24007
+ else crash.take(status, String(data.message), finish);
23852
24008
  } else if (data?.type === WASM_THREAD_SPAWN && data.thread) {
23853
24009
  spawnThread(data.thread);
23854
24010
  } else if (data?.type === JS_THREAD_SPAWN && jsThreads) {
@@ -23858,8 +24014,7 @@ function spawnWasmProcess(opts) {
23858
24014
  const onMessage = (event) => handle(event.data);
23859
24015
  const onError = (event) => {
23860
24016
  event.preventDefault();
23861
- opts.onError?.(String(event.message ?? "worker error"));
23862
- finish(CRASHED);
24017
+ crash.take([CRASHED], workerError(event), finish);
23863
24018
  };
23864
24019
  worker.addEventListener("message", onMessage);
23865
24020
  worker.addEventListener("error", onError);
@@ -26697,7 +26852,7 @@ var RULE = new RegExp(
26697
26852
  String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?(?:Cwd\s+(${WORD})\s+)?Cmnd(?:\s+(.*))?$`
26698
26853
  );
26699
26854
  var TERM = /^[A-Za-z0-9._+-]+$/;
26700
- var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
26855
+ var PLAIN2 = /^[A-Za-z0-9_@%+=:,./-]+$/;
26701
26856
  var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
26702
26857
  function failed2(errno3, error) {
26703
26858
  return { error, errno: errno3 };
@@ -26724,7 +26879,7 @@ function splitWords(text) {
26724
26879
  return words2;
26725
26880
  }
26726
26881
  function quoteWord(word) {
26727
- return PLAIN.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
26882
+ return PLAIN2.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
26728
26883
  }
26729
26884
  function validWho(who) {
26730
26885
  if (who === "ALL") return true;