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.
- aiq_platform_api-0.1.1/LICENSE +21 -0
- aiq_platform_api-0.1.1/PKG-INFO +77 -0
- aiq_platform_api-0.1.1/README.md +60 -0
- aiq_platform_api-0.1.1/aiq_platform_api/.env_template +6 -0
- aiq_platform_api-0.1.1/aiq_platform_api/__init__.py +28 -0
- aiq_platform_api-0.1.1/aiq_platform_api/assessment_use_cases.py +124 -0
- aiq_platform_api-0.1.1/aiq_platform_api/asset_use_cases.py +167 -0
- aiq_platform_api-0.1.1/aiq_platform_api/common_utils.py +534 -0
- aiq_platform_api-0.1.1/aiq_platform_api/env.py +36 -0
- aiq_platform_api-0.1.1/aiq_platform_api/integration_use_cases.py +35 -0
- aiq_platform_api-0.1.1/aiq_platform_api/phase_log_use_cases.py +64 -0
- aiq_platform_api-0.1.1/aiq_platform_api/phase_results_use_cases.py +51 -0
- aiq_platform_api-0.1.1/aiq_platform_api/result_use_cases.py +48 -0
- aiq_platform_api-0.1.1/aiq_platform_api/tag_use_cases.py +72 -0
- aiq_platform_api-0.1.1/examples/.env.example +6 -0
- aiq_platform_api-0.1.1/examples/basic_usage.py +86 -0
- aiq_platform_api-0.1.1/pyproject.toml +26 -0
|
@@ -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,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()
|