Skip to content

Release 4.7.0 - #330

Merged
vhmartinezm merged 20 commits into
masterfrom
develop
Oct 6, 2026
Merged

vhmartinezm merged 20 commits into
masterfrom
develop

Conversation

@vhmartinezm

@vhmartinezm vhmartinezm commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Release 4.7.0

4.6.0 was declared on develop but never published; this release supersedes it.

Why this release is on the critical path

polyswarm-cli 4.6.0 pins polyswarm_api>=4.7.0. This release has to publish before the CLI's.

Requires

  • polyswarm/artifact-index#2005 (artifact-index#1997) deployed to prod (with_artifacts on /v3/ioc/search). Without it the flag is ignored upstream and callers get sha256 strings.

Deploy steps

  1. Merge; PyPI publishes 4.7.0 automatically (version change on master).
  2. Then the CLI release.

Threat-intel reports print indicators defanged (hxxps[:]//evil[.]com,
127[.]0[.]0[.]1). The server looks URLs up by an exact hash and stores
a submitted URL verbatim, so a defanged value silently missed a search
or became a broken URL artifact.

Add polyswarm_api.refang (refang_text, is_network_ioc, refang_ioc) and
a refang_iocs=True constructor flag. When on, the client refangs the
URL/domain/IP inputs of search_url, search_by_metadata (ips/urls/domains,
never the free-form query), search_by_ioc, check_known_hosts, the
known-host writes, and every URL submission path. Live values are sent
unchanged; refang_iocs=False sends inputs verbatim.

The rules, gate and case table are shared with other PolySwarm clients;
test/fixtures/refang_cases.json is kept byte-identical across them.
Python's case-insensitive flag folds Unicode (U+212A KELVIN SIGN matched
"k", so "evil[.]Kom" was refanged), \s/\S cover Unicode whitespace, and
"$" matches before a trailing newline - all unlike the other clients
that implement the same contract. Spell letters as explicit [aA]
classes, use the ASCII whitespace set, trim only that set, and
fullmatch the network-IoC pattern. The shared case table grows to 39
cases pinning these.
submit already skipped the refang for preprocessing type qrcode, whose
argument names an image file rather than a URL; sandbox_file did not, so
a file called qr[.]png was sent as qr.png. Pin both paths on both
transports, cover the async opt-out of the known-host writes and the
bare-string form of the list arguments, and put the httpx_kwargs
paragraph back under the constructor block in the contract spec.
A default-on refang changes what the write paths store (known-host rows,
URL submissions), which the versioning table only allows in a major.
With refang_iocs=False as the default the new keyword is purely additive;
the CLI opts in through its own option. Also read the QR preprocessing
type with .get() in submit, as sandbox_file already does, and cover the
uploaded URL content on both transports with its opt-out twin.
The QR branch handed the path string to from_content, so the upload body was the text of the path and the server failed the task as an unrecognised image. Read it with from_path, as submit does. Also point refang.py's docstring at its spec section instead of repeating it, and say the case-table pin guards only its own copy.
A respx test on both clients asserts the S3 PUT body is the image and the create body names its unrefanged basename; the spy-based tests could not see the uploaded body. Also fix the two spec sentences that still described the path being passed on as content.
The reverse IOC search answers bare sha256s, so a caller that wants to show
the matching artifacts has to fan out one metadata search per hash. The
server gains an opt-in `with_artifacts` that returns each artifact's
metadata-search row instead; this exposes it on both clients as an optional
keyword.

- Absent or False sends nothing new: every existing call, including one with
  no search term, builds exactly the request it did before and yields the
  same IOC/sha256 items.
- True sends `with_artifacts=1` and parses the rows with `Metadata`, the
  resource `search_by_metadata` already yields. An int, not a bool: the
  session renders bools as 'True', which the server's boolean parser refuses.

The row parse is covered by a respx body on both transports because the e2e
stack does not serve the parameter yet; recording the live pass is parked in
specs/99-open-questions.md. Builder and pass-through tests pin the request
shape on both clients, including that a bare call is still sent unchanged.
… 400 rule

The with_artifacts rows carry the server's include set, which also keeps
scan.first_scan.created and hash.ssdeep/tlsh, so first_seen, ssdeep and tlsh
are populated. The endpoint table and docstring now list that set as the
server's, and state the refusal precisely: with_artifacts=True and no term is
a 400; without the flag a bare call behaves as before.

The respx fixture mirrors the set exactly and asserts the populated fields
plus one that falls outside it. The new test module is listed in the
testing spec.
The CLI client adopts `search_by_ioc(with_artifacts=)` in the paired change
set and expresses that as `polyswarm_api>=4.7.0`. develop already declares
4.6.0 for a surface that does not include this keyword, so the floor needs
the next version up; a floor cannot name a version this repo has not
declared, so the bump lands here, in the feature PR (AGENTS.md's standing
exception).

Minor: one new optional keyword whose default preserves today's request, so
no existing call changes behaviour. Bumped with bump-my-version; the emitted
string is a clean `4.7.0`. The `with_artifacts=True` path needs a server that
supports the parameter, so this repo releases only after that server leg is
deployed, and before the CLI.
…t invariant 6

Invariant 6 said version bumps never go in feature PRs, which contradicts
AGENTS.md's standing exception and the bump this change carries. It now
names the exception: when a sibling resolves this repo from source by branch
name and raises its floor to the new surface's version, the clean X.Y.0 bump
lands in the feature PR.
fix: sandbox_file reads a QR-code image instead of uploading its path
@claude

claude Bot commented Oct 6, 2026

Copy link
Copy Markdown

Release PR checks out against AGENTS.md: develop → master, version is a clean 4.7.0 in pyproject.toml, [tool.bumpversion] and __init__.py, and the new surface (refang module, refang_iocs=False, with_artifacts=False) is all opt-in, so a minor bump is right. I found two things to handle before merging:

  1. Merge gate (correctness). specs/03-endpoints.md says: "this SDK version must not be released ahead of that server". With an older server, search_by_ioc(with_artifacts=True) raises a TypeError because Metadata(<sha256 str>) fails on self.artifact.get. Merging this PR publishes to PyPI right away, so please confirm the artifact-index with_artifacts change is live in prod before merging. Having it under "Requires" isn't enough. The CLI 4.6.0 release depends on this one, so the same gate applies to it.

  2. Spec drift: specs/05-downstream-contract.md gives the wrong version. 4.6.0 was never published (see the PR body), but the spec still says:

    • L219: "refang at its own edge (added in 4.6.0, opt-in)"
    • L233: "4.6.0 falls under the 'new optional keyword argument …' row"

    Both should say 4.7.0, because downstream users will check this version to see when the surface appeared. 03-endpoints.md already says with_artifacts arrived in 4.7.0. The fix belongs on develop as a docs PR, or as a commit on this branch if you'd rather not do another develop round-trip.

Nothing else needs action. The 99-open-questions.md entry already records that with_artifacts isn't pinned live yet.

@vhmartinezm
vhmartinezm requested review from kyle-buchmiller, mjbradford89 and sbneto and removed request for kyle-buchmiller October 6, 2026 17:15
@vhmartinezm
vhmartinezm merged commit 4925f12 into master Oct 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants