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.1.0.tar.gz (162.1 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.1.0-py3-none-any.whl (67.4 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for threatzone-1.1.0.tar.gz
Algorithm Hash digest
SHA256 46d2cefab3910ff7d83c17cb6fa791b70b028815228fb02fd70e08939c3d8fa5
MD5 1855ca370d8837e7e8ba5c2c230e0145
BLAKE2b-256 a5fead59a44046b4aba22ddc90740b5c2adf0b87a1c5f50fbbe61482c5b10107

See more details on using hashes here.

Provenance

The following attestation bundles were made for threatzone-1.1.0.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.1.0-py3-none-any.whl.

File metadata

  • Download URL: threatzone-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 67.4 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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d34edbe7f559d988731538e08c3d35fcdf865428935366183f734d5f89f1c443
MD5 8a2b10fabcabce921533fca3fb0c7894
BLAKE2b-256 0ee9c2f717fa0349f084f77f9073351a00d7038fbaacaca8f79193879e4bab1a

See more details on using hashes here.

Provenance

The following attestation bundles were made for threatzone-1.1.0-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