Release note · August 4, 2026

One relationship part, one transform

By SybilGambleyyu · DocFence 0.43.0

A relationship transform is not unique merely because it is unique inside one XMLDSIG Reference. Generic XMLDSIG can bind several Manifest objects, and those Manifests can each point at the same relationships part. OPC says a producer must not specify more than one relationship transform for a particular relationships part; a consumer must treat more than one as an error.

Correction, August 4, 2026: 0.43 enforced relationship-transform cardinality but did not constrain transform lists on direct non-relationship package-part references. DocFence 0.44 now credits those parts only when the list is absent or a bounded C14N-only form. The 0.43 parser did not claim XMLDSIG validity, but this was a separate structural declaration gap.

Further correction, August 4, 2026: the cross-object topology discussed below is not OPC's package-specific signature topology. DocFence 0.46 requires the one idPackageObject with its exact Manifest/SignatureProperties children and one SignedInfo binding, so it no longer combines declarations across arbitrary objects. The within-Manifest duplicate-transform condition remains part of the bounded audit.

A signature-level rule

The OPC relationships-transform definition gives a relationship transform its selectors, canonicalization successor, and cardinality rule. The selector can name a relationship ID or a relationship type, but duplicating the transform against the same relationships part does not create a second valid declaration.

Pre-0.46 generic XMLDSIG topology
  ├─ bound Manifest A → word relationships part → relationship transform
  └─ bound Manifest B → same word relationships part → relationship transform

Result: unsupported, not two coverage declarations

0.46: rejected before coverage as non-OPC package topology

The two transforms could select the same relationship, two different relationships, or different type groups. That distinction is intentionally not exposed in a DocFence report. The standards constraint comes first: there may be only one transform for that relationships part in that signature.

What DocFence 0.43 does

Before 0.46, DocFence gathered transform-bearing references from every bound package Manifest before resolving selectors. When more than one relationship transform named the same relationships part, every transform-bearing declaration for that part became an aggregate unsupported reference and none of its selectors contributed coverage. Version 0.46 retains that check inside its one qualifying package-specific Manifest.

That is deliberately fail-closed but still privacy-preserving. Public output can show the aggregate unsupported-reference and uncovered-relationship effects; it does not reveal the relationships-part path, object identifiers, selector values, relationship IDs or types, reference URIs, digest material, certificate data, or private semantic fingerprints.

Static coverage still is not signature validation

This release adds one structural condition to the static declaration boundary. It does not recompute transformations or digest values, validate XMLDSIG, validate a certificate chain, establish signer identity, check revocation or timestamps, or decide whether an Office client trusts the package.

version: 1
rules:
  require_complete_package_signature_coverage: true
  no_package_signature_coverage_changes: true

DFP092 and DFP093 remain review controls for a bounded declaration surface, not cryptographic assertions.

Tested across Manifest objects

The 0.43 regression suite exercised duplicate transforms both within one Manifest and across two separate bound Manifest objects in the same XML signature. In 0.46, the latter is rejected as non-OPC package topology before coverage; the former remains aggregate-only unsupported evidence. The full 67-test suite passed, as did DCAB’s optional DocFence adapter across its 42 deterministic fixtures, including the existing standards-shaped relationship-type reassignment pair.

The tagged source passed hosted CI on Python 3.11 and 3.13 plus its distribution build. Fresh wheel installation exercised the DCAB package-signature fixture, and the public GitHub wheel and source distribution were downloaded and byte-compared against the verified local builds.

python -m pip install https://github.com/SybilGambleyyu/docfence/releases/download/v0.43.0/docfence-0.43.0-py3-none-any.whl

docfence check approved.docx candidate.docx --policy docfence.yml --format sarif --output docfence.sarif

The tagged policy reference, threat model, and validation notes describe the exact boundary. A useful static signal is specific about the declaration it can credit—and equally specific about the malformed structures it cannot.