Skip to main content

Python wrapper for the AttackForge API

Project description

PyAttackForge

A lightweight Python library for interacting with the AttackForge API.


Features

  • Create and fetch projects
  • Manage assets
  • Submit vulnerabilities
  • Create findings from existing writeups by passing a writeup_id
  • Upload evidence to findings or testcases
  • Update/assign testcases to link findings or add notes
  • Link vulnerabilities to testcases via the client
  • Dry-run mode for testing

Install

mkdir PyAttackForgeEnv
cd PyAttackForgeEnv
virtualenv venv
source ./venv/bin/activate
pip install git+https://github.com/Tantalum-Labs/PyAttackForge.git

Use

from pyattackforge import PyAttackForgeClient

# Initialize client - Note: Make sure to set your AttackForge URL and API Key
client = PyAttackForgeClient(api_key="your-api-key", base_url="https://demo.attackforge.com", dry_run=False)

# Create a project
project = client.create_project("My Project", scope=["Asset1", "Asset2"])

## Create a security finding (vulnerability)
client.create_vulnerability(
    project_id="abc123",
    title="Open SSH Port",
    affected_assets=[{"name": "ssh-prod-1"}],
    priority="High",
    likelihood_of_exploitation=10,
    description="SSH port 22 is open to the internet.",
    attack_scenario="An attacker can brute-force SSH credentials.",
    remediation_recommendation="Restrict SSH access to trusted IPs.",
    steps_to_reproduce="1. Scan the host\n2. Observe port 22 is open",
    writeup_id="68e92c7a821c05c8405a8003",  # optional: use an existing writeup
    tags=["ssh", "exposure"],
    notes=["Observed on 2025-09-09"],
    is_zeroday=False,
    is_visible=True
)

Creating Security Findings

To create a security finding (vulnerability) in AttackForge, use the create_vulnerability method:

client.create_vulnerability(
    project_id="abc123",
    title="Open SSH Port",
    affected_assets=[{"name": "ssh-prod-1"}],
    priority="High",
    likelihood_of_exploitation=10,
    description="SSH port 22 is open to the internet.",
    attack_scenario="An attacker can brute-force SSH credentials.",
    remediation_recommendation="Restrict SSH access to trusted IPs.",
    steps_to_reproduce="1. Scan the host\n2. Observe port 22 is open",
    writeup_id="68e92c7a821c05c8405a8003",  # optional: reuse an existing writeup
    tags=["ssh", "exposure"],
    notes=["Observed on 2025-09-09"],
    is_zeroday=False,
    is_visible=True
)

Creating a finding from an existing writeup

If you already have a writeup/library entry and just need to create a finding bound to it, you can either pass writeup_id to create_vulnerability (as above) or call create_finding_from_writeup directly. Prefer the 24-character writeup id (id / _id); if only a numeric reference_id is available, use that. You can also specify the library key (e.g., approved_writeups, Main Vulnerabilities):

client.create_finding_from_writeup(
    project_id="abc123",
    writeup_id="68e92c7a821c05c8405a8003",  # writeup id
    library="approved_writeups",             # optional: library key/name
    priority="High",
    affected_assets=[{"name": "ssh-prod-1"}],
    linked_testcases=["5e8017d2e1385f0c58e8f4f8"],  # optional: link testcases at creation
    likelihood_of_exploitation=5,
    steps_to_reproduce="1. Do something\n2. Observe result",
    notes=[{"note": "Created via API", "type": "PLAINTEXT"}],
    tags=["automation"]
)

Evidence and testcase helpers

Upload evidence to an existing finding:

client.upload_finding_evidence(
    vulnerability_id="6768d29db1782d7362a2df5f",
    file_path="evidence.png"
)

Upload evidence to a testcase:

client.upload_testcase_evidence(
    project_id="abc123",
    testcase_id="5e8017d2e1385f0c58e8f4f8",
    file_path="testcase-evidence.png"
)

Add a note to a finding (deduplicates by note text):

client.add_note_to_finding(
    vulnerability_id="6768d29db1782d7362a2df5f",
    note="Observed during retest on 2025-09-19."
)

Add a note/update to a testcase (PUT to the testcase endpoint):

client.add_note_to_testcase(
    project_id="abc123",
    testcase_id="5e8017d2e1385f0c58e8f4f8",
    note="Observed during retest on 2025-09-19.",
    status="Tested"  # optional
)

Associate findings to a testcase:

client.assign_findings_to_testcase(
    project_id="abc123",
    testcase_id="5e8017d2e1385f0c58e8f4f8",
    vulnerability_ids=["66849b77950ab45e68fc7b48", "6768d29db1782d7362a2df5f"],
    additional_fields={"status": "Tested"} # optional
)

Or link from the vulnerability side using its update endpoint:

client.link_vulnerability_to_testcases(
    vulnerability_id="69273ef0f4a7c85d03930667",
    testcase_ids=["5e8017d2e1385f0c58e8f4f8"],
    project_id="abc123",  # optional
)

Fetch project testcases:

testcases = client.get_testcases("abc123")

Fetch a single testcase (if supported in your tenant):

testcase = client.get_testcase("abc123", "5e8017d2e1385f0c58e8f4f8")

Merge and add findings to a testcase in one call:

client.add_findings_to_testcase(
    project_id="abc123",
    testcase_id="5e8017d2e1385f0c58e8f4f8",
    vulnerability_ids=["69273ef0f4a7c85d03930667"],
    additional_fields={"status": "Tested"} # optional
)

Parameters:

  • project_id (str): The project ID.
  • title (str): The title of the finding.
  • affected_assets (list): List of affected assets (e.g., [{"name": "host1"}]).
  • priority (str): The priority (e.g., "Critical", "High", "Medium", "Low").
  • likelihood_of_exploitation (int): Likelihood of exploitation (e.g., 10).
  • description (str): Description of the finding.
  • attack_scenario (str): Attack scenario details.
  • remediation_recommendation (str): Remediation recommendation.
  • steps_to_reproduce (str): Steps to reproduce the finding.
  • writeup_id (str, optional): Existing writeup/library reference ID to use directly.
  • tags (list, optional): List of tags.
  • notes (list, optional): List of notes.
  • is_zeroday (bool, optional): Whether this is a zero-day finding.
  • is_visible (bool, optional): Whether the finding is visible.
  • import_to_library (str, optional): Library to import to.
  • import_source (str, optional): Source of import.
  • import_source_id (str, optional): Source ID for import.
  • custom_fields (list, optional): List of custom fields.
  • linked_testcases (list, optional): List of linked testcases.
  • custom_tags (list, optional): List of custom tags.

See the source code for full details and docstrings.


API Reference

PyAttackForgeClient

  • __init__(api_key: str, base_url: str = ..., dry_run: bool = False)
  • get_assets() -> dict
  • get_asset_by_name(name: str) -> dict or None
  • create_asset(asset_data: dict) -> dict
  • get_project_by_name(name: str) -> dict or None
  • get_project_scope(project_id: str) -> set
  • update_project_scope(project_id: str, new_assets: list) -> dict
  • create_project(name: str, **kwargs) -> dict
  • update_project(project_id: str, update_fields: dict) -> dict
  • create_vulnerability( project_id: str, title: str, affected_assets: list, priority: str, likelihood_of_exploitation: int, description: str, attack_scenario: str, remediation_recommendation: str, steps_to_reproduce: str, tags: Optional[list] = None, notes: Optional[list] = None, is_zeroday: bool = False, is_visible: bool = True, import_to_library: Optional[str] = None, import_source: Optional[str] = None, import_source_id: Optional[str] = None, custom_fields: Optional[list] = None, linked_testcases: Optional[list] = None, custom_tags: Optional[list] = None, writeup_custom_fields: Optional[list] = None, ) -> dict
  • create_finding_from_writeup(project_id: str, writeup_id: str, priority: str, affected_assets: Optional[list] = None, linked_testcases: Optional[list] = None, **kwargs) -> dict
  • get_findings_for_project(project_id: str, priority: Optional[str] = None) -> list
  • upsert_finding_for_project(...)
  • get_vulnerability(vulnerability_id: str) -> dict
  • add_note_to_finding(vulnerability_id: str, note: Any, note_type: str = "PLAINTEXT") -> dict
  • upload_finding_evidence(vulnerability_id: str, file_path: str) -> dict
  • upload_testcase_evidence(project_id: str, testcase_id: str, file_path: str) -> dict
  • get_testcases(project_id: str) -> list
  • get_testcase(project_id: str, testcase_id: str) -> dict or None
  • link_vulnerability_to_testcases(vulnerability_id: str, testcase_ids: List[str], project_id: Optional[str] = None) -> dict
  • assign_findings_to_testcase(project_id: str, testcase_id: str, vulnerability_ids: List[str], existing_linked_vulnerabilities: Optional[List[str]] = None, additional_fields: Optional[Dict[str, Any]] = None) -> dict
  • add_findings_to_testcase(project_id: str, testcase_id: str, vulnerability_ids: List[str], additional_fields: Optional[Dict[str, Any]] = None) -> dict
  • add_note_to_testcase(project_id: str, testcase_id: str, note: str, status: Optional[str] = None) -> dict

See the source code for full details and docstrings.


Contributing

Contributions are welcome! Please open issues or submit pull requests via GitHub.

  • Ensure code is PEP8-compliant and includes docstrings and type hints.
  • Add or update tests for new features or bugfixes.
  • Do not commit API keys or other secrets.

Security

Never commit your API keys or other sensitive information to version control.


License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).

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

pyattackforge-0.1.7.tar.gz (31.4 kB view details)

Uploaded Source

Built Distribution

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

pyattackforge-0.1.7-py3-none-any.whl (29.4 kB view details)

Uploaded Python 3

File details

Details for the file pyattackforge-0.1.7.tar.gz.

File metadata

  • Download URL: pyattackforge-0.1.7.tar.gz
  • Upload date:
  • Size: 31.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for pyattackforge-0.1.7.tar.gz
Algorithm Hash digest
SHA256 8c76eee6feed81d216aa78acf554a0531c8274aa0b56712fb0d7044bbd4af3c7
MD5 d50bbe7b847f9bff9ae31ca0971d8ea3
BLAKE2b-256 9a9ac84c89b2b0f912a9882650ac3301917cd4fa23e76df5ce5522610911402e

See more details on using hashes here.

File details

Details for the file pyattackforge-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: pyattackforge-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 29.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for pyattackforge-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 f81eb660ab2cca9c62995517afd8976988b0ab37cf59bcb7adee49adcc5652a4
MD5 c2a0d39c25d9c7786932022c131ece6d
BLAKE2b-256 ba001147dbdf16fcd301742aaa5c3000163af7adac0313fe49d4492556de0bca

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