diff --git a/install.ps1 b/install.ps1 index 49faa7a0..e0d59c89 100644 --- a/install.ps1 +++ b/install.ps1 @@ -8,7 +8,7 @@ param( $ErrorActionPreference = "Stop" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -$defaultInstallSha256 = "44492e7dea7639fec18b2b9c040606481b11dd7a91987e8b2548313fc82c902d" +$defaultInstallSha256 = "d5f352fbe34cfd8ae40c0980c3986158ec1d9dd1babfe2bbc0918ffc0fa0bdf0" $maximumInstallerBytes = 2MB $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } $temporary = $null diff --git a/install.py b/install.py index d346bc46..34cb7395 100644 --- a/install.py +++ b/install.py @@ -980,6 +980,24 @@ def _validate_manifest(data: object, plat: str) -> dict: for index, entry in enumerate(copies): if not isinstance(entry, dict): raise ValueError(f"standalone_copies[{index}] must be an object") + # The shape do_standalone_copies reads, checked here: a note with a + # list for detect, a copy naming neither file nor pattern, or a list + # for emulator passed and crashed the copy step after the download. + where = f"standalone_copies[{index}]" + if "note" in entry: + detect = entry.get("detect", {}) + if not isinstance(entry["note"], str) or not isinstance(detect, dict): + raise ValueError(f"invalid note entry at {where}") + for values in detect.values(): + if not isinstance(values, list) or not all( + isinstance(value, str) for value in values + ): + raise ValueError(f"invalid note entry at {where}") + continue + if "file" not in entry and "pattern" not in entry: + raise ValueError(f"{where} names neither a file nor a pattern") + if not isinstance(entry.get("emulator", ""), str): + raise ValueError(f"invalid emulator at {where}") if "file" in entry: _safe_relative_path( entry["file"], f"standalone_copies[{index}].file" diff --git a/install.sh b/install.sh index 49747189..8f6d3608 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ esac TEMP_INSTALLER="" TEMP_DIRECTORY="" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -DEFAULT_INSTALL_SHA256="44492e7dea7639fec18b2b9c040606481b11dd7a91987e8b2548313fc82c902d" +DEFAULT_INSTALL_SHA256="d5f352fbe34cfd8ae40c0980c3986158ec1d9dd1babfe2bbc0918ffc0fa0bdf0" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/tests/test_install.py b/tests/test_install.py index d0a17b20..5219fae6 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1138,6 +1138,19 @@ class TestStandaloneCopyTargetsAreUntrusted(unittest.TestCase): with self.assertRaises(ValueError): install._validate_manifest(manifest, "retroarch") + def test_entries_the_copy_step_cannot_read_are_refused(self): + """Each passed validation and crashed do_standalone_copies after the + download: a list for detect, no file nor pattern, a list for emulator.""" + for entry in ( + {"note": "hi", "detect": ["/x"]}, + {"emulator": "pcsx2", "targets": {"linux": ["/tmp"]}}, + {"file": "bios/x.bin", "emulator": ["a"], "targets": {"linux": ["/tmp"]}}, + ): + manifest = {"manifest_version": 2, "platform": "retroarch", "files": [], + "standalone_copies": [entry]} + with self.subTest(entry=entry), self.assertRaises(ValueError): + install._validate_manifest(manifest, "retroarch") + def test_plain_absolute_target_still_works(self): manifest = { "manifest_version": 2,