diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 946c2fcfde2c..afff36f95dca 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -7,3 +7,8 @@ **Vulnerability:** SQL Injection via string-interpolated subqueries with LIMIT. The `query` function in `optional-skills/mcp/fastmcp/templates/database_server.py` wrapped user SQL in a subquery: `SELECT * FROM ({sql}) LIMIT N`. This allowed malicious users to bypass simple checks (e.g. ensuring it starts with SELECT) and inject additional clauses or statements by manipulating the closing parenthesis. **Learning:** SQLite does not natively support parameterization for the FROM clause (e.g., subqueries or table names). Attempting to string-interpolate user input into a subquery creates an injection vector, especially when trying to enforce a LIMIT clause on user-provided queries. **Prevention:** To prevent SQL injection when applying limits to user-provided SQL queries, execute the raw user query directly and restrict the output rows in Python using `cursor.fetchmany(limit)` instead of trying to wrap the query in another SELECT with a LIMIT clause. + +## 2024-05-26 - Security Enhancement: XXE Prevention +**Vulnerability:** XML External Entity (XXE) vulnerability via `xml.etree.ElementTree`. +**Learning:** `xml.etree.ElementTree` is vulnerable to XML External Entity (XXE) attacks when parsing untrusted or external XML data. This vulnerability was found in scripts making external requests (e.g. `skills/research/arxiv/scripts/search_arxiv.py`, `optional-skills/devops/watchers/scripts/watch_rss.py`) and receiving callback requests (`gateway/platforms/wecom_callback.py`). +**Prevention:** Always use `defusedxml.ElementTree` (or `defusedxml.minidom`) when parsing XML data from untrusted sources to mitigate XXE and billion laughs attacks. diff --git a/gateway/platforms/wecom_callback.py b/gateway/platforms/wecom_callback.py index 139c67fe7c1e..ae00e2a14368 100644 --- a/gateway/platforms/wecom_callback.py +++ b/gateway/platforms/wecom_callback.py @@ -17,7 +17,7 @@ import socket as _socket import time from typing import Any, Dict, List, Optional -from xml.etree import ElementTree as ET +import defusedxml.ElementTree as ET try: from aiohttp import web diff --git a/optional-skills/devops/watchers/scripts/watch_rss.py b/optional-skills/devops/watchers/scripts/watch_rss.py index cc729f91b139..d4fec764033a 100755 --- a/optional-skills/devops/watchers/scripts/watch_rss.py +++ b/optional-skills/devops/watchers/scripts/watch_rss.py @@ -19,7 +19,7 @@ import urllib.error import urllib.request from pathlib import Path -from xml.etree import ElementTree as ET +import defusedxml.ElementTree as ET sys.path.insert(0, str(Path(__file__).parent)) from _watermark import Watermark, format_items_as_markdown # type: ignore diff --git a/skills/research/arxiv/scripts/search_arxiv.py b/skills/research/arxiv/scripts/search_arxiv.py index 9acd8b97ec9a..1418ed136492 100644 --- a/skills/research/arxiv/scripts/search_arxiv.py +++ b/skills/research/arxiv/scripts/search_arxiv.py @@ -13,7 +13,7 @@ import sys import urllib.request import urllib.parse -import xml.etree.ElementTree as ET +import defusedxml.ElementTree as ET NS = {'a': 'http://www.w3.org/2005/Atom'}