///|
let bibliography_ns = "http://schemas.openxmlformats.org/officeDocument/2006/bibliography"
///|
let custom_properties_ns = "http://schemas.openxmlformats.org/officeDocument/2006/customXml"
///|
let custom_properties_ct = "application/vnd.openxmlformats-officedocument.customXmlProperties+xml"
///|
fn word_custom_relationship(
r : Relationship,
parts : Array[PartRecord],
nodes : Map[String, Array[Node]],
) -> Bool {
if r.external {
return false
}
if r.kind == office_rel + "customXml" {
parts.any(fn(p) {
p.name == r.source && [docx_ct, docm_ct].contains(p.content_type)
}) &&
parts.any(fn(p) {
p.name == r.resolved && p.content_type == "application/xml"
}) &&
nodes
.get(r.resolved)
.map(fn(ns) {
ns.length() > 0 &&
ns[0].element.name.namespace_uri == Some(bibliography_ns) &&
ns[0].element.name.local_name == "Sources"
})
.unwrap_or(false)
} else if r.kind == office_rel + "customXmlProps" {
parts.any(fn(p) {
p.name == r.source && p.content_type == "application/xml"
}) &&
parts.any(fn(p) {
p.name == r.resolved && p.content_type == custom_properties_ct
}) &&
nodes
.get(r.source)
.map(fn(ns) {
ns.length() > 0 &&
ns[0].element.name.namespace_uri == Some(bibliography_ns) &&
ns[0].element.name.local_name == "Sources"
})
.unwrap_or(false)
} else {
false
}
}
///|
// Empty built-in APA bibliography metadata only. No general custom XML,
// transformation, stylesheet loading, data binding or schema retrieval.
fn validate_word_bibliography(pkg : Package) -> Map[String, Bool] raise {
let owned : Map[String, Bool] = Map([])
let ids : Map[String, Bool] = Map([])
let data_relations = pkg.audit.relationships.filter(fn(r) {
r.kind == office_rel + "customXml"
})
guard data_relations.length() <= 32 else {
raise Refused("bibliography item limit")
}
for r in data_relations {
pkg.control.checkpoint("word-bibliography-item", 1)
guard r.source == pkg.audit.main_part &&
word_custom_relationship(r, pkg.audit.parts, pkg.xml_nodes) &&
graph_incoming(pkg.graph, r.resolved).length() == 1 &&
!owned.contains(r.resolved) else {
raise Invalid("ambiguous bibliography data ownership")
}
let nodes = pkg.xml_nodes[r.resolved]
guard nodes.length() == 1 &&
empty_node(nodes[0]) &&
exact_node_attributes(nodes[0], None, [
"SelectedStyle", "StyleName", "URI",
]) &&
attr(nodes[0], "SelectedStyle") == "\\APA.XSL" &&
attr(nodes[0], "StyleName") == "APA" &&
attr_opt(nodes[0], "URI")
.map(fn(v) {
v == "http://schemas.openxmlformats.org/bibliographicStyle/APA"
})
.unwrap_or(true) else {
raise Refused("unsupported bibliography sources or style")
}
let outgoing = pkg.audit.relationships.filter(fn(edge) {
edge.source == r.resolved
})
guard outgoing.length() == 1 &&
outgoing[0].kind == office_rel + "customXmlProps" &&
word_custom_relationship(outgoing[0], pkg.audit.parts, pkg.xml_nodes) else {
raise Refused("bibliography properties ownership missing or unsupported")
}
let properties = outgoing[0].resolved
guard graph_incoming(pkg.graph, properties).length() == 1 &&
!owned.contains(properties) &&
!pkg.audit.relationships.any(fn(edge) { edge.source == properties }) else {
raise Invalid("shared or nested bibliography properties")
}
let props = pkg.xml_nodes[properties]
guard props.length() == 3 &&
props[0].depth == 0 &&
props[0].element.name.namespace_uri == Some(custom_properties_ns) &&
props[0].element.name.local_name == "datastoreItem" &&
exact_node_attributes(props[0], Some(custom_properties_ns), ["itemID"]) &&
props[0].element.attributes.length() == 1 &&
props[1].depth == 1 &&
props[1].element.name.namespace_uri == Some(custom_properties_ns) &&
props[1].element.name.local_name == "schemaRefs" &&
props[1].element.attributes.length() == 0 &&
props[2].depth == 2 &&
props[2].element.name.namespace_uri == Some(custom_properties_ns) &&
props[2].element.name.local_name == "schemaRef" &&
exact_node_attributes(props[2], Some(custom_properties_ns), ["uri"]) &&
props[2].element.attributes.length() == 1 &&
props[2].element.attributes[0].value == bibliography_ns &&
props.all(empty_node) else {
raise Refused("unsupported bibliography property schema")
}
let id = props[0].element.attributes[0].value.to_lower()
guard valid_guid(id) && !ids.contains(id) else {
raise Invalid("invalid or duplicate bibliography datastore ID")
}
ids[id] = true
owned[r.resolved] = true
owned[properties] = true
}
for r in pkg.audit.relationships {
if r.kind == office_rel + "customXmlProps" {
guard owned.contains(r.source) && owned.contains(r.resolved) else {
raise Invalid("orphan bibliography properties relation")
}
}
}
for p in pkg.audit.parts {
if p.name != "[Content_Types].xml" &&
["application/xml", custom_properties_ct].contains(p.content_type) {
guard owned.contains(p.name) else {
raise Refused("unmodeled or orphan custom XML part")
}
}
}
owned
}