From 7d7c5a0c5f5c89409e2b4e4f693783f3b5256752 Mon Sep 17 00:00:00 2001 From: Joscha Date: Wed, 15 May 2019 06:25:54 +0000 Subject: [PATCH] Make bot less vulnerable The bot still stores any number of explanations for a given word, and prints all of them when using !wtf detail . This means that it is still vulnerable to some types of attacks. Maybe I'll fix that sometime in the future. --- wtf.py | 65 ++++++++++++++++++++++++++++++++++++++++++++------------ wtfdb.py | 10 +++++---- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/wtf.py b/wtf.py index 1e6bde5..37c4e10 100644 --- a/wtf.py +++ b/wtf.py @@ -12,6 +12,11 @@ else: logger = logging.getLogger(__name__) class Wtf(yaboli.Module): + MAX_TERMS = 5 + MAX_TERM_LENGTH = 1024 + MAX_EXPLANATIONS = 15 + MAX_EXPLANATION_LENGTH = 1024 + DESCRIPTION = ("a database of explanations for words, acronyms and" " initialisms") HELP_GENERAL = DESCRIPTION @@ -53,49 +58,75 @@ class Wtf(yaboli.Module): self.register_general("wtf", self.cmd_wtf) - @staticmethod - def _format_explanations(explanations, detail=False): + def _format_explanations(self, explanations, detail=False): # Id, Term, Explanation, Author if detail: - return [f"{i}: {t} — {e} (by {a})" for i, t, e, a in explanations] + explanations = [f"{i}: {t} — {e} (by {a})" for i, t, e, a in explanations] else: - return [f"{t} — {e}" for _, t, e, _ in explanations] + explanations = [f"{t} — {e}" for _, t, e, _ in explanations] + + if len(explanations) > self.MAX_EXPLANATIONS: + message = ("Some explanations were omitted because this bot only" + f" displays {self.MAX_EXPLANATIONS} explanations per" + " term.") + explanations = explanations[:-1] + [message] + + return explanations async def _find_explanations(self, terms, detail=False): lines = [] for term in terms: - explanations = await self.db.find_full(term) + explanations = await self.db.find_full(term, self.MAX_EXPLANATIONS + 1) if explanations: lines.extend(self._format_explanations(explanations, detail=detail)) else: lines.append(f"{term!r} not found.") return lines + async def send_explanations(self, message, termstr): + terms = [term for term in termstr.split() if term] + terms = terms[:self.MAX_TERMS] + + if not terms: return + + if max(map(len, terms)) > self.MAX_TERM_LENGTH: + await message.reply(("A term can be at most" + f" {self.MAX_TERM_LENGTH} characters long.")) + return + + lines = await self._find_explanations(terms) + await message.reply("\n".join(lines)) + return + async def on_send(self, room, message): await super().on_send(room, message) match = self.RE_WTF_IS.fullmatch(message.content) if match: terms = match.group(1) - terms = [term for term in terms.split() if term] - if not terms: return - lines = await self._find_explanations(terms) - await message.reply("\n".join(lines)) + await self.send_explanations(message, terms) async def cmd_wtf(self, room, message, args): match_is = self.RE_IS.fullmatch(args.raw) if match_is: terms = match_is.group(1) - terms = [term for term in terms.split() if term] - if not terms: return - lines = await self._find_explanations(terms) - await message.reply("\n".join(lines)) - return + await self.send_explanations(message, terms) match_add = self.RE_ADD.fullmatch(args.raw) if match_add: term = match_add.group(1) explanation = match_add.group(2).strip() + + if len(term) > self.MAX_TERM_LENGTH: + await message.reply(("A term can be at most" + f" {self.MAX_TERM_LENGTH} characters long.")) + return + + if len(explanation) > self.MAX_EXPLANATION_LENGTH: + await message.reply(("An explanation can be at most" + f" {self.MAX_EXPLANATION_LENGTH} characters long.")) + return + await self.db.add(term, explanation, message.sender.nick) logger.info((f"{message.sender.atmention} added explanation:" f" {term} - {explanation}")) @@ -124,6 +155,12 @@ class Wtf(yaboli.Module): if match_replace: aid = match_replace.group(1) explanation = match_replace.group(2).strip() + + if len(explanation) > self.MAX_EXPLANATION_LENGTH: + await message.reply(("An explanation can be at most" + f" {self.MAX_EXPLANATION_LENGTH} characters long.")) + return + term = await self.db.get(aid) if term is None: await message.reply(f"No explanation with id {aid} exists.") diff --git a/wtfdb.py b/wtfdb.py index 1bc57d5..81a7169 100644 --- a/wtfdb.py +++ b/wtfdb.py @@ -31,21 +31,23 @@ class WtfDB(yaboli.Database): """, (acronym, explanation, author)) @yaboli.operation - def find(self, db, acronym): + def find(self, db, acronym, limit): c = db.execute(""" SELECT acronym, explanation FROM acronyms WHERE NOT deleted AND p_lower(acronym) = ? ORDER BY acronym_id ASC - """, (acronym.lower(),)) + LIMIT ? + """, (acronym.lower(), limit)) return c.fetchall() @yaboli.operation - def find_full(self, db, acronym): + def find_full(self, db, acronym, limit): c = db.execute(""" SELECT acronym_id, acronym, explanation, author FROM acronyms WHERE NOT deleted AND p_lower(acronym) = ? ORDER BY acronym_id ASC - """, (acronym.lower(),)) + LIMIT ? + """, (acronym.lower(), limit)) return c.fetchall() @yaboli.operation