Skip to content

fix: guard loot and money chat listeners against secret string taint (#176)#178

Merged
Xerrion merged 2 commits intomasterfrom
fix/176-loot-listener-secret-string
Apr 20, 2026
Merged

fix: guard loot and money chat listeners against secret string taint (#176)#178
Xerrion merged 2 commits intomasterfrom
fix/176-loot-listener-secret-string

Conversation

@Xerrion
Copy link
Copy Markdown
Owner

@Xerrion Xerrion commented Apr 20, 2026

Description

Fixes the retail secret-string taint error in LootListener_Shared.lua:250 affecting both loot and money chat handlers.

Closes #176
Closes #177

Root Cause

On retail, some CHAT_MSG_LOOT / CHAT_MSG_MONEY payloads arrive as Blizzard "secret" (censored) strings. Any index operation on them - including msg:match(patternPair.multi) - raises attempt to index local 'msg' (a secret string value tainted by 'DragonToast'). The handler passed msg straight into the parsers with no guard.

Fix

Three layers of defense applied to OnChatMsgLoot and OnChatMsgMoney:

  1. Type guard: type(msg) ~= "string" short-circuits non-string payloads.
  2. Censor check: extract lineID from the event payload and skip when C_ChatInfo.IsChatLineCensored(lineID) is true. Nil-guarded so it no-ops on TBC Anniversary and MoP Classic where the API does not exist.
  3. pcall wrapper: parser errors surface via geterrorhandler() so they show up in BugSack / the default error handler, instead of being swallowed or taking down the event dispatcher.

Layers 1 and 2 are encapsulated in a shared IsIndexableChatMessage(msg, lineID) helper. .luacheckrc gains C_ChatInfo and geterrorhandler in the DragonToast read_globals block.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Testing

  • Luacheck passes (luacheck .)
  • Tested in-game manually
  • WoW version(s) tested on: Retail, TBC Anniversary, MoP Classic

Checklist

  • My code follows the project's code style (4-space indent, 120 char lines)
  • I have tested my changes in-game
  • Luacheck reports no warnings
  • My commits follow conventional commit format

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of loot and money chat messages with input validation and safer parsing to avoid processing invalid or censored messages and to surface parsing errors without crashing.
  • Chores

    • Updated lint/configuration to recognize additional API symbols.

…176)

Retail CHAT_MSG_LOOT and CHAT_MSG_MONEY payloads occasionally arrive as Blizzard "secret" (censored) strings. Any index operation on them (including msg:match) raises:

    attempt to index local 'msg' (a secret string value tainted by 'DragonToast')

Add three layers of defense to OnChatMsgLoot and OnChatMsgMoney in LootListener_Shared.lua:

1. Type guard: skip if msg is not a string.
2. Censor check: extract lineID from the event payload and skip when C_ChatInfo.IsChatLineCensored(lineID) is true. Nil-guarded so the check no-ops on TBC Anniversary and MoP Classic where the API does not exist.
3. pcall wrapper: parser errors surface through geterrorhandler() so they are visible to BugSack/BugGrabber/the default error handler without breaking the AceEvent dispatcher.

The shared IsIndexableChatMessage helper encapsulates layers 1 and 2 for both handlers. C_ChatInfo and geterrorhandler added to .luacheckrc read_globals for DragonToast.

Closes #176
Closes #177
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

Adds defensive guards and error handling to chat loot/money handlers to avoid indexing tainted/secret chat strings, exposes a utility to validate indexable chat messages (including censorship checks), and updates luacheck config to recognize new WoW globals.

Changes

Cohort / File(s) Summary
Configuration
.luacheckrc
Added C_ChatInfo and geterrorhandler to read_globals for DragonToast/.
Listener utilities
DragonToast/Core/ListenerUtils.lua
Added Utils.IsIndexableChatMessage(msg, lineID) and cached type, C_ChatInfo to check string-ness and chat-line censorship via C_ChatInfo.IsChatLineCensored when available.
Chat message handlers
DragonToast/Listeners/LootListener_Shared.lua
Updated OnChatMsgLoot/OnChatMsgMoney to accept varargs and derive lineID via select(10, ...); early-return unless Utils.IsIndexableChatMessage passes. Wrapped ParseLootMessage/ParseMoneyMessage in pcall() and report errors to geterrorhandler(); added local bindings for pcall, select, tostring, and geterrorhandler.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

core

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR title uses correct Conventional Commit format (fix: scope and description) and accurately describes the main fix: guarding loot/money chat listeners against secret string taint.
Linked Issues check ✅ Passed PR fulfills all coding requirements from #176 and #177: type guards msg strings, checks C_ChatInfo.IsChatLineCensored via lineID, wraps parsers in pcall with geterrorhandler routing, and exposes shared IsIndexableChatMessage utility.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #176/#177 objectives: .luacheckrc additions for new globals, LootListener event handlers and pcall wrapping, and ListenerUtils helper introduction. No unrelated refactoring or feature creep.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
DragonToast/Listeners/LootListener_Shared.lua (1)

29-46: Consider hoisting this guard to ns.ListenerUtils for cross-listener reuse. 🧰

The secret-string taint issue isn't unique to loot/money chat — any CHAT_MSG_* driven listener (e.g. XPListener, HonorListener, future additions) is exposed to the same Blizzard censoring contract. Since ns.ListenerUtils is the sanctioned shared sub-table per project conventions, exposing this helper there would:

  • keep the defense consistent across all chat-backed listeners,
  • avoid copy/paste drift when Blizzard adds a new IsChatLineCensored-adjacent API,
  • remove the need for each listener file to cache C_ChatInfo locally.

Non-blocking — the current placement is perfectly functional. Just a "Dragon soul bind on the whole raid, not just the main tank" kind of suggestion.

♻️ Sketch
-- In ListenerUtils.lua
+local C_ChatInfo = C_ChatInfo
+
+function Utils.IsIndexableChatMessage(msg, lineID)
+    if type(msg) ~= "string" then return false end
+    if C_ChatInfo and C_ChatInfo.IsChatLineCensored and lineID
+        and C_ChatInfo.IsChatLineCensored(lineID) then
+        return false
+    end
+    return true
+end

Then in LootListener_Shared.lua, replace the local with Utils.IsIndexableChatMessage(msg, lineID).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DragonToast/Listeners/LootListener_Shared.lua` around lines 29 - 46, Move the
local IsIndexableChatMessage helper into the shared namespace table
(ns.ListenerUtils) so other chat-driven listeners can reuse it: add a function
ns.ListenerUtils.IsIndexableChatMessage(msg, lineID) with the exact same logic
(type(msg) check and C_ChatInfo and IsChatLineCensored nil-check) and remove the
local declaration from LootListener_Shared.lua; then replace the local call
sites in LootListener_Shared.lua with
ns.ListenerUtils.IsIndexableChatMessage(msg, lineID) (and update any other
listeners to call ns.ListenerUtils.IsIndexableChatMessage as needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@DragonToast/Listeners/LootListener_Shared.lua`:
- Around line 29-46: Move the local IsIndexableChatMessage helper into the
shared namespace table (ns.ListenerUtils) so other chat-driven listeners can
reuse it: add a function ns.ListenerUtils.IsIndexableChatMessage(msg, lineID)
with the exact same logic (type(msg) check and C_ChatInfo and IsChatLineCensored
nil-check) and remove the local declaration from LootListener_Shared.lua; then
replace the local call sites in LootListener_Shared.lua with
ns.ListenerUtils.IsIndexableChatMessage(msg, lineID) (and update any other
listeners to call ns.ListenerUtils.IsIndexableChatMessage as needed).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 91e93c39-85e0-4746-99b4-a441352fad0c

📥 Commits

Reviewing files that changed from the base of the PR and between 61feaf3 and 84f9158.

📒 Files selected for processing (2)
  • .luacheckrc
  • DragonToast/Listeners/LootListener_Shared.lua

Per CodeRabbit review on #178. The secret-string / censored-chat
guard is not loot-specific - any future CHAT_MSG_* listener that
parses the message text faces the same retail taint risk. Move the
helper to the shared ns.ListenerUtils table so text-parsing listeners
can adopt it when they need it, without copy/paste drift.

No behavioral change: logic is byte-identical to the prior file-local.
Only the two existing call sites in LootListener_Shared.lua are
updated; other listeners are unaffected.
@Xerrion
Copy link
Copy Markdown
Owner Author

Xerrion commented Apr 20, 2026

@coderabbitai good call - hoisted \ into \ in aa8deac. Kept scope limited to the loot/money handlers since those are the only confirmed callers of \ today; other \ listeners can adopt the shared helper when they're next touched.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Tip

For best results, initiate chat on the files or code changes.

@Xerrion Solid incremental approach — the IsIndexableChatMessage helper living in LootListener_Shared.lua with its scope pinned to the two confirmed callers is exactly the right size for this fix. Keeping other listeners on a "adopt-on-next-touch" basis avoids churn and lets the pattern prove itself before spreading. 🐉🍞


✏️ Learnings added
Learnt from: Xerrion
URL: https://github.com/Xerrion/DragonToast/pull/178

Timestamp: 2026-04-20T12:04:15.282Z
Learning: In `DragonToast`, the `IsIndexableChatMessage(msg, lineID)` helper (guards against secret/censored string taint) lives in `DragonToast/Listeners/LootListener_Shared.lua`. Its scope is intentionally limited to `OnChatMsgLoot` and `OnChatMsgMoney` — the only confirmed callers as of PR `#178`. Other chat listeners should adopt it incrementally when they are next modified, rather than being updated proactively.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: Xerrion/DragonToast PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-07T06:28:06.258Z
Learning: Place version-specific loot/XP/honor event parsing logic in `Listeners/` directory

@Xerrion
Copy link
Copy Markdown
Owner Author

Xerrion commented Apr 20, 2026

@coderabbitai good call - hoisted IsIndexableChatMessage into ns.ListenerUtils in aa8deac. Kept scope limited to the loot/money handlers since those are the only confirmed callers of msg:match today; other CHAT_MSG_* listeners can adopt the shared helper when they're next touched.

@coderabbitai coderabbitai Bot added core and removed listeners labels Apr 20, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Tip

For best results, initiate chat on the files or code changes.

@Xerrion — even better! Landing IsIndexableChatMessage in ns.ListenerUtils gives it the right namespace gravity for a shared helper, making it easy for future CHAT_MSG_* callers to discover and adopt without hunting inside a listener-specific file. The incremental rollout stance still stands — promote on next touch, not speculatively. 🐉🍞

The old learning pointing to LootListener_Shared.lua is now stale — updating below.


✏️ Learnings added
Learnt from: Xerrion
URL: https://github.com/Xerrion/DragonToast/pull/178

Timestamp: 2026-04-20T12:05:13.993Z
Learning: In `DragonToast`, the `IsIndexableChatMessage(msg, lineID)` helper (guards against secret/censored string taint) lives in `ns.ListenerUtils` (promoted in PR `#178`, commit aa8deac). Its scope is intentionally limited to `OnChatMsgLoot` and `OnChatMsgMoney` — the only confirmed callers of `msg:match` as of PR `#178`. Other `CHAT_MSG_*` listeners should adopt it incrementally when they are next modified, rather than being updated proactively.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


⛔ Learnings removed
Learnt from: Xerrion
Repo: Xerrion/DragonToast PR: 0
File: :0-0
Timestamp: 2026-04-20T12:04:15.282Z
Learning: In `DragonToast`, the `IsIndexableChatMessage(msg, lineID)` helper (guards against secret/censored string taint) lives in `DragonToast/Listeners/LootListener_Shared.lua`. Its scope is intentionally limited to `OnChatMsgLoot` and `OnChatMsgMoney` — the only confirmed callers as of PR `#178`. Other chat listeners should adopt it incrementally when they are next modified, rather than being updated proactively.

🧠 Learnings used
Learnt from: Xerrion
Repo: Xerrion/DragonToast PR: 0
File: :0-0
Timestamp: 2026-04-20T12:04:15.282Z
Learning: In `DragonToast`, the `IsIndexableChatMessage(msg, lineID)` helper (guards against secret/censored string taint) lives in `DragonToast/Listeners/LootListener_Shared.lua`. Its scope is intentionally limited to `OnChatMsgLoot` and `OnChatMsgMoney` — the only confirmed callers as of PR `#178`. Other chat listeners should adopt it incrementally when they are next modified, rather than being updated proactively.

Learnt from: CR
Repo: Xerrion/DragonToast PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-07T06:28:06.258Z
Learning: Place version-specific loot/XP/honor event parsing logic in `Listeners/` directory

Learnt from: CR
Repo: Xerrion/DragonToast PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-07T06:28:06.258Z
Learning: Applies to **/*.lua : All modules must attach to the `ns` namespace with appropriate sub-tables: `ns.Addon`, `ns.ToastManager`, `ns.ToastFrame`, `ns.ToastAnimations`, `ns.ElvUISkin`, `ns.LootListener`, `ns.XPListener`, `ns.HonorListener`, `ns.MailListener`, `ns.MessageBridge`, `ns.ListenerUtils`, `ns.MinimapIcon`, `ns.Print`

@Xerrion Xerrion merged commit 1885fce into master Apr 20, 2026
3 checks passed
@Xerrion Xerrion deleted the fix/176-loot-listener-secret-string branch April 20, 2026 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: secret value errors [Bug]: attempt to index local 'msg' (a secret string)

1 participant