audit week 3 quality batch: QUAL-004/005/007/008/010 + SEC-002
QUAL-004 handlers/messages.js — DM-on-customer-reply now reads guild.members.cache.get(claimerId) first and only falls back to guild.members.fetch on cache miss. Avoids a REST round-trip per non-staff reply on busy tickets. GuildMembers intent already keeps the cache warm. QUAL-005 handlers/buttons.js (runFinalClose) + handlers/commands/close.js (finalizeForceClose) — close paths now $unset welcomeMessageId alongside the status: 'closed' write. Stops a stale message-ID from carrying into a future reopen on the same Gmail thread, where escalation's "edit welcome buttons" path would silently fail trying to fetch a message in a deleted channel. QUAL-007 services/configPersistence.js — writeEnvFile mismatch error now includes the missing/extra key sets, not just count vs count. Saves the operator from guessing which key vanished after a partial write. QUAL-008 utils.js stripEmailQuotes — replaced order-dependent first-match loop with an earliest-match-across-all-markers scan. The previous code could truncate at a late "_____" signature underline even when an earlier "On X wrote:" reply header was the real cutoff. New test in tests/utils.test.js exercises the dual-marker case. QUAL-010 broccolini-discord.js — moved `let httpServer / internalServer / appReady` declarations from after the ready handler to before it. Same runtime behavior (module-load completes before ready fires asynchronously), but the read order now matches the assignment order. SEC-002 routes/internalApi.js — POST /restart now goes through a tighter 2/min limiter on top of the shared 10/min internalLimiter. Defense in depth in case INTERNAL_API_SECRET ever leaks; an attacker with the secret can no longer crash-loop the container. Skipped: QUAL-009 (re-checked the regex; ^\s*\n* → \n is already idempotent — the audit finding was incorrect). vitest run: 88/88 (one new test for QUAL-008).
This commit is contained in:
@@ -165,6 +165,14 @@ client.on('messageCreate', async msg => {
|
||||
await handleDiscordReply(msg);
|
||||
});
|
||||
|
||||
// HTTP server handles + readiness flag. Assigned inside the ready callback
|
||||
// (httpServer, appReady) and the INTERNAL_API_SECRET branch below
|
||||
// (internalServer); declared here so they're visible to the ready callback,
|
||||
// the express middleware below, and the shutdown handler at the bottom.
|
||||
let httpServer = null;
|
||||
let internalServer = null;
|
||||
let appReady = false;
|
||||
|
||||
client.once('ready', async () => {
|
||||
if (!process.env.MONGODB_URI) {
|
||||
console.error('MONGODB_URI is not set in .env. Broccolini Bot requires MongoDB.');
|
||||
@@ -228,7 +236,7 @@ client.login(CONFIG.DISCORD_TOKEN);
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
// Reject API traffic with 503 until ready event has fired and routes are mounted.
|
||||
let appReady = false;
|
||||
// (appReady is declared at module top so the ready callback can flip it.)
|
||||
app.use((req, res, next) => {
|
||||
if (!appReady && req.path.startsWith('/api')) {
|
||||
return res.status(503).json({ error: 'Bot is starting; API not ready yet.' });
|
||||
@@ -243,8 +251,6 @@ const internalApi = require('./routes/internalApi');
|
||||
const internalApp = express();
|
||||
internalApp.use('/internal', internalApi);
|
||||
|
||||
let httpServer = null;
|
||||
let internalServer = null;
|
||||
if (CONFIG.INTERNAL_API_SECRET) {
|
||||
// Must bind all-interfaces inside the bot container: the settings-site is a
|
||||
// separate container on broccoli-net and reaches this API over the docker
|
||||
|
||||
Reference in New Issue
Block a user