feat(inbox): .xlsx extraction — formula gate, hidden sheets, cell comments (stage 2h)
This commit is contained in:
parent
a46a96db0a
commit
ca26e117ea
3 changed files with 132 additions and 1 deletions
|
|
@ -422,3 +422,87 @@ def test_clean_pptx_grouped_shape_admits(tmp_path):
|
|||
fp = _pptx_with_grouped_text(tmp_path, "clean grouped text")
|
||||
_extracted, _result, verdict = receive([fp])
|
||||
assert verdict == "ADMIT"
|
||||
|
||||
|
||||
# --- slice 2h: .xlsx (openpyxl) ---------------------------------------------
|
||||
# Three planted regions (PLAN §247). A formula-lead cell (=cmd|'…', =HYPERLINK)
|
||||
# is a spreadsheet threat the guard would not recognize, so the front-end refuses
|
||||
# it (mirrors .csv). A hidden sheet and a cell comment hide injection text where a
|
||||
# human reading the workbook does not look; the extractor surfaces both so the
|
||||
# stage-2 scan catches them. openpyxl reads formulas as their string, iterates
|
||||
# hidden sheets, and exposes cell comments (verified empirically).
|
||||
|
||||
|
||||
def _make_xlsx(tmp_path, name="book.xlsx", *, cell="A normal value",
|
||||
formula=None, hidden_sheet=None, comment=None):
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.comments import Comment
|
||||
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws["A1"] = cell
|
||||
if formula is not None:
|
||||
ws["A2"] = formula # leading '=' -> stored as a formula
|
||||
if comment is not None:
|
||||
ws["A1"].comment = Comment(comment, "m")
|
||||
if hidden_sheet is not None:
|
||||
hs = wb.create_sheet("secret")
|
||||
hs.sheet_state = "hidden" # invisible tab in Excel
|
||||
hs["A1"] = hidden_sheet
|
||||
fp = tmp_path / name
|
||||
wb.save(str(fp))
|
||||
return fp
|
||||
|
||||
|
||||
def test_xlsx_formula_injection_cell_is_flagged(tmp_path):
|
||||
fp = _make_xlsx(tmp_path, name="data.xlsx", formula="=cmd|'/c calc'!A1")
|
||||
extracted, _result, verdict = receive([fp])
|
||||
assert extracted.provenance[0].source_type == "xlsx"
|
||||
assert any("data.xlsx" in n for n, _r in extracted.rejected)
|
||||
assert verdict == "REJECT"
|
||||
|
||||
|
||||
def test_xlsx_hyperlink_formula_is_flagged(tmp_path):
|
||||
fp = _make_xlsx(tmp_path, formula="=HYPERLINK('http://evil')")
|
||||
extracted, _result, verdict = receive([fp])
|
||||
assert extracted.rejected != ()
|
||||
assert verdict == "REJECT"
|
||||
|
||||
|
||||
def test_xlsx_hidden_sheet_injection_is_caught(tmp_path):
|
||||
fp = _make_xlsx(tmp_path, hidden_sheet=_INJECTION)
|
||||
_extracted, result, verdict = receive([fp])
|
||||
assert result.disposition is Disposition.FAIL_SECURE # caught by the guard
|
||||
assert verdict == "REJECT"
|
||||
|
||||
|
||||
def test_xlsx_cell_comment_injection_is_caught(tmp_path):
|
||||
fp = _make_xlsx(tmp_path, comment=_INJECTION)
|
||||
_extracted, result, verdict = receive([fp])
|
||||
assert result.disposition is Disposition.FAIL_SECURE
|
||||
assert verdict == "REJECT"
|
||||
|
||||
|
||||
def test_clean_xlsx_admits(tmp_path):
|
||||
fp = _make_xlsx(tmp_path, name="clean.xlsx", cell="A routine value. No behavior change.")
|
||||
extracted, _result, verdict = receive([fp])
|
||||
assert "uploads/clean.md" in extracted.bundle
|
||||
assert extracted.rejected == ()
|
||||
assert verdict == "ADMIT"
|
||||
|
||||
|
||||
def test_xlsx_formula_detach_proof(tmp_path):
|
||||
# The same workbook with a plain cell (no formula lead) has no flag -> ADMIT,
|
||||
# so the flag is the formula content, not the .xlsx suffix or the filename.
|
||||
fp = _make_xlsx(tmp_path, name="data.xlsx", formula="calc")
|
||||
extracted, _result, verdict = receive([fp])
|
||||
assert extracted.rejected == ()
|
||||
assert verdict == "ADMIT"
|
||||
|
||||
|
||||
def test_xlsx_hidden_sheet_detach_proof(tmp_path):
|
||||
# Same visible cell, no hidden sheet -> ADMIT: it is the extractor surfacing the
|
||||
# hidden-sheet region that caught it, not merely "an xlsx".
|
||||
fp = _make_xlsx(tmp_path, cell="A routine value.")
|
||||
_extracted, _result, verdict = receive([fp])
|
||||
assert verdict == "ADMIT"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue