fix perms checks

This commit is contained in:
Erik 2022-07-28 10:45:24 +03:00
parent 2581d91481
commit 79f09fe03c
Signed by untrusted user: Navy.gif
GPG Key ID: 811EC0CD80E7E5FB
14 changed files with 110 additions and 56 deletions

View File

@ -22,10 +22,10 @@ class AuditLogObserver extends Observer {
}
async guildBanAdd({ guild, user, guildWrapper: wrapper }) {
async guildBanAdd({ user, guildWrapper: wrapper }) {
const settings = await wrapper.settings();
if (!settings.moderation.channel || !settings.moderation.infractions.includes('BAN')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier.
const audit = await this._fetchFirstEntry(guild, user, 'MemberBanAdd');
const audit = await this._fetchFirstEntry(wrapper, user, 'MemberBanAdd');
if (!audit) return undefined;
new Infraction(this.client, {
type: 'BAN',
@ -37,10 +37,10 @@ class AuditLogObserver extends Observer {
}).handle();
}
async guildBanRemove({ guild, user, guildWrapper: wrapper }) {
async guildBanRemove({ user, guildWrapper: wrapper }) {
const settings = await wrapper.settings();
if (!settings.moderation.channel || !settings.moderation.infractions.includes('UNBAN')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier.
const audit = await this._fetchFirstEntry(guild, user, 'MemberBanRemove');
const audit = await this._fetchFirstEntry(wrapper, user, 'MemberBanRemove');
if (!audit) return undefined;
new Infraction(this.client, {
type: 'UNBAN',
@ -56,7 +56,7 @@ class AuditLogObserver extends Observer {
const { guildWrapper: wrapper } = member;
const settings = await wrapper.settings();
if (!settings.moderation.channel || !settings.moderation.infractions.includes('KICK')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier.
const audit = await this._fetchFirstEntry(member.guild, member.user, 'MemberKick');
const audit = await this._fetchFirstEntry(wrapper, member.user, 'MemberKick');
if (!audit) return undefined;
new Infraction(this.client, {
type: 'KICK',
@ -123,7 +123,7 @@ class AuditLogObserver extends Observer {
const mutedRole = settings.mute.role;
if (!mutedRole) return undefined;
const audit = await this._fetchFirstEntry(newMember.guild, newMember.user, 'MemberRoleUpdate');
const audit = await this._fetchFirstEntry(wrapper, newMember.user, 'MemberRoleUpdate');
if (!audit) return undefined;
let type = null;
@ -148,7 +148,8 @@ class AuditLogObserver extends Observer {
}
async _fetchFirstEntry(guild, user, type, subtype = null) {
if (!guild.members.me.permissions.has('ViewAuditLog')) return null;
const me = await guild.resolveMember(this.client.user);
if (!me.permissions.has('ViewAuditLog')) return null;
type = AuditLogEvent[type];
const audit = await guild.fetchAuditLogs({ limit: 1, type });
if (audit.entries.size === 0) return null;

View File

@ -133,7 +133,7 @@ module.exports = class AutoModeration extends Observer {
if (!enabled || roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return;
const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages');
const missing = channel.permissionsFor(this.client.user).missing('ManageMessages');
if (missing.length) {
this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'word', permissions: missing });
return;
@ -432,7 +432,7 @@ module.exports = class AutoModeration extends Observer {
if (roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return;
const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages');
const missing = channel.permissionsFor(this.client.user).missing('ManageMessages');
if (missing.length) {
this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'link', permissions: missing });
return;
@ -454,9 +454,10 @@ module.exports = class AutoModeration extends Observer {
let log = `${guild.name} Link filter debug:`;
for (const match of matches) {
const { domain } = match.match(this.regex.linkReg).groups;
let { domain } = match.match(this.regex.linkReg).groups;
domain = domain.toLowerCase();
// Invites are filtered separately
if (domain.toLowerCase() === 'discord.gg') continue;
if (domain === 'discord.gg') continue;
log += `\nMatched link ${match}: `;
const predicate = (dom) => {
@ -550,13 +551,13 @@ module.exports = class AutoModeration extends Observer {
const member = message.member || await guild.members.fetch(author.id).catch(() => null);
const settings = await wrapper.settings();
const { invitefilter: setting } = settings;
const { bypass, ignore, actions, silent, enabled, whitelist } = setting;
const { bypass, ignore, actions, silent, enabled, whitelist = [] } = setting;
if (!enabled) return;
const roles = member?.roles.cache.map((r) => r.id) || [];
if (roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return;
const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages');
const missing = channel.permissionsFor(this.client.user).missing('ManageMessages');
if (missing.length) {
this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'invite', permissions: missing });
return;
@ -613,7 +614,7 @@ module.exports = class AutoModeration extends Observer {
if (!enabled || roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return;
const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages');
const missing = channel.permissionsFor(this.client.user).missing('ManageMessages');
if (missing.length) {
this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'mention', permissions: missing });
return;

View File

@ -1,5 +1,5 @@
/* eslint-disable no-labels */
const { WebhookClient, AttachmentBuilder } = require('discord.js');
const { WebhookClient, AttachmentBuilder, AuditLogEvent } = require('discord.js');
const { stripIndents } = require('common-tags');
const moment = require('moment');
const { inspect } = require('util');
@ -103,22 +103,23 @@ class GuildLogger extends Observer {
const hook = await guild.getWebhook('messages');
if (!hook) {
this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`);
// this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`);
return this.client.emit('logError', { guild, logger: 'threadLogger', reason: 'MSGLOG_NO_HOOK' });
}
let actor = null;
const auditLogPerm = guild.members.me.permissions.has('ViewAuditLog');
const me = await this.client.resolver.resolveMember(this.client.user, null, guild);
const auditLogPerm = me.permissions.has('ViewAuditLog');
if (type === 'CREATE') actor = owner;
else if (type === 'DELETE' && auditLogPerm) {
const auditLogs = await guild.fetchAuditLogs({ type: 'THREAD_DELETE', limit: 1 });
const auditLogs = await guild.fetchAuditLogs({ type: AuditLogEvent.ThreadDelete, limit: 1 });
const log = auditLogs.entries.first();
if (log) {
if (thread.id !== log.target.id) return;
actor = log.executor;
}
} else if (['ARCHIVE', 'UNARCHIVE'].includes(type) && auditLogPerm) {
const auditLogs = await guild.fetchAuditLogs({ type: 'THREAD_UPDATE', limit: 1 });
const auditLogs = await guild.fetchAuditLogs({ type: AuditLogEvent.ThreadUpdate, limit: 1 });
const log = auditLogs.entries.first();
if (log) {
if (thread.id !== log.target.id) return;
@ -178,7 +179,7 @@ class GuildLogger extends Observer {
const hook = await wrapper.getWebhook('messages');
if (!hook) {
this.logger.debug(`Missing messageLog hook in ${wrapper.name} (${wrapper.id})`);
// this.logger.debug(`Missing messageLog hook in ${wrapper.name} (${wrapper.id})`);
return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_HOOK' });
}
@ -340,13 +341,13 @@ class GuildLogger extends Observer {
const { ignore, bypass } = chatlogs;
if (ignore.includes(channel.id)) return;
const missing = logChannel.permissionsFor(guild.members.me).missing(['ViewChannel', 'EmbedLinks', 'SendMessages', 'ManageWebhooks']);
const missing = logChannel.permissionsFor(this.client.user).missing(['ViewChannel', 'EmbedLinks', 'SendMessages', 'ManageWebhooks']);
if (missing.length)
return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_PERMS', params: { missing: missing.join(', ') } });
const hook = await wrapper.getWebhook('messages');
if (!hook) {
this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`);
// this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`);
return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_HOOK' });
}
@ -364,6 +365,7 @@ class GuildLogger extends Observer {
content = Util.escapeMarkdown(content);
if (author.bot) continue;
// TODO: Apparently the cache for this doesn't work properly and the bot hits rate limits occasionally, make own cache at some point
if (!member || member.partial) member = await guild.members.fetch(message.author.id).catch(() => {
return false;
});
@ -533,7 +535,7 @@ class GuildLogger extends Observer {
const hook = await wrapper.getWebhook('messages');
if (!hook) {
this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`);
// this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`);
return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_HOOK' });
}
@ -710,7 +712,7 @@ class GuildLogger extends Observer {
async memberLeave(member) {
const { guild, guildWrapper: wrapper } = member;
const { guildWrapper: wrapper } = member;
const settings = await wrapper.settings();
const setting = settings.members;
if (!setting.channel || !setting.enabled) return;
@ -732,7 +734,7 @@ class GuildLogger extends Observer {
if (oldMember.nickname === newMember.nickname) return;
const { guild, user, guildWrapper: wrapper } = oldMember;
const { user, guildWrapper: wrapper } = oldMember;
const settings = await wrapper.settings();
const setting = settings.nicknames;
if (!setting.channel || !setting.enabled) return;

View File

@ -0,0 +1,31 @@
const { Observer } = require("../../interfaces");
class Metrics extends Observer {
constructor(client) {
super(client, {
name: 'metrics',
priority: 10,
disabled: false
});
this.hooks = [
['apiRequest', this.request.bind(this)],
['apiResponse', this.response.bind(this)]
];
this.cache = {};
}
async request(request) {
// console.log(request);
}
async response(request, response) {
// console.log(response, await response.json(), response.status);
}
}
module.exports = Metrics;

View File

@ -51,7 +51,8 @@ class UtilityHook extends Observer {
const { guildWrapper: guild } = member;
const settings = await guild.settings();
const setting = settings.mute;
if (!guild.members.me.permissions.has('ManageRoles')) return;
const me = await guild.resolveMember(this.client.user);
if (!me.permissions.has('ManageRoles')) return;
const infraction = await this.client.storageManager.mongodb.infractions.findOne({
duration: { $gt: 0 },
@ -79,7 +80,7 @@ class UtilityHook extends Observer {
if (managed) {
await member.roles.add(setting.role, 'automute upon rejoin, type 1');
await member.roles.remove(remove, 'removing excess roles for type 1 mute');
} else await member.roles.set(setting.role, 'automute upon join, type 1');
} else await member.roles.set([role], 'automute upon join, type 1');
} else if (infraction.data.muteType === 2) {
@ -100,7 +101,8 @@ class UtilityHook extends Observer {
const settings = await guild.settings();
const setting = settings.stickyrole;
if (!setting.roles.length || guild.premium < 1) return;
if (!guild.members.me.permissions.has('ManageRoles')) return;
const me = await guild.resolveMember(this.client.user);
if (!me.permissions.has('ManageRoles')) return;
const data = await this.client.storageManager.mongodb.role_cache.findOne({ guild: guild.id, member: member.id });
if (!data) return;
@ -115,7 +117,8 @@ class UtilityHook extends Observer {
const settings = await guild.settings();
const setting = settings.autorole;
if (!setting.enabled) return;
if (!guild.members.me.permissions.has('ManageRoles')) return;
const me = await guild.resolveMember(this.client.user);
if (!me.permissions.has('ManageRoles')) return;
const _roles = await guild.resolveRoles(setting.roles);
const roles = _roles.map((r) => r.id);
@ -153,7 +156,8 @@ class UtilityHook extends Observer {
const { guild } = invite;
if (!guild) return;
if (!guild.members.me.permissions.has('ManageGuild')) return;
const me = await this.client.resolver.resolveMember(this.client.user, null, guild);
if (!me.permissions.has('ManageGuild')) return;
if (!guild.invites) guild.invites = await guild.fetchInvites();
guild.invites.set(invite.code, invite);
@ -163,7 +167,8 @@ class UtilityHook extends Observer {
const { guild } = invite;
if (!guild) return;
if (!guild.members.me.permissions.has('ManageGuild')) return;
const me = await this.client.resolver.resolveMember(this.client.user, null, guild);
if (!me.permissions.has('ManageGuild')) return;
if (!guild.invites) guild.invites = await guild.fetchInvites();
guild.invites.delete(invite.code);
@ -203,7 +208,8 @@ class UtilityHook extends Observer {
!selfrole.channel || selfrole.channel !== channel.id ||
!selfrole.roles.length) return;
const missing = guild.members.me.permissions.missing(['ManageRoles']);
const me = await guild.resolveMember(this.client.user);
const missing = me.permissions.missing(['ManageRoles']);
if (missing.length)
return this.client.emit('utilityError', { guild, utility: 'selfrole', reason: 'UTILITY_SELFROLE_PERMS', params: { missing: missing.join(', ') } });

View File

@ -53,7 +53,8 @@ class AddroleInfraction extends Infraction {
const { grantable } = await this.guild.settings();
let filtered = [];
const { highest: clientHighest } = this.guild.members.me.roles;
const me = await this.guild.resolveMember(this.client.user);
const { highest: clientHighest } = me.roles;
filtered = this.data.roles.filter((r) => r.comparePositionTo(clientHighest) < 0);
if (filtered.length === 0) {
return super._fail('C_ADDROLE_ROLEHIERARCHYBOT');

View File

@ -140,7 +140,7 @@ class LockdownInfraction extends Infraction {
async verify() {
const perms = this.target.permissionsFor(this.guild.members.me);
const perms = this.target.permissionsFor(this.client.user);
if(perms.missing('ManageRoles').length) return this._fail('INFRACTION_LOCKDOWN_MISSING_PERMS');
return super._verify();

View File

@ -48,6 +48,8 @@ class MuteInfraction extends Infraction {
role = await this.client.resolver.resolveRole(setting.role, true, this.guild);
}
const me = await this.guild.resolveMember(this.client.user);
let removed = [];
switch (setting.type) {
case 0:
@ -60,12 +62,12 @@ class MuteInfraction extends Infraction {
break;
case 1:
removed = this.member.roles.cache.filter((r) => !r.managed &&
r.comparePositionTo(this.guild.members.me.roles.highest) < 0 &&
r.comparePositionTo(me.roles.highest) < 0 &&
r.id !== this.guild.id);
try {
await this.member.roles.set([
...this.member.roles.cache.filter((r) => r.managed ||
r.comparePositionTo(this.guild.members.me.roles.highest) >= 0 ||
r.comparePositionTo(me.roles.highest) >= 0 ||
r.id === this.guild.id).values(),
role
], this._reason);
@ -76,11 +78,11 @@ class MuteInfraction extends Infraction {
break;
case 2:
removed = this.member.roles.cache.filter((r) => !r.managed &&
r.comparePositionTo(this.guild.members.me.roles.highest) < 0 &&
r.comparePositionTo(me.roles.highest) < 0 &&
r.id !== this.guild.id);
try {
await this.member.roles.set(this.member.roles.cache.filter((r) => r.managed ||
r.comparePositionTo(this.guild.members.me.roles.highest) >= 0 ||
r.comparePositionTo(me.roles.highest) >= 0 ||
r.id === this.guild.id), this._reason);
} catch (error) {
this.client.logger.error(`Mute infraction failed to calculate removeable roles, might want to check this out.\n${error.stack || error}`);
@ -96,6 +98,8 @@ class MuteInfraction extends Infraction {
}
}
if (this.member.voice.channel) await this.member.voice.disconnect(this._reason);
this.data = {
removedRoles: removed.map((r) => r.id),
muteType: setting.type,
@ -107,7 +111,7 @@ class MuteInfraction extends Infraction {
if (callback) {
this.data.removedRoles = [...new Set([...this.data.removedRoles, ...callback.infraction.data.removedRoles])];
this.client.moderationManager.removeCallback(callback.infraction);
this.client.moderationManager.removeCallback(callback.infraction, true);
}
// if(callbacks.size > 0) callbacks.map((c) => this.client.moderationManager._removeExpiration(c));
@ -134,10 +138,12 @@ class MuteInfraction extends Infraction {
return this._fail('COMMAND_MUTE_INVALIDMUTEROLE', true);
}
}
const me = await this.guild.resolveMember(this.client.user);
if (settings.mute.type === 3) {
if (this.guild.members.me.permissions.missing('ModerateMembers').length) return this._fail('COMMAND_MUTE_MISSING_MODERATE_PERM', true);
if (me.permissions.missing('ModerateMembers').length) return this._fail('COMMAND_MUTE_MISSING_MODERATE_PERM', true);
if (me.roles.highest.position <= this.member.roles.highest.position) return this._fail('COMMAND_MUTE_HIERARCHY_ERROR');
// if (!this.duration && !settings.mute.default)
} else if (this.guild.members.me.permissions.missing('ManageRoles').length) return this._fail('COMMAND_MUTE_MISSING_MANAGEROLE_PERM');
} else if (me.permissions.missing('ManageRoles').length) return this._fail('COMMAND_MUTE_MISSING_MANAGEROLE_PERM');
return super._verify();

View File

@ -82,7 +82,8 @@ class NicknameInfraction extends Infraction {
async verify() {
const { highest } = this.member.roles;
if (highest.comparePositionTo(this.guild.members.me.roles.highest) > 0 || !this.guild.members.me.permissions.has('ManageNicknames')) {
const me = await this.guild.resolveMember(this.client.user);
if (highest.comparePositionTo(me.roles.highest) > 0 || !me.permissions.has('ManageNicknames')) {
return this._fail('C_NICKNAME_MISSINGPERMISSIONS');
}

View File

@ -53,7 +53,8 @@ class RemoveroleInfraction extends Infraction {
const { grantable } = await this.guild.settings();
let filtered = [];
const { highest: clientHighest } = this.guild.members.me.roles;
const me = await this.guild.resolveMember(this.client.user);
const { highest: clientHighest } = me.roles;
filtered = this.data.roles.filter((r) => r.comparePositionTo(clientHighest) < 0);
if (filtered.length === 0) {
return super._fail('C_REMOVEROLE_ROLEHIERARCHYBOT');

View File

@ -39,7 +39,7 @@ class UnbanInfraction extends Infraction {
const callbacks = this.client.moderationManager.callbacks.filter((c) => c.infraction.type === 'BAN'
&& c.infraction.target === this.target.id);
if (callbacks.size > 0) callbacks.map((c) => this.client.moderationManager.removeCallback(c.infraction));
if (callbacks.size > 0) callbacks.map((c) => this.client.moderationManager.removeCallback(c.infraction, true));
await this.handle();
return this._succeed();

View File

@ -95,7 +95,7 @@ class UnlockdownInfraction extends Infraction {
async verify() {
const perms = this.target.permissionsFor(this.guild.members.me);
const perms = this.target.permissionsFor(this.client.user);
if (perms.missing('ManageRoles').length) return this._fail('INFRACTION_LOCKDOWN_MISSING_PERMS');
return super._verify();

View File

@ -321,7 +321,9 @@ module.exports = class FilterUtility {
matched: true,
_matcher: _word,
matcher: `fuzzy [\`${_word}\`, \`${sim}\`, \`${threshold}\`]`,
type: 'fuzzy'
type: 'fuzzy',
_threshold: threshold,
_sim: sim
};
}

View File

@ -206,7 +206,7 @@ class SettingsMigrator {
stickyrole: result.stickyrole ? { ...result.stickyrole, enabled: Boolean(result.stickyrole.roles.length) } : undefined,
welcomer: result.welcomer,
commands: { disabled: result.disabledCommands, custom: {} },
prefix: result.prefix
textcommands: { prefix: result.prefix, enabled: false }
};
return settings;
}
@ -287,14 +287,15 @@ class SettingsMigrator {
};
if (linkfilter) settings.linkfilter = {
enabled: result.linkfilter?.enabled || false,
silent: result.linkfilter?.silent || false,
ignore: result.linkfilter?.channels || [],
bypass: result.linkfilter?.roles || [],
enabled: linkfilter.enabled || false,
silent: linkfilter.silent || false,
ignore: linkfilter.channels || [],
bypass: linkfilter.roles || [],
actions: [],
whitelist: result.linkfilter?.whitelist === true ? result.linkfilter.filter : [],
blacklist: result.linkfilter?.whitelist === false ? result.linkfilter.filter : [],
greylist: []
whitelist: linkfilter.whitelist === true ? linkfilter.filter : [],
blacklist: linkfilter.whitelist === false ? linkfilter.filter : [],
greylist: [],
whitelistMode: linkfilter.whitelist || false
};
if (ignore) settings.ignore = {
@ -334,7 +335,7 @@ class SettingsMigrator {
};
if (autorole) settings.autorole = autorole;
if (prefix) settings.prefix = prefix;
if (prefix) settings.textcommands = { prefix, enabled: false };
if (welcomer) {
settings.welcomer.enabled = welcomer.enabled;
settings.welcomer.message = welcomer.message || null;
@ -398,7 +399,8 @@ class SettingsMigrator {
silent: false,
bypass: result.invitefilter.roles,
enabled: result.invitefilter.enabled,
actions: []
actions: [],
whitelist: []
};
const channels = Object.entries(invitefilter.channels || {});
for (const [id, value] of channels) {