Skip to content

feat: search_by_ioc(with_artifacts=) returns artifact metadata rows - #329

Merged
vhmartinezm merged 5 commits into
developfrom
DN-8545-ioc-search-artifacts
Sep 30, 2026
Merged

vhmartinezm merged 5 commits into
developfrom
DN-8545-ioc-search-artifacts

Conversation

@vhmartinezm

Copy link
Copy Markdown
Contributor

Summary

search_by_ioc(...) (sync and async) gains an optional with_artifacts=False keyword. When it is True, the SDK sends with_artifacts=1 and parses each result row as a Metadata resource instead of a sha256 string. That exposes the server's new IOC → artifacts rows.

Semantics

  • The default path is unchanged. Without the keyword, the request and the parsed results are exactly as before, including a call with no IOC term. The SDK adds no client-side validation.

  • The field set is owned by the server:

    • artifact.*
    • the scan summary, including scan.first_scan.created (so first_seen is populated)
    • hash.ssdeep and hash.tlsh
    • polyunite.malware_family

    Fields outside that set, such as the strings.* lists, come back as None.

  • Errors. With with_artifacts=True and no IOC term, the server answers 400, which the caller sees as the usual typed exception.

  • Encoding. The flag is sent as 1 because the server's boolean parser accepts 0/1/true/false, and the SDK would otherwise stringify True. A test pins this.

Version

This PR bumps the version to 4.7.0 (develop already declares 4.6.0 for unreleased work). That is the standing exception in AGENTS.md: the CLI's CI installs this SDK from the same-named branch, so the CLI's polyswarm_api>=4.7.0 floor must name a version this branch declares. specs/05-downstream-contract.md invariant 6 predates that exception, which 4.4.0, 4.5.0 and 4.6.0 already used.

Requires / deploy order

  • Release this only after the server supports with_artifacts. A server that ignores the flag returns sha256 strings, and the Metadata parse then fails loudly.
  • The CLI companion PR pins >=4.7.0.

Tests

  • test/ioc_search_test.py uses respx and runs against both the sync and async clients:
    • builder and pass-through for the new keyword;
    • rows parsed as Metadata, with a fixture that mirrors the server's field set;
    • a bare call still sends the same request as before.
  • A live recorded exchange is still pending until the server leg reaches the e2e stack; it is tracked in specs/99-open-questions.md.
  • The sync client copy is regenerated.

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.
@claude

claude Bot commented Sep 29, 2026

Copy link
Copy Markdown

Code, codegen mirror, tests and the 03/04/99 spec updates look correct. The base is develop. One thing needs action:

Spec drift: specs/05-downstream-contract.md invariant 6. The invariant still reads "Version bumps go on the develop → master step, not feature PRs." This PR bumps to 4.7.0 in a feature PR. The PR body says the invariant "predates" the AGENTS.md standing exception, which 4.4.0, 4.5.0 and 4.6.0 have also used. Per AGENTS.md ("if a PR drifts from the spec, the spec is wrong until proven otherwise … update the spec in the same PR"), please amend invariant 6 here. It should carry the same carve-out AGENTS.md states: a sibling's from-source CI needs a floor naming the new version, and that version must be a clean X.Y.0. Otherwise the spec and the release process keep disagreeing.

…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.
@claude

claude Bot commented Sep 29, 2026

Copy link
Copy Markdown

Checked against AGENTS.md and specs/02, 03, 04, 05 and 99. I found no correctness or spec-drift issues:

  • The 1-instead-of-True reasoning is right, because core._normalise_bool_params does render bools as 'True'.
  • The builder routing, the Metadata parser swap and the sync mirror all check out.
  • The version bump to 4.7.0 fits the standing exception, and invariant 6 in spec 05 has been updated to match.
  • The respx stand-in is tracked in 99-open-questions and has a concrete action for removing it.

One small thing (gitflow/hygiene): the head branch DN-8545-… carries an internal ticket ID. A merge commit will write it into the public develop history ("Merge pull request #329 from polyswarm/DN-8545-…"). AGENTS.md keeps ticket IDs out of history. Either squash-merge, or edit the merge commit message so the branch name is left out. Renaming the branch would also mean moving the CLI companion PR, whose CI resolves this SDK by branch name.

@vhmartinezm
vhmartinezm requested a review from sbneto September 29, 2026 19:42
@claude

claude Bot commented Sep 29, 2026

Copy link
Copy Markdown

Clean against AGENTS.md and specs: the builder and parser change is correct on both transports, the sync mirror is regenerated, specs 03/04/05/99 are updated, the base is develop, and the 4.7.0 bump fits the standing floor exception.

One gitflow nit: the head branch DN-8545-ioc-search-artifacts carries an internal ticket ID. With a merge commit (rather than squash), that name lands in public history as "Merge pull request #329 from …/DN-8545-…". AGENTS.md says ticket IDs stay out of history, so squash-merge this PR or rename the branch.

@vhmartinezm
vhmartinezm merged commit a830109 into develop Sep 30, 2026
2 checks passed
@vhmartinezm
vhmartinezm deleted the DN-8545-ioc-search-artifacts branch September 30, 2026 21:50
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