Compare commits
2 commits
eb676256e4
...
93bbc5ea0f
| Author | SHA1 | Date | |
|---|---|---|---|
| 93bbc5ea0f | |||
| 43b8b02b40 |
3 changed files with 76 additions and 2 deletions
41
src/db/migrations/0037-remove-leaked-webhooks.js
Normal file
41
src/db/migrations/0037-remove-leaked-webhooks.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
const {discord, db, from, select, sync} = require("../../passthrough")
|
||||||
|
/** @type {import("../../discord/utils")} */
|
||||||
|
const dUtils = sync.require("../../discord/utils")
|
||||||
|
|
||||||
|
const ones = "₀₁₂₃₄₅₆₇₈₉"
|
||||||
|
const tens = "0123456789"
|
||||||
|
|
||||||
|
/* c8 ignore start */
|
||||||
|
|
||||||
|
module.exports = async function(db) {
|
||||||
|
// added tolerance to https://discordstatus.com/incidents/4hpm4454hxtx
|
||||||
|
const OUTAGE_START = 1778263200000
|
||||||
|
const OUTAGE_END = 1778284800000
|
||||||
|
|
||||||
|
const startSnowflake = dUtils.timestampToSnowflakeInexact(OUTAGE_START)
|
||||||
|
const endSnowflake = dUtils.timestampToSnowflakeInexact(OUTAGE_END)
|
||||||
|
|
||||||
|
const affectedChannels = from("message_room").join("historical_channel_room", "historical_room_index")
|
||||||
|
.pluck("reference_channel_id").selectUnsafe("DISTINCT reference_channel_id")
|
||||||
|
.and("WHERE message_id >= ? AND message_id <= ? AND length(message_id) = ?").all(startSnowflake, endSnowflake, startSnowflake.length)
|
||||||
|
const affectedWebhooks = select("webhook", ["channel_id", "webhook_id", "webhook_token"], {channel_id: affectedChannels}).all()
|
||||||
|
|
||||||
|
if (affectedWebhooks.length) {
|
||||||
|
process.stdout.write(` revoking ${affectedWebhooks.length} possibly compromised webhooks... `)
|
||||||
|
for (let counter = 1; counter <= affectedWebhooks.length; counter++) {
|
||||||
|
const webhook = affectedWebhooks[counter-1]
|
||||||
|
|
||||||
|
await discord.snow.webhook.deleteWebhookToken(webhook.webhook_id, webhook.webhook_token, "Webhook token possibly compromised during 8th May 2026 outage").catch(e => {
|
||||||
|
if (e.message === `{"message": "Unknown Webhook", "code": 10015}`) {
|
||||||
|
// OK
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
})
|
||||||
|
db.prepare("DELETE FROM webhook WHERE channel_id = ?").run(webhook.channel_id)
|
||||||
|
|
||||||
|
process.stdout.write(String(counter).at(-1) === "0" ? tens[(counter/10)%10] : ones[counter%10])
|
||||||
|
}
|
||||||
|
process.stdout.write("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -94,6 +94,11 @@ function printError(type, source, e, payload) {
|
||||||
console.dir(payload, {depth: null})
|
console.dir(payload, {depth: null})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {string} stack */
|
||||||
|
function cleanErrorStack(stack) {
|
||||||
|
return stack.replace(/(\/webhooks\/[0-9]+\/)[a-zA-Z0-9_-]+/g, "$1(redacted)")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} roomID
|
* @param {string} roomID
|
||||||
* @param {"Discord" | "Matrix"} source
|
* @param {"Discord" | "Matrix"} source
|
||||||
|
|
@ -134,7 +139,7 @@ async function sendError(roomID, source, type, e, payload) {
|
||||||
builder.addLine(errorIntroLine)
|
builder.addLine(errorIntroLine)
|
||||||
|
|
||||||
// Where
|
// Where
|
||||||
const stack = stringifyErrorStack(e)
|
const stack = cleanErrorStack(stringifyErrorStack(e))
|
||||||
builder.addLine(`Error trace:\n${stack}`, tag`<details><summary>Error trace</summary><pre>${stack}</pre></details>`)
|
builder.addLine(`Error trace:\n${stack}`, tag`<details><summary>Error trace</summary><pre>${stack}</pre></details>`)
|
||||||
|
|
||||||
// How
|
// How
|
||||||
|
|
@ -502,5 +507,6 @@ async event => {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
module.exports.stringifyErrorStack = stringifyErrorStack
|
module.exports.stringifyErrorStack = stringifyErrorStack
|
||||||
|
module.exports.cleanErrorStack = cleanErrorStack
|
||||||
module.exports.sendError = sendError
|
module.exports.sendError = sendError
|
||||||
module.exports.printError = printError
|
module.exports.printError = printError
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const {stringifyErrorStack} = require("./event-dispatcher")
|
const {stringifyErrorStack, cleanErrorStack} = require("./event-dispatcher")
|
||||||
|
|
||||||
test("stringify error stack: works", t => {
|
test("stringify error stack: works", t => {
|
||||||
function a() {
|
function a() {
|
||||||
|
|
@ -21,3 +21,30 @@ test("stringify error stack: works", t => {
|
||||||
t.match(str, /^ \[prop\]: 2.1$/m)
|
t.match(str, /^ \[prop\]: 2.1$/m)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("clean error stack: removes webhook token", t => {
|
||||||
|
t.notMatch(
|
||||||
|
cleanErrorStack(`
|
||||||
|
DiscordAPIError: Service resource is being rate limited.
|
||||||
|
at fn (/var/home/cadence/out-of-your-element/node_modules/snowtransfer/src/RequestHandler.ts:591:13)
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
|
||||||
|
at exports.RequestHandler.request (/var/home/cadence/out-of-your-element/node_modules/snowtransfer/src/RequestHandler.ts:546:17)
|
||||||
|
at WebhookMethods.executeWebhook (/var/home/cadence/out-of-your-element/node_modules/snowtransfer/src/methods/Webhook.ts:249:35)
|
||||||
|
at /var/home/cadence/out-of-your-element/src/m2d/actions/channel-webhook.js:65:31
|
||||||
|
at withWebhook (/var/home/cadence/out-of-your-element/src/m2d/actions/channel-webhook.js:47:9)
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
|
||||||
|
at async Object.sendMessageWithWebhook (/var/home/cadence/out-of-your-element/src/m2d/actions/channel-webhook.js:64:17)
|
||||||
|
at async Object.sendEvent (/var/home/cadence/out-of-your-element/src/m2d/actions/send-event.js:132:27)
|
||||||
|
at async /var/home/cadence/out-of-your-element/src/m2d/event-dispatcher.js:208:27
|
||||||
|
at async AppService.<anonymous> (/var/home/cadence/out-of-your-element/src/m2d/event-dispatcher.js:162:11) {
|
||||||
|
[method]: "POST"
|
||||||
|
[path]: "/webhooks/1160903754728611841/pfRqHl9vVZImdqwWWSZxxH8T-JJMnauxroMnHsvC6ARA-3B9_STH_bnHB9pd7QQaUVCG"
|
||||||
|
[code]: 40062
|
||||||
|
[httpStatus]: 429
|
||||||
|
[request]: {"endpoint":"/webhooks/1160903754728611841/pfRqHl9vVZImdqwWWSZxxH8T-JJMnauxroMnHsvC6ARA-3B9_STH_bnHB9pd7QQaUVCG","method":"POST","dataType":"json","data":{"content":"https://discordstatus.com/#day\nOnly what discord tell us right now","allowed_mentions":{"parse":["roles"],"users":[]},"username":"lewri","avatar_url":"https://bridge.cadence.moe/download/matrix/matrix.org/URWwrtSUONGOYhfMsdUzcrir"}}
|
||||||
|
[response]: {}
|
||||||
|
[name]: "DiscordAPIError"`
|
||||||
|
),
|
||||||
|
/pfRqHl9v/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue