1
- import traceback
1
+ import pathlib
2
2
import sys
3
+ import traceback
3
4
import warnings
4
- import pathlib
5
+
5
6
import tyro
6
7
7
- from contentctl .actions .initialize import Initialize
8
- from contentctl .objects .config import init , validate , build , new , deploy_acs , test , test_servers , inspect , report , test_common , release_notes
9
- from contentctl .actions .validate import Validate
10
- from contentctl .actions .new_content import NewContent
8
+ from contentctl .actions .build import Build , BuildInputDto , DirectorOutputDto
9
+ from contentctl .actions .deploy_acs import Deploy
11
10
from contentctl .actions .detection_testing .GitService import GitService
12
- from contentctl .actions .build import (
13
- BuildInputDto ,
14
- DirectorOutputDto ,
15
- Build ,
16
- )
17
- from contentctl .actions .test import Test
18
- from contentctl .actions .test import TestInputDto
19
- from contentctl .actions .reporting import ReportingInputDto , Reporting
11
+ from contentctl .actions .initialize import Initialize
20
12
from contentctl .actions .inspect import Inspect
21
- from contentctl .input .yml_reader import YmlReader
22
- from contentctl .actions .deploy_acs import Deploy
13
+ from contentctl .actions .new_content import NewContent
23
14
from contentctl .actions .release_notes import ReleaseNotes
15
+ from contentctl .actions .reporting import Reporting , ReportingInputDto
16
+ from contentctl .actions .test import Test , TestInputDto
17
+ from contentctl .actions .validate import Validate
18
+ from contentctl .input .yml_reader import YmlReader
19
+ from contentctl .objects .config import (
20
+ build ,
21
+ deploy_acs ,
22
+ init ,
23
+ inspect ,
24
+ new ,
25
+ release_notes ,
26
+ report ,
27
+ test ,
28
+ test_common ,
29
+ test_servers ,
30
+ validate ,
31
+ )
24
32
25
33
# def print_ascii_art():
26
34
# print(
27
35
# """
28
- # Running Splunk Security Content Control Tool (contentctl)
36
+ # Running Splunk Security Content Control Tool (contentctl)
29
37
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
30
38
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢶⠛⡇⠀⠀⠀⠀⠀⠀⣠⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
31
39
# ⠀⠀⠀⠀⠀⠀⠀⠀⣀⠼⠖⠛⠋⠉⠉⠓⠢⣴⡻⣾⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
53
61
# )
54
62
55
63
56
-
57
-
58
- def init_func (config :test ):
64
+ def init_func (config : test ):
59
65
Initialize ().execute (config )
60
66
61
67
62
- def validate_func (config :validate )-> DirectorOutputDto :
68
+ def validate_func (config : validate ) -> DirectorOutputDto :
63
69
validate = Validate ()
64
70
return validate .execute (config )
65
71
66
- def report_func (config :report )-> None :
72
+
73
+ def report_func (config : report ) -> None :
67
74
# First, perform validation. Remember that the validate
68
75
# configuration is actually a subset of the build configuration
69
76
director_output_dto = validate_func (config )
70
-
71
- r = Reporting ()
72
- return r .execute (ReportingInputDto (director_output_dto = director_output_dto ,
73
- config = config ))
74
-
75
77
76
- def build_func (config :build )-> DirectorOutputDto :
78
+ r = Reporting ()
79
+ return r .execute (
80
+ ReportingInputDto (director_output_dto = director_output_dto , config = config )
81
+ )
82
+
83
+
84
+ def build_func (config : build ) -> DirectorOutputDto :
77
85
# First, perform validation. Remember that the validate
78
86
# configuration is actually a subset of the build configuration
79
87
director_output_dto = validate_func (config )
80
88
builder = Build ()
81
89
return builder .execute (BuildInputDto (director_output_dto , config ))
82
90
83
- def inspect_func (config :inspect )-> str :
84
- #Make sure that we have built the most recent version of the app
91
+
92
+ def inspect_func (config : inspect ) -> str :
93
+ # Make sure that we have built the most recent version of the app
85
94
_ = build_func (config )
86
95
inspect_token = Inspect ().execute (config )
87
96
return inspect_token
88
-
89
97
90
- def release_notes_func (config :release_notes )-> None :
98
+
99
+ def release_notes_func (config : release_notes ) -> None :
91
100
ReleaseNotes ().release_notes (config )
92
101
93
- def new_func (config :new ):
94
- NewContent ().execute (config )
95
102
103
+ def new_func (config : new ):
104
+ NewContent ().execute (config )
96
105
97
106
98
- def deploy_acs_func (config :deploy_acs ):
107
+ def deploy_acs_func (config : deploy_acs ):
99
108
print ("Building and inspecting app..." )
100
109
token = inspect_func (config )
101
110
print ("App successfully built and inspected." )
102
111
print ("Deploying app..." )
103
112
Deploy ().execute (config , token )
104
113
105
- def test_common_func (config :test_common ):
114
+
115
+ def test_common_func (config : test_common ):
106
116
if type (config ) == test :
107
- #construct the container Infrastructure objects
117
+ # construct the container Infrastructure objects
108
118
config .getContainerInfrastructureObjects ()
109
- #otherwise, they have already been passed as servers
119
+ # otherwise, they have already been passed as servers
110
120
111
121
director_output_dto = build_func (config )
112
- gitServer = GitService (director = director_output_dto ,config = config )
122
+ gitServer = GitService (director = director_output_dto , config = config )
113
123
detections_to_test = gitServer .getContent ()
114
124
115
-
116
-
117
125
test_input_dto = TestInputDto (detections_to_test , config )
118
-
126
+
119
127
t = Test ()
120
128
t .filter_tests (test_input_dto )
121
-
129
+
122
130
if config .plan_only :
123
- #Emit the test plan and quit. Do not actually run the test
124
- config .dumpCICDPlanAndQuit (gitServer .getHash (),test_input_dto .detections )
125
- return
126
-
131
+ # Emit the test plan and quit. Do not actually run the test
132
+ config .dumpCICDPlanAndQuit (gitServer .getHash (), test_input_dto .detections )
133
+ return
134
+
127
135
success = t .execute (test_input_dto )
128
-
136
+
129
137
if success :
130
- #Everything passed!
138
+ # Everything passed!
131
139
print ("All tests have run successfully or been marked as 'skipped'" )
132
140
return
133
141
raise Exception ("There was at least one unsuccessful test" )
134
142
143
+
144
+ CONTENTCTL_5_WARNING = """
145
+ *****************************************************************************
146
+ WARNING - THIS IS AN ALPHA BUILD OF CONTENTCTL 5.
147
+ THERE HAVE BEEN NUMEROUS CHANGES IN CONTENTCTL (ESPECIALLY TO YML FORMATS).
148
+ YOU ALMOST CERTAINLY DO NOT WANT TO USE THIS BUILD.
149
+ IF YOU ENCOUNTER ERRORS, PLEASE USE THE LATEST CURRENTYLY SUPPORTED RELEASE:
150
+
151
+ CONTENTCTL==4.4.7
152
+
153
+ YOU HAVE BEEN WARNED!
154
+ *****************************************************************************
155
+ """
156
+
157
+
135
158
def main ():
159
+ print (CONTENTCTL_5_WARNING )
136
160
try :
137
161
configFile = pathlib .Path ("contentctl.yml" )
138
-
162
+
139
163
# We MUST load a config (with testing info) object so that we can
140
164
# properly construct the command line, including 'contentctl test' parameters.
141
165
if not configFile .is_file ():
142
- if "init" not in sys .argv and "--help" not in sys .argv and "-h" not in sys .argv :
143
- raise Exception (f"'{ configFile } ' not found in the current directory.\n "
144
- "Please ensure you are in the correct directory or run 'contentctl init' to create a new content pack." )
145
-
166
+ if (
167
+ "init" not in sys .argv
168
+ and "--help" not in sys .argv
169
+ and "-h" not in sys .argv
170
+ ):
171
+ raise Exception (
172
+ f"'{ configFile } ' not found in the current directory.\n "
173
+ "Please ensure you are in the correct directory or run 'contentctl init' to create a new content pack."
174
+ )
175
+
146
176
if "--help" in sys .argv or "-h" in sys .argv :
147
- print ("Warning - contentctl.yml is missing from this directory. The configuration values showed at the default and are informational only.\n "
148
- "Please ensure that contentctl.yml exists by manually creating it or running 'contentctl init'" )
177
+ print (
178
+ "Warning - contentctl.yml is missing from this directory. The configuration values showed at the default and are informational only.\n "
179
+ "Please ensure that contentctl.yml exists by manually creating it or running 'contentctl init'"
180
+ )
149
181
# Otherwise generate a stub config file.
150
182
# It will be used during init workflow
151
183
152
184
t = test ()
153
185
config_obj = t .model_dump ()
154
-
186
+
155
187
else :
156
- #The file exists, so load it up!
157
- config_obj = YmlReader ().load_file (configFile ,add_fields = False )
188
+ # The file exists, so load it up!
189
+ config_obj = YmlReader ().load_file (configFile , add_fields = False )
158
190
t = test .model_validate (config_obj )
159
191
except Exception as e :
160
192
print (f"Error validating 'contentctl.yml':\n { str (e )} " )
161
193
sys .exit (1 )
162
-
163
-
194
+
164
195
# For ease of generating the constructor, we want to allow construction
165
196
# of an object from default values WITHOUT requiring all fields to be declared
166
197
# with defaults OR in the config file. As such, we construct the model rather
@@ -169,30 +200,26 @@ def main():
169
200
170
201
models = tyro .extras .subcommand_type_from_defaults (
171
202
{
172
- "init" :init .model_validate (config_obj ),
203
+ "init" : init .model_validate (config_obj ),
173
204
"validate" : validate .model_validate (config_obj ),
174
205
"report" : report .model_validate (config_obj ),
175
- "build" :build .model_validate (config_obj ),
206
+ "build" : build .model_validate (config_obj ),
176
207
"inspect" : inspect .model_construct (** t .__dict__ ),
177
- "new" :new .model_validate (config_obj ),
178
- "test" :test .model_validate (config_obj ),
179
- "test_servers" :test_servers .model_construct (** t .__dict__ ),
208
+ "new" : new .model_validate (config_obj ),
209
+ "test" : test .model_validate (config_obj ),
210
+ "test_servers" : test_servers .model_construct (** t .__dict__ ),
180
211
"release_notes" : release_notes .model_construct (** config_obj ),
181
- "deploy_acs" : deploy_acs .model_construct (** t .__dict__ )
212
+ "deploy_acs" : deploy_acs .model_construct (** t .__dict__ ),
182
213
}
183
214
)
184
-
185
-
186
215
187
-
188
216
config = None
189
217
try :
190
218
# Since some model(s) were constructed and not model_validated, we have to catch
191
219
# warnings again when creating the cli
192
220
with warnings .catch_warnings (action = "ignore" ):
193
221
config = tyro .cli (models )
194
222
195
-
196
223
if type (config ) == init :
197
224
t .__dict__ .update (config .__dict__ )
198
225
init_func (t )
@@ -219,21 +246,29 @@ def main():
219
246
print (e )
220
247
sys .exit (1 )
221
248
except Exception as e :
249
+ print (CONTENTCTL_5_WARNING )
250
+
222
251
if config is None :
223
- print ("There was a serious issue where the config file could not be created.\n "
224
- "The entire stack trace is provided below (please include it if filing a bug report).\n " )
252
+ print (
253
+ "There was a serious issue where the config file could not be created.\n "
254
+ "The entire stack trace is provided below (please include it if filing a bug report).\n "
255
+ )
225
256
traceback .print_exc ()
226
257
elif config .verbose :
227
- print ("Verbose error logging is ENABLED.\n "
228
- "The entire stack trace has been provided below (please include it if filing a bug report):\n " )
258
+ print (
259
+ "Verbose error logging is ENABLED.\n "
260
+ "The entire stack trace has been provided below (please include it if filing a bug report):\n "
261
+ )
229
262
traceback .print_exc ()
230
263
else :
231
- print ("Verbose error logging is DISABLED.\n "
232
- "Please use the --verbose command line argument if you need more context for your error or file a bug report." )
264
+ print (
265
+ "Verbose error logging is DISABLED.\n "
266
+ "Please use the --verbose command line argument if you need more context for your error or file a bug report."
267
+ )
233
268
print (e )
234
-
269
+
235
270
sys .exit (1 )
236
271
237
272
238
273
if __name__ == "__main__" :
239
- main ()
274
+ main ()
0 commit comments