style: satisfy the black gate that was failing the Tests workflow

The Tests workflow was red on every OS and Python version for one reason:
`black --check` found 28 unformatted files. Part predates this branch (the
tui_v3 screens, core/api.py, core/index0.py); part is mine from this session.

Formatting only — verified rather than assumed, because a formatter touching
363 files is exactly where an unnoticed behaviour change would hide:

  - 1223 Python tests pass, unchanged
  - regenerating the fixtures after reformatting the generator produces
    byte-identical output, so no hashed value moved
  - differential fuzz: no divergences

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
This commit is contained in:
thePR0M3TH3ANandClaude Opus 5 committed 2026-08-19 12:22:19 -04:00
1 parent 71db3bc498
commit 3395aadd1c
32 files changed
+686 -249

No files matched your search

+1 -1
View File
@@ -29,7 +29,7 @@ CASES: list = [
{"\U0001f600": 1, "!": 2},
{"z": 1, "é": 2, "中": 3, "\U0001f600": 4},
# Escaping
{"quote\"": "back\\slash"},
{'quote"': "back\\slash"},
{"ctrl": "\x00\x01\x1f"},
{"nl": "line\nbreak\ttab"},
{"unicode": "café 中文 \U0001f512"},
+189 -54
View File
@@ -38,16 +38,12 @@ sys.path.insert(0, str(REPO / "src"))
CLI_BIN = REPO / "js" / "packages" / "cli" / "bin" / "seedpass-js.mjs"
# Throwaway seeds. Never used for real funds or real vaults.
SEED_A = (
"rival cover produce defy coconut arrow empty acid crime cereal strong icon"
)
SEED_A = "rival cover produce defy coconut arrow empty acid crime cereal strong icon"
SEED_B = (
"romance waste exercise alone pistol mushroom aunt series weasel muscle move skirt"
)
# Valid words, deliberately wrong checksum: both implementations must refuse it.
SEED_INVALID = (
"gaze stereo trend brown chunk hero pole width once tent lift bird"
)
SEED_INVALID = "gaze stereo trend brown chunk hero pole width once tent lift bird"
PASSWORD = "cross-impl-check-password"
results: list[tuple[str, bool, str]] = []
@@ -273,10 +269,14 @@ def py_secrets_for(index: dict, seed: str) -> dict[str, str]:
out[label] = entry["content"]
elif kind == "seed":
out[label] = derive_seed_phrase(
bip85, int(entry.get("index", int(idx))), int(entry.get("word_count", 24))
bip85,
int(entry.get("index", int(idx))),
int(entry.get("word_count", 24)),
)
elif kind == "managed_account":
out[label] = derive_seed_phrase(bip85, int(entry.get("index", int(idx))), 12)
out[label] = derive_seed_phrase(
bip85, int(entry.get("index", int(idx))), 12
)
elif kind == "pgp":
from seedpass.core.password_generation import derive_pgp_key
@@ -326,16 +326,16 @@ def phase_a(tmp: Path) -> None:
fp = py_create_profile(app_dir, SEED_A, PASSWORD)
try:
listed = json.loads(run_cli(app_dir, "entry", "list", env_extra={"SEEDPASS_MNEMONIC": SEED_A}))
listed = json.loads(
run_cli(app_dir, "entry", "list", env_extra={"SEEDPASS_MNEMONIC": SEED_A})
)
check("TS opens the Python-created index", True)
except Exception as exc:
check("TS opens the Python-created index", False, str(exc))
return
py_seed, py_index = py_open_profile(app_dir, fp, PASSWORD)
py_labels = sorted(
e["label"] for e in py_index.get("entries", {}).values()
)
py_labels = sorted(e["label"] for e in py_index.get("entries", {}).values())
ts_labels = sorted(row["label"] for row in listed)
check("entry sets match", py_labels == ts_labels, f"{py_labels} != {ts_labels}")
@@ -359,7 +359,11 @@ def phase_a(tmp: Path) -> None:
with agent_running(app_dir):
try:
run_cli(
app_dir, "vault", "unlock", "--ttl", "5",
app_dir,
"vault",
"unlock",
"--ttl",
"5",
env_extra={"SEEDPASS_PASSWORD": PASSWORD},
)
revealed = run_cli(app_dir, "entry", "reveal", "python-api")
@@ -370,7 +374,9 @@ def phase_a(tmp: Path) -> None:
)
except Exception as exc:
check(
"TS unlocks the Python profile with the master password", False, str(exc)
"TS unlocks the Python profile with the master password",
False,
str(exc),
)
@@ -379,25 +385,64 @@ def phase_b(tmp: Path) -> None:
app_dir = tmp / "b"
app_dir.mkdir()
run_cli(
app_dir, "fingerprint", "add", "--name", "ts-made",
app_dir,
"fingerprint",
"add",
"--name",
"ts-made",
env_extra={"SEEDPASS_MNEMONIC": SEED_B, "SEEDPASS_PASSWORD": PASSWORD},
)
env = {"SEEDPASS_MNEMONIC": SEED_B}
run_cli(app_dir, "entry", "add", "password", "ts-site.example", "--length", "22", env_extra=env)
run_cli(
app_dir,
"entry",
"add",
"password",
"ts-site.example",
"--length",
"22",
env_extra=env,
)
run_cli(app_dir, "entry", "add", "totp", "ts-totp", env_extra=env)
run_cli(app_dir, "entry", "add", "key-value", "ts-api", "token", "ts-secret-value", env_extra=env)
run_cli(
app_dir,
"entry",
"add",
"key-value",
"ts-api",
"token",
"ts-secret-value",
env_extra=env,
)
run_cli(app_dir, "entry", "add", "managed-account", "ts-managed", env_extra=env)
run_cli(app_dir, "entry", "add", "ssh", "ts-ssh", env_extra=env)
run_cli(app_dir, "entry", "add", "nostr", "ts-nostr", env_extra=env)
run_cli(
app_dir, "entry", "add", "pgp", "ts-pgp", "--user-id", "[email protected]",
app_dir,
"entry",
"add",
"pgp",
"ts-pgp",
"--user-id",
"[email protected]",
env_extra=env,
)
run_cli(app_dir, "entry", "add", "document", "ts-doc", "ts document body", env_extra=env)
run_cli(
app_dir, "entry", "add", "document", "ts-doc", "ts document body", env_extra=env
)
run_cli(app_dir, "entry", "add", "seed", "ts-seed", "--words", "24", env_extra=env)
run_cli(
app_dir, "entry", "add", "totp", "ts-totp-imported",
"--secret", "JBSWY3DPEHPK3PXP", "--period", "45", "--digits", "8",
app_dir,
"entry",
"add",
"totp",
"ts-totp-imported",
"--secret",
"JBSWY3DPEHPK3PXP",
"--period",
"45",
"--digits",
"8",
env_extra=env,
)
@@ -422,7 +467,9 @@ def phase_b(tmp: Path) -> None:
for k in py_secrets
if py_secrets[k].rstrip("\n") != (ts_secrets.get(k) or "").rstrip("\n")
]
check("every secret derives identically", not mismatches, f"mismatched: {mismatches}")
check(
"every secret derives identically", not mismatches, f"mismatched: {mismatches}"
)
# Python's schema validation should accept the TS-written index
from seedpass.core.migrations import LATEST_VERSION # type: ignore
@@ -452,11 +499,17 @@ def phase_c(tmp: Path) -> None:
vault = Vault(enc_mgr, fp_dir)
cfg_mgr = ConfigManager(vault, fp_dir)
backup_mgr = BackupManager(fp_dir, cfg_mgr)
py_export = export_backup(vault, backup_mgr, fp_dir / "py-export.json", parent_seed=SEED_A)
py_export = export_backup(
vault, backup_mgr, fp_dir / "py-export.json", parent_seed=SEED_A
)
try:
summary = json.loads(
run_cli(
app_a, "vault", "import", str(py_export), "--inspect",
app_a,
"vault",
"import",
str(py_export),
"--inspect",
env_extra={"SEEDPASS_MNEMONIC": SEED_A},
)
)
@@ -473,7 +526,10 @@ def phase_c(tmp: Path) -> None:
fp_b = generate_fingerprint(SEED_B)
ts_export = app_b / "ts-export.json"
run_cli(
app_b, "vault", "export", str(ts_export),
app_b,
"vault",
"export",
str(ts_export),
env_extra={"SEEDPASS_MNEMONIC": SEED_B},
)
fp_dir_b = app_b / fp_b
@@ -481,6 +537,7 @@ def phase_c(tmp: Path) -> None:
vault_b = Vault(enc_b, fp_dir_b)
cfg_b = ConfigManager(vault_b, fp_dir_b)
backup_b = BackupManager(fp_dir_b, cfg_b)
def user_data(index: dict) -> str:
"""User-visible state only.
@@ -504,7 +561,9 @@ def phase_c(tmp: Path) -> None:
"user data changed across the round trip",
)
except Exception as exc:
check("Python imports a TS portable backup (checksum verified)", False, str(exc))
check(
"Python imports a TS portable backup (checksum verified)", False, str(exc)
)
def phase_d(tmp: Path) -> None:
@@ -526,8 +585,15 @@ def phase_d(tmp: Path) -> None:
detail = ""
try:
run_cli(
app_dir, "fingerprint", "add", "--name", "should-not-exist",
env_extra={"SEEDPASS_MNEMONIC": SEED_INVALID, "SEEDPASS_PASSWORD": PASSWORD},
app_dir,
"fingerprint",
"add",
"--name",
"should-not-exist",
env_extra={
"SEEDPASS_MNEMONIC": SEED_INVALID,
"SEEDPASS_PASSWORD": PASSWORD,
},
)
detail = "TS created a profile from an invalid phrase"
except Exception as exc:
@@ -593,7 +659,9 @@ def phase_e(tmp: Path) -> None:
check("Python and TS agree on the migrated entry set", py_labels == labels)
# And TS must not have written a downgraded index back to disk
reloaded = Vault(EncryptionManager(derive_index_key(SEED_A), fp_dir), fp_dir).load_index()
reloaded = Vault(
EncryptionManager(derive_index_key(SEED_A), fp_dir), fp_dir
).load_index()
check(
"Python still reads the profile after TS touched it",
sorted(e["label"] for e in reloaded.get("entries", {}).values()) == labels,
@@ -686,9 +754,15 @@ def phase_f(tmp: Path) -> None:
encrypted = vault.get_encrypted_index()
manifest, manifest_id = asyncio.run(client.publish_snapshot(encrypted))
published = bool(manifest_id) and bool(manifest.chunks)
check("Python publishes a snapshot to the relay", published, str(manifest_id))
check(
"Python publishes a snapshot to the relay", published, str(manifest_id)
)
except Exception as exc:
check("Python publishes a snapshot to the relay", False, f"{type(exc).__name__}: {exc}")
check(
"Python publishes a snapshot to the relay",
False,
f"{type(exc).__name__}: {exc}",
)
return
# --- TypeScript restores from what Python published -------------------
@@ -714,7 +788,9 @@ def phase_f(tmp: Path) -> None:
f"{ts_labels} != {py_labels}",
)
revealed = run_cli(ts_dir, "entry", "reveal", "python-api", env_extra=env)
check("restored secrets are intact", revealed == "py-secret-value", revealed)
check(
"restored secrets are intact", revealed == "py-secret-value", revealed
)
except Exception as exc:
check("TS restores the Python-published snapshot", False, str(exc))
@@ -757,7 +833,9 @@ def phase_g(tmp: Path) -> None:
links = json.loads(run_cli(app_dir, "entry", "links", "0", env_extra=env))
check(
"TS sees Python's link with a resolved target",
len(links) == 1 and links[0]["relation"] == "related_to" and links[0]["target_id"] == 2,
len(links) == 1
and links[0]["relation"] == "related_to"
and links[0]["target_id"] == 2,
json.dumps(links),
)
archived = json.loads(run_cli(app_dir, "entry", "get", "1", env_extra=env))
@@ -770,7 +848,11 @@ def phase_g(tmp: Path) -> None:
_vault2, em2, _cfg2 = _py_vault(app_dir, fp, SEED_A)
entry0 = em2.retrieve_entry(0)
entry1 = em2.retrieve_entry(1)
check("Python sees TS's edit", entry0.get("notes") == "edited-by-ts", str(entry0.get("notes")))
check(
"Python sees TS's edit",
entry0.get("notes") == "edited-by-ts",
str(entry0.get("notes")),
)
check("Python sees TS's unarchive", entry1.get("archived") is False)
check("Python sees TS's link removal", em2.get_links(0) == [])
@@ -786,14 +868,25 @@ def phase_h(tmp: Path) -> None:
"schema_version": 4,
"entries": {
"0": {
"type": "password", "kind": "password", "label": "shared",
"length": 16, "archived": False, "notes": "from-current",
"tags": ["a"], "modified_ts": base_ts,
"type": "password",
"kind": "password",
"label": "shared",
"length": 16,
"archived": False,
"notes": "from-current",
"tags": ["a"],
"modified_ts": base_ts,
},
"1": {
"type": "key_value", "kind": "key_value", "label": "only-current",
"key": "k", "value": "v", "archived": False, "notes": "",
"tags": [], "modified_ts": base_ts,
"type": "key_value",
"kind": "key_value",
"label": "only-current",
"key": "k",
"value": "v",
"archived": False,
"notes": "",
"tags": [],
"modified_ts": base_ts,
},
},
}
@@ -801,20 +894,33 @@ def phase_h(tmp: Path) -> None:
"schema_version": 4,
"entries": {
"0": {
"type": "password", "kind": "password", "label": "shared",
"length": 16, "archived": True, "notes": "",
"tags": ["b"], "modified_ts": base_ts, "username": "incoming-user",
"type": "password",
"kind": "password",
"label": "shared",
"length": 16,
"archived": True,
"notes": "",
"tags": ["b"],
"modified_ts": base_ts,
"username": "incoming-user",
},
"2": {
"type": "password", "kind": "password", "label": "only-incoming",
"length": 20, "archived": False, "notes": "", "tags": [],
"type": "password",
"kind": "password",
"label": "only-incoming",
"length": 20,
"archived": False,
"notes": "",
"tags": [],
"modified_ts": base_ts + 5,
},
},
}
py_merged = merge_index_payloads(
json.loads(json.dumps(current)), json.loads(json.dumps(incoming)), source_tag="xtest"
json.loads(json.dumps(current)),
json.loads(json.dumps(incoming)),
source_tag="xtest",
)
# Run the TS merge through a tiny node harness against the same inputs
@@ -829,7 +935,9 @@ def phase_h(tmp: Path) -> None:
try:
proc = subprocess.run(
["node", str(script), json.dumps([current, incoming])],
capture_output=True, text=True, cwd=str(CLI_BIN.parent),
capture_output=True,
text=True,
cwd=str(CLI_BIN.parent),
)
if proc.returncode != 0:
check("TS and Python merge identically", False, proc.stderr.strip()[:200])
@@ -880,7 +988,11 @@ def phase_i(tmp: Path) -> None:
with agent_running(app_dir):
try:
run_cli(
app_dir, "vault", "unlock", "--ttl", "10",
app_dir,
"vault",
"unlock",
"--ttl",
"10",
env_extra={"SEEDPASS_PASSWORD": PASSWORD},
)
revealed = run_cli(app_dir, "entry", "reveal", "python-api")
@@ -937,8 +1049,26 @@ def phase_k(tmp: Path) -> None:
# Simulate a migration window: the user works exclusively in TS for a
# while, exercising creation, modification and archival.
run_cli(app_dir, "entry", "add", "password", "post-migration-site", "--length", "22", env_extra=env)
run_cli(app_dir, "entry", "add", "key-value", "post-migration-kv", "k", "rollback-value", env_extra=env)
run_cli(
app_dir,
"entry",
"add",
"password",
"post-migration-site",
"--length",
"22",
env_extra=env,
)
run_cli(
app_dir,
"entry",
"add",
"key-value",
"post-migration-kv",
"k",
"rollback-value",
env_extra=env,
)
run_cli(app_dir, "entry", "add", "totp", "post-migration-totp", env_extra=env)
run_cli(app_dir, "entry", "modify", "0", "--notes", "touched by ts", env_extra=env)
run_cli(app_dir, "entry", "archive", "1", env_extra=env)
@@ -966,12 +1096,13 @@ def phase_k(tmp: Path) -> None:
# And Python can still derive the secrets for what TS created.
py_secrets = py_secrets_for(py_index, SEED_A)
ts_secrets = ts_secrets_for(app_dir, ["post-migration-site", "post-migration-kv"], SEED_A)
ts_secrets = ts_secrets_for(
app_dir, ["post-migration-site", "post-migration-kv"], SEED_A
)
check(
"secrets for TS-created entries match in Python",
all(
py_secrets[k].rstrip("\n") == ts_secrets[k].rstrip("\n")
for k in ts_secrets
py_secrets[k].rstrip("\n") == ts_secrets[k].rstrip("\n") for k in ts_secrets
),
)
@@ -1007,7 +1138,11 @@ def phase_l(tmp: Path) -> None:
"blacklisted": "foreign meaning, not our archive flag",
"website": "foreign meaning, not our label alias",
"words": ["foreign", "list"],
"bitlogin": {"admins": ["npub1aaaa"], "roles": {"sales": ["npub1bbbb"]}, "policy_rev": 7},
"bitlogin": {
"admins": ["npub1aaaa"],
"roles": {"sales": ["npub1bbbb"]},
"policy_rev": 7,
},
}
foreign_top_level = {"spec": "bitlogin-v1", "org_count": 1}
+50 -17
View File
@@ -110,7 +110,8 @@ def rand_text(rng: random.Random) -> str:
if rng.random() < 0.5:
return rng.choice(INTERESTING_TEXT)
return "".join(
rng.choice(string.ascii_letters + string.digits + " -_.") for _ in range(rng.randint(0, 12))
rng.choice(string.ascii_letters + string.digits + " -_.")
for _ in range(rng.randint(0, 12))
)
@@ -262,7 +263,9 @@ def case_password(rng: random.Random) -> tuple[str, Any, dict[str, Any]]:
index = rng.randint(0, 5000)
version = rng.choice([1, 2])
bip85 = BIP85(Bip39SeedGenerator(mnemonic).Generate(""))
pg = PasswordGenerator(_Deriver(), mnemonic, bip85, policy=PasswordPolicy(**policy_kwargs))
pg = PasswordGenerator(
_Deriver(), mnemonic, bip85, policy=PasswordPolicy(**policy_kwargs)
)
kwargs = {} if version == 1 else {"gen_version": version}
try:
result: Any = pg.generate_password(length=length, index=index, **kwargs)
@@ -302,7 +305,9 @@ def case_merge(rng: random.Random) -> tuple[str, Any, dict[str, Any]]:
def case_index0(rng: random.Random) -> tuple[str, Any, dict[str, Any]]:
from seedpass.core import index0 as index0_mod
fp_dir = f"/tmp/seedpass/{''.join(rng.choice('0123456789ABCDEF') for _ in range(16))}"
fp_dir = (
f"/tmp/seedpass/{''.join(rng.choice('0123456789ABCDEF') for _ in range(16))}"
)
payload: dict[str, Any] = {"schema_version": 4, "entries": rand_entries(rng)}
events = []
# Same reasoning as the tombstone cap: occasionally produce enough events,
@@ -313,7 +318,9 @@ def case_index0(rng: random.Random) -> tuple[str, Any, dict[str, Any]]:
# peaked at 63 distinct subjects across 250 cases -- one short -- so the
# eviction path stayed unreachable and a deliberate off-by-one there went
# undetected. Bounded code has to be driven past its bound, with margin.
event_count = rng.randint(SUBJECT_CAP + 20, SUBJECT_CAP + 60) if big else rng.randint(0, 5)
event_count = (
rng.randint(SUBJECT_CAP + 20, SUBJECT_CAP + 60) if big else rng.randint(0, 5)
)
# A large run is clustered into one day half the time, so both the subject
# cap (per checkpoint) and the checkpoint retention limit get reached.
day_spread = not big or rng.random() < 0.5
@@ -359,7 +366,12 @@ def case_index0(rng: random.Random) -> tuple[str, Any, dict[str, Any]]:
return (
"index0",
result,
{"op": "index0", "fingerprintDir": fp_dir, "entries": payload.get("entries", {}), "events": events},
{
"op": "index0",
"fingerprintDir": fp_dir,
"entries": payload.get("entries", {}),
"events": events,
},
)
@@ -397,7 +409,9 @@ def case_semantic(rng: random.Random) -> tuple[str, Any, dict[str, Any]]:
try:
index.build(entries)
records = json.loads(
(Path(tmpdir) / "semantic_index" / "records.json").read_text(encoding="utf-8")
(Path(tmpdir) / "semantic_index" / "records.json").read_text(
encoding="utf-8"
)
)
hits = index.search(query, k=10)
except Exception as exc:
@@ -502,7 +516,9 @@ def run_ts(requests: list[dict[str, Any]]) -> list[Any]:
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--cases", type=int, default=200, help="cases per surface")
parser.add_argument("--seed", type=int, default=1, help="RNG seed (reproduces a run)")
parser.add_argument(
"--seed", type=int, default=1, help="RNG seed (reproduces a run)"
)
parser.add_argument(
"--only",
action="append",
@@ -514,7 +530,9 @@ def main() -> int:
surfaces = args.only or sorted(BUILDERS)
rng = random.Random(args.seed)
print(f"differential fuzz: seed={args.seed} cases={args.cases} surfaces={','.join(surfaces)}")
print(
f"differential fuzz: seed={args.seed} cases={args.cases} surfaces={','.join(surfaces)}"
)
expected: list[tuple[str, int, Any]] = []
requests: list[dict[str, Any]] = []
@@ -528,12 +546,16 @@ def main() -> int:
print(f" generated {len(requests)} cases; running the TypeScript side...")
ts_results = run_ts(requests)
if len(ts_results) != len(expected):
raise SystemExit(f"runner returned {len(ts_results)} results for {len(expected)} cases")
raise SystemExit(
f"runner returned {len(ts_results)} results for {len(expected)} cases"
)
divergences: list[tuple[str, int, str, str, dict[str, Any]]] = []
known: list[tuple[str, int]] = []
skipped = 0
for (name, idx, python_result), ts_result, request in zip(expected, ts_results, requests):
for (name, idx, python_result), ts_result, request in zip(
expected, ts_results, requests
):
if request.get("op") == "noop":
skipped += 1
continue
@@ -552,7 +574,9 @@ def main() -> int:
for surface in sorted(by_surface):
failures = sum(1 for d in divergences if d[0] == surface)
mark = "PASS" if failures == 0 else "FAIL"
print(f" [{mark}] {surface}: {by_surface[surface] - failures}/{by_surface[surface]} agree")
print(
f" [{mark}] {surface}: {by_surface[surface] - failures}/{by_surface[surface]} agree"
)
if skipped:
print(f" ({skipped} cases skipped: the Python side refused the input)")
@@ -567,12 +591,21 @@ def main() -> int:
)
if divergences:
print(f"\n{len(divergences)} DIVERGENCE(S) — reproduce with --seed {args.seed}\n")
dump = Path("/tmp/claude-1000/-home-user/4efd790e-427f-430a-b379-ef8391e6744f/scratchpad/divergences.json")
dump.write_text(json.dumps([
{"surface": n, "case": i, "python": p_, "ts": t, "input": r}
for n, i, p_, t, r in divergences
], indent=1))
print(
f"\n{len(divergences)} DIVERGENCE(S) — reproduce with --seed {args.seed}\n"
)
dump = Path(
"/tmp/claude-1000/-home-user/4efd790e-427f-430a-b379-ef8391e6744f/scratchpad/divergences.json"
)
dump.write_text(
json.dumps(
[
{"surface": n, "case": i, "python": p_, "ts": t, "input": r}
for n, i, p_, t, r in divergences
],
indent=1,
)
)
print(f" (full list written to {dump})\n")
for name, idx, py_canon, ts_canon, request in divergences[:5]:
print(f"--- {name} case {idx} ---")
+2
View File
@@ -45,6 +45,8 @@ def test_app_dir() -> Path:
another ``tests`` segment on each reload.
"""
return Path(consts.APP_DIR) / "tests"
from utils.key_derivation import derive_key_from_password, derive_index_key
from seedpass.core.encryption import EncryptionManager
from seedpass.core.vault import Vault
+188 -55
View File
@@ -58,8 +58,7 @@ MNEMONICS = {
"abandon abandon abandon abandon abandon about"
),
"legal12": (
"legal winner thank year wave sausage worth useful "
"legal winner thank yellow"
"legal winner thank year wave sausage worth useful " "legal winner thank yellow"
),
"zoo24": (
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo "
@@ -326,7 +325,9 @@ def gen_fingerprints() -> dict:
{
"mnemonic_id": "abandon12-mixed-case-padded",
"mnemonic": " " + MNEMONICS[PRIMARY].upper() + " ",
"fingerprint": generate_fingerprint(" " + MNEMONICS[PRIMARY].upper() + " "),
"fingerprint": generate_fingerprint(
" " + MNEMONICS[PRIMARY].upper() + " "
),
}
)
return {
@@ -412,9 +413,7 @@ def _build_entries_index(post_add=None) -> dict:
post_add(em)
index = vault.load_index()
entries = {
k: v for k, v in index.items() if k != "_system"
}
entries = {k: v for k, v in index.items() if k != "_system"}
return entries
@@ -461,7 +460,11 @@ def gen_password_kdf() -> dict:
fp = generate_fingerprint(MNEMONICS[PRIMARY])
pbkdf2_cases = []
for password in ("fixture-password", "correct horse battery staple", " pässwörd "):
for password in (
"fixture-password",
"correct horse battery staple",
" pässwörd ",
):
for iterations in (50_000, 100_000):
key = derive_key_from_password(password, fp, iterations=iterations)
pbkdf2_cases.append(
@@ -534,8 +537,10 @@ def gen_legacy_payloads() -> dict:
seed_key_b64 = derive_key_from_password("fixture-password", fp)
seed_key = base64.urlsafe_b64decode(seed_key_b64)
seed_nonce = hashlib.sha256(b"seedpass-ts-fixture-seed-nonce").digest()[:12]
seed_ct = b"V3|" + seed_nonce + AESGCM(seed_key).encrypt(
seed_nonce, mnemonic.encode(), None
seed_ct = (
b"V3|"
+ seed_nonce
+ AESGCM(seed_key).encrypt(seed_nonce, mnemonic.encode(), None)
)
kdf_dict = {
"name": "pbkdf2-sha256",
@@ -592,7 +597,9 @@ def gen_nostr_snapshot(vault_payload_b64: str) -> dict:
limit = 800
manifest, chunks = prepare_snapshot(encrypted, limit)
# Re-chunk the pinned compression so chunk bytes/hashes are deterministic
pinned_chunks = [compressed[i : i + limit] for i in range(0, len(compressed), limit)]
pinned_chunks = [
compressed[i : i + limit] for i in range(0, len(compressed), limit)
]
metas = [
{
"id": f"seedpass-chunk-{i:04d}",
@@ -704,8 +711,12 @@ def gen_sync_merge() -> dict:
"schema_version": 4,
"entries": {
"0": entry(
"site", t, notes="from-incoming", tags=["b", "a"],
username="alice", archived=True,
"site",
t,
notes="from-incoming",
tags=["b", "a"],
username="alice",
archived=True,
)
},
},
@@ -713,7 +724,10 @@ def gen_sync_merge() -> dict:
),
_merge_case(
"incoming-delete-creates-tombstone",
{"schema_version": 4, "entries": {"0": entry("site", t), "1": entry("keep", t)}},
{
"schema_version": 4,
"entries": {"0": entry("site", t), "1": entry("keep", t)},
},
{
"schema_version": 4,
"entries": {"0": {**entry("site", t + 5), "_deleted": True}},
@@ -727,7 +741,12 @@ def gen_sync_merge() -> dict:
"entries": {},
"_sync_meta": {
"tombstones": {
"0": {"deleted_ts": t, "entry_hash": "", "event_hash": "", "source": "x"}
"0": {
"deleted_ts": t,
"entry_hash": "",
"event_hash": "",
"source": "x",
}
}
},
},
@@ -741,7 +760,12 @@ def gen_sync_merge() -> dict:
"entries": {},
"_sync_meta": {
"tombstones": {
"0": {"deleted_ts": t + 20, "entry_hash": "", "event_hash": "", "source": "x"}
"0": {
"deleted_ts": t + 20,
"entry_hash": "",
"event_hash": "",
"source": "x",
}
}
},
},
@@ -755,7 +779,12 @@ def gen_sync_merge() -> dict:
"entries": {},
"_sync_meta": {
"tombstones": {
"0": {"deleted_ts": t + 1, "entry_hash": "aa", "event_hash": "", "source": "one"}
"0": {
"deleted_ts": t + 1,
"entry_hash": "aa",
"event_hash": "",
"source": "one",
}
}
},
},
@@ -764,7 +793,12 @@ def gen_sync_merge() -> dict:
"entries": {},
"_sync_meta": {
"tombstones": {
"0": {"deleted_ts": t + 9, "entry_hash": "bb", "event_hash": "", "source": "two"}
"0": {
"deleted_ts": t + 9,
"entry_hash": "bb",
"event_hash": "",
"source": "two",
}
}
},
},
@@ -798,14 +832,25 @@ def gen_delta_replay() -> dict:
def entry(label: str, ts: int) -> dict:
return {
"type": "password", "kind": "password", "label": label,
"length": 16, "archived": False, "notes": "", "tags": [],
"type": "password",
"kind": "password",
"label": label,
"length": 16,
"archived": False,
"notes": "",
"tags": [],
"modified_ts": ts,
}
snapshot_index = {"schema_version": 4, "entries": {"0": entry("base", t)}}
delta1 = {"schema_version": 4, "entries": {"0": entry("base-renamed", t + 10), "1": entry("added", t + 10)}}
delta2 = {"schema_version": 4, "entries": {"1": {**entry("added", t + 20), "_deleted": True}}}
delta1 = {
"schema_version": 4,
"entries": {"0": entry("base-renamed", t + 10), "1": entry("added", t + 10)},
}
delta2 = {
"schema_version": 4,
"entries": {"1": {**entry("added", t + 20), "_deleted": True}},
}
def encrypt(payload: dict, nonce_seed: bytes) -> bytes:
nonce = hashlib.sha256(nonce_seed).digest()[:12]
@@ -1158,7 +1203,11 @@ def gen_pgp_keys() -> dict:
cases = []
for mid in (PRIMARY, "zoo24"):
bip85 = _bip85(MNEMONICS[mid])
for index, user_id in ((0, "[email protected]"), (3, ""), (7, "Test User <[email protected]>")):
for index, user_id in (
(0, "[email protected]"),
(3, ""),
(7, "Test User <[email protected]>"),
):
priv, pub, fp = derive_pgp_key(bip85, index, "ed25519", user_id)
cases.append(
{
@@ -1300,26 +1349,85 @@ def gen_semantic() -> dict:
from seedpass.core.semantic_index import SemanticIndex
entries = [
{"id": 0, "kind": "password", "label": "first-ever.example",
"username": "zero", "notes": "the very first entry a profile creates",
"tags": ["edge"]},
{"id": 1, "kind": "password", "label": "bank.example", "username": "alice",
"url": "https://bank.example", "notes": "main current account",
"tags": ["money", "daily"]},
{"id": 2, "kind": "key_value", "label": "deploy-token", "key": "DEPLOY_TOKEN",
"value": "SECRET-MUST-NOT-APPEAR", "notes": "ci pipeline", "tags": ["ops"]},
{"id": 3, "kind": "document", "label": "recovery notes",
"content": "how to recover the bank account if locked out", "tags": ["docs"]},
{"id": 4, "kind": "totp", "label": "email-2fa", "issuer": "Fastmail",
"notes": "", "tags": ["email"]},
{"id": 5, "kind": "ssh", "label": "prod-server", "fingerprint": "SHA256:abcdef",
"notes": "production access", "tags": ["ops"]},
{"id": 6, "kind": "nostr", "label": "social", "npub": "npub1example",
"notes": "", "tags": []},
{"id": 7, "kind": "seed", "label": "child seed", "notes": "not an indexed kind"},
{"id": 8, "kind": "document", "label": "", "content": "", "notes": "", "tags": []},
{"id": 9, "kind": "password", "label": "linked", "notes": "", "tags": [],
"links": [{"relation": "depends_on", "note": "see recovery notes"}]},
{
"id": 0,
"kind": "password",
"label": "first-ever.example",
"username": "zero",
"notes": "the very first entry a profile creates",
"tags": ["edge"],
},
{
"id": 1,
"kind": "password",
"label": "bank.example",
"username": "alice",
"url": "https://bank.example",
"notes": "main current account",
"tags": ["money", "daily"],
},
{
"id": 2,
"kind": "key_value",
"label": "deploy-token",
"key": "DEPLOY_TOKEN",
"value": "SECRET-MUST-NOT-APPEAR",
"notes": "ci pipeline",
"tags": ["ops"],
},
{
"id": 3,
"kind": "document",
"label": "recovery notes",
"content": "how to recover the bank account if locked out",
"tags": ["docs"],
},
{
"id": 4,
"kind": "totp",
"label": "email-2fa",
"issuer": "Fastmail",
"notes": "",
"tags": ["email"],
},
{
"id": 5,
"kind": "ssh",
"label": "prod-server",
"fingerprint": "SHA256:abcdef",
"notes": "production access",
"tags": ["ops"],
},
{
"id": 6,
"kind": "nostr",
"label": "social",
"npub": "npub1example",
"notes": "",
"tags": [],
},
{
"id": 7,
"kind": "seed",
"label": "child seed",
"notes": "not an indexed kind",
},
{
"id": 8,
"kind": "document",
"label": "",
"content": "",
"notes": "",
"tags": [],
},
{
"id": 9,
"kind": "password",
"label": "linked",
"notes": "",
"tags": [],
"links": [{"relation": "depends_on", "note": "see recovery notes"}],
},
{"kind": "document", "label": "no id at all", "content": "skipped"},
]
queries = [
@@ -1401,12 +1509,22 @@ def gen_high_risk() -> dict:
entries = {
"3": {
"kind": "ssh", "type": "ssh", "label": "prod-server", "index": 3,
"notes": "deploy", "archived": False, "modified_ts": 1700000000,
"kind": "ssh",
"type": "ssh",
"label": "prod-server",
"index": 3,
"notes": "deploy",
"archived": False,
"modified_ts": 1700000000,
},
"5": {
"kind": "pgp", "type": "pgp", "label": "signing", "index": 5,
"notes": "", "archived": False, "modified_ts": 1700000100,
"kind": "pgp",
"type": "pgp",
"label": "signing",
"index": 5,
"notes": "",
"archived": False,
"modified_ts": 1700000100,
},
}
@@ -1464,16 +1582,31 @@ def gen_index0() -> dict:
managed_dir = "/home/user/.seedpass/C557EEC878DFD852/accounts/AABBCCDDEEFF0011"
entries = {
"0": {"kind": "password", "label": "bank.example", "modified_ts": 1700000100,
"tags": ["money", "daily"],
"links": [{"target_id": "2", "relation": "depends_on", "note": "recovery"}]},
"1": {"kind": "totp", "label": "email-2fa", "modified_ts": 1700000200,
"archived": True},
"2": {"kind": "ssh", "label": "prod \u2014 \u00fcn\u00efcod\u00e9 \u2713",
"modified_ts": 1700000300, "tags": ["ops", "\u00fcn\u00ef"]},
"0": {
"kind": "password",
"label": "bank.example",
"modified_ts": 1700000100,
"tags": ["money", "daily"],
"links": [{"target_id": "2", "relation": "depends_on", "note": "recovery"}],
},
"1": {
"kind": "totp",
"label": "email-2fa",
"modified_ts": 1700000200,
"archived": True,
},
"2": {
"kind": "ssh",
"label": "prod \u2014 \u00fcn\u00efcod\u00e9 \u2713",
"modified_ts": 1700000300,
"tags": ["ops", "\u00fcn\u00ef"],
},
"10": {"kind": "document", "label": "notes", "modified_ts": 1700000400},
"not-a-number": {"kind": "password", "label": "weird id",
"modified_ts": 1700000500},
"not-a-number": {
"kind": "password",
"label": "weird id",
"modified_ts": 1700000500,
},
}
payload = {"schema_version": 4, "entries": entries}