fix: injection protections and concurrency improvements

This commit is contained in:
Alex committed 2026-06-29 19:56:10 +02:00
1 parent b144265094
commit 99484e02c8
12 files changed
+452 -84

No files matched your search

@@ -153,6 +153,35 @@ def test_renderer_keeps_injection_text_as_literal_content():
assert prs.slides[0].shapes.title.text == payload
def test_spreadsheet_renderer_neutralizes_formula_injection():
# Spec content is model / prompt-injection controlled. A string cell starting
# with a formula trigger must be stored as TEXT (data_type 's'), never as a
# live formula; genuine numbers stay numeric.
from openpyxl import load_workbook
spec = {
"sheets": [
{
"name": "S",
"rows": [
['=HYPERLINK("http://evil","x")', "+1+1", "@cmd", "-danger"],
["safe text", -5, 3.14],
],
}
]
}
wb = load_workbook(_render_in_process("spreadsheet", spec))
ws = wb["S"]
# None of the trigger cells are formulas; each is a quote-prefixed string.
for coord in ("A1", "B1", "C1", "D1"):
assert ws[coord].data_type == "s", coord
assert str(ws[coord].value).startswith("'")
# Benign text is unchanged; numbers stay numeric (no spurious quoting).
assert ws["A2"].value == "safe text" and ws["A2"].data_type == "s"
assert ws["B2"].value == -5 and ws["B2"].data_type == "n"
assert ws["C2"].value == 3.14 and ws["C2"].data_type == "n"
def test_pdf_renderer_escapes_markup_and_does_not_execute():
payload = "<b>not bold</b> & \"</para>\" '''os.system('x')'''"
spec = {"title": payload, "blocks": [{"type": "paragraph", "text": payload}]}
+22 -2
View File
@@ -34,8 +34,12 @@ class _FakeFile:
def __init__(self, data: bytes) -> None:
self._data = data
def read(self) -> bytes:
return self._data
def read(self, size: int = -1) -> bytes:
# Mirror BinaryIO.read(n): the bridge does a bounded read to cap memory.
return self._data if size is None or size < 0 else self._data[:size]
def close(self) -> None:
pass
class _FakeStorage:
@@ -224,6 +228,22 @@ def test_bridge_missing_upload_path_errors(monkeypatch):
bridge_attachment(att, user_id=USER, conversation_id=CONV)
@pytest.mark.unit
def test_bridge_rejects_oversize_attachment_before_reading(monkeypatch):
# An oversize attachment is rejected via its authoritative ``size`` column,
# BEFORE the bytes are buffered into worker memory (a memory-DoS guard).
from application.core.settings import settings
_FakeArtifactsRepo.bridged = {}
storage, calls = _patch_bridge(monkeypatch)
att = _attachment()
att["size"] = int(settings.ARTIFACT_MAX_BYTES) + 1
with pytest.raises(AttachmentBridgeError, match="size limit"):
bridge_attachment(att, user_id=USER, conversation_id=CONV)
assert calls == [] # never persisted
assert storage.requested == [] # never even read the bytes
# ---------------------------------------------------------------------------
# code_executor wiring: fallback fires, stages bytes, succeeds
# ---------------------------------------------------------------------------