Skip to main content

A Python client and wrapper around the Cyver API.

Project description

cyver-reporting

A Python client library for the Cyver Pentest Management API. Developed and maintained by penetration testers at Thoropass.


Features

  • Two authentication modes — username/password login with email-based 2FA and automatic token refresh, or stateless static API key via X-API-Key.
  • Portal validation — availability probe before credentials are ever sent; raises a clear error if the portal is inactive or unreachable. Skipped automatically when a local cache confirms the portal was active in a previous run.
  • Pentester role (PentesterSession) — manage clients, assets, users, teams, projects, continuous projects, findings, and reference data.
  • Client role (ClientSession) — read projects and continuous projects scoped to the authenticated client user.
  • Data buildersCyverClient, CyverAsset, CyverFinding, CyverFindingEvidence, CyverFindingCustomField, and companion classes provide a validated, fluent interface for constructing API request bodies, catching type and format errors before any request is dispatched.
  • Typed return valuesget_clients(), get_all_clients(), get_client_by_id(), create_client(), update_client_by_id(), get_assets_by_client_id(), get_all_assets_by_client_id(), create_client_asset(), and update_client_asset_by_id() all return CyverClient / CyverAsset instances. get_users(), get_all_users(), and get_user_by_id() return CyverUser instances. create_finding() and update_finding_by_id() return CyverFinding instances. The id property on each object carries the server-assigned UUID.
  • Transparent pagination — every list endpoint ships with a companion get_all_*() generator that pages through all results automatically.
  • Secure token caching — OAuth tokens are encrypted with a PBKDF2-derived Fernet key and stored in the OS user-cache directory with owner-only file permissions. The cached portal hostname enables probe-skip on warm starts and stale cache cleanup on failures. API key sessions maintain a separate lightweight cache for the same portal-validation purpose.
  • Resilient HTTP transport — automatic retries with exponential back-off on transient server errors and rate-limiting responses.
  • Thread-safe — all token state is protected by a reentrant lock, safe for use from multiple threads.
  • Context manager support — use with blocks to guarantee the underlying HTTP session is always closed.
  • Type annotations — fully annotated and ships with a py.typed marker (PEP 561).

Requirements

  • Python 3.10 or later
  • A valid Cyver portal account with Pentester or Client role

Installation

pip install cyver-reporting

Quick Start

Pentester — username/password

from CyverReporting import PentesterSession, _project_status

with PentesterSession() as client:
    client.authenticate("app.cyver.io", "user@example.com", "s3cr3t")

    # Iterate over every active project without managing pagination manually
    for project in client.get_all_projects(filter_status=_project_status.testing):
        print(project["name"])

    # Fetch all critical and high findings for a specific project
    for finding in client.get_all_findings(
        project_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        severity_list=["Critical", "High"],
    ):
        print(finding["title"], finding["severity"])

Pentester — static API key

Pass portal and api_key to the constructor. The session is immediately ready — no call to authenticate() is needed or allowed:

from CyverReporting import PentesterSession, _project_status

with PentesterSession(portal="app.cyver.io", api_key="sk-...") as client:
    for project in client.get_all_projects(filter_status=_project_status.testing):
        print(project["name"])

Client — username/password

from CyverReporting import ClientSession

with ClientSession() as client:
    client.authenticate("app.cyver.io", "user@example.com", "s3cr3t")

    for project in client.get_all_projects():
        print(project["name"])

Client — static API key

from CyverReporting import ClientSession

with ClientSession(portal="app.cyver.io", api_key="sk-...") as client:
    for project in client.get_all_projects():
        print(project["name"])

Two-Factor Authentication

When 2FA is enabled on the account, a verification code is sent to the registered e-mail address and a Cyver2FARequired is raised on the first call:

from CyverReporting import PentesterSession
from CyverReporting.Exceptions import Cyver2FARequired

client = PentesterSession()

try:
    client.authenticate("app.cyver.io", "user@example.com", "s3cr3t")
except Cyver2FARequired:
    code = input("Enter the 2FA code sent to your email: ")
    client.authenticate("app.cyver.io", "user@example.com", "s3cr3t",
                        verification_code=code)

Creating a Client (data builder)

Use CyverClient to construct and validate the request body before calling create_client(). All setter methods return self for fluent chaining:

from CyverReporting import PentesterSession, CyverClient, CyverClientInformation, CyverClientAddress

with PentesterSession(portal="app.cyver.io", api_key="sk-...") as pentester:
    new_client = (
        CyverClient("Acme Corp")
        .set_status(1)
        .set_client_number("CLI-0042")
        .set_client_information(
            CyverClientInformation()
            .set_company_name("Acme Corporation")
            .set_website("https://acme.example.com")
            .set_address(
                CyverClientAddress()
                .set_street("123 Main St")
                .set_city("San Francisco")
                .set_country("United States")
            )
        )
    )
    created = pentester.create_client(new_client)
    print(created.id)  # server-assigned UUID

Creating a Client Asset (data builder)

Use CyverAsset to construct and validate an asset request body. Each asset type imposes additional required fields (e.g. url for Web Application, ip for Network) that are enforced before the request is dispatched:

from CyverReporting import PentesterSession, CyverAsset

with PentesterSession(portal="app.cyver.io", api_key="sk-...") as pentester:
    asset = (
        CyverAsset("Main Portal")
        .set_url("https://portal.example.com")
        .set_type(2)           # Web Application — also validates url is set
        .set_environment(3)    # Production
        .set_hosting_type(1)   # Public Cloud (Azure)
        .set_public_facing(2)  # External
    )
    created = pentester.create_client_asset(client_id, asset)
    print(created.id)  # server-assigned asset UUID

Creating a Finding (data builder)

Use CyverFinding to construct and validate a finding request body. name is the only required field; all other fields are optional:

from CyverReporting import PentesterSession, CyverFinding, _finding_severity, _finding_status

with PentesterSession(portal="app.cyver.io", api_key="sk-...") as pentester:
    finding = (
        CyverFinding("SQL Injection in Login Form")
        .set_severity(_finding_severity.high)
        .set_status(_finding_status.draft)
        .set_cvss31_vector("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H")
        .set_cvss31_score(9.8)
        .add_cwe("CWE-89")
    )
    created = pentester.create_finding(finding)
    print(created.id)  # server-assigned UUID

Documentation

Document Description
Authentication Login, API key auth, portal probe, 2FA, token caching, and session lifecycle
Pentester API All PentesterSession methods and parameters
Client API All ClientSession methods and parameters
Exceptions Exception hierarchy and error handling guide

API Coverage

The library covers 54 of 69 Cyver API V2.2 endpoints (78%). The remaining endpoints are write-heavy operations (resource creation, full updates, file uploads, report downloads) whose exact request body schemas require additional information beyond what the public API specification provides.

Section Implemented Total
Authentication 3 3
Clients 8 9
Users 3 5
Teams 3 5
Projects (Pentester) 10 16
Continuous Projects (Pentester) 11 14
Findings 6 7
Reference Data 6 6
Projects (Client) 2 2
Continuous Projects (Client) 2 2

Project Structure

CyverReporting/
├── __init__.py               # Public exports: CyverSession, PentesterSession, ClientSession, exceptions, data builders, constants
├── Session.py                # Base session: authentication, portal probe, token cache, HTTP transport
├── PentesterSession.py       # PentesterSession — pentester-role API methods
├── ClientSession.py          # ClientSession — client-role API methods
├── DataTypes/                # Cyver* data-builder classes (CyverClient, CyverAsset, CyverUser, …)
│   ├── __init__.py
│   ├── CyverClient.py
│   ├── CyverAsset.py
│   ├── CyverUser.py
│   ├── CyverFinding.py
│   ├── CyverFindingEvidence.py
│   ├── CyverFindingCustomField.py
│   └── …
├── Utils/                    # Private helpers, compiled regexes, format validators, and CY_* static constant classes
│   ├── __init__.py
│   ├── RegExPatterns.py
│   ├── StaticAliases.py
│   ├── FormatValidators.py
│   └── FieldExtractors.py
├── Exceptions/               # Full exception hierarchy (CyverError, CyverAuthError, CyverDataValidationError, …)
│   ├── __init__.py
│   ├── Base.py
│   ├── Authentication.py
│   ├── Connection.py
│   └── Validation.py
└── py.typed                  # PEP 561 marker
tests/
├── conftest.py               # Shared fixtures and helpers
├── test_session.py           # Session, authentication, portal probe tests
├── test_client.py            # ClientSession tests
├── test_pentester.py         # PentesterSession tests
├── test_validators_client.py # CyverClient / CyverClientInformation / CyverClientAddress tests
├── test_validators_user.py   # CyverUser tests
├── test_finding_evidence.py  # CyverFindingEvidence tests
├── test_finding_custom_field.py # CyverFindingCustomField tests
├── test_finding.py           # CyverFinding tests
├── test_exceptions.py        # Exception hierarchy tests
└── test_utils.py             # Format validator tests

Error Handling

from CyverReporting import PentesterSession, _project_status
from CyverReporting.Exceptions import (
    Cyver2FARequired,
    CyverAuthError,
    CyverDataValidationError,
    CyverHTTPError,
)

client = PentesterSession()

try:
    client.authenticate(portal, username, password)
except Cyver2FARequired:
    # 2FA code required — re-call authenticate() with verification_code=
    code = input("Enter the 2FA code sent to your email: ")
    client.authenticate(portal, username, password, verification_code=code)
except CyverAuthError as e:
    if e.reason == "portal_inactive":
        print(f"Portal '{portal}' does not exist or is not active.")
    elif e.reason == "portal_unreachable":
        print(f"Portal '{portal}' could not be reached. Check connectivity.")
    else:
        print(f"Authentication failed: {e}")
except CyverDataValidationError as e:
    # Invalid argument passed to a method or data builder (wrong type, bad UUID, etc.)
    print(f"Data error on field '{e.field}' ({e.reason}): {e}")
except CyverHTTPError as e:
    # Network failure, non-200 status code, or malformed JSON response
    print(f"HTTP error: {e}")

License

MIT — see LICENSE for details.

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

cyver_reporting-0.0.1.tar.gz (55.8 kB view details)

Uploaded Source

Built Distribution

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

cyver_reporting-0.0.1-py3-none-any.whl (26.3 kB view details)

Uploaded Python 3

File details

Details for the file cyver_reporting-0.0.1.tar.gz.

File metadata

  • Download URL: cyver_reporting-0.0.1.tar.gz
  • Upload date:
  • Size: 55.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for cyver_reporting-0.0.1.tar.gz
Algorithm Hash digest
SHA256 3a79fb2f8e69d081d227cfc65666cf33510b7f7f0f17adc7ae68ba8def204cc1
MD5 02dc1d4f5899df69b3e70f51dd2d9544
BLAKE2b-256 c3937f79bbf86658e8faa423ce921668321cfe3fc1f83c2e5be294410e1656ce

See more details on using hashes here.

File details

Details for the file cyver_reporting-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for cyver_reporting-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b64ac26fec1281d86c53619e488c7480bc94bcc55b4c27e72138d82fa1b07223
MD5 9c1b3a19b2dd62ada42edcf5bdbcc2b0
BLAKE2b-256 72aa2ac1cb435ecce14179b85efea32340ddb770f344a810988fb08859e279c9

See more details on using hashes here.

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