diff --git a/package-lock.json b/package-lock.json index f0f904aa..7df5df5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -517,7 +517,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -989,7 +988,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -1612,10 +1610,20 @@ } }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -1720,10 +1728,21 @@ } }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", + "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" } @@ -1778,14 +1797,25 @@ } }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "linkify-it": "^5.0.1", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -2664,7 +2694,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3002,7 +3031,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/undici-types": { "version": "7.24.6", diff --git a/package.json b/package.json index 8fcc3bae..552f38cb 100644 --- a/package.json +++ b/package.json @@ -374,5 +374,13 @@ "engines": { "node": ">=18" }, + "overrides": { + "markdown-it": ">=14.2.0", + "js-yaml": ">=4.2.0" + }, + "resolutions": { + "markdown-it": ">=14.2.0", + "js-yaml": ">=4.2.0" + }, "packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c" } diff --git a/scripts/discord/ecc-bot.mjs b/scripts/discord/ecc-bot.mjs index 7699d9ed..7ae8b421 100644 --- a/scripts/discord/ecc-bot.mjs +++ b/scripts/discord/ecc-bot.mjs @@ -26,7 +26,35 @@ const REPO_URL = 'https://github.com/affaan-m/ECC'; const INVITE = process.env.DISCORD_INVITE || ''; const API = 'https://discord.com/api/v10'; -const log = (...a) => console.log(new Date().toISOString(), ...a); +// Strip CR/LF from string args so Discord-payload-controlled values (command +// names, usernames) cannot forge or inject extra log lines (log injection). +const log = (...a) => + console.log(new Date().toISOString(), ...a.map(x => (typeof x === 'string' ? x.replace(/[\r\n]+/g, ' ') : x))); + +// Interaction ids are Discord snowflakes (numeric) and tokens are a bounded +// URL-safe set. Validate before building the callback URL so a malformed or +// hostile gateway payload cannot inject path segments / alter the request +// target (SSRF). The host is always the fixed API constant. +const SNOWFLAKE_RE = /^[0-9]{1,20}$/; +const INTERACTION_TOKEN_RE = /^[A-Za-z0-9._-]{1,255}$/; + +function interactionCallbackUrl(interaction) { + const id = String(interaction?.id ?? ''); + const token = String(interaction?.token ?? ''); + if (!SNOWFLAKE_RE.test(id) || !INTERACTION_TOKEN_RE.test(token)) { + throw new Error('invalid interaction id/token'); + } + return `${API}/interactions/${id}/${token}/callback`; +} + +// Clamp a remote-supplied timer interval to a sane range so a hostile/bogus +// heartbeat_interval cannot spin a tight loop or hang the bot (resource +// exhaustion). Discord's real value is ~41250ms. +function clampHeartbeatInterval(value) { + const n = Number(value); + if (!Number.isFinite(n)) return 41250; + return Math.max(1000, Math.min(n, 600000)); +} // ---------- skill + docs lookup (local clone as the data source) ---------- @@ -148,7 +176,13 @@ const handlers = { async function respond(interaction) { const name = interaction.data?.name; const handler = handlers[name]; - const url = `${API}/interactions/${interaction.id}/${interaction.token}/callback`; + let url; + try { + url = interactionCallbackUrl(interaction); + } catch (err) { + log('rejected interaction', err.message); + return; + } if (!handler) { await fetch(url, { method: 'POST', @@ -192,7 +226,7 @@ async function main() { if (msg.s) seq = msg.s; switch (msg.op) { case 10: { // HELLO - const interval = msg.d.heartbeat_interval; + const interval = clampHeartbeatInterval(msg.d.heartbeat_interval); setTimeout(() => { send({ op: 1, d: seq }); setInterval(() => { diff --git a/yarn.lock b/yarn.lock index f8564d41..538807a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,16 +221,16 @@ __metadata: linkType: hard "@opencode-ai/plugin@npm:^1.16.2": - version: 1.16.2 - resolution: "@opencode-ai/plugin@npm:1.16.2" + version: 1.17.3 + resolution: "@opencode-ai/plugin@npm:1.17.3" dependencies: - "@opencode-ai/sdk": "npm:1.16.2" + "@opencode-ai/sdk": "npm:1.17.3" effect: "npm:4.0.0-beta.74" zod: "npm:4.1.8" peerDependencies: - "@opentui/core": ">=0.3.2" - "@opentui/keymap": ">=0.3.2" - "@opentui/solid": ">=0.3.2" + "@opentui/core": ">=0.3.4" + "@opentui/keymap": ">=0.3.4" + "@opentui/solid": ">=0.3.4" peerDependenciesMeta: "@opentui/core": optional: true @@ -238,16 +238,16 @@ __metadata: optional: true "@opentui/solid": optional: true - checksum: 10c0/a10d6259557c2b587afd93dc690bf5b3b0132272c0b0bca1f77c5a7ea9e3440ebbf1fbdf0a24e4a20ae05f23961c546a2feab53c8f50b10a9d237090c41d2146 + checksum: 10c0/c78d3915ca1e479d638230d4f4a2f439163691c45e88284a6862f53ca349916845bf97ea08b40d59acb00b9af7522d2b45f399b420cfb17cfc2a3db3b02653cc languageName: node linkType: hard -"@opencode-ai/sdk@npm:1.16.2": - version: 1.16.2 - resolution: "@opencode-ai/sdk@npm:1.16.2" +"@opencode-ai/sdk@npm:1.17.3": + version: 1.17.3 + resolution: "@opencode-ai/sdk@npm:1.17.3" dependencies: cross-spawn: "npm:7.0.6" - checksum: 10c0/0f8de5a64bfb4411ecddd0bb2c3049df07a5661a05e7c965e2404692f80394652b00a4d46985e97aabf8b26ac85e4313d8dbf7bce2280a297eb4fa88bf700a6a + checksum: 10c0/5de73b708545623640a03bafd0618961777201c82d542570eca65fe43a485e095ce74d687042cb290fa9a8c3c480e2ed2dba7b02a951ea2f0f3c68cdc7208560 languageName: node linkType: hard @@ -318,10 +318,10 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^4.0.0": - version: 4.0.0 - resolution: "abbrev@npm:4.0.0" - checksum: 10c0/b4cc16935235e80702fc90192e349e32f8ef0ed151ef506aa78c81a7c455ec18375c4125414b99f84b2e055199d66383e787675f0bcd87da7a4dbd59f9eac1d5 +"abbrev@npm:^5.0.0": + version: 5.0.0 + resolution: "abbrev@npm:5.0.0" + checksum: 10c0/8e88f5c798ea4562d28c5a3e9ad69e3879890bc5d695d8f2dffb8609be4c890aacc8f80ef4553fdd2c6a62d70c2ce8bc57b38074e383beb7487bdafa9ed42ea5 languageName: node linkType: hard @@ -344,26 +344,26 @@ __metadata: linkType: hard "ajv@npm:^6.12.4": - version: 6.15.0 - resolution: "ajv@npm:6.15.0" + version: 6.14.0 + resolution: "ajv@npm:6.14.0" dependencies: fast-deep-equal: "npm:^3.1.1" fast-json-stable-stringify: "npm:^2.0.0" json-schema-traverse: "npm:^0.4.1" uri-js: "npm:^4.2.2" - checksum: 10c0/67966499dd272ecde1c2e467084411132891523d057487587879d39ac04207f4351b7b2324c83198013967fbfa632c1612adc960114a30770fbe07a0773b32c2 + checksum: 10c0/a2bc39b0555dc9802c899f86990eb8eed6e366cddbf65be43d5aa7e4f3c4e1a199d5460fd7ca4fb3d864000dbbc049253b72faa83b3b30e641ca52cb29a68c22 languageName: node linkType: hard "ajv@npm:^8.18.0": - version: 8.20.0 - resolution: "ajv@npm:8.20.0" + version: 8.18.0 + resolution: "ajv@npm:8.18.0" dependencies: fast-deep-equal: "npm:^3.1.3" fast-uri: "npm:^3.0.1" json-schema-traverse: "npm:^1.0.0" require-from-string: "npm:^2.0.2" - checksum: 10c0/5df9a1c8f83863cde1bd3a9ddb426f599718f88e3dc9153616c79fb28e0be455335830d7f21d745576519f057b371352daa31047b6a33d7036fe08777d60cf2a + checksum: 10c0/e7517c426173513a07391be951879932bdf3348feaebd2199f5b901c20f99d60db8cd1591502d4d551dc82f594e82a05c4fe1c70139b15b8937f7afeaed9532f languageName: node linkType: hard @@ -967,9 +967,9 @@ __metadata: linkType: hard "globals@npm:^17.4.0": - version: 17.6.0 - resolution: "globals@npm:17.6.0" - checksum: 10c0/cf94fb4329cc5c68cf81018fd68324f413181ee169f0235b0b33b82bc93fe7825a21beea951f83a80e8e4bbdad9c0c80515a145b5fd4b5cb52f2a80db899a93f + version: 17.4.0 + resolution: "globals@npm:17.4.0" + checksum: 10c0/2be9e8c2b9035836f13d420b22f0247a328db82967d3bebfc01126d888ed609305f06c05895914e969653af5c6ba35fd7a0920f3e6c869afa60666c810630feb languageName: node linkType: hard @@ -1135,14 +1135,14 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.1, js-yaml@npm:~4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" +"js-yaml@npm:>=4.2.0": + version: 4.2.0 + resolution: "js-yaml@npm:4.2.0" dependencies: argparse: "npm:^2.0.1" bin: js-yaml: bin/js-yaml.js - checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 + checksum: 10c0/1916456c118746603b067d74bbcbb0445d9a1d5e474ad4ae775e7b20525bed902e01d9d97dd0c81fcd8d4f596162309d0eb057f4aa38f3e9647f14075e9dea45 languageName: node linkType: hard @@ -1225,12 +1225,12 @@ __metadata: languageName: node linkType: hard -"linkify-it@npm:^5.0.0": - version: 5.0.0 - resolution: "linkify-it@npm:5.0.0" +"linkify-it@npm:^5.0.1": + version: 5.0.1 + resolution: "linkify-it@npm:5.0.1" dependencies: uc.micro: "npm:^2.0.0" - checksum: 10c0/ff4abbcdfa2003472fc3eb4b8e60905ec97718e11e33cca52059919a4c80cc0e0c2a14d23e23d8c00e5402bc5a885cdba8ca053a11483ab3cc8b3c7a52f88e2d + checksum: 10c0/d06d04f1ed03be131740fc900a5e74ea1f49886b052213599e306d469d5ffe2303db76dd8f771de9f28e2b0b38852de22ec46ae597d245f8b66439b0ceb19b10 languageName: node linkType: hard @@ -1266,19 +1266,19 @@ __metadata: languageName: node linkType: hard -"markdown-it@npm:~14.1.1": - version: 14.1.1 - resolution: "markdown-it@npm:14.1.1" +"markdown-it@npm:>=14.2.0": + version: 14.2.0 + resolution: "markdown-it@npm:14.2.0" dependencies: argparse: "npm:^2.0.1" entities: "npm:^4.4.0" - linkify-it: "npm:^5.0.0" + linkify-it: "npm:^5.0.1" mdurl: "npm:^2.0.0" punycode.js: "npm:^2.3.1" uc.micro: "npm:^2.1.0" bin: markdown-it: bin/markdown-it.mjs - checksum: 10c0/c67f2a4c8069a307c78d8c15104bbcb15a2c6b17f4c904364ca218ec2eccf76a397eba1ea05f5ac5de72c4b67fcf115d422d22df0bfb86a09b663f55b9478d4f + checksum: 10c0/1d3a50061d2fe4efbcf317aac853dbee6892ed6f5a217570eead723f2ef2dd1c9baaeef5a687cd283480c45c2d20724a73e84a9ed72843cf7b3b719067af40ef languageName: node linkType: hard @@ -1742,33 +1742,33 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 12.3.0 - resolution: "node-gyp@npm:12.3.0" + version: 13.0.0 + resolution: "node-gyp@npm:13.0.0" dependencies: env-paths: "npm:^2.2.0" exponential-backoff: "npm:^3.1.1" graceful-fs: "npm:^4.2.6" - nopt: "npm:^9.0.0" - proc-log: "npm:^6.0.0" + nopt: "npm:^10.0.0" + proc-log: "npm:^7.0.0" semver: "npm:^7.3.5" tar: "npm:^7.5.4" tinyglobby: "npm:^0.2.12" undici: "npm:^6.25.0" - which: "npm:^6.0.0" + which: "npm:^7.0.0" bin: node-gyp: bin/node-gyp.js - checksum: 10c0/9d9032b405cbe42f72a105259d9eb679376470c102df4a2dbaa51e07d59bf741dcffb85897087ea9d8318b9cabb824a8978af51508ae142f0239ae1e6a3c2329 + checksum: 10c0/e7525c427db2d16aa368b8947187de83083d2a8dda23e3e096a71c22ae637ac5bb8ed7cf6c871f1b9118cd2729dbfee4ff3a4245e2b79226900227b15831b492 languageName: node linkType: hard -"nopt@npm:^9.0.0": - version: 9.0.0 - resolution: "nopt@npm:9.0.0" +"nopt@npm:^10.0.0": + version: 10.0.1 + resolution: "nopt@npm:10.0.1" dependencies: - abbrev: "npm:^4.0.0" + abbrev: "npm:^5.0.0" bin: nopt: bin/nopt.js - checksum: 10c0/1822eb6f9b020ef6f7a7516d7b64a8036e09666ea55ac40416c36e4b2b343122c3cff0e2f085675f53de1d2db99a2a89a60ccea1d120bcd6a5347bf6ceb4a7fd + checksum: 10c0/980d89257f9587f3e1f77877ddbf905d6aa3b738ec33e49a4fa1a059a0dd82eb28063982b150654a7ae9de386f2ead60e56172db7d37cf56de545f7392a2a26a languageName: node linkType: hard @@ -1866,10 +1866,10 @@ __metadata: languageName: node linkType: hard -"proc-log@npm:^6.0.0": - version: 6.1.0 - resolution: "proc-log@npm:6.1.0" - checksum: 10c0/4f178d4062733ead9d71a9b1ab24ebcecdfe2250916a5b1555f04fe2eda972a0ec76fbaa8df1ad9c02707add6749219d118a4fc46dc56bdfe4dde4b47d80bb82 +"proc-log@npm:^7.0.0": + version: 7.0.0 + resolution: "proc-log@npm:7.0.0" + checksum: 10c0/b89c2d862604f35fec795477b0c7e376feab3ba0d4f4d291c4e959567442697cf451ac557d0623c1cc38af45a78128b983410f397a10c5d3a67f76c33de4754b languageName: node linkType: hard @@ -1929,7 +1929,16 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.5.3": +"semver@npm:^7.3.5": + version: 7.8.4 + resolution: "semver@npm:7.8.4" + bin: + semver: bin/semver.js + checksum: 10c0/81b7c296fd7927b80f67fa516b75fa1017caac8167795320de28e76ccbc6f7f01763c30ecd10d6a0d8fd089708ab0548a5aebb94b0870e99c2a2b4600a46389b + languageName: node + linkType: hard + +"semver@npm:^7.5.3": version: 7.7.4 resolution: "semver@npm:7.7.4" bin: @@ -2055,12 +2064,12 @@ __metadata: linkType: hard "tinyglobby@npm:^0.2.12": - version: 0.2.16 - resolution: "tinyglobby@npm:0.2.16" + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" dependencies: fdir: "npm:^6.5.0" picomatch: "npm:^4.0.4" - checksum: 10c0/f2e09fd93dd95c41e522113b686ff6f7c13020962f8698a864a257f3d7737599afc47722b7ab726e12f8a813f779906187911ff8ee6701ede65072671a7e934b + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c languageName: node linkType: hard @@ -2125,9 +2134,9 @@ __metadata: linkType: hard "undici@npm:^6.25.0": - version: 6.25.0 - resolution: "undici@npm:6.25.0" - checksum: 10c0/2597cc6689bdb02c210c557b1f85febbfda65becae6e6fc1061508e2f33734d25207f81cd8af56ada9956329eb3a7bd7431e87dcfeceba20ee87059b57dcf985 + version: 6.27.0 + resolution: "undici@npm:6.27.0" + checksum: 10c0/f88c3dae3957dbf9d93cb481440aced317bd3c4941b5914fea5efba516d51138988cdb5c76006f0bb1337e41d56c3443351055d492e73af2428521c37ba2a76f languageName: node linkType: hard @@ -2171,14 +2180,14 @@ __metadata: languageName: node linkType: hard -"which@npm:^6.0.0": - version: 6.0.1 - resolution: "which@npm:6.0.1" +"which@npm:^7.0.0": + version: 7.0.0 + resolution: "which@npm:7.0.0" dependencies: isexe: "npm:^4.0.0" bin: node-which: bin/which.js - checksum: 10c0/7e710e54ea36d2d6183bee2f9caa27a3b47b9baf8dee55a199b736fcf85eab3b9df7556fca3d02b50af7f3dfba5ea3a45644189836df06267df457e354da66d5 + checksum: 10c0/ca0b54f198f78bbc4b7c02e34bda8d335cb352e0adb4cbca1c37b1a957af3a879a82c4c27ca6525bc942f548d8b64f816ef6528360af9f3de55ffb9b979b620d languageName: node linkType: hard