@wacando/agent 2.2.0 → 2.4.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.
@@ -1,10 +1,370 @@
1
- // Embedded from lbframe/wacando@618922ecaf2213da18949a8b27d343c8767b64c5 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
1
+ // Embedded from lbframe/wacando@40dd0c30b3653095f08994f9534fd5110e1d0925 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
2
2
  import {
3
3
  XMPP_DOMAIN
4
- } from "./chunk-PZDQXGA6.mjs";
4
+ } from "./chunk-FFLYSYV6.mjs";
5
5
 
6
- // packages/xmpp/src/muc.ts
6
+ // packages/xmpp/src/scram.ts
7
+ import { client } from "@xmpp/client";
7
8
  import xml from "@xmpp/xml";
9
+ var SASL1_NS = "urn:ietf:params:xml:ns:xmpp-sasl";
10
+ var SASL2_NS = "urn:xmpp:sasl:2";
11
+ var SCRAM_MIN_ITERATIONS = 4096;
12
+ var SCRAM_MAX_ITERATIONS = 1e5;
13
+ var HASH_BYTES = { "SHA-1": 20, "SHA-256": 32 };
14
+ var REASON_TEXT = {
15
+ "missing-server-signature": "server sent no ServerSignature (v=)",
16
+ "server-signature-mismatch": "server ServerSignature (v=) does not match",
17
+ "malformed-server-message": "malformed SCRAM server message",
18
+ "server-error": "server reported a SCRAM error (e=)",
19
+ "nonce-mismatch": "server nonce does not extend the client nonce",
20
+ "weak-iteration-count": `server iteration count below ${SCRAM_MIN_ITERATIONS}`,
21
+ "excessive-iteration-count": `server iteration count above ${SCRAM_MAX_ITERATIONS}`,
22
+ "sasl2-refused": "server offers SASL2, which this client refuses (SASL1 SCRAM only)",
23
+ "no-scram-mechanism": "server offers no supported SCRAM mechanism",
24
+ "out-of-order": "SCRAM message out of order"
25
+ };
26
+ var ScramAuthError = class extends Error {
27
+ reason;
28
+ mechanism;
29
+ constructor(reason, mechanism) {
30
+ super(
31
+ `SCRAM authentication refused${mechanism ? ` (${mechanism})` : ""}: ${REASON_TEXT[reason]}`
32
+ );
33
+ this.name = "ScramAuthError";
34
+ this.reason = reason;
35
+ this.mechanism = mechanism;
36
+ }
37
+ };
38
+ var encoder = new TextEncoder();
39
+ function utf8(text) {
40
+ return new Uint8Array(encoder.encode(text));
41
+ }
42
+ function bytesToBinary(bytes) {
43
+ let out = "";
44
+ for (const b of bytes) out += String.fromCharCode(b);
45
+ return out;
46
+ }
47
+ function binaryToBytes(binary) {
48
+ const out = new Uint8Array(binary.length);
49
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i) & 255;
50
+ return out;
51
+ }
52
+ function base64Encode(bytes) {
53
+ return globalThis.btoa(bytesToBinary(bytes));
54
+ }
55
+ function base64Decode(text) {
56
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(text)) {
57
+ return void 0;
58
+ }
59
+ try {
60
+ return binaryToBytes(globalThis.atob(text));
61
+ } catch {
62
+ return void 0;
63
+ }
64
+ }
65
+ function constantTimeEqual(a, b) {
66
+ let diff = a.length ^ b.length;
67
+ const n = Math.max(a.length, b.length);
68
+ for (let i = 0; i < n; i++) diff |= (a[i] ?? 0) ^ (b[i] ?? 0);
69
+ return diff === 0;
70
+ }
71
+ function xor(a, b) {
72
+ const out = new Uint8Array(a.length);
73
+ for (let i = 0; i < a.length; i++) out[i] = (a[i] ?? 0) ^ (b[i] ?? 0);
74
+ return out;
75
+ }
76
+ async function hmac(hash, key, data) {
77
+ const k = await globalThis.crypto.subtle.importKey("raw", key, { name: "HMAC", hash }, false, [
78
+ "sign"
79
+ ]);
80
+ return new Uint8Array(await globalThis.crypto.subtle.sign("HMAC", k, data));
81
+ }
82
+ async function digest(hash, data) {
83
+ return new Uint8Array(await globalThis.crypto.subtle.digest(hash, data));
84
+ }
85
+ async function hi(hash, password, salt, iterations) {
86
+ const key = await globalThis.crypto.subtle.importKey("raw", utf8(password), "PBKDF2", false, [
87
+ "deriveBits"
88
+ ]);
89
+ return new Uint8Array(
90
+ await globalThis.crypto.subtle.deriveBits(
91
+ { name: "PBKDF2", hash, salt, iterations },
92
+ key,
93
+ HASH_BYTES[hash] * 8
94
+ )
95
+ );
96
+ }
97
+ function parseAttributes(message) {
98
+ const attrs = /* @__PURE__ */ new Map();
99
+ for (const part of message.split(",")) {
100
+ const m = /^([A-Za-z])=(.*)$/s.exec(part);
101
+ if (!m) return void 0;
102
+ const [, key, value] = m;
103
+ if (attrs.has(key)) return void 0;
104
+ attrs.set(key, value);
105
+ }
106
+ return attrs;
107
+ }
108
+ function saslname(name) {
109
+ return name.replace(/=/g, "=3D").replace(/,/g, "=2C");
110
+ }
111
+ async function scramProofs(hash, password, salt, iterations, authMessage) {
112
+ const salted = await hi(hash, password, salt, iterations);
113
+ const clientKey = await hmac(hash, salted, utf8("Client Key"));
114
+ const storedKey = await digest(hash, clientKey);
115
+ const am = utf8(authMessage);
116
+ const clientSignature = await hmac(hash, storedKey, am);
117
+ const serverKey = await hmac(hash, salted, utf8("Server Key"));
118
+ return {
119
+ clientProof: xor(clientKey, clientSignature),
120
+ serverSignature: await hmac(hash, serverKey, am)
121
+ };
122
+ }
123
+ function verifyServerFinal(serverFinal, expected, mechanism) {
124
+ if (serverFinal === void 0 || serverFinal === "") {
125
+ throw new ScramAuthError("missing-server-signature", mechanism);
126
+ }
127
+ const attrs = parseAttributes(serverFinal);
128
+ if (!attrs) throw new ScramAuthError("malformed-server-message", mechanism);
129
+ if (attrs.has("e")) throw new ScramAuthError("server-error", mechanism);
130
+ const v = attrs.get("v");
131
+ if (v === void 0) throw new ScramAuthError("missing-server-signature", mechanism);
132
+ const received = base64Decode(v);
133
+ if (!received) throw new ScramAuthError("malformed-server-message", mechanism);
134
+ if (!constantTimeEqual(received, expected)) {
135
+ throw new ScramAuthError("server-signature-mismatch", mechanism);
136
+ }
137
+ }
138
+ var ScramMechanism = class {
139
+ constructor(hash, genNonce = defaultNonce) {
140
+ this.hash = hash;
141
+ this.genNonce = genNonce;
142
+ this.name = hash === "SHA-1" ? "SCRAM-SHA-1" : "SCRAM-SHA-256";
143
+ }
144
+ hash;
145
+ genNonce;
146
+ name;
147
+ clientFirst = true;
148
+ stage = "initial";
149
+ clientNonce = "";
150
+ clientFirstBare = "";
151
+ serverFirst = "";
152
+ expectedServerSignature;
153
+ pendingServerFinal;
154
+ verifiedFlag = false;
155
+ /** True once the server's ServerSignature has been verified. */
156
+ get verified() {
157
+ return this.verifiedFlag;
158
+ }
159
+ /** True once the client-final-message was sent (a `v=` is now due). */
160
+ get awaitingServerFinal() {
161
+ return this.stage === "final";
162
+ }
163
+ async response(cred) {
164
+ switch (this.stage) {
165
+ case "initial":
166
+ return this.clientFirstMessage(cred);
167
+ case "challenge":
168
+ return this.clientFinalMessage(cred);
169
+ case "final":
170
+ await this.final(this.pendingServerFinal);
171
+ return "";
172
+ default:
173
+ throw new ScramAuthError("out-of-order", this.name);
174
+ }
175
+ }
176
+ challenge(chal) {
177
+ if (this.stage === "challenge") {
178
+ this.serverFirst = utf8Decode(chal);
179
+ return;
180
+ }
181
+ if (this.stage === "final") {
182
+ this.pendingServerFinal = chal;
183
+ return;
184
+ }
185
+ throw new ScramAuthError("out-of-order", this.name);
186
+ }
187
+ /**
188
+ * server-final-message (binary string, as decoded from base64) from
189
+ * <success/> (SASL1), <additional-data/> (SASL2) or a last <challenge/>.
190
+ */
191
+ async final(data) {
192
+ if (this.verifiedFlag) return;
193
+ if (this.stage !== "final" || !this.expectedServerSignature) {
194
+ throw new ScramAuthError("out-of-order", this.name);
195
+ }
196
+ this.stage = "done";
197
+ const expected = this.expectedServerSignature;
198
+ this.expectedServerSignature = void 0;
199
+ verifyServerFinal(data === void 0 ? void 0 : utf8Decode(data), expected, this.name);
200
+ this.verifiedFlag = true;
201
+ }
202
+ clientFirstMessage(cred) {
203
+ this.clientNonce = this.genNonce();
204
+ this.clientFirstBare = `n=${saslname(cred.username ?? "")},r=${this.clientNonce}`;
205
+ this.stage = "challenge";
206
+ return utf8Binary(`n,,${this.clientFirstBare}`);
207
+ }
208
+ async clientFinalMessage(cred) {
209
+ const attrs = parseAttributes(this.serverFirst);
210
+ const nonce = attrs?.get("r");
211
+ const saltText = attrs?.get("s");
212
+ const iterText = attrs?.get("i");
213
+ if (attrs?.has("e")) throw new ScramAuthError("server-error", this.name);
214
+ if (!attrs || attrs.has("m") || !nonce || !saltText || !iterText) {
215
+ throw new ScramAuthError("malformed-server-message", this.name);
216
+ }
217
+ if (!nonce.startsWith(this.clientNonce) || nonce.length <= this.clientNonce.length) {
218
+ throw new ScramAuthError("nonce-mismatch", this.name);
219
+ }
220
+ const salt = base64Decode(saltText);
221
+ if (!salt || salt.length === 0 || !/^[1-9][0-9]{0,9}$/.test(iterText)) {
222
+ throw new ScramAuthError("malformed-server-message", this.name);
223
+ }
224
+ const iterations = Number(iterText);
225
+ if (iterations < SCRAM_MIN_ITERATIONS) {
226
+ throw new ScramAuthError("weak-iteration-count", this.name);
227
+ }
228
+ if (iterations > SCRAM_MAX_ITERATIONS) {
229
+ throw new ScramAuthError("excessive-iteration-count", this.name);
230
+ }
231
+ const withoutProof = `c=biws,r=${nonce}`;
232
+ const authMessage = `${this.clientFirstBare},${this.serverFirst},${withoutProof}`;
233
+ const { clientProof, serverSignature } = await scramProofs(
234
+ this.hash,
235
+ cred.password ?? "",
236
+ salt,
237
+ iterations,
238
+ authMessage
239
+ );
240
+ this.expectedServerSignature = serverSignature;
241
+ this.stage = "final";
242
+ return utf8Binary(`${withoutProof},p=${base64Encode(clientProof)}`);
243
+ }
244
+ };
245
+ function utf8Binary(text) {
246
+ return bytesToBinary(utf8(text));
247
+ }
248
+ function utf8Decode(binary) {
249
+ return new TextDecoder().decode(binaryToBytes(binary));
250
+ }
251
+ function defaultNonce() {
252
+ const bytes = new Uint8Array(24);
253
+ globalThis.crypto.getRandomValues(bytes);
254
+ return base64Encode(bytes);
255
+ }
256
+ var SCRAM_HASHES = ["SHA-256", "SHA-1"];
257
+ var SCRAM_NAMES = SCRAM_HASHES.map(
258
+ (h) => h === "SHA-1" ? "SCRAM-SHA-1" : "SCRAM-SHA-256"
259
+ );
260
+ var NS_STREAM = "http://etherx.jabber.org/streams";
261
+ function saslFeaturesRefusal(features) {
262
+ if (features.getChild("authentication", SASL2_NS)) return "sasl2-refused";
263
+ const sasl1 = features.getChild("mechanisms", SASL1_NS);
264
+ if (!sasl1) return void 0;
265
+ const offered = sasl1.getChildren("mechanism").map((m) => m.text().trim());
266
+ return SCRAM_NAMES.some((name) => offered.includes(name)) ? void 0 : "no-scram-mechanism";
267
+ }
268
+ var states = /* @__PURE__ */ new WeakMap();
269
+ function scramClient(options) {
270
+ const { username, password, ...rest } = options;
271
+ let current;
272
+ let rejectFailure = () => {
273
+ };
274
+ const state = {
275
+ failure: new Promise((_, reject) => {
276
+ rejectFailure = reject;
277
+ })
278
+ };
279
+ state.failure.catch(() => {
280
+ });
281
+ const userAgent = xml("user-agent", { id: globalThis.crypto.randomUUID() });
282
+ const cut = (entity, err) => {
283
+ state.cutting ??= (async () => {
284
+ entity.reconnect?.stop();
285
+ await entity.stop().catch(() => {
286
+ });
287
+ rejectFailure(err);
288
+ })();
289
+ return state.cutting;
290
+ };
291
+ let refused;
292
+ const xmpp = client({
293
+ ...rest,
294
+ credentials: async (authenticate, mechanisms, _fast, entity) => {
295
+ const failClosed = async (err) => {
296
+ await cut(entity, err);
297
+ throw err;
298
+ };
299
+ if (refused) return failClosed(refused);
300
+ const mechanism = SCRAM_NAMES.find((name) => mechanisms.includes(name));
301
+ if (!mechanism) return failClosed(new ScramAuthError("no-scram-mechanism"));
302
+ let successData;
303
+ const onNonza = (el) => {
304
+ if (el.is("success", SASL1_NS)) successData = decodeSuccess(el.text());
305
+ };
306
+ entity.on("nonza", onNonza);
307
+ current = void 0;
308
+ try {
309
+ await authenticate({ username, password }, mechanism, userAgent);
310
+ const mech = current;
311
+ if (!mech) throw new ScramAuthError("out-of-order", mechanism);
312
+ if (!mech.verified) await mech.final(successData);
313
+ } catch (err) {
314
+ if (err instanceof ScramAuthError) return failClosed(err);
315
+ throw err;
316
+ } finally {
317
+ entity.removeListener("nonza", onNonza);
318
+ current = void 0;
319
+ }
320
+ }
321
+ });
322
+ const factory = xmpp.saslFactory;
323
+ const bound = SCRAM_HASHES.map((hash) => ({
324
+ name: hash === "SHA-1" ? "SCRAM-SHA-1" : "SCRAM-SHA-256",
325
+ mech: class extends ScramMechanism {
326
+ constructor() {
327
+ super(hash);
328
+ current = this;
329
+ }
330
+ }
331
+ }));
332
+ factory._mechs.splice(0, factory._mechs.length, ...bound);
333
+ xmpp.on("nonza", (el) => {
334
+ if (!el.is("features", NS_STREAM)) return;
335
+ const reason = saslFeaturesRefusal(el);
336
+ if (!reason) return;
337
+ refused = new ScramAuthError(reason);
338
+ void cut(xmpp, refused);
339
+ });
340
+ states.set(xmpp, state);
341
+ return xmpp;
342
+ }
343
+ function decodeSuccess(text) {
344
+ const trimmed = text.trim();
345
+ if (trimmed === "" || trimmed === "=") return void 0;
346
+ try {
347
+ return globalThis.atob(trimmed);
348
+ } catch {
349
+ return void 0;
350
+ }
351
+ }
352
+ async function startScramClient(xmpp) {
353
+ const state = states.get(xmpp);
354
+ if (!state) return xmpp.start();
355
+ try {
356
+ return await Promise.race([xmpp.start(), state.failure]);
357
+ } catch (err) {
358
+ if (state.cutting) {
359
+ await state.cutting;
360
+ return state.failure;
361
+ }
362
+ throw err;
363
+ }
364
+ }
365
+
366
+ // packages/xmpp/src/muc.ts
367
+ import xml2 from "@xmpp/xml";
8
368
 
9
369
  // packages/xmpp/src/jid.ts
10
370
  var COMPONENT_JID = `policy.${XMPP_DOMAIN}`;
@@ -113,7 +473,7 @@ var MucJoinError = class extends MucError {
113
473
  nick;
114
474
  };
115
475
  function joinPresenceEl(room, nick) {
116
- return xml("presence", { to: occupantJid(room, nick) }, xml("x", { xmlns: NS_MUC }));
476
+ return xml2("presence", { to: occupantJid(room, nick) }, xml2("x", { xmlns: NS_MUC }));
117
477
  }
118
478
  async function joinRoom(entity, room, nick, timeoutMs = defaultTimeoutMs()) {
119
479
  const self = occupantJid(room, nick);
@@ -266,24 +626,24 @@ var MucOriginTracker = class {
266
626
  };
267
627
 
268
628
  // packages/xmpp/src/mam.ts
269
- import xml2 from "@xmpp/xml";
629
+ import xml3 from "@xmpp/xml";
270
630
  var NS_MAM = "urn:xmpp:mam:2";
271
631
  var NS_FORWARD2 = "urn:xmpp:forward:0";
272
632
  var NS_RSM = "http://jabber.org/protocol/rsm";
273
633
  var queryCounter = 0;
274
634
  function filterField(varName, value) {
275
- return xml2("field", { var: varName }, xml2("value", {}, value));
635
+ return xml3("field", { var: varName }, xml3("value", {}, value));
276
636
  }
277
637
  function mamQueryEl(options) {
278
- const fields = [xml2("field", { var: "FORM_TYPE", type: "hidden" }, xml2("value", {}, NS_MAM))];
638
+ const fields = [xml3("field", { var: "FORM_TYPE", type: "hidden" }, xml3("value", {}, NS_MAM))];
279
639
  if (options.withJid !== void 0) fields.push(filterField("with", options.withJid));
280
640
  if (options.start !== void 0) fields.push(filterField("start", options.start));
281
641
  if (options.end !== void 0) fields.push(filterField("end", options.end));
282
- const children = [xml2("x", { xmlns: NS_DATA_FORMS, type: "submit" }, ...fields)];
642
+ const children = [xml3("x", { xmlns: NS_DATA_FORMS, type: "submit" }, ...fields)];
283
643
  if (options.max !== void 0) {
284
- children.push(xml2("set", { xmlns: NS_RSM }, xml2("max", {}, String(options.max))));
644
+ children.push(xml3("set", { xmlns: NS_RSM }, xml3("max", {}, String(options.max))));
285
645
  }
286
- return xml2("query", { xmlns: NS_MAM, queryid: options.queryId }, ...children);
646
+ return xml3("query", { xmlns: NS_MAM, queryid: options.queryId }, ...children);
287
647
  }
288
648
  async function queryMam(entity, options, timeoutMs = defaultTimeoutMs()) {
289
649
  const queryId = options.queryId ?? `mam${++queryCounter}`;
@@ -326,7 +686,7 @@ function forwardedStanza(forwarded) {
326
686
 
327
687
  // packages/xmpp/src/roster.ts
328
688
  import { jid as parseJid } from "@xmpp/jid";
329
- import xml3 from "@xmpp/xml";
689
+ import xml4 from "@xmpp/xml";
330
690
  function subscriptionRefusal(stanza) {
331
691
  if (!stanza.is("presence") || stanza.getAttr("type") !== "subscribe") return null;
332
692
  const from = stanza.getAttr("from");
@@ -337,7 +697,7 @@ function subscriptionRefusal(stanza) {
337
697
  } catch {
338
698
  return null;
339
699
  }
340
- return xml3("presence", { to: bare, type: "unsubscribed" });
700
+ return xml4("presence", { to: bare, type: "unsubscribed" });
341
701
  }
342
702
  function refuseSubscriptionRequests(entity) {
343
703
  const onStanza = (stanza) => {
@@ -356,26 +716,26 @@ var UNKNOWN_PRESENCE = Object.freeze({
356
716
  });
357
717
 
358
718
  // packages/xmpp/src/pubsub.ts
359
- import xml4 from "@xmpp/xml";
719
+ import xml5 from "@xmpp/xml";
360
720
  var NS_PUBSUB = "http://jabber.org/protocol/pubsub";
361
721
  var NS_PUBSUB_PUBLISH_OPTIONS = "http://jabber.org/protocol/pubsub#publish-options";
362
722
  function pepPublishEl(node, itemId, payload, accessModel = "open") {
363
- return xml4(
723
+ return xml5(
364
724
  "pubsub",
365
725
  { xmlns: NS_PUBSUB },
366
- xml4("publish", { node }, xml4("item", { id: itemId }, payload)),
367
- xml4(
726
+ xml5("publish", { node }, xml5("item", { id: itemId }, payload)),
727
+ xml5(
368
728
  "publish-options",
369
729
  {},
370
- xml4(
730
+ xml5(
371
731
  "x",
372
732
  { xmlns: NS_DATA_FORMS, type: "submit" },
373
- xml4(
733
+ xml5(
374
734
  "field",
375
735
  { var: "FORM_TYPE", type: "hidden" },
376
- xml4("value", {}, NS_PUBSUB_PUBLISH_OPTIONS)
736
+ xml5("value", {}, NS_PUBSUB_PUBLISH_OPTIONS)
377
737
  ),
378
- xml4("field", { var: "pubsub#access_model" }, xml4("value", {}, accessModel))
738
+ xml5("field", { var: "pubsub#access_model" }, xml5("value", {}, accessModel))
379
739
  )
380
740
  )
381
741
  );
@@ -383,7 +743,7 @@ function pepPublishEl(node, itemId, payload, accessModel = "open") {
383
743
  function pubsubItemsEl(node, maxItems) {
384
744
  const attrs = { node };
385
745
  if (maxItems !== void 0) attrs.max_items = String(maxItems);
386
- return xml4("pubsub", { xmlns: NS_PUBSUB }, xml4("items", attrs));
746
+ return xml5("pubsub", { xmlns: NS_PUBSUB }, xml5("items", attrs));
387
747
  }
388
748
  async function pepPublish(entity, to, node, itemId, payload, timeoutMs = defaultTimeoutMs()) {
389
749
  await entity.iqCaller.set(pepPublishEl(node, itemId, payload), to, timeoutMs);
@@ -408,6 +768,9 @@ function isItemNotFound(err) {
408
768
  }
409
769
 
410
770
  export {
771
+ ScramAuthError,
772
+ scramClient,
773
+ startScramClient,
411
774
  defaultTimeoutMs,
412
775
  waitForStanza,
413
776
  MucJoinError,
@@ -1,4 +1,4 @@
1
- // Embedded from lbframe/wacando@618922ecaf2213da18949a8b27d343c8767b64c5 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
1
+ // Embedded from lbframe/wacando@40dd0c30b3653095f08994f9534fd5110e1d0925 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
2
2
  import {
3
3
  BODY_MARKER,
4
4
  GCM_IV_BYTES,
@@ -29,8 +29,8 @@ import {
29
29
  signerJid,
30
30
  signerRole,
31
31
  targetJid
32
- } from "./chunks/chunk-PHGOH4AU.mjs";
33
- import "./chunks/chunk-PZDQXGA6.mjs";
32
+ } from "./chunks/chunk-F7J3K43U.mjs";
33
+ import "./chunks/chunk-FFLYSYV6.mjs";
34
34
 
35
35
  // packages/omemo/src/policy-wire.ts
36
36
  import xml from "@xmpp/xml";
@@ -1,9 +1,9 @@
1
- // Embedded from lbframe/wacando@618922ecaf2213da18949a8b27d343c8767b64c5 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
1
+ // Embedded from lbframe/wacando@40dd0c30b3653095f08994f9534fd5110e1d0925 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
2
2
  import {
3
3
  authenticatedMessageOrigin,
4
4
  pepFetchItems,
5
5
  pepPublish
6
- } from "./chunks/chunk-53E7SSTF.mjs";
6
+ } from "./chunks/chunk-FUNUGIGH.mjs";
7
7
  import {
8
8
  BODY_MARKER,
9
9
  GCM_IV_BYTES,
@@ -37,13 +37,13 @@ import {
37
37
  signerJid,
38
38
  signerRole,
39
39
  utf8Encode
40
- } from "./chunks/chunk-PHGOH4AU.mjs";
40
+ } from "./chunks/chunk-F7J3K43U.mjs";
41
41
  import {
42
42
  ALLOWED_MEDIA_TYPES,
43
43
  MAX_FILE_BYTES,
44
44
  MIN_FILE_BYTES,
45
45
  looksLikePlaintext
46
- } from "./chunks/chunk-PZDQXGA6.mjs";
46
+ } from "./chunks/chunk-FFLYSYV6.mjs";
47
47
 
48
48
  // packages/omemo/src/libsignal.ts
49
49
  import { jid as parseJid2 } from "@xmpp/jid";
@@ -585,8 +585,8 @@ var OmemoProvider = class {
585
585
  /**
586
586
  * Resolve OMEMO device targets for a member set via PEP — fail closed:
587
587
  * missing devicelist or missing bundle throws OmemoError before any
588
- * plaintext is touched. Bundle elements are kept raw: each session
589
- * backend parses the flavor it accepts (kyber → PQXDH, else classic).
588
+ * plaintext is touched. Bundle elements are kept raw: the session
589
+ * backend parses them and refuses any non-PQXDH (classic) bundle.
590
590
  */
591
591
  async resolveDeviceTargets(entity, memberBareJids, timeoutMs = 1e4) {
592
592
  const recipients = [.../* @__PURE__ */ new Set([...memberBareJids, this.bareJid])];
@@ -1175,6 +1175,7 @@ var NAME_INVISIBLE = new RegExp(
1175
1175
  "g"
1176
1176
  );
1177
1177
  var NAME_RESERVED = /[<>:"|?*]/g;
1178
+ var NAME_DOT_LOOKALIKE = /[\uff0e\ufe52\u2024]/g;
1178
1179
  var WINDOWS_DEVICE = /^(con|prn|aux|nul|conin\$|conout\$|clock\$|com[0-9\u00b9\u00b2\u00b3]|lpt[0-9\u00b9\u00b2\u00b3])\s*(\..*)?$/i;
1179
1180
  function utf8Length(s) {
1180
1181
  return new TextEncoder().encode(s).length;
@@ -1188,7 +1189,7 @@ function cutUtf8(s, max) {
1188
1189
  return out;
1189
1190
  }
1190
1191
  function sanitizeFileName(raw) {
1191
- let name = raw.normalize("NFC").replace(NAME_INVISIBLE, "");
1192
+ let name = raw.normalize("NFC").replace(NAME_INVISIBLE, "").replace(NAME_DOT_LOOKALIKE, ".");
1192
1193
  name = name.split(/[/\\]/).pop() ?? "";
1193
1194
  name = name.replace(NAME_RESERVED, "_").trim();
1194
1195
  name = name.replace(/^[.\s]+/, "").replace(/[.\s]+$/, "");
@@ -1540,7 +1541,10 @@ function fileIdOfShare(share) {
1540
1541
  }
1541
1542
  async function downloadFile(share, opts) {
1542
1543
  const { fetchUrl } = parseAesgcmUrl(share.url, opts.pin);
1543
- const limit = share.ciphertextSize;
1544
+ const expected = share.ciphertextSize;
1545
+ if (!Number.isSafeInteger(expected) || expected < FILE_TAG_BYTES)
1546
+ throw new FileCryptoError("invalid_descriptor", "file share: ciphertext size");
1547
+ const limit = Math.min(expected, MAX_FILE_CIPHERTEXT_BYTES);
1544
1548
  const doFetch = opts.fetch ?? globalThis.fetch;
1545
1549
  const signal = AbortSignal.timeout(opts.timeoutMs ?? FILE_DOWNLOAD_TIMEOUT_MS);
1546
1550
  const failed = (what) => new FileCryptoError("download_failed", `download ${what}`);
@@ -1593,6 +1597,7 @@ async function downloadFile(share, opts) {
1593
1597
  if (e instanceof FileCryptoError) throw e;
1594
1598
  throw failed(signal.aborted ? "timed out" : "interrupted");
1595
1599
  }
1600
+ if (total !== expected) throw failed("truncated");
1596
1601
  const out = new Uint8Array(total);
1597
1602
  let at = 0;
1598
1603
  for (const c of chunks) {
@@ -1,17 +1,20 @@
1
- // Embedded from lbframe/wacando@618922ecaf2213da18949a8b27d343c8767b64c5 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
1
+ // Embedded from lbframe/wacando@40dd0c30b3653095f08994f9534fd5110e1d0925 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
2
2
  import {
3
3
  MucJoinError,
4
4
  MucOriginTracker,
5
+ ScramAuthError,
5
6
  authenticatedMessageOrigin,
6
7
  defaultTimeoutMs,
7
8
  forwardedStanza,
8
9
  joinRoom,
9
10
  queryMam,
10
11
  refuseSubscriptionRequests,
12
+ scramClient,
13
+ startScramClient,
11
14
  unwrapPolicyDelivery,
12
15
  waitForStanza
13
- } from "./chunks/chunk-53E7SSTF.mjs";
14
- import "./chunks/chunk-PZDQXGA6.mjs";
16
+ } from "./chunks/chunk-FUNUGIGH.mjs";
17
+ import "./chunks/chunk-FFLYSYV6.mjs";
15
18
 
16
19
  // packages/xmpp/src/index.ts
17
20
  import { default as default2 } from "@xmpp/xml";
@@ -73,6 +76,7 @@ async function requestUploadSlot(entity, service, req, timeoutMs = defaultTimeou
73
76
  export {
74
77
  MucJoinError,
75
78
  MucOriginTracker,
79
+ ScramAuthError,
76
80
  announceAvailable,
77
81
  authenticatedMessageOrigin,
78
82
  forwardedStanza,
@@ -80,6 +84,8 @@ export {
80
84
  queryMam,
81
85
  refuseSubscriptionRequests,
82
86
  requestUploadSlot,
87
+ scramClient,
88
+ startScramClient,
83
89
  unwrapPolicyDelivery,
84
90
  uploadServiceJid,
85
91
  default2 as xml
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wacando",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Connect agents with Wacando onboarding and view conversations in the built-in Codex browser.",
5
5
  "author": { "name": "Wacando" },
6
6
  "skills": "./skills/",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wacando-claude",
3
3
  "displayName": "Wacando",
4
- "version": "2.2.0",
4
+ "version": "2.4.0",
5
5
  "description": "Connect agents with Wacando onboarding and view live Claude Code conversations locally.",
6
6
  "author": { "name": "Wacando" },
7
7
  "keywords": ["agents", "conversation", "local", "observability"],
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wacando-cursor",
3
3
  "displayName": "Wacando",
4
- "version": "2.2.0",
4
+ "version": "2.4.0",
5
5
  "description": "Connect agents with Wacando onboarding and view conversations in the built-in Cursor browser.",
6
6
  "author": { "name": "Wacando" },
7
7
  "keywords": ["agents", "conversation", "local", "observability"],