Skip to main content

Python SDK for the Threat.Zone malware analysis platform

Project description

Threat.Zone Python SDK

Official Python SDK for the Threat.Zone malware analysis platform. Targets the Threat.Zone Public API, ships fully typed Pydantic v2 models for every endpoint, and exposes both synchronous (ThreatZone) and asynchronous (AsyncThreatZone) clients with identical method surfaces. Requires Python 3.10+.

[!WARNING]

⚠️ Version Compatibility Notice

This SDK requires Threat.Zone v3.2.0 or later.

This SDK targets the Public API shipped with Threat.Zone v3.2.0. It will not function correctly against earlier versions of the platform.

Running an older Threat.Zone version? Pin to a pre-v3.2.0 release of this SDK, or coordinate with your admin to upgrade the platform first.

Install

pip install threatzone
# or
uv add threatzone

Configure

from threatzone import ThreatZone

client = ThreatZone(api_key="<your-api-key>")

The client reads THREATZONE_API_KEY from the environment, so ThreatZone() is enough if you export it. The default base_url is https://app.threat.zone/public-api.

For on-prem or local development, pass base_url explicitly:

client = ThreatZone(
    api_key="<your-api-key>",
    base_url="https://threatzone.your-company.internal/public-api",
)

The base_url must include the /public-api suffix. For self-signed certs, leave verify_ssl=False (the default).

Quick usage

from pathlib import Path

account = client.get_user_info()
print(f"Workspace: {account.workspace_name}")

submission = client.create_sandbox_submission(Path("./sample.exe"))
completed = client.wait_for_completion(submission.uuid, timeout=600)
print(f"Verdict: {completed.level}")
print(f"MITRE techniques: {completed.mitre_techniques}")
  • Pass auto_select_environment=True to let the server route the sample to a Windows / Linux / macOS / Android environment based on its detected type. See recipe 14.

Filename canonicalisation (dynamic_mimetype_check)

By default the server appends the real (Magika-detected) extension to the persisted filename when it disagrees with what the file was named. Opt out per submission — sandbox / open-in-browser take it as a metafields key, static / CDR take it as a top-level kwarg:

# sandbox / open-in-browser → inside metafields
client.create_sandbox_submission(
    "./malware.txt",
    environment="w10_x64",
    metafields={"dynamic_mimetype_check": False, "timeout": 120},
)

# static / CDR → top-level kwarg
client.create_static_submission("./malware.txt", dynamic_mimetype_check=False)
client.create_cdr_submission("./report.doc", dynamic_mimetype_check=False)

See Recipe 13 for the full breakdown including reading back FileInfo.is_mimetype_checked after the submission completes.

Core concepts

  • Submissions — one analysis target (file or URL) with a stable uuid, a workspace owner, a private flag, and up to four reports.
  • Reportsdynamic, static, cdr, url_analysis. Independent; poll each via submission.is_complete() / submission.has_errors(). Every report carries its own status, level, score.
  • Observation streams — the dynamic report produces indicators (rule hits), iocs (concrete IOC values), behaviours (OS-level operations — the os query param is required), and syscalls (raw log lines).
  • Artifacts — files produced or extracted during analysis; keyed by hashes.sha256.
  • Access control — public submissions are visible to every API key; private submissions are workspace-scoped. Cross-workspace reads of private submissions raise PermissionDeniedError (403).

Async

AsyncThreatZone mirrors ThreatZone method-for-method:

import asyncio
from threatzone import AsyncThreatZone

async def main() -> None:
    async with AsyncThreatZone(api_key="<your-api-key>") as client:
        account = await client.get_user_info()
        print(account.email)

asyncio.run(main())

Use asyncio.gather() for concurrent fan-out. See recipe 10.

Errors

Every error inherits from ThreatZoneError. HTTP errors additionally inherit from APIError and carry the parsed error envelope on .body, with the structured code at .body["code"]. Branch on .code, never on .message.

Status Exception When
ThreatZoneError Base class. Catch for a single safety net.
APIError HTTP error base class. .status_code, .body, .response.
400 BadRequestError INVALID_UUID, INVALID_QUERY_PARAM.
401 AuthenticationError Missing/invalid API key.
402 PaymentRequiredError Workspace subscription inactive.
403 PermissionDeniedError Cross-workspace private read, or module not enabled.
404 NotFoundError Submission, artifact, or media missing.
409 ReportUnavailableError Required report not yet available. Carries .code, .submission_uuid, .required_report, .current_status, .available_reports.
429 RateLimitError API quota exceeded. Has .retry_after.
5xx InternalServerError Server-side failure.
TimeoutError HTTP request timed out.
ConnectionError Network-level failure.
AnalysisTimeoutError wait_for_completion() exceeded its timeout. .uuid, .elapsed.
202 YaraRulePendingError YARA rule generation still in progress. .retry_after.
from threatzone import ReportUnavailableError, ThreatZone

try:
    indicators = client.get_indicators("00000000-0000-0000-0000-000000000000")
except ReportUnavailableError as exc:
    if exc.code == "DYNAMIC_REPORT_UNAVAILABLE":
        print(f"Dynamic report not ready (status: {exc.current_status}).")

See recipe 9 for the full discrimination pattern and recipe 7 for the recommended retry loop.

Documentation

Document Purpose
Recipes Runnable examples covering common tasks.
Type Reference Pydantic model overview grouped by feature area.

Testing utilities

The SDK ships a stateful in-process fake Threat.Zone API for consumer-side tests:

from threatzone import ThreatZone
from threatzone.testing import FakeThreatZoneAPI, scenarios

fake = FakeThreatZoneAPI()
scenarios.seed_malicious_pe(fake, sha256="a" * 64)

client = ThreatZone(
    api_key="test-key",
    base_url="https://fake.threat.zone/public-api",
    http_client=fake.as_httpx_client(),
)

Available scenarios: seed_malicious_pe, seed_benign_document, seed_cdr_document, seed_phishing_url, seed_static_only_submission, seed_private_cross_workspace.

Development

uv sync --all-extras --dev
uv run pytest tests/
uv run ruff check src/ tests/
uv run mypy src/threatzone
uv build

All tests run in-process against FakeThreatZoneAPI — no network, no API token. Live smoke-testing uses the scripts in examples/ with PUBLIC_API_TOKEN exported; see examples/README.md.

GitHub Actions workflows can be exercised locally via act — see .github/workflows/README.md.

Links

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

threatzone-1.0.3.tar.gz (155.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

threatzone-1.0.3-py3-none-any.whl (63.9 kB view details)

Uploaded Python 3

File details

Details for the file threatzone-1.0.3.tar.gz.

File metadata

  • Download URL: threatzone-1.0.3.tar.gz
  • Upload date:
  • Size: 155.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for threatzone-1.0.3.tar.gz
Algorithm Hash digest
SHA256 acd26696bc558fd7ddd611df405a4219e8c40d162f886bb38805870f34e82c64
MD5 6daeac4481e1d6058e10d61210f53e66
BLAKE2b-256 b5bd1e85f0fbe305ac9389ff7329a6998bc983ee29e4a72991f972416db0dd8c

See more details on using hashes here.

Provenance

The following attestation bundles were made for threatzone-1.0.3.tar.gz:

Publisher: publish.yml on Malwation/threatzone-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file threatzone-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: threatzone-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 63.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for threatzone-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 4b8ebbb424d85d0c670962bbc1f50458807c92a13f42a9f93275918df91cd196
MD5 87b424978d1fc59a55460498c8e08333
BLAKE2b-256 0e61200b72951f7874e9078bc50b540f5b69b750713cb2e92bf894ff5c20c13c

See more details on using hashes here.

Provenance

The following attestation bundles were made for threatzone-1.0.3-py3-none-any.whl:

Publisher: publish.yml on Malwation/threatzone-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page