Add phishing validation and Phishlist

This commit is contained in:
Zeva Rose 2022-10-04 12:44:39 -06:00
parent b7e0381b8a
commit 1a73d6bbf3
2 changed files with 109 additions and 53 deletions

View file

@ -1,6 +1,6 @@
"""JARVIS component event mixin.""" """JARVIS component event mixin."""
from jarvis_core.db import q from jarvis_core.db import q
from jarvis_core.db.models import Action, Modlog, Note, Reminder, Star from jarvis_core.db.models import Action, Modlog, Note, Phishlist, Reminder, Star
from naff import listen from naff import listen
from naff.api.events.internal import Button from naff.api.events.internal import Button
from naff.models.discord.embed import EmbedField from naff.models.discord.embed import EmbedField
@ -136,9 +136,40 @@ class ComponentEventMixin:
await context.send("Reminder copied!", ephemeral=True) await context.send("Reminder copied!", ephemeral=True)
async def _handle_phishlist_button(self, event: Button) -> None:
context = event.context
if not context.custom_id.startswith("pl|"):
return
if not context.deferred and not context.responded:
await context.defer(ephemeral=True)
_, valid, id_ = context.custom_id.split("|")
valid = valid == "valid"
pl = await Phishlist.find_one(q(_id=id_))
if not pl:
self.logger.warn(f"Phishlist {id_} does not exist!")
return
pl.valid = valid
pl.confirmed = True
await pl.commit()
for row in context.message.components:
for component in row.components:
component.disabled = True
embed = context.message.embeds[0]
embed.add_field(name="Valid", value="Yes" if valid else "No")
await context.message.edit(components=context.message.components, embeds=embed)
await context.send("Confirmed! Thank you for confirming this URL.")
@listen() @listen()
async def on_button(self, event: Button) -> None: async def on_button(self, event: Button) -> None:
"""Process button events.""" """Process button events."""
await self._handle_modcase_button(event) await self._handle_modcase_button(event)
await self._handle_delete_button(event) await self._handle_delete_button(event)
await self._handle_copy_button(event) await self._handle_copy_button(event)
await self._handle_phishlist_button(event)

View file

@ -7,8 +7,8 @@ from jarvis_core.db import q
from jarvis_core.db.models import ( from jarvis_core.db.models import (
Autopurge, Autopurge,
Autoreact, Autoreact,
Filter,
Mute, Mute,
Phishlist,
Roleping, Roleping,
Setting, Setting,
Warning, Warning,
@ -18,15 +18,16 @@ from naff import listen
from naff.api.events.discord import MessageCreate, MessageDelete, MessageUpdate from naff.api.events.discord import MessageCreate, MessageDelete, MessageUpdate
from naff.client.utils.misc_utils import find_all from naff.client.utils.misc_utils import find_all
from naff.models.discord.channel import DMChannel, GuildText from naff.models.discord.channel import DMChannel, GuildText
from naff.models.discord.components import ActionRow, Button
from naff.models.discord.embed import EmbedField from naff.models.discord.embed import EmbedField
from naff.models.discord.enums import Permissions from naff.models.discord.enums import ButtonStyles, Permissions
from naff.models.discord.message import Message from naff.models.discord.message import Message
from naff.models.discord.user import Member from naff.models.discord.user import Member
from jarvis.branding import get_command_color from jarvis.branding import get_command_color
from jarvis.embeds.admin import warning_embed
from jarvis.tracking import malicious_tracker, warnings_tracker from jarvis.tracking import malicious_tracker, warnings_tracker
from jarvis.utils import build_embed from jarvis.utils import build_embed
from jarvis.utils.embeds import warning_embed
class MessageEventMixin: class MessageEventMixin:
@ -83,6 +84,10 @@ class MessageEventMixin:
] ]
if (m := match.group(1)) not in allowed and setting.value: if (m := match.group(1)) not in allowed and setting.value:
self.logger.debug(f"Removing non-allowed invite `{m}` from {message.guild.id}") self.logger.debug(f"Removing non-allowed invite `{m}` from {message.guild.id}")
try:
await message.delete()
except Exception:
self.logger.debug("Message deleted before action taken")
expires_at = datetime.now(tz=timezone.utc) + timedelta(hours=24) expires_at = datetime.now(tz=timezone.utc) + timedelta(hours=24)
await Warning( await Warning(
@ -96,49 +101,12 @@ class MessageEventMixin:
).commit() ).commit()
tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name) tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name)
tracker.inc() tracker.inc()
embed = warning_embed(message.author, "Sent an invite link") embed = warning_embed(message.author, "Sent an invite link", self.user)
try: try:
await message.reply(embeds=embed) await message.channel.send(embeds=embed)
except Exception: except Exception:
self.logger.warn("Failed to send warning embed") self.logger.warn("Failed to send warning embed")
try:
await message.delete()
except Exception:
self.logger.debug("Message deleted before action taken")
async def filters(self, message: Message) -> None:
"""Handle filter evennts."""
filters = await Filter.find(q(guild=message.guild.id)).to_list(None)
for item in filters:
for f in item.filters:
if re.search(f, message.content, re.IGNORECASE):
expires_at = datetime.now(tz=timezone.utc) + timedelta(hours=24)
await Warning(
active=True,
admin=self.user.id,
duration=24,
expires_at=expires_at,
guild=message.guild.id,
reason="Sent a message with a filtered word",
user=message.author.id,
).commit()
tracker = warnings_tracker.labels(
guild_id=message.guild.id, guild_name=message.guild.name
)
tracker.inc()
embed = warning_embed(message.author, "Sent a message with a filtered word")
try:
await message.reply(embeds=embed)
except Exception:
self.logger.warn("Failed to send warning embed")
try:
await message.delete()
except Exception:
self.logger.debug("Message deleted before action taken")
return
async def massmention(self, message: Message) -> None: async def massmention(self, message: Message) -> None:
"""Handle massmention events.""" """Handle massmention events."""
massmention = await Setting.find_one( massmention = await Setting.find_one(
@ -168,9 +136,9 @@ class MessageEventMixin:
).commit() ).commit()
tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name) tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name)
tracker.inc() tracker.inc()
embed = warning_embed(message.author, "Mass Mention") embed = warning_embed(message.author, "Mass Mention", self.user)
try: try:
await message.reply(embeds=embed) await message.channel.send(embeds=embed)
except Exception: except Exception:
self.logger.warn("Failed to send warning embed") self.logger.warn("Failed to send warning embed")
@ -232,9 +200,11 @@ class MessageEventMixin:
).commit() ).commit()
tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name) tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name)
tracker.inc() tracker.inc()
embed = warning_embed(message.author, "Pinged a blocked role/user with a blocked role") embed = warning_embed(
message.author, "Pinged a blocked role/user with a blocked role", self.user
)
try: try:
await message.reply(embeds=embed) await message.channel.send(embeds=embed)
except Exception: except Exception:
self.logger.warn("Failed to send warning embed") self.logger.warn("Failed to send warning embed")
@ -242,6 +212,9 @@ class MessageEventMixin:
"""Check if the message contains any known phishing domains.""" """Check if the message contains any known phishing domains."""
for match in url.finditer(message.content): for match in url.finditer(message.content):
if (m := match.group("domain")) in self.phishing_domains: if (m := match.group("domain")) in self.phishing_domains:
pl = await Phishlist.find_one(q(url=m))
if pl and pl.confirmed and not pl.valid:
return False
self.logger.debug( self.logger.debug(
f"Phishing url `{m}` detected in {message.guild.id}/{message.channel.id}/{message.id}" f"Phishing url `{m}` detected in {message.guild.id}/{message.channel.id}/{message.id}"
) )
@ -257,9 +230,9 @@ class MessageEventMixin:
).commit() ).commit()
tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name) tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name)
tracker.inc() tracker.inc()
embed = warning_embed(message.author, "Phishing URL") embed = warning_embed(message.author, "Phishing URL", self.user)
try: try:
await message.reply(embeds=embed) await message.channel.send(embeds=embed)
except Exception: except Exception:
self.logger.warn("Failed to send warning embed") self.logger.warn("Failed to send warning embed")
try: try:
@ -268,12 +241,41 @@ class MessageEventMixin:
self.logger.warn("Failed to delete malicious message") self.logger.warn("Failed to delete malicious message")
tracker = malicious_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name) tracker = malicious_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name)
tracker.inc() tracker.inc()
if not pl or not pl.confirmed:
if not pl:
pl = Phishlist(url=m)
await pl.commit()
embed = build_embed(
title="Phishing URL detected",
description="Please confirm that this is valid",
fields=[EmbedField(name="URL", value=m)],
)
valid_button = Button(
style=ButtonStyles.GREEN, emoji="✔️", custom_id=f"pl|valid|{pl.id}"
)
invalid_button = Button(
style=ButtonStyles.RED, emoji="✖️", custom_id=f"pl|invalid|{pl.id}"
)
channel = await self.fetch_channel(1026918337554423868)
components = [ActionRow(invalid_button, valid_button)]
await channel.send(embeds=embed, components=components)
return True return True
return False return False
async def malicious_url(self, message: Message) -> None: async def malicious_url(self, message: Message) -> None:
"""Check if the message contains any known phishing domains.""" """Check if the message contains any known phishing domains."""
for match in url.finditer(message.content): for match in url.finditer(message.content):
m = match.group("domain")
pl = await Phishlist.find_one(q(url=m))
if pl and pl.confirmed and not pl.valid:
return False
async with ClientSession() as session: async with ClientSession() as session:
resp = await session.post( resp = await session.post(
"https://anti-fish.bitflow.dev/check", "https://anti-fish.bitflow.dev/check",
@ -303,9 +305,9 @@ class MessageEventMixin:
tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name) tracker = warnings_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name)
tracker.inc() tracker.inc()
reasons = ", ".join(f"{m['source']}: {m['type']}" for m in data["matches"]) reasons = ", ".join(f"{m['source']}: {m['type']}" for m in data["matches"])
embed = warning_embed(message.author, reasons) embed = warning_embed(message.author, reasons, self.user)
try: try:
await message.reply(embeds=embed) await message.channel.send(embeds=embed)
except Exception: except Exception:
self.logger.warn("Failed to send warning embed") self.logger.warn("Failed to send warning embed")
try: try:
@ -314,6 +316,31 @@ class MessageEventMixin:
self.logger.warn("Failed to delete malicious message") self.logger.warn("Failed to delete malicious message")
tracker = malicious_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name) tracker = malicious_tracker.labels(guild_id=message.guild.id, guild_name=message.guild.name)
tracker.inc() tracker.inc()
if not pl or not pl.confirmed:
if not pl:
pl = Phishlist(url=m)
await pl.commit()
embed = build_embed(
title="Malicious URL detected",
description="Please confirm that this is valid",
fields=[EmbedField(name="URL", value=m)],
)
valid_button = Button(
style=ButtonStyles.GREEN, emoji="✔️", custom_id=f"pl|valid|{pl.id}"
)
invalid_button = Button(
style=ButtonStyles.RED, emoji="✖️", custom_id=f"pl|invalid|{pl.id}"
)
channel = await self.fetch_channel(1026918337554423868)
components = [ActionRow(invalid_button, valid_button)]
await channel.send(embeds=embed, components=components)
return True return True
return False return False
@ -362,7 +389,6 @@ class MessageEventMixin:
malicious = await self.malicious_url(message) malicious = await self.malicious_url(message)
if phish or malicious: if phish or malicious:
await self.timeout_user(message.author, message.channel) await self.timeout_user(message.author, message.channel)
await self.filters(message)
@listen() @listen()
async def on_message_edit(self, event: MessageUpdate) -> None: async def on_message_edit(self, event: MessageUpdate) -> None:
@ -418,7 +444,6 @@ class MessageEventMixin:
malicious = await self.malicious_url(after) malicious = await self.malicious_url(after)
if phish or malicious: if phish or malicious:
await self.timeout_user(after.author, after.channel) await self.timeout_user(after.author, after.channel)
await self.filters(after)
@listen() @listen()
async def on_message_delete(self, event: MessageDelete) -> None: async def on_message_delete(self, event: MessageDelete) -> None: