19 Commits

Author SHA1 Message Date
c79463fc2a security: gate /help, signature modal submit, and cancel_delete_tag on staff role
Closes the remaining non-broccolini interaction paths after the prior
TICKET_BUTTON_HANDLERS gate. After this commit, every bot interaction is
staff-only except the panel buttons (open_ticket / open_ticket_thread /
open_ticket_channel) and their ticket-creation modal submit — those have
to stay public because they're how members and customers open tickets.

Specific changes:

- handlers/commands/index.js: handleCommand no longer has the
  `!== 'help'` carve-out. /help now goes through requireStaffRole like
  every other slash command. Non-staff get the same ephemeral
  "only available to the support team" reply.

- broccolini-discord.js: the signature_modal_* modal-submit handler now
  calls requireStaffRole before writing to StaffSignature. /signature
  already gates the modal display via the slash-command staff check;
  this is defense in depth against directly crafted submissions.

- handlers/buttons.js: cancel_delete_tag moved out of
  FREE_BUTTON_HANDLERS and gated alongside confirm_delete_tag::*. The
  dialog is only shown ephemerally to the staff who triggered
  /response delete, so non-staff can't reach it in normal flow; gating
  keeps the button surface consistent.

Kept public (by design — these are the customer entry points):
  open_ticket / open_ticket_thread / open_ticket_channel buttons
  ticket_modal / ticket_modal_thread / ticket_modal_channel submits
2026-05-19 19:58:41 +00:00
3c13e55dad 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).
2026-05-08 20:46:04 +00:00
cdf85f6364 audit week 1: creator ID tracking, channel-queue migration, deprecation cleanup
QUAL-006  store ticket.creatorId on creation; legacy split-pop returned the
          message ID for discord-msg-* tickets, breaking transcript DM, close
          log, and channel rename for context-menu-created tickets. Adds the
          field to the Ticket schema and writes a one-shot backfill script
          (scripts/backfill-creatorId.js, dry-run by default).

QUEUE-001 add enqueueOverwrite + enqueueTopic to services/channelQueue.js
          (chain on renameChains alongside enqueueMove). Migrate handleAdd /
          handleRemove / handleMove / handleTopic so permissionOverwrites,
          setParent, and setTopic no longer race pending renames or sends.
          handleMove now uses the existing enqueueMove. Initial overwrites in
          handleTicketModal stay inline; channel doesn't exist yet so no race.

DISCORD-001 replace ephemeral: true with flags: MessageFlags.Ephemeral across
            broccolini-discord.js, handlers/sharedHelpers.js, handlers/buttons.js,
            handlers/commands.js. runDeferred opts now take { flags } directly.

SEC-003   /gmailpoll min interval is 30s. Drop the 5s/10s slash-command
          choices and clamp Math.max(30000, ms) in handleGmailPoll for
          defense in depth.

QUAL-001  upgrade silent .catch(() => {}) on the lastActivity updateOne in
          handlers/messages.js to log via logError, so transient Mongo errors
          surface in the debug channel instead of disappearing.

QUAL-002  drop await from logError/logWarn calls in services/staffThread.js
          and services/pinMessage.js — fire-and-forget per CLAUDE.md hard rule.

QUAL-003  wrap stray setTimeouts (handleConfirmCloseRequest force-close timer,
          runFinalClose channel-delete + overflow-cleanup, checkAutoClose
          delete-after-email) in trackTimeout via lazy require so they clear
          on shutdown.
2026-05-08 20:19:14 +00:00
840b6bfcf8 simplify: prune dead code, dedup gmail send, drop neutered log stubs
- Remove no-op log stubs (logGmail, logAutomation, logSecurity, logSystem)
  and ~17 callsites; dead counters in tickets.js and gmail-poll.js go too
- Dedup three near-identical Gmail send paths into sendThreadedEmail helper
- Drop dead Mongoose fields: broccoliniTicketId, lastSyncedBroccoliniArticleId,
  renameCount, renameWindowStart, reminderSent, staffChannelId,
  unclaimedRemindersSent, lastMessageAuthorIsStaff
- Drop dead config fields and their .env.example entries
- Inline api/botClient.js (3-line wrapper, 2 callers)
- Trim unused exports across utils.js, tickets.js, configSchema.js, debugLog.js
- Fix handlers/messages.js to use isStaff() — old partial check ignored
  ADDITIONAL_STAFF_ROLES, so those members were treated as customers
- Drop unused deps p-queue + dotenv-expand; move mongodb to devDependencies

Net: -583 LOC source + -57 LOC lockfile. All 23 modules load clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:37:14 +00:00
f3ee27ed7a more mvp strip 2026-04-21 17:24:03 +00:00
bf901039bc mvp & email signature 2026-04-21 16:15:18 +00:00
636348d824 strip: remove pattern/surge/chat alert monitoring + unused commands
- delete services/{patternChecker,patternStore,surgeChecker,chatAlertChecker,staffNotifications,staffChannel,notificationRegistry,notificationEnabled,staffPresence}.js
- remove /notification, /staffnotification, /tag, /priority
- /escalate: drop action param, always unclaim
- purge PATTERN_*, SURGE_*, CHAT_ALERT_*, STAFF_* env vars from config + .env.example
- drop StaffNotification model
- ~2500 LOC removed
- settings-site /internal/notifications/* endpoints gone (UI will 404 until trimmed)
2026-04-21 15:57:18 +00:00
298cf13d5c settings site changes 2026-04-21 15:30:40 +00:00
33b1f276c6 audit 2026-04-20 18:05:36 +00:00
d134f5f678 broccolini-bot: bind internal API to 0.0.0.0 for bridge access; mount .env for config persistence 2026-04-18 16:26:37 +00:00
21618efbad security hardening 2026-04-18 11:10:41 +00:00
4426c4ee0f manual commit 2026-04-10T19:45:00Z 2026-04-10 19:45:00 +00:00
1017ef6ae7 manual commit 2026-04-10T19:27:53Z 2026-04-10 19:27:53 +00:00
indifferentketchup
9dc8b77683 network again 2026-04-07 10:29:46 -05:00
indifferentketchup
03794ceb25 scan for deleted tickets 2026-04-07 10:15:58 -05:00
indifferentketchup
69c247ed1b huge changes 2026-04-07 01:43:06 -05:00
indifferentketchup
c5d7539677 staff notifications 2026-04-06 23:53:32 -05:00
samkintop
6821424663 First upload 2026-02-17 21:49:58 -06:00
samkintop
29a13768f7 Sync broccolini-bot: rename from zammad, docs in docs/, security gitignore, remove zammad deps
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 02:56:00 -06:00