fix(security): transcript access control (closes #555)

This commit is contained in:
Isaac
2024-09-06 03:59:24 +01:00
parent 2d2f350284
commit b8b5ac946a
3 changed files with 69 additions and 30 deletions

View File

@@ -1,5 +1,8 @@
const { SlashCommand } = require('@eartharoid/dbf');
const { ApplicationCommandOptionType } = require('discord.js');
const {
ApplicationCommandOptionType,
PermissionsBitField,
} = require('discord.js');
const { isStaff } = require('../../lib/users');
const ExtendedEmbedBuilder = require('../../lib/embed');
const Cryptr = require('cryptr');
@@ -60,12 +63,45 @@ module.exports = class TicketsSlashCommand extends SlashCommand {
}
const fields = [];
let base_filter;
if (member.id === interaction.member.id) {
base_filter = {
createdById: member.id,
guildId: interaction.guild.id,
};
} else {
const { categories } = await client.prisma.guild.findUnique({
select: {
categories: {
select: {
id: true,
staffRoles: true,
},
},
},
where: { id: interaction.guild.id },
});
const allow_category_ids = (
(
client.supers.includes(interaction.member.id) ||
interaction.member.permissions.has(PermissionsBitField.Flags.ManageGuild)
)
? categories
: categories.filter(c => c.staffRoles.some(id => interaction.member.roles.cache.has(id)))
)
.map(c => c.id);
base_filter = {
categoryId: { in: allow_category_ids },
createdById: member.id,
guildId: interaction.guild.id,
};
}
const open = await client.prisma.ticket.findMany({
include: { category: true },
where: {
createdById: member.id,
guildId: interaction.guild.id,
...base_filter,
open: true,
},
});
@@ -75,8 +111,7 @@ module.exports = class TicketsSlashCommand extends SlashCommand {
orderBy: { createdAt: 'desc' },
take: 10, // max 10 rows
where: {
createdById: member.id,
guildId: interaction.guild.id,
...base_filter,
open: false,
},
});
@@ -84,8 +119,8 @@ module.exports = class TicketsSlashCommand extends SlashCommand {
if (open.length >= 1) {
fields.push({
name: getMessage('commands.slash.tickets.response.fields.open.name'),
value: open.map(ticket =>{
const topic = ticket.topic ? `- \`${decrypt(ticket.topic).replace(/\n/g, ' ').slice(0, 30) }\`` : '';
value: open.map(ticket => {
const topic = ticket.topic ? `- \`${decrypt(ticket.topic).replace(/\n/g, ' ').slice(0, 30)}\`` : '';
return `> <#${ticket.id}> ${topic}`;
}).join('\n'),
});

View File

@@ -1,12 +1,14 @@
const { SlashCommand } = require('@eartharoid/dbf');
const { ApplicationCommandOptionType } = require('discord.js');
const {
ApplicationCommandOptionType,
PermissionsBitField,
} = require('discord.js');
const fs = require('fs');
const { join } = require('path');
const Mustache = require('mustache');
const { AttachmentBuilder } = require('discord.js');
const Cryptr = require('cryptr');
const { decrypt } = new Cryptr(process.env.ENCRYPTION_KEY);
const { isStaff } = require('../../lib/users');
const ExtendedEmbedBuilder = require('../../lib/embed');
module.exports = class TranscriptSlashCommand extends SlashCommand {
@@ -46,6 +48,15 @@ module.exports = class TranscriptSlashCommand extends SlashCommand {
);
}
shouldAllowAccess(interaction, ticket) {
if (interaction.guild.id !== ticket.guildId) return false;
if (ticket.createdById === interaction.member.id) return true;
if (interaction.client.supers.includes(interaction.member.id)) return true;
if (interaction.member.permissions.has(PermissionsBitField.Flags.ManageGuild)) return true;
if (interaction.member.roles.cache.filter(role => ticket.category.staffRoles.includes(role.id)).size > 0) return true;
return false;
}
async fillTemplate(ticket) {
/** @type {import("client")} */
const client = this.client;
@@ -146,10 +157,7 @@ module.exports = class TranscriptSlashCommand extends SlashCommand {
if (!ticket) throw new Error(`Ticket ${ticketId} does not exist`);
if (
ticket.createdById !== interaction.member.id &&
!(await isStaff(interaction.guild, interaction.member.id))
) {
if (!this.shouldAllowAccess(interaction, ticket)) {
const settings = await client.prisma.guild.findUnique({ where: { id: interaction.guild.id } });
const getMessage = client.i18n.getLocale(settings.locale);
return await interaction.editReply({