Repository navigation
feat: support the full Hatching Triage API - #13
Merged
Merged
Conversation
Add every Triage endpoint to PyHatchingClient: sample listing (owned/org/public) and search with pagination, sample deletion, interactive profile selection, sample events, static/task/summary/kernel reports, task file, pcap, and archive downloads (plus chunked iter_* variants), profile update/delete, Yara rule deletion, and user/API key management for private instances. Fixes: - Use pydantic v2 and current aiohttp (the pins were pydantic 1 / aiohttp 3.8.4 while the code already used pydantic v2 APIs). - Import enums in the client module (the module failed to import). - Handle non-2xx API errors, and non-JSON error bodies, as ErrorResponse / PyHatchingApiError (now carrying status, error, and message). - get_sample used the hash instead of the resolved sample ID; sample_id only returned an ID when a hash had more than one sample. - update_profile used POST (the API uses PUT) and parsed the wrong model. - URL submissions dropped every option besides the URL; file uploads from a path had no filename; fetch submissions are now supported. - The overview report uses the v1 endpoint, as the official clients do. - Credentials use the API's username/password fields (user/pass still accepted). - Response models keep unknown fields, and hashes must match the whole value (ssdeep is now supported). - CLI fixes for the report, submit, and yara export commands. Add pyhatching.testing.FakeTriage, a fake Triage API server, and a test suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBLBzmMhxaREfczMZ8wa31
…r events end Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBLBzmMhxaREfczMZ8wa31
iter_sample, iter_task_file, iter_sample_archive, and kernel_report used the sample argument as an ID, unlike their non-streaming versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBLBzmMhxaREfczMZ8wa31
… suite
Rereading the public cloud API docs (tria.ge/docs/cloud-api/) surfaced
endpoints and fields pyhatching was missing:
- GET /resources (get_resources) and GET /geolocations (get_geolocations)
- GET /stats/user and /stats/org (user_stats, org_stats)
- GET /org/settings and PUT /org/settings/magic (get_org_settings,
set_magic_link)
- GET /samples/{id}/magic (magic_token)
- report_urlscan.json (urlscan_report) and a task_screenshot helper
- the import submission kind, the vpn network option, and
defaults.geolocation / profile geolocation fields
- user name/role/email_confirmed fields (first_name/last_name are
deprecated by the API), username OR email on create_user
- newer search tags (clipper, collection, crypter, exploiter, fakeav,
spam, spreader) and user management error names
FakeTriage serves all of the new endpoints and the fake suite covers them.
tests/test_live.py is a new live suite that runs against the real public
API when HATCHING_TOKEN is set (read-only by default; HATCHING_LIVE_WRITE
and HATCHING_LIVE_SUBMIT gate the mutating tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBLBzgsLYJhWB4Z8ZS1ejz
The public /samples listing omits tasks, so the live suite never found a sample with a behavioral task and skipped every report test. Fetch the full sample for candidates without tasks. The live API also answers an unknown task report with NOT_AVAILABLE, so add it to ErrorNames and accept it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBLBzmCN8e76GcUB5rEPEL
Replace the benign example.com URL submission with a resubmission of a recent reported public file sample whose Triage score is above 1 (not clean). The sample is downloaded in memory, its sha256 checked against the summary, and uploaded again as a new file sample. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBLBzmCN8e76GcUB5rEPEL
HATCHING_LIVE_SUBMIT now always resubmits SUBMIT_SHA256 (Triage score 10/10) instead of picking a recent flagged public sample, so the opt-in test never makes a new file public. The test asserts the pinned sample still exists with score 10 and that the download matches its sha256. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBLBzmCN8e76GcUB5rEPEL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings
PyHatchingClientup to the whole Triage API, fixes several existing bugs, and adds a fake Triage server plus the first test suite. This is the client the newslib.hatchingStorm service in gormaniac/stormsvcs is built on.New endpoints
owned/org/public) and search (samples_page,search_page,get_samples,iter_samples,iter_search, andsearchwith alimit),delete_sample,set_sample_profilefor interactive submissions, andfetchandimportsubmissions.sample_eventsandsamples_eventsstream status changes as NDJSON.static_report,task_report,sample_summary,urlscan_report, andkernel_report, which picks the kernel log for the task's platform.overviewnow uses/v1, as the official clients do.task_file,task_pcap,task_screenshot,sample_archive(tar/zip), and chunkediter_*versions of every download.delete_profile,delete_rule, andupdate_profilenow uses PUT. Profiles and submission defaults support thevpnnetwork option with ageolocation.get_resources,get_geolocations,user_stats, andorg_stats.magic_tokenfor samples, plusget_org_settingsandset_magic_link.get_users,get_user,create_user(username or email, with the API'sname/role/email_confirmedfields),delete_user,get_user_apikeys,create_user_apikey, anddelete_user_apikey.PRIVATE_URL, plusapi_pathandtrust_env.Fixes
import pyhatchingfailed becauseenumswas never imported.pydantic>=2.5,<3andaiohttp>=3.9,<4.ErrorResponse(or aPyHatchingApiErrorwhenraise_on_api_erris set). Both carrystatus,error, andmessage.ErrorNamesgainsNOT_AVAILABLE, which the live API returns for a report of a task that doesn't exist.get_samplesent the hash instead of the resolved sample ID.sample_idonly returned an ID when a hash matched more than one sample.Credentialsnow uses the API'susername/passwordfields.user/passare still accepted.samples report,samples submit, andyara export.Testing
pyhatching.testing.FakeTriageis a fake Triage server with canned data that records every request, now covering every endpoint above. Projects built on pyhatching can reuse it for their own tests.tests/test_client.pycovers every endpoint against it. CI now runs pytest.tests/test_live.pyis a live suite that runs against the real public API whenHATCHING_TOKENis set:/sampleslisting leaves outtasks, so the suite fetches each candidate sample in full to find one.HATCHING_LIVE_WRITE=1adds the Yara rule and profile write tests. They create objects namedpyhatching-livetest*and delete them afterwards.HATCHING_LIVE_SUBMIT=1resubmits one pinned sample,SUBMIT_SHA256(59f22c39…dbbc7). It is already public on tria.ge and scored 10/10, so the test never makes a new file public. The test fails before submitting if the sample is missing, its score isn't 10, or the download doesn't match the hash. Each run adds a public sample that can't be deleted.org_stats, which the test account isn't allowed to read (UNAUTHORIZED).Notes
importkind, and VPN geolocation coverage came from.make releasecan do the bump.Pipfile.lockis out of date.pipenv install -din CI will re-lock it.search()takeslimit(default 20) and pages through results, where it used to return only the first page.ErrorResponse.erroris now astrinstead of an enum.update_profilereturnsNoneon success.🤖 Generated with Claude Code
https://claude.ai/code/session_01KBLBzmCN8e76GcUB5rEPEL