fix: one sha1 rule at the manifest boundary

This commit is contained in:
Abdessamad Derraz committed 2026-11-05 19:08:03 +01:00
1 parent c624a820f0
commit 4b02ab33da
5 files changed
+38 -16

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
# and Invoke-WebRequest throws on a cut transfer before the hash is read:
# the site's build rewrites this line there (scripts/sitebootstrap.py).
$mismatchAdvice = "The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
$defaultInstallSha256 = "f0410caadbcc354efc86038decb95da63478ddf018cad03ec09e41ce2b5e4d63"
$defaultInstallSha256 = "0aacd5847f927fe058de40f34f09ba4676acafe9d3e3d6c4228e46a474ed8744"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+5 -6
View File
@@ -138,8 +138,10 @@ OMISSION_REASONS = {
# says, and as the installer words it. Firmware carries no category.
GAME_DATA = "game_data"
CATEGORY_LABELS = {GAME_DATA: "game data"}
_SHA1_RE = re.compile(r"^[0-9a-fA-F]{40}$")
_SHA256_RE = re.compile(r"^[0-9a-fA-F]{64}$")
# The manifest writes its digests in lowercase, as hashlib gives them, and
# every comparison reads them as written.
_SHA1_RE = re.compile(r"^[0-9a-f]{40}$")
_SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
# The region vocabulary of scripts/region.py, which this file cannot import.
# A test holds the two copies equal.
@@ -3592,9 +3594,6 @@ def _check_entry(
return size, largest
_SHA1_RE = re.compile(r"^[0-9a-f]{40}$")
def _check_data_directory(entry: object, where: str) -> None:
"""A data directory: its source archive, its mapping, its file list."""
if not isinstance(entry, dict):
@@ -3631,7 +3630,7 @@ def _check_data_directory(entry: object, where: str) -> None:
if (
not isinstance(meta, dict)
or not isinstance(meta.get("sha1"), str)
or not _SHA1_RE.match(meta["sha1"])
or not _SHA1_RE.fullmatch(meta["sha1"])
or not isinstance(meta.get("size"), int)
or not 0 <= meta["size"] <= MAX_DOWNLOAD_SIZE
):
+1 -1
View File
@@ -23,7 +23,7 @@ DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/ins
# curl or wget stop on a cut transfer before the hash is read: the site's
# build rewrites this line there (scripts/sitebootstrap.py).
MISMATCH_ADVICE="The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
DEFAULT_INSTALL_SHA256="f0410caadbcc354efc86038decb95da63478ddf018cad03ec09e41ce2b5e4d63"
DEFAULT_INSTALL_SHA256="0aacd5847f927fe058de40f34f09ba4676acafe9d3e3d6c4228e46a474ed8744"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+8 -8
View File
@@ -27,8 +27,8 @@
{
"dest": "roms/ports/retrobios/install.py",
"repo_path": "install.py",
"size": 1089187,
"sha256": "f0410caadbcc354efc86038decb95da63478ddf018cad03ec09e41ce2b5e4d63",
"size": 1089259,
"sha256": "0aacd5847f927fe058de40f34f09ba4676acafe9d3e3d6c4228e46a474ed8744",
"executable": false
},
{
@@ -71,8 +71,8 @@
"muos": {
"archive": {
"dest": "ARCHIVE/RetroBIOS.muxapp",
"size": 1142814,
"sha256": "090c40f23a9c087641c9835a79a0d4181fc702865ce20e5115fe5a7ca72a31fc"
"size": 1142886,
"sha256": "4068768b8a88f1caa5238a8f9b5f0a0a34c1a6acab3577b54075c4163ca37ca6"
},
"members": [
{
@@ -92,8 +92,8 @@
{
"name": "RetroBIOS/install.py",
"repo_path": "install.py",
"size": 1089187,
"sha256": "f0410caadbcc354efc86038decb95da63478ddf018cad03ec09e41ce2b5e4d63",
"size": 1089259,
"sha256": "0aacd5847f927fe058de40f34f09ba4676acafe9d3e3d6c4228e46a474ed8744",
"executable": false
},
{
@@ -166,8 +166,8 @@
{
"dest": ".config/modules/retrobios/install.py",
"repo_path": "install.py",
"size": 1089187,
"sha256": "f0410caadbcc354efc86038decb95da63478ddf018cad03ec09e41ce2b5e4d63",
"size": 1089259,
"sha256": "0aacd5847f927fe058de40f34f09ba4676acafe9d3e3d6c4228e46a474ed8744",
"executable": false
},
{
+23
View File
@@ -192,6 +192,29 @@ class TheBoundaryBoundsAlternatives(unittest.TestCase):
install._validate_manifest(manifest, "retroarch")
class OneDigestRuleAtTheBoundary(unittest.TestCase):
"""install.py defined _SHA1_RE twice: the second, lowercase only,
replaced the first everywhere, so an uppercase sha1 was refused where
an uppercase sha256 passed. Every comparison reads the digest as
hashlib writes it, so both refuse uppercase, data trees included."""
def _manifest(self, **digests: str) -> dict:
entry = {"dest": "a.bin", "size": 1, "repo_path": "bios/a.bin", **digests}
return {"manifest_version": 2, "platform": "retroarch", "files": [entry]}
def test_sha1_and_sha256_follow_the_same_rule(self):
install._validate_manifest(self._manifest(sha1="a" * 40), "retroarch")
install._validate_manifest(self._manifest(sha256="a" * 64), "retroarch")
for digests in ({"sha1": "A" * 40}, {"sha256": "A" * 64}):
with self.subTest(digests=digests), self.assertRaises(ValueError):
install._validate_manifest(self._manifest(**digests), "retroarch")
def test_one_definition_each(self):
source = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
for name in ("_SHA1_RE", "_SHA256_RE"):
self.assertEqual(source.count(f"\n{name} = "), 1, name)
class APlatformOmissionKeepsItsClaim(unittest.TestCase):
"""A file the platform declares and a core reads, absent from the
collection, was recorded under the core alone: RetroPie's dsi_nand.bin