pyattackforge 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyattackforge/client.py CHANGED
@@ -258,59 +258,101 @@ class PyAttackForgeClient:
258
258
 
259
259
  def create_vulnerability(
260
260
  self,
261
- vulnerability_data: Dict[str, Any],
262
- auto_create_assets: bool = False,
263
- default_asset_type: str = "Placeholder",
264
- default_asset_library_ids: Optional[List[str]] = None
261
+ project_id: str,
262
+ title: str,
263
+ affected_asset_name: str,
264
+ priority: str,
265
+ likelihood_of_exploitation: int,
266
+ description: str,
267
+ attack_scenario: str,
268
+ remediation_recommendation: str,
269
+ steps_to_reproduce: str,
270
+ tags: Optional[list] = None,
271
+ notes: Optional[list] = None,
272
+ is_zeroday: bool = False,
273
+ is_visible: bool = True,
274
+ import_to_library: Optional[str] = None,
275
+ import_source: Optional[str] = None,
276
+ import_source_id: Optional[str] = None,
277
+ custom_fields: Optional[list] = None,
278
+ linked_testcases: Optional[list] = None,
279
+ custom_tags: Optional[list] = None,
265
280
  ) -> Dict[str, Any]:
266
281
  """
267
- Create a new vulnerability in AttackForge.
282
+ Create a new security finding (vulnerability) in AttackForge.
268
283
 
269
284
  Args:
270
- vulnerability_data (dict): Vulnerability details (must include 'projectId').
271
- auto_create_assets (bool, optional): If True, create missing assets automatically.
272
- default_asset_type (str, optional): Asset type for auto-created assets.
273
- default_asset_library_ids (list, optional): Library IDs for auto-created assets.
285
+ project_id (str): The project ID.
286
+ title (str): The title of the finding.
287
+ affected_asset_name (str): The name of the affected asset.
288
+ priority (str): The priority (e.g., "Critical").
289
+ likelihood_of_exploitation (int): Likelihood of exploitation (e.g., 10).
290
+ description (str): Description of the finding.
291
+ attack_scenario (str): Attack scenario details.
292
+ remediation_recommendation (str): Remediation recommendation.
293
+ steps_to_reproduce (str): Steps to reproduce the finding.
294
+ tags (list, optional): List of tags.
295
+ notes (list, optional): List of notes.
296
+ is_zeroday (bool, optional): Whether this is a zero-day finding.
297
+ is_visible (bool, optional): Whether the finding is visible.
298
+ import_to_library (str, optional): Library to import to.
299
+ import_source (str, optional): Source of import.
300
+ import_source_id (str, optional): Source ID for import.
301
+ custom_fields (list, optional): List of custom fields.
302
+ linked_testcases (list, optional): List of linked testcases.
303
+ custom_tags (list, optional): List of custom tags.
274
304
 
275
305
  Returns:
276
306
  dict: Created vulnerability details.
277
307
 
278
308
  Raises:
279
- ValueError: If 'projectId' is missing.
309
+ ValueError: If any required field is missing.
280
310
  RuntimeError: If vulnerability creation fails.
281
311
  """
282
- affected_assets = vulnerability_data.get("affected_assets", [])
283
- project_id = vulnerability_data.get("projectId")
284
- if not project_id:
285
- raise ValueError("vulnerability_data must include 'projectId'")
286
-
287
- new_asset_names = []
288
-
289
- if auto_create_assets:
290
- for asset_ref in affected_assets:
291
- asset_name = asset_ref.get("assetName")
292
- if not asset_name:
293
- continue
294
- if not self.get_asset_by_name(asset_name):
295
- logger.info("Asset '%s' not found. Creating it.", asset_name)
296
- asset_payload = {
297
- "name": asset_name,
298
- "type": default_asset_type,
299
- "external_id": asset_name,
300
- "details": "Auto-created by PyAttackForge",
301
- "groups": [],
302
- "custom_fields": [],
303
- }
304
- if default_asset_library_ids:
305
- asset_payload["asset_library_ids"] = default_asset_library_ids
306
- self.create_asset(asset_payload)
307
- new_asset_names.append(asset_name)
308
-
309
- if new_asset_names:
310
- logger.info("Adding %d new assets to project '%s' scope.", len(new_asset_names), project_id)
311
- self.update_project_scope(project_id, new_asset_names)
312
-
313
- resp = self._request("post", "/api/ss/vulnerability", json_data=vulnerability_data)
312
+ # Validate required fields
313
+ required_fields = [
314
+ ("project_id", project_id),
315
+ ("title", title),
316
+ ("affected_asset_name", affected_asset_name),
317
+ ("priority", priority),
318
+ ("likelihood_of_exploitation", likelihood_of_exploitation),
319
+ ("description", description),
320
+ ("attack_scenario", attack_scenario),
321
+ ("remediation_recommendation", remediation_recommendation),
322
+ ("steps_to_reproduce", steps_to_reproduce),
323
+ ]
324
+ for field_name, value in required_fields:
325
+ if value is None:
326
+ raise ValueError(f"Missing required field: {field_name}")
327
+
328
+ payload = {
329
+ "projectId": project_id,
330
+ "title": title,
331
+ "affected_asset_name": affected_asset_name,
332
+ "priority": priority,
333
+ "likelihood_of_exploitation": likelihood_of_exploitation,
334
+ "description": description,
335
+ "attack_scenario": attack_scenario,
336
+ "remediation_recommendation": remediation_recommendation,
337
+ "steps_to_reproduce": steps_to_reproduce,
338
+ "tags": tags or [],
339
+ "is_zeroday": is_zeroday,
340
+ "is_visible": is_visible,
341
+ "import_to_library": import_to_library,
342
+ "import_source": import_source,
343
+ "import_source_id": import_source_id,
344
+ "custom_fields": custom_fields or [],
345
+ "linked_testcases": linked_testcases or [],
346
+ "custom_tags": custom_tags or [],
347
+ }
348
+ # Only include notes if it is a non-empty list
349
+ if notes:
350
+ payload["notes"] = notes
351
+
352
+ # Remove None values (for optional fields)
353
+ payload = {k: v for k, v in payload.items() if v is not None}
354
+
355
+ resp = self._request("post", "/api/ss/vulnerability", json_data=payload)
314
356
  if resp.status_code in (200, 201):
315
357
  return resp.json()
316
358
  raise RuntimeError(f"Vulnerability creation failed: {resp.text}")
@@ -322,6 +364,7 @@ class DummyResponse:
322
364
  """
323
365
  def __init__(self) -> None:
324
366
  self.status_code = 200
367
+ self.text = "[DRY RUN] No real API call performed."
325
368
 
326
369
  def json(self) -> Dict[str, Any]:
327
370
  return {}
@@ -0,0 +1,215 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyattackforge
3
+ Version: 0.1.1
4
+ Summary: Python wrapper for the AttackForge API
5
+ Home-page: https://github.com/Tantalum-Labs/PyAttackForge
6
+ Author: Shane S
7
+ License: AGPL-3.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Requires-Python: >=3.7
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: requests>=2.20.0
18
+ Dynamic: author
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: license
24
+ Dynamic: license-file
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # PyAttackForge
30
+
31
+ A lightweight Python library for interacting with the AttackForge API.
32
+
33
+ ---
34
+
35
+ ## Features
36
+
37
+ - Create and fetch projects
38
+ - Manage assets
39
+ - Submit vulnerabilities
40
+ - Dry-run mode for testing
41
+
42
+ ---
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ mkdir PyAttackForgeEnv
48
+ cd PyAttackForgeEnv
49
+ virtualenv venv
50
+ source ./venv/bin/activate
51
+ pip install git+https://github.com/Tantalum-Labs/PyAttackForge.git
52
+ ```
53
+
54
+ ## Use
55
+
56
+ ```python
57
+ from pyattackforge import PyAttackForgeClient
58
+
59
+ # Initialize client - Note: Make sure to set your AttackForge URL and API Key
60
+ client = PyAttackForgeClient(api_key="your-api-key", base_url="https://demo.attackforge.com", dry_run=False)
61
+
62
+ # Create a project
63
+ project = client.create_project("My Project", scope=["Asset1", "Asset2"])
64
+
65
+ ## Create a security finding (vulnerability)
66
+ client.create_vulnerability(
67
+ project_id="abc123",
68
+ title="Open SSH Port",
69
+ affected_asset_name="ssh-prod-1",
70
+ priority="High",
71
+ likelihood_of_exploitation=10,
72
+ description="SSH port 22 is open to the internet.",
73
+ attack_scenario="An attacker can brute-force SSH credentials.",
74
+ remediation_recommendation="Restrict SSH access to trusted IPs.",
75
+ steps_to_reproduce="1. Scan the host\n2. Observe port 22 is open",
76
+ tags=["ssh", "exposure"],
77
+ notes=["Observed on 2025-09-09"],
78
+ is_zeroday=False,
79
+ is_visible=True
80
+ )
81
+
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Creating Security Findings
87
+
88
+ To create a security finding (vulnerability) in AttackForge, use the `create_vulnerability` method:
89
+
90
+ ```python
91
+ client.create_vulnerability(
92
+ project_id="abc123",
93
+ title="Open SSH Port",
94
+ affected_asset_name="ssh-prod-1",
95
+ priority="High",
96
+ likelihood_of_exploitation=10,
97
+ description="SSH port 22 is open to the internet.",
98
+ attack_scenario="An attacker can brute-force SSH credentials.",
99
+ remediation_recommendation="Restrict SSH access to trusted IPs.",
100
+ steps_to_reproduce="1. Scan the host\n2. Observe port 22 is open",
101
+ tags=["ssh", "exposure"],
102
+ notes=["Observed on 2025-09-09"],
103
+ is_zeroday=False,
104
+ is_visible=True
105
+ )
106
+ ```
107
+
108
+ **Parameters:**
109
+ - `project_id` (str): The project ID.
110
+ - `title` (str): The title of the finding.
111
+ - `affected_asset_name` (str): The name of the affected asset.
112
+ - `priority` (str): The priority (e.g., "Critical", "High", "Medium", "Low").
113
+ - `likelihood_of_exploitation` (int): Likelihood of exploitation (e.g., 10).
114
+ - `description` (str): Description of the finding.
115
+ - `attack_scenario` (str): Attack scenario details.
116
+ - `remediation_recommendation` (str): Remediation recommendation.
117
+ - `steps_to_reproduce` (str): Steps to reproduce the finding.
118
+ - `tags` (list, optional): List of tags.
119
+ - `notes` (list, optional): List of notes.
120
+ - `is_zeroday` (bool, optional): Whether this is a zero-day finding.
121
+ - `is_visible` (bool, optional): Whether the finding is visible.
122
+ - `import_to_library` (str, optional): Library to import to.
123
+ - `import_source` (str, optional): Source of import.
124
+ - `import_source_id` (str, optional): Source ID for import.
125
+ - `custom_fields` (list, optional): List of custom fields.
126
+ - `linked_testcases` (list, optional): List of linked testcases.
127
+ - `custom_tags` (list, optional): List of custom tags.
128
+
129
+ See the source code for full details and docstrings.
130
+
131
+ ---
132
+
133
+ ## API Reference
134
+
135
+ ### `PyAttackForgeClient`
136
+
137
+ - `__init__(api_key: str, base_url: str = ..., dry_run: bool = False)`
138
+ - `get_assets() -> dict`
139
+ - `get_asset_by_name(name: str) -> dict or None`
140
+ - `create_asset(asset_data: dict) -> dict`
141
+ - `get_project_by_name(name: str) -> dict or None`
142
+ - `get_project_scope(project_id: str) -> set`
143
+ - `update_project_scope(project_id: str, new_assets: list) -> dict`
144
+ - `create_project(name: str, **kwargs) -> dict`
145
+ - `update_project(project_id: str, update_fields: dict) -> dict`
146
+ - `create_vulnerability(
147
+ project_id: str,
148
+ title: str,
149
+ affected_asset_name: str,
150
+ priority: str,
151
+ likelihood_of_exploitation: int,
152
+ description: str,
153
+ attack_scenario: str,
154
+ remediation_recommendation: str,
155
+ steps_to_reproduce: str,
156
+ tags: Optional[list] = None,
157
+ notes: Optional[list] = None,
158
+ is_zeroday: bool = False,
159
+ is_visible: bool = True,
160
+ import_to_library: Optional[str] = None,
161
+ import_source: Optional[str] = None,
162
+ import_source_id: Optional[str] = None,
163
+ custom_fields: Optional[list] = None,
164
+ linked_testcases: Optional[list] = None,
165
+ custom_tags: Optional[list] = None,
166
+ ) -> dict`
167
+
168
+ See the source code for full details and docstrings.
169
+
170
+ ---
171
+ - `create_vulnerability(
172
+ project_id: str,
173
+ title: str,
174
+ affected_asset_name: str,
175
+ priority: str,
176
+ likelihood_of_exploitation: int,
177
+ description: str,
178
+ attack_scenario: str,
179
+ remediation_recommendation: str,
180
+ steps_to_reproduce: str,
181
+ tags: Optional[list] = None,
182
+ notes: Optional[list] = None,
183
+ is_zeroday: bool = False,
184
+ is_visible: bool = True,
185
+ import_to_library: Optional[str] = None,
186
+ import_source: Optional[str] = None,
187
+ import_source_id: Optional[str] = None,
188
+ custom_fields: Optional[list] = None,
189
+ linked_testcases: Optional[list] = None,
190
+ custom_tags: Optional[list] = None,
191
+ ) -> dict`
192
+
193
+ See the source code for full details and docstrings.
194
+
195
+ ---
196
+
197
+ ## Contributing
198
+
199
+ Contributions are welcome! Please open issues or submit pull requests via GitHub.
200
+
201
+ - Ensure code is PEP8-compliant and includes docstrings and type hints.
202
+ - Add or update tests for new features or bugfixes.
203
+ - Do **not** commit API keys or other secrets.
204
+
205
+ ---
206
+
207
+ ## Security
208
+
209
+ **Never commit your API keys or other sensitive information to version control.**
210
+
211
+ ---
212
+
213
+ ## License
214
+
215
+ This project is licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html).
@@ -0,0 +1,7 @@
1
+ pyattackforge/__init__.py,sha256=xZxubdjv_Fc1bRqfVRMR4j9SCTLXg9TfckWn9CQHH_E,839
2
+ pyattackforge/client.py,sha256=0Ki2MhHd-H6ybw3pMazs7vCABMbzjzxGN50LD6Ufb_s,14652
3
+ pyattackforge-0.1.1.dist-info/licenses/LICENSE,sha256=bx5iLIKjgAdYQ7sISn7DsfHRKkoCUm1154sJJKhgqnU,35184
4
+ pyattackforge-0.1.1.dist-info/METADATA,sha256=yYfGjLUdJ27Nkg-TDIj3Mk_RriVd8bTgxmxeOSGRsCw,6732
5
+ pyattackforge-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ pyattackforge-0.1.1.dist-info/top_level.txt,sha256=1rDeMkWvFWuX3MS8V65no7KuybYyvtfIgbYSt5m_uPU,14
7
+ pyattackforge-0.1.1.dist-info/RECORD,,
@@ -1,120 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pyattackforge
3
- Version: 0.1.0
4
- Summary: Python wrapper for the AttackForge API
5
- Home-page: https://github.com/Tantalum-Labs/PyAttackForge
6
- Author: Shane S
7
- License: AGPL-3.0
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Development Status :: 3 - Alpha
12
- Classifier: Intended Audience :: Developers
13
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
- Requires-Python: >=3.7
15
- Description-Content-Type: text/markdown
16
- License-File: LICENSE
17
- Requires-Dist: requests>=2.20.0
18
- Dynamic: author
19
- Dynamic: classifier
20
- Dynamic: description
21
- Dynamic: description-content-type
22
- Dynamic: home-page
23
- Dynamic: license
24
- Dynamic: license-file
25
- Dynamic: requires-dist
26
- Dynamic: requires-python
27
- Dynamic: summary
28
-
29
- # PyAttackForge
30
-
31
- A lightweight Python library for interacting with the AttackForge API.
32
-
33
- ---
34
-
35
- ## Features
36
-
37
- - Create and fetch projects
38
- - Manage assets
39
- - Submit vulnerabilities
40
- - Dry-run mode for testing
41
-
42
- ---
43
-
44
- ## Install
45
-
46
- ```bash
47
- mkdir PyAttackForgeEnv
48
- cd PyAttackForgeEnv
49
- virtualenv venv
50
- source ./venv/bin/activate
51
- pip install git+https://github.com/Tantalum-Labs/PyAttackForge.git
52
- ```
53
-
54
- ## Use
55
-
56
- ```python
57
- from pyattackforge import PyAttackForgeClient
58
-
59
- # Initialize client - Note: Make sure to set your AttackForge URL and API Key
60
- client = PyAttackForgeClient(api_key="your-api-key", base_url="https://demo.attackforge.com", dry_run=False)
61
-
62
- # Create a project
63
- project = client.create_project("My Project", scope=["Asset1", "Asset2"])
64
-
65
- ## Create a vulnerability with auto-created assets
66
- client.create_vulnerability(
67
- vulnerability_data={
68
- "projectId": "abc123",
69
- "title": "Open SSH Port",
70
- "affected_assets": [{"assetName": "ssh-prod-1"}],
71
- "priority": "High",
72
- "likelihood_of_exploitation": 10,
73
- },
74
- auto_create_assets=True,
75
- default_asset_type="Cloud",
76
- default_asset_library_ids=["your-lib-id"]
77
- )
78
-
79
- ```
80
-
81
- ---
82
-
83
- ## API Reference
84
-
85
- ### `PyAttackForgeClient`
86
-
87
- - `__init__(api_key: str, base_url: str = ..., dry_run: bool = False)`
88
- - `get_assets() -> dict`
89
- - `get_asset_by_name(name: str) -> dict or None`
90
- - `create_asset(asset_data: dict) -> dict`
91
- - `get_project_by_name(name: str) -> dict or None`
92
- - `get_project_scope(project_id: str) -> set`
93
- - `update_project_scope(project_id: str, new_assets: list) -> dict`
94
- - `create_project(name: str, **kwargs) -> dict`
95
- - `update_project(project_id: str, update_fields: dict) -> dict`
96
- - `create_vulnerability(vulnerability_data: dict, auto_create_assets: bool = False, ...) -> dict`
97
-
98
- See the source code for full details and docstrings.
99
-
100
- ---
101
-
102
- ## Contributing
103
-
104
- Contributions are welcome! Please open issues or submit pull requests via GitHub.
105
-
106
- - Ensure code is PEP8-compliant and includes docstrings and type hints.
107
- - Add or update tests for new features or bugfixes.
108
- - Do **not** commit API keys or other secrets.
109
-
110
- ---
111
-
112
- ## Security
113
-
114
- **Never commit your API keys or other sensitive information to version control.**
115
-
116
- ---
117
-
118
- ## License
119
-
120
- This project is licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html).
@@ -1,7 +0,0 @@
1
- pyattackforge/__init__.py,sha256=xZxubdjv_Fc1bRqfVRMR4j9SCTLXg9TfckWn9CQHH_E,839
2
- pyattackforge/client.py,sha256=wYyWCscQGYlysBgvxhnQlZnEdhTT8555RmSJ8FH9lrw,12784
3
- pyattackforge-0.1.0.dist-info/licenses/LICENSE,sha256=bx5iLIKjgAdYQ7sISn7DsfHRKkoCUm1154sJJKhgqnU,35184
4
- pyattackforge-0.1.0.dist-info/METADATA,sha256=Xm1YpRAVkEKp5KeQqS2JM5zvfrCYEX90qmfNI9B6FtA,3311
5
- pyattackforge-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
- pyattackforge-0.1.0.dist-info/top_level.txt,sha256=1rDeMkWvFWuX3MS8V65no7KuybYyvtfIgbYSt5m_uPU,14
7
- pyattackforge-0.1.0.dist-info/RECORD,,