diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index d02fa121..959cdb04 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -1 +1,3 @@
ff87bcaf1741e8ecf15cb8d401438592dfef3ba7 # Mass reformat with adoption of ruff
+9d96a56b763ece491a07441dbe26176777a1964c # Add I rules to ruff
+4eefa15b8afbb387b9fa5338dc45759e1915ef56 # Add E, W, and RUF
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 81e6bac0..7bd9a339 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -8,7 +8,7 @@ repos:
- id: detect-private-key
- id: forbid-submodules
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.11.2
+ rev: v0.12.5
hooks:
- id: ruff
args: [ --fix ]
diff --git a/contentctl/actions/deploy_acs.py b/contentctl/actions/deploy_acs.py
index b38e1273..5826da0b 100644
--- a/contentctl/actions/deploy_acs.py
+++ b/contentctl/actions/deploy_acs.py
@@ -1,7 +1,9 @@
-from contentctl.objects.config import deploy_acs, StackType
-from requests import post
import pprint
+from requests import post
+
+from contentctl.objects.config import StackType, deploy_acs
+
class Deploy:
def execute(self, config: deploy_acs, appinspect_token: str) -> None:
@@ -33,7 +35,7 @@ def execute(self, config: deploy_acs, appinspect_token: str) -> None:
raise Exception(f"Unsupported stack type: '{config.stack_type}'")
except Exception as e:
raise Exception(
- f"Error installing to stack '{config.splunk_cloud_stack}' (stack_type='{config.stack_type}') via ACS:\n{str(e)}"
+ f"Error installing to stack '{config.splunk_cloud_stack}' (stack_type='{config.stack_type}') via ACS:\n{e!s}"
)
try:
diff --git a/contentctl/actions/detection_testing/DetectionTestingManager.py b/contentctl/actions/detection_testing/DetectionTestingManager.py
index ae0df1e3..c7370a44 100644
--- a/contentctl/actions/detection_testing/DetectionTestingManager.py
+++ b/contentctl/actions/detection_testing/DetectionTestingManager.py
@@ -162,10 +162,10 @@ def sigint_handler(signum, frame):
print()
print(f"[{error_type}]:")
for error in errors[error_type]:
- print(f"\t❌ {str(error)}")
+ print(f"\t❌ {error!s}")
if isinstance(error, ExceptionGroup):
for suberror in error.exceptions: # type: ignore
- print(f"\t\t❌ {str(suberror)}") # type: ignore
+ print(f"\t\t❌ {suberror!s}") # type: ignore
print()
return self.output_dto
@@ -208,7 +208,7 @@ def create_DetectionTestingInfrastructureObjects(self):
except Exception as e:
raise Exception(
"Failed to pull docker container image "
- f"[{self.input_dto.config.container_settings.full_image_path}]: {str(e)}"
+ f"[{self.input_dto.config.container_settings.full_image_path}]: {e!s}"
)
already_staged_container_files = False
diff --git a/contentctl/actions/detection_testing/GitService.py b/contentctl/actions/detection_testing/GitService.py
index 159e00bb..5beb916d 100644
--- a/contentctl/actions/detection_testing/GitService.py
+++ b/contentctl/actions/detection_testing/GitService.py
@@ -99,7 +99,7 @@ def getChanges(self, target_branch: str) -> List[Detection]:
updated_detections.add(detectionObject)
else:
raise Exception(
- f"Error getting detection object for file {str(decoded_path)}"
+ f"Error getting detection object for file {decoded_path!s}"
)
elif (
@@ -111,7 +111,7 @@ def getChanges(self, target_branch: str) -> List[Detection]:
updated_macros.add(macroObject)
else:
raise Exception(
- f"Error getting macro object for file {str(decoded_path)}"
+ f"Error getting macro object for file {decoded_path!s}"
)
elif (
@@ -125,7 +125,7 @@ def getChanges(self, target_branch: str) -> List[Detection]:
updated_datasources.add(datasourceObject)
else:
raise Exception(
- f"Error getting data source object for file {str(decoded_path)}"
+ f"Error getting data source object for file {decoded_path!s}"
)
elif decoded_path.is_relative_to(self.config.path / "lookups"):
@@ -172,7 +172,7 @@ def getChanges(self, target_branch: str) -> List[Detection]:
else:
raise Exception(
- f"Detected a changed file in the lookups/ directory '{str(decoded_path)}'.\n"
+ f"Detected a changed file in the lookups/ directory '{decoded_path!s}'.\n"
"Only files ending in .csv, .yml, or .mlmodel are supported in this "
"directory. This file must be removed from the lookups/ directory."
)
diff --git a/contentctl/actions/detection_testing/generate_detection_coverage_badge.py b/contentctl/actions/detection_testing/generate_detection_coverage_badge.py
index 749a6b75..77c90eb9 100644
--- a/contentctl/actions/detection_testing/generate_detection_coverage_badge.py
+++ b/contentctl/actions/detection_testing/generate_detection_coverage_badge.py
@@ -47,13 +47,13 @@
try:
results = parser.parse_args()
except Exception as e:
- print(f"Error parsing arguments: {str(e)}")
+ print(f"Error parsing arguments: {e!s}")
exit(1)
try:
summary_info = json.loads(results.input_summary_file.read())
except Exception as e:
- print(f"Error loading {results.input_summary_file.name} JSON file: {str(e)}")
+ print(f"Error loading {results.input_summary_file.name} JSON file: {e!s}")
sys.exit(1)
if "summary" not in summary_info:
@@ -75,7 +75,7 @@
RAW_BADGE_SVG.format(results.badge_string, "{:2.1f}%".format(pass_percent))
)
except Exception as e:
- print(f"Error generating badge: {str(e)}")
+ print(f"Error generating badge: {e!s}")
sys.exit(1)
diff --git a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py
index 30e91e97..e3ce2ac7 100644
--- a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py
+++ b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py
@@ -197,7 +197,7 @@ def setup(self):
self.check_for_teardown()
except Exception as e:
- msg = f"[{self.get_name()}]: {str(e)}"
+ msg = f"[{self.get_name()}]: {e!s}"
self.finish()
if isinstance(e, ExceptionGroup):
raise ExceptionGroup(msg, e.exceptions) from e # type: ignore
@@ -310,7 +310,7 @@ def configure_hec(self):
return
except Exception as e:
- raise (Exception(f"Failure creating HEC Endpoint: {str(e)}"))
+ raise (Exception(f"Failure creating HEC Endpoint: {e!s}"))
def get_all_indexes(self) -> None:
"""
@@ -327,7 +327,7 @@ def get_all_indexes(self) -> None:
# Retrieve all available indexes on the splunk instance
self.all_indexes_on_server = indexes
except Exception as e:
- raise (Exception(f"Failure getting indexes: {str(e)}"))
+ raise (Exception(f"Failure getting indexes: {e!s}"))
def get_conn(self) -> client.Service:
try:
@@ -382,7 +382,7 @@ def connect_to_api(self, sleep_seconds: int = 5):
pass
except Exception as e:
self.pbar.write(
- f"Error getting API connection (not quitting) '{type(e).__name__}': {str(e)}"
+ f"Error getting API connection (not quitting) '{type(e).__name__}': {e!s}"
)
for _ in range(sleep_seconds):
@@ -402,7 +402,7 @@ def create_replay_index(self):
pass
else:
raise Exception(
- f"Error creating index {self.sync_obj.replay_index} - {str(e)}"
+ f"Error creating index {self.sync_obj.replay_index} - {e!s}"
)
def configure_imported_roles(
@@ -426,7 +426,7 @@ def configure_imported_roles(
)
return
except Exception as e:
- msg = f"Error configuring roles: {str(e)}"
+ msg = f"Error configuring roles: {e!s}"
self.pbar.write(msg)
raise Exception(msg) from e
@@ -436,7 +436,7 @@ def configure_delete_indexes(self):
self.get_conn().post(endpoint, value=";".join(self.all_indexes_on_server))
except Exception as e:
self.pbar.write(
- f"Error configuring deleteIndexesAllowed with '{self.all_indexes_on_server}': [{str(e)}]"
+ f"Error configuring deleteIndexesAllowed with '{self.all_indexes_on_server}': [{e!s}]"
)
def wait_for_conf_file(self, app_name: str, conf_file_name: str):
@@ -474,12 +474,12 @@ def configure_conf_file_datamodels(self, APP_NAME: str = "Splunk_SA_CIM"):
parser.read(custom_acceleration_datamodels)
if len(parser.keys()) > 1:
self.pbar.write(
- f"Read {len(parser) - 1} custom datamodels from {str(custom_acceleration_datamodels)}!"
+ f"Read {len(parser) - 1} custom datamodels from {custom_acceleration_datamodels!s}!"
)
if not cim_acceleration_datamodels.is_file():
self.pbar.write(
- f"******************************\nDATAMODEL ACCELERATION FILE {str(cim_acceleration_datamodels)} NOT "
+ f"******************************\nDATAMODEL ACCELERATION FILE {cim_acceleration_datamodels!s} NOT "
"FOUND. CIM DATAMODELS NOT ACCELERATED\n******************************\n"
)
else:
@@ -499,7 +499,7 @@ def configure_conf_file_datamodels(self, APP_NAME: str = "Splunk_SA_CIM"):
except Exception as e:
self.pbar.write(
- f"Error creating the conf Datamodel {datamodel_name} key/value {name}/{value}: {str(e)}"
+ f"Error creating the conf Datamodel {datamodel_name} key/value {name}/{value}: {e!s}"
)
def execute(self):
@@ -528,9 +528,7 @@ def execute(self):
self.finish()
return
except Exception as e:
- self.pbar.write(
- f"Error testing detection: {type(e).__name__}: {str(e)}"
- )
+ self.pbar.write(f"Error testing detection: {type(e).__name__}: {e!s}")
raise e
finally:
self.sync_obj.outputQueue.append(detection)
@@ -1365,7 +1363,7 @@ def delete_attack_data(self, attack_data_files: list[TestAttackData]):
except Exception as e:
raise (
Exception(
- f"Trouble deleting data using the search {splunk_search}: {str(e)}"
+ f"Trouble deleting data using the search {splunk_search}: {e!s}"
)
)
@@ -1440,7 +1438,7 @@ def replay_attack_data_file(
except Exception as e:
raise (
Exception(
- f"Could not download attack data file [{attack_data_file.data}]:{str(e)}"
+ f"Could not download attack data file [{attack_data_file.data}]:{e!s}"
)
)
@@ -1514,7 +1512,7 @@ def hec_raw_replay(
except Exception as e:
raise (
Exception(
- f"There was an exception sending attack_data to HEC: {str(e)}"
+ f"There was an exception sending attack_data to HEC: {e!s}"
)
)
@@ -1558,7 +1556,7 @@ def hec_raw_replay(
)
)
except Exception as e:
- raise (Exception(f"There was an exception in the post: {str(e)}"))
+ raise (Exception(f"There was an exception in the post: {e!s}"))
def status(self):
pass
diff --git a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py
index ceced0eb..d0a3318f 100644
--- a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py
+++ b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py
@@ -1,11 +1,12 @@
+import docker
+import docker.models.containers
+import docker.models.resource
+import docker.types
+
from contentctl.actions.detection_testing.infrastructures.DetectionTestingInfrastructure import (
DetectionTestingInfrastructure,
)
from contentctl.objects.config import test
-import docker.models.resource
-import docker.models.containers
-import docker
-import docker.types
class DetectionTestingInfrastructureContainer(DetectionTestingInfrastructure):
@@ -34,7 +35,7 @@ def finish(self):
self.removeContainer()
pass
except Exception as e:
- raise (Exception(f"Error removing container: {str(e)}"))
+ raise (Exception(f"Error removing container: {e!s}"))
super().finish()
def get_name(self) -> str:
@@ -46,7 +47,7 @@ def get_docker_client(self):
return c
except Exception as e:
- raise (Exception(f"Failed to get docker client: {str(e)}"))
+ raise (Exception(f"Failed to get docker client: {e!s}"))
def check_for_teardown(self):
try:
@@ -56,7 +57,7 @@ def check_for_teardown(self):
except Exception as e:
if self.sync_obj.terminate is not True:
self.pbar.write(
- f"Error: could not get container [{self.get_name()}]: {str(e)}"
+ f"Error: could not get container [{self.get_name()}]: {e!s}"
)
self.sync_obj.terminate = True
else:
@@ -175,6 +176,6 @@ def removeContainer(self, removeVolumes: bool = True, forceRemove: bool = True):
except Exception as e:
raise (
Exception(
- f"Could not remove Docker Container [{self.get_name()}]: {str(e)}"
+ f"Could not remove Docker Container [{self.get_name()}]: {e!s}"
)
)
diff --git a/contentctl/actions/detection_testing/progress_bar.py b/contentctl/actions/detection_testing/progress_bar.py
index 183497d4..b66685a0 100644
--- a/contentctl/actions/detection_testing/progress_bar.py
+++ b/contentctl/actions/detection_testing/progress_bar.py
@@ -1,7 +1,8 @@
+import datetime
import time
from enum import StrEnum
+
from tqdm import tqdm
-import datetime
class TestReportingType(StrEnum):
diff --git a/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py b/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py
index 2246783f..2b9cc559 100644
--- a/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py
+++ b/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py
@@ -1,10 +1,11 @@
+import time
+
+import tqdm
+
from contentctl.actions.detection_testing.views.DetectionTestingView import (
DetectionTestingView,
)
-import time
-import tqdm
-
class DetectionTestingViewCLI(DetectionTestingView, arbitrary_types_allowed=True):
pbar: tqdm.tqdm = None
diff --git a/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py b/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py
index 40f5be9e..89022657 100644
--- a/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py
+++ b/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py
@@ -1,8 +1,10 @@
+import pathlib
+
+import yaml
+
from contentctl.actions.detection_testing.views.DetectionTestingView import (
DetectionTestingView,
)
-import pathlib
-import yaml
OUTPUT_FOLDER = "test_results"
OUTPUT_FILENAME = "summary.yml"
diff --git a/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py b/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py
index d7cf73fa..ac7f8e28 100644
--- a/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py
+++ b/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py
@@ -1,9 +1,9 @@
+import webbrowser
from threading import Thread
+from wsgiref.simple_server import WSGIRequestHandler, make_server
-from bottle import template, Bottle, ServerAdapter
-from wsgiref.simple_server import make_server, WSGIRequestHandler
import jinja2
-import webbrowser
+from bottle import Bottle, ServerAdapter, template
from pydantic import ConfigDict
from contentctl.actions.detection_testing.views.DetectionTestingView import (
@@ -39,8 +39,8 @@
{% for containerName, data in currentTestingQueue.items() %}
| {{ containerName }} |
- {{ data["name"] }} |
- {{ data["search"] }} |
+ {{ data["name"] }} |
+ {{ data["search"] }} |
{% endfor %}
@@ -69,7 +69,7 @@
{% else %}
False |
{% endif %}
-
+
{% endfor %}
{% endfor %}
@@ -118,7 +118,7 @@ def setup(self):
try:
webbrowser.open(f"http://{self.server.host}:{DEFAULT_WEB_UI_PORT}")
except Exception as e:
- print(f"Could not open webbrowser for status page: {str(e)}")
+ print(f"Could not open webbrowser for status page: {e!s}")
def stop(self):
if self.server.server is None:
diff --git a/contentctl/actions/doc_gen.py b/contentctl/actions/doc_gen.py
index c9d89896..d151ab81 100644
--- a/contentctl/actions/doc_gen.py
+++ b/contentctl/actions/doc_gen.py
@@ -1,8 +1,7 @@
import os
-
from dataclasses import dataclass
-from contentctl.input.director import DirectorInputDto, Director, DirectorOutputDto
+from contentctl.input.director import Director, DirectorInputDto, DirectorOutputDto
from contentctl.output.doc_md_output import DocMdOutput
diff --git a/contentctl/actions/release_notes.py b/contentctl/actions/release_notes.py
index decf8c54..ce18301a 100644
--- a/contentctl/actions/release_notes.py
+++ b/contentctl/actions/release_notes.py
@@ -155,7 +155,7 @@ def create_notes(
except yaml.YAMLError as exc:
raise Exception(
- f"Error parsing YAML file for release_notes {file_path}: {str(exc)}"
+ f"Error parsing YAML file for release_notes {file_path}: {exc!s}"
)
else:
warnings.append(
diff --git a/contentctl/actions/reporting.py b/contentctl/actions/reporting.py
index 70e1cd33..8a37d660 100644
--- a/contentctl/actions/reporting.py
+++ b/contentctl/actions/reporting.py
@@ -1,9 +1,9 @@
from dataclasses import dataclass
from contentctl.input.director import DirectorOutputDto
-from contentctl.output.svg_output import SvgOutput
-from contentctl.output.attack_nav_output import AttackNavOutput
from contentctl.objects.config import report
+from contentctl.output.attack_nav_output import AttackNavOutput
+from contentctl.output.svg_output import SvgOutput
@dataclass(frozen=True)
@@ -24,7 +24,7 @@ def execute(self, input_dto: ReportingInputDto) -> None:
)
else:
raise Exception(
- f"Error writing reporting : '{input_dto.config.getReportingPath()}': {str(e)}"
+ f"Error writing reporting : '{input_dto.config.getReportingPath()}': {e!s}"
)
print("Creating GitHub Badges...")
diff --git a/contentctl/actions/test.py b/contentctl/actions/test.py
index 90ac3951..c751af7f 100644
--- a/contentctl/actions/test.py
+++ b/contentctl/actions/test.py
@@ -18,9 +18,8 @@
from contentctl.actions.detection_testing.views.DetectionTestingViewWeb import (
DetectionTestingViewWeb,
)
-from contentctl.objects.config import Changes, Selected
+from contentctl.objects.config import Changes, Selected, test_servers
from contentctl.objects.config import test as test_
-from contentctl.objects.config import test_servers
from contentctl.objects.detection import Detection
from contentctl.objects.integration_test import IntegrationTest
@@ -151,5 +150,5 @@ def execute(self, input_dto: TestInputDto) -> bool:
return summary.get("success", False)
except Exception as e:
- print(f"Error determining if whole test was successful: {str(e)}")
+ print(f"Error determining if whole test was successful: {e!s}")
return False
diff --git a/contentctl/actions/validate.py b/contentctl/actions/validate.py
index 5dc34f16..2ff41bc0 100644
--- a/contentctl/actions/validate.py
+++ b/contentctl/actions/validate.py
@@ -118,7 +118,7 @@ def validate_latest_TA_information(self, data_sources: list[DataSource]) -> None
)
except Exception as e:
errors.append(
- f"Error processing checking version of TA {supported_TA.name}: {str(e)}"
+ f"Error processing checking version of TA {supported_TA.name}: {e!s}"
)
if len(errors) > 0:
diff --git a/contentctl/api.py b/contentctl/api.py
index 037ac5ce..d1163349 100644
--- a/contentctl/api.py
+++ b/contentctl/api.py
@@ -1,9 +1,10 @@
from pathlib import Path
-from typing import Any, Union, Type
+from typing import Any, Type, Union
+
+from contentctl.input.director import DirectorOutputDto
from contentctl.input.yml_reader import YmlReader
-from contentctl.objects.config import test_common, test, test_servers
+from contentctl.objects.config import test, test_common, test_servers
from contentctl.objects.security_content_object import SecurityContentObject
-from contentctl.input.director import DirectorOutputDto
def config_from_file(
@@ -36,7 +37,7 @@ def config_from_file(
except Exception as e:
raise Exception(
- f"Failed to load contentctl configuration from file '{path}': {str(e)}"
+ f"Failed to load contentctl configuration from file '{path}': {e!s}"
)
# Apply settings that have been overridden from the ones in the file
@@ -45,7 +46,7 @@ def config_from_file(
except Exception as e:
raise Exception(
f"Failed updating dictionary of values read from file '{path}'"
- f" with the dictionary of arguments passed: {str(e)}"
+ f" with the dictionary of arguments passed: {e!s}"
)
# The function below will throw its own descriptive exception if it fails
@@ -78,7 +79,7 @@ def config_from_dict(
try:
test_object = configType.model_validate(config)
except Exception as e:
- raise Exception(f"Failed to load contentctl configuration from dict:\n{str(e)}")
+ raise Exception(f"Failed to load contentctl configuration from dict:\n{e!s}")
return test_object
diff --git a/contentctl/contentctl.py b/contentctl/contentctl.py
index c0f37d9a..573c7ba0 100644
--- a/contentctl/contentctl.py
+++ b/contentctl/contentctl.py
@@ -206,7 +206,7 @@ def main():
config_obj = YmlReader().load_file(configFile, add_fields=False)
t = test.model_validate(config_obj)
except Exception as e:
- print(f"Error validating 'contentctl.yml':\n{str(e)}")
+ print(f"Error validating 'contentctl.yml':\n{e!s}")
sys.exit(1)
# For ease of generating the constructor, we want to allow construction
diff --git a/contentctl/enrichments/attack_enrichment.py b/contentctl/enrichments/attack_enrichment.py
index 33729f72..da7dfafa 100644
--- a/contentctl/enrichments/attack_enrichment.py
+++ b/contentctl/enrichments/attack_enrichment.py
@@ -183,7 +183,7 @@ def get_attack_lookup(
)
except Exception as err:
- raise Exception(f"Error getting MITRE Enrichment: {str(err)}")
+ raise Exception(f"Error getting MITRE Enrichment: {err!s}")
print("Done!")
return attack_lookup
diff --git a/contentctl/enrichments/cve_enrichment.py b/contentctl/enrichments/cve_enrichment.py
index 75ffbd5a..424fff06 100644
--- a/contentctl/enrichments/cve_enrichment.py
+++ b/contentctl/enrichments/cve_enrichment.py
@@ -1,8 +1,11 @@
from __future__ import annotations
-from pycvesearch import CVESearch
-from typing import Annotated, Union, TYPE_CHECKING
-from pydantic import ConfigDict, BaseModel, Field, computed_field
+
from decimal import Decimal
+from typing import TYPE_CHECKING, Annotated, Union
+
+from pycvesearch import CVESearch
+from pydantic import BaseModel, ConfigDict, Field, computed_field
+
from contentctl.objects.annotated_types import CVE_TYPE
if TYPE_CHECKING:
@@ -46,7 +49,7 @@ def getCveEnrichment(
return CveEnrichment(use_enrichment=True, cve_api_obj=cve_api_obj)
except Exception as e:
raise Exception(
- f"Error setting CVE_SEARCH API to: {CVESSEARCH_API_URL}: {str(e)}"
+ f"Error setting CVE_SEARCH API to: {CVESSEARCH_API_URL}: {e!s}"
)
return CveEnrichment(use_enrichment=False, cve_api_obj=None)
@@ -57,14 +60,14 @@ def enrich_cve(
if not self.use_enrichment:
return CveEnrichmentObj(
id=cve_id,
- cvss=Decimal(5.0),
+ cvss=Decimal("5.0"),
summary="SUMMARY NOT AVAILABLE! ONLY THE LINK WILL BE USED AT THIS TIME",
)
else:
print("WARNING - Dynamic enrichment not supported at this time.")
return CveEnrichmentObj(
id=cve_id,
- cvss=Decimal(5.0),
+ cvss=Decimal("5.0"),
summary="SUMMARY NOT AVAILABLE! ONLY THE LINK WILL BE USED AT THIS TIME",
)
# Depending on needs, we may add dynamic enrichment functionality back to the tool
diff --git a/contentctl/enrichments/splunk_app_enrichment.py b/contentctl/enrichments/splunk_app_enrichment.py
index 00d7c1be..9c30e2b9 100644
--- a/contentctl/enrichments/splunk_app_enrichment.py
+++ b/contentctl/enrichments/splunk_app_enrichment.py
@@ -1,8 +1,9 @@
-import requests
-import xmltodict
import functools
-import shelve
import os
+import shelve
+
+import requests
+import xmltodict
SPLUNKBASE_API_URL = "https://apps.splunk.com/api/apps/entriesbyid/"
@@ -75,7 +76,7 @@ def enrich_splunk_app(
splunk_app_enriched["url"] = ""
except Exception as e:
print(
- f"There was an unknown error enriching the Splunk TA [{splunk_ta}]: {str(e)}"
+ f"There was an unknown error enriching the Splunk TA [{splunk_ta}]: {e!s}"
)
splunk_app_enriched["name"] = splunk_ta
splunk_app_enriched["url"] = ""
diff --git a/contentctl/helper/link_validator.py b/contentctl/helper/link_validator.py
index 181642ec..9f036e81 100644
--- a/contentctl/helper/link_validator.py
+++ b/contentctl/helper/link_validator.py
@@ -1,13 +1,13 @@
-from pydantic import BaseModel, model_validator
-from typing import Union, Callable, Any
-import requests
-import urllib3
-import urllib3.exceptions
-import time
import abc
-
import os
import shelve
+import time
+from typing import Any, Callable, Union
+
+import requests
+import urllib3
+import urllib3.exceptions
+from pydantic import BaseModel, model_validator
DEFAULT_USER_AGENT_STRING = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36"
ALLOWED_HTTP_CODES = [200]
diff --git a/contentctl/helper/splunk_app.py b/contentctl/helper/splunk_app.py
index 34920e54..6e0d877f 100644
--- a/contentctl/helper/splunk_app.py
+++ b/contentctl/helper/splunk_app.py
@@ -1,7 +1,7 @@
import json
-from typing import Optional, Collection
-from pathlib import Path
import xml.etree.ElementTree as ET
+from pathlib import Path
+from typing import Collection, Optional
from urllib.parse import urlencode
import requests
@@ -139,7 +139,7 @@ def get_app_info_by_uid(self) -> dict:
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise SplunkBaseError(
- f"Error fetching app info for app_uid {self.app_uid}: {str(e)}"
+ f"Error fetching app info for app_uid {self.app_uid}: {e!s}"
)
# parse JSON and set cache
@@ -191,7 +191,7 @@ def set_app_uid(self) -> None:
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise SplunkBaseError(
- f"Error fetching app_uid for app_name_id '{self.app_name_id}': {str(e)}"
+ f"Error fetching app_uid for app_name_id '{self.app_name_id}': {e!s}"
)
# Extract the app_uid from the redirect path
@@ -233,7 +233,7 @@ def __fetch_url_latest_version_info(self) -> str:
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise SplunkBaseError(
- f"Error fetching app entries for app_name_id '{self.app_name_id}': {str(e)}"
+ f"Error fetching app entries for app_name_id '{self.app_name_id}': {e!s}"
)
# parse xml
@@ -269,7 +269,7 @@ def __fetch_url_latest_version_download(self, info_url: str) -> str:
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise SplunkBaseError(
- f"Error fetching download info for app_name_id '{self.app_name_id}': {str(e)}"
+ f"Error fetching download info for app_name_id '{self.app_name_id}': {e!s}"
)
# parse XML and extract download URL
diff --git a/contentctl/helper/utils.py b/contentctl/helper/utils.py
index 78b44226..b11613ac 100644
--- a/contentctl/helper/utils.py
+++ b/contentctl/helper/utils.py
@@ -60,7 +60,7 @@ def get_security_content_files_from_directory(
if not path.exists() or not path.is_dir():
raise Exception(
- f"Unable to get security_content files, required directory '{str(path)}' does not exist or is not a directory"
+ f"Unable to get security_content files, required directory '{path!s}' does not exist or is not a directory"
)
allowedFiles: list[pathlib.Path] = []
@@ -275,7 +275,7 @@ def verify_file_exists(
# This is a file and we know it exists
return None
except Exception as e:
- print(f"Could not copy local file {file_path} the file because {str(e)}")
+ print(f"Could not copy local file {file_path} the file because {e!s}")
# Try to make a head request to verify existence of the file
try:
@@ -285,7 +285,7 @@ def verify_file_exists(
if req.status_code > 400:
raise (Exception(f"Return code={req.status_code}"))
except Exception as e:
- raise (Exception(f"HTTP Resolution Failed: {str(e)}"))
+ raise (Exception(f"HTTP Resolution Failed: {e!s}"))
@staticmethod
def copy_local_file(
@@ -326,7 +326,7 @@ def copy_local_file(
except Exception as e:
raise (
Exception(
- f"Error: Could not copy local file [{sourcePath}] to [{destPath}]: [{str(e)}]"
+ f"Error: Could not copy local file [{sourcePath}] to [{destPath}]: [{e!s}]"
)
)
if verbose_print:
@@ -417,26 +417,26 @@ def download_file_from_http(
except requests.exceptions.ConnectionError as e:
raise (
Exception(
- f"Error: Could not download file [{file_path}] to [{destinationPath}] (Unable to connect to server. Are you sure the server exists and you have connectivity to it?): [{str(e)}]"
+ f"Error: Could not download file [{file_path}] to [{destinationPath}] (Unable to connect to server. Are you sure the server exists and you have connectivity to it?): [{e!s}]"
)
)
except requests.exceptions.HTTPError as e:
raise (
Exception(
- f"Error: Could not download file [{file_path}] to [{destinationPath}] (The file was probably not found on the server): [{str(e)}]"
+ f"Error: Could not download file [{file_path}] to [{destinationPath}] (The file was probably not found on the server): [{e!s}]"
)
)
except requests.exceptions.Timeout as e:
raise (
Exception(
- f"Error: Could not download file [{file_path}] to [{destinationPath}] (Timeout getting file): [{str(e)}]"
+ f"Error: Could not download file [{file_path}] to [{destinationPath}] (Timeout getting file): [{e!s}]"
)
)
except Exception as e:
raise (
Exception(
- f"Error: Could not download file [{file_path}] to [{destinationPath}] (Unknown Reason): [{str(e)}]"
+ f"Error: Could not download file [{file_path}] to [{destinationPath}] (Unknown Reason): [{e!s}]"
)
)
finally:
diff --git a/contentctl/input/director.py b/contentctl/input/director.py
index 5b8a42d7..c6e6b462 100644
--- a/contentctl/input/director.py
+++ b/contentctl/input/director.py
@@ -316,7 +316,7 @@ def createSecurityContent(
print(f"{Colors.BOLD}{Colors.BRIGHT_MAGENTA}╚{'═' * 60}╝{Colors.END}\n")
print(
- f"{Colors.BOLD}{Colors.GREEN}{Colors.SPARKLE} Validation Completed{Colors.END} – Issues detected in {Colors.RED}{Colors.BOLD}{len(validation_errors)}{Colors.END} files.\n"
+ f"{Colors.BOLD}{Colors.GREEN}{Colors.SPARKLE} Validation Completed{Colors.END} - Issues detected in {Colors.RED}{Colors.BOLD}{len(validation_errors)}{Colors.END} files.\n"
)
for index, entry in enumerate(validation_errors, 1):
@@ -372,7 +372,7 @@ def createSecurityContent(
f" {Colors.RED}{Colors.ERROR} {error_msg}{Colors.END}"
)
else:
- print(f" {Colors.RED}{Colors.ERROR} {str(error)}{Colors.END}")
+ print(f" {Colors.RED}{Colors.ERROR} {error!s}{Colors.END}")
print("")
# Clean footer with next steps
diff --git a/contentctl/input/new_content_questions.py b/contentctl/input/new_content_questions.py
index a7ce0e56..35621ae1 100644
--- a/contentctl/input/new_content_questions.py
+++ b/contentctl/input/new_content_questions.py
@@ -1,4 +1,5 @@
from typing import Any
+
from contentctl.objects.enums import DataSource
diff --git a/contentctl/input/yml_reader.py b/contentctl/input/yml_reader.py
index 84418563..3a7fa07a 100644
--- a/contentctl/input/yml_reader.py
+++ b/contentctl/input/yml_reader.py
@@ -16,7 +16,7 @@ def load_file(
file_handler = open(file_path, "r", encoding="utf-8")
except OSError as exc:
print(
- f"\nThere was an unrecoverable error when opening the file '{file_path}' - we will exit immediately:\n{str(exc)}"
+ f"\nThere was an unrecoverable error when opening the file '{file_path}' - we will exit immediately:\n{exc!s}"
)
sys.exit(1)
@@ -57,7 +57,7 @@ def load_file(
)
except yaml.YAMLError as exc:
print(
- f"\nThere was an unrecoverable YML Parsing error when reading or parsing the file '{file_path}' - we will exit immediately:\n{str(exc)}"
+ f"\nThere was an unrecoverable YML Parsing error when reading or parsing the file '{file_path}' - we will exit immediately:\n{exc!s}"
)
sys.exit(1)
diff --git a/contentctl/objects/abstract_security_content_objects/detection_abstract.py b/contentctl/objects/abstract_security_content_objects/detection_abstract.py
index 81e8f737..d0c2e785 100644
--- a/contentctl/objects/abstract_security_content_objects/detection_abstract.py
+++ b/contentctl/objects/abstract_security_content_objects/detection_abstract.py
@@ -463,7 +463,7 @@ def risk(self) -> list[dict[str, Any]]:
)
"""
action.risk.param._risk
- of the conf file only contains a list of dicts. We do not eant to
+ of the conf file only contains a list of dicts. We do not eant to
include the message here, so we do not return it.
"""
rba_dict = self.rba.model_dump()
diff --git a/contentctl/objects/alert_action.py b/contentctl/objects/alert_action.py
index c50e9bdb..d90f01e7 100644
--- a/contentctl/objects/alert_action.py
+++ b/contentctl/objects/alert_action.py
@@ -1,12 +1,14 @@
from __future__ import annotations
-from pydantic import BaseModel, model_serializer, ConfigDict
+
from typing import Optional
+from pydantic import BaseModel, ConfigDict, model_serializer
+
from contentctl.objects.deployment_email import DeploymentEmail
from contentctl.objects.deployment_notable import DeploymentNotable
+from contentctl.objects.deployment_phantom import DeploymentPhantom
from contentctl.objects.deployment_rba import DeploymentRBA
from contentctl.objects.deployment_slack import DeploymentSlack
-from contentctl.objects.deployment_phantom import DeploymentPhantom
class AlertAction(BaseModel):
diff --git a/contentctl/objects/atomic.py b/contentctl/objects/atomic.py
index 49ac443b..5c518942 100644
--- a/contentctl/objects/atomic.py
+++ b/contentctl/objects/atomic.py
@@ -1,16 +1,19 @@
from __future__ import annotations
+
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from contentctl.objects.config import validate
-from contentctl.input.yml_reader import YmlReader
-from pydantic import BaseModel, model_validator, ConfigDict, FilePath, UUID4
import dataclasses
-from typing import List, Optional, Dict, Union, Self
import pathlib
-from enum import StrEnum, auto
import uuid
+from enum import StrEnum, auto
+from typing import Dict, List, Optional, Self, Union
+
+from pydantic import UUID4, BaseModel, ConfigDict, FilePath, model_validator
+
+from contentctl.input.yml_reader import YmlReader
class SupportedPlatform(StrEnum):
@@ -125,7 +128,7 @@ def parseArtRepo(cls, repo_path: pathlib.Path) -> dict[uuid.UUID, AtomicTest]:
try:
atomic_files.append(cls.constructAtomicFile(obj_path))
except Exception as e:
- error_messages.append(f"File [{obj_path}]\n{str(e)}")
+ error_messages.append(f"File [{obj_path}]\n{e!s}")
if len(error_messages) > 0:
exceptions_string = "\n\n".join(error_messages)
diff --git a/contentctl/objects/base_test.py b/contentctl/objects/base_test.py
index 4505b4d3..5cd174b8 100644
--- a/contentctl/objects/base_test.py
+++ b/contentctl/objects/base_test.py
@@ -1,6 +1,6 @@
+from abc import ABC, abstractmethod
from enum import StrEnum
from typing import Union
-from abc import ABC, abstractmethod
from pydantic import BaseModel, ConfigDict
diff --git a/contentctl/objects/base_test_result.py b/contentctl/objects/base_test_result.py
index c528969a..8ddd003c 100644
--- a/contentctl/objects/base_test_result.py
+++ b/contentctl/objects/base_test_result.py
@@ -1,7 +1,7 @@
-from typing import Union, Any
from enum import StrEnum
+from typing import Any, Union
-from pydantic import ConfigDict, BaseModel
+from pydantic import BaseModel, ConfigDict
from splunklib.data import Record # type: ignore
from contentctl.helper.utils import Utils
diff --git a/contentctl/objects/baseline_tags.py b/contentctl/objects/baseline_tags.py
index c8911cc9..54a4c2ac 100644
--- a/contentctl/objects/baseline_tags.py
+++ b/contentctl/objects/baseline_tags.py
@@ -1,18 +1,19 @@
from __future__ import annotations
+
+from typing import Any, List, Union
+
from pydantic import (
BaseModel,
+ ConfigDict,
Field,
- field_validator,
ValidationInfo,
+ field_validator,
model_serializer,
- ConfigDict,
)
-from typing import List, Any, Union
-from contentctl.objects.story import Story
from contentctl.objects.detection import Detection
-from contentctl.objects.enums import SecurityContentProductName
-from contentctl.objects.enums import SecurityDomain
+from contentctl.objects.enums import SecurityContentProductName, SecurityDomain
+from contentctl.objects.story import Story
class BaselineTags(BaseModel):
diff --git a/contentctl/objects/config.py b/contentctl/objects/config.py
index bffa66d6..6d785cb0 100644
--- a/contentctl/objects/config.py
+++ b/contentctl/objects/config.py
@@ -200,7 +200,7 @@ def validate_version(cls, v, values):
raise (
ValueError(
"The specified version does not follow the semantic versioning spec "
- f"(https://semver.org/). {str(e)}"
+ f"(https://semver.org/). {e!s}"
)
)
return v
@@ -1094,7 +1094,7 @@ def dumpCICDPlanAndQuit(self, githash: str, detections: List[Detection]):
f"Successfully wrote a test plan for [{len(self.mode.files)} detections] using [{len(self.apps)} apps] to [{output_file}]"
)
except Exception as e:
- raise Exception(f"Error writing test plan file [{output_file}]: {str(e)}")
+ raise Exception(f"Error writing test plan file [{output_file}]: {e!s}")
def getLocalAppDir(self) -> pathlib.Path:
# docker really wants absolute paths
@@ -1189,7 +1189,7 @@ def getContainerInfrastructureObjects(self) -> Self:
return self
except Exception as e:
- raise ValueError(f"Error constructing container test_instances: {str(e)}")
+ raise ValueError(f"Error constructing container test_instances: {e!s}")
@model_validator(mode="after")
def ensureAppsAreGood(self) -> Self:
@@ -1213,7 +1213,7 @@ def ensureAppsAreGood(self) -> Self:
stage_file=False, include_custom_app=False
)
except Exception as e:
- raise Exception(f"Error validating test apps: {str(e)}")
+ raise Exception(f"Error validating test apps: {e!s}")
return self
def getContainerEnvironmentString(
@@ -1363,7 +1363,7 @@ def releaseNotesFilename(self, filename: str) -> pathlib.Path:
p.mkdir(exist_ok=True, parents=True)
except Exception as e:
raise Exception(
- f"Error making the directory '{p}' to hold release_notes: {str(e)}"
+ f"Error making the directory '{p}' to hold release_notes: {e!s}"
)
return p / filename
diff --git a/contentctl/objects/dashboard.py b/contentctl/objects/dashboard.py
index 4c2d05b8..f05a2d23 100644
--- a/contentctl/objects/dashboard.py
+++ b/contentctl/objects/dashboard.py
@@ -79,7 +79,7 @@ def validate_fields_from_json(cls, data: Any) -> Any:
try:
json_obj: dict[str, Any] = json.load(jsonFilePointer)
except Exception as e:
- raise ValueError(f"Unable to load data from {json_file_path}: {str(e)}")
+ raise ValueError(f"Unable to load data from {json_file_path}: {e!s}")
name_from_file = data.get("name", None)
name_from_json = json_obj.get("title", None)
diff --git a/contentctl/objects/deployment_email.py b/contentctl/objects/deployment_email.py
index a4f829f0..92e00c74 100644
--- a/contentctl/objects/deployment_email.py
+++ b/contentctl/objects/deployment_email.py
@@ -1,4 +1,5 @@
from __future__ import annotations
+
from pydantic import BaseModel, ConfigDict
diff --git a/contentctl/objects/deployment_notable.py b/contentctl/objects/deployment_notable.py
index 8ac77568..ea0037bc 100644
--- a/contentctl/objects/deployment_notable.py
+++ b/contentctl/objects/deployment_notable.py
@@ -1,7 +1,9 @@
from __future__ import annotations
-from pydantic import BaseModel, ConfigDict
+
from typing import List
+from pydantic import BaseModel, ConfigDict
+
class DeploymentNotable(BaseModel):
model_config = ConfigDict(extra="forbid")
diff --git a/contentctl/objects/deployment_phantom.py b/contentctl/objects/deployment_phantom.py
index 8bd96516..bc8e58c2 100644
--- a/contentctl/objects/deployment_phantom.py
+++ b/contentctl/objects/deployment_phantom.py
@@ -1,4 +1,5 @@
from __future__ import annotations
+
from pydantic import BaseModel, ConfigDict
diff --git a/contentctl/objects/deployment_rba.py b/contentctl/objects/deployment_rba.py
index 53df26f4..3fc4394f 100644
--- a/contentctl/objects/deployment_rba.py
+++ b/contentctl/objects/deployment_rba.py
@@ -1,4 +1,5 @@
from __future__ import annotations
+
from pydantic import BaseModel, ConfigDict
diff --git a/contentctl/objects/deployment_scheduling.py b/contentctl/objects/deployment_scheduling.py
index 177fbb30..4783c89e 100644
--- a/contentctl/objects/deployment_scheduling.py
+++ b/contentctl/objects/deployment_scheduling.py
@@ -1,4 +1,5 @@
from __future__ import annotations
+
from pydantic import BaseModel, ConfigDict
diff --git a/contentctl/objects/deployment_slack.py b/contentctl/objects/deployment_slack.py
index 63f99767..e6224c6e 100644
--- a/contentctl/objects/deployment_slack.py
+++ b/contentctl/objects/deployment_slack.py
@@ -1,4 +1,5 @@
from __future__ import annotations
+
from pydantic import BaseModel, ConfigDict
diff --git a/contentctl/objects/detection_stanza.py b/contentctl/objects/detection_stanza.py
index 8d4b545d..6ddfee98 100644
--- a/contentctl/objects/detection_stanza.py
+++ b/contentctl/objects/detection_stanza.py
@@ -1,6 +1,6 @@
-from typing import ClassVar
import hashlib
from functools import cached_property
+from typing import ClassVar
from pydantic import BaseModel, Field, computed_field
diff --git a/contentctl/objects/integration_test.py b/contentctl/objects/integration_test.py
index c1078e89..63cc8f09 100644
--- a/contentctl/objects/integration_test.py
+++ b/contentctl/objects/integration_test.py
@@ -1,9 +1,9 @@
from pydantic import Field
from contentctl.objects.base_test import BaseTest, TestType
-from contentctl.objects.unit_test import UnitTest
-from contentctl.objects.integration_test_result import IntegrationTestResult
from contentctl.objects.base_test_result import TestResultStatus
+from contentctl.objects.integration_test_result import IntegrationTestResult
+from contentctl.objects.unit_test import UnitTest
class IntegrationTest(BaseTest):
diff --git a/contentctl/objects/investigation_tags.py b/contentctl/objects/investigation_tags.py
index a8ff6307..02847a33 100644
--- a/contentctl/objects/investigation_tags.py
+++ b/contentctl/objects/investigation_tags.py
@@ -1,18 +1,21 @@
from __future__ import annotations
+
from typing import List
+
from pydantic import (
BaseModel,
+ ConfigDict,
Field,
- field_validator,
ValidationInfo,
+ field_validator,
model_serializer,
- ConfigDict,
)
-from contentctl.objects.story import Story
+
from contentctl.objects.enums import (
SecurityContentInvestigationProductName,
SecurityDomain,
)
+from contentctl.objects.story import Story
class InvestigationTags(BaseModel):
diff --git a/contentctl/objects/manual_test.py b/contentctl/objects/manual_test.py
index d77f21fe..04c2580b 100644
--- a/contentctl/objects/manual_test.py
+++ b/contentctl/objects/manual_test.py
@@ -2,10 +2,10 @@
from pydantic import Field
-from contentctl.objects.test_attack_data import TestAttackData
-from contentctl.objects.manual_test_result import ManualTestResult
from contentctl.objects.base_test import BaseTest, TestType
from contentctl.objects.base_test_result import TestResultStatus
+from contentctl.objects.manual_test_result import ManualTestResult
+from contentctl.objects.test_attack_data import TestAttackData
class ManualTest(BaseTest):
diff --git a/contentctl/objects/playbook_tags.py b/contentctl/objects/playbook_tags.py
index 3068e6df..6924098a 100644
--- a/contentctl/objects/playbook_tags.py
+++ b/contentctl/objects/playbook_tags.py
@@ -1,7 +1,10 @@
from __future__ import annotations
-from typing import Optional, List
-from pydantic import BaseModel, Field, ConfigDict
+
import enum
+from typing import List, Optional
+
+from pydantic import BaseModel, ConfigDict, Field
+
from contentctl.objects.detection import Detection
diff --git a/contentctl/objects/risk_analysis_action.py b/contentctl/objects/risk_analysis_action.py
index aa0d2c89..e04abcec 100644
--- a/contentctl/objects/risk_analysis_action.py
+++ b/contentctl/objects/risk_analysis_action.py
@@ -1,5 +1,5 @@
-from typing import Any
import json
+from typing import Any
from pydantic import BaseModel, field_validator
diff --git a/contentctl/objects/savedsearches_conf.py b/contentctl/objects/savedsearches_conf.py
index 582ec5d9..79fcec28 100644
--- a/contentctl/objects/savedsearches_conf.py
+++ b/contentctl/objects/savedsearches_conf.py
@@ -1,8 +1,8 @@
-from pathlib import Path
-from typing import Any, ClassVar
import re
-import tempfile
import tarfile
+import tempfile
+from pathlib import Path
+from typing import Any, ClassVar
from pydantic import BaseModel, Field, PrivateAttr
diff --git a/contentctl/objects/story_tags.py b/contentctl/objects/story_tags.py
index 1abf30f3..6af77346 100644
--- a/contentctl/objects/story_tags.py
+++ b/contentctl/objects/story_tags.py
@@ -45,7 +45,7 @@ class StoryTags(BaseModel):
def getCategory_conf(self) -> str:
# if len(self.category) > 1:
# print("Story with more than 1 category. We can only have 1 category, fix it!")
- return list(self.category)[0]
+ return next(iter(self.category))
@model_serializer
def serialize_model(self):
diff --git a/contentctl/objects/test_group.py b/contentctl/objects/test_group.py
index cd651f54..28b67b07 100644
--- a/contentctl/objects/test_group.py
+++ b/contentctl/objects/test_group.py
@@ -1,9 +1,9 @@
from pydantic import BaseModel
-from contentctl.objects.unit_test import UnitTest
+from contentctl.objects.base_test_result import TestResultStatus
from contentctl.objects.integration_test import IntegrationTest
from contentctl.objects.test_attack_data import TestAttackData
-from contentctl.objects.base_test_result import TestResultStatus
+from contentctl.objects.unit_test import UnitTest
class TestGroup(BaseModel):
diff --git a/contentctl/objects/throttling.py b/contentctl/objects/throttling.py
index de6f9cd9..da7137d3 100644
--- a/contentctl/objects/throttling.py
+++ b/contentctl/objects/throttling.py
@@ -1,6 +1,7 @@
-from pydantic import BaseModel, Field, field_validator
from typing import Annotated
+from pydantic import BaseModel, Field, field_validator
+
# Alert Suppression/Throttling settings have been taken from
# https://docs.splunk.com/Documentation/Splunk/9.2.2/Admin/Savedsearchesconf
diff --git a/contentctl/objects/unit_test.py b/contentctl/objects/unit_test.py
index 1bfc6308..d84a0de6 100644
--- a/contentctl/objects/unit_test.py
+++ b/contentctl/objects/unit_test.py
@@ -2,10 +2,10 @@
from pydantic import Field
-from contentctl.objects.test_attack_data import TestAttackData
-from contentctl.objects.unit_test_result import UnitTestResult
from contentctl.objects.base_test import BaseTest, TestType
from contentctl.objects.base_test_result import TestResultStatus
+from contentctl.objects.test_attack_data import TestAttackData
+from contentctl.objects.unit_test_result import UnitTestResult
class UnitTest(BaseTest):
diff --git a/contentctl/objects/unit_test_baseline.py b/contentctl/objects/unit_test_baseline.py
index ca7e2a3e..d4b5ed2a 100644
--- a/contentctl/objects/unit_test_baseline.py
+++ b/contentctl/objects/unit_test_baseline.py
@@ -1,6 +1,7 @@
-from pydantic import BaseModel, ConfigDict
from typing import Union
+from pydantic import BaseModel, ConfigDict
+
class UnitTestBaseline(BaseModel):
model_config = ConfigDict(extra="forbid")
diff --git a/contentctl/objects/unit_test_result.py b/contentctl/objects/unit_test_result.py
index 56aa863a..c8b62834 100644
--- a/contentctl/objects/unit_test_result.py
+++ b/contentctl/objects/unit_test_result.py
@@ -1,7 +1,9 @@
from __future__ import annotations
-from typing import Union, TYPE_CHECKING
+from typing import TYPE_CHECKING, Union
+
from splunklib.data import Record
+
from contentctl.objects.base_test_result import BaseTestResult, TestResultStatus
if TYPE_CHECKING:
@@ -70,7 +72,7 @@ def set_job_content(
elif content is None:
self.status = TestResultStatus.ERROR
if self.exception is not None:
- self.message = f"EXCEPTION: {str(self.exception)}"
+ self.message = f"EXCEPTION: {self.exception!s}"
else:
self.message = "ERROR with no more specific message available."
self.sid_link = NO_SID
diff --git a/contentctl/output/conf_output.py b/contentctl/output/conf_output.py
index b90d0121..df8c0d8a 100644
--- a/contentctl/output/conf_output.py
+++ b/contentctl/output/conf_output.py
@@ -270,7 +270,7 @@ def packageAppSlim(self) -> None:
output_dir=pathlib.Path(self.config.getBuildDir()),
)
except SystemExit as e:
- raise Exception(f"Error building package with slim: {str(e)}")
+ raise Exception(f"Error building package with slim: {e!s}")
except Exception as e:
print(
@@ -278,7 +278,7 @@ def packageAppSlim(self) -> None:
"Packaging app with tar instead. This should still work, but appinspect may catch "
"errors that otherwise would have been flagged by slim."
)
- raise Exception(f"slim (splunk packaging toolkit) not installed: {str(e)}")
+ raise Exception(f"slim (splunk packaging toolkit) not installed: {e!s}")
def packageApp(self, method: Callable[[ConfOutput], None] = packageAppTar) -> None:
return method(self)
diff --git a/contentctl/output/conf_writer.py b/contentctl/output/conf_writer.py
index bcfb6d19..9b366e5a 100644
--- a/contentctl/output/conf_writer.py
+++ b/contentctl/output/conf_writer.py
@@ -339,7 +339,7 @@ def writeConfFile(
f"Name:{obj.name if not isinstance(obj, CustomApp) else obj.title}\n"
f"Type {type(obj)}: \n"
f"Output File: {app_output_path}\n"
- f"Error: {str(e)}\n"
+ f"Error: {e!s}\n"
)
output_path.parent.mkdir(parents=True, exist_ok=True)
@@ -376,7 +376,7 @@ def validateConfFile(path: pathlib.Path):
try:
_ = configparser.RawConfigParser().read(path)
except Exception as e:
- raise Exception(f"Failed to validate .conf file {str(path)}: {str(e)}")
+ raise Exception(f"Failed to validate .conf file {path!s}: {e!s}")
@staticmethod
def validateXmlFile(path: pathlib.Path):
@@ -390,7 +390,7 @@ def validateXmlFile(path: pathlib.Path):
with open(path, "r") as xmlFile:
_ = ET.fromstring(xmlFile.read())
except Exception as e:
- raise Exception(f"Failed to validate .xml file {str(path)}: {str(e)}")
+ raise Exception(f"Failed to validate .xml file {path!s}: {e!s}")
@staticmethod
def validateManifestFile(path: pathlib.Path):
@@ -405,9 +405,9 @@ def validateManifestFile(path: pathlib.Path):
_ = json.load(manifestFile)
except Exception as e:
raise Exception(
- f"Failed to validate .manifest file {str(path)} (Note that .manifest files should contain only valid JSON-formatted data): {str(e)}"
+ f"Failed to validate .manifest file {path!s} (Note that .manifest files should contain only valid JSON-formatted data): {e!s}"
)
except Exception as e:
raise Exception(
- f"Failed to validate .manifest file {str(path)} (Note that .manifest files should contain only valid JSON-formatted data): {str(e)}"
+ f"Failed to validate .manifest file {path!s} (Note that .manifest files should contain only valid JSON-formatted data): {e!s}"
)
diff --git a/contentctl/output/doc_md_output.py b/contentctl/output/doc_md_output.py
index 9d128742..26c732b6 100644
--- a/contentctl/output/doc_md_output.py
+++ b/contentctl/output/doc_md_output.py
@@ -1,6 +1,5 @@
import os
import sys
-
from pathlib import Path
from contentctl.output.jinja_writer import JinjaWriter
diff --git a/contentctl/output/jinja_writer.py b/contentctl/output/jinja_writer.py
index 0cd0a45a..6a626743 100644
--- a/contentctl/output/jinja_writer.py
+++ b/contentctl/output/jinja_writer.py
@@ -1,5 +1,6 @@
import os
from typing import Any
+
from jinja2 import Environment, FileSystemLoader
diff --git a/contentctl/output/json_writer.py b/contentctl/output/json_writer.py
index c17ac787..f823494a 100644
--- a/contentctl/output/json_writer.py
+++ b/contentctl/output/json_writer.py
@@ -27,5 +27,5 @@ def writeJsonObject(
except Exception as e:
raise Exception(
- f"Error serializing object to Json File '{file_path}': {str(e)}"
+ f"Error serializing object to Json File '{file_path}': {e!s}"
)
diff --git a/contentctl/output/yml_writer.py b/contentctl/output/yml_writer.py
index a7d0381a..3c84e7cc 100644
--- a/contentctl/output/yml_writer.py
+++ b/contentctl/output/yml_writer.py
@@ -1,6 +1,7 @@
-import yaml
+from enum import IntEnum, StrEnum
from typing import Any
-from enum import StrEnum, IntEnum
+
+import yaml
# Set the following so that we can write StrEnum and IntEnum
# to files. Otherwise, we will get the following errors when trying
diff --git a/pyproject.toml b/pyproject.toml
index ca1726d8..46d940d7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,7 +30,7 @@ tqdm = "^4.66.5"
pygit2 = "^1.15.1"
#We are pinned to this version of tyro because 0.9.23 and above
#have an issue when parsing an extremely large number of files
-#(in our testing great than 130) when using the mode:selected
+#(in our testing great than 130) when using the mode:selected
#--mode.files command.
tyro = "^0.9.2,<0.9.23"
gitpython = "^3.1.43"
@@ -38,7 +38,7 @@ setuptools = ">=69.5.1,<81.0.0"
rich = "^14.0.0"
[tool.poetry.group.dev.dependencies]
-ruff = "^0.11.2"
+ruff = "^0.12.5"
[build-system]
requires = ["poetry-core>=1.0.0"]
@@ -47,32 +47,32 @@ build-backend = "poetry.core.masonry.api"
[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
- ".bzr",
- ".direnv",
- ".eggs",
- ".git",
- ".git-rewrite",
- ".hg",
- ".ipynb_checkpoints",
- ".mypy_cache",
- ".nox",
- ".pants.d",
- ".pyenv",
- ".pytest_cache",
- ".pytype",
- ".ruff_cache",
- ".svn",
- ".tox",
- ".venv",
- ".vscode",
- "__pypackages__",
- "_build",
- "buck-out",
- "build",
- "dist",
- "node_modules",
- "site-packages",
- "venv",
+ ".bzr",
+ ".direnv",
+ ".eggs",
+ ".git",
+ ".git-rewrite",
+ ".hg",
+ ".ipynb_checkpoints",
+ ".mypy_cache",
+ ".nox",
+ ".pants.d",
+ ".pyenv",
+ ".pytest_cache",
+ ".pytype",
+ ".ruff_cache",
+ ".svn",
+ ".tox",
+ ".venv",
+ ".vscode",
+ "__pypackages__",
+ "_build",
+ "buck-out",
+ "build",
+ "dist",
+ "node_modules",
+ "site-packages",
+ "venv",
]
# Same as Black.
@@ -85,8 +85,8 @@ target-version = "py311"
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
-select = ["E4", "E7", "E9", "F"]
-ignore = []
+select = ["E", "W", "F", "I", "RUF"]
+ignore = ["E501", "RUF012"]
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]