Skip to content

Feat/export csv#51

Open
lkaesberg wants to merge 5 commits intomainfrom
feat/export-csv
Open

Feat/export csv#51
lkaesberg wants to merge 5 commits intomainfrom
feat/export-csv

Conversation

@lkaesberg
Copy link
Owner

@lkaesberg lkaesberg commented Jan 15, 2026

Note

Medium Risk
Adds new premium/monetization gating that can block verification email sending and CSV features if misconfigured, plus database migrations for new per-guild settings/credits. Also changes verification eligibility to include an uploaded allowlist, so mistakes in list handling or locale JSON could affect user verification flows.

Overview
Adds CSV-based admin tooling: a new /emaillist command to upload/list/clear a per-server allowed-email allowlist, and an /export logs command to export verification log channel messages as CSV.

Introduces premium/monetization support via PremiumManager, including a free monthly email cap with optional subscription/credit-pack bypass, a /premium command (status + redeem entitlements), and premium-gating for CSV import/export; the verification flow now checks both domain allowlists and the uploaded email list, and blocks sending when the monthly limit is reached.

Updates persistence and UX: adds DB migrations for allowedEmails and a guild_premium table (credits + CSV unlock), extends /status and the verify modal to display allowed-email and premium info, and adds localized strings across supported languages.

Written by Cursor Bugbot for commit 49fe50b. This will update automatically on new commits. Configure here.

escapeCsvField(parsed.email),
parsed.type,
parsed.verifiedBy || '',
tags
Copy link

Choose a reason for hiding this comment

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

Tags field missing CSV escape handling for quotes/newlines

Medium Severity

The tags field is not passed through escapeCsvField() before being included in the CSV output. While commas are replaced with semicolons, quotes and newlines in tag values would corrupt the CSV structure. The regex pattern [^\]]+ allows tags to contain quotes or newlines, which need proper escaping (wrapping in quotes and doubling internal quotes) to produce valid CSV. Unlike username and email which use escapeCsvField(), the tags field bypasses this protection.

Fix in Cursor Fix in Web

escapeCsvField(parsed.email),
parsed.type,
parsed.verifiedBy || '',
tags
Copy link

Choose a reason for hiding this comment

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

Tags field not escaped for CSV output

Medium Severity

The tags field is added directly to the CSV row without being passed through escapeCsvField(), unlike username and email which are properly escaped. The tags originate from Discord role names (via assignedRoleNames), which can contain double quotes or newlines. Replacing commas with semicolons on line 104 doesn't protect against these other CSV-breaking characters, resulting in malformed CSV output.

Fix in Cursor Fix in Web

username = member ? member.user.username : '';
} catch {
username = '';
}
Copy link

Choose a reason for hiding this comment

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

Per-message member fetch causes timeout on large exports

Medium Severity

For every parsed log message, interaction.guild.members.fetch(parsed.userId) makes a Discord API call. With up to 10,000 messages and many unique users (especially ones who've left the server and aren't cached), this can trigger thousands of sequential API calls subject to rate limiting. This risks exceeding the 15-minute deferred interaction timeout, causing the export to silently fail.

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

// Deduplicate
const uniqueEmails = [...new Set(emails)];

serverSettings.allowedEmails = uniqueEmails;
Copy link

Choose a reason for hiding this comment

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

Upload silently replaces email list instead of appending

Medium Severity

The upload subcommand assigns serverSettings.allowedEmails = uniqueEmails, which completely replaces the existing allowed email list. However, the success message (emaillistUploaded) says "Added %VAR% unique email address(es)" across all languages, implying the new emails were appended to the existing list. An admin who uploads a second CSV expecting it to merge with the first list will silently lose all previously allowed emails with no warning.

Additional Locations (1)

Fix in Cursor Fix in Web

}
} else if (onlyEmailList) {
// Only email list, no domains - show default roles
const defaultRoleNamesDisplay = getRoleNames(defaultRoles)
Copy link

Choose a reason for hiding this comment

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

Redundant getRoleNames call duplicates existing variable

Low Severity

defaultRoleNamesDisplay on line 158 is computed as getRoleNames(defaultRoles), which is identical to defaultRoleNames already computed on line 119. The existing variable can be reused directly instead of making a redundant call.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant