pyattackforge 0.2.2__tar.gz → 0.2.3__tar.gz

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.
Files changed (27) hide show
  1. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/PKG-INFO +20 -1
  2. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/README.md +19 -0
  3. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/config.py +1 -1
  4. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/findings.py +3 -0
  5. pyattackforge-0.2.3/pyattackforge/resources/groups.py +35 -0
  6. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/projects.py +5 -0
  7. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge.egg-info/PKG-INFO +20 -1
  8. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyproject.toml +1 -1
  9. pyattackforge-0.2.2/pyattackforge/resources/groups.py +0 -20
  10. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/LICENSE +0 -0
  11. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/__init__.py +0 -0
  12. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/cache.py +0 -0
  13. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/client.py +0 -0
  14. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/exceptions.py +0 -0
  15. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/__init__.py +0 -0
  16. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/assets.py +0 -0
  17. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/base.py +0 -0
  18. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/notes.py +0 -0
  19. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/reports.py +0 -0
  20. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/users.py +0 -0
  21. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/resources/writeups.py +0 -0
  22. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge/transport.py +0 -0
  23. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge.egg-info/SOURCES.txt +0 -0
  24. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge.egg-info/dependency_links.txt +0 -0
  25. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge.egg-info/requires.txt +0 -0
  26. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/pyattackforge.egg-info/top_level.txt +0 -0
  27. {pyattackforge-0.2.2 → pyattackforge-0.2.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyattackforge
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Python SDK for the AttackForge SSAPI
5
5
  Author: hackman238
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -812,6 +812,25 @@ with AttackForgeClient() as client:
812
812
  client.link_vulnerability_to_testcases("project_id", "vuln_id", ["testcase_id"])
813
813
  ```
814
814
 
815
+ Group management (examples):
816
+
817
+ ```python
818
+ from pyattackforge import AttackForgeClient
819
+
820
+ with AttackForgeClient() as client:
821
+ group = client.groups.create_group({
822
+ "name": "SDK Group",
823
+ "group_owner": "Owner Name",
824
+ "primary_contact_name": "Owner Name",
825
+ "primary_contact_email": "owner@example.com",
826
+ "primary_contact_number": "0000000000",
827
+ })
828
+ group_id = group.get("group_id") or group.get("id")
829
+ client.projects.add_project_to_group("project_id", group_id)
830
+ client.groups.get_group_projects(group_id)
831
+ client.groups.get_group_vulnerabilities(group_id)
832
+ ```
833
+
815
834
  ## Testing
816
835
 
817
836
  Unit tests:
@@ -127,6 +127,25 @@ with AttackForgeClient() as client:
127
127
  client.link_vulnerability_to_testcases("project_id", "vuln_id", ["testcase_id"])
128
128
  ```
129
129
 
130
+ Group management (examples):
131
+
132
+ ```python
133
+ from pyattackforge import AttackForgeClient
134
+
135
+ with AttackForgeClient() as client:
136
+ group = client.groups.create_group({
137
+ "name": "SDK Group",
138
+ "group_owner": "Owner Name",
139
+ "primary_contact_name": "Owner Name",
140
+ "primary_contact_email": "owner@example.com",
141
+ "primary_contact_number": "0000000000",
142
+ })
143
+ group_id = group.get("group_id") or group.get("id")
144
+ client.projects.add_project_to_group("project_id", group_id)
145
+ client.groups.get_group_projects(group_id)
146
+ client.groups.get_group_vulnerabilities(group_id)
147
+ ```
148
+
130
149
  ## Testing
131
150
 
132
151
  Unit tests:
@@ -16,7 +16,7 @@ class ClientConfig:
16
16
  timeout: float = 30.0
17
17
  max_retries: int = 3
18
18
  backoff_factor: float = 0.5
19
- user_agent: str = "pyattackforge/0.2.2"
19
+ user_agent: str = "pyattackforge/0.2.3"
20
20
  http2: bool = True
21
21
  # Default visibility for newly created findings. False = pending/hidden.
22
22
  default_findings_visible: bool = False
@@ -36,6 +36,9 @@ class FindingsResource(BaseResource):
36
36
  def get_vulnerability(self, vulnerability_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
37
37
  return self._get(f"/api/ss/vulnerability/{vulnerability_id}", params=params)
38
38
 
39
+ def get_vulnerabilities_by_group(self, group_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
40
+ return self._get(f"/api/ss/groups/{group_id}/vulnerabilities", params=params)
41
+
39
42
  def get_project_vulnerabilities(self, project_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
40
43
  return self._get(f"/api/ss/project/{project_id}/vulnerabilities", params=params)
41
44
 
@@ -0,0 +1,35 @@
1
+ """Resource: groups."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from .base import BaseResource
8
+
9
+
10
+ class GroupsResource(BaseResource):
11
+ """Groups API resource wrapper."""
12
+
13
+ def create_group(self, payload: Dict[str, Any]) -> Any:
14
+ return self._post("/api/ss/group", json=payload)
15
+
16
+ def get_groups(self, params: Optional[Dict[str, Any]] = None) -> Any:
17
+ return self._get("/api/ss/groups", params=params)
18
+
19
+ def get_group(self, group_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
20
+ return self._get(f"/api/ss/group/{group_id}", params=params)
21
+
22
+ def update_group(self, group_id: str, payload: Dict[str, Any]) -> Any:
23
+ return self._put(f"/api/ss/group/{group_id}", json=payload)
24
+
25
+ def archive_group(self, group_id: str) -> Any:
26
+ return self._put(f"/api/ss/group/{group_id}/archive")
27
+
28
+ def restore_group(self, group_id: str) -> Any:
29
+ return self._put(f"/api/ss/group/{group_id}/restore")
30
+
31
+ def get_group_projects(self, group_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
32
+ return self._get(f"/api/ss/groups/{group_id}/projects", params=params)
33
+
34
+ def get_group_vulnerabilities(self, group_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
35
+ return self._get(f"/api/ss/groups/{group_id}/vulnerabilities", params=params)
@@ -33,6 +33,9 @@ class ProjectsResource(BaseResource):
33
33
  def get_projects(self, params: Optional[Dict[str, Any]] = None) -> Any:
34
34
  return self._get("/api/ss/projects", params=params)
35
35
 
36
+ def get_projects_by_group(self, group_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
37
+ return self._get(f"/api/ss/groups/{group_id}/projects", params=params)
38
+
36
39
  def get_projects_and_vulnerabilities(self, params: Optional[Dict[str, Any]] = None) -> Any:
37
40
  return self._get("/api/ss/projects-and-vulnerabilities", params=params)
38
41
 
@@ -148,6 +151,8 @@ class ProjectsResource(BaseResource):
148
151
  project = project["data"]
149
152
  if isinstance(project, dict) and isinstance(project.get("project"), dict):
150
153
  project = project["project"]
154
+ if not isinstance(project, dict):
155
+ raise ValueError("Project response is not a dict; cannot resolve groups")
151
156
  groups = project.get("groups") or project.get("project_groups") or []
152
157
  group_ids: List[str] = []
153
158
  if isinstance(groups, list):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyattackforge
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Python SDK for the AttackForge SSAPI
5
5
  Author: hackman238
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -812,6 +812,25 @@ with AttackForgeClient() as client:
812
812
  client.link_vulnerability_to_testcases("project_id", "vuln_id", ["testcase_id"])
813
813
  ```
814
814
 
815
+ Group management (examples):
816
+
817
+ ```python
818
+ from pyattackforge import AttackForgeClient
819
+
820
+ with AttackForgeClient() as client:
821
+ group = client.groups.create_group({
822
+ "name": "SDK Group",
823
+ "group_owner": "Owner Name",
824
+ "primary_contact_name": "Owner Name",
825
+ "primary_contact_email": "owner@example.com",
826
+ "primary_contact_number": "0000000000",
827
+ })
828
+ group_id = group.get("group_id") or group.get("id")
829
+ client.projects.add_project_to_group("project_id", group_id)
830
+ client.groups.get_group_projects(group_id)
831
+ client.groups.get_group_vulnerabilities(group_id)
832
+ ```
833
+
815
834
  ## Testing
816
835
 
817
836
  Unit tests:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyattackforge"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Python SDK for the AttackForge SSAPI"
9
9
  readme = "README.md"
10
10
  license = {file = "LICENSE"}
@@ -1,20 +0,0 @@
1
- """Resource: groups."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import Any, Dict, Optional
6
-
7
- from .base import BaseResource
8
-
9
-
10
- class GroupsResource(BaseResource):
11
- """Groups API resource wrapper."""
12
-
13
- def create_group(self, payload: Dict[str, Any]) -> Any:
14
- return self._post("/api/ss/group", json=payload)
15
-
16
- def get_groups(self, params: Optional[Dict[str, Any]] = None) -> Any:
17
- return self._get("/api/ss/groups", params=params)
18
-
19
- def get_group(self, group_id: str, params: Optional[Dict[str, Any]] = None) -> Any:
20
- return self._get(f"/api/ss/group/{group_id}", params=params)
File without changes
File without changes