PyAttackForge
Next-generation Python SDK for the AttackForge SSAPI (Python 3.10+).
AI Documentation
DOCUMENTATION.md is written for AI coding assistants (Codex/Claude) to implement or extend the library.
Install (local)
pip install -e .
Install (PyPI)
pip install pyattackforge
Build
python -m build --sdist --wheel
Configuration
Environment variables:
ATTACKFORGE_BASE_URLATTACKFORGE_API_KEYATTACKFORGE_TEST_PROJECT_ID(only needed for live integration tests)ATTACKFORGE_FINDINGS_VISIBLE_DEFAULT(optional, set totrueto make new findings visible; default is pending/hidden)ATTACKFORGE_FINDINGS_SUBSTATUS_KEY(optional, defaultsubstatus)ATTACKFORGE_FINDINGS_SUBSTATUS_VALUE(optional, defaultObserved)ATTACKFORGE_UI_BASE_URL(optional, UI base URL for project testcase uploads/verification)ATTACKFORGE_UI_TOKEN(optional, UI authorization token for project testcase uploads/verification)
Example .env:
ATTACKFORGE_BASE_URL=https://demo.attackforge.com
ATTACKFORGE_API_KEY=replace-me
ATTACKFORGE_TEST_PROJECT_ID=replace-me
ATTACKFORGE_FINDINGS_VISIBLE_DEFAULT=false
ATTACKFORGE_FINDINGS_SUBSTATUS_KEY=substatus
ATTACKFORGE_FINDINGS_SUBSTATUS_VALUE=Observed
ATTACKFORGE_UI_BASE_URL=https://demo.attackforge.com
ATTACKFORGE_UI_TOKEN=replace-me
Usage (sync)
from pyattackforge import AttackForgeClient
with AttackForgeClient() as client:
projects = client.projects.get_projects()
print(projects)
Touch a testcase (updates last_tested and testcase_type in project_testcase_custom_fields):
from pyattackforge import AttackForgeClient
with AttackForgeClient() as client:
client.testcases.touch_testcase("project_id", "testcase_id")
By default, the SDK merges last_tested and testcase_type into existing project testcase custom fields. Use overwrite=True to replace the list.
Default format for last_tested is YYYY-MM-DD (date picker friendly). Override by supplying timestamp=.
Default testcase_type is Security Test Case. Override by supplying testcase_type=.
Create a testcase note with optional deduplication (case-insensitive, trimmed):
from pyattackforge import AttackForgeClient
with AttackForgeClient() as client:
payload = {"note": "Observed weak password policy", "note_type": "PLAINTEXT"}
client.testcases.create_testcase_note("project_id", "testcase_id", payload, dedupe=True)
When dedupe=True, the SDK compares against notes returned by the project testcase listing. If notes are not included in that response, the check is best-effort and the note will be created.
If a UI token is configured (ATTACKFORGE_UI_TOKEN), the SDK will also check the UI notes endpoint to avoid duplicates.
Upload vulnerability evidence with FIFO retention and dedupe:
from pyattackforge import AttackForgeClient
with AttackForgeClient() as client:
client.findings.upload_vulnerability_evidence("vuln_id", "evidence.png")
# keep_last defaults to 2 (FIFO, keep most recent). Set keep_last=None to disable.
client.findings.upload_vulnerability_evidence("vuln_id", "evidence.png", dedupe=True)
For more reliable FIFO/dedupe, pass project_id so the SDK can use the project vulnerability listing (which contains evidence metadata):
client.findings.upload_vulnerability_evidence("vuln_id", "evidence.png", project_id="project_id")
Upload project testcase evidence with FIFO retention and dedupe:
from pyattackforge import AttackForgeClient
with AttackForgeClient() as client:
client.testcases.upload_testcase_file("project_id", "testcase_id", "evidence.png")
# keep_last defaults to 2 (FIFO, keep most recent). Set keep_last=None to disable.
client.testcases.upload_testcase_file("project_id", "testcase_id", "evidence.png", dedupe=True)
When a UI token is configured, FIFO cleanup for testcase evidence uses the UI delete endpoint:
/api/projects/:projectId/meta/:fileId/delete (UI expects GET/POST semantics).
Helper methods (examples):
with AttackForgeClient() as client:
project = client.projects.find_project_by_name("Tantalum Labs Fake Pentest")
suite = client.testsuites.find_testsuite_by_name("OWASP Web App Penetration Test")
if suite:
testcases = client.testsuites.get_testsuite_testcases(suite["id"])
client.link_vulnerability_to_testcases("project_id", "vuln_id", ["testcase_id"])
Group management (examples):
from pyattackforge import AttackForgeClient
with AttackForgeClient() as client:
group = client.groups.create_group({
"name": "SDK Group",
"group_owner": "Owner Name",
"primary_contact_name": "Owner Name",
"primary_contact_email": "owner@example.com",
"primary_contact_number": "0000000000",
})
group_id = group.get("group_id") or group.get("id")
client.projects.add_project_to_group("project_id", group_id)
client.groups.get_group_projects(group_id)
client.groups.get_group_vulnerabilities(group_id)
Testing
Unit tests:
python3 -m pytest
Integration tests (require env vars):
python3 -m pytest -m integration
Integration tests create and modify data in the target tenant and will attempt cleanup. They read ATTACKFORGE_BASE_URL, ATTACKFORGE_API_KEY, and ATTACKFORGE_TEST_PROJECT_ID.
Status
This is a from-scratch rewrite. See COVERAGE.md for endpoint coverage and assumptions.
Scripts
Import writeups from a JSON export:
python3 scripts/import_writeups.py --input writeups.json
By default the script will ensure custom libraries exist. If the SSAPI returns an error for a custom library key, it will create a small bootstrap writeup in that library. Use --no-ensure-library to skip.
Add missing writeup custom fields (Settings -> Writeups) based on the import file:
python3 scripts/import_writeups.py --input writeups.json --ensure-writeup-fields
Metadata
Release files for pyattackforge 0.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pyattackforge-0.2.4.tar.gz | 63.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyattackforge-0.2.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 116.7 kB
Release files / pyattackforge-0.2.4.tar.gz
| Download URL | pyattackforge-0.2.4.tar.gz |
|---|---|
| Size | 63.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1bc805cb5e530891f9c13e793a842d599172469ad94382610085ada8b20b5f20
|
|
BLAKE2b-256 checksum How to use checksums |
3d66fed943ab7fab6655ca21e54d75b5b36a99249cf26c73ebfc2c661b87d78c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|
Release files / pyattackforge-0.2.4-py3-none-any.whl
| Download URL | pyattackforge-0.2.4-py3-none-any.whl |
|---|---|
| Size | 53.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bb1a9082d594c60b2174b4e8e92307d2194a8e276b6b9f10f1dcf003879f89df
|
|
BLAKE2b-256 checksum How to use checksums |
fed24ea227380112e61ac022c5de47aeb7109a22ccf7fcfd4071df064cbae270
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|
Security for pyattackforge 0.2.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.