serverless-ircd 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +28 -0
- package/.github/workflows/deploy-cf-tcp.yml +26 -2
- package/.github/workflows/deploy-cf.yml +26 -0
- package/CHANGELOG.md +289 -0
- package/README.md +153 -20
- package/apps/aws-stack/bin/aws.ts +36 -0
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +221 -15
- package/apps/aws-stack/tests/stack.test.ts +450 -16
- package/apps/cf-tcp-container/Dockerfile +37 -5
- package/apps/cf-tcp-container/package.json +7 -2
- package/apps/cf-tcp-container/src/config-loader.ts +113 -2
- package/apps/cf-tcp-container/src/container-server.ts +256 -79
- package/apps/cf-tcp-container/src/main.ts +22 -7
- package/apps/cf-tcp-container/src/proxy-protocol.ts +112 -0
- package/apps/cf-tcp-container/terraform/spectrum.tf +40 -11
- package/apps/cf-tcp-container/tests/config-loader.test.ts +170 -0
- package/apps/cf-tcp-container/tests/container-server-tls.test.ts +382 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +358 -31
- package/apps/cf-tcp-container/tests/dockerfile.test.ts +110 -0
- package/apps/cf-tcp-container/tests/proxy-protocol.test.ts +187 -0
- package/apps/cf-tcp-container/tests/spectrum-terraform.test.ts +135 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +5 -1
- package/apps/cf-tcp-container/wrangler.toml +17 -4
- package/apps/cf-worker/package.json +2 -2
- package/apps/cf-worker/src/worker.ts +77 -5
- package/apps/cf-worker/tests/raw-modules.d.ts +11 -0
- package/apps/cf-worker/tests/smoke.test.ts +4 -0
- package/apps/cf-worker/tests/wrangler-config.test.ts +47 -0
- package/apps/cf-worker/tests/ws-admission.test.ts +112 -0
- package/apps/cf-worker/tests/ws-rate-limit.test.ts +133 -0
- package/apps/cf-worker/wrangler.test.toml +15 -1
- package/apps/cf-worker/wrangler.toml +86 -9
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +14 -2
- package/apps/local-cli/src/line-scanner.ts +26 -0
- package/apps/local-cli/src/server.ts +23 -2
- package/apps/local-cli/tests/line-scanner.test.ts +64 -0
- package/apps/local-cli/tests/tcp.test.ts +29 -0
- package/apps/web/package.json +1 -1
- package/docs/AWS-Deployment.md +123 -22
- package/docs/AWS-TCP-Deployment.md +37 -2
- package/docs/Chat-History.md +55 -0
- package/docs/Cloudflare-Deployment-Guide.md +9 -2
- package/docs/Cloudflare-TCP-Deployment.md +135 -52
- package/docs/SASL-EXTERNAL.md +175 -0
- package/package.json +3 -3
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/admission.ts +28 -13
- package/packages/aws-adapter/src/aws-runtime.ts +30 -3
- package/packages/aws-adapter/src/cdk-table-defs.ts +34 -6
- package/packages/aws-adapter/src/config-loader.ts +134 -6
- package/packages/aws-adapter/src/dynamo-services-store.ts +12 -0
- package/packages/aws-adapter/src/handlers/connect.ts +47 -1
- package/packages/aws-adapter/src/handlers/default.ts +95 -6
- package/packages/aws-adapter/src/handlers/index.ts +31 -2
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +132 -8
- package/packages/aws-adapter/src/ip-admission.ts +79 -0
- package/packages/aws-adapter/src/serialize.ts +8 -0
- package/packages/aws-adapter/src/tables.ts +9 -0
- package/packages/aws-adapter/tests/admission.test.ts +60 -2
- package/packages/aws-adapter/tests/aws-harness.ts +23 -1
- package/packages/aws-adapter/tests/aws-runtime.test.ts +64 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +151 -0
- package/packages/aws-adapter/tests/connect.test.ts +199 -2
- package/packages/aws-adapter/tests/default-frame-limit.test.ts +231 -0
- package/packages/aws-adapter/tests/default-occ.test.ts +10 -3
- package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +123 -1
- package/packages/aws-adapter/tests/handlers.test.ts +57 -1
- package/packages/aws-adapter/tests/nlb-secure.test.ts +362 -0
- package/packages/aws-adapter/tests/nlb-stream.test.ts +628 -9
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/cf-runtime.ts +48 -9
- package/packages/cf-adapter/src/config-loader.ts +133 -8
- package/packages/cf-adapter/src/connection-do.ts +154 -21
- package/packages/cf-adapter/src/counter-do.ts +142 -0
- package/packages/cf-adapter/src/d1-services-store.ts +47 -5
- package/packages/cf-adapter/src/env.ts +88 -0
- package/packages/cf-adapter/src/index.ts +17 -1
- package/packages/cf-adapter/src/rate-limit-do.ts +87 -0
- package/packages/cf-adapter/tests/cf-runtime.test.ts +104 -15
- package/packages/cf-adapter/tests/config-loader.test.ts +159 -0
- package/packages/cf-adapter/tests/connection-do-counter.test.ts +165 -0
- package/packages/cf-adapter/tests/connection-do-frame-limit.test.ts +177 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +74 -5
- package/packages/cf-adapter/tests/connection-do-ws-spec-contract.test.ts +7 -4
- package/packages/cf-adapter/tests/counter-do.test.ts +181 -0
- package/packages/cf-adapter/tests/d1-services-store.test.ts +192 -1
- package/packages/cf-adapter/tests/rate-limit-do.test.ts +160 -0
- package/packages/cf-adapter/tests/worker/main.ts +4 -0
- package/packages/cf-adapter/wrangler.test.toml +18 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +25 -0
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +74 -0
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/src/caps/capabilities.ts +20 -10
- package/packages/irc-core/src/certfp.ts +178 -0
- package/packages/irc-core/src/commands/cap.ts +10 -2
- package/packages/irc-core/src/commands/chanserv.ts +117 -14
- package/packages/irc-core/src/commands/chathistory.ts +13 -5
- package/packages/irc-core/src/commands/hostserv.ts +84 -8
- package/packages/irc-core/src/commands/index.ts +2 -1
- package/packages/irc-core/src/commands/invite.ts +1 -7
- package/packages/irc-core/src/commands/join.ts +1 -16
- package/packages/irc-core/src/commands/kick.ts +1 -8
- package/packages/irc-core/src/commands/list.ts +1 -8
- package/packages/irc-core/src/commands/mode.ts +1 -8
- package/packages/irc-core/src/commands/multiline.ts +4 -10
- package/packages/irc-core/src/commands/names.ts +53 -13
- package/packages/irc-core/src/commands/nickserv.ts +40 -1
- package/packages/irc-core/src/commands/oper.ts +361 -8
- package/packages/irc-core/src/commands/part.ts +4 -10
- package/packages/irc-core/src/commands/privmsg.ts +8 -4
- package/packages/irc-core/src/commands/registration.ts +146 -2
- package/packages/irc-core/src/commands/sasl.ts +136 -19
- package/packages/irc-core/src/commands/topic.ts +10 -12
- package/packages/irc-core/src/commands/who.ts +1 -8
- package/packages/irc-core/src/config.ts +393 -20
- package/packages/irc-core/src/effects.ts +24 -0
- package/packages/irc-core/src/flood-control.ts +10 -10
- package/packages/irc-core/src/frame-rate-limit.ts +82 -0
- package/packages/irc-core/src/index.ts +8 -0
- package/packages/irc-core/src/oper-hashing.ts +43 -0
- package/packages/irc-core/src/oper-lockout.ts +87 -0
- package/packages/irc-core/src/ports.ts +395 -36
- package/packages/irc-core/src/protocol/bytes.ts +65 -0
- package/packages/irc-core/src/protocol/channel-name.ts +37 -0
- package/packages/irc-core/src/protocol/index.ts +12 -1
- package/packages/irc-core/src/protocol/outbound.ts +43 -10
- package/packages/irc-core/src/protocol/parser.ts +79 -10
- package/packages/irc-core/src/state/connection.ts +13 -0
- package/packages/irc-core/src/types.ts +228 -13
- package/packages/irc-core/src/ws-framing.ts +5 -4
- package/packages/irc-core/tests/bytes.test.ts +89 -0
- package/packages/irc-core/tests/certfp.test.ts +117 -0
- package/packages/irc-core/tests/commands/cap.test.ts +76 -2
- package/packages/irc-core/tests/commands/chanserv.test.ts +166 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +140 -0
- package/packages/irc-core/tests/commands/hostserv.test.ts +316 -0
- package/packages/irc-core/tests/commands/join.test.ts +78 -1
- package/packages/irc-core/tests/commands/names.test.ts +193 -0
- package/packages/irc-core/tests/commands/nickserv.test.ts +182 -2
- package/packages/irc-core/tests/commands/oper.test.ts +560 -2
- package/packages/irc-core/tests/commands/privmsg.test.ts +16 -0
- package/packages/irc-core/tests/commands/registration.test.ts +463 -1
- package/packages/irc-core/tests/commands/sasl.test.ts +596 -7
- package/packages/irc-core/tests/commands/topic.test.ts +137 -2
- package/packages/irc-core/tests/commands/unified-account.test.ts +2 -0
- package/packages/irc-core/tests/config.test.ts +534 -2
- package/packages/irc-core/tests/effects.test.ts +14 -0
- package/packages/irc-core/tests/flood-control.test.ts +29 -1
- package/packages/irc-core/tests/frame-rate-limit.test.ts +98 -0
- package/packages/irc-core/tests/oper-hashing.test.ts +60 -0
- package/packages/irc-core/tests/oper-lockout.test.ts +74 -0
- package/packages/irc-core/tests/outbound.test.ts +148 -0
- package/packages/irc-core/tests/parser.test.ts +287 -5
- package/packages/irc-core/tests/persistent-services-store.test.ts +141 -0
- package/packages/irc-core/tests/ports.test.ts +99 -7
- package/packages/irc-core/tests/services-store.test.ts +376 -14
- package/packages/irc-core/tests/ws-framing.test.ts +45 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +123 -8
- package/packages/irc-server/src/dispatch.ts +1 -0
- package/packages/irc-server/src/index.ts +7 -0
- package/packages/irc-server/src/redact.ts +159 -0
- package/packages/irc-server/src/runtime.ts +14 -0
- package/packages/irc-server/src/transport.ts +28 -1
- package/packages/irc-server/tests/actor.test.ts +544 -7
- package/packages/irc-server/tests/dispatch.test.ts +31 -0
- package/packages/irc-server/tests/redact.test.ts +198 -0
- package/packages/irc-server/tests/runtime.test.ts +2 -0
- package/packages/irc-server/tests/transport.test.ts +66 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +4 -0
- package/scripts/package.json +1 -1
- package/tools/ci-hardening/package.json +2 -2
- package/tools/ci-hardening/src/cf-deploy-cli.ts +3 -0
- package/tools/ci-hardening/src/cf-deploy.ts +118 -0
- package/tools/ci-hardening/src/deploy-hostname.ts +118 -0
- package/tools/ci-hardening/src/env-var-drift.ts +192 -0
- package/tools/ci-hardening/src/hostname-guard.ts +11 -0
- package/tools/ci-hardening/src/index.ts +17 -0
- package/tools/ci-hardening/tests/__wrangler_missing__.toml +2 -0
- package/tools/ci-hardening/tests/__wrangler_placeholder__.toml +3 -0
- package/tools/ci-hardening/tests/__wrangler_real__.toml +3 -0
- package/tools/ci-hardening/tests/cf-deploy.test.ts +200 -0
- package/tools/ci-hardening/tests/deploy-hostname.test.ts +348 -0
- package/tools/ci-hardening/tests/env-var-drift.test.ts +284 -0
- package/tools/ci-hardening/vitest.config.ts +5 -1
- package/tools/hash-oper-cred.ts +85 -0
- package/tools/load-test/package.json +1 -1
- package/tools/tcp-ws-forwarder/package.json +1 -1
|
@@ -29,9 +29,10 @@ function makeCtx(
|
|
|
29
29
|
conn: ConnectionState,
|
|
30
30
|
clock = new FakeClock(1_000),
|
|
31
31
|
services?: ServicesStore,
|
|
32
|
+
config: ServerConfig = serverConfig,
|
|
32
33
|
): Ctx {
|
|
33
34
|
return buildCtx({
|
|
34
|
-
serverConfig,
|
|
35
|
+
serverConfig: config,
|
|
35
36
|
clock,
|
|
36
37
|
ids: new SequentialIdFactory(),
|
|
37
38
|
motd: EmptyMotdProvider,
|
|
@@ -182,6 +183,21 @@ describe('topicReducer — writes', () => {
|
|
|
182
183
|
expect(out.state.topic?.text).toBe('a'.repeat(serverConfig.topicLen));
|
|
183
184
|
});
|
|
184
185
|
|
|
186
|
+
it('keeps an ASCII topic of exactly topicLen bytes unchanged (no truncation)', () => {
|
|
187
|
+
const chan = makeChan('#foo');
|
|
188
|
+
addMember(chan, 'c1', 'alice');
|
|
189
|
+
const conn = makeConn();
|
|
190
|
+
const ctx = makeCtx(conn, new FakeClock(1_000), undefined, {
|
|
191
|
+
...serverConfig,
|
|
192
|
+
topicLen: 64,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const topic = 'a'.repeat(64);
|
|
196
|
+
const out = topicReducer(chan, { command: 'TOPIC', params: ['#foo', topic], tags: {} }, ctx);
|
|
197
|
+
|
|
198
|
+
expect(out.state.topic?.text).toBe(topic);
|
|
199
|
+
});
|
|
200
|
+
|
|
185
201
|
it('emits 482 ERR_CHANOPRIVSNEEDED when +t is set and the writer is a non-op member', () => {
|
|
186
202
|
const chan = makeChan('#foo');
|
|
187
203
|
chan.modes.topicLock = true;
|
|
@@ -238,9 +254,91 @@ describe('topicReducer — writes', () => {
|
|
|
238
254
|
});
|
|
239
255
|
|
|
240
256
|
// ============================================================================
|
|
241
|
-
// topicReducer —
|
|
257
|
+
// topicReducer — byte-budget truncation (TOPICLEN as a UTF-8 byte ceiling)
|
|
242
258
|
// ============================================================================
|
|
243
259
|
|
|
260
|
+
describe('topicReducer — byte-budget truncation', () => {
|
|
261
|
+
const byteCtx = (conn: ConnectionState): Ctx =>
|
|
262
|
+
makeCtx(conn, new FakeClock(1_000), undefined, { ...serverConfig, topicLen: 64 });
|
|
263
|
+
|
|
264
|
+
it('truncates a multi-byte Latin-1 topic to the byte budget, not the char budget', () => {
|
|
265
|
+
const chan = makeChan('#foo');
|
|
266
|
+
addMember(chan, 'c1', 'alice');
|
|
267
|
+
const conn = makeConn();
|
|
268
|
+
const ctx = byteCtx(conn);
|
|
269
|
+
|
|
270
|
+
// 50 `é` chars = 50 UTF-16 units (under the 64 char budget) but 100
|
|
271
|
+
// UTF-8 bytes (over the 64 byte budget). Char-based slicing keeps all
|
|
272
|
+
// 50; byte-based truncation must keep exactly 32 (64 bytes).
|
|
273
|
+
const out = topicReducer(
|
|
274
|
+
chan,
|
|
275
|
+
{ command: 'TOPIC', params: ['#foo', 'é'.repeat(50)], tags: {} },
|
|
276
|
+
ctx,
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(out.state.topic?.text).toBe('é'.repeat(32));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('backs off to a complete-char boundary when the budget lands mid-CJK-char', () => {
|
|
283
|
+
const chan = makeChan('#foo');
|
|
284
|
+
addMember(chan, 'c1', 'alice');
|
|
285
|
+
const conn = makeConn();
|
|
286
|
+
const ctx = byteCtx(conn);
|
|
287
|
+
|
|
288
|
+
// `好` is 3 UTF-8 bytes. 64 bytes fits 21 whole chars (63 bytes); the
|
|
289
|
+
// 22nd would need 66 bytes, so it is dropped whole and the result is a
|
|
290
|
+
// valid-UTF-8 string 1 byte under budget — never split mid-char.
|
|
291
|
+
const out = topicReducer(
|
|
292
|
+
chan,
|
|
293
|
+
{ command: 'TOPIC', params: ['#foo', '好'.repeat(30)], tags: {} },
|
|
294
|
+
ctx,
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
expect(out.state.topic?.text).toBe('好'.repeat(21));
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('truncates an emoji topic without splitting surrogate pairs', () => {
|
|
301
|
+
const chan = makeChan('#foo');
|
|
302
|
+
addMember(chan, 'c1', 'alice');
|
|
303
|
+
const conn = makeConn();
|
|
304
|
+
const ctx = byteCtx(conn);
|
|
305
|
+
|
|
306
|
+
// `😀` is astral: 2 UTF-16 code units and 4 UTF-8 bytes each. 20 emoji
|
|
307
|
+
// = 40 UTF-16 units (under 64 chars) but 80 bytes (over budget). The
|
|
308
|
+
// byte budget fits exactly 16 emoji (64 bytes); a naive UTF-16 slice
|
|
309
|
+
// at 64 units would keep all 20, and a naive byte slice at 64 would
|
|
310
|
+
// split a surrogate pair.
|
|
311
|
+
const out = topicReducer(
|
|
312
|
+
chan,
|
|
313
|
+
{ command: 'TOPIC', params: ['#foo', '😀'.repeat(20)], tags: {} },
|
|
314
|
+
ctx,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
expect(out.state.topic?.text).toBe('😀'.repeat(16));
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('broadcasts and persists the byte-truncated text', () => {
|
|
321
|
+
const chan = makeChan('#foo');
|
|
322
|
+
addMember(chan, 'c1', 'alice');
|
|
323
|
+
const conn = makeConn();
|
|
324
|
+
const ctx = byteCtx(conn);
|
|
325
|
+
|
|
326
|
+
const out = topicReducer(
|
|
327
|
+
chan,
|
|
328
|
+
{ command: 'TOPIC', params: ['#foo', 'é'.repeat(50)], tags: {} },
|
|
329
|
+
ctx,
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
const truncated = 'é'.repeat(32);
|
|
333
|
+
expect(out.effects).toEqual<EffectType[]>([
|
|
334
|
+
Effect.applyChannelDelta('#foo', {
|
|
335
|
+
topic: { text: truncated, setter: 'alice!alice@example.com', setAt: 1_000 },
|
|
336
|
+
}),
|
|
337
|
+
Effect.broadcast('#foo', [L(`:alice!alice@example.com TOPIC #foo :${truncated}`)]),
|
|
338
|
+
]);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
244
342
|
describe('topicReducer — rejections', () => {
|
|
245
343
|
it('emits 461 ERR_NEEDMOREPARAMS when no channel is supplied', () => {
|
|
246
344
|
const chan = makeChan('#foo');
|
|
@@ -279,6 +377,43 @@ describe('topicReducer — rejections', () => {
|
|
|
279
377
|
]);
|
|
280
378
|
});
|
|
281
379
|
|
|
380
|
+
it('emits 403 ERR_NOSUCHCHANNEL for a channel name containing NUL', () => {
|
|
381
|
+
const chan = makeChan('#foo');
|
|
382
|
+
const conn = makeConn();
|
|
383
|
+
const ctx = makeCtx(conn);
|
|
384
|
+
|
|
385
|
+
const out = topicReducer(chan, { command: 'TOPIC', params: ['#foo\0bar'], tags: {} }, ctx);
|
|
386
|
+
|
|
387
|
+
expect(out.effects).toEqual<EffectType[]>([
|
|
388
|
+
Effect.send('c1', [L(':irc.example.com 403 alice #foo\0bar :No such channel')]),
|
|
389
|
+
]);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('emits 403 ERR_NOSUCHCHANNEL for a channel name containing a control character', () => {
|
|
393
|
+
const chan = makeChan('#foo');
|
|
394
|
+
const conn = makeConn();
|
|
395
|
+
const ctx = makeCtx(conn);
|
|
396
|
+
|
|
397
|
+
const out = topicReducer(chan, { command: 'TOPIC', params: ['#foo\x1fbar'], tags: {} }, ctx);
|
|
398
|
+
|
|
399
|
+
expect(out.effects).toEqual<EffectType[]>([
|
|
400
|
+
Effect.send('c1', [L(':irc.example.com 403 alice #foo\x1fbar :No such channel')]),
|
|
401
|
+
]);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('emits 403 ERR_NOSUCHCHANNEL for a channel name whose UTF-8 byte size exceeds the cap', () => {
|
|
405
|
+
const long = `#${'é'.repeat(25)}`; // 51 UTF-8 bytes, only 26 UTF-16 units
|
|
406
|
+
const chan = makeChan(long);
|
|
407
|
+
const conn = makeConn();
|
|
408
|
+
const ctx = makeCtx(conn);
|
|
409
|
+
|
|
410
|
+
const out = topicReducer(chan, { command: 'TOPIC', params: [long], tags: {} }, ctx);
|
|
411
|
+
|
|
412
|
+
expect(out.effects).toEqual<EffectType[]>([
|
|
413
|
+
Effect.send('c1', [L(`:irc.example.com 403 alice ${long} :No such channel`)]),
|
|
414
|
+
]);
|
|
415
|
+
});
|
|
416
|
+
|
|
282
417
|
it('emits 403 ERR_NOSUCHCHANNEL for an empty channel name', () => {
|
|
283
418
|
const chan = makeChan('#foo');
|
|
284
419
|
const conn = makeConn();
|
|
@@ -409,6 +409,8 @@ describe('T156 unified account: scrypt-hashed credentials at rest', () => {
|
|
|
409
409
|
createdAt: NOW,
|
|
410
410
|
enforce: 'none',
|
|
411
411
|
certSubjects: [],
|
|
412
|
+
identifyFailures: [],
|
|
413
|
+
pendingIdentifyFailureNotice: 0,
|
|
412
414
|
// Malformed credential — empty hash, rejected by verifyHashedPassword.
|
|
413
415
|
credential: { account: 'legacy', algorithm: 'scrypt', salt: 's', hash: '' },
|
|
414
416
|
},
|