diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-19 20:53:28 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-19 20:53:28 +0200 |
| commit | d29fc1cd128b7d6a18e3e50d8ff4d6262f7e88ae (patch) | |
| tree | 1d65e63f4ad94b2dcd040f46dd281e0679d61606 /tests | |
| parent | 8afb50ff9a3a499d8952dfc54378df29161cde0e (diff) | |
| download | sb-builder-d29fc1cd128b7d6a18e3e50d8ff4d6262f7e88ae.tar.gz sb-builder-d29fc1cd128b7d6a18e3e50d8ff4d6262f7e88ae.zip | |
sbb-remote 0.2.0: inventory, slackrepo and publish jobs, signing key
inventory lists every package with its effective version (hint over
.info), build state from the slackrepo DB, summary, and the parsed
hint found by slackrepo's search order; hints are parsed with shlex,
never sourced. Job kinds now return argv plus env: slackrepo
build/rebuild/update/remove (packages validated against the tree,
--preview, --no-publish sets SBB_PUBLISH=n) and publish (sb-publish).
gpg-status and gpg-unlock let the GUI unlock the package signing key
in gpg-agent; the passphrase comes on stdin only, every other
subprocess gets stdin=DEVNULL. A real slackrepo run is refused while
the key is locked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_remote.py | 159 |
1 files changed, 155 insertions, 4 deletions
diff --git a/tests/test_remote.py b/tests/test_remote.py index 31d0597..c2b9857 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -45,7 +45,9 @@ class Base(unittest.TestCase): with open(conf, "w") as f: f.write(f'state_dir = "{self.tmp}"\n' f'repo_dir = "{self.tmp}/repo"\n' - f'slackrepo_db = "{self.tmp}/slackrepo.sqlite3"\n') + f'slackrepo_db = "{self.tmp}/slackrepo.sqlite3"\n' + f'hintdir = "{self.tmp}/hints"\n' + f'genreprc = "{self.tmp}/genreprc"\n') patcher = mock.patch.dict(os.environ, {"SBB_CONFIG": conf}) patcher.start() self.addCleanup(patcher.stop) @@ -61,6 +63,34 @@ class Base(unittest.TestCase): open(os.path.join(d, "log"), "wb").close() return jid, d + def make_pkg(self, path, info="", hint=None, desc=None): + d = os.path.join(self.cfg["repo_dir"], path) + pkg = os.path.basename(path) + os.makedirs(d, exist_ok=True) + open(os.path.join(d, pkg + ".SlackBuild"), "w").close() + with open(os.path.join(d, pkg + ".info"), "w") as f: + f.write(info) + if desc: + with open(os.path.join(d, "slack-desc"), "w") as f: + f.write(f"{pkg}: {pkg} ({desc})\n{pkg}:\n") + if hint is not None: + os.makedirs(self.cfg["hintdir"], exist_ok=True) + with open(os.path.join(self.cfg["hintdir"], pkg + ".hint"), "w") as f: + f.write(hint) + + def fake_bin(self, name, script): + bindir = os.path.join(self.tmp, "bin") + os.makedirs(bindir, exist_ok=True) + path = os.path.join(bindir, name) + with open(path, "w") as f: + f.write("#!/bin/sh\n" + script) + os.chmod(path, 0o755) + if bindir not in os.environ["PATH"]: + patcher = mock.patch.dict(os.environ, {"PATH": bindir + os.pathsep + os.environ["PATH"]}) + patcher.start() + self.addCleanup(patcher.stop) + return path + def hold_lock(self): f = open(os.path.join(self.tmp, "lock"), "a") fcntl.flock(f, fcntl.LOCK_EX) @@ -109,11 +139,33 @@ class ConfigTest(Base): class KindsTest(Base): def test_sbpull_argv(self): self.assertEqual(sbb.KINDS["sbpull"](self.cfg, ["personal", "--no"]), - ["bash", "-c", '. "$1" && sbpull "$2" "$3"', "_", - self.cfg["sbpull_script"], "personal", "--no"]) + (["bash", "-c", '. "$1" && sbpull "$2" "$3"', "_", + self.cfg["sbpull_script"], "personal", "--no"], {})) def test_sbshadow_argv(self): - self.assertEqual(sbb.KINDS["sbshadow"](self.cfg, ["--yes"]), ["sbshadow", "--yes"]) + self.assertEqual(sbb.KINDS["sbshadow"](self.cfg, ["--yes"]), (["sbshadow", "--yes"], {})) + + def test_slackrepo_argv(self): + self.make_pkg("misc/nuclei") + self.make_pkg("personal/foo") + kind = sbb.KINDS["slackrepo"] + self.assertEqual(kind(self.cfg, ["update", "--preview", "nuclei", "foo", "nuclei"]), + (["slackrepo", "--repo=SBo-danix", "update", "--preview", "nuclei", "foo"], {})) + self.assertEqual(kind(self.cfg, ["build", "--no-publish", "foo"]), + (["slackrepo", "--repo=SBo-danix", "build", "foo"], {"SBB_PUBLISH": "n"})) + self.assertEqual(kind(self.cfg, ["update"]), (["slackrepo", "--repo=SBo-danix", "update"], {})) + + def test_slackrepo_rejects(self): + self.make_pkg("misc/nuclei") + for args in (["install", "nuclei"], ["build"], ["remove"], ["build", "nope"], + ["build", "../etc"], ["build", "--force", "nuclei"]): + with self.subTest(args=args), self.assertRaises(sbb.Error): + sbb.KINDS["slackrepo"](self.cfg, args) + + def test_publish(self): + self.assertEqual(sbb.KINDS["publish"](self.cfg, []), (["sb-publish"], {})) + with self.assertRaises(sbb.Error): + sbb.KINDS["publish"](self.cfg, ["x"]) def test_rejects_bad_args(self): for kind, args in (("sbpull", ["evil", "--no"]), ("sbpull", ["personal"]), @@ -138,6 +190,100 @@ class MainTest(Base): (0, {"version": sbb.VERSION, "overlays": ["personal", "pentesting"]})) +class InventoryTest(Base): + def test_rows(self): + self.make_pkg("misc/nuclei", 'VERSION="3.11.0"\nMAINTAINER="Some One"\n' + 'REQUIRES="go \\\n git"\n', desc="Fast scanner") + self.make_pkg("personal/foo", 'VERSION="1.0"\n', + hint='VERSION="1.1" # bumped\nDELREQUIRES="a b"\nOPTIONS="X=$(uname -m)"\n') + os.makedirs(os.path.join(self.cfg["repo_dir"], "misc", "notapkg")) + db = sqlite3.connect(self.cfg["slackrepo_db"]) + db.executescript(""" + create table buildresults (itemid text primary key, time text, result text); + create table revisions (itemid text, dep text, deplist text, version text, + built text, rev text, os text, hintcksum text, primary key (itemid, dep)); + insert into buildresults values ('misc/nuclei', '5', 'ok'), ('nuclei', '1', 'bad'); + insert into revisions values ('misc/nuclei', '/', '/', '3.11.0', '1788978222', 'r', 'o', '/'); + """) + db.close() + inv = sbb.cmd_inventory(self.cfg) + rows = [dict(zip(inv["columns"], r)) for r in inv["rows"]] + self.assertEqual([r["path"] for r in rows], ["misc/nuclei", "personal/foo"]) + nuclei, foo = rows + self.assertEqual((nuclei["version"], nuclei["built_version"], nuclei["built_time"], + nuclei["result"], nuclei["maintainer"], nuclei["requires"], + nuclei["summary"], nuclei["hint_path"]), + ("3.11.0", "3.11.0", 1788978222, "ok", "Some One", ["go", "git"], + "Fast scanner", None)) + self.assertEqual((foo["version"], foo["info_version"], foo["hint_version"], foo["result"]), + ("1.1", "1.0", "1.1", None)) + self.assertEqual(foo["hints"]["DELREQUIRES"], "a b") + self.assertEqual(foo["hints"]["OPTIONS"], {"value": '"X=$(uname -m)"', "shell": True}) + json.dumps(inv) + + def test_hint_search_order(self): + self.make_pkg("misc/foo") + sub = os.path.join(self.cfg["hintdir"], "misc", "foo") + os.makedirs(sub) + with open(os.path.join(sub, "foo.hint"), "w") as f: + f.write('VERSION="2"\n') + self.assertEqual(sbb.find_hint(self.cfg, "misc/foo"), os.path.join(sub, "foo.hint")) + with open(os.path.join(self.cfg["hintdir"], "foo.hint"), "w") as f: + f.write('VERSION="3"\n') + self.assertEqual(sbb.find_hint(self.cfg, "misc/foo"), + os.path.join(self.cfg["hintdir"], "foo.hint")) + in_repo = os.path.join(self.cfg["repo_dir"], "misc", "foo", "foo.hint") + open(in_repo, "w").close() + self.assertEqual(sbb.find_hint(self.cfg, "misc/foo"), in_repo) + + def test_missing_db(self): + self.make_pkg("misc/foo", 'VERSION="1"\n') + row = sbb.cmd_inventory(self.cfg)["rows"][0] + self.assertEqual(row[4:7], [None, None, None]) + + +class GpgTest(Base): + def setup_key(self, cached): + with open(self.cfg["genreprc"], "w") as f: + f.write('REPOSOWNERGPG="ABCD1234"\nUSE_GPGAGENT=1\n') + self.fake_bin("gpg", 'case "$*" in\n' + ' *--list-secret-keys*) printf "sec:u:255:22:K:1::::::scESC:\\n' + 'grp:::::::::GRIPSIGN:\\nssb:u:255:18:S:1::::::e:\\ngrp:::::::::GRIPENC:\\n" ;;\n' + f' *-bas*) [ -f "{self.tmp}/good" ] ;;\n' + 'esac\n') + flag = "1" if cached else "-" + self.fake_bin("gpg-connect-agent", f'echo "S KEYINFO GRIPSIGN D - - {flag} P - - -"; echo OK\n') + + def test_status(self): + self.assertEqual(sbb.cmd_gpg_status(self.cfg), {"unlocked": None, "key": None}) + self.setup_key(cached=False) + self.assertEqual(sbb.cmd_gpg_status(self.cfg), {"unlocked": False, "key": "ABCD1234"}) + self.setup_key(cached=True) + self.assertEqual(sbb.cmd_gpg_status(self.cfg)["unlocked"], True) + + def test_locked_refuses_real_run_only(self): + self.setup_key(cached=False) + self.make_pkg("misc/foo") + with self.assertRaisesRegex(sbb.Error, "signing key locked"): + sbb.cmd_job_start(self.cfg, "slackrepo", "build", "foo") + self.fake_bin("slackrepo", "exit 0\n") + self.assertIn("id", sbb.cmd_job_start(self.cfg, "slackrepo", "build", "--preview", "foo")) + + def test_unlock(self): + self.setup_key(cached=False) + log = os.path.join(self.tmp, "preset.log") + preset = self.fake_bin("preset", f'echo "$* $(cat)" >> {log}\n') + with mock.patch.object(sbb, "PRESET", preset): + with self.assertRaisesRegex(sbb.Error, "bad passphrase"): + sbb.cmd_gpg_unlock(self.cfg, stdin=io.BytesIO(b"wrong\n")) + open(os.path.join(self.tmp, "good"), "w").close() + self.assertEqual(sbb.cmd_gpg_unlock(self.cfg, stdin=io.BytesIO(b"right\n")), + {"unlocked": True}) + with open(log) as f: + self.assertEqual(f.read().splitlines(), + ["--preset GRIPSIGN wrong", "--forget GRIPSIGN ", "--preset GRIPSIGN right"]) + + class JobsTest(Base): def log(self, d): with open(os.path.join(d, "log"), "rb") as f: @@ -220,6 +366,11 @@ class JobsTest(Base): patcher.start() self.addCleanup(patcher.stop) + def test_env_reaches_child(self): + jid, d = self.make_job(["sh", "-c", 'echo "publish=$SBB_PUBLISH"'], env={"SBB_PUBLISH": "n"}) + sbb.cmd_run(self.cfg, jid) + self.assertEqual(self.log(d), b"publish=n\n") + def test_start_end_to_end(self): self.fake_sbshadow() rc, data = self.run_main("job-start", "sbshadow", "--no") |
