reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
"""Device-side command construction, exercised without a device.
The behaviour under test is the one the repository's own documentation says is
the reason these helpers exist: a device command is built as an **argv list** and
handed to `subprocess` directly, so the host shell never expands `$`, `|`, `>`,
`;` or quotes before adb sees them. Every assertion here inspects the argv list
that would have been executed; none of them launches adb.
"""
from __future__ import annotations
import re
import subprocess
import sys
import pytest
from conftest import load_script
devsh = load_script("devsh.py")
coldstart = load_script("coldstart.py")
install_test = load_script("install_test.py")
pytestmark = pytest.mark.integration
class Recorder:
"""Captures the argv lists handed to a patched subprocess runner."""
def __init__(self, stdout="", stderr="", returncode=0):
self.calls = []
self.stdout = stdout
self.stderr = stderr
self.returncode = returncode
def __call__(self, cmd, *args, **kwargs):
self.calls.append((list(cmd), kwargs))
return subprocess.CompletedProcess(cmd, self.returncode, self.stdout, self.stderr)
@property
def last(self):
return self.calls[-1][0]
# --------------------------------------------------------------------------- #
# devsh.py
# --------------------------------------------------------------------------- #
@pytest.fixture
def devsh_recorder(monkeypatch):
rec = Recorder(stdout="ok\n")
monkeypatch.setattr(devsh.subprocess, "run", rec)
monkeypatch.delenv("ADB_PATH", raising=False)
monkeypatch.delenv("ADB_SERIAL", raising=False)
return rec
def run_devsh(monkeypatch, rec, argv):
monkeypatch.setattr(sys, "argv", ["devsh.py"] + argv)
return devsh.main()
class TestDevshCommandConstruction:
def test_shell_command_is_one_argv_element_not_a_shell_string(self, monkeypatch, devsh_recorder):
"""`|` and `grep` must survive as literal text inside a single argument."""
assert run_devsh(monkeypatch, devsh_recorder, ["sh", "pm list packages | grep myapp"]) == 0
assert devsh_recorder.last == ["adb", "shell", "pm list packages | grep myapp"]
def test_serial_is_injected_as_dash_s(self, monkeypatch, devsh_recorder):
run_devsh(monkeypatch, devsh_recorder, ["--serial", "SERIAL123", "sh", "id"])
assert devsh_recorder.last == ["adb", "-s", "SERIAL123", "shell", "id"]
def test_serial_equals_form_is_accepted(self, monkeypatch, devsh_recorder):
run_devsh(monkeypatch, devsh_recorder, ["--adb=x", "--serial=SERIAL123", "sh", "id"])
assert devsh_recorder.last == ["x", "-s", "SERIAL123", "shell", "id"]
def test_adb_path_comes_from_the_environment_when_not_given(self, monkeypatch, devsh_recorder):
monkeypatch.setenv("ADB_PATH", "/opt/platform-tools/adb")
monkeypatch.setenv("ADB_SERIAL", "ENVSERIAL")
run_devsh(monkeypatch, devsh_recorder, ["sh", "id"])
assert devsh_recorder.last == ["/opt/platform-tools/adb", "-s", "ENVSERIAL", "shell", "id"]
def test_device_listing_explicitly_ignores_the_serial(self, monkeypatch, devsh_recorder):
run_devsh(monkeypatch, devsh_recorder, ["dev"])
assert devsh_recorder.last == ["adb", "devices", "-l"]
def test_pull_and_push_keep_their_arguments_positional(self, monkeypatch, devsh_recorder):
run_devsh(monkeypatch, devsh_recorder, ["pull", "/data/local/tmp/x.apk", "./x.apk"])
assert devsh_recorder.last == ["adb", "pull", "/data/local/tmp/x.apk", "./x.apk"]
run_devsh(monkeypatch, devsh_recorder, ["push", "./x.apk", "/data/local/tmp/x.apk"])
assert devsh_recorder.last == ["adb", "push", "./x.apk", "/data/local/tmp/x.apk"]
def test_a_nonzero_device_status_is_reported_as_one(self, monkeypatch, devsh_recorder):
devsh_recorder.returncode = 1
assert run_devsh(monkeypatch, devsh_recorder, ["sh", "false"]) == 1
def test_the_su_wrapper_is_posix_single_quoted(self, monkeypatch, devsh_recorder):
# Assert the *property*, not the representation. The previous version of this test pinned
# `su -c "..."` with `\"` escapes, which is exactly the form that let `;`, `$( )` and
# backticks through to the device shell -- the test was guarding the defect.
run_devsh(monkeypatch, devsh_recorder, ["su", 'pm grant com.example.app "android.permission.X"'])
payload = devsh_recorder.last[-1]
assert payload.startswith("su -c '") and payload.endswith("'")
inner = payload[len("su -c '"):-1]
# A POSIX single-quoted string: no bare quote may appear except as the '\'' escape.
assert re.fullmatch(r"[^']*('\\''[^']*)*", inner), payload
assert '"' in inner # the caller's double quotes survive as literal characters
def test_an_unknown_mode_prints_usage_and_exits_two(self, monkeypatch, devsh_recorder):
assert run_devsh(monkeypatch, devsh_recorder, ["frobnicate", "x"]) == 2
def test_no_arguments_prints_usage_and_exits_two(self, monkeypatch, devsh_recorder):
assert run_devsh(monkeypatch, devsh_recorder, []) == 2
class TestDevshSuQuotingGap:
"""The quoting contract, now unified through `scripts/device_shell.py`.
This class used to record a *gap*: `devsh.su` wrapped the command in double quotes and escaped
only `"`, so `$`, backticks, `;` and `|` from a caller-supplied value still reached the device
shell, while `coldstart.Device.shell` used the POSIX single-quote escape and did neutralise them.
Both now go through the shared module; the tests below pin the property from both sides, so a
future edit that reintroduces double-quote escaping fails here instead of on a device.
"""
@pytest.mark.parametrize("payload", [
"id; rm -rf /sdcard/x",
"id$(touch /data/local/tmp/pwned)",
"id`touch /data/local/tmp/pwned`",
"id | tee /data/local/tmp/pwned",
"id 'quoted'",
])
def test_su_wrapper_neutralises_shell_metacharacters(self, monkeypatch, devsh_recorder, payload):
run_devsh(monkeypatch, devsh_recorder, ["su", payload])
wrapped = devsh_recorder.last[-1]
assert wrapped.startswith("su -c '"), wrapped
inner = wrapped[len("su -c '"):-1]
# The payload must be present verbatim, with only the '\'' sequence able to break out --
# i.e. the whole payload is one argument to sh, not syntax.
assert re.fullmatch(r"[^']*('\\''[^']*)*", inner), wrapped
assert payload.replace("'", "'\\''") == inner
def test_a_malformed_path_is_refused_before_the_device_shell(self, monkeypatch, devsh_recorder):
"""`suf` interpolates a path, so it validates rather than quotes-and-hopes."""
rc = run_devsh(monkeypatch, devsh_recorder, ["suf", "/data/local/tmp/x;id", "200"])
assert rc == 2
assert not devsh_recorder.calls, "the refused command must never reach adb"
def test_coldstart_uses_the_posix_single_quote_escape(self, monkeypatch):
"""The contrasting implementation: this one does neutralise metacharacters."""
rec = Recorder()
monkeypatch.setattr(coldstart, "run", rec)
dev = coldstart.Device(None, use_su=False)
dev.su = True
dev.shell("echo 'hi'; rm -rf /sdcard/x")
payload = rec.last[-1]
assert payload.startswith("su -c '")
assert payload.endswith("'")
assert "'\\''" in payload, "the POSIX single-quote escape is missing"
# --------------------------------------------------------------------------- #
# coldstart.Device: command shape and screenshot handling
# --------------------------------------------------------------------------- #
class TestDeviceHelperCommands:
def make_device(self, monkeypatch, serial=None):
rec = Recorder()
monkeypatch.setattr(coldstart, "run", rec)
dev = coldstart.Device(serial, use_su=False)
return dev, rec
def test_shell_without_root_passes_the_command_as_one_argument(self, monkeypatch):
dev, rec = self.make_device(monkeypatch, serial="SERIAL123")
dev.shell("dumpsys window | grep mCurrentFocus")
assert rec.last == ["adb", "-s", "SERIAL123", "shell", "dumpsys window | grep mCurrentFocus"]
def test_shell_without_a_serial_omits_dash_s(self, monkeypatch):
dev, rec = self.make_device(monkeypatch)
dev.shell("id")
assert rec.last == ["adb", "shell", "id"]
def test_foreground_activity_parses_the_resumed_component(self, monkeypatch):
dev, rec = self.make_device(monkeypatch, serial="S")
rec.stdout = ("mResumedActivity: ActivityRecord{9f2c1 u0 "
"com.example.app/.MainActivity t42}\n")
assert coldstart.foreground_activity(dev) == "com.example.app/.MainActivity"
def test_foreground_activity_returns_none_when_absent(self, monkeypatch):
dev, rec = self.make_device(monkeypatch, serial="S")
rec.stdout = "no matches\n"
assert coldstart.foreground_activity(dev) is None
def test_installed_facts_extracts_version_and_uid(self, monkeypatch):
dev, rec = self.make_device(monkeypatch, serial="S")
def respond(cmd, *args, **kwargs):
rec.calls.append((list(cmd), kwargs))
if "versionCode" in cmd[-1]:
out = " versionCode=42 minSdk=24 targetSdk=34\n versionName=1.2.3\n"
elif "sha256sum" in cmd[-1]:
out = "a" * 64 + " /data/app/base.apk\n"
elif "base.apk" in cmd[-1]:
out = "/data/app/~~x==/com.example.app-1/base.apk\n"
elif "userId=" in cmd[-1]:
out = " userId=10123\n"
else:
out = ""
return subprocess.CompletedProcess(cmd, 0, out, "")
monkeypatch.setattr(coldstart, "run", respond)
facts = coldstart.installed_facts(dev, "com.example.app")
assert facts["versionName"] == "1.2.3"
assert facts["uid"] == "10123"
assert facts["apk_sha256"] == "a" * 64
def test_version_code_swallows_the_rest_of_the_line(self, monkeypatch):
"""Measured sub-string behaviour of `installed_facts`, recorded as a work item.
The extraction is `r'\\s*(versionCode|...)=(.*)'`, so on a real
`dumpsys package` line -- `versionCode=42 minSdk=24 targetSdk=34` --
`facts['versionCode']` is the whole remainder, not `42`. It is still a
usable before/after fingerprint, which is how the caller uses it, but the
field name overstates what it holds.
"""
dev = coldstart.Device(None, use_su=False)
def respond(cmd, *args, **kwargs):
if "versionCode" in cmd[-1]:
out = " versionCode=42 minSdk=24 targetSdk=34\n"
else:
out = ""
return subprocess.CompletedProcess(cmd, 0, out, "")
monkeypatch.setattr(coldstart, "run", respond)
facts = coldstart.installed_facts(dev, "com.example.app")
assert facts["versionCode"] == "42 minSdk=24 targetSdk=34"
class TestScreenshotValidation:
"""`screencap` is judged by the bytes on disk, not by the command's exit status."""
PNG_MAGIC = b"\x89PNG\r\n\x1a\n"
def make_device(self, monkeypatch):
dev = coldstart.Device(None, use_su=False)
return dev
def test_a_png_header_is_accepted_and_the_size_returned(self, monkeypatch, tmp_path):
dev = self.make_device(monkeypatch)
payload = self.PNG_MAGIC + b"\x00" * 100
def fake_run(cmd, *args, **kwargs):
kwargs["stdout"].write(payload)
return subprocess.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr(coldstart.subprocess, "run", fake_run)
out = tmp_path / "shot.png"
assert dev.screencap(str(out)) == len(payload)
assert out.read_bytes() == payload
def test_the_capture_uses_exec_out_not_shell_redirect(self, monkeypatch, tmp_path):
dev = self.make_device(monkeypatch)
seen = []
def fake_run(cmd, *args, **kwargs):
seen.append(list(cmd))
kwargs["stdout"].write(self.PNG_MAGIC + b"\x00" * 100)
return subprocess.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr(coldstart.subprocess, "run", fake_run)
dev.screencap(str(tmp_path / "shot.png"))
assert seen[0] == ["adb", "exec-out", "screencap", "-p"]
def test_a_non_png_body_is_rejected(self, monkeypatch, tmp_path):
"""A device that printed an error into the stream must not be read as a frame."""
dev = self.make_device(monkeypatch)
def fake_run(cmd, *args, **kwargs):
kwargs["stdout"].write(b"error: device offline\n")
return subprocess.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr(coldstart.subprocess, "run", fake_run)
assert dev.screencap(str(tmp_path / "shot.png")) == -1
def test_a_truncated_body_is_rejected(self, monkeypatch, tmp_path):
dev = self.make_device(monkeypatch)
def fake_run(cmd, *args, **kwargs):
kwargs["stdout"].write(b"\x89PNG")
return subprocess.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr(coldstart.subprocess, "run", fake_run)
assert dev.screencap(str(tmp_path / "shot.png")) == -1
def test_a_timeout_is_reported_as_a_failed_capture(self, monkeypatch, tmp_path):
dev = self.make_device(monkeypatch)
def fake_run(cmd, *args, **kwargs):
raise subprocess.TimeoutExpired(cmd, 30)
monkeypatch.setattr(coldstart.subprocess, "run", fake_run)
assert dev.screencap(str(tmp_path / "shot.png")) == -1
# --------------------------------------------------------------------------- #
# install_test.py: argument plumbing
# --------------------------------------------------------------------------- #
class TestInstallTestAdbWrappers:
def test_serial_is_injected_as_dash_s(self, monkeypatch):
rec = Recorder()
monkeypatch.setattr(install_test.subprocess, "run", rec)
install_test.adb(["shell", "id"], "SERIAL")
assert rec.last == ["adb", "-s", "SERIAL", "shell", "id"]
def test_without_a_serial_no_dash_s_is_added(self, monkeypatch):
rec = Recorder()
monkeypatch.setattr(install_test.subprocess, "run", rec)
install_test.adb(["devices", "-l"])
assert rec.last == ["adb", "devices", "-l"]
def test_stderr_is_appended_so_device_errors_are_not_silently_dropped(self, monkeypatch):
rec = Recorder(stdout="out", stderr="error: device offline")
monkeypatch.setattr(install_test.subprocess, "run", rec)
combined = install_test.adb(["devices"])
assert "out" in combined
assert "device offline" in combined
def test_su_wrapper_keeps_the_command_as_one_argument(self, monkeypatch):
rec = Recorder()
monkeypatch.setattr(install_test.subprocess, "run", rec)
install_test.su('pm grant com.example.app "android.permission.X"', "S")
assert rec.last == [
"adb", "-s", "S", "shell",
'su -c "pm grant com.example.app \\"android.permission.X\\""',
]