Add Phase 9 validation and advisory review

Record the finite dependency search, Wi-Fi maintenance blocker, and
pinned
icon provenance. Add bounded host orchestration and fixture coverage,
and
update release documentation with current evidence.
This commit is contained in:
2026-09-16 16:26:46 +02:00
parent 51f835c22f
commit cdc4d4a8df
31 changed files with 1748 additions and 45 deletions
+22 -5
View File
@@ -1,7 +1,7 @@
# Release notice tool tests
Run `python3 tests/release_notices/run.py` from the repository root. Uses Python's
standard library and isolated temporary fixtures only; no managed package,
standard library, isolated temporary fixtures and checked-in icon evidence; no managed package,
SDK, toolchain, PlatformIO, network, or device is required. Linux/POSIX path and
descriptor semantics match the notice tool.
@@ -10,8 +10,25 @@ across moved roots/changed mtimes; missing, empty, changed and oversized inputs;
source body drift outside excerpts; bounds/schema; traversal and symlinks in
input/output ancestry; FIFOs/directories; existing user-data preservation;
explicit output requirement; unlisted secret/config/build exclusion; incomplete
write behavior; and success/failure CLI exits.
write behavior; and success/failure CLI exits. Additional offline icon tests check
exact upstream SHA-256/Git blob identities, release and author metadata, absence
of NOTICE paths in the complete pinned tree, actual retained SVG coordinates,
USB mockup transforms, manual firmware row bytes, and catalog inclusion. A
changed coordinate or added transform is rejected; differing mockup Wi-Fi and
unproven manual rasterization are not described as exact upstream matches.
Real installed-input assembly and recipient delivery are separate checks; see
`docs/release_packaging.md`. Passing these tests is not license clearance or
proof of corresponding-source compliance.
Real installed-input assembly is an explicit, separate offline check:
```sh
python3 tests/release_notices/actual_bundle.py \
--sdk-root /home/mscholz/.platformio/packages/framework-espidf \
--toolchain-root /home/mscholz/.platformio/packages/toolchain-xtensa-esp-elf
```
Use literal paths for the reviewed installed snapshot. This invokes the real
CLI twice with fresh temporary outputs outside input roots, verifies catalog
identity, every manifest entry and payload hash/size, absence of extra files,
and identical relative names/bytes across outputs. Temporary outputs are removed
after the check. No fetch, dependency installation, build or device access.
Recipient delivery remains separate; see `docs/release_packaging.md`. Passing
these tests is not license clearance or corresponding-source compliance.
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-only
"""Assemble two real bundles in temporary directories and verify every byte."""
import argparse
import hashlib
import json
from pathlib import Path
import subprocess
import sys
import tempfile
PROJECT = Path(__file__).absolute().parents[2]
def contents(root):
return {p.relative_to(root).as_posix(): p.read_bytes()
for p in root.rglob("*") if p.is_file()}
def validate(files, catalog_data):
manifest = json.loads(files["manifest.json"])
catalog = json.loads(catalog_data)
assert manifest["catalog_sha256"] == hashlib.sha256(catalog_data).hexdigest()
assert manifest["snapshot"] == catalog["snapshot"]
assert len(manifest["inputs"]) == len(catalog["inputs"])
expected_entries = {(x["root"], x["path"]): x for x in catalog["inputs"]}
expected_files = {"manifest.json"}
seen = set()
for entry in manifest["inputs"]:
key = (entry["root"], entry["path"])
assert key not in seen
seen.add(key)
original = expected_entries[key]
assert {k: entry[k] for k in original} == original
path = "inputs/" + entry["root"] + "/" + entry["path"]
if entry["range"] is not None:
path += ".notice.txt"
assert entry["output"] == path
expected_files.add(path)
data = files[path]
assert len(data) == entry["output_size"]
assert hashlib.sha256(data).hexdigest() == entry["output_sha256"]
assert seen == set(expected_entries)
assert len(manifest["generated"]) == 1
assert manifest["generated"][0]["path"] == "README.txt"
for entry in manifest["generated"]:
expected_files.add(entry["path"])
data = files[entry["path"]]
assert len(data) == entry["size"]
assert hashlib.sha256(data).hexdigest() == entry["sha256"]
assert set(files) == expected_files, "unexpected or missing bundle files"
return len(manifest["inputs"])
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--sdk-root", type=Path, required=True)
parser.add_argument("--toolchain-root", type=Path, required=True)
args = parser.parse_args()
catalog = (PROJECT / "third_party/release-notices/inputs.json").read_bytes()
with tempfile.TemporaryDirectory(prefix="actual-release-notices-") as temporary:
base = Path(temporary)
outputs = []
for name in ("first", "second"):
output = base / name
subprocess.run([
sys.executable, str(PROJECT / "tools/release_notices.py"),
"--sdk-root", str(args.sdk_root),
"--toolchain-root", str(args.toolchain_root), "--output", str(output),
], check=True, timeout=60)
files = contents(output)
count = validate(files, catalog)
outputs.append(files)
assert outputs[0] == outputs[1], "bundle names/bytes differ"
files = outputs[0]
print(f"PASS: two actual bundles; {count} inputs; {len(files)} files; "
f"{sum(map(len, files.values()))} bytes; all payloads verified and deterministic")
print("manifest SHA-256:", hashlib.sha256(files["manifest.json"]).hexdigest())
return 0
if __name__ == "__main__":
sys.exit(main())
+173
View File
@@ -0,0 +1,173 @@
# SPDX-License-Identifier: GPL-3.0-only
"""Offline checks of actual project icon derivatives, not a raster generator."""
import copy
import hashlib
import json
from pathlib import Path
import re
import unittest
import xml.etree.ElementTree as ET
PROJECT = Path(__file__).absolute().parents[2]
ICONS = PROJECT / "third_party/material-design-icons"
UPSTREAM = ICONS / "upstream-7.4.47"
COMMIT = "9e04201d4557e729822fb57f62a316c3dea1d4a8"
TAG = "5edde266e281d26a03dcfa89fb651183cbab0f2e"
SVG = "{http://www.w3.org/2000/svg}"
PINS = {
"usb.svg": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22",
"wifi-strength-4.svg": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f",
"LICENSE": "f3bc8715bad84b26396bb42d2abd11f919cf58163be158eab79ecbaabf84cdf2",
"package.json": "ffeb0e4cc17b4cb124408bd0068667b9a35e985971792978e40d4cd69c7ca8b7",
"README.md": "db5acca5eb4e2c9113c3408ed1ae7d17c76f653340a5d4e3f8a58d2bcdf50b7d",
"meta.json": "ed9d7c7a5ca3ce7dee023fb6c1d62715dbec5b27503d268cbc10b9c8894ef3e8",
"tag.json": "f02dc7fc1e862c344c66877828b1914543f0656db8544c05a1ed3b7d990b6d75",
"tree.json": "b75710c2b9ee4d0ad3f79016e9767a85103f16df6cbbe5f0629efc3b470d1569",
"Apache-2.0.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
}
def load(name):
return json.loads((UPSTREAM / name).read_bytes())
def geometry(element):
"""Strict comparison: no coordinate normalization or hidden transforms."""
if set(element.attrib) != {"id", "viewBox"}:
raise ValueError("unexpected root/symbol geometry attributes")
if element.attrib["viewBox"] != "0 0 24 24":
raise ValueError("unexpected viewBox")
if len(element) != 1 or element[0].tag != SVG + "path":
raise ValueError("expected exactly one path")
if set(element[0].attrib) != {"d"}:
raise ValueError("unexpected path geometry attributes")
return element[0].attrib["d"]
class IconProvenanceTests(unittest.TestCase):
def test_download_pins_and_git_blobs(self):
records = load("downloads.json")
self.assertEqual({r["path"] for r in records}, set(PINS))
self.assertEqual(len(records), len(PINS))
tree = {x["path"]: x for x in load("tree.json")["tree"]}
for record in records:
name = record["path"]
with self.subTest(name=name):
data = (UPSTREAM / name).read_bytes()
self.assertEqual(len(data), record["size"])
self.assertEqual(hashlib.sha256(data).hexdigest(), PINS[name])
self.assertEqual(record["sha256"], PINS[name])
if name not in ("tag.json", "tree.json", "Apache-2.0.txt"):
path = "svg/" + name if name.endswith(".svg") else name
self.assertEqual(record["url"],
"https://raw.githubusercontent.com/Templarian/"
f"MaterialDesign-SVG/{COMMIT}/{path}")
self.assertEqual(tree[path]["size"], len(data))
blob = b"blob " + str(len(data)).encode() + b"\0" + data
self.assertEqual(hashlib.sha1(blob).hexdigest(), tree[path]["sha"])
for name in ("usb.svg", "wifi-strength-4.svg", "LICENSE"):
self.assertEqual((ICONS / name).read_bytes(), (UPSTREAM / name).read_bytes())
def test_release_license_authors_and_notice_scope(self):
tag = load("tag.json")
self.assertEqual(tag["sha"], TAG)
self.assertEqual(tag["tag"], "v7.4.47")
self.assertEqual(tag["object"]["sha"], COMMIT)
self.assertEqual(tag["object"]["type"], "commit")
self.assertFalse(tag["verification"]["verified"])
package = load("package.json")
self.assertEqual((package["name"], package["version"], package["license"]),
("@mdi/svg", "7.4.47", "Apache-2.0"))
expected = {
"usb": ("Google", "25033E0B-3AD4-414D-9972-559F2690FC1D", "1.5.54"),
"wifi-strength-4": ("Simran", "41B86B22-7245-4A97-9BAA-3E9EBD44CEB0", "2.3.50"),
}
selected = [x for x in load("meta.json") if x["name"] in expected]
self.assertEqual(len(selected), 2)
for icon in selected:
self.assertEqual((icon["author"], icon["id"], icon["version"]), expected[icon["name"]])
self.assertNotIn("license", icon)
tree = load("tree.json")
self.assertFalse(tree["truncated"])
self.assertFalse([x["path"] for x in tree["tree"] if "notice" in x["path"].lower()])
def test_exact_retained_geometry_and_negative_changes(self):
for name in ("usb.svg", "wifi-strength-4.svg"):
original = ET.parse(UPSTREAM / name).getroot()
local = ET.parse(ICONS / name).getroot()
self.assertEqual(geometry(local), geometry(original))
changed = copy.deepcopy(local)
changed[0].set("d", changed[0].get("d").replace("M", "M0,0L", 1))
self.assertNotEqual(geometry(changed), geometry(original))
for node in (0, None):
changed = copy.deepcopy(local)
(changed if node is None else changed[node]).set("transform", "translate(1 0)")
with self.assertRaises(ValueError):
geometry(changed)
def test_actual_mockup_paths_and_transforms(self):
root = ET.parse(PROJECT / "docs/phase7c_icon_mockup.svg").getroot()
parents = {child: parent for parent in root.iter() for child in parent}
symbols = {x.get("id"): x for x in root.iter(SVG + "symbol")}
self.assertEqual(geometry(symbols["usb"]), geometry(ET.parse(UPSTREAM / "usb.svg").getroot()))
self.assertNotEqual(geometry(symbols["wifi"]),
geometry(ET.parse(UPSTREAM / "wifi-strength-4.svg").getroot()))
uses = [x for x in root.iter(SVG + "use") if x.get("href") == "#usb"]
self.assertEqual([x.attrib for x in uses], [
{"href": "#usb", "x": "150", "y": "4", "width": "30", "height": "30"},
{"href": "#usb", "x": "8", "y": "72", "width": "30", "height": "30"},
])
def ancestry(node):
result = []
while node in parents:
node = parents[node]
if "transform" in node.attrib:
result.append(node.get("transform"))
# Nested SVG viewports would require additional mappings.
if node is not root:
self.assertNotEqual(node.tag, SVG + "svg")
return result
self.assertEqual(ancestry(uses[0]), [])
self.assertEqual(parents[parents[uses[0]]].get("id"), "status")
self.assertEqual(ancestry(uses[1]), ["translate(62 438)"])
status_uses = [x for x in root.iter(SVG + "use") if x.get("href") == "#status"]
self.assertEqual([x.attrib for x in status_uses], [{"href": "#status"}] * 4)
self.assertEqual([ancestry(x) for x in status_uses], [
["translate(62 98)"], ["translate(592 98)"],
["translate(62 438)"], ["translate(592 438)"],
])
def test_actual_manual_masks_not_inferred_rasterization(self):
source = (PROJECT / "src/local_status_ui.c").read_text()
self.assertIn("hand-rasterized 8x8 derivative", source)
self.assertIn('adaptations of "wifi-strength-4"', source)
masks = {
"usb": "18 3c 18 5a 3e 18 3c 18",
"wifi_full": "7e 81 3c 42 18 24 00 18",
"wifi_three": "00 00 3c 42 18 24 00 18",
"wifi_two": "00 00 00 00 18 24 00 18",
"wifi_one": "00 00 00 00 00 00 00 18",
}
for name, expected in masks.items():
match = re.search(r"s_icon_" + name + r"\[LOCAL_STATUS_UI_ICON_SIZE\]\s*=\s*\{([^}]+)\}", source)
self.assertIsNotNone(match)
actual = re.findall(r"0x([0-9a-fA-F]{2})U", match[1])
self.assertEqual(bytes.fromhex(" ".join(actual)), bytes.fromhex(expected))
def test_all_icon_evidence_is_pinned_in_catalog(self):
catalog = json.loads((PROJECT / "third_party/release-notices/inputs.json").read_bytes())
entries = {(x["root"], x["path"]): x for x in catalog["inputs"]}
paths = [UPSTREAM / name for name in (*PINS, "downloads.json")]
paths += [ICONS / name for name in ("LICENSE", "usb.svg", "wifi-strength-4.svg")]
paths += [PROJECT / "docs/icon_provenance.md"]
for path in paths:
with self.subTest(path=path.name):
entry = entries[("project", path.relative_to(PROJECT).as_posix())]
data = path.read_bytes()
self.assertIsNone(entry["range"])
self.assertEqual(entry["size"], len(data))
self.assertEqual(entry["sha256"], hashlib.sha256(data).hexdigest())
self.assertEqual(entry["output_sha256"], entry["sha256"])
+2
View File
@@ -14,6 +14,8 @@ import unittest
from unittest import mock
sys.dont_write_bytecode = True
from icon_provenance import IconProvenanceTests
PROJECT = Path(__file__).absolute().parents[2]
SPEC = importlib.util.spec_from_file_location("release_notices", PROJECT / "tools/release_notices.py")
notices = importlib.util.module_from_spec(SPEC)