Release note · August 3, 2026
A Word save transform is a review boundary
A document can carry instructions for what to do when it is saved as a single XML file. That stored configuration matters in a review even when ordinary document text, the Settings XML anchor, and the rest of the package stay unchanged. It does not, however, make a static reviewer an XSLT host.
DocFence 0.28 adds a private-by-default inventory for that narrow Word configuration: w:useXSLTWhenSaving, w:saveThroughXslt, its standard external transform relationship, and an optional local solution identifier. It gives CI a durable stored-state signal without following a target or executing a transform.
The setting is real; the runtime is deliberately out of scope
Microsoft’s Open XML SDK documentation for SaveThroughXslt describes a custom XSL transform used when saving a document as a single XML file. Its UseXsltWhenSaving companion says that the transform is not applied unless the setting is enabled. The OOXML Document Settings relationship contract gives the corresponding transform relationship an external target mode.
word/settings.xml
w:useXSLTWhenSaving explicit enabled or disabled setting
w:saveThroughXslt transform anchor (relationship or local solution ID)
word/_rels/settings.xml.rels
transform relationship external stored target
DocFence inventories the stored package topology. It does not open Word, invoke a save operation, fetch a transform, parse its source, execute it, or predict the XML a client would emit. Those are different operations with materially different trust and execution boundaries.
Useful aggregate evidence, no operational disclosure
The public profile reports five aggregate counts: enabled settings, disabled settings, transform anchors, standard transform relationships, and local solution identifiers. JSON, Markdown, and SARIF intentionally omit transform targets, relationship IDs, local solution values, Settings-part paths, and private fingerprints.
That distinction preserves useful review behavior. A same-count target or solution-ID rewrite changes the private semantic inventory and is visible in a diff; a relationship-ID renumbering with unchanged semantics remains quiet. Orphaned standard transform relationships are also retained as stored evidence rather than silently discarded.
Two readable policy choices
A clean handoff can reject any persisted XSLT-on-save configuration:
rules:
require_no_save_through_xslt: true
That candidate-state gate emits DFP069. A controlled template that permits approved configuration can instead protect its baseline:
rules:
no_save_through_xslt_changes: true
DFP070 flags a material private-inventory change. Neither rule classifies a transform as safe or unsafe; they make stored configuration reviewable in a policy a team can inspect.
Validation that keeps the boundary honest
The 54-test release suite covers enabled and disabled settings, external transform relationships, local-solution-only anchors, orphan relationships, strict and transitional namespace forms, malformed relationship and anchor rejection, same-count rewrites, relationship-ID stability, privacy redaction, and JSON/Markdown/SARIF output. Hosted CI passed for both the release commit and tag, and fresh wheel and source-distribution installs passed the package smoke checks.
The included Document Change Assurance Benchmark 0.18.0 integration demonstrates the boundary with a pair that keeps the enabled Settings markup and relationship ID fixed while changing only a synthetic external transform target. The adapter consumes aggregate evidence only.
python -m pip install https://github.com/SybilGambleyyu/docfence/releases/download/v0.28.0/docfence-0.28.0-py3-none-any.whl
docfence profile candidate.docx --format markdown
docfence check approved.docx candidate.docx --policy docfence.yml --format sarif --output docfence.sarif
The public release includes the wheel and source archive. The tagged policy reference, threat model, and validation notes define the exact evidence contract and its limits.