Pin PlatformIO packages and toolchains, rebase protected SDK overrides, and add WebSocket receive regression coverage. Document isolated candidate validation, archive provenance, and remaining gates.
99 lines
4.6 KiB
Python
99 lines
4.6 KiB
Python
"""Offline fail-closed helper checks; does not download/install/build."""
|
|
import hashlib
|
|
import io
|
|
import pathlib
|
|
import tarfile
|
|
import tempfile
|
|
import unittest
|
|
import json
|
|
from unittest import mock
|
|
|
|
import stage_app
|
|
import prepare
|
|
|
|
|
|
class Contracts(unittest.TestCase):
|
|
def test_hash_and_size(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
path = pathlib.Path(directory) / "artifact"
|
|
path.write_bytes(b"candidate")
|
|
item = {"size": 9, "checksum": {"sha256": hashlib.sha256(b"candidate").hexdigest()}}
|
|
prepare.verify(path, item)
|
|
path.write_bytes(b"Candidate")
|
|
with self.assertRaises(ValueError):
|
|
prepare.verify(path, item)
|
|
path.write_bytes(b"candidate-extra")
|
|
with self.assertRaises(ValueError):
|
|
prepare.verify(path, item)
|
|
|
|
def test_network_allowlist(self):
|
|
prepare.check_url("https://dl.registry.platformio.org/download/a")
|
|
prepare.check_url("https://dl.registry.nm1.platformio.org/download/a")
|
|
for url in ("http://dl.registry.platformio.org/a", "https://example.com/a", "https://dl.registry.platformio.org.evil/a"):
|
|
with self.assertRaises(ValueError):
|
|
prepare.check_url(url)
|
|
|
|
def test_contract_failure(self):
|
|
with self.assertRaises(ValueError):
|
|
prepare.require(False)
|
|
|
|
def test_member_ambiguity(self):
|
|
stream = io.BytesIO()
|
|
with tarfile.open(fileobj=stream, mode="w") as archive:
|
|
for name in ("package.json", "./package.json"):
|
|
entry = tarfile.TarInfo(name)
|
|
entry.size = 2
|
|
archive.addfile(entry, io.BytesIO(b"{}"))
|
|
stream.seek(0)
|
|
with tarfile.open(fileobj=stream) as archive:
|
|
with self.assertRaises(ValueError):
|
|
prepare.member(archive, "package.json")
|
|
with self.assertRaises(ValueError):
|
|
prepare.member(archive, "missing")
|
|
|
|
|
|
class Staging(unittest.TestCase):
|
|
def test_fresh_snapshot_and_manifest(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = pathlib.Path(directory)
|
|
base = root / ".pio/idf-candidate-5.5.3"
|
|
base.mkdir(parents=True)
|
|
(base / "app").mkdir()
|
|
(base / "app/sentinel").write_text("old")
|
|
(root / "platformio.ini").write_text("[platformio]\nplatform = platformio/espressif32@6.13.0\n")
|
|
(root / "src").mkdir()
|
|
(root / "src/main.c").write_text("modified working tree")
|
|
(root / "src/secret").write_text("never copy")
|
|
ws = root / "tests/sdk_security_overrides/ws.c"
|
|
ws.parent.mkdir(parents=True)
|
|
ws.write_text("explicit fixture")
|
|
items = [{"package": "platformio/platform/espressif32", "version": "6.13.0", "name": "platform.tar.gz"}]
|
|
with mock.patch.object(stage_app.subprocess, "check_output", return_value=b"platformio.ini\0src/main.c\0"):
|
|
dest = stage_app.stage("app-validated", root, base, items)
|
|
manifest = json.loads((dest / "app-source-manifest.json").read_text())
|
|
for name, digest in manifest.items():
|
|
self.assertEqual(hashlib.sha256((dest / name).read_bytes()).hexdigest(), digest)
|
|
self.assertIn(str(base / "core"), (dest / "platformio.ini").read_text())
|
|
self.assertEqual((dest / "src/main.c").read_text(), "modified working tree")
|
|
self.assertTrue((dest / "tests/sdk_security_overrides/ws.c").exists())
|
|
self.assertFalse((dest / "src/secret").exists())
|
|
self.assertEqual((base / "app/sentinel").read_text(), "old")
|
|
for name in ("app", "app-validated"):
|
|
with self.assertRaises(FileExistsError):
|
|
stage_app.stage(name, root, base, items)
|
|
for name in ("../escape", "/tmp/escape", "", ".", "..", "app/nested"):
|
|
with self.assertRaises(ValueError):
|
|
stage_app.stage(name, root, base, items)
|
|
(base / "linked").symlink_to(root, target_is_directory=True)
|
|
with self.assertRaises(ValueError):
|
|
stage_app.stage("linked", root, base, items)
|
|
(root / "src/main.c").unlink()
|
|
(root / "src/main.c").symlink_to(root / "platformio.ini")
|
|
with self.assertRaises(ValueError):
|
|
stage_app.stage("unsafe", root, base, items)
|
|
self.assertFalse((base / "unsafe").exists())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|