From 9f42cdfb52da6768a189c1c2a11ad3ed42d644c1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 7 Jun 2026 11:44:00 +0000 Subject: [PATCH] Prevent internal RPC error leakage Replaced instances of message.reply(err.message) with a generic user-facing error string. Appended .catch(console.error) to discord API replies. Logged actual error instances internally with console.error. Co-authored-by: DerUntote <8378077+DerUntote@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ bot/modules/bot-uptime.js | 20 ++++++++++---------- bot/modules/dogeTipper.js | 20 +++++++++++++++++--- bot/modules/exampleTipper.js | 9 ++++++--- bot/modules/ftcTipper.js | 20 +++++++++++++++++--- bot/modules/helpTipper.js | 32 +++++++++++++++++++++++--------- bot/modules/lbcTipper.js | 20 +++++++++++++++++--- bot/modules/protonTipper.js | 20 +++++++++++++++++--- bot/modules/pxcTipper.js | 20 +++++++++++++++++--- bot/modules/rvnTipper.js | 20 +++++++++++++++++--- bot/modules/ufoTipper.js | 20 +++++++++++++++++--- bot/modules/vtlTipper.js | 20 +++++++++++++++++--- 12 files changed, 179 insertions(+), 46 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..e987d16 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-06-07 - Prevent internal RPC error leakage +**Vulnerability:** The tipbot leaked internal bitcoind-rpc error messages to users via Discord API `message.reply(err.message)`. +**Learning:** Returning unhandled or raw errors directly to the user is a security vulnerability, as it might disclose sensitive system information, paths, or connection issues in production. Unhandled promise rejections can also crash the entire process. +**Prevention:** Always log the `err` object internally with `console.error(err)` and replace the raw message sent to users with a sanitized, generic message such as 'An internal error occurred while processing your request.' Always use `.catch()` on async functions returning a promise like Discord API calls to prevent unhandled rejection crashes. diff --git a/bot/modules/bot-uptime.js b/bot/modules/bot-uptime.js index 7df747a..69a2f8a 100644 --- a/bot/modules/bot-uptime.js +++ b/bot/modules/bot-uptime.js @@ -6,20 +6,20 @@ exports.commands = ['uptime']; exports.uptime = { usage: '', description: 'gets Uptime for Bot', - process: function(bot, msg, suffix) { + process: function (bot, msg, suffix) { if (suffix != pm2Name) { return; } msg.channel.send( 'i have been Online for ' + - Math.round(bot.uptime / (1000 * 60 * 60 * 24)) + - ' days, ' + - Math.round(bot.uptime / (1000 * 60 * 60)) + - ' hours, ' + - Math.round(bot.uptime / (1000 * 60)) % 60 + - ' minutes, and ' + - Math.round(bot.uptime / 1000) % 60 + - ' seconds' + Math.round(bot.uptime / (1000 * 60 * 60 * 24)) + + ' days, ' + + Math.round(bot.uptime / (1000 * 60 * 60)) + + ' hours, ' + + (Math.round(bot.uptime / (1000 * 60)) % 60) + + ' minutes, and ' + + (Math.round(bot.uptime / 1000) % 60) + + ' seconds', ); - } + }, }; diff --git a/bot/modules/dogeTipper.js b/bot/modules/dogeTipper.js index 54258df..cd182ac 100644 --- a/bot/modules/dogeTipper.js +++ b/bot/modules/dogeTipper.js @@ -158,7 +158,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } doge.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -263,7 +267,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendDOGE(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { doge.sendFrom( tipper, @@ -274,7 +282,13 @@ function sendDOGE(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/exampleTipper.js b/bot/modules/exampleTipper.js index 25be559..2f02b5e 100644 --- a/bot/modules/exampleTipper.js +++ b/bot/modules/exampleTipper.js @@ -143,7 +143,8 @@ function doWithdraw(message, tipper, words, helpmsg) { } ltc.sendFrom(tipper, address, Number(amount), function(err, txId) { if (err) { - message.reply(err.message).then(message => message.delete(10000)); + console.error(err); + message.reply('An internal error occurred while processing your request.').then(message => message.delete(10000)).catch(console.error); } else { message.channel.send({embed:{ title: '**:outbox_tray::money_with_wings::moneybag:Litecoin (LTC) Transaction Completed!:moneybag::money_with_wings::outbox_tray:**', @@ -228,11 +229,13 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendLTC(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function(err, address) { if (err) { - message.reply(err.message).then(message => message.delete(10000)); + console.error(err); + message.reply('An internal error occurred while processing your request.').then(message => message.delete(10000)).catch(console.error); } else { ltc.sendFrom(tipper, address, Number(amount), 1, null, null, function(err, txId) { if (err) { - message.reply(err.message).then(message => message.delete(10000)); + console.error(err); + message.reply('An internal error occurred while processing your request.').then(message => message.delete(10000)).catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient) // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/ftcTipper.js b/bot/modules/ftcTipper.js index 22bb0b9..38e5221 100644 --- a/bot/modules/ftcTipper.js +++ b/bot/modules/ftcTipper.js @@ -158,7 +158,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } ftc.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -263,7 +267,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendFTC(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { ftc.sendFrom( tipper, @@ -274,7 +282,13 @@ function sendFTC(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/helpTipper.js b/bot/modules/helpTipper.js index 95041c9..f87ef49 100644 --- a/bot/modules/helpTipper.js +++ b/bot/modules/helpTipper.js @@ -11,27 +11,41 @@ exports.commands = ['tiphelp']; exports.tiphelp = { usage: '', description: 'This commands has been changed to currency specific commands!', - process: function(bot, message) { + process: function (bot, message) { message.author.send( - '__**Ravencoin (RVN) Tipper**__\nTransaction Fees: **' + ravenFee + '**\n **!tiprvn balance** : get your balance\n **!tiprvn deposit** : get address for your deposits\n **!tiprvn withdraw
** : withdraw coins to specified address\n **!tiprvn <@user> ** :mention a user with @ and then the amount to tip them\n **!tiprvn private ** : put private before Mentioning a user to tip them privately.\n' + '__**Ravencoin (RVN) Tipper**__\nTransaction Fees: **' + + ravenFee + + '**\n **!tiprvn balance** : get your balance\n **!tiprvn deposit** : get address for your deposits\n **!tiprvn withdraw
** : withdraw coins to specified address\n **!tiprvn <@user> ** :mention a user with @ and then the amount to tip them\n **!tiprvn private ** : put private before Mentioning a user to tip them privately.\n', ); message.author.send( - '__**Dogecoin (DOGE) Tipper**__\nTransaction Fees: **' + dogeFee + '**\n **!tipdoge balance** : get your balance\n **!tipdoge deposit** : get address for your deposits\n **!tipdoge withdraw
** : withdraw coins to specified address\n **!tipdoge <@user> ** :mention a user with @ and then the amount to tip them\n **!tipdoge private ** : put private before Mentioning a user to tip them privately.\n' + '__**Dogecoin (DOGE) Tipper**__\nTransaction Fees: **' + + dogeFee + + '**\n **!tipdoge balance** : get your balance\n **!tipdoge deposit** : get address for your deposits\n **!tipdoge withdraw
** : withdraw coins to specified address\n **!tipdoge <@user> ** :mention a user with @ and then the amount to tip them\n **!tipdoge private ** : put private before Mentioning a user to tip them privately.\n', ); message.author.send( - '__**LBRY Credit (LBC) Tipper**__\nTransaction Fees: **' + lbryFee + '**\n **!tiplbc balance** : get your balance\n **!tiplbc deposit** : get address for your deposits\n **!tiplbc withdraw
** : withdraw coins to specified address\n **!tiplbc <@user> ** :mention a user with @ and then the amount to tip them\n **!tiplbc private ** : put private before Mentioning a user to tip them privately.\n' + '__**LBRY Credit (LBC) Tipper**__\nTransaction Fees: **' + + lbryFee + + '**\n **!tiplbc balance** : get your balance\n **!tiplbc deposit** : get address for your deposits\n **!tiplbc withdraw
** : withdraw coins to specified address\n **!tiplbc <@user> ** :mention a user with @ and then the amount to tip them\n **!tiplbc private ** : put private before Mentioning a user to tip them privately.\n', ); message.author.send( - '__**Proton (PROTON) Tipper**__\nTransaction Fees: **' + protonFee + '**\n **!tipproton balance** : get your balance\n **!tipproton deposit** : get address for your deposits\n **!tipproton withdraw
** : withdraw coins to specified address\n **!tipproton <@user> ** :mention a user with @ and then the amount to tip them\n **!tipproton private ** : put private before Mentioning a user to tip them privately.\n' + '__**Proton (PROTON) Tipper**__\nTransaction Fees: **' + + protonFee + + '**\n **!tipproton balance** : get your balance\n **!tipproton deposit** : get address for your deposits\n **!tipproton withdraw
** : withdraw coins to specified address\n **!tipproton <@user> ** :mention a user with @ and then the amount to tip them\n **!tipproton private ** : put private before Mentioning a user to tip them privately.\n', ); message.author.send( - '__**Uniform Fiscal Object (UFO) Tipper**__\nTransaction Fees: **' + ufoFee + '**\n **!tipufo balance** : get your balance\n **!tipufo deposit** : get address for your deposits\n **!tipufo withdraw
** : withdraw coins to specified address\n **!tipufo <@user> ** :mention a user with @ and then the amount to tip them\n **!tipufo private ** : put private before Mentioning a user to tip them privately.\n' + '__**Uniform Fiscal Object (UFO) Tipper**__\nTransaction Fees: **' + + ufoFee + + '**\n **!tipufo balance** : get your balance\n **!tipufo deposit** : get address for your deposits\n **!tipufo withdraw
** : withdraw coins to specified address\n **!tipufo <@user> ** :mention a user with @ and then the amount to tip them\n **!tipufo private ** : put private before Mentioning a user to tip them privately.\n', ); message.author.send( - '__**Phoenixcoin (PXC) Tipper**__\nTransaction Fees: **' + phoenixFee + '**\n **!tippxc balance** : get your balance\n **!tippxc deposit** : get address for your deposits\n **!tippxc withdraw
** : withdraw coins to specified address\n **!tippxc <@user> ** :mention a user with @ and then the amount to tip them\n **!tippxc private ** : put private before Mentioning a user to tip them privately.\n' + '__**Phoenixcoin (PXC) Tipper**__\nTransaction Fees: **' + + phoenixFee + + '**\n **!tippxc balance** : get your balance\n **!tippxc deposit** : get address for your deposits\n **!tippxc withdraw
** : withdraw coins to specified address\n **!tippxc <@user> ** :mention a user with @ and then the amount to tip them\n **!tippxc private ** : put private before Mentioning a user to tip them privately.\n', ); message.author.send( - '__**Feathercoin (FTC) Tipper**__\nTransaction Fees: **' + featherFee + '**\n **!tipftc balance** : get your balance\n **!tipftc deposit** : get address for your deposits\n **!tipufo withdraw
** : withdraw coins to specified address\n **!tipftc <@user> ** :mention a user with @ and then the amount to tip them\n **!tipftc private ** : put private before Mentioning a user to tip them privately.\n\n **<> : Replace with appropriate value.**' + '__**Feathercoin (FTC) Tipper**__\nTransaction Fees: **' + + featherFee + + '**\n **!tipftc balance** : get your balance\n **!tipftc deposit** : get address for your deposits\n **!tipufo withdraw
** : withdraw coins to specified address\n **!tipftc <@user> ** :mention a user with @ and then the amount to tip them\n **!tipftc private ** : put private before Mentioning a user to tip them privately.\n\n **<> : Replace with appropriate value.**', ); - } + }, }; diff --git a/bot/modules/lbcTipper.js b/bot/modules/lbcTipper.js index 09ff576..fb9fd43 100644 --- a/bot/modules/lbcTipper.js +++ b/bot/modules/lbcTipper.js @@ -158,7 +158,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } lbc.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -263,7 +267,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendLBC(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { lbc.sendFrom( tipper, @@ -274,7 +282,13 @@ function sendLBC(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/protonTipper.js b/bot/modules/protonTipper.js index 53da2cd..cc90396 100644 --- a/bot/modules/protonTipper.js +++ b/bot/modules/protonTipper.js @@ -158,7 +158,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } proton.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -263,7 +267,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendPROTON(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { proton.sendFrom( tipper, @@ -274,7 +282,13 @@ function sendPROTON(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/pxcTipper.js b/bot/modules/pxcTipper.js index db95dc4..f7243d2 100644 --- a/bot/modules/pxcTipper.js +++ b/bot/modules/pxcTipper.js @@ -158,7 +158,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } pxc.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -263,7 +267,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendPXC(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { pxc.sendFrom( tipper, @@ -274,7 +282,13 @@ function sendPXC(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/rvnTipper.js b/bot/modules/rvnTipper.js index 814abc4..baef896 100644 --- a/bot/modules/rvnTipper.js +++ b/bot/modules/rvnTipper.js @@ -158,7 +158,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } rvn.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -263,7 +267,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendRVN(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { rvn.sendFrom( tipper, @@ -274,7 +282,13 @@ function sendRVN(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/ufoTipper.js b/bot/modules/ufoTipper.js index 74c1cd5..692ac0f 100644 --- a/bot/modules/ufoTipper.js +++ b/bot/modules/ufoTipper.js @@ -162,7 +162,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } ufo.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -267,7 +271,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendUFO(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { ufo.sendFrom( tipper, @@ -278,7 +286,13 @@ function sendUFO(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search; diff --git a/bot/modules/vtlTipper.js b/bot/modules/vtlTipper.js index 040a3fa..a2fb4c4 100644 --- a/bot/modules/vtlTipper.js +++ b/bot/modules/vtlTipper.js @@ -158,7 +158,11 @@ function doWithdraw(message, tipper, words, helpmsg) { } vtl.sendFrom(tipper, address, Number(amount), function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { message.channel.send({ embed: { @@ -263,7 +267,11 @@ function doTip(bot, message, tipper, words, helpmsg) { function sendVTL(bot, message, tipper, recipient, amount, privacyFlag) { getAddress(recipient.toString(), function (err, address) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply('An internal error occurred while processing your request.') + .then((message) => message.delete(10000)) + .catch(console.error); } else { vtl.sendFrom( tipper, @@ -274,7 +282,13 @@ function sendVTL(bot, message, tipper, recipient, amount, privacyFlag) { null, function (err, txId) { if (err) { - message.reply(err.message).then((message) => message.delete(10000)); + console.error(err); + message + .reply( + 'An internal error occurred while processing your request.', + ) + .then((message) => message.delete(10000)) + .catch(console.error); } else { if (privacyFlag) { let userProfile = message.guild.members.get(recipient); // ⚡ Bolt: O(1) direct ID lookup vs O(N) linear search;