-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement profanity.dev as the swear filter with filter notifications. #112
base: staging
Are you sure you want to change the base?
Conversation
…into feat-bridge
…into feat-rev-filter
…into feat-rev-filter
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis pull request introduces a comprehensive set of changes across the entire application, focusing on module resolution, feature expansion, and code structure improvements. The primary modifications include adding Changes
Sequence DiagramsequenceDiagram
participant User
participant WebApp
participant APIServer
participant Database
participant DiscordBot
User->>WebApp: Access new features
WebApp->>APIServer: Request feature data
APIServer->>Database: Query data
Database-->>APIServer: Return results
APIServer-->>WebApp: Send feature data
WebApp->>User: Render feature page
alt Bridge Command
User->>DiscordBot: Use /bridge command
DiscordBot->>APIServer: Submit bridge request
APIServer->>Database: Store/Process bridge command
Database-->>APIServer: Confirm action
APIServer-->>DiscordBot: Return status
DiscordBot->>User: Display bridge command result
end
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
app.post(baseEndpoint + "/clear", async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
|
||
try { | ||
db.query( | ||
`TRUNCATE bridge;`, | ||
function (error, results, fields) { | ||
if (error) { | ||
return res.send({ | ||
success: false, | ||
message: `${error}`, | ||
}); | ||
} | ||
|
||
return res.send({ | ||
success: true, | ||
message: `Bridge has now been cleared.`, | ||
}); | ||
} | ||
); | ||
} catch (error) { | ||
res.send({ | ||
success: false, | ||
message: `${error}`, | ||
}); | ||
} | ||
|
||
return res; | ||
}); |
Check failure
Code scanning / CodeQL
Missing rate limiting High
a database access
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI 12 days ago
To fix the problem, we need to introduce rate limiting to the route handlers in the bridgeApiRoute
function. The best way to achieve this is by using the express-rate-limit
middleware. This middleware allows us to set a maximum number of requests a user can make within a specified time window. We will apply this middleware to the relevant routes to ensure they are protected against excessive requests.
We will need to:
- Install the
express-rate-limit
package. - Import the
express-rate-limit
package in theapi/routes/bridge.js
file. - Set up a rate limiter with appropriate configuration.
- Apply the rate limiter to the relevant routes.
-
Copy modified line R7 -
Copy modified lines R12-R19 -
Copy modified line R74 -
Copy modified line R108 -
Copy modified line R138 -
Copy modified line R168
@@ -6,2 +6,3 @@ | ||
} from "../common.js"; | ||
import RateLimit from "express-rate-limit"; | ||
|
||
@@ -10,2 +11,10 @@ | ||
|
||
// set up rate limiter: maximum of 100 requests per 15 minutes | ||
const limiter = RateLimit({ | ||
windowMs: 15 * 60 * 1000, // 15 minutes | ||
max: 100, // max 100 requests per windowMs | ||
}); | ||
|
||
app.use(limiter); | ||
|
||
app.get(baseEndpoint + "/get", async function (req, res) { | ||
@@ -64,3 +73,3 @@ | ||
|
||
app.post(baseEndpoint + "/command/add", async function (req, res) { | ||
app.post(baseEndpoint + "/command/add", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
@@ -98,3 +107,3 @@ | ||
|
||
app.post(baseEndpoint + "/clear", async function (req, res) { | ||
app.post(baseEndpoint + "/clear", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
@@ -128,3 +137,3 @@ | ||
|
||
app.get(baseEndpoint + "/server/get", async function (req, res) { | ||
app.get(baseEndpoint + "/server/get", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
@@ -158,3 +167,3 @@ | ||
|
||
app.post(baseEndpoint + "/server/update", async function (req, res) { | ||
app.post(baseEndpoint + "/server/update", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); |
-
Copy modified lines R52-R53
@@ -51,3 +51,4 @@ | ||
"querystring": "^0.2.1", | ||
"zero-md": "^2.3.1" | ||
"zero-md": "^2.3.1", | ||
"express-rate-limit": "^7.5.0" | ||
} |
Package | Version | Security advisories |
express-rate-limit (npm) | 7.5.0 | None |
app.get(baseEndpoint + "/server/get", async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
|
||
try { | ||
db.query( | ||
`SELECT * FROM serverStatus;`, | ||
function (error, results) { | ||
if (error) { | ||
return res.send({ | ||
success: false, | ||
message: `Failed: ${error}`, | ||
}); | ||
} | ||
|
||
return res.send({ | ||
success: true, | ||
data: results, | ||
}); | ||
} | ||
); | ||
} catch (error) { | ||
return res.send({ | ||
success: false, | ||
message: `Unexpected error: ${error}`, | ||
}); | ||
} | ||
|
||
return res; | ||
}); |
Check failure
Code scanning / CodeQL
Missing rate limiting High
a database access
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI 12 days ago
To fix the problem, we need to introduce rate limiting to the route handlers that perform database access operations. The best way to achieve this is by using the express-rate-limit
middleware. This middleware allows us to set a maximum number of requests that can be made to the server within a specified time window. We will apply this middleware to the relevant routes to ensure that they are protected against denial-of-service attacks.
We will need to:
- Install the
express-rate-limit
package. - Import the
express-rate-limit
package in the file. - Set up a rate limiter with appropriate configuration.
- Apply the rate limiter to the relevant routes.
-
Copy modified line R7 -
Copy modified lines R10-R13 -
Copy modified line R70 -
Copy modified line R104 -
Copy modified line R134 -
Copy modified line R164 -
Copy modified line R198
@@ -6,4 +6,9 @@ | ||
} from "../common.js"; | ||
import RateLimit from "express-rate-limit"; | ||
|
||
export default function bridgeApiRoute(app, config, db, features, lang) { | ||
const limiter = RateLimit({ | ||
windowMs: 15 * 60 * 1000, // 15 minutes | ||
max: 100, // max 100 requests per windowMs | ||
}); | ||
const baseEndpoint = "/api/bridge"; | ||
@@ -64,3 +69,3 @@ | ||
|
||
app.post(baseEndpoint + "/command/add", async function (req, res) { | ||
app.post(baseEndpoint + "/command/add", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
@@ -98,3 +103,3 @@ | ||
|
||
app.post(baseEndpoint + "/clear", async function (req, res) { | ||
app.post(baseEndpoint + "/clear", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
@@ -128,3 +133,3 @@ | ||
|
||
app.get(baseEndpoint + "/server/get", async function (req, res) { | ||
app.get(baseEndpoint + "/server/get", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
@@ -158,3 +163,3 @@ | ||
|
||
app.post(baseEndpoint + "/server/update", async function (req, res) { | ||
app.post(baseEndpoint + "/server/update", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
@@ -192,3 +197,3 @@ | ||
|
||
app.post(baseEndpoint + "/command/process", async function (req, res) { | ||
app.post(baseEndpoint + "/command/process", limiter, async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); |
-
Copy modified lines R52-R53
@@ -51,3 +51,4 @@ | ||
"querystring": "^0.2.1", | ||
"zero-md": "^2.3.1" | ||
"zero-md": "^2.3.1", | ||
"express-rate-limit": "^7.5.0" | ||
} |
Package | Version | Security advisories |
express-rate-limit (npm) | 7.5.0 | None |
app.post(baseEndpoint + "/server/update", async function (req, res) { | ||
isFeatureEnabled(features.bridge, res, lang); | ||
|
||
const serverInfo = required(req.body, "serverInfo", res); | ||
let lastUpdated = required(req.body, "lastUpdated", res); | ||
|
||
try { | ||
const serverInfoString = JSON.stringify(serverInfo); | ||
|
||
db.query( | ||
`UPDATE serverStatus SET statusInfo = ?, lastUpdated = ? WHERE serverStatusId = 1;`, | ||
[serverInfoString, lastUpdated], | ||
function (error, updateResults) { | ||
if (error) { | ||
return res.send({ | ||
success: false, | ||
message: `Update failed: ${error}`, | ||
}); | ||
} | ||
|
||
return res.send({ | ||
success: true, | ||
message: `Server status updated successfully.`, | ||
}); | ||
} | ||
); | ||
} catch (error) { | ||
return res.send({ | ||
success: false, | ||
message: `Unexpected error: ${error}`, | ||
}); | ||
} | ||
}); |
Check failure
Code scanning / CodeQL
Missing rate limiting High
a database access
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI 12 days ago
To fix the problem, we need to introduce a rate-limiting middleware to the Express application. The express-rate-limit
package is a well-known library for this purpose. We will configure the rate limiter to allow a maximum of 100 requests per 15 minutes and apply it to all routes in the bridgeApiRoute
function.
- Install the
express-rate-limit
package. - Import the
express-rate-limit
package in theapi/routes/bridge.js
file. - Configure the rate limiter with appropriate settings.
- Apply the rate limiter to the Express application within the
bridgeApiRoute
function.
-
Copy modified line R7 -
Copy modified lines R10-R15
@@ -6,4 +6,11 @@ | ||
} from "../common.js"; | ||
import RateLimit from "express-rate-limit"; | ||
|
||
export default function bridgeApiRoute(app, config, db, features, lang) { | ||
const limiter = RateLimit({ | ||
windowMs: 15 * 60 * 1000, // 15 minutes | ||
max: 100, // max 100 requests per windowMs | ||
}); | ||
|
||
app.use(limiter); | ||
const baseEndpoint = "/api/bridge"; |
-
Copy modified lines R52-R53
@@ -51,3 +51,4 @@ | ||
"querystring": "^0.2.1", | ||
"zero-md": "^2.3.1" | ||
"zero-md": "^2.3.1", | ||
"express-rate-limit": "^7.5.0" | ||
} |
Package | Version | Security advisories |
express-rate-limit (npm) | 7.5.0 | None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
🛑 Comments failed to post (53)
commands/shopdirectory.mjs (1)
79-89:
⚠️ Potential issueHandle potential exceeding of Discord embed field limits
Discord embeds can have a maximum of 25 fields. If
apiData.data
contains more than 25 shop items, adding a field for each may exceed this limit and cause the bot to throw an error. Consider implementing pagination or limiting the number of items displayed to handle larger datasets gracefully.Apply this diff to implement pagination using buttons or select menus:
+// Import necessary components for pagination +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; + // Add fields for each shop item -apiData.data.forEach((shop) => { +const itemsPerPage = 5; +let page = 0; +const totalPages = Math.ceil(apiData.data.length / itemsPerPage); + +const generateEmbed = (page) => { + const start = page * itemsPerPage; + const currentItems = apiData.data.slice(start, start + itemsPerPage); + const embed = new EmbedBuilder() + .setTitle("🛍️ Shop Directory") + .setDescription( + `🔍 Here are the shop items${ + material ? ` for material: \`${material}\`` : "" + }.\nPage ${page + 1} of ${totalPages}` + ) + .setColor(Colors.Blue); + + currentItems.forEach((shop) => { const transactionType = shop.stock === -1 ? "💰 Buying" : "📦 Selling"; const stockInfo = shop.stock !== -1 ? `**Stock:** ${shop.stock}\n` : ""; - itemsEmbed.addFields([ + embed.addFields([ { name: `Item: ${shop.itemData.displayName}`, value: `**Seller:** \`${shop.userData.username}\`\n**Amount:** ${shop.amount}\n**Price:** $${shop.price}\n${stockInfo}**Type:** ${transactionType}\n**Location:** ${shop.x}, ${shop.y}, ${shop.z}`, }, ]); - }); + }); + + return embed; +}; + +const embedMessage = await interaction.editReply({ + embeds: [generateEmbed(page)], + components: [ + new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId("prevPage") + .setLabel("Previous") + .setStyle(ButtonStyle.Primary) + .setDisabled(page === 0), + new ButtonBuilder() + .setCustomId("nextPage") + .setLabel("Next") + .setStyle(ButtonStyle.Primary) + .setDisabled(page === totalPages - 1) + ), + ], +}); + +// Set up a collector to handle button interactions +const collector = embedMessage.createMessageComponentCollector({ + time: 60000, // Collector will listen for interactions for 60 seconds +}); + +collector.on("collect", async (i) => { + if (i.customId === "prevPage" && page > 0) { + page--; + } else if (i.customId === "nextPage" && page < totalPages - 1) { + page++; + } + await i.update({ + embeds: [generateEmbed(page)], + components: [ + new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId("prevPage") + .setLabel("Previous") + .setStyle(ButtonStyle.Primary) + .setDisabled(page === 0), + new ButtonBuilder() + .setCustomId("nextPage") + .setLabel("Next") + .setStyle(ButtonStyle.Primary) + .setDisabled(page === totalPages - 1) + ), + ], + }); +}); + +collector.on("end", () => { + // Disable buttons after collector ends + embedMessage.edit({ components: [] }); +});Alternatively, limit the number of items displayed to 25.
Committable suggestion skipped: line range outside the PR's diff.
commands/report.mjs (3)
55-55:
⚠️ Potential issueEnsure
userData
is not null before accessing its propertiesIf the reporter's user data is not found,
userData
may benull
. AccessinguserData.discordId
without checking ifuserData
exists can cause a runtime error. Update the condition to handle this scenario.Apply this diff:
- if (!userData.discordId) { + if (!userData || !userData.discordId) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (!userData || !userData.discordId) {
48-48:
⚠️ Potential issueUse User ID instead of username for reported user
Discord usernames are not unique and can change. Using the User ID ensures the correct user is reported. Modify the code to use
id
instead ofusername
.Apply this diff:
- const reportedUser = interaction.options.getUser("user").username; + const reportedUser = interaction.options.getUser("user").id;Also, update the API request body accordingly.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const reportedUser = interaction.options.getUser("user").id;
96-99:
⚠️ Potential issueAvoid multiple replies to the same interaction
You are using
interaction.reply
in both success and error cases without returning or deferring the reply earlier. If the interaction has already been replied to or deferred, callingreply
again will throw an error. Since you're not deferring the interaction, ensure that you only reply once or useeditReply
.Apply this diff to use
editReply
instead:- interaction.reply({ + interaction.editReply({And add a
return
statement after the reply to prevent further code execution.Committable suggestion skipped: line range outside the PR's diff.
api/routes/filter.js (4)
85-85:
⚠️ Potential issueSanitize content before embedding to prevent abuse
Including raw user content in embeds can lead to abuse, such as injection of malicious content or exceeding character limits. Sanitize the
content
to remove any harmful elements before adding it to the embed.Consider applying input validation or truncating the content if it's too long.
112-114:
⚠️ Potential issueAvoid exposing internal error details in API responses
Returning internal errors directly to the client can expose sensitive information. Instead, log the error internally and return a generic error message.
Apply this diff:
console.log("Error processing request:", error); return res.send({ success: false, - message: error, + message: "An internal server error occurred.", });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.console.log("Error processing request:", error); return res.send({ success: false, message: "An internal server error occurred.",
41-47:
⚠️ Potential issueSanitize patterns before constructing regular expressions
Constructing regular expressions from untrusted input in
filter.links
can lead to Regular Expression Denial of Service (ReDoS) attacks or syntax errors. Ensure that the patterns are properly sanitized or escape any special characters.Apply this diff to escape special characters:
filter.links.forEach((link) => { + const escapedLink = link.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const regex = new RegExp(link, "i"); + const regex = new RegExp(escapedLink, "i"); if (regex.test(content)) {Alternatively, pre-compile the regex patterns safely when defining
filter.links
.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.filter.links.forEach((link) => { const escapedLink = link.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp(escapedLink, "i"); if (regex.test(content)) { console.log(`URL detected: ${link}`); urlDetected = true; flaggedFor.push("URL/Advertising"); }
35-35: 🛠️ Refactor suggestion
Avoid logging user-generated content to console in production
Logging the
content
received from users may expose sensitive or personal information in server logs. Consider removing this log statement or ensuring logs are appropriately sanitized and secured.Apply this diff:
- console.log("Received content:", content);
api/routes/report.js (1)
43-47:
⚠️ Potential issuePrevent SQL Injection by Using Parameterized Queries
The SQL query at line 45 directly interpolates
reportedId
, which can lead to SQL injection vulnerabilities ifreportedId
contains malicious input.Apply this diff to use parameterized queries:
- let dbQuery = `SELECT * FROM reports WHERE reportedId=${reportedId};`; + let dbQuery = `SELECT * FROM reports WHERE reportedId = ?;`; + await getReports(dbQuery, [reportedId]);Also, modify the
getReports
function to accept parameters:- function getReports(dbQuery) { + function getReports(dbQuery, params = []) { return new Promise((resolve, reject) => { - db.query(dbQuery, function (error, results, fields) { + db.query(dbQuery, params, function (error, results, fields) { // Rest of the code...Committable suggestion skipped: line range outside the PR's diff.
api/routes/vault.js (1)
43-44:
⚠️ Potential issuePrevent SQL Injection by Using Parameterized Queries
The SQL query at line 43 directly interpolates
vaultId
, which can lead to SQL injection vulnerabilities ifvaultId
contains malicious input.Update the query to use parameterized queries:
- let dbQuery = `SELECT * FROM vault WHERE vaultId=${vaultId};`; + let dbQuery = `SELECT * FROM vault WHERE vaultId = ?;`; + getVault(dbQuery, [vaultId]);Modify the
getVault
function to accept parameters:- function getVault(dbQuery) { + function getVault(dbQuery, params = []) { db.query(dbQuery, params, function (error, results, fields) { // Rest of the code...Committable suggestion skipped: line range outside the PR's diff.
api/routes/bridge.js (6)
99-127:
⚠️ Potential issueAdd Rate Limiting and Authentication to Sensitive Endpoint
The
/api/bridge/clear
endpoint truncates thebridge
table without any rate limiting or authentication. This could be exploited to disrupt service.Implement authentication checks and rate limiting to secure this endpoint. Only authorized users should have access to this operation.
🧰 Tools
🪛 GitHub Check: CodeQL
[failure] 99-127: Missing rate limiting
This route handler performs a database access, but is not rate-limited.
129-157:
⚠️ Potential issueSecure Endpoint with Rate Limiting and Authentication
The
/api/bridge/server/get
endpoint lacks rate limiting and authentication, potentially exposing server status information to unauthorized users.Add authentication checks and rate limiting to protect this endpoint from abuse and unauthorized access.
🧰 Tools
🪛 GitHub Check: CodeQL
[failure] 129-157: Missing rate limiting
This route handler performs a database access, but is not rate-limited.
159-191:
⚠️ Potential issueImplement Authentication and Input Validation
The
/api/bridge/server/update
endpoint allows updating server status without authentication or proper input validation.Ensure that only authenticated and authorized users can access this endpoint, and validate inputs to prevent malicious data from being saved.
🧰 Tools
🪛 GitHub Check: CodeQL
[failure] 159-191: Missing rate limiting
This route handler performs a database access, but is not rate-limited.
205-221:
⚠️ Potential issueCorrect Parameter Mismatch in Query
At line 207, there is a mismatch between the number of placeholders in the SQL query and the provided parameters.
Adjust the parameters to match the placeholders:
db.query( `DELETE FROM bridge WHERE bridgeId = ?;`, - [1, bridgeId], + [bridgeId], function (error, results, fields) { // Rest of the code...📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.db.query( `DELETE FROM bridge WHERE bridgeId=?;`, [bridgeId], function (error, results, fields) { if (error) { return res.send({ success: false, message: `${error}`, }); } return res.send({ success: true, message: `Bridge ID ${bridgeId} has been executed.`, }); } );
42-44:
⚠️ Potential issuePrevent SQL Injection by Using Parameterized Queries
The SQL query at line 42 directly interpolates
bridgeId
, introducing a potential SQL injection vulnerability.Use parameterized queries to safeguard against SQL injection:
- let dbQuery = `SELECT * FROM bridge WHERE bridgeId=${bridgeId};`; + let dbQuery = `SELECT * FROM bridge WHERE bridgeId = ?;`; + getBridge(dbQuery, [bridgeId]);Update the
getBridge
function to accept parameters:- function getBridge(dbQuery) { + function getBridge(dbQuery, params = []) { db.query(dbQuery, params, function (error, results, fields) { // Rest of the code...Committable suggestion skipped: line range outside the PR's diff.
47-50:
⚠️ Potential issuePrevent SQL Injection by Using Parameterized Queries
The SQL query at line 48 directly interpolates
targetServer
, which can lead to SQL injection iftargetServer
contains malicious input.Modify the query to use parameterization:
- let dbQuery = `SELECT * FROM bridge WHERE targetServer='${targetServer}' AND processed=0;`; + let dbQuery = `SELECT * FROM bridge WHERE targetServer = ? AND processed=0;`; + getBridge(dbQuery, [targetServer]);Ensure
getBridge
accepts parameters as shown previously.Committable suggestion skipped: line range outside the PR's diff.
commands/bridge.mjs (1)
148-149:
⚠️ Potential issueAdd input validation for user-provided inputs to enhance security
The
command
andtargetedServer
inputs are directly used in the API request body without validation or sanitization. This could lead to security vulnerabilities, such as injection attacks or processing invalid data.Consider adding input validation to ensure the inputs meet expected formats and to prevent potential security risks.
Also applies to: 159-160
api/internal_redirect/report.js (2)
9-16:
⚠️ Potential issueAwait asynchronous operations to ensure proper execution order
The
postAPIRequest
function is likely asynchronous, but it's not awaited. Additionally,res.redirect
is called immediately after, which may cause the redirection to occur before the API request completes.Apply this diff to await the
postAPIRequest
and ensure the API request completes before redirecting:+ await postAPIRequest( `${process.env.siteAddress}/api/report/create`, req.body, `${process.env.siteAddress}/report`, res ); - res.redirect(`${process.env.siteAddress}/`);Alternatively, if
postAPIRequest
handles the response and redirection internally, you may remove the redundantres.redirect
call.Committable suggestion skipped: line range outside the PR's diff.
7-7: 💡 Codebase verification
Add authentication checks to protect the report creation endpoint
The route is in the internal_redirect section which bypasses the global authentication middleware. While
hasPermission
is imported, it's not being used. You should add both session validation and permission checks:app.post(baseEndpoint + "/create", hasPermission('report.create'), async function (req, res) { if (!req.session?.user?.username) { return res.status(401).send({ error: 'Unauthorized' }); } req.body.reporterUser = req.session.user.username; // ... rest of the code });🔗 Analysis chain
Add null checks for session user data to prevent runtime errors
The code assumes that
req.session.user.username
exists. Ifreq.session
orreq.session.user
is undefined (e.g., if the user is not authenticated), this will cause a runtime error.Consider adding checks to ensure
req.session.user
is defined before accessingusername
. For example:if (req.session.user && req.session.user.username) { req.body.reporterUser = req.session.user.username; } else { // Handle the case where the user is not authenticated }🏁 Scripts executed
The following scripts were executed for the analysis:
Error: No shell scripts found
Script:
#!/bin/bash # Check the content of the report.js file cat api/internal_redirect/report.js # Look for authentication middleware patterns ast-grep --pattern 'app.use($middleware)' # Search for session checks patterns rg "req\.session" -A 2 # Look for similar username access patterns rg "req\.session\.user\.username" -A 2Length of output: 12655
Script:
#!/bin/bash # Check for route protection patterns in report.js ast-grep --pattern 'app.post($_, hasPermission($$$))' # Look for other middleware usage in report.js rg "hasPermission" api/internal_redirect/report.js -A 2Length of output: 239
Script:
#!/bin/bash # Check for global middleware setup in app.js cat app.js # Look for route protection patterns in the main application setup rg "app.use\(.*hasPermission" -A 2Length of output: 4328
api/internal_redirect/bridge.js (1)
12-19:
⚠️ Potential issueAwait asynchronous operations to ensure proper execution order
The
postAPIRequest
function is likely asynchronous but is not awaited. Callingres.redirect
immediately after may cause the redirection to occur before the API request completes.Apply this diff to await the
postAPIRequest
and ensure the API request completes before redirecting:+ await postAPIRequest( `${process.env.siteAddress}/api/bridge/command/add`, req.body, `${process.env.siteAddress}/dashboard/bridge`, res ); - res.redirect(`${process.env.siteAddress}/dashboard/bridge`);Alternatively, if
postAPIRequest
handles the response and redirection internally, you may remove the redundantres.redirect
call.Committable suggestion skipped: line range outside the PR's diff.
commands/ranks.mjs (1)
19-31:
⚠️ Potential issueAdd missing import statement for features.json.
The code references
features
but is missing the import statement.Add this import at the top of the file:
+import features from "../features.json" assert { type: "json" };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.import features from "../features.json" assert { type: "json" }; if (!features.ranks) { const errorEmbed = new EmbedBuilder() .setTitle("Feature Disabled") .setDescription( `This feature has been disabled by your System Administrator.` ) .setColor(Colors.Red); return interaction.reply({ embeds: [errorEmbed], ephemeral: true, }); }
commands/vault.mjs (2)
40-43:
⚠️ Potential issueFix typo in ephemeral property.
The 'ephemeral' property is misspelled as 'empheral'.
interaction.reply({ embeds: [embed], - empheral: false, + ephemeral: false, });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.interaction.reply({ embeds: [embed], ephemeral: false, });
5-45:
⚠️ Potential issueFix class name to match functionality.
The class name
RanksCommand
does not match its functionality. The command handles vault operations, not ranks.-export class RanksCommand extends Command { +export class VaultCommand extends Command {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.export class VaultCommand extends Command { constructor(context, options) { super(context, { ...options }); } registerApplicationCommands(registry) { registry.registerChatInputCommand((builder) => builder .setName("vault") .setDescription("Display link to view link to the map vault.") ); } async chatInputRun(interaction) { if (!features.vault) { const errorEmbed = new EmbedBuilder() .setTitle("Feature Disabled") .setDescription( `This feature has been disabled by your System Administrator.` ) .setColor(Colors.Red); return interaction.reply({ embeds: [errorEmbed], ephemeral: true, }); } const embed = new EmbedBuilder() .setTitle(`Vault`) .setDescription( `Explore the Vault to discover and download our previous maps and explore the worlds and builds of our community: ${process.env.siteAddress}/vault` ) .setColor(Colors.DarkGold); interaction.reply({ embeds: [embed], empheral: false, }); } }
cron/discordStatsUpdateCron.js (2)
11-13:
⚠️ Potential issueReview API key exposure in headers.
The API key is exposed in request headers. Consider using more secure authentication methods or moving sensitive data to environment variables.
21-26: 🛠️ Refactor suggestion
Add error handling for JSON parsing.
The
JSON.parse()
operation could throw an error ifstatusInfo
is not valid JSON. Consider adding try-catch block:apiData.data.forEach((server) => { + try { const serverInfo = JSON.parse(server.statusInfo); serverInfo.forEach((status) => { totalPlayers += status.playerCount; }); + } catch (error) { + console.error(`Failed to parse statusInfo for server: ${error}`); + } });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.apiData.data.forEach((server) => { try { const serverInfo = JSON.parse(server.statusInfo); serverInfo.forEach((status) => { totalPlayers += status.playerCount; }); } catch (error) { console.error(`Failed to parse statusInfo for server: ${error}`); } });
cron/cakeDayUserCheck.js (1)
19-34: 🛠️ Refactor suggestion
Implement batch processing for webhook messages.
Currently, webhook messages are sent individually for each user. Consider batching them to reduce API calls:
- results.forEach((user) => { - const joinDate = moment(user.joined); - const years = moment().diff(joinDate, "years"); + const batchSize = 10; + for (let i = 0; i < results.length; i += batchSize) { + const batch = results.slice(i, i + batchSize); + const messages = batch.map(user => { + const joinDate = moment(user.joined); + const years = moment().diff(joinDate, "years"); + return new MessageBuilder() + .setTitle(`🎂 Happy cake day to ${user.username}! :tada:`) + .setDescription( + `Celebrating ${years} year(s) with ${config.siteConfiguration.siteName}` + ) + .setColor(Colors.Blurple) + .setFooter( + `To get your cake day mention, make sure you are a member on our website.` + ); + }); + await Promise.all(messages.map(msg => welcomeHook.send(msg))); + }Committable suggestion skipped: line range outside the PR's diff.
views/vault.ejs (1)
34-36: 💡 Codebase verification
Security vulnerabilities found in vault download implementation
Several critical security issues need to be addressed:
- SQL Injection vulnerability in
/api/vault/get
endpoint wherevaultId
is directly concatenated into the query- No validation or sanitization of
redirectUrl
before storing or rendering- Insufficient permission validation for vault access
- Direct embedding of user-provided URLs in href attributes
Please implement:
- Use parameterized queries for all SQL operations
- Add URL validation and sanitization for redirectUrl
- Implement proper permission checks before serving vault content
- Consider using Content Security Policy (CSP) headers
🔗 Analysis chain
Verify download URL security.
The
redirectUrl
is directly embedded in the href attribute. Ensure that:
- The URL is properly sanitized to prevent XSS attacks
- The download endpoint validates user permissions
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for URL sanitization in the codebase rg -A 5 "redirectUrl.*vault"Length of output: 30
Script:
#!/bin/bash # Find vault-related files fd vault # Search for download or redirect handling rg -i "download.*vault" -A 5 rg -i "redirect.*vault" -A 5 # Look for URL processing/sanitization ast-grep --pattern 'app.get("/vault", $$$)' ast-grep --pattern 'router.get("/vault", $$$)'Length of output: 5400
Script:
#!/bin/bash # Check vault API implementation cat api/routes/vault.js # Look for URL sanitization rg -i "sanitize.*url" -A 5 rg -i "validate.*url" -A 5 # Check vault route implementation ast-grep --pattern 'router.get("/api/vault", $$$)' ast-grep --pattern 'app.get("/api/vault", $$$)'Length of output: 71141
api/internal_redirect/vault.js (2)
6-22: 🛠️ Refactor suggestion
Refactor to reduce code duplication.
The three route handlers share identical structure. Consider refactoring to a common handler:
+ function createVaultHandler(operation) { + return async function(req, res) { + if (!hasPermission("zander.web.vault", req, res)) return; + req.body.actioningUser = req.session.user.userId; + postAPIRequest( + `${process.env.siteAddress}/api/vault/${operation}`, + req.body, + `${process.env.siteAddress}/dashboard/vault`, + res + ); + res.redirect(`${process.env.siteAddress}/dashboard/vault`); + return res; + }; + } - app.post(baseEndpoint + "/create", async function (req, res) { - // ... duplicate code ... - }); + app.post(baseEndpoint + "/create", createVaultHandler("create")); - app.post(baseEndpoint + "/edit", async function (req, res) { - // ... duplicate code ... - }); + app.post(baseEndpoint + "/edit", createVaultHandler("edit")); - app.post(baseEndpoint + "/delete", async function (req, res) { - // ... duplicate code ... - }); + app.post(baseEndpoint + "/delete", createVaultHandler("delete"));Also applies to: 24-40, 42-58
12-22:
⚠️ Potential issueImprove error handling in postAPIRequest.
The current implementation:
- Sends a redirect response
- Then tries to redirect again
- Returns the response object
This could lead to "Cannot set headers after they are sent" errors.
- postAPIRequest( - `${process.env.siteAddress}/api/vault/create`, - req.body, - `${process.env.siteAddress}/dashboard/vault`, - res - ); - res.redirect(`${process.env.siteAddress}/dashboard/vault`); - return res; + await postAPIRequest( + `${process.env.siteAddress}/api/vault/create`, + req.body, + `${process.env.siteAddress}/dashboard/vault`, + res + ); + return;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.await postAPIRequest( `${process.env.siteAddress}/api/vault/create`, req.body, `${process.env.siteAddress}/dashboard/vault`, res ); return; });
listeners/filter.js (1)
39-56: 🛠️ Refactor suggestion
Enhance error handling and user feedback.
The current implementation has several areas for improvement:
- No rate limiting for repeat offenders
- No escalation for multiple violations
- Message deletion delay could be configurable
Consider implementing a more robust solution:
if (dataResponse.success === false) { + // Track violation count + const violations = await getViolationCount(message.author.id); + // Create an embed to warn the user const embed = new EmbedBuilder() .setTitle(`Prohibited content detected!`) .setDescription( - `\`${message.author.username}\`, please refrain from using prohibited content/phrases. Continued violations may result in penalties.` + `\`${message.author.username}\`, this is violation #${violations}. Please refrain from using prohibited content/phrases. ${violations >= 3 ? 'Further violations will result in a temporary mute.' : 'Continued violations may result in penalties.'}` ) .setColor(`#ff3333`); // Send the embed to the channel await message.reply({ embeds: [embed] }); + // Apply penalties for repeat offenders + if (violations >= 3) { + await timeoutUser(message.member, 300000); // 5-minute timeout + } // Delete the message after a short delay to ensure the embed is sent first setTimeout(async () => { if (message.deletable) { await message.delete(); } - }, 500); // Delay of 500ms before deleting the message + }, process.env.MESSAGE_DELETE_DELAY || 500); }Committable suggestion skipped: line range outside the PR's diff.
routes/dashboard/dashboard.js (1)
59-82:
⚠️ Potential issueEnhance error handling in bridge route.
The current implementation lacks proper error handling for API failures and doesn't validate the response data.
app.get("/dashboard/bridge", async function (req, res) { if (!hasPermission("zander.web.bridge", req, res, features)) return; - const fetchURL = `${process.env.siteAddress}/api/bridge/get`; - const response = await fetch(fetchURL, { - headers: { "x-access-token": process.env.apiKey }, - }); - const apiData = await response.json(); - - console.log(apiData); + try { + const fetchURL = `${process.env.siteAddress}/api/bridge/get`; + const response = await fetch(fetchURL, { + headers: { "x-access-token": process.env.apiKey }, + }); + + if (!response.ok) { + throw new Error(`API request failed with status ${response.status}`); + } + + const apiData = await response.json(); + if (!Array.isArray(apiData)) { + throw new Error('Invalid response format'); + } + return res.view("dashboard/bridge", { + pageTitle: `Dashboard - Bridge`, + config: config, + apiData: apiData, + features: features, + req: req, + globalImage: getGlobalImage(), + moment: moment, + announcementWeb: await getWebAnnouncement(), + }); + } catch (error) { + console.error('Bridge API Error:', error); + return res.status(500).view("error", { + pageTitle: "Error", + error: "Failed to load bridge data", + config: config, + features: features, + req: req, + globalImage: getGlobalImage(), + }); + } - res.view("dashboard/bridge", { - pageTitle: `Dashboard - Bridge`, - config: config, - apiData: apiData, - features: features, - req: req, - globalImage: getGlobalImage(), - moment: moment, - announcementWeb: await getWebAnnouncement(), - }); - - return res; });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.app.get("/dashboard/bridge", async function (req, res) { if (!hasPermission("zander.web.bridge", req, res, features)) return; try { const fetchURL = `${process.env.siteAddress}/api/bridge/get`; const response = await fetch(fetchURL, { headers: { "x-access-token": process.env.apiKey }, }); if (!response.ok) { throw new Error(`API request failed with status ${response.status}`); } const apiData = await response.json(); if (!Array.isArray(apiData)) { throw new Error('Invalid response format'); } return res.view("dashboard/bridge", { pageTitle: `Dashboard - Bridge`, config: config, apiData: apiData, features: features, req: req, globalImage: getGlobalImage(), moment: moment, announcementWeb: await getWebAnnouncement(), }); } catch (error) { console.error('Bridge API Error:', error); return res.status(500).view("error", { pageTitle: "Error", error: "Failed to load bridge data", config: config, features: features, req: req, globalImage: getGlobalImage(), }); } });
views/report.ejs (2)
19-24:
⚠️ Potential issueSanitize alert content before rendering.
The alert content from cookies is rendered directly in the template, which could lead to XSS vulnerabilities.
<% if (req.cookies.alertType) { %> <%- include("../partials/alert.ejs", { alertType: req.cookies.alertType, - content: req.cookies.alertContent + content: sanitizeHtml(req.cookies.alertContent) }) %> <% } %>Committable suggestion skipped: line range outside the PR's diff.
26-58:
⚠️ Potential issueAdd CSRF protection and input validation.
The form submission lacks CSRF protection and client-side input validation.
-<form method="post" action="<%= process.env.siteAddress %>/redirect/report/create"> +<form method="post" action="<%= process.env.siteAddress %>/redirect/report/create" onsubmit="return validateForm()"> + <%- include("partials/form/csrfToken.ejs") %> <%- include("partials/form/inputHidden.ejs", { elementName: "reportPlatform", required: true, elementValue: "WEB" }) %> <label for="reportedUser">User to Report</label> <%- include("partials/form/inputText.ejs", { elementName: "reportedUser", required: true, - elementValue: null + elementValue: null, + pattern: "^[a-zA-Z0-9_]{3,16}$", + title: "Username must be 3-16 characters long and contain only letters, numbers, and underscores" }) %> <br> <label for="reportReason">Report Reason</label> <%- include("partials/form/inputText.ejs", { elementName: "reportReason", required: true, - elementValue: null + elementValue: null, + minlength: "10", + maxlength: "200" }) %> <br> <label for="reportReasonEvidence">Report Reason Evidence</label> <%- include("partials/form/inputTextArea.ejs", { elementName: "reportReasonEvidence", required: false, - elementValue: null + elementValue: null, + maxlength: "1000" }) %> <br><br> <button type="submit" class="btn btn-primary">Submit Report</button> </form> +<script> +function validateForm() { + const reportedUser = document.getElementsByName("reportedUser")[0].value; + const reportReason = document.getElementsByName("reportReason")[0].value; + + if (!/^[a-zA-Z0-9_]{3,16}$/.test(reportedUser)) { + alert("Invalid username format"); + return false; + } + + if (reportReason.length < 10 || reportReason.length > 200) { + alert("Report reason must be between 10 and 200 characters"); + return false; + } + + return true; +} +</script>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.<form method="post" action="<%= process.env.siteAddress %>/redirect/report/create" onsubmit="return validateForm()"> <%- include("partials/form/csrfToken.ejs") %> <%- include("partials/form/inputHidden.ejs", { elementName: "reportPlatform", required: true, elementValue: "WEB" }) %> <label for="reportedUser">User to Report</label> <%- include("partials/form/inputText.ejs", { elementName: "reportedUser", required: true, elementValue: null, pattern: "^[a-zA-Z0-9_]{3,16}$", title: "Username must be 3-16 characters long and contain only letters, numbers, and underscores" }) %> <br> <label for="reportReason">Report Reason</label> <%- include("partials/form/inputText.ejs", { elementName: "reportReason", required: true, elementValue: null, minlength: "10", maxlength: "200" }) %> <br> <label for="reportReasonEvidence">Report Reason Evidence</label> <%- include("partials/form/inputTextArea.ejs", { elementName: "reportReasonEvidence", required: false, elementValue: null, maxlength: "1000" }) %> <br><br> <button type="submit" class="btn btn-primary">Submit Report</button> </form> <script> function validateForm() { const reportedUser = document.getElementsByName("reportedUser")[0].value; const reportReason = document.getElementsByName("reportReason")[0].value; if (!/^[a-zA-Z0-9_]{3,16}$/.test(reportedUser)) { alert("Invalid username format"); return false; } if (reportReason.length < 10 || reportReason.length > 200) { alert("Report reason must be between 10 and 200 characters"); return false; } return true; } </script>
routes/dashboard/applications.js (1)
66-66: 💡 Codebase verification
Update is correct, but needs consistency across the file
The change to use
id
as the query parameter is correct and matches the API implementation. However, there's another usage of this endpoint in the same file that still usesapplicationId
as the parameter name, which should be updated for consistency.
routes/dashboard/applications.js
: Update the other instance usingapplicationId
query parameter to useid
instead🔗 Analysis chain
Verify API endpoint compatibility.
The query parameter has been changed from
applicationId
toid
. Please ensure this change is consistent with the API endpoint implementation.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the API endpoint accepts both old and new parameter names # to avoid breaking changes rg -g "*.js" -A 5 "application/get\?(?:application)?[iI]d="Length of output: 519
Script:
#!/bin/bash # Find the API route implementation ast-grep --pattern 'router.$_("/api/application/get", $_)' # Find any other fetch calls to this endpoint rg -g "*.js" "fetch.*api/application/get" # Look for route handler implementations rg -g "*.js" "/api/application/get.*req.*res"Length of output: 467
Script:
#!/bin/bash # Find potential API implementation files fd -e js application # Look for route handlers with different patterns ast-grep --pattern 'app.$_("/api/application/get", $_)' # Search for API documentation fd -e md -e txt -e yaml -e json apiLength of output: 214
Script:
#!/bin/bash # Check contents of potential implementation files cat api/routes/application.js cat api/internal_redirect/application.js # Additional search for route handler with specific parameter rg -g "*.js" "req\.query\.(applicationId|id)"Length of output: 8673
routes/dashboard/announcement.js (1)
69-70: 💡 Codebase verification
Bug found in announcement GET endpoint implementation
The parameter change from
announcementSlug
toannouncementId
is consistent with the API implementation. However, there's a critical bug in the endpoint that prevents it from working:
- In
api/routes/announcement.js
, line 45:if (req.query === "announcementId")
is incorrect and should beif (announcementId)
🔗 Analysis chain
Verify API endpoint compatibility.
The query parameter has been changed from
announcementSlug
toannouncementId
. Please ensure this change is consistent with the API endpoint implementation.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the API endpoint accepts both old and new parameter names # to avoid breaking changes rg -g "*.js" -A 5 "announcement/get\?(?:announcement)?(?:[sS]lug|[iI]d)="Length of output: 544
Script:
#!/bin/bash # Find the API endpoint implementation ast-grep --pattern 'app.$_("/api/announcement/get", $_)' # Search for any historical usage with 'slug' rg -g "*.js" "announcement/get.*[sS]lug" # Find any route handlers for this endpoint ast-grep --pattern 'router.$_("/announcement/get", $_)'Length of output: 159
Script:
#!/bin/bash # Find announcement-related route handlers and API implementations fd "announcement" --type f --exec rg -l "router\.|app\." {} \; # Search for files handling announcement query parameters rg -g "*.js" "req\.query\.announcement(?:Id|Slug)" # Look for announcement-related API files fd "announcement" --type f --exec rg -l "module\.exports.*=.*\{" {} \;Length of output: 382
Script:
#!/bin/bash # Check the contents of API implementation files echo "=== API Routes Implementation ===" cat "./api/routes/announcement.js" echo -e "\n=== Internal Redirect Implementation ===" cat "./api/internal_redirect/announcement.js"Length of output: 8747
routes/dashboard/vault.js (2)
25-30: 🛠️ Refactor suggestion
Add error handling for API response.
The API response handling is missing error checks. Consider adding proper error handling:
const fetchURL = `${process.env.siteAddress}/api/vault/get`; const response = await fetch(fetchURL, { headers: { "x-access-token": process.env.apiKey }, }); + if (!response.ok) { + return res.status(response.status).send(`API Error: ${response.statusText}`); + } const apiData = await response.json(); + if (!apiData.success) { + return res.status(400).send(apiData.message || 'Failed to fetch vault data'); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const fetchURL = `${process.env.siteAddress}/api/vault/get`; const response = await fetch(fetchURL, { headers: { "x-access-token": process.env.apiKey }, }); if (!response.ok) { return res.status(response.status).send(`API Error: ${response.statusText}`); } const apiData = await response.json(); if (!apiData.success) { return res.status(400).send(apiData.message || 'Failed to fetch vault data'); }
63-64: 🛠️ Refactor suggestion
Add input validation for vaultId.
The
vaultId
query parameter is used without validation. Consider adding validation to prevent potential issues:+ if (!req.query.vaultId) { + return res.status(400).send('Missing vaultId parameter'); + } const vaultId = req.query.vaultId;Committable suggestion skipped: line range outside the PR's diff.
commands/profile.mjs (1)
50-50: 🛠️ Refactor suggestion
Add error handling for getProfilePicture.
The profile picture retrieval lacks error handling. Consider adding a fallback:
- let profilePicture = await getProfilePicture(apiData.data.profileData.username); + let profilePicture; + try { + profilePicture = await getProfilePicture(apiData.data.profileData.username); + } catch (error) { + console.error('Failed to fetch profile picture:', error); + profilePicture = 'default_avatar_url'; // Add a default avatar URL + }Also applies to: 64-64
views/dashboard/vault/vault-list.ejs (2)
51-55:
⚠️ Potential issueEnhance security of the delete form submission.
The form submission could be vulnerable to CSRF attacks. Consider adding CSRF protection tokens.
Add CSRF token to the form:
<form action="<%= process.env.siteAddress %>/redirect/vault/delete" method="post"> + <input type="hidden" name="_csrf" value="<%= csrfToken %>"> <input type="hidden" name="vaultId" value="<%= vault.vaultId %>"> <input type="hidden" name="actioningUser" value="<%= req.session.user.userId %>"> <button class="btn btn-sm btn-danger" type="submit">Delete</button> </form>
Committable suggestion skipped: line range outside the PR's diff.
46-46:
⚠️ Potential issuePrevent potential XSS in vault display name.
The vault display name is rendered without escaping, which could lead to XSS attacks.
Escape the display name:
-<td><%= vault.displayName %></td> +<td><%- htmlEscape(vault.displayName) %></td>Committable suggestion skipped: line range outside the PR's diff.
commands/status.mjs (2)
47-48:
⚠️ Potential issueAdd error handling for JSON parsing.
The JSON parsing could throw an error if the data is malformed.
Wrap the JSON parsing in try-catch:
-const statusInfo = JSON.parse(serverStatus.statusInfo); -statusInfo.forEach((server) => { +try { + const statusInfo = JSON.parse(serverStatus.statusInfo); + statusInfo.forEach((server) => { +} catch (error) { + console.error('Error parsing status info:', error); + return; +}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.try { const statusInfo = JSON.parse(serverStatus.statusInfo); statusInfo.forEach((server) => { } catch (error) { console.error('Error parsing status info:', error); return; }
24-27: 🛠️ Refactor suggestion
Enhance security of API key transmission.
The API key is sent as a header without additional security measures.
Consider:
- Using HTTPS for all API calls
- Adding request signing or additional authentication measures
- Implementing rate limiting
const response = await fetch(fetchURL, { headers: { "x-access-token": process.env.apiKey, + "Content-Type": "application/json", + "User-Agent": "StatusCommand/1.0" }, + timeout: 5000 });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const fetchURL = `${process.env.siteAddress}/api/bridge/server/get`; const response = await fetch(fetchURL, { headers: { "x-access-token": process.env.apiKey, "Content-Type": "application/json", "User-Agent": "StatusCommand/1.0" }, timeout: 5000 });
views/dashboard/bridge.ejs (2)
24-50:
⚠️ Potential issueAdd input validation for command submission.
The command input lacks validation, which could lead to command injection or unauthorized commands being executed.
Add input validation:
- Implement client-side validation
- Add pattern matching for allowed commands
- Consider adding a dropdown for predefined commands instead of free text
<div class="form-group col"> <label for="command">Command</label> <%- include("../partials/form/inputText.ejs", { elementName: "command", elementValue: null, - required: true + required: true, + pattern: "^[a-zA-Z0-9_\-]+$", + maxlength: "50", + onkeypress: "return validateCommandInput(event)" }) %> </div>Committable suggestion skipped: line range outside the PR's diff.
81-82:
⚠️ Potential issueEscape command output in table display.
The command output is displayed without escaping, which could lead to XSS attacks.
Escape the command output:
-<td><code><%= bridge.command %></code></td> +<td><code><%- htmlEscape(bridge.command) %></code></td>Committable suggestion skipped: line range outside the PR's diff.
app.js (1)
42-45:
⚠️ Potential issueRemove duplicate imports.
The following imports are duplicated:
discordController.js
(lines 23 and 42)bridgeCleanupCron.js
(lines 25 and 44)Apply this diff to remove the duplicates:
-import("./controllers/discordController.js"); -import("./cron/userCodeExpiryCron.js"); -import("./cron/bridgeCleanupCron.js"); -import("./cron/discordStatsUpdateCron.js");Committable suggestion skipped: line range outside the PR's diff.
routes/profileRoutes.js (1)
54-58:
⚠️ Potential issuePotential security vulnerability in report fetching.
The userId is directly interpolated into the URL without sanitization. While the API might handle sanitization, it's safer to encode URL parameters.
Apply this diff to encode the userId:
-const fetchReportsURL = `${process.env.siteAddress}/api/report/get?reporterId=${profileApiData.data[0].userId}`; +const fetchReportsURL = `${process.env.siteAddress}/api/report/get?reporterId=${encodeURIComponent(profileApiData.data[0].userId)}`;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const fetchReportsURL = `${process.env.siteAddress}/api/report/get?reporterId=${encodeURIComponent(profileApiData.data[0].userId)}`; const reportsResponse = await fetch(fetchReportsURL, { headers: { "x-access-token": process.env.apiKey }, }); const profileReportsApiData = await reportsResponse.json();
api/routes/shopdirectory.js (2)
39-53: 🛠️ Refactor suggestion
Improve error handling for external API calls.
The Craftdex API call could benefit from timeout and retry logic to handle temporary failures.
Consider implementing:
- Request timeout
- Retry logic with exponential backoff
- Circuit breaker pattern for API health
118-122:
⚠️ Potential issueCritical: SQL injection vulnerability in material parameter.
Direct string interpolation in SQL queries is unsafe. Use parameterized queries instead.
Apply this diff to use parameterized queries:
-const dbQuery = material - ? `SELECT * FROM shoppingDirectory WHERE item LIKE '%${material}%' OR item LIKE '%${material - .toUpperCase() - .replace(/ /g, "_")}%';` - : `SELECT * FROM shoppingDirectory;`; +const dbQuery = material + ? { + text: 'SELECT * FROM shoppingDirectory WHERE item LIKE $1 OR item LIKE $2', + values: [`%${material}%`, `%${material.toUpperCase().replace(/ /g, "_")}%`] + } + : 'SELECT * FROM shoppingDirectory';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const dbQuery = material ? { text: 'SELECT * FROM shoppingDirectory WHERE item LIKE $1 OR item LIKE $2', values: [`%${material}%`, `%${material.toUpperCase().replace(/ /g, "_")}%`] } : 'SELECT * FROM shoppingDirectory';
routes/index.js (1)
153-153: 🛠️ Refactor suggestion
Remove debug console.log statement.
Production code should not contain debug console.log statements.
- console.log(shopApiData.data[0].userData);
views/dashboard/vault/vault-editor.ejs (1)
108-121: 🛠️ Refactor suggestion
Add type validation for position field.
The position field should be restricted to numeric values for proper ordering.
- elementName: "position", - elementValue: null, - required: true + elementName: "position", + elementValue: null, + required: true, + type: "number", + min: "0"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.<% if (type === 'create') { %> <%- include("../../partials/form/inputText.ejs", { elementName: "position", elementValue: null, required: true, type: "number", min: "0" }) %> <% } %> <% if (type === 'edit') { %> <%- include("../../partials/form/inputText.ejs", { elementName: "position", elementValue: vaultApiData.position, required: true }) %> <% } %>
views/modules/profile/profile.ejs (1)
153-179: 💡 Codebase verification
Add defensive checks for reports data to prevent potential errors
The reports feature needs additional error handling:
- Add null check for
profileReportsApiData.data
before passing to view- Provide fallback empty array when data is undefined
- Validate API response structure
Example:
const profileReportsApiData = { data: (await reportsResponse.json())?.data || [] };🔗 Analysis chain
Consider enhancing the reports table with additional features.
The reports table implementation is good, but could benefit from:
- Pagination for large datasets
- Sorting capabilities
- Status indicators for reports
- Action buttons for report management
Also, verify that
profileReportsApiData.data
is properly handled when empty or undefined.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for proper error handling in the reports API route # Search for error handling in the reports API route ast-grep --pattern 'app.get($_, function($_, $_) { $$$ if (!results || !results.length) { $$$ } $$$ })'Length of output: 117
Script:
#!/bin/bash # Find relevant API route files echo "=== Searching for API route files ===" fd -e js -e ts "routes|api" --type f echo -e "\n=== Searching for profileReportsApiData usage ===" rg "profileReportsApiData" -l echo -e "\n=== Searching for reports-related API endpoints ===" rg "app\.(get|post).*reports.*" -A 5Length of output: 610
Script:
#!/bin/bash # Examine profileRoutes.js content echo "=== Checking profileRoutes.js content ===" cat routes/profileRoutes.js echo -e "\n=== Looking for reports-related code blocks ===" rg -A 10 -B 5 "profileReportsApiData" routes/profileRoutes.jsLength of output: 6430
api/routes/user.js (2)
129-130:
⚠️ Potential issueFix SQL injection vulnerability in user queries.
The queries are vulnerable to SQL injection. Use parameterized queries consistently.
- `SELECT * FROM users WHERE discordId=?;` + `SELECT * FROM users WHERE discordId = ?;` - `SELECT * FROM users WHERE userId=?;` + `SELECT * FROM users WHERE userId = ?;`Also applies to: 154-155
127-170: 🛠️ Refactor suggestion
Refactor duplicate error handling logic.
The error handling code is duplicated across the
discordId
anduserId
query handlers. Consider extracting this into a reusable function.+ function handleQueryError(error, results, res, lang) { + if (error) { + return res.send({ + success: false, + message: error, + }); + } + + if (!results || !results.length) { + return res.send({ + success: false, + message: lang.api.userDoesNotExist, + }); + } + + return res.send({ + success: true, + data: results, + }); + } // Then use it in each query: db.query( `SELECT * FROM users WHERE discordId=?;`, [discordId], - function (error, results, fields) { - if (error) { - return res.send({ - success: false, - message: error, - }); - } - - if (!results || !results.length) { - return res.send({ - success: false, - message: lang.api.userDoesNotExist, - }); - } - - return res.send({ - success: true, - data: results, - }); - } + (error, results, fields) => handleQueryError(error, results, res, lang) );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.} else if (discordId) { function handleQueryError(error, results, res, lang) { if (error) { return res.send({ success: false, message: error, }); } if (!results || !results.length) { return res.send({ success: false, message: lang.api.userDoesNotExist, }); } return res.send({ success: true, data: results, }); } db.query( `SELECT * FROM users WHERE discordId=?;`, [discordId], (error, results, fields) => handleQueryError(error, results, res, lang) ); } else if (userId) { db.query( `SELECT * FROM users WHERE userId=?;`, [userId], (error, results, fields) => handleQueryError(error, results, res, lang) );
From https://github.com/ModularSoftAU/zander-issues/issues/181
Summary by CodeRabbit
Based on the comprehensive summary of changes, here are the high-level release notes:
New Features
Improvements
.js
file extensionsSecurity Enhancements
httpOnly
flag to cookiesPerformance
Bug Fixes