Design doc (read in full first): docs/design/docx_io_package_split.md — §2, §4 Slice 2.
Slice 2 of 5 of the docx_io.py package split. One function, no state — extracting it early unblocks every later slice from re-deciding where it lives.
Scope
Move _safe_fromstring() (docx_io.py:18) into marcut/docx/xml_utils.py. This is the lxml parser configured with resolve_entities=False (XXE hardening) used by nearly every other section of the module.
Re-export it from docx_io.py so existing references (incl. tests/test_security.py:4 — from marcut.docx_io import _safe_fromstring) keep working.
Non-negotiable requirements
- XXE hardening must be preserved exactly.
resolve_entities=False (and any other hardening flags on the parser) must survive the move unchanged. tests/test_security.py's external-entity payload test must still pass — the external entity must not resolve.
- Use the package name agreed in Slice 1 (
#72).
Acceptance criteria
Verification loop (design doc §3.3)
PYTHONPATH=src/python python3 -m pytest tests/test_docx_io.py tests/test_metadata_scrubbing.py tests/test_pipeline.py tests/test_url_redaction.py tests/test_failure_scenarios.py tests/test_security.py -q.
- Golden-file harness — empty diff.
marcut redact against a sample-files/ fixture, diffed vs baseline.
Plus the standard gate: full pytest -q, swift build, swift test, bash scripts/release_preflight.sh.
Dependencies
Blocked by #70. Independent of the other slices. Follow the package-naming decision from #72.
Land as its own PR; do not combine with other issues in this set. Do not reopen design-spike #25.
Design doc (read in full first):
docs/design/docx_io_package_split.md— §2, §4 Slice 2.Slice 2 of 5 of the
docx_io.pypackage split. One function, no state — extracting it early unblocks every later slice from re-deciding where it lives.Scope
Move
_safe_fromstring()(docx_io.py:18) intomarcut/docx/xml_utils.py. This is thelxmlparser configured withresolve_entities=False(XXE hardening) used by nearly every other section of the module.Re-export it from
docx_io.pyso existing references (incl.tests/test_security.py:4—from marcut.docx_io import _safe_fromstring) keep working.Non-negotiable requirements
resolve_entities=False(and any other hardening flags on the parser) must survive the move unchanged.tests/test_security.py's external-entity payload test must still pass — the external entity must not resolve.#72).Acceptance criteria
_safe_fromstring()moved tomarcut/docx/xml_utils.py, re-exported fromdocx_io.py.tests/test_security.pypasses unchanged (or with only an import-path edit) — XXE payload still does not resolve.#70) diffs to zero.marcut redactrun diffed against pre-slice baseline → no difference.Verification loop (design doc §3.3)
PYTHONPATH=src/python python3 -m pytest tests/test_docx_io.py tests/test_metadata_scrubbing.py tests/test_pipeline.py tests/test_url_redaction.py tests/test_failure_scenarios.py tests/test_security.py -q.marcut redactagainst asample-files/fixture, diffed vs baseline.Plus the standard gate: full
pytest -q,swift build,swift test,bash scripts/release_preflight.sh.Dependencies
Blocked by
#70. Independent of the other slices. Follow the package-naming decision from#72.Land as its own PR; do not combine with other issues in this set. Do not reopen design-spike #25.