build_firmware()/find_firmware_src() cross-compile fullimage.elf via 'make -C firmware PLATFORM=<explicit> armsrc/all' and pm3flash --build flashes it. PLATFORM is explicit (never inferred from is_rdv4 — that's the running firmware's flag, not the board). Hardened per an adversarial multi-agent review of the change: - --build now implies --force: it must flash the image it just built. A dirty rebuild keeps the same base SHA, so matches_pin can't distinguish it from the old firmware and would otherwise silently skip the flash (exit 0). - make missing/not-executable -> FlashError (was an uncaught OSError traceback). - find_firmware_src no longer falls back to a CWD-relative ./firmware — running make on a stray Makefile is arbitrary code execution; only explicit arg / $PM3PY_FIRMWARE_SRC / the package repo root. Suite 1045 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
3.0 KiB
Python
104 lines
3.0 KiB
Python
"""Tests for the firmware pin + needs-reflash detection (no hardware)."""
|
|
import asyncio
|
|
import pytest
|
|
|
|
from pm3py._firmware import FirmwarePin, FIRMWARE_PIN, matches
|
|
from pm3py.core.client import Proxmark3
|
|
from pm3py import flash_cli
|
|
|
|
|
|
def run(coro):
|
|
return asyncio.get_event_loop().run_until_complete(coro)
|
|
|
|
|
|
# --- matches() ---
|
|
|
|
def test_matches_sha_substring():
|
|
pin = FirmwarePin(sha="303bd5873")
|
|
assert matches("Iceman/main/303bd5873-dirty", pin) is True
|
|
assert matches("Iceman/main/303bd58-clean", pin) is True # 7-char prefix
|
|
|
|
|
|
def test_matches_rejects_other_build():
|
|
pin = FirmwarePin(sha="303bd5873")
|
|
assert matches("Iceman/master/deadbeef", pin) is False
|
|
|
|
|
|
def test_matches_empty_is_false():
|
|
assert matches("", FirmwarePin(sha="303bd5873")) is False
|
|
|
|
|
|
def test_matches_tag():
|
|
pin = FirmwarePin(sha="303bd5873", tag="fw-v0.1.0")
|
|
assert matches("pm3py fw-v0.1.0 build", pin) is True
|
|
|
|
|
|
def test_default_pin_is_present():
|
|
assert FIRMWARE_PIN.sha # a pin is baked in
|
|
|
|
|
|
# --- _probe_firmware wiring ---
|
|
|
|
def _fake_probe(pm3, version_string):
|
|
async def fake_ping(length=32):
|
|
return {"success": True}
|
|
|
|
async def fake_version():
|
|
return {"version_string": version_string, "chip_id": "0x1", "section_size": 0}
|
|
|
|
pm3.hw.ping = fake_ping
|
|
pm3.hw.version = fake_version
|
|
|
|
|
|
def test_probe_sets_matches_pin_true():
|
|
pm3 = Proxmark3("/dev/ttyFAKE")
|
|
_fake_probe(pm3, f"Iceman/main/{FIRMWARE_PIN.sha}-dirty")
|
|
run(pm3._probe_firmware())
|
|
assert pm3.firmware.matches_pin is True
|
|
assert pm3.firmware.expected_firmware == FIRMWARE_PIN.sha
|
|
|
|
|
|
def test_probe_sets_matches_pin_false_for_stock():
|
|
pm3 = Proxmark3("/dev/ttyFAKE")
|
|
_fake_probe(pm3, "Iceman/master/00000000")
|
|
run(pm3._probe_firmware())
|
|
assert pm3.firmware.matches_pin is False
|
|
|
|
|
|
# --- CLI wiring ---
|
|
|
|
def test_cli_help_exits_clean(capsys):
|
|
try:
|
|
flash_cli.main(["--help"])
|
|
except SystemExit as e:
|
|
assert e.code == 0
|
|
out = capsys.readouterr().out
|
|
assert "pm3flash" in out
|
|
|
|
|
|
def test_cli_build_and_image_mutually_exclusive(capsys):
|
|
with pytest.raises(SystemExit):
|
|
flash_cli.main(["--build", "PM3GENERIC", "--image", "x.elf"])
|
|
|
|
|
|
def test_cli_build_invokes_builder(monkeypatch, tmp_path):
|
|
elf = tmp_path / "fullimage.elf"
|
|
elf.write_bytes(b"\x7fELF")
|
|
monkeypatch.setattr(flash_cli, "build_firmware", lambda platform: elf)
|
|
seen = {}
|
|
|
|
async def fake_run(args):
|
|
seen["image"] = args.image
|
|
seen["force"] = args.force
|
|
return 0
|
|
|
|
monkeypatch.setattr(flash_cli, "_run", fake_run)
|
|
# main() uses asyncio.run(); route it through the suite's shared loop so it
|
|
# doesn't close/null the global loop and break later get_event_loop() tests.
|
|
monkeypatch.setattr(flash_cli.asyncio, "run",
|
|
lambda coro: asyncio.get_event_loop().run_until_complete(coro))
|
|
rc = flash_cli.main(["--build", "PM3GENERIC"])
|
|
assert rc == 0
|
|
assert seen["image"] == str(elf)
|
|
assert seen["force"] is True # --build flashes what it built, regardless of the pin
|