///|
let spreadsheet_ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
///|
let drawing_ns = "http://schemas.openxmlformats.org/drawingml/2006/main"
///|
let supported_relationship_types : Array[String] = [
office_rel + "officeDocument",
office_rel + "worksheet",
office_rel + "styles",
office_rel + "theme",
office_rel + "sharedStrings",
office_rel + "extended-properties",
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
vba_rel,
]
///|
let supported_content_types : Array[String] = [
xlsm_ct, xlsx_ct, vba_ct, "application/vnd.openxmlformats-package.relationships+xml",
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
"application/vnd.openxmlformats-officedocument.theme+xml", "application/vnd.openxmlformats-package.core-properties+xml",
"application/vnd.openxmlformats-officedocument.extended-properties+xml",
]
///|
fn validate_profile(pkg : Package) -> Unit raise {
if is_word_format(pkg.audit.format) {
validate_word_profile(pkg)
return
}
let report = pkg.audit
guard pkg.graph.graph.coverage_gaps.length() == 0 else {
raise Refused("graph coverage gap: " + pkg.graph.graph.coverage_gaps[0])
}
for r in report.relationships {
guard !r.external && supported_relationship_types.contains(r.kind) else {
raise Refused("external or unsupported relationship capability")
}
let target = report.parts[pkg.graph.part_index[r.resolved]]
let expected = if r.kind == office_rel + "officeDocument" {
target.content_type == xlsx_ct || target.content_type == xlsm_ct
} else if r.kind == vba_rel {
target.content_type == vba_ct
} else if r.kind ==
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" {
target.content_type ==
"application/vnd.openxmlformats-package.core-properties+xml"
} else {
let suffix = r.kind[office_rel.length():].to_owned()
let wanted = if suffix == "extended-properties" {
"application/vnd.openxmlformats-officedocument.extended-properties+xml"
} else {
"application/vnd.openxmlformats-officedocument." +
(if suffix == "theme" { "theme" } else { "spreadsheetml." + suffix }) +
"+xml"
}
target.content_type == wanted
}
guard expected else {
raise Refused("relationship/content-type evidence conflict")
}
guard (if r.kind == vba_rel ||
r.kind == office_rel + "worksheet" ||
r.kind == office_rel + "styles" ||
r.kind == office_rel + "theme" ||
r.kind == office_rel + "sharedStrings" {
r.source == report.main_part
} else {
r.source == "/"
}) else {
raise Refused("unsupported relationship source")
}
}
guard report.findings.length() <= 1 else {
raise Refused("multiple VBA parts unsupported")
}
for f in report.findings {
guard report.format == "XLSM" else {
raise Refused("VBA in non-macro main type")
}
let incoming = graph_incoming(pkg.graph, f.part)
guard incoming.length() == 1 &&
incoming[0].kind == vba_rel &&
incoming[0].source == report.main_part else {
raise Refused("orphan/shared VBA or evidence conflict")
}
guard pkg.archive.get(relationship_part(f.part)[:]) is None else {
raise Refused("VBA companion relationship parts unsupported")
}
guard get_part(pkg.archive, f.part).has_prefix(
b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1",
) else {
raise Refused("VBA part is not a CFB container")
}
}
for p in report.parts {
validate_static_xml_part(pkg, p)
}
}
///|
fn validate_static_xml_part(pkg : Package, p : PartRecord) -> Unit raise {
let report = pkg.audit
if p.name == "[Content_Types].xml" {
return
}
guard supported_content_types.contains(p.content_type) else {
raise Refused("unknown/unsupported part content type")
}
if p.content_type == vba_ct {
return
}
if p.name.has_suffix(".rels") {
guard p.content_type ==
"application/vnd.openxmlformats-package.relationships+xml" else {
raise Refused("relationship part content type mismatch")
}
return
}
guard pkg.xml_nodes.get(p.name) is Some(nodes) else {
raise Refused("XML content type on unsupported payload name")
}
let root = nodes[0].element.name.local_name
let expected_root = if p.name == report.main_part {
"workbook"
} else if p.content_type.contains(".worksheet+") {
"worksheet"
} else if p.content_type.contains(".styles+") {
"styleSheet"
} else if p.content_type.contains(".sharedStrings+") {
"sst"
} else if p.content_type.contains(".theme+") {
"theme"
} else if p.content_type.contains("core-properties") {
"coreProperties"
} else {
"Properties"
}
guard root == expected_root else {
raise Refused("part root/content type mismatch")
}
let parents : Array[String] = []
let sheet_refs : Array[String] = []
let sheet_names : Array[String] = []
for node in nodes {
let ns = node.element.name.namespace_uri.unwrap_or("")
let local_name = node.element.name.local_name
while parents.length() > node.depth {
ignore(parents.pop())
}
let parent = if parents.length() > 0 {
parents[parents.length() - 1]
} else {
""
}
if expected_root == "workbook" {
let shape_ok = match local_name {
"workbook" => node.depth == 0
"fileVersion" | "workbookPr" | "bookViews" | "sheets" | "calcPr" =>
parent == "workbook"
"workbookView" => parent == "bookViews"
"sheet" => parent == "sheets"
_ => false
}
guard shape_ok else {
raise Refused("unsupported workbook element placement")
}
if local_name == "sheet" {
let refs = node.element.attributes.filter(fn(a) {
a.name.namespace_uri ==
Some(office_rel[0:office_rel.length() - 1].to_owned()) &&
a.name.local_name == "id"
})
guard refs.length() == 1 && !sheet_refs.contains(refs[0].value) else {
raise Refused("missing or reused worksheet XML relationship ID")
}
sheet_refs.push(refs[0].value)
let sheet_name = attr(node, "name")
guard sheet_name.length() > 0 &&
sheet_name.length() <= 31 &&
!sheet_names.contains(sheet_name.to_lower()) else {
raise Refused("invalid or duplicate sheet name")
}
sheet_names.push(sheet_name.to_lower())
ignore(attr(node, "sheetId"))
}
} else if expected_root == "worksheet" {
let shape_ok = match local_name {
"worksheet" => node.depth == 0
"sheetPr"
| "dimension"
| "sheetViews"
| "sheetFormatPr"
| "cols"
| "sheetData"
| "pageMargins"
| "pageSetup"
| "printOptions"
| "mergeCells" => parent == "worksheet"
"pageSetUpPr" | "outlinePr" => parent == "sheetPr"
"sheetView" => parent == "sheetViews"
"selection" => parent == "sheetView"
"col" => parent == "cols"
"row" => parent == "sheetData"
"c" => parent == "row"
"v" | "is" => parent == "c"
"t" => parent == "is" || parent == "r"
"r" => parent == "is"
"rPr" => parent == "r"
"mergeCell" => parent == "mergeCells"
_ => false
}
guard shape_ok else {
raise Refused("unsupported worksheet element placement")
}
}
parents.push(local_name)
let permitted = if expected_root == "workbook" {
ns == spreadsheet_ns &&
[
"workbook", "fileVersion", "workbookPr", "bookViews", "workbookView", "sheets",
"sheet", "calcPr",
].contains(local_name)
} else if expected_root == "worksheet" {
ns == spreadsheet_ns &&
[
"worksheet", "sheetPr", "pageSetUpPr", "outlinePr", "dimension", "sheetViews",
"sheetView", "selection", "sheetFormatPr", "cols", "col", "sheetData", "row",
"c", "v", "is", "t", "r", "rPr", "pageMargins", "pageSetup", "printOptions",
"mergeCells", "mergeCell",
].contains(local_name)
} else if expected_root == "sst" {
ns == spreadsheet_ns &&
[
"sst", "si", "t", "r", "rPr", "b", "i", "u", "sz", "color", "rFont", "family",
"scheme", "vertAlign",
].contains(local_name)
} else if expected_root == "styleSheet" {
ns == spreadsheet_ns &&
[
"styleSheet", "numFmts", "numFmt", "fonts", "font", "b", "i", "u", "sz",
"color", "name", "family", "scheme", "vertAlign", "fills", "fill", "patternFill",
"fgColor", "bgColor", "borders", "border", "left", "right", "top", "bottom",
"diagonal", "cellStyleXfs", "cellXfs", "xf", "alignment", "protection", "cellStyles",
"cellStyle", "dxfs", "tableStyles",
].contains(local_name)
} else if expected_root == "theme" {
ns == drawing_ns &&
[
"theme", "themeElements", "clrScheme", "dk1", "lt1", "dk2", "lt2", "accent1",
"accent2", "accent3", "accent4", "accent5", "accent6", "hlink", "folHlink",
"sysClr", "srgbClr", "fontScheme", "majorFont", "minorFont", "latin", "ea",
"cs", "font", "fmtScheme", "fillStyleLst", "solidFill", "schemeClr", "gradFill",
"gsLst", "gs", "tint", "satMod", "shade", "lin", "path", "fillToRect", "lnStyleLst",
"ln", "prstDash", "miter", "effectStyleLst", "effectStyle", "effectLst",
"outerShdw", "alpha", "bgFillStyleLst", "objectDefaults", "extraClrSchemeLst",
"scene3d", "camera", "lightRig", "rot", "sp3d", "bevelT",
].contains(local_name)
} else if expected_root == "coreProperties" {
[
"http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
"http://purl.org/dc/elements/1.1/", "http://purl.org/dc/terms/",
].contains(ns) &&
[
"coreProperties", "title", "subject", "creator", "keywords", "description",
"lastModifiedBy", "revision", "created", "modified", "category", "contentStatus",
"lastPrinted", "version", "identifier", "language",
].contains(local_name)
} else {
[
"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes",
].contains(ns) &&
[
"Properties", "Application", "AppVersion", "DocSecurity", "ScaleCrop", "HeadingPairs",
"TitlesOfParts", "Company", "LinksUpToDate", "SharedDoc", "HyperlinksChanged",
"vector", "variant", "lpstr", "i4",
].contains(local_name)
}
let permitted = permitted ||
(
is_word_format(report.format) &&
expected_root == "Properties" &&
ns ==
"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" &&
[
"Template", "TotalTime", "Pages", "Words", "Characters", "Lines", "Paragraphs",
"CharactersWithSpaces",
].contains(local_name)
)
if is_word_format(report.format) && expected_root == "Properties" {
guard ns ==
"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" &&
(if local_name == "Properties" {
node.depth == 0
} else {
node.depth == 1
}) else {
raise Refused("unsupported Word document-property placement")
}
if local_name == "Template" {
guard node.text_fragments.join("").trim() == "Normal.dotm" else {
raise Refused("non-default Word template property unsupported")
}
}
}
guard permitted else {
raise Refused("unsupported XML namespace or feature: " + local_name)
}
for a in node.element.attributes {
let ans = a.name.namespace_uri.unwrap_or("")
guard ans == "" ||
ans == "http://www.w3.org/XML/1998/namespace" ||
ans == office_rel[0:office_rel.length() - 1].to_owned() ||
(
expected_root == "coreProperties" &&
ans == "http://www.w3.org/2001/XMLSchema-instance"
) else {
raise Refused("unsupported namespaced XML attribute")
}
if ans == office_rel[0:office_rel.length() - 1].to_owned() {
guard expected_root == "workbook" &&
local_name == "sheet" &&
a.name.local_name == "id" else {
raise Refused("unsupported source XML relationship reference")
}
guard graph_relationship(pkg.graph, p.name, a.value) is Some(match_rel) &&
match_rel.kind == office_rel + "worksheet" else {
raise Refused("dangling or wrongly-bound source XML reference")
}
}
}
}
if expected_root == "workbook" {
guard sheet_refs.length() > 0 else {
raise Refused("workbook has no supported sheet")
}
for relation in report.relationships {
if relation.source == p.name && relation.kind == office_rel + "worksheet" {
guard sheet_refs.contains(relation.id) else {
raise Refused("unreferenced worksheet relationship")
}
}
}
}
}