aiq-platform-api 0.1.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 AttackIQ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.1
2
+ Name: aiq-platform-api
3
+ Version: 0.1.1
4
+ Summary: Utility functions for AttackIQ Platform API usage
5
+ License: MIT
6
+ Author: Rajesh Sharma
7
+ Author-email: rajesh.sharma@attackiq.com
8
+ Requires-Python: >=3.9,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ # AttackIQ Platform API Utilities
19
+
20
+ ⚠️ **BETA / WORK IN PROGRESS** ⚠️
21
+
22
+ This package provides utility functions for interacting with the AttackIQ Platform API.
23
+
24
+ ## Status
25
+
26
+ This project is currently in beta and under active development. Features and APIs may change without notice. Feedback and contributions are welcome!
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install aiq-platform-api
32
+ ```
33
+
34
+ ## Examples
35
+
36
+ To get started quickly with the example code:
37
+
38
+ 1. Copy the example files to your project:
39
+ ```bash
40
+ cp examples/basic_usage.py your-project/
41
+ cp examples/.env.example your-project/.env
42
+ ```
43
+
44
+ 2. Configure your credentials:
45
+ ```bash
46
+ # Edit .env file with your AttackIQ Platform credentials
47
+ vim .env
48
+ ```
49
+
50
+ 3. Install required packages:
51
+ ```bash
52
+ pip install aiq-platform-api python-dotenv
53
+ ```
54
+
55
+ 4. Run the example:
56
+ ```bash
57
+ python basic_usage.py
58
+ ```
59
+
60
+ ## Basic Usage
61
+
62
+ ```python
63
+ from aiq_platform_api import assessment_use_cases
64
+
65
+ assessment_use_cases.get_assessment_use_cases()
66
+ ```
67
+
68
+ ## Contributing
69
+
70
+ We welcome feedback and contributions! Please feel free to:
71
+ - Open issues for bugs or feature requests
72
+ - Submit pull requests
73
+ - Provide feedback on the API design
74
+
75
+ ## License
76
+
77
+ MIT License - See LICENSE file for details
@@ -0,0 +1,60 @@
1
+ # AttackIQ Platform API Utilities
2
+
3
+ ⚠️ **BETA / WORK IN PROGRESS** ⚠️
4
+
5
+ This package provides utility functions for interacting with the AttackIQ Platform API.
6
+
7
+ ## Status
8
+
9
+ This project is currently in beta and under active development. Features and APIs may change without notice. Feedback and contributions are welcome!
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install aiq-platform-api
15
+ ```
16
+
17
+ ## Examples
18
+
19
+ To get started quickly with the example code:
20
+
21
+ 1. Copy the example files to your project:
22
+ ```bash
23
+ cp examples/basic_usage.py your-project/
24
+ cp examples/.env.example your-project/.env
25
+ ```
26
+
27
+ 2. Configure your credentials:
28
+ ```bash
29
+ # Edit .env file with your AttackIQ Platform credentials
30
+ vim .env
31
+ ```
32
+
33
+ 3. Install required packages:
34
+ ```bash
35
+ pip install aiq-platform-api python-dotenv
36
+ ```
37
+
38
+ 4. Run the example:
39
+ ```bash
40
+ python basic_usage.py
41
+ ```
42
+
43
+ ## Basic Usage
44
+
45
+ ```python
46
+ from aiq_platform_api import assessment_use_cases
47
+
48
+ assessment_use_cases.get_assessment_use_cases()
49
+ ```
50
+
51
+ ## Contributing
52
+
53
+ We welcome feedback and contributions! Please feel free to:
54
+ - Open issues for bugs or feature requests
55
+ - Submit pull requests
56
+ - Provide feedback on the API design
57
+
58
+ ## License
59
+
60
+ MIT License - See LICENSE file for details
@@ -0,0 +1,6 @@
1
+ ATTACKIQ_PLATFORM_URL=https://firedrill.attackiq.com
2
+ ATTACKIQ_API_TOKEN=
3
+ ATTACKIQ_ASSESSMENT_ID=
4
+ ATTACKIQ_ASSET_ID=
5
+ ATTACKIQ_RESULT_SUMMARY_ID=
6
+ JWT_TOKEN=
@@ -0,0 +1,28 @@
1
+ """AttackIQ Platform API Utilities
2
+
3
+ This package provides utility functions for interacting with the AttackIQ Platform API.
4
+ """
5
+
6
+ __version__ = "0.1.1"
7
+
8
+ # Import all submodules to make them available when importing the package
9
+ from . import assessment_use_cases
10
+ from . import asset_use_cases
11
+ from . import integration_use_cases
12
+ from . import phase_log_use_cases
13
+ from . import phase_results_use_cases
14
+ from . import result_use_cases
15
+ from . import tag_use_cases
16
+
17
+ # Import key utilities from common_utils
18
+ from .common_utils import (
19
+ AttackIQRestClient,
20
+ AssessmentUtils,
21
+ AssetUtils,
22
+ ResultsUtils,
23
+ TagUtils,
24
+ TaggedItemUtils,
25
+ PhaseResultsUtils,
26
+ PhaseLogsUtils,
27
+ )
28
+
@@ -0,0 +1,124 @@
1
+ import itertools
2
+ import os
3
+ import time
4
+ from typing import Optional
5
+
6
+ from common_utils import AttackIQRestClient, AttackIQLogger, AssessmentUtils, AssetUtils
7
+ from env import ATTACKIQ_API_TOKEN, ATTACKIQ_PLATFORM_URL
8
+
9
+ logger = AttackIQLogger.get_logger(__name__)
10
+
11
+
12
+ def list_assessments(client: AttackIQRestClient, limit: Optional[int] = None) -> int:
13
+ logger.info(f"Listing assessments (limit: {limit if limit else 'None'})...")
14
+ count = 0
15
+ assessments = AssessmentUtils.get_assessments(client)
16
+
17
+ for assessment in itertools.islice(assessments, limit):
18
+ count += 1
19
+ logger.info(f"Assessment {count}:")
20
+ logger.info(f" ID: {assessment.get('id', 'N/A')}")
21
+ logger.info(f" Name: {assessment.get('name', 'N/A')}")
22
+ logger.info(f" Status: {assessment.get('status', 'N/A')}")
23
+ logger.info("---")
24
+
25
+ logger.info(f"Total assessments listed: {count}")
26
+ return count
27
+
28
+
29
+ def get_assessment_results(
30
+ client: AttackIQRestClient, assessment_id: str, limit: Optional[int] = None
31
+ ):
32
+ logger.info(f"Fetching results for assessment ID: {assessment_id}")
33
+ results = AssessmentUtils.get_assessment_results(client, assessment_id)
34
+ for i, result in enumerate(itertools.islice(results, limit)):
35
+ logger.info(f"Result {i}:")
36
+ logger.info(f" ID: {result.get('id', 'N/A')}")
37
+ logger.info(f" Status: {result.get('status', 'N/A')}")
38
+ logger.info(f" Start Time: {result.get('start_time', 'N/A')}")
39
+ logger.info(f" End Time: {result.get('end_time', 'N/A')}")
40
+ logger.info("---")
41
+
42
+
43
+ def run_assessment(client: AttackIQRestClient, assessment_id: str) -> Optional[str]:
44
+ logger.info(f"Running assessment with ID: {assessment_id}")
45
+ try:
46
+ run_id = AssessmentUtils.run_assessment(client, assessment_id)
47
+ logger.info(f"Assessment started successfully. Run ID: {run_id}")
48
+ return run_id
49
+ except Exception as e:
50
+ logger.error(f"Failed to run assessment: {str(e)}")
51
+ return None
52
+
53
+
54
+ def wait_for_assessment_completion(
55
+ client: AttackIQRestClient,
56
+ assessment_id: str,
57
+ run_id: Optional[str] = None,
58
+ timeout: int = 600,
59
+ check_interval: int = 10,
60
+ ) -> bool:
61
+ start_time = time.time()
62
+ while time.time() - start_time < timeout:
63
+ if not AssessmentUtils.is_assessment_running(client, assessment_id):
64
+ elapsed_time = round(time.time() - start_time, 2)
65
+ logger.info(
66
+ f"Assessment {assessment_id} (Run ID: {run_id}) completed in {elapsed_time} seconds."
67
+ )
68
+ return True
69
+
70
+ if check_interval > 1:
71
+ logger.info(
72
+ f"Assessment {assessment_id} is still running. Waiting {check_interval} seconds..."
73
+ )
74
+ time.sleep(check_interval)
75
+
76
+ logger.warning(
77
+ f"Assessment {assessment_id} (Run ID: {run_id}) did not complete within {timeout} seconds."
78
+ )
79
+ return False
80
+
81
+
82
+ def list_assets_in_assessment(
83
+ client: AttackIQRestClient, project_id: str, limit: Optional[int] = None
84
+ ):
85
+ logger.info(
86
+ f"Listing assets for project ID: {project_id} (limit: {limit if limit else 'None'})..."
87
+ )
88
+ params = {"hide_hosted_agents": "true", "project_id": project_id}
89
+ assets = AssetUtils.get_assets(client, params=params)
90
+ asset_count = 0
91
+ for asset in itertools.islice(assets, limit):
92
+ asset_count += 1
93
+ logger.info(f"Asset {asset_count}:")
94
+ logger.info(f" ID: {asset.get('id', 'N/A')}")
95
+ logger.info(f" Name: {asset.get('name', 'N/A')}")
96
+ logger.info(f" Type: {asset.get('type', 'N/A')}")
97
+ logger.info(f" IP Address: {asset.get('ipv4_address', 'N/A')}")
98
+ logger.info("---")
99
+ logger.info(f"Total assets listed: {asset_count}")
100
+ return asset_count
101
+
102
+
103
+ def main():
104
+ client = AttackIQRestClient(ATTACKIQ_PLATFORM_URL, ATTACKIQ_API_TOKEN)
105
+ assessment_id = os.environ.get("ATTACKIQ_ASSESSMENT_ID")
106
+
107
+ list_assessments(client, limit=5)
108
+
109
+ if assessment_id:
110
+ list_assets_in_assessment(client, assessment_id, limit=5)
111
+ get_assessment_results(client, assessment_id, limit=5)
112
+ run_id = run_assessment(client, assessment_id)
113
+ if run_id:
114
+ wait_for_assessment_completion(
115
+ client, assessment_id, run_id, timeout=20, check_interval=1
116
+ )
117
+ else:
118
+ logger.warning(
119
+ "ATTACKIQ_ASSESSMENT_ID environment variable is not set. Skipping assessment-specific operations."
120
+ )
121
+
122
+
123
+ if __name__ == "__main__":
124
+ main()
@@ -0,0 +1,167 @@
1
+ import itertools
2
+ import os
3
+
4
+ from common_utils import (
5
+ AttackIQRestClient,
6
+ AttackIQLogger,
7
+ AssetUtils,
8
+ TagUtils,
9
+ TaggedItemUtils,
10
+ AssetStatus,
11
+ )
12
+ from env import ATTACKIQ_API_TOKEN, ATTACKIQ_PLATFORM_URL
13
+
14
+ logger = AttackIQLogger.get_logger(__name__)
15
+
16
+
17
+ def fetch_and_log_assets(client: AttackIQRestClient, limit: int):
18
+ logger.info(f"Fetching and processing assets with limit {limit}...")
19
+ asset_count = 0
20
+ assets = AssetUtils.get_assets(client)
21
+
22
+ for asset in itertools.islice(assets, limit):
23
+ asset_count += 1
24
+ logger.info(f"Asset {asset_count}:")
25
+ logger.info(f" ID: {asset.get('id')}")
26
+ logger.info(f" Name: {asset.get('name')}")
27
+ logger.info(f" Type: {asset.get('type')}")
28
+ logger.info(f" IP Address: {asset.get('ipv4_address')}")
29
+ logger.info("---")
30
+
31
+ if asset_count == 0:
32
+ logger.error("Failed to retrieve any assets.")
33
+ else:
34
+ logger.info(f"Successfully processed {asset_count} assets.")
35
+
36
+
37
+ def find_asset_by_hostname(client: AttackIQRestClient, hostname: str):
38
+ logger.info(f"Searching for asset with hostname: {hostname}")
39
+ asset = AssetUtils.get_asset_by_hostname(client, hostname)
40
+
41
+ if asset:
42
+ logger.info("Asset found:")
43
+ logger.info(f" ID: {asset.get('id')}")
44
+ logger.info(f" Name: {asset.get('name')}")
45
+ logger.info(f" Type: {asset.get('type')}")
46
+ logger.info(f" IP Address: {asset.get('ipv4_address')}")
47
+ logger.info(f" Hostname: {asset.get('hostname')}")
48
+ else:
49
+ logger.info(f"No asset found with hostname: {hostname}")
50
+
51
+
52
+ def uninstall_asset_by_uuid(client: AttackIQRestClient, asset_id: str):
53
+ if not asset_id:
54
+ logger.error("Asset id not provided.")
55
+ return
56
+
57
+ asset = AssetUtils.get_asset_by_id(client, asset_id)
58
+ if not asset:
59
+ logger.error(f"Asset with id {asset_id} not found.")
60
+ return
61
+
62
+ logger.info(f"Attempting to uninstall asset with id: {asset_id}")
63
+ success = AssetUtils.uninstall_asset(client, asset_id)
64
+
65
+ if success:
66
+ logger.info(f"Asset {asset_id} uninstall job submitted successfully.")
67
+ else:
68
+ logger.error(f"Failed to submit uninstall job for asset {asset_id}.")
69
+
70
+
71
+ def list_asset_tags(client: AttackIQRestClient, asset_id: str, limit: int):
72
+ logger.info(f"Listing tags for asset with ID: {asset_id}")
73
+ tag_count = 0
74
+ tagged_items = TaggedItemUtils.get_tagged_items(client, "asset", asset_id)
75
+ for tagged_item in itertools.islice(tagged_items, limit):
76
+ tag_count += 1
77
+ tag_id = tagged_item.get("tag_id")
78
+ logger.info(f"Tagged Item {tag_count}:")
79
+ logger.info(f" ID: {tagged_item.get('id')}")
80
+ logger.info(f" Tag ID: {tag_id}")
81
+ logger.info(f"Total tags listed: {tag_count}")
82
+
83
+
84
+ def tag_asset(client: AttackIQRestClient, asset_id: str, tag_id: str) -> str:
85
+ logger.info(f"Tagging asset with ID: {asset_id} with tag ID: {tag_id}")
86
+ tagged_item = TaggedItemUtils.get_tagged_item(client, "asset", asset_id, tag_id)
87
+ tagged_item_id = tagged_item.get("id") if tagged_item else ""
88
+ if tagged_item_id:
89
+ logger.info(
90
+ f"Asset {asset_id} is already tagged with tag item ID {tagged_item_id}"
91
+ )
92
+ return tagged_item_id
93
+ tagged_item_id = AssetUtils.add_tag(client, asset_id, tag_id)
94
+ if tagged_item_id:
95
+ logger.info(
96
+ f"Successfully tagged asset {asset_id} with tag item ID {tagged_item_id}"
97
+ )
98
+ return tagged_item_id
99
+ else:
100
+ logger.error(f"Failed to tag asset {asset_id} with tag ID {tag_id}")
101
+ return ""
102
+
103
+
104
+ def untag_asset(client: AttackIQRestClient, tagged_item_id: str):
105
+ logger.info(f"Removing tag item with ID: {tagged_item_id}")
106
+ success = TaggedItemUtils.delete_tagged_item(client, tagged_item_id)
107
+ if success:
108
+ logger.info(f"Successfully removed tag item with ID {tagged_item_id}")
109
+ else:
110
+ logger.error(f"Failed to remove tag item with ID {tagged_item_id}")
111
+
112
+
113
+ def delete_tag(client: AttackIQRestClient, tag_id: str) -> bool:
114
+ logger.info(f"Deleting tag with ID: {tag_id}")
115
+ success = TagUtils.delete_tag(client, tag_id)
116
+ if success:
117
+ logger.info(f"Successfully deleted tag with ID {tag_id}")
118
+ else:
119
+ logger.error(f"Failed to delete tag with ID {tag_id}")
120
+ return success
121
+
122
+
123
+ def get_and_log_total_assets(client: AttackIQRestClient):
124
+ total_assets = AssetUtils.get_total_assets(client)
125
+ if total_assets is not None:
126
+ logger.info(f"Total number of assets: {total_assets}")
127
+ else:
128
+ logger.error("Failed to retrieve total number of assets.")
129
+
130
+
131
+ def get_and_log_assets_count_by_status(client: AttackIQRestClient, status: AssetStatus):
132
+ assets_count = AssetUtils.get_assets_count_by_status(client, status)
133
+ if assets_count is not None:
134
+ logger.info(f"Number of {status.value} assets: {assets_count}")
135
+ else:
136
+ logger.error(f"Failed to retrieve count of {status.value} assets.")
137
+
138
+
139
+ def main():
140
+ client = AttackIQRestClient(ATTACKIQ_PLATFORM_URL, ATTACKIQ_API_TOKEN)
141
+
142
+ get_and_log_total_assets(client)
143
+ get_and_log_assets_count_by_status(client, AssetStatus.ACTIVE)
144
+ get_and_log_assets_count_by_status(client, AssetStatus.INACTIVE)
145
+
146
+ fetch_and_log_assets(client, limit=25)
147
+ find_asset_by_hostname(client, "AIQ-CY4C7CC9W5")
148
+
149
+ asset_id = os.environ.get("ATTACKIQ_ASSET_ID")
150
+ if asset_id:
151
+ if AssetUtils.get_asset_by_id(client, asset_id):
152
+ tag_name = "TEST_TAG"
153
+ if tag_id := TagUtils.get_or_create_custom_tag(client, tag_name):
154
+ logger.info(f"Tag ID: {tag_id} for tag '{tag_name}'")
155
+ if tagged_item_id := tag_asset(client, asset_id, tag_id):
156
+ list_asset_tags(client, asset_id, limit=5)
157
+ untag_asset(client, tagged_item_id)
158
+ delete_tag(client, tag_id)
159
+ uninstall_asset_by_uuid(client, asset_id)
160
+ else:
161
+ logger.warning(
162
+ "ATTACKIQ_ASSET_UUID environment variable is not set. Skipping asset-specific operations."
163
+ )
164
+
165
+
166
+ if __name__ == "__main__":
167
+ main()