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:
2026-05-08 20:46:04 +00:00
parent 3e9ad658d0
commit 3c13e55dad
8 changed files with 61 additions and 13 deletions

View File

@@ -44,6 +44,13 @@ describe('stripEmailQuotes', () => {
const input = 'New reply.\r\nOn Monday Bob <b@x.com> wrote:\r\n> quoted';
expect(stripEmailQuotes(input)).toBe('New reply.');
});
it('picks earliest cutoff when multiple markers match', () => {
// Earlier in the body: "On X wrote:". Later: "_____" underline.
// The earliest cutoff is the reply marker, not the underline.
const input = 'My new reply.\nOn Mon Bob wrote:\n> quoted text\n_____\nsignature';
expect(stripEmailQuotes(input)).toBe('My new reply.');
});
});
describe('stripMobileFooter', () => {