contentctl 5.5.7__py3-none-any.whl → 5.5.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. contentctl/actions/deploy_acs.py +5 -3
  2. contentctl/actions/detection_testing/DetectionTestingManager.py +3 -3
  3. contentctl/actions/detection_testing/GitService.py +4 -4
  4. contentctl/actions/detection_testing/generate_detection_coverage_badge.py +3 -3
  5. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +15 -17
  6. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +9 -8
  7. contentctl/actions/detection_testing/progress_bar.py +2 -1
  8. contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +4 -3
  9. contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +4 -2
  10. contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +7 -7
  11. contentctl/actions/doc_gen.py +1 -2
  12. contentctl/actions/release_notes.py +2 -2
  13. contentctl/actions/reporting.py +3 -3
  14. contentctl/actions/test.py +2 -3
  15. contentctl/actions/validate.py +1 -1
  16. contentctl/api.py +7 -6
  17. contentctl/contentctl.py +1 -1
  18. contentctl/enrichments/attack_enrichment.py +1 -1
  19. contentctl/enrichments/cve_enrichment.py +9 -6
  20. contentctl/enrichments/splunk_app_enrichment.py +5 -4
  21. contentctl/helper/link_validator.py +7 -7
  22. contentctl/helper/splunk_app.py +6 -6
  23. contentctl/helper/utils.py +8 -8
  24. contentctl/input/director.py +3 -2
  25. contentctl/input/new_content_questions.py +1 -0
  26. contentctl/input/yml_reader.py +2 -2
  27. contentctl/objects/abstract_security_content_objects/detection_abstract.py +1 -1
  28. contentctl/objects/alert_action.py +4 -2
  29. contentctl/objects/atomic.py +8 -5
  30. contentctl/objects/base_test.py +1 -1
  31. contentctl/objects/base_test_result.py +2 -2
  32. contentctl/objects/baseline_tags.py +7 -6
  33. contentctl/objects/config.py +5 -5
  34. contentctl/objects/correlation_search.py +156 -139
  35. contentctl/objects/dashboard.py +1 -1
  36. contentctl/objects/deployment_email.py +1 -0
  37. contentctl/objects/deployment_notable.py +3 -1
  38. contentctl/objects/deployment_phantom.py +1 -0
  39. contentctl/objects/deployment_rba.py +1 -0
  40. contentctl/objects/deployment_scheduling.py +1 -0
  41. contentctl/objects/deployment_slack.py +1 -0
  42. contentctl/objects/detection_stanza.py +1 -1
  43. contentctl/objects/integration_test.py +2 -2
  44. contentctl/objects/investigation_tags.py +6 -3
  45. contentctl/objects/manual_test.py +2 -2
  46. contentctl/objects/playbook_tags.py +5 -2
  47. contentctl/objects/risk_analysis_action.py +1 -1
  48. contentctl/objects/savedsearches_conf.py +3 -3
  49. contentctl/objects/story_tags.py +1 -1
  50. contentctl/objects/test_group.py +2 -2
  51. contentctl/objects/throttling.py +2 -1
  52. contentctl/objects/unit_test.py +2 -2
  53. contentctl/objects/unit_test_baseline.py +2 -1
  54. contentctl/objects/unit_test_result.py +4 -2
  55. contentctl/output/conf_output.py +2 -2
  56. contentctl/output/conf_writer.py +5 -5
  57. contentctl/output/doc_md_output.py +0 -1
  58. contentctl/output/jinja_writer.py +1 -0
  59. contentctl/output/json_writer.py +1 -1
  60. contentctl/output/yml_writer.py +3 -2
  61. {contentctl-5.5.7.dist-info → contentctl-5.5.8.dist-info}/METADATA +3 -3
  62. {contentctl-5.5.7.dist-info → contentctl-5.5.8.dist-info}/RECORD +65 -65
  63. {contentctl-5.5.7.dist-info → contentctl-5.5.8.dist-info}/LICENSE.md +0 -0
  64. {contentctl-5.5.7.dist-info → contentctl-5.5.8.dist-info}/WHEEL +0 -0
  65. {contentctl-5.5.7.dist-info → contentctl-5.5.8.dist-info}/entry_points.txt +0 -0
@@ -60,7 +60,7 @@ class Utils:
60
60
 
61
61
  if not path.exists() or not path.is_dir():
62
62
  raise Exception(
63
- f"Unable to get security_content files, required directory '{str(path)}' does not exist or is not a directory"
63
+ f"Unable to get security_content files, required directory '{path!s}' does not exist or is not a directory"
64
64
  )
65
65
 
66
66
  allowedFiles: list[pathlib.Path] = []
@@ -275,7 +275,7 @@ class Utils:
275
275
  # This is a file and we know it exists
276
276
  return None
277
277
  except Exception as e:
278
- print(f"Could not copy local file {file_path} the file because {str(e)}")
278
+ print(f"Could not copy local file {file_path} the file because {e!s}")
279
279
 
280
280
  # Try to make a head request to verify existence of the file
281
281
  try:
@@ -285,7 +285,7 @@ class Utils:
285
285
  if req.status_code > 400:
286
286
  raise (Exception(f"Return code={req.status_code}"))
287
287
  except Exception as e:
288
- raise (Exception(f"HTTP Resolution Failed: {str(e)}"))
288
+ raise (Exception(f"HTTP Resolution Failed: {e!s}"))
289
289
 
290
290
  @staticmethod
291
291
  def copy_local_file(
@@ -326,7 +326,7 @@ class Utils:
326
326
  except Exception as e:
327
327
  raise (
328
328
  Exception(
329
- f"Error: Could not copy local file [{sourcePath}] to [{destPath}]: [{str(e)}]"
329
+ f"Error: Could not copy local file [{sourcePath}] to [{destPath}]: [{e!s}]"
330
330
  )
331
331
  )
332
332
  if verbose_print:
@@ -417,26 +417,26 @@ class Utils:
417
417
  except requests.exceptions.ConnectionError as e:
418
418
  raise (
419
419
  Exception(
420
- 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)}]"
420
+ 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}]"
421
421
  )
422
422
  )
423
423
 
424
424
  except requests.exceptions.HTTPError as e:
425
425
  raise (
426
426
  Exception(
427
- f"Error: Could not download file [{file_path}] to [{destinationPath}] (The file was probably not found on the server): [{str(e)}]"
427
+ f"Error: Could not download file [{file_path}] to [{destinationPath}] (The file was probably not found on the server): [{e!s}]"
428
428
  )
429
429
  )
430
430
  except requests.exceptions.Timeout as e:
431
431
  raise (
432
432
  Exception(
433
- f"Error: Could not download file [{file_path}] to [{destinationPath}] (Timeout getting file): [{str(e)}]"
433
+ f"Error: Could not download file [{file_path}] to [{destinationPath}] (Timeout getting file): [{e!s}]"
434
434
  )
435
435
  )
436
436
  except Exception as e:
437
437
  raise (
438
438
  Exception(
439
- f"Error: Could not download file [{file_path}] to [{destinationPath}] (Unknown Reason): [{str(e)}]"
439
+ f"Error: Could not download file [{file_path}] to [{destinationPath}] (Unknown Reason): [{e!s}]"
440
440
  )
441
441
  )
442
442
  finally:
@@ -200,6 +200,7 @@ class Director:
200
200
  author=self.input_dto.app.author_name,
201
201
  description="A lookup file that contains the data source objects for detections.",
202
202
  lookup_type=Lookup_Type.csv,
203
+ case_sensitive_match=False,
203
204
  contents=RuntimeCsvWriter.generateDatasourceCSVContent(
204
205
  self.output_dto.data_sources
205
206
  ),
@@ -315,7 +316,7 @@ class Director:
315
316
  print(f"{Colors.BOLD}{Colors.BRIGHT_MAGENTA}╚{'═' * 60}╝{Colors.END}\n")
316
317
 
317
318
  print(
318
- 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"
319
+ 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"
319
320
  )
320
321
 
321
322
  for index, entry in enumerate(validation_errors, 1):
@@ -371,7 +372,7 @@ class Director:
371
372
  f" {Colors.RED}{Colors.ERROR} {error_msg}{Colors.END}"
372
373
  )
373
374
  else:
374
- print(f" {Colors.RED}{Colors.ERROR} {str(error)}{Colors.END}")
375
+ print(f" {Colors.RED}{Colors.ERROR} {error!s}{Colors.END}")
375
376
  print("")
376
377
 
377
378
  # Clean footer with next steps
@@ -1,4 +1,5 @@
1
1
  from typing import Any
2
+
2
3
  from contentctl.objects.enums import DataSource
3
4
 
4
5
 
@@ -16,7 +16,7 @@ class YmlReader:
16
16
  file_handler = open(file_path, "r", encoding="utf-8")
17
17
  except OSError as exc:
18
18
  print(
19
- f"\nThere was an unrecoverable error when opening the file '{file_path}' - we will exit immediately:\n{str(exc)}"
19
+ f"\nThere was an unrecoverable error when opening the file '{file_path}' - we will exit immediately:\n{exc!s}"
20
20
  )
21
21
  sys.exit(1)
22
22
 
@@ -57,7 +57,7 @@ class YmlReader:
57
57
  )
58
58
  except yaml.YAMLError as exc:
59
59
  print(
60
- f"\nThere was an unrecoverable YML Parsing error when reading or parsing the file '{file_path}' - we will exit immediately:\n{str(exc)}"
60
+ f"\nThere was an unrecoverable YML Parsing error when reading or parsing the file '{file_path}' - we will exit immediately:\n{exc!s}"
61
61
  )
62
62
  sys.exit(1)
63
63
 
@@ -463,7 +463,7 @@ class Detection_Abstract(SecurityContentObject):
463
463
  )
464
464
  """
465
465
  action.risk.param._risk
466
- of the conf file only contains a list of dicts. We do not eant to
466
+ of the conf file only contains a list of dicts. We do not eant to
467
467
  include the message here, so we do not return it.
468
468
  """
469
469
  rba_dict = self.rba.model_dump()
@@ -1,12 +1,14 @@
1
1
  from __future__ import annotations
2
- from pydantic import BaseModel, model_serializer, ConfigDict
2
+
3
3
  from typing import Optional
4
4
 
5
+ from pydantic import BaseModel, ConfigDict, model_serializer
6
+
5
7
  from contentctl.objects.deployment_email import DeploymentEmail
6
8
  from contentctl.objects.deployment_notable import DeploymentNotable
9
+ from contentctl.objects.deployment_phantom import DeploymentPhantom
7
10
  from contentctl.objects.deployment_rba import DeploymentRBA
8
11
  from contentctl.objects.deployment_slack import DeploymentSlack
9
- from contentctl.objects.deployment_phantom import DeploymentPhantom
10
12
 
11
13
 
12
14
  class AlertAction(BaseModel):
@@ -1,16 +1,19 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
6
  from contentctl.objects.config import validate
6
7
 
7
- from contentctl.input.yml_reader import YmlReader
8
- from pydantic import BaseModel, model_validator, ConfigDict, FilePath, UUID4
9
8
  import dataclasses
10
- from typing import List, Optional, Dict, Union, Self
11
9
  import pathlib
12
- from enum import StrEnum, auto
13
10
  import uuid
11
+ from enum import StrEnum, auto
12
+ from typing import Dict, List, Optional, Self, Union
13
+
14
+ from pydantic import UUID4, BaseModel, ConfigDict, FilePath, model_validator
15
+
16
+ from contentctl.input.yml_reader import YmlReader
14
17
 
15
18
 
16
19
  class SupportedPlatform(StrEnum):
@@ -125,7 +128,7 @@ class AtomicTest(BaseModel):
125
128
  try:
126
129
  atomic_files.append(cls.constructAtomicFile(obj_path))
127
130
  except Exception as e:
128
- error_messages.append(f"File [{obj_path}]\n{str(e)}")
131
+ error_messages.append(f"File [{obj_path}]\n{e!s}")
129
132
 
130
133
  if len(error_messages) > 0:
131
134
  exceptions_string = "\n\n".join(error_messages)
@@ -1,6 +1,6 @@
1
+ from abc import ABC, abstractmethod
1
2
  from enum import StrEnum
2
3
  from typing import Union
3
- from abc import ABC, abstractmethod
4
4
 
5
5
  from pydantic import BaseModel, ConfigDict
6
6
 
@@ -1,7 +1,7 @@
1
- from typing import Union, Any
2
1
  from enum import StrEnum
2
+ from typing import Any, Union
3
3
 
4
- from pydantic import ConfigDict, BaseModel
4
+ from pydantic import BaseModel, ConfigDict
5
5
  from splunklib.data import Record # type: ignore
6
6
 
7
7
  from contentctl.helper.utils import Utils
@@ -1,18 +1,19 @@
1
1
  from __future__ import annotations
2
+
3
+ from typing import Any, List, Union
4
+
2
5
  from pydantic import (
3
6
  BaseModel,
7
+ ConfigDict,
4
8
  Field,
5
- field_validator,
6
9
  ValidationInfo,
10
+ field_validator,
7
11
  model_serializer,
8
- ConfigDict,
9
12
  )
10
- from typing import List, Any, Union
11
13
 
12
- from contentctl.objects.story import Story
13
14
  from contentctl.objects.detection import Detection
14
- from contentctl.objects.enums import SecurityContentProductName
15
- from contentctl.objects.enums import SecurityDomain
15
+ from contentctl.objects.enums import SecurityContentProductName, SecurityDomain
16
+ from contentctl.objects.story import Story
16
17
 
17
18
 
18
19
  class BaselineTags(BaseModel):
@@ -200,7 +200,7 @@ class CustomApp(App_Base):
200
200
  raise (
201
201
  ValueError(
202
202
  "The specified version does not follow the semantic versioning spec "
203
- f"(https://semver.org/). {str(e)}"
203
+ f"(https://semver.org/). {e!s}"
204
204
  )
205
205
  )
206
206
  return v
@@ -1094,7 +1094,7 @@ class test_common(build):
1094
1094
  f"Successfully wrote a test plan for [{len(self.mode.files)} detections] using [{len(self.apps)} apps] to [{output_file}]"
1095
1095
  )
1096
1096
  except Exception as e:
1097
- raise Exception(f"Error writing test plan file [{output_file}]: {str(e)}")
1097
+ raise Exception(f"Error writing test plan file [{output_file}]: {e!s}")
1098
1098
 
1099
1099
  def getLocalAppDir(self) -> pathlib.Path:
1100
1100
  # docker really wants absolute paths
@@ -1189,7 +1189,7 @@ class test(test_common):
1189
1189
  return self
1190
1190
 
1191
1191
  except Exception as e:
1192
- raise ValueError(f"Error constructing container test_instances: {str(e)}")
1192
+ raise ValueError(f"Error constructing container test_instances: {e!s}")
1193
1193
 
1194
1194
  @model_validator(mode="after")
1195
1195
  def ensureAppsAreGood(self) -> Self:
@@ -1213,7 +1213,7 @@ class test(test_common):
1213
1213
  stage_file=False, include_custom_app=False
1214
1214
  )
1215
1215
  except Exception as e:
1216
- raise Exception(f"Error validating test apps: {str(e)}")
1216
+ raise Exception(f"Error validating test apps: {e!s}")
1217
1217
  return self
1218
1218
 
1219
1219
  def getContainerEnvironmentString(
@@ -1363,7 +1363,7 @@ class release_notes(Config_Base):
1363
1363
  p.mkdir(exist_ok=True, parents=True)
1364
1364
  except Exception as e:
1365
1365
  raise Exception(
1366
- f"Error making the directory '{p}' to hold release_notes: {str(e)}"
1366
+ f"Error making the directory '{p}' to hold release_notes: {e!s}"
1367
1367
  )
1368
1368
  return p / filename
1369
1369