diff options
| -rw-r--r-- | cogs/math_commands.py | 58 |
1 files changed, 53 insertions, 5 deletions
diff --git a/cogs/math_commands.py b/cogs/math_commands.py index bf164b3..e30c92b 100644 --- a/cogs/math_commands.py +++ b/cogs/math_commands.py | |||
| @@ -5,12 +5,45 @@ | |||
| 5 | 5 | ||
| 6 | import logging | 6 | import logging |
| 7 | import asyncio | 7 | import asyncio |
| 8 | import re | ||
| 8 | 9 | ||
| 9 | from discord import ApplicationContext | 10 | from discord import ApplicationContext |
| 10 | from discord.ext import commands | 11 | from discord.ext import commands |
| 11 | 12 | ||
| 12 | logger = logging.getLogger(__name__) | 13 | logger = logging.getLogger(__name__) |
| 13 | 14 | ||
| 15 | MATH_SANITIZE_PATTERN = re.compile(r'^[\d\s\+\-\*\/\^\%\(\)\.\,\=\\<\>\!\|\&\~\?\:\'\"\[\]\{\}a-zA-Z]+$') | ||
| 16 | |||
| 17 | |||
| 18 | def sanitize_math_input(equation: str) -> str | None: | ||
| 19 | if not equation or len(equation) > 1000: | ||
| 20 | return None | ||
| 21 | equation = equation.strip() | ||
| 22 | if not equation: | ||
| 23 | return None | ||
| 24 | if not MATH_SANITIZE_PATTERN.match(equation): | ||
| 25 | return None | ||
| 26 | for char in equation: | ||
| 27 | if ord(char) < 32 or ord(char) > 126: | ||
| 28 | return None | ||
| 29 | if equation.count('(') != equation.count(')'): | ||
| 30 | return None | ||
| 31 | if equation.count('[') != equation.count(']'): | ||
| 32 | return None | ||
| 33 | if equation.count('{') != equation.count('}'): | ||
| 34 | return None | ||
| 35 | dangerous_patterns = [ | ||
| 36 | r'eval', r'exec', r'__import__', r'os\.', r'sys\.', | ||
| 37 | r'subprocess', r'open(', r'file', r'shutil', | ||
| 38 | r'lambda:', r'compile', r'dir(', r'vars(', | ||
| 39 | r'\|\|', r'&&', r';', r'`', r'\$', r'\\n', r'\\r', | ||
| 40 | r'\x', r'\0', r'\a', r'\b', r'\f', r'\v', | ||
| 41 | ] | ||
| 42 | for pattern in dangerous_patterns: | ||
| 43 | if re.search(pattern, equation, re.IGNORECASE): | ||
| 44 | return None | ||
| 45 | return equation | ||
| 46 | |||
| 14 | 47 | ||
| 15 | class Math(commands.Cog): | 48 | class Math(commands.Cog): |
| 16 | def __init__(self, bot): | 49 | def __init__(self, bot): |
| @@ -21,13 +54,28 @@ class Math(commands.Cog): | |||
| 21 | description="wrug szyskih matfisuf" | 54 | description="wrug szyskih matfisuf" |
| 22 | ) | 55 | ) |
| 23 | async def math(self, ctx: ApplicationContext, equation: str): | 56 | async def math(self, ctx: ApplicationContext, equation: str): |
| 57 | sanitized = sanitize_math_input(equation) | ||
| 58 | if sanitized is None: | ||
| 59 | await ctx.respond("Invalid equation. Only basic math expressions allowed.", ephemeral=True) | ||
| 60 | logger.warning(f"Math command rejected invalid input: {equation}") | ||
| 61 | return | ||
| 24 | try: | 62 | try: |
| 25 | math = await asyncio.create_subprocess_exec(f"qalc -e '{equation}'") | 63 | math_proc = await asyncio.create_subprocess_exec( |
| 26 | result = await math.wait() | 64 | "qalc", "-e", sanitized, |
| 27 | await ctx.respond(f"{equation} = {result.decode('utf-8').strip()}") | 65 | stdout=asyncio.subprocess.PIPE, |
| 28 | logger.info(f"Math command executed: {equation}") | 66 | stderr=asyncio.subprocess.PIPE |
| 67 | ) | ||
| 68 | stdout, stderr = await math_proc.communicate() | ||
| 69 | if math_proc.returncode != 0: | ||
| 70 | error_output = stderr.decode('utf-8', errors='replace').strip() | ||
| 71 | await ctx.respond(f"Calculation error: {error_output}", ephemeral=True) | ||
| 72 | logger.error(f"qalc error: {error_output}") | ||
| 73 | return | ||
| 74 | result = stdout.decode('utf-8', errors='replace').strip() | ||
| 75 | await ctx.respond(f"{sanitized} = {result}") | ||
| 76 | logger.info(f"Math command executed: {sanitized}") | ||
| 29 | except Exception as e: | 77 | except Exception as e: |
| 30 | await ctx.respond(e, ephemeral=True) | 78 | await ctx.respond(f"Error: {str(e)}", ephemeral=True) |
| 31 | logger.error(f"Error in math command: {e}", exc_info=True) | 79 | logger.error(f"Error in math command: {e}", exc_info=True) |
| 32 | 80 | ||
| 33 | 81 | ||
