contentctl 5.1.0__py3-none-any.whl → 5.2.0__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.
- contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +1 -1
- contentctl/objects/abstract_security_content_objects/detection_abstract.py +28 -1
- contentctl/objects/data_source.py +2 -0
- contentctl/objects/lookup.py +16 -3
- contentctl/output/attack_nav_output.py +11 -4
- contentctl/output/attack_nav_writer.py +53 -37
- contentctl/output/templates/transforms.j2 +2 -2
- {contentctl-5.1.0.dist-info → contentctl-5.2.0.dist-info}/METADATA +1 -1
- {contentctl-5.1.0.dist-info → contentctl-5.2.0.dist-info}/RECORD +12 -12
- {contentctl-5.1.0.dist-info → contentctl-5.2.0.dist-info}/LICENSE.md +0 -0
- {contentctl-5.1.0.dist-info → contentctl-5.2.0.dist-info}/WHEEL +0 -0
- {contentctl-5.1.0.dist-info → contentctl-5.2.0.dist-info}/entry_points.txt +0 -0
|
@@ -89,7 +89,7 @@ class DetectionTestingManagerOutputDto:
|
|
|
89
89
|
start_time: Union[datetime.datetime, None] = None
|
|
90
90
|
replay_index: str = "contentctl_testing_index"
|
|
91
91
|
replay_host: str = "CONTENTCTL_HOST"
|
|
92
|
-
timeout_seconds: int =
|
|
92
|
+
timeout_seconds: int = 120
|
|
93
93
|
terminate: bool = False
|
|
94
94
|
|
|
95
95
|
|
|
@@ -474,7 +474,7 @@ class Detection_Abstract(SecurityContentObject):
|
|
|
474
474
|
"name": lookup.name,
|
|
475
475
|
"description": lookup.description,
|
|
476
476
|
"filename": lookup.filename.name,
|
|
477
|
-
"default_match":
|
|
477
|
+
"default_match": lookup.default_match,
|
|
478
478
|
"case_sensitive_match": "true"
|
|
479
479
|
if lookup.case_sensitive_match
|
|
480
480
|
else "false",
|
|
@@ -1055,3 +1055,30 @@ class Detection_Abstract(SecurityContentObject):
|
|
|
1055
1055
|
# Return the summary
|
|
1056
1056
|
|
|
1057
1057
|
return summary_dict
|
|
1058
|
+
|
|
1059
|
+
@model_validator(mode="after")
|
|
1060
|
+
def validate_data_source_output_fields(self):
|
|
1061
|
+
# Skip validation for Hunting and Correlation types, or non-production detections
|
|
1062
|
+
if self.status != DetectionStatus.production or self.type in {
|
|
1063
|
+
AnalyticsType.Hunting,
|
|
1064
|
+
AnalyticsType.Correlation,
|
|
1065
|
+
}:
|
|
1066
|
+
return self
|
|
1067
|
+
|
|
1068
|
+
# Validate that all required output fields are present in the search
|
|
1069
|
+
for data_source in self.data_source_objects:
|
|
1070
|
+
if not data_source.output_fields:
|
|
1071
|
+
continue
|
|
1072
|
+
|
|
1073
|
+
missing_fields = [
|
|
1074
|
+
field for field in data_source.output_fields if field not in self.search
|
|
1075
|
+
]
|
|
1076
|
+
|
|
1077
|
+
if missing_fields:
|
|
1078
|
+
raise ValueError(
|
|
1079
|
+
f"Data source '{data_source.name}' has output fields "
|
|
1080
|
+
f"{missing_fields} that are not present in the search "
|
|
1081
|
+
f"for detection '{self.name}'"
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
return self
|
|
@@ -17,10 +17,12 @@ class DataSource(SecurityContentObject):
|
|
|
17
17
|
source: str = Field(...)
|
|
18
18
|
sourcetype: str = Field(...)
|
|
19
19
|
separator: Optional[str] = None
|
|
20
|
+
separator_value: None | str = None
|
|
20
21
|
configuration: Optional[str] = None
|
|
21
22
|
supported_TA: list[TA] = []
|
|
22
23
|
fields: None | list = None
|
|
23
24
|
field_mappings: None | list = None
|
|
25
|
+
mitre_components: list[str] = []
|
|
24
26
|
convert_to_log_source: None | list = None
|
|
25
27
|
example_log: None | str = None
|
|
26
28
|
output_fields: list[str] = []
|
contentctl/objects/lookup.py
CHANGED
|
@@ -6,9 +6,10 @@ import pathlib
|
|
|
6
6
|
import re
|
|
7
7
|
from enum import StrEnum, auto
|
|
8
8
|
from functools import cached_property
|
|
9
|
-
from typing import TYPE_CHECKING, Annotated, Any, Literal,
|
|
9
|
+
from typing import TYPE_CHECKING, Annotated, Any, Literal, Self
|
|
10
10
|
|
|
11
11
|
from pydantic import (
|
|
12
|
+
BeforeValidator,
|
|
12
13
|
Field,
|
|
13
14
|
FilePath,
|
|
14
15
|
NonNegativeInt,
|
|
@@ -69,7 +70,19 @@ class Lookup_Type(StrEnum):
|
|
|
69
70
|
|
|
70
71
|
# TODO (#220): Split Lookup into 2 classes
|
|
71
72
|
class Lookup(SecurityContentObject, abc.ABC):
|
|
72
|
-
|
|
73
|
+
# We need to make sure that this is converted to a string because we widely
|
|
74
|
+
# use the string "False" in our lookup content. However, PyYAML reads this
|
|
75
|
+
# as a BOOL and this causes parsing to fail. As such, we will always
|
|
76
|
+
# convert this to a string if it is passed as a bool
|
|
77
|
+
default_match: Annotated[
|
|
78
|
+
str, BeforeValidator(lambda dm: str(dm).lower() if isinstance(dm, bool) else dm)
|
|
79
|
+
] = Field(
|
|
80
|
+
default="",
|
|
81
|
+
description="This field is given a default value of ''"
|
|
82
|
+
"because it is the default value specified in the transforms.conf "
|
|
83
|
+
"docs. Giving it a type of str rather than str | None simplifies "
|
|
84
|
+
"the typing for the field.",
|
|
85
|
+
)
|
|
73
86
|
# Per the documentation for transforms.conf, EXACT should not be specified in this list,
|
|
74
87
|
# so we include only WILDCARD and CIDR
|
|
75
88
|
match_type: list[Annotated[str, Field(pattern=r"(^WILDCARD|CIDR)\(.+\)$")]] = Field(
|
|
@@ -88,7 +101,7 @@ class Lookup(SecurityContentObject, abc.ABC):
|
|
|
88
101
|
|
|
89
102
|
# All fields custom to this model
|
|
90
103
|
model = {
|
|
91
|
-
"default_match":
|
|
104
|
+
"default_match": self.default_match,
|
|
92
105
|
"match_type": self.match_type_to_conf_format,
|
|
93
106
|
"min_matches": self.min_matches,
|
|
94
107
|
"max_matches": self.max_matches,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from typing import List, Union
|
|
2
1
|
import pathlib
|
|
2
|
+
from typing import List, Union
|
|
3
3
|
|
|
4
4
|
from contentctl.objects.detection import Detection
|
|
5
5
|
from contentctl.output.attack_nav_writer import AttackNavWriter
|
|
@@ -10,14 +10,21 @@ class AttackNavOutput:
|
|
|
10
10
|
self, detections: List[Detection], output_path: pathlib.Path
|
|
11
11
|
) -> None:
|
|
12
12
|
techniques: dict[str, dict[str, Union[List[str], int]]] = {}
|
|
13
|
+
|
|
13
14
|
for detection in detections:
|
|
14
15
|
for tactic in detection.tags.mitre_attack_id:
|
|
15
16
|
if tactic not in techniques:
|
|
16
17
|
techniques[tactic] = {"score": 0, "file_paths": []}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
detection_type = detection.source
|
|
20
|
+
detection_id = detection.id
|
|
21
|
+
|
|
22
|
+
# Store all three pieces of information separately
|
|
23
|
+
detection_info = f"{detection_type}|{detection_id}|{detection.name}"
|
|
24
|
+
|
|
25
|
+
techniques[tactic]["score"] = techniques[tactic].get("score", 0) + 1
|
|
26
|
+
if isinstance(techniques[tactic]["file_paths"], list):
|
|
27
|
+
techniques[tactic]["file_paths"].append(detection_info)
|
|
21
28
|
|
|
22
29
|
"""
|
|
23
30
|
for detection in objects:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import Union, List
|
|
3
2
|
import pathlib
|
|
3
|
+
from typing import List, Union
|
|
4
4
|
|
|
5
|
-
VERSION = "4.
|
|
5
|
+
VERSION = "4.5"
|
|
6
6
|
NAME = "Detection Coverage"
|
|
7
|
-
DESCRIPTION = "
|
|
8
|
-
DOMAIN = "
|
|
7
|
+
DESCRIPTION = "Security Content Detection Coverage"
|
|
8
|
+
DOMAIN = "enterprise-attack"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class AttackNavWriter:
|
|
@@ -14,52 +14,68 @@ class AttackNavWriter:
|
|
|
14
14
|
mitre_techniques: dict[str, dict[str, Union[List[str], int]]],
|
|
15
15
|
output_path: pathlib.Path,
|
|
16
16
|
) -> None:
|
|
17
|
-
max_count =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
max_count = mitre_techniques[technique_id]["score"]
|
|
17
|
+
max_count = max(
|
|
18
|
+
(technique["score"] for technique in mitre_techniques.values()), default=0
|
|
19
|
+
)
|
|
21
20
|
|
|
22
21
|
layer_json = {
|
|
23
|
-
"
|
|
22
|
+
"versions": {"attack": "16", "navigator": "5.1.0", "layer": VERSION},
|
|
24
23
|
"name": NAME,
|
|
25
24
|
"description": DESCRIPTION,
|
|
26
25
|
"domain": DOMAIN,
|
|
27
26
|
"techniques": [],
|
|
27
|
+
"gradient": {
|
|
28
|
+
"colors": ["#ffffff", "#66b1ff", "#096ed7"],
|
|
29
|
+
"minValue": 0,
|
|
30
|
+
"maxValue": max_count,
|
|
31
|
+
},
|
|
32
|
+
"filters": {
|
|
33
|
+
"platforms": [
|
|
34
|
+
"Windows",
|
|
35
|
+
"Linux",
|
|
36
|
+
"macOS",
|
|
37
|
+
"Network",
|
|
38
|
+
"AWS",
|
|
39
|
+
"GCP",
|
|
40
|
+
"Azure",
|
|
41
|
+
"Azure AD",
|
|
42
|
+
"Office 365",
|
|
43
|
+
"SaaS",
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"layout": {
|
|
47
|
+
"layout": "side",
|
|
48
|
+
"showName": True,
|
|
49
|
+
"showID": True,
|
|
50
|
+
"showAggregateScores": False,
|
|
51
|
+
},
|
|
52
|
+
"legendItems": [
|
|
53
|
+
{"label": "No detections", "color": "#ffffff"},
|
|
54
|
+
{"label": "Has detections", "color": "#66b1ff"},
|
|
55
|
+
],
|
|
56
|
+
"showTacticRowBackground": True,
|
|
57
|
+
"tacticRowBackground": "#dddddd",
|
|
58
|
+
"selectTechniquesAcrossTactics": True,
|
|
28
59
|
}
|
|
29
60
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
layer_json["filters"] = {
|
|
37
|
-
"platforms": [
|
|
38
|
-
"Windows",
|
|
39
|
-
"Linux",
|
|
40
|
-
"macOS",
|
|
41
|
-
"AWS",
|
|
42
|
-
"GCP",
|
|
43
|
-
"Azure",
|
|
44
|
-
"Office 365",
|
|
45
|
-
"SaaS",
|
|
46
|
-
]
|
|
47
|
-
}
|
|
61
|
+
for technique_id, data in mitre_techniques.items():
|
|
62
|
+
links = []
|
|
63
|
+
for detection_info in data["file_paths"]:
|
|
64
|
+
# Split the detection info into its components
|
|
65
|
+
detection_type, detection_id, detection_name = detection_info.split("|")
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
# Construct research website URL (without the name)
|
|
68
|
+
research_url = (
|
|
69
|
+
f"https://research.splunk.com/{detection_type}/{detection_id}/"
|
|
70
|
+
)
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
layer_json["tacticRowBackground"] = "#dddddd"
|
|
56
|
-
layer_json["sorting"] = 3
|
|
72
|
+
links.append({"label": detection_name, "url": research_url})
|
|
57
73
|
|
|
58
|
-
for technique_id in mitre_techniques.keys():
|
|
59
74
|
layer_technique = {
|
|
60
75
|
"techniqueID": technique_id,
|
|
61
|
-
"score":
|
|
62
|
-
"
|
|
76
|
+
"score": data["score"],
|
|
77
|
+
"enabled": True,
|
|
78
|
+
"links": links,
|
|
63
79
|
}
|
|
64
80
|
layer_json["techniques"].append(layer_technique)
|
|
65
81
|
|
|
@@ -7,8 +7,8 @@ filename = {{ lookup.app_filename.name }}
|
|
|
7
7
|
collection = {{ lookup.collection }}
|
|
8
8
|
external_type = kvstore
|
|
9
9
|
{% endif %}
|
|
10
|
-
{% if lookup.default_match
|
|
11
|
-
default_match = {{ lookup.default_match
|
|
10
|
+
{% if lookup.default_match != '' %}
|
|
11
|
+
default_match = {{ lookup.default_match }}
|
|
12
12
|
{% endif %}
|
|
13
13
|
{% if lookup.case_sensitive_match is defined and lookup.case_sensitive_match != None %}
|
|
14
14
|
case_sensitive_match = {{ lookup.case_sensitive_match | lower }}
|
|
@@ -4,7 +4,7 @@ contentctl/actions/deploy_acs.py,sha256=w3OqO8GXzB_5zHrE8lDYbadAy4Etw7F2o84Gze74
|
|
|
4
4
|
contentctl/actions/detection_testing/DetectionTestingManager.py,sha256=TWZpmDjMqWRWyzsLyiYol_jAovAr6ok9J_GzE9-kNN0,9079
|
|
5
5
|
contentctl/actions/detection_testing/GitService.py,sha256=a6y7lqCgSL1KdSVEgJDxawea8ZgEkGNfOKEf9v_BgLo,11135
|
|
6
6
|
contentctl/actions/detection_testing/generate_detection_coverage_badge.py,sha256=bGUVKjKv96lTw1GZ4Kw1JX-Yicu4aOJWm-IL524e9HI,2302
|
|
7
|
-
contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py,sha256=
|
|
7
|
+
contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py,sha256=dus-8ANS0VgKq1HOdis4wVF5DKtdot6csbcpayxqXDo,57282
|
|
8
8
|
contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py,sha256=qYWgRW7uc-15jzwv5xSUF2xyLDmtyGyMfuXkQK9j-aM,7221
|
|
9
9
|
contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureServer.py,sha256=Q1ZfCYOp54O39bgTScZMInkmZiU-bGAM9Hiwr2mq5ms,370
|
|
10
10
|
contentctl/actions/detection_testing/progress_bar.py,sha256=UrpNCqxTmQ4hfoRZgxPJ1xvDVwMrTq0UnotdryHN0gM,3232
|
|
@@ -32,7 +32,7 @@ contentctl/helper/utils.py,sha256=rigwZzCwWzn11sKTVWDkYEtLmRSf0yBbJ671OSRQnOM,19
|
|
|
32
32
|
contentctl/input/director.py,sha256=7mx93-k_8DUc4pwYjxwq39n8-BlDYZOvRBkdzdGA8Qc,10919
|
|
33
33
|
contentctl/input/new_content_questions.py,sha256=z2C4Mg7-EyxtiF2z9m4SnSbi6QO4CUPB3wg__JeMXIQ,4067
|
|
34
34
|
contentctl/input/yml_reader.py,sha256=ymmAqsWsf9Oj56waDOhCh_E4SomkSCmu4dAx7iURFt8,2050
|
|
35
|
-
contentctl/objects/abstract_security_content_objects/detection_abstract.py,sha256=
|
|
35
|
+
contentctl/objects/abstract_security_content_objects/detection_abstract.py,sha256=VMt_0eA8eFa64DD2j96kIqfhREVNMPBeJAclsv0eFUw,43978
|
|
36
36
|
contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py,sha256=P1v5n8hM4XzJOjNZbJ5m3y4Bu-cQYaddLPdbE6K-4Xw,12164
|
|
37
37
|
contentctl/objects/alert_action.py,sha256=iEvdEOT4TrTXT0z4rQ_W5v79hPJpPhFPSzo7TuHDxwA,1376
|
|
38
38
|
contentctl/objects/annotated_types.py,sha256=xR4EKvdOpNDEt0doGs8XjxCzKK99J2NHZgHFAmt7p2c,424
|
|
@@ -45,7 +45,7 @@ contentctl/objects/config.py,sha256=3l8tFVwrBDpAnS7aBgj6to0Kc8_s4bxuZY5Bm5vel8k,
|
|
|
45
45
|
contentctl/objects/constants.py,sha256=VwwQtJBGC_zb3ukjb3A7P0CwAlyhacWiXczwAW5Jiog,5466
|
|
46
46
|
contentctl/objects/correlation_search.py,sha256=ab6v-0nbzujhTMpwaXynQiInWpRO1zB5KR4eZLCav_M,45234
|
|
47
47
|
contentctl/objects/dashboard.py,sha256=9DiHP_Nx2XBQv4-zUaz6v9XH5yeTJhxaGDlaQqCsbIU,4468
|
|
48
|
-
contentctl/objects/data_source.py,sha256=
|
|
48
|
+
contentctl/objects/data_source.py,sha256=AtB6lAe43mx0GicphWTkmYupp8Fb1hmWgm_GpbX73wo,1567
|
|
49
49
|
contentctl/objects/deployment.py,sha256=FRsgsX2T1gvA_0A44_sFPr22rsedxXVIhtO7o9F7eZM,2902
|
|
50
50
|
contentctl/objects/deployment_email.py,sha256=_Sdr_BNjvXECiFonRHLkiOrIQp3slnUaERbptqRbD0Q,206
|
|
51
51
|
contentctl/objects/deployment_notable.py,sha256=j5AniTRDcw32El5H91qKOXDVZvUYxnIuM4Zzlhrm9cM,258
|
|
@@ -64,7 +64,7 @@ contentctl/objects/integration_test.py,sha256=TYjKyH4YinUnYXOse5BQGCa4-ez_5mtoMw
|
|
|
64
64
|
contentctl/objects/integration_test_result.py,sha256=_uUSgqgjFhEZM8UwOJI6Q9K-ekIrbKU6OPdqHZycl-s,279
|
|
65
65
|
contentctl/objects/investigation.py,sha256=kmvQ0grCd1YVEW5wVF4-_Rx87PnOxPiNoN_ufTLc3ZM,3659
|
|
66
66
|
contentctl/objects/investigation_tags.py,sha256=qDGNusrWDvCX_GcBEzag2MydSV0LIhGxoXZGgxDXfHA,1317
|
|
67
|
-
contentctl/objects/lookup.py,sha256=
|
|
67
|
+
contentctl/objects/lookup.py,sha256=uP8N2Munu9vDGOzkC3mBlM93xkICyVBi62fAXAloI_I,13656
|
|
68
68
|
contentctl/objects/macro.py,sha256=usbxyOPIRIJoDmvawfP2DxtFNf71GaDwffxiZsRkP5A,3594
|
|
69
69
|
contentctl/objects/manual_test.py,sha256=cx_XAtQ8VG8Ui_F553Xnut75vFEOtRwm1dDIIWNpOaM,952
|
|
70
70
|
contentctl/objects/manual_test_result.py,sha256=FyCVVf-f1DKs-qBkM4tbKfY6mkrW25NcIEBqyaDC2rE,156
|
|
@@ -89,8 +89,8 @@ contentctl/objects/unit_test.py,sha256=-rtSmZ8N2UZ4NkDsfzNXzXiF6dTDwt_jsQ_14xp0h
|
|
|
89
89
|
contentctl/objects/unit_test_baseline.py,sha256=ezg8Ctih_3che2ln2tuVCAtRPHaf5tDMR3dGb34MqaA,287
|
|
90
90
|
contentctl/objects/unit_test_result.py,sha256=gqHqYN5XGBKdV-mdKhAdwfOw4_PpN3i9z_b6ciByDSc,2928
|
|
91
91
|
contentctl/output/api_json_output.py,sha256=QWe_KWlHHxE4Mhd3BHRfJbUJ4z2mLHZn_eMWfMVInik,8237
|
|
92
|
-
contentctl/output/attack_nav_output.py,sha256=
|
|
93
|
-
contentctl/output/attack_nav_writer.py,sha256=
|
|
92
|
+
contentctl/output/attack_nav_output.py,sha256=cbQNZkcNCKaQm7Ht70_tcmTvixtsuVDjQB4BpZ8s-Ts,2489
|
|
93
|
+
contentctl/output/attack_nav_writer.py,sha256=AiQU3q8hzz_lJECI-sjyqOsWx64HUugg3aAHEeZl-qM,2750
|
|
94
94
|
contentctl/output/conf_output.py,sha256=2_ofRqMro4xmFzf6ZmPRDd93pCG-LQhOiB_kE4owADc,10609
|
|
95
95
|
contentctl/output/conf_writer.py,sha256=9eqt2tm1xjs397pwWLz5oPJcMHbs62ejRG7KghGQQCI,15137
|
|
96
96
|
contentctl/output/data_source_writer.py,sha256=hjr0b5zfJ2UHcDLbCkmTrqma1ngu8F5vWFPJEwOZwU8,1860
|
|
@@ -124,7 +124,7 @@ contentctl/output/templates/savedsearches_baselines.j2,sha256=WHZB4e0vmeym8832Vx
|
|
|
124
124
|
contentctl/output/templates/savedsearches_detections.j2,sha256=B7_b8aBjEKAV7mJm0DxUS_HyCt63bQZtpacCQfDgDqc,6033
|
|
125
125
|
contentctl/output/templates/savedsearches_investigations.j2,sha256=KH2r8SgyAMiettSHypSbA2-1XmQ_8_8xzk3BkbZ1Re4,1196
|
|
126
126
|
contentctl/output/templates/server.conf.j2,sha256=sPZUkiuJNGm9R8rpjfRKyuAvmmQb0C4w9Q6hpmvmPeU,127
|
|
127
|
-
contentctl/output/templates/transforms.j2,sha256=
|
|
127
|
+
contentctl/output/templates/transforms.j2,sha256=TEKZi8DWpcCysRTNvuLEgAwx-g1SZ2E0CkLiu6v6AlU,1339
|
|
128
128
|
contentctl/output/templates/workflow_actions.j2,sha256=DFoZVnCa8dMRHjW2AdpoydBC0THgiH_W-Nx7WI4-uR4,925
|
|
129
129
|
contentctl/output/yml_writer.py,sha256=gGgbamHWunHKjj47TcqB04k0xliX6w3H7iajZtUZRSU,2124
|
|
130
130
|
contentctl/templates/README.md,sha256=GoRmywUqwnjaehY_GLmGqxsFXCLP9plpDYwB6W6nVPs,428
|
|
@@ -161,8 +161,8 @@ contentctl/templates/detections/web/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
|
161
161
|
contentctl/templates/macros/security_content_ctime.yml,sha256=Gg1YNllHVsX_YB716H1SJLWzxXZEfuJlnsgB2fuyoHU,159
|
|
162
162
|
contentctl/templates/macros/security_content_summariesonly.yml,sha256=9BYUxAl2E4Nwh8K19F3AJS8Ka7ceO6ZDBjFiO3l3LY0,162
|
|
163
163
|
contentctl/templates/stories/cobalt_strike.yml,sha256=uj8idtDNOAIqpZ9p8usQg6mop1CQkJ5TlB4Q7CJdTIE,3082
|
|
164
|
-
contentctl-5.
|
|
165
|
-
contentctl-5.
|
|
166
|
-
contentctl-5.
|
|
167
|
-
contentctl-5.
|
|
168
|
-
contentctl-5.
|
|
164
|
+
contentctl-5.2.0.dist-info/LICENSE.md,sha256=hQWUayRk-pAiOZbZnuy8djmoZkjKBx8MrCFpW-JiOgo,11344
|
|
165
|
+
contentctl-5.2.0.dist-info/METADATA,sha256=SxtNJ4XnhZzf7fFzZR3NWsl7hPwh3gX1lqwRTzQFaGc,5097
|
|
166
|
+
contentctl-5.2.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
167
|
+
contentctl-5.2.0.dist-info/entry_points.txt,sha256=5bjZ2NkbQfSwK47uOnA77yCtjgXhvgxnmCQiynRF_-U,57
|
|
168
|
+
contentctl-5.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|