Release note · August 1, 2026
The Word picture hyperlink hiding in legacy VML
A Word package can contain a picture-shaped piece of legacy VML with two different relationship attributes. Treating every relationship near that picture as an image link—or every external relationship as a hyperlink—makes document review noisier and less trustworthy.
DocFence 0.23 adds a narrow, privacy-safe inventory for the second surface: a direct legacy v:imagedata/@r:href marker in a supported Word story. It is stored-package evidence for CI review, not a renderer, link resolver, or network monitor.
One VML element, separate relationship roles
The Open XML SDK documents v:imagedata and names its r:id property the explicit relationship to image data, while its r:href property is the explicit relationship to a hyperlink target. Those are distinct stored attributes. They should not be collapsed into a generic external-relationship total, a VML shape href, a w:hyperlink element, a HYPERLINK field, or a DrawingML link.
Real legacy XML carries the form: a public Word XML fragment shows v:imagedata r:id="…" r:href="…" inside a VML shape. That is enough to justify a direct markup boundary, but not enough to predict client behavior.
Classify what is stored; do not make the relationship look tidier than it is
DocFence records every direct v:imagedata/@r:href marker in body, header, footer, footnote, endnote, comment, and glossary stories. Duplicate markers and markers in Markup Compatibility branches remain separate stored markers; DocFence does not select a branch or decide which picture, if any, a client renders.
When a marker resolves to a standard hyperlink relationship, DocFence classifies the stored target mode as external or internal. Every other resolved relationship type or mode is preserved as unsupported evidence rather than silently relabeled a conventional hyperlink. That distinction matters: a public document-processing report shows an r:href marker tied to an external standard image relationship. It demonstrates why review tooling must preserve the odd case without claiming a Word client will follow it.
The inventory deliberately excludes the image-data r:id, r:pict, raw src, and o:relid attributes. Those are separate legacy surfaces, not fallback spellings for the hyperlink marker. An unused relationship also produces no marker count.
Private semantics, useful gates
Reports expose only aggregate marker/story counts plus external, internal, and unsupported relationship-classification counts. Targets, relationship IDs, VML markup, story paths, and fingerprints remain private. The internal signature tracks only reviewed r:href relationship semantics: a same-count target rewrite is review-visible, while unchanged-semantics relationship-ID renumbering and changes to excluded image-data attributes are quiet for this inventory.
For a handoff that must contain no stored marker:
rules:
require_no_word_vml_image_hyperlinks: true
This produces DFP059. A controlled document that intentionally carries approved markers can instead protect its private baseline:
rules:
no_word_vml_image_hyperlink_changes: true
DFP060 detects a changed marker inventory. These rules add precise markup context to a broader relationship review; they do not replace it.
Evidence, and the boundary it does not cross
The 49-test release suite covers standard external/internal hyperlink relationships, an unsupported external image relationship, duplicate markers, body/header stories, Transitional and Strict namespaces, unavailable relationship failure, orphaned relationship exclusion, excluded attributes, same-count target changes, relationship-ID renumbering, privacy redaction, and JSON/Markdown/SARIF policy output.
Release validation combines that controlled package with the public real XML fragment above. The fragment is not presented as a downloadable whole-document fixture, so the release does not overclaim an end-to-end public-package smoke test for this exact marker. Hosted CI passed on Python 3.11 and 3.13 for the release commit and tag. The wheel and source archive were independently rebuilt from the tagged source and matched byte-for-byte; fresh public downloads passed checksum and metadata verification, and an isolated installed wheel reproduced the controlled marker counts.
DocFence does not resolve, retrieve, follow, validate, evaluate, render, or execute a target. It does not establish that a client will honor a marker, that a relationship is safe or reachable, or that a visual picture is actually associated with it. Use it as a bounded, local review signal alongside an appropriate rendering and domain review process.
python -m pip install https://github.com/SybilGambleyyu/docfence/releases/download/v0.23.0/docfence-0.23.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, source archive, and SHA-256 manifest. The tagged policy reference, threat model, and validation notes define the exact behavior and limits.