11import logging
2+ import re
23import sys
34
45import click
56
7+ from startleft .api .controllers .iac .iac_type import IacType
68from startleft .api .errors import CommonError
79from startleft .iac_to_otm import IacToOtm
10+ from startleft .messages import messages
811from startleft .project .iriusrisk_project_repository import IriusriskProjectRepository
912from startleft .project .otm_project import OtmProject
1013from startleft .project .otm_project_service import OtmProjectService
1417__version__ = "0.1.0"
1518
1619
17- def validate_logging (ctx , param , value ):
20+ def get_log_level (ctx , param , value ):
1821 levels = {
19- 'none ' : 100 ,
20- 'crit ' : logging .CRITICAL ,
21- 'error ' : logging .ERROR ,
22- 'warn ' : logging .WARNING ,
23- 'info ' : logging .INFO ,
24- 'debug ' : logging .DEBUG
22+ 'NONE ' : 100 ,
23+ 'CRIT ' : logging .CRITICAL ,
24+ 'ERROR ' : logging .ERROR ,
25+ 'WARN ' : logging .WARNING ,
26+ 'INFO ' : logging .INFO ,
27+ 'DEBUG ' : logging .DEBUG
2528 }
2629
27- if value .lower () in levels :
28- return levels [value .lower ()]
30+ if value .upper () in levels :
31+ return levels [value .upper ()]
2932 raise click .BadParameter (f"Log level must be one of: { ', ' .join (levels .keys ())} " )
3033
3134
@@ -37,6 +40,16 @@ def configure_logger(level, verbose):
3740 logging .basicConfig (format = '%(message)s' , level = level )
3841
3942
43+ def validate_server (ctx , param , value ):
44+ regex = "((http|https)://)(www.)?[a-zA-Z0-9@:%._\\ +~#?&//=]{2,256}(\\ .[a-z]{2,6}\\ b([-a-zA-Z0-9@:%._\\ +~#?&//=]*))?(:[0-9]+)?"
45+ p = re .compile (regex )
46+
47+ if value is None or not re .search (p , value ):
48+ raise click .BadParameter ("IriusRisk host must follow the convention 'proto://server[:port]'" )
49+
50+ return value
51+
52+
4053class CatchAllExceptions (click .Group ):
4154 def __call__ (self , * args , ** kwargs ):
4255 try :
@@ -49,9 +62,10 @@ def __call__(self, *args, **kwargs):
4962
5063
5164@click .group (cls = CatchAllExceptions )
52- @click .option ('--log-level' , '-l' , callback = validate_logging , default = 'info' ,
53- help = 'Set the log level. Must be one of: crit, error, warn, info, debug, none.' )
54- @click .option ('--verbose/--no-verbose' , default = False , help = 'Makes logging more verbose.' )
65+ @click .option ('--log-level' , '-l' ,
66+ type = click .Choice (['CRIT' , 'ERROR' , 'WARN' , 'INFO' , 'DEBUG' , 'NONE' ], case_sensitive = False ),
67+ callback = get_log_level , default = 'info' , help = 'Set the log level.' )
68+ @click .option ('--verbose/--no-verbose' , '-v/-nv' , default = False , help = 'Makes logging more verbose.' )
5569@click .version_option (__version__ )
5670def cli (log_level , verbose ):
5771 """
@@ -61,100 +75,90 @@ def cli(log_level, verbose):
6175
6276
6377@cli .command ()
64- @click .option ('--type' , '-t' ,
65- type = click .Choice (['JSON' , 'YAML' , 'CloudFormation' , 'HCL2' , 'Terraform' , 'XML' ], case_sensitive = False ),
66- default = "JSON" ,
67- help = 'Specify the source file type.' )
68- @click .option ('--map' , '-m' , help = 'Map file to use when parsing source files' )
69- @click .option ('--otm' , '-o' , default = 'threatmodel.otm' , help = 'OTM output file name' )
70- @click .option ('--name' , '-n' , help = 'Project name' )
71- @click .option ('--id' , help = 'Project ID' )
72- @click .option ('--recreate/--no-recreate' , default = False , help = 'Delete and recreate the project each time' )
73- @click .option ('--irius-server' , default = '' , envvar = 'IRIUS_SERVER' ,
74- help = 'IriusRisk server to connect to (proto://server[:port])' )
75- @click .option ('--api-token' , default = '' , envvar = 'IRIUS_API_TOKEN' , help = 'IriusRisk API token' )
76- @click .argument ('filename' , nargs = - 1 )
77- def run (type , map , otm , name , id , recreate , irius_server , api_token , filename ):
78+ @click .option (messages .IAC_TYPE_NAME , messages .IAC_TYPE_SHORTNAME , type = click .Choice (messages .IAC_TYPE_SUPPORTED , case_sensitive = False ), required = True , help = messages .IAC_TYPE_DESC )
79+ @click .option (messages .MAPPING_FILE_NAME , messages .MAPPING_FILE_SHORTNAME , help = messages .MAPPING_FILE_DESC )
80+ @click .option (messages .OUTPUT_FILE_NAME , messages .OUTPUT_FILE_SHORTNAME , default = messages .OUTPUT_FILE , help = messages .OUTPUT_FILE_DESC )
81+ @click .option (messages .PROJECT_NAME_NAME , messages .PROJECT_NAME_SHORTNAME , required = True , help = messages .PROJECT_NAME_DESC )
82+ @click .option (messages .PROJECT_ID_NAME , messages .PROJECT_ID_SHORTNAME , required = True , help = messages .PROJECT_ID_DESC )
83+ @click .option (messages .RECREATE_NAME , messages .RECREATE_SHORTNAME , default = False , help = messages .RECREATE_DESC )
84+ @click .option (messages .IRIUS_SERVER_NAME , messages .IRIUS_SERVER_SHORTNAME , default = '' , envvar = messages .IRIUS_SERVER_ENVAR , callback = validate_server , help = messages .IRIUS_SERVER_DESC )
85+ @click .option (messages .API_TOKEN_NAME , messages .API_TOKEN_SHORTNAME , default = '' , envvar = messages .API_TOKEN_ENVAR , help = messages .API_TOKEN_DESC )
86+ @click .argument (messages .IAC_FILE_NAME , required = True , nargs = - 1 )
87+ def run (iac_type , mapping_file , output_file , project_name , project_id , recreate , irius_server , api_token ,
88+ iac_file ):
7889 """
79- Parses IaC source files into Open Threat Model and immediately uploads threat model to IriusRisk
90+ Parses an IaC file into an Open Threat Model (OTM) and uploads it to IriusRisk
8091 """
8192 logger .info ("Parsing IaC source files into OTM" )
82- otm_project = OtmProject .from_iac_file (id , name , type , filename , map , otm )
93+ otm_project = OtmProject .from_iac_file (project_id , project_name , IacType (iac_type .upper ()), iac_file ,
94+ mapping_file , output_file )
8395 otm_service = OtmProjectService (IriusriskProjectRepository (api_token , irius_server ))
8496
8597 __create_or_recreate_otm_project (recreate , otm_service , otm_project )
8698
8799
88100@cli .command ()
89- @click .option ('--type' , '-t' ,
90- type = click .Choice (['JSON' , 'YAML' , 'CloudFormation' , 'HCL2' , 'Terraform' , 'XML' ], case_sensitive = False ),
91- default = "JSON" ,
92- help = 'Specify the source file type.' )
93- @click .option ('--map' , '-m' , help = 'Map file to use when parsing source files' )
94- @click .option ('--otm' , '-o' , default = 'threatmodel.otm' , help = 'OTM output file name' )
95- @click .option ('--name' , '-n' , help = 'Project name' )
96- @click .option ('--id' , help = 'Project ID' )
97- @click .argument ('filename' , nargs = - 1 )
98- def parse (type , map , otm , name , id , filename ):
101+ @click .option (messages .IAC_TYPE_NAME , messages .IAC_TYPE_SHORTNAME , type = click .Choice (messages .IAC_TYPE_SUPPORTED , case_sensitive = False ), required = True , help = messages .IAC_TYPE_DESC )
102+ @click .option (messages .MAPPING_FILE_NAME , messages .MAPPING_FILE_SHORTNAME , help = messages .MAPPING_FILE_DESC )
103+ @click .option (messages .OUTPUT_FILE_NAME , messages .OUTPUT_FILE_SHORTNAME , default = messages .OUTPUT_FILE , help = messages .OUTPUT_FILE_DESC )
104+ @click .option (messages .PROJECT_NAME_NAME , messages .PROJECT_NAME_SHORTNAME , required = True , help = messages .PROJECT_NAME_DESC )
105+ @click .option (messages .PROJECT_ID_NAME , messages .PROJECT_ID_SHORTNAME , required = True , help = messages .PROJECT_ID_DESC )
106+ @click .argument (messages .IAC_FILE_NAME , required = True , nargs = - 1 )
107+ def parse (iac_type , mapping_file , output_file , project_name , project_id , iac_file ):
99108 """
100109 Parses IaC source files into Open Threat Model
101110 """
102111 logger .info ("Parsing IaC source files into OTM" )
103- OtmProject .from_iac_file (id , name , type , filename , map , otm )
112+ OtmProject .from_iac_file (project_id , project_name , IacType ( iac_type . upper ()), iac_file , mapping_file , output_file )
104113
105114
106115@cli .command ()
107- @click .option ('--recreate/--no-recreate' , default = False , help = 'Delete and recreate the project each time' )
108- @click .option ('--irius-server' , default = '' , envvar = 'IRIUS_SERVER' ,
109- help = 'IriusRisk server to connect to (proto://server[:port])' )
110- @click .option ('--api-token' , default = '' , envvar = 'IRIUS_API_TOKEN' , help = 'IriusRisk API token' )
111- @click .argument ('filename' , nargs = - 1 )
112- def threatmodel (recreate , irius_server , api_token , filename ):
116+ @click .option (messages .RECREATE_NAME , messages .RECREATE_SHORTNAME , default = False , help = messages .RECREATE_DESC )
117+ @click .option (messages .IRIUS_SERVER_NAME , messages .IRIUS_SERVER_SHORTNAME , default = '' , envvar = messages .IRIUS_SERVER_ENVAR , callback = validate_server , help = messages .IRIUS_SERVER_DESC )
118+ @click .option (messages .API_TOKEN_NAME , messages .API_TOKEN_SHORTNAME , default = '' , envvar = messages .API_TOKEN_ENVAR , help = messages .API_TOKEN_DESC )
119+ @click .argument (messages .IAC_FILE_NAME , required = True , nargs = - 1 )
120+ def threatmodel (recreate , irius_server , api_token , iac_file ):
113121 """
114122 Uploads an OTM file to IriusRisk
115123 """
116- otm_project = OtmProject .from_otm_file (filename )
124+ otm_project = OtmProject .from_otm_file (iac_file )
117125 otm_service = OtmProjectService (IriusriskProjectRepository (api_token , irius_server ))
118126
119127 __create_or_recreate_otm_project (recreate , otm_service , otm_project )
120128
121129
122130@cli .command ()
123- @click .option ('--map' , '-m' , multiple = True , help = 'Map file to validate' )
124- @click .option ('--otm' , '-o' , multiple = True , help = 'OTM file to validate' )
125- def validate (map , otm ):
131+ @click .option (messages . MAPPING_FILE_NAME , messages . MAPPING_FILE_SHORTNAME , help = messages . MAPPING_FILE_DESC )
132+ @click .option (messages . OUTPUT_FILE_NAME , messages . OUTPUT_FILE_SHORTNAME , default = messages . OUTPUT_FILE , help = messages . OUTPUT_FILE_DESC )
133+ def validate (mapping_file , output_file ):
126134 """
127135 Validates a mapping or OTM file
128136 """
129- if map :
137+ if mapping_file :
130138 logger .info ("Validating IaC mapping files" )
131- OtmProject .validate_iac_mappings_file (map )
139+ OtmProject .validate_iac_mappings_file (mapping_file )
132140
133- if otm :
141+ if output_file :
134142 logger .info ("Validating OTM file" )
135- OtmProject .load_and_validate_otm_file (otm )
143+ OtmProject .load_and_validate_otm_file (output_file )
136144
137145
138146@cli .command ()
139- @click .option ('--type' , '-t' ,
140- type = click .Choice (['JSON' , 'YAML' , 'CloudFormation' , 'HCL2' , 'Terraform' , 'XML' ], case_sensitive = False ),
141- default = "JSON" ,
142- help = 'Specify the source file type.' )
143- @click .option ('--query' , help = 'JMESPath query to run against source files' )
144- @click .argument ('filename' , nargs = - 1 )
145- def search (type , query , filename ):
147+ @click .option (messages .IAC_TYPE_NAME , messages .IAC_TYPE_SHORTNAME , type = click .Choice (messages .IAC_TYPE_SUPPORTED , case_sensitive = False ), required = True , help = messages .IAC_TYPE_DESC )
148+ @click .option ('--query' , '-q' , help = 'JMESPath query to run against the IaC file.' )
149+ @click .argument (messages .IAC_FILE_NAME , required = True , nargs = - 1 )
150+ def search (iac_type , query , iac_file ):
146151 """
147152 Searches source files for the given query
148153 """
149154
150- iac_to_otm = IacToOtm (None , None )
151- logger .info ("Running JMESPath search query against source files " )
152- iac_to_otm .search (type , query , filename )
155+ iac_to_otm = IacToOtm (None , None , IacType ( iac_type . upper ()) )
156+ logger .info ("Running JMESPath search query against the IaC file " )
157+ iac_to_otm .search (IacType ( iac_type . upper ()) , query , iac_file )
153158
154159
155- @click .option ('--port' , default = 5000 , envvar = 'application port' ,
156- help = 'The port to deploy this application to' )
157160@cli .command ()
161+ @click .option ('--port' , '-p' , default = 5000 , envvar = 'STARTLEFT_PORT' , help = 'Startleft deployment port.' )
158162def server (port : int ):
159163 """
160164 Launches the REST server to generate OTMs from requests
0 commit comments