boman-cli 2.4.7__tar.gz → 2.4.8__tar.gz

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.
@@ -1,17 +1,22 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: boman-cli
3
- Version: 2.4.7
3
+ Version: 2.4.8
4
4
  Summary: CLI tool of boman.ai
5
5
  Home-page: https://boman.ai
6
6
  Author: Sumeru Software Solutions Pvt. Ltd.
7
7
  Author-email: support@boman.ai
8
8
  License: BSD 2-clause
9
- Platform: UNKNOWN
10
9
  Classifier: Development Status :: 5 - Production/Stable
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: License :: OSI Approved :: BSD License
13
12
  Classifier: Operating System :: OS Independent
14
13
  Description-Content-Type: text/markdown
14
+ Requires-Dist: docker<=7.0.0
15
+ Requires-Dist: requests<=2.31.0
16
+ Requires-Dist: pyyaml
17
+ Requires-Dist: coloredlogs<=15.0.1
18
+ Requires-Dist: xmltodict<=0.13.0
19
+ Requires-Dist: pyfiglet<=1.0.2
15
20
 
16
21
  # Introduction
17
22
  Boman CLI is a Orchestration script written in python to run security scans on the local or CI/CD environment and upload the results to Boman.ai SaaS server.
@@ -95,11 +100,18 @@ Example: boman-cli -a run -zap_session_script ./session.js
95
100
 
96
101
  ### Release Note:
97
102
 
103
+ ### 2.4.8
104
+ - **New:** Semgrep pro (API) integration. Navigate to **Integrations -> Semgrep pro/api** in the Boman SaaS to setup Semgrep pro.
105
+
106
+ - **New:** Reachability analysis for SCA enabled.
107
+
108
+ - **Feature Request:** when Failbuild is configured. Boman should not take false positive, Accepted Risk, Not applicable and Muted vulnerabilities into account.
109
+
98
110
  ### V2.4.7
99
111
  - **BUG:** Failing build if token is not configured.
100
112
 
101
113
  ### V2.4.6
102
- - **New:** Semgrep API integration. Pass the semgrpe token as -semgrep_token {value}.
114
+ - **New:** Semgrep CLI integration. Pass the semgrep token as -semgrep_token {value}.
103
115
 
104
116
  ### V2.4.5
105
117
  - **New:** Snyk API integration. Navigate to **Integrations -> Snyk** in the Boman SaaS to setup Snyk.
@@ -165,5 +177,3 @@ Released on: 21 May 2024
165
177
 
166
178
 
167
179
 
168
-
169
-
@@ -80,11 +80,18 @@ Example: boman-cli -a run -zap_session_script ./session.js
80
80
 
81
81
  ### Release Note:
82
82
 
83
+ ### 2.4.8
84
+ - **New:** Semgrep pro (API) integration. Navigate to **Integrations -> Semgrep pro/api** in the Boman SaaS to setup Semgrep pro.
85
+
86
+ - **New:** Reachability analysis for SCA enabled.
87
+
88
+ - **Feature Request:** when Failbuild is configured. Boman should not take false positive, Accepted Risk, Not applicable and Muted vulnerabilities into account.
89
+
83
90
  ### V2.4.7
84
91
  - **BUG:** Failing build if token is not configured.
85
92
 
86
93
  ### V2.4.6
87
- - **New:** Semgrep API integration. Pass the semgrpe token as -semgrep_token {value}.
94
+ - **New:** Semgrep CLI integration. Pass the semgrep token as -semgrep_token {value}.
88
95
 
89
96
  ### V2.4.5
90
97
  - **New:** Snyk API integration. Navigate to **Integrations -> Snyk** in the Boman SaaS to setup Snyk.
@@ -1,17 +1,22 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: boman-cli
3
- Version: 2.4.7
3
+ Version: 2.4.8
4
4
  Summary: CLI tool of boman.ai
5
5
  Home-page: https://boman.ai
6
6
  Author: Sumeru Software Solutions Pvt. Ltd.
7
7
  Author-email: support@boman.ai
8
8
  License: BSD 2-clause
9
- Platform: UNKNOWN
10
9
  Classifier: Development Status :: 5 - Production/Stable
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: License :: OSI Approved :: BSD License
13
12
  Classifier: Operating System :: OS Independent
14
13
  Description-Content-Type: text/markdown
14
+ Requires-Dist: docker<=7.0.0
15
+ Requires-Dist: requests<=2.31.0
16
+ Requires-Dist: pyyaml
17
+ Requires-Dist: coloredlogs<=15.0.1
18
+ Requires-Dist: xmltodict<=0.13.0
19
+ Requires-Dist: pyfiglet<=1.0.2
15
20
 
16
21
  # Introduction
17
22
  Boman CLI is a Orchestration script written in python to run security scans on the local or CI/CD environment and upload the results to Boman.ai SaaS server.
@@ -95,11 +100,18 @@ Example: boman-cli -a run -zap_session_script ./session.js
95
100
 
96
101
  ### Release Note:
97
102
 
103
+ ### 2.4.8
104
+ - **New:** Semgrep pro (API) integration. Navigate to **Integrations -> Semgrep pro/api** in the Boman SaaS to setup Semgrep pro.
105
+
106
+ - **New:** Reachability analysis for SCA enabled.
107
+
108
+ - **Feature Request:** when Failbuild is configured. Boman should not take false positive, Accepted Risk, Not applicable and Muted vulnerabilities into account.
109
+
98
110
  ### V2.4.7
99
111
  - **BUG:** Failing build if token is not configured.
100
112
 
101
113
  ### V2.4.6
102
- - **New:** Semgrep API integration. Pass the semgrpe token as -semgrep_token {value}.
114
+ - **New:** Semgrep CLI integration. Pass the semgrep token as -semgrep_token {value}.
103
115
 
104
116
  ### V2.4.5
105
117
  - **New:** Snyk API integration. Navigate to **Integrations -> Snyk** in the Boman SaaS to setup Snyk.
@@ -165,5 +177,3 @@ Released on: 21 May 2024
165
177
 
166
178
 
167
179
 
168
-
169
-
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  boman-cli = bomancli.main:default
3
-
@@ -91,6 +91,7 @@ class Config:
91
91
  secret_scan_status = None
92
92
  secret_scan_upload_status = None
93
93
  secret_scan_errors = None
94
+ secret_scan_lang = None
94
95
 
95
96
 
96
97
  jenkins = None
@@ -120,7 +121,7 @@ class Config:
120
121
 
121
122
  log_level = "INFO"
122
123
 
123
- version = 'v2.4.7'
124
+ version = 'v2.4.8'
124
125
 
125
126
  boman_config_file = 'boman.yaml'
126
127
 
@@ -165,6 +166,7 @@ class Config:
165
166
  sbom_upload_status=None
166
167
  sbom_scan_status=None
167
168
  sbom_target=None
169
+ sbom_output_file=None
168
170
 
169
171
  #IAC scanning
170
172
  iac_scan_present=None
@@ -189,4 +191,27 @@ class Config:
189
191
  iac_scan_configuration = None
190
192
  semgrep_token = None
191
193
 
192
- dast_adv_auth_enabled = None
194
+ dast_adv_auth_enabled = None
195
+
196
+ supported_languages_reachability =[
197
+ "js",
198
+ "python",
199
+ "php",
200
+ "java",
201
+ "ruby",
202
+ "c",
203
+ "c++"
204
+ ]
205
+
206
+ reachability_present=None
207
+ reachability_build_dir=None
208
+ reachability_message=None
209
+ reachability_response=None
210
+ reachability_errors=None
211
+ reachability_upload_status=None
212
+ reachability_status=None
213
+ reachability_language=None
214
+
215
+ fail_build = False
216
+ polling_time = 60
217
+ polling_frequency = 10
@@ -164,7 +164,25 @@ def authorize():
164
164
  Config.scan_name = json_response['data']['scan_name']
165
165
  Config.con_scan_response = json_response['data']['cs']
166
166
  Config.sbom_response = json_response['data']['sbom']
167
- Config.iac_scan_response = json_response['data']['iac']
167
+ Config.iac_scan_response = json_response['data']['iac']
168
+
169
+
170
+ if 'reachability' in json_response['data'].keys():
171
+ Config.reachability_response = json_response['data']['reachability']
172
+ if 'code_language' in json_response.keys():
173
+ Config.reachability_language = json_response['code_language']
174
+ if Utils.reachability_support_check(Config.reachability_language):
175
+ Config.reachability_language = Utils.reachability_support_check(Config.reachability_language)
176
+ Config.reachability_present = True
177
+ Config.sbom_present = True
178
+ else:
179
+ Config.reachability_present = False
180
+ else:
181
+ Config.reachability_present = False
182
+ logging.warning("Reachability: Not Configured!!!! Message: Code language was not found on JSON Response")
183
+ else:
184
+ Config.reachability_present = False
185
+ logging.warning("Reachability: Not Configured!!!! Message: Tool configuration data was not found on JSON Response")
168
186
 
169
187
  return 1
170
188
  except:
@@ -36,7 +36,7 @@ parser = argparse.ArgumentParser(
36
36
  description='''
37
37
  #This is a CLI tool to communicate with Boman.ai SaaS server
38
38
  ''',
39
- epilog='copyright (c) 2025 SUMERU'
39
+ epilog='copyright (c) 2025 BOMAN.AI'
40
40
  )
41
41
 
42
42
 
@@ -81,7 +81,10 @@ def runImage(data=None,type=None):
81
81
  output_file= data['output_file']
82
82
  will_generate_output = data['will_generate_output']
83
83
  tool_id= data['tool_id']
84
- scan_details_id= data['scan_details_id']
84
+ if tool_name != "atom":
85
+ scan_details_id= data['scan_details_id']
86
+ else:
87
+ scan_details_id=0
85
88
  conversion_required = data['conversion_required']
86
89
 
87
90
 
@@ -171,7 +174,7 @@ def runImage(data=None,type=None):
171
174
  else:
172
175
  try:
173
176
 
174
- #print('token pressentcle')
177
+ # logging.info('Semgrep token present')
175
178
 
176
179
  if Config.jenkins == 'yes':
177
180
 
@@ -179,7 +182,7 @@ def runImage(data=None,type=None):
179
182
 
180
183
  Config.build_dir = Config.sast_build_dir
181
184
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.sast_build_dir: {
182
- 'bind': data['bind']}}, user=userid,detach=detach,environment=env_var)
185
+ 'bind': data['bind']}}, user=userid,detach=detach,environment=env_var, remove=True)
183
186
  logging.info('SAST Docker: SUCCESS !!!. Message: %s Scan Completed',tool_name)
184
187
 
185
188
  else:
@@ -327,7 +330,7 @@ def runImage(data=None,type=None):
327
330
  try:
328
331
  Config.build_dir = Config.sast_build_dir
329
332
  container= docker.containers.run(docker_image, command_line, volumes={Config.sast_build_dir: {
330
- 'bind': data['bind'], 'mode': 'rw'}},user=userid,detach=detach)
333
+ 'bind': data['bind'], 'mode': 'rw'}},user=userid,detach=detach, remove=True)
331
334
 
332
335
  #print(output_file,toolname,tool_id,scan_details_id)
333
336
  logging.info('[SUCCESS]: %s Scan Completed',tool_name)
@@ -355,7 +358,7 @@ def runImage(data=None,type=None):
355
358
  Config.build_dir = Config.sast_build_dir
356
359
  logging.info('DAST Docker: Command for running the scan %s',command_line)
357
360
  container= docker.containers.run(docker_image, command_line, volumes={Config.sast_build_dir: {
358
- 'bind': data['bind'], 'mode': 'rw'}},user=userid,detach=detach)
361
+ 'bind': data['bind'], 'mode': 'rw'}},user=userid,detach=detach, remove=True)
359
362
 
360
363
  #print(output_file,toolname,tool_id,scan_details_id)
361
364
  logging.info('DAST Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
@@ -404,14 +407,14 @@ def runImage(data=None,type=None):
404
407
  Config.build_dir = Config.sca_build_dir
405
408
  command_line = "% s" % command_line.format(target = './'+target)
406
409
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.sca_build_dir: {
407
- 'bind': data['bind']}}, user=uid)
410
+ 'bind': data['bind']}}, user=uid, remove=True)
408
411
  logging.info('SCA Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
409
412
  else:
410
413
  target = Utils.remove_leading_slash(Config.sca_target)
411
414
  Config.build_dir = Config.sca_build_dir
412
415
  command_line = "% s" % command_line.format(target ='/src/'+target)
413
416
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.sca_build_dir: {
414
- 'bind': data['bind']}}, user=uid)
417
+ 'bind': data['bind']}}, user=uid, remove=True)
415
418
  logging.info('SCA Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
416
419
  else:
417
420
  if Config.sca_ignore:
@@ -422,7 +425,7 @@ def runImage(data=None,type=None):
422
425
  logging.info('SCA Docker: Command ==> %s',command_line)
423
426
  Config.build_dir = Config.sca_build_dir
424
427
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.sca_build_dir: {
425
- 'bind': data['bind']}}, user=uid)
428
+ 'bind': data['bind']}}, user=uid, remove=True)
426
429
  logging.info('SCA Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
427
430
  Config.sca_message ='SCA scan completed'
428
431
  Config.sca_scan_status ='Completed'
@@ -474,7 +477,7 @@ def runImage(data=None,type=None):
474
477
  logging.info(f"CS Docker: command ==> {command_line}")
475
478
  Config.build_dir = Config.con_scan_build_dir
476
479
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.con_scan_build_dir: {
477
- 'bind': data['bind']}}, user=uid)
480
+ 'bind': data['bind']}}, user=uid, remove=True)
478
481
  logging.info('CS Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
479
482
  Config.con_scan_message ='Container Scan completed'
480
483
  Config.con_scan_status ='Completed'
@@ -507,14 +510,15 @@ def runImage(data=None,type=None):
507
510
  Utils.logError(msg,str(e))
508
511
 
509
512
  if type == 'sbom':
513
+ Config.sbom_output_file = output_file
510
514
  Utils.checkImageAlreadyExsist(docker_image)
511
515
  logging.info('Running %s',tool_name)
512
516
 
513
517
  try:
514
- Config.build_dir = Config.sbom_build_dir
518
+ Config.build_dir = Config.sbom_build_dir = os.getcwd()+'/'
515
519
  command_line = "% s" % command_line.format(target = 'src/')
516
520
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.sbom_build_dir: {
517
- 'bind': data['bind']}}, user=uid)
521
+ 'bind': data['bind']}}, user=uid, remove=True)
518
522
  logging.info('SBOM Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
519
523
  Config.sbom_message ='SBOM scan completed'
520
524
  Config.sbom_scan_status ='Completed'
@@ -555,7 +559,7 @@ def runImage(data=None,type=None):
555
559
  command_line = "% s" % command_line.format(target = os.path.join('/src',Utils.remove_leading_slash(Config.iac_scan_target)))
556
560
  Config.build_dir = Config.iac_scan_build_dir
557
561
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.iac_scan_build_dir: {
558
- 'bind': data['bind']}}, user=uid)
562
+ 'bind': data['bind']}}, user=uid, remove=True)
559
563
  logging.info('IaC Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
560
564
  Config.iac_scan_message ='IaC Scan completed'
561
565
  Config.iac_scan_status ='Completed'
@@ -591,13 +595,62 @@ def runImage(data=None,type=None):
591
595
  logging.error('IaC Docker: Failed!!! Message: Error recorded while uploading the report %s',str(e))
592
596
  Config.iac_scan_message ='Error recorded while uploading the report of IaC Scan, Please check your directory for the files.' ## need to change logic here -- MM
593
597
  msg = 'Error recorded while uploading the report'
594
- Utils.logError(msg,str(e))
598
+ Utils.logError(msg,str(e))
599
+
600
+ if type == 'reachability' and Config.sbom_scan_status.lower() != "failed":
601
+ Utils.checkImageAlreadyExsist(docker_image)
602
+ logging.info('Running %s',tool_name)
603
+
604
+ try:
605
+ command_line = "% s" % command_line.format(lang = Config.reachability_language)
606
+ Config.build_dir = Config.reachability_build_dir = Config.sca_build_dir
607
+ container_output = docker.containers.run(docker_image, command_line, volumes={'/tmp': {'bind': '/tmp'},
608
+ f'{os.environ["HOME"]}': {'bind': os.environ["HOME"]},Config.reachability_build_dir: {
609
+ 'bind': data['bind'], 'mode': 'rw'}}, user=uid, remove=True)
610
+ logging.info('Reachability Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
611
+ reachables = Utils.reachability_analysis(f'{Config.sbom_build_dir}/{Config.sbom_output_file}',f'{Config.sca_build_dir}/{output_file}')
612
+ Config.reachability_message ='Reachability Completed'
613
+ Config.reachability_status ='Completed'
614
+ except errors.ContainerError as exc:
615
+ logging.error('Reachability Docker: Failed!!! Message: Some Error recorded while scanning %s',str(exc))
616
+ msg='\n The following error has been recorded while scanning IaC'
617
+ Config.reachability_status ='Failed'
618
+ Config.reachability_errors =f'{str(exc)}]'
619
+ Utils.logError(msg,str(exc))
620
+
621
+ try:
622
+ if will_generate_output == 1:
623
+ logging.info('Reachability: Uploading %s to the server',output_file)
624
+ output_reachability = {
625
+ 'output_file': output_file,
626
+ 'output': reachables
627
+ }
628
+ if uploadReport(output_reachability,tool_name,tool_id,scan_details_id,'reachability'):
629
+ Config.reachability_status ='Completed'
630
+ Config.reachability_upload_status = 'Completed'
631
+ Config.reachability_message ='Scan Completed'
632
+ else:
633
+ Config.reachability_status ='Failed'
634
+ Config.reachability_upload_status = 'Failed'
635
+ Config.reachability_message ='Error occured while uploading the report, Please check the cli logs'
636
+ else:
637
+ logging.error('reachability: Failed!!! Message: Cant upload files to the server',tool_name)
638
+ Config.reachability_message ='Cant upload files to the server for reachability, Please check your directory If output file is present.'
639
+
640
+ except EnvironmentError as e:
641
+ logging.error('reachability: Failed!!! Message: Error recorded while uploading the report %s',str(e))
642
+ Config.reachability_message ='Error recorded while uploading the report of reachability, Please check your directory If output file is present.' ## need to change logic here -- MM
643
+ msg = 'Error recorded while uploading the report'
644
+ Utils.logError(msg,str(e))
645
+ elif type == "reachability":
646
+ Config.reachability_status ='Failed'
647
+ Config.reachability_message ='Reachability: SBOM scan failed. Reachability analysis requires sbom file'
595
648
 
596
649
 
597
650
  #### function to upload the test report to the server with other data -- MM ------------------------------------
598
651
  def uploadReport(filename,toolname,tool_id,scan_details_id,type):
599
652
 
600
- logging.info('Uploading %s report with filename: %s', toolname,filename)
653
+ # logging.info('Uploading %s report with filename: %s', toolname,filename)
601
654
  if True:
602
655
  #build_dir = '/home/boxuser/box/trainingdata/repos/youtube-dl/'
603
656
  #print(Config.sast_build_dir+filename)
@@ -626,6 +679,9 @@ def uploadReport(filename,toolname,tool_id,scan_details_id,type):
626
679
  elif type =="iac":
627
680
  message = Config.iac_scan_message
628
681
  errors = Config.iac_scan_errors
682
+ elif type =="reachability":
683
+ message = Config.reachability_message
684
+ errors = Config.reachability_errors
629
685
  except:
630
686
  message = 'NA'
631
687
  errors = 'NA'
@@ -634,20 +690,29 @@ def uploadReport(filename,toolname,tool_id,scan_details_id,type):
634
690
 
635
691
 
636
692
  try:
637
- logging.info('fetching the %s file from the directory %s',filename,Config.build_dir)
638
- ##path = '/home/boxuser/box/Vuln-code/boman_njsscan.json'
639
-
640
-
641
- # print(str(Config.build_dir))
642
- # print(str(filename))
693
+
694
+ if type == 'reachability':
695
+ path_reachability = Path(str(Config.sca_build_dir)) / str("app.atom")
696
+ data = filename['output']
697
+ path_bom = Path(str(Config.sbom_build_dir)) / str("bom.json")
698
+ path = Path(str(Config.sca_build_dir)) / str(filename['output_file'])
699
+ logging.info('Uploading %s report with filename: %s', toolname,filename['output_file'])
700
+
701
+ else:
702
+ logging.info('Uploading %s report with filename: %s', toolname,filename)
703
+ logging.info('fetching the %s file from the directory %s',filename,Config.build_dir)
704
+ ##path = '/home/boxuser/box/Vuln-code/boman_njsscan.json'
643
705
 
644
- data_folder = Path(str(Config.build_dir))
645
706
 
646
- path = data_folder / str(filename)
647
-
648
- with open(path) as f:
649
- f.seek(0)
650
- data = json.load(f)
707
+ # print(str(Config.build_dir))
708
+ # print(str(filename))
709
+ data_folder = Path(str(Config.build_dir))
710
+ path = data_folder / str(filename)
711
+
712
+
713
+ with open(path) as f:
714
+ f.seek(0)
715
+ data = json.load(f)
651
716
 
652
717
 
653
718
  except EnvironmentError as e:
@@ -657,10 +722,24 @@ def uploadReport(filename,toolname,tool_id,scan_details_id,type):
657
722
  Utils.logError(msg,str(e))
658
723
  values = {'tool_name': toolname, 'time': time.time(),'scan_token':Config.scan_token, 'app_token':Config.app_token,'customer_token':Config.customer_token,'tool_id':tool_id,'scan_details_id':scan_details_id,"tool_results":None,"message":message,"errors":errors,"app_loc":Config.app_loc}
659
724
  url = Config.boman_url+"/api/app/upload"
660
- # with open(path) as f:
661
- # file_obj = f
662
725
  r = requests.post(url,json=values)
663
- return 0
726
+ return 0
727
+
728
+ try:
729
+ logging.info('Removing the result file')
730
+ if type != "sbom" and type != "reachability":
731
+ os.remove(path)
732
+ elif Config.reachability_present is not True and type == 'sbom':
733
+ os.remove(path)
734
+ elif type == "reachability":
735
+ os.remove(path)
736
+ os.remove(Path(str(Config.sbom_build_dir))/Config.sbom_response[0]['output_file'])
737
+ os.remove(path_reachability)
738
+ os.remove(path_bom)
739
+
740
+
741
+ except Exception as e:
742
+ logging.error(f"unable to remove the file: {path} ---> Error: {e}")
664
743
 
665
744
  tool_output = json.dumps(data, ensure_ascii=False, indent=4)
666
745
 
@@ -674,7 +753,10 @@ def uploadReport(filename,toolname,tool_id,scan_details_id,type):
674
753
  r = requests.post(url,json=values)
675
754
  #print(r.status_code)
676
755
  if r.status_code == 200:
677
- logging.info('[COMPLETED]: %s Report uploaded Successfully! Report Name: %s',toolname,filename)
756
+ if type == "reachability":
757
+ logging.info('[COMPLETED]: %s Report uploaded Successfully! Report Name: %s',toolname,filename["output_file"])
758
+ else:
759
+ logging.info('[COMPLETED]: %s Report uploaded Successfully! Report Name: %s',toolname,filename)
678
760
  logging.info('Removing the result file')
679
761
  #os.remove(path)
680
762
  return 1
@@ -769,37 +851,51 @@ def exitFunction():
769
851
  customer_token = Config.customer_token
770
852
 
771
853
  logging.info('Summary: Fetching Vulnerabilities reported by the tools')
772
- url = Config.boman_url+"/api/vuln/get"
773
- values = {'app_token':app_token, 'scan_token':scan_token, 'customer_token':customer_token}
774
- try:
775
- vuln = {"LOW":0,"MEDIUM":0,"HIGH":0,"CRITICAL":0}
776
- x = requests.post(url,json=values)
777
-
778
-
779
- response = x.json()
780
-
781
- if response['status'] == True:
782
- logging.info('Summary: Analyzing the vulnerabilities found')
783
- for data in response['data'] :
784
- vuln[data['_id']] = data['count']
785
-
786
- Config.low_count = vuln['LOW']
787
- Config.medium_count = vuln['MEDIUM']
788
- Config.high_count = vuln['HIGH']
789
- Config.critical_count = vuln['CRITICAL']
854
+
855
+ end_time = time.time() + Config.polling_time
856
+ while time.time() < end_time:
857
+
858
+ url = Config.boman_url+"/api/vuln/get"
859
+ values = {'app_token':app_token, 'scan_token':scan_token, 'customer_token':customer_token}
860
+ try:
790
861
 
791
- logging.info('Summary: Analyzing Vulnerabitlites Done')
792
-
793
- else:
794
- logging.info('Summary: Error Analyzing Vulnerabitlites.')
795
-
862
+ x = requests.post(url,json=values)
863
+ response = x.json()
864
+
865
+ if not Config.fail_build:
866
+ logging.info("Fail Build was not configured")
867
+ Utils.get_summary_of_vulns(response)
868
+ break
869
+
870
+ if "ml_status" in response.keys():
871
+ if response["ml_status"] == True:
872
+ logging.info("Poll Status: ML scan is Done. Collecting the summary")
873
+ Utils.get_summary_of_vulns(response)
874
+ break
875
+ else:
876
+ logging.info("Poll status: Waiting for ML to complete......")
877
+ time.sleep(Config.polling_frequency)
796
878
 
879
+ except requests.ConnectionError as e:
880
+
881
+ logging.error(f"Connection Error: Can't connect to the Server, Please check your Internet connection. Error: {e}")
882
+ exit(1) #server/saas error
883
+
884
+ else:
885
+ logging.warning("Poll Status: Polling time reached the limit. But ml scan has not commpleted. Try using --pollTime to increase the polling time")
886
+ url = Config.boman_url+"/api/vuln/get"
887
+ values = {'app_token':app_token, 'scan_token':scan_token, 'customer_token':customer_token}
888
+ try:
889
+
890
+ x = requests.post(url,json=values)
891
+ response = x.json()
892
+
893
+ Utils.get_summary_of_vulns(response)
797
894
 
798
- except requests.ConnectionError as e:
799
-
800
- logging.error(f"Connection Error: Can't connect to the Server, Please check your Internet connection. Error: {e}")
801
- exit(1) #server/saas error
802
-
895
+ except requests.ConnectionError as e:
896
+
897
+ logging.error(f"Connection Error: Can't connect to the Server, Please check your Internet connection. Error: {e}")
898
+ exit(1) #server/saas error
803
899
 
804
900
 
805
901
 
@@ -861,7 +957,7 @@ def main():
861
957
 
862
958
  if Config.sast_present is True:
863
959
 
864
- if Config.sast_lang != 'sonar':
960
+ if (Config.sast_lang != 'sonar') and (Config.sast_lang != 'semgrep-pro'):
865
961
 
866
962
  logging.info('SAST: Preparing SAST Scan')
867
963
  logging.info('SAST: Working directory is %s',Config.sast_build_dir)
@@ -881,9 +977,9 @@ def main():
881
977
  runImage(data=data,type='SAST')
882
978
  logging.info("SAST: Successfull !!!")
883
979
  else:
884
- logging.warning('SAST: Configured with Sonar it will run on SaaS')
980
+ logging.warning(f'SAST: Configured with {Config.sast_lang} it will run on SaaS')
885
981
  Config.sast_scan_status = 'SUCCESS'
886
- Config.sast_message = f'SAST Scan: Success. Sonar is configured and will run on SaaS'
982
+ Config.sast_message = f'SAST Scan: Success. {Config.sast_lang} is configured and will run on SaaS'
887
983
 
888
984
 
889
985
  else:
@@ -919,7 +1015,7 @@ def main():
919
1015
 
920
1016
 
921
1017
  if Config.sca_present is True:
922
- if Config.sca_response[0]['tool'] != "SNYK":
1018
+ if Config.sca_response[0]['tool'] != "SNYK" and Config.sca_response[0]['tool'] != "Semgrep Pro":
923
1019
  logging.info('SCA: Preparing scan')
924
1020
 
925
1021
 
@@ -932,33 +1028,36 @@ def main():
932
1028
  runImage(data=data,type='SCA')
933
1029
  logging.info("SCA: Successfull !!!")
934
1030
  else:
935
- logging.warning('SCA: Configured with SNYK it will run on SaaS')
1031
+ logging.warning(f"SCA: Configured with {Config.sca_response[0]['tool']} it will run on SaaS")
936
1032
  Config.sca_scan_status = 'SUCCESS'
937
- Config.sca_message = f'SCA Scan: Success. SNYK is configured and will run on SaaS'
1033
+ Config.sca_message = f"SCA Scan: Success. {Config.sca_response[0]['tool']} is configured and will run on SaaS"
938
1034
  else:
939
1035
  logging.info('SCA: Ignoring scan. Message: Not Configured')
940
1036
 
941
1037
  if Config.secret_scan_present == True:
942
-
943
- if Utils.isGitDirectory(Config.sast_build_dir):
944
- logging.info('Secret Scan: Preparing Scan')
945
- for data in Config.secret_scan_response:
946
- initSecertScan(Config.sast_build_dir,data=Config.secret_scan_response)
947
- logging.info("Secret Scan: Successfull !!!")
948
-
1038
+ if Config.secret_scan_response[0]['tool'] != "Semgrep Pro":
1039
+ if Utils.isGitDirectory(Config.sast_build_dir):
1040
+ logging.info('Secret Scan: Preparing Scan')
1041
+ for data in Config.secret_scan_response:
1042
+ initSecertScan(Config.sast_build_dir,data=Config.secret_scan_response)
1043
+ logging.info("Secret Scan: Successfull !!!")
1044
+ else:
1045
+ logging.error('Secret Scan: Failed !!!. Message: The Directory is not Git directory %s',Config.sast_build_dir)
1046
+ data=Config.secret_scan_response
1047
+ tool_name = data[0]['tool']
1048
+ file_name = data[0]['output_file']
1049
+ tool_id = data[0]['tool_id']
1050
+ scan_details_id = data[0]['scan_details_id']
1051
+ uploadReport(file_name,tool_name,tool_id,scan_details_id,'SS')
1052
+ Config.secret_scan_status = 'Failed'
1053
+ Config.secret_scan_upload_status ='NA'
1054
+ Config.secret_scan_errors = "Secret Scan: Failed !!!. Message: The Directory is not Git directory %s"
1055
+ Config.secret_scan_message = f'Secret Scan: Failed. The Directory is not Git directory {Config.sast_build_dir}'
1056
+ logging.warning('Secret Scan: Ignoring scan')
949
1057
  else:
950
- logging.error('Secret Scan: Failed !!!. Message: The Directory is not Git directory %s',Config.sast_build_dir)
951
- data=Config.secret_scan_response
952
- tool_name = data[0]['tool']
953
- file_name = data[0]['output_file']
954
- tool_id = data[0]['tool_id']
955
- scan_details_id = data[0]['scan_details_id']
956
- uploadReport(file_name,tool_name,tool_id,scan_details_id,'SS')
957
- Config.secret_scan_status = 'Failed'
958
- Config.secret_scan_upload_status ='NA'
959
- Config.secret_scan_errors = "Secret Scan: Failed !!!. Message: The Directory is not Git directory %s"
960
- Config.secret_scan_message = f'Secret Scan: Failed. The Directory is not Git directory {Config.sast_build_dir}'
961
- logging.warning('Secret Scan: Ignoring scan')
1058
+ logging.warning(f"Secret Scan: Configured with {Config.secret_scan_response[0]['tool']} it will run on SaaS")
1059
+ Config.secret_scan_status ='SUCCESS'
1060
+ Config.secret_scan_message =f"Secret Scan: Success. {Config.secret_scan_response[0]['tool']} is configured and will run on SaaS"
962
1061
  else:
963
1062
  logging.warning('Secret Scan: Ignoring scan. Message: Not Configured')
964
1063
 
@@ -1012,7 +1111,16 @@ def main():
1012
1111
  Config.iac_scan_message = f'IaC Scan: Success. SNYK is configured and will run on SaaS'
1013
1112
  else:
1014
1113
  logging.warning('IaC: Ignoring scan. Message: Not Configured')
1015
-
1114
+
1115
+ if Config.reachability_present is True:
1116
+ logging.info("Reachability: Preparing requirements")
1117
+
1118
+ for data in Config.reachability_response:
1119
+ Utils.copy_contents(f'{Config.sbom_build_dir}/{Config.sbom_output_file}',f'{Config.sca_build_dir}/bom.json')
1120
+ runImage(data=data,type='reachability')
1121
+ else:
1122
+ logging.warning('Reachability: Ignoring. Message: Not Configured')
1123
+
1016
1124
  exitFunction()
1017
1125
  return 1
1018
1126
 
@@ -1033,6 +1141,8 @@ def default():
1033
1141
  parser.add_argument('-zap_session_script','--zap_custom_session_script',default='session_management.js',help="Pass the file name if you have any custom zap session script file name. eg:custom-script.js")
1034
1142
  # parser.add_argument('-uid','--user_id',default='1000:1000',help="[internal] Pass the custom userid:groupid incase the lingu detec function is failed")
1035
1143
  parser.add_argument('-semgrep_token','--semgrepToken',default='No',help="Pass the semgrep community token here")
1144
+ parser.add_argument('-pot','--pollTime',default=60,help="Polling Time. Min : 30s, Max: 5min, Default : 1min. Format: input the time in seconds eg: 1min => 60 ")
1145
+ parser.add_argument('-pof','--pollFrequency',default=10,help="Polling Frequency. Min : 5s, Max: 1min, Default : 10s. Format: input the time in seconds eg: 1min => 60 ")
1036
1146
  args = parser.parse_args()
1037
1147
 
1038
1148
  # if len(sys.args) == 1:
@@ -1128,6 +1238,24 @@ def default():
1128
1238
  else:
1129
1239
  logging.error(f"Authorization Config: Failed !!!, Exit Code: 4 (Misconfiguration/Validation). The {Config.boman_config_file} file is not found and tokens were not found in cli arguments as well")
1130
1240
  exit(4) #validation error
1241
+
1242
+
1243
+ if (args.pollTime >= 30) and (args.pollTime <= 300):
1244
+ Config.polling_time = args.pollTime
1245
+ logging.info(f"Configuring polling time to {Config.polling_time}s")
1246
+ else:
1247
+ logging.info(f"Resorting to default polling time {Config.polling_time}s")
1248
+
1249
+ if (args.pollFrequency >= 5) and (args.pollFrequency <= 60):
1250
+ Config.polling_frequency = args.pollFrequency
1251
+ logging.info(f"Configuring polling frequency to {Config.polling_frequency}s")
1252
+ else:
1253
+ logging.info(f"Resorting to default polling frequency {Config.polling_frequency}s")
1254
+
1255
+
1256
+ if args.failBuild != "pass":
1257
+ Config.fail_build = True
1258
+
1131
1259
  ## auth method args validation
1132
1260
 
1133
1261
  # if args.custom_dast_auth_config == True:
@@ -1140,13 +1268,13 @@ def default():
1140
1268
  # exit(4)
1141
1269
 
1142
1270
 
1143
- if Config.zap_custom_auth_method == 'form':
1144
- if Config.custom_zap_plan_present :
1145
- Config.dast_auth_present == True
1146
- Config.custom_zap_plan_present = True
1147
- else:
1148
- logging.error('Zap auth method "form" needs plan file')
1149
- exit(4)
1271
+ # if Config.zap_custom_auth_method == 'form':
1272
+ # if Config.custom_zap_plan_present :
1273
+ # Config.dast_auth_present == True
1274
+ # Config.custom_zap_plan_present = True
1275
+ # else:
1276
+ # logging.error('Zap auth method "form" needs plan file')
1277
+ # exit(4)
1150
1278
 
1151
1279
 
1152
1280
 
@@ -1172,30 +1300,35 @@ def default():
1172
1300
  if args.failBuild == 'fail':
1173
1301
  total_vuln = Config.high_count + Config.medium_count + Config.low_count + Config.critical_count
1174
1302
  if total_vuln > 0:
1303
+ logging.warning(f"Failing the build as {args.failBuild} is configured in failBuild argument. Boman has found {total_vuln} vulnerabilities")
1175
1304
  exit(-1)
1176
1305
  else:
1177
1306
  exit(0)
1178
1307
 
1179
1308
  elif args.failBuild == 'high':
1180
1309
  if Config.high_count > 0 or Config.critical_count > 0:
1310
+ logging.warning(f"Failing the build as {args.failBuild} is configured in failBuild argument. Boman has found {Config.high_count} {args.failBuild} vulnerabilities")
1181
1311
  exit(-1)
1182
1312
  else:
1183
1313
  exit(0)
1184
1314
 
1185
1315
  elif args.failBuild == 'medium':
1186
1316
  if Config.medium_count > 0 or Config.high_count > 0:
1317
+ logging.warning(f"Failing the build as {args.failBuild} is configured in failBuild argument. Boman has found {Config.medium_count} {args.failBuild} vulnerabilities")
1187
1318
  exit(-1)
1188
1319
  else:
1189
1320
  exit(0)
1190
1321
 
1191
1322
  elif args.failBuild == 'low':
1192
1323
  if Config.low_count > 0 or Config.medium_count > 0 or Config.high_count > 0:
1324
+ logging.warning(f"Failing the build as {args.failBuild} is configured in failBuild argument. Boman has found {Config.low_count} {args.failBuild} vulnerabilities")
1193
1325
  exit(-1)
1194
1326
  else:
1195
1327
  exit(0)
1196
1328
 
1197
1329
  elif args.failBuild == 'critical':
1198
1330
  if Config.critical_count > 0:
1331
+ logging.warning(f"Failing the build as {args.failBuild} is configured in failBuild argument. Boman has found {Config.critical_count} {args.failBuild} vulnerabilities")
1199
1332
  exit(-1)
1200
1333
  else:
1201
1334
  exit(0)
@@ -1,139 +1,139 @@
1
- ---
2
- env:
3
- contexts:
4
- - name: "Boman Authenticated Scan"
5
- urls:
6
- - "https://demo.testfire.net/"
7
- authentication:
8
- method: "form"
9
- parameters: # May include any required for scripts. All of the parameters support vars except for the port
10
- loginPageUrl: "https://demo.testfire.net/login.jsp"
11
- loginRequestUrl: "https://demo.testfire.net/doLogin"
12
- loginRequestBody: "uid={%username%}&passw={%password%}&btnSubmit=Login"
13
- verification:
14
- method: "both" # String, one of 'response', 'request', 'both', 'poll'
15
- loggedOutRegex: ".*Sign In.*" # String, regex pattern for determining if logged
16
- sessionManagement:
17
- method: ""
18
- parameters:
19
- script: ""
20
- scriptEngine: ""
21
- users: # List of one or more users available to use for authentication
22
- - name: "demoadmin" # String, the name to be used by the jobs
23
- credentials: # List of user credentials - may include any required for scripts
24
- username: "admin" # String, the username to use when authenticating, vars supported
25
- password: "admin"
26
- includePaths: []
27
- excludePaths: []
28
- technology:
29
- exclude:
30
- - "C"
31
- - "ASP"
32
- - "IBM DB2"
33
- - "PHP"
34
- - "CouchDB"
35
- - "XML"
36
- - "Microsoft SQL Server"
37
- - "JSP/Servlet"
38
- - "Firebird"
39
- - "MongoDB"
40
- - "HypersonicSQL"
41
- - "SAP MaxDB"
42
- - "Ruby"
43
- - "SCM"
44
- - "WS"
45
- - "Microsoft Access"
46
- - "Sybase"
47
- - "Python"
48
- parameters:
49
- failOnError: true
50
- failOnWarning: false
51
- progressToStdout: true
52
- vars: {}
53
- jobs:
54
- - parameters:
55
- scanOnlyInScope: true
56
- enableTags: false
57
- disableAllRules: false
58
- rules: []
59
- name: "passiveScan-config"
60
- type: "passiveScan-config"
61
- - parameters:
62
- context: "Demo testfire"
63
- user: "demoadmin"
64
- url: "https://demo.testfire.net"
65
- maxDuration: 1
66
- maxDepth: 2
67
- maxChildren: 0
68
- name: "spider"
69
- type: "spider"
70
- tests:
71
- - onFail: "INFO"
72
- statistic: "automation.spider.urls.added"
73
- site: ""
74
- operator: ">="
75
- value: 100
76
- name: "At least 100 URLs found"
77
- type: "stats"
78
- - name: "spider logged in" # Name of the test, optional
79
- type: stats # Specifies that the test is of type 'stats'
80
- statistic: "stats.auth.success" # Name of an integer / long statistic
81
- operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
82
- value: 2 # Value to compare statistic against
83
- onFail: "info"
84
- - name: "spider failed" # Name of the test, optional
85
- type: stats # Specifies that the test is of type 'stats'
86
- statistic: "stats.auth.failure" # Name of an integer / long statisti
87
- operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
88
- value: 2 # Value to compare statistic against
89
- onFail: "info"
90
- - parameters: {}
91
- name: "passiveScan-wait"
92
- type: "passiveScan-wait"
93
- - parameters:
94
- context: "Demo testfire"
95
- user: "demoadmin"
96
- policy: ""
97
- maxRuleDurationInMins: 0
98
- maxScanDurationInMins: 5
99
- maxAlertsPerRule: 0
100
- policyDefinition:
101
- defaultStrength: "medium"
102
- defaultThreshold: "medium"
103
- rules: []
104
- tests:
105
- - name: "spider logged in" # Name of the test, optional
106
- type: stats # Specifies that the test is of type 'stats'
107
- statistic: "stats.auth.success" # Name of an integer / long statistic
108
- operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
109
- value: 2 # Value to compare statistic against
110
- onFail: "info"
111
- - name: "spider failed" # Name of the test, optional
112
- type: stats # Specifies that the test is of type 'stats'
113
- statistic: "stats.auth.failure" # Name of an integer / long statisti
114
- operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
115
- value: 2 # Value to compare statistic against
116
- onFail: "info"
117
- name: "activeScan"
118
- type: "activeScan"
119
- - parameters:
120
- template: "traditional-json"
121
- reportDir: ""
122
- reportFile: ""
123
- reportTitle: "Boman_Scanning_Report"
124
- reportDescription: ""
125
- displayReport: false
126
- risks:
127
- - "info"
128
- - "low"
129
- - "medium"
130
- - "high"
131
- confidences:
132
- - "falsepositive"
133
- - "low"
134
- - "medium"
135
- - "high"
136
- - "confirmed"
137
- sites: []
138
- name: "report"
139
- type: "report"
1
+ ---
2
+ env:
3
+ contexts:
4
+ - name: "Boman Authenticated Scan"
5
+ urls:
6
+ - "https://demo.testfire.net/"
7
+ authentication:
8
+ method: "form"
9
+ parameters: # May include any required for scripts. All of the parameters support vars except for the port
10
+ loginPageUrl: "https://demo.testfire.net/login.jsp"
11
+ loginRequestUrl: "https://demo.testfire.net/doLogin"
12
+ loginRequestBody: "uid={%username%}&passw={%password%}&btnSubmit=Login"
13
+ verification:
14
+ method: "both" # String, one of 'response', 'request', 'both', 'poll'
15
+ loggedOutRegex: ".*Sign In.*" # String, regex pattern for determining if logged
16
+ sessionManagement:
17
+ method: ""
18
+ parameters:
19
+ script: ""
20
+ scriptEngine: ""
21
+ users: # List of one or more users available to use for authentication
22
+ - name: "demoadmin" # String, the name to be used by the jobs
23
+ credentials: # List of user credentials - may include any required for scripts
24
+ username: "admin" # String, the username to use when authenticating, vars supported
25
+ password: "admin"
26
+ includePaths: []
27
+ excludePaths: []
28
+ technology:
29
+ exclude:
30
+ - "C"
31
+ - "ASP"
32
+ - "IBM DB2"
33
+ - "PHP"
34
+ - "CouchDB"
35
+ - "XML"
36
+ - "Microsoft SQL Server"
37
+ - "JSP/Servlet"
38
+ - "Firebird"
39
+ - "MongoDB"
40
+ - "HypersonicSQL"
41
+ - "SAP MaxDB"
42
+ - "Ruby"
43
+ - "SCM"
44
+ - "WS"
45
+ - "Microsoft Access"
46
+ - "Sybase"
47
+ - "Python"
48
+ parameters:
49
+ failOnError: true
50
+ failOnWarning: false
51
+ progressToStdout: true
52
+ vars: {}
53
+ jobs:
54
+ - parameters:
55
+ scanOnlyInScope: true
56
+ enableTags: false
57
+ disableAllRules: false
58
+ rules: []
59
+ name: "passiveScan-config"
60
+ type: "passiveScan-config"
61
+ - parameters:
62
+ context: "Demo testfire"
63
+ user: "demoadmin"
64
+ url: "https://demo.testfire.net"
65
+ maxDuration: 1
66
+ maxDepth: 2
67
+ maxChildren: 0
68
+ name: "spider"
69
+ type: "spider"
70
+ tests:
71
+ - onFail: "INFO"
72
+ statistic: "automation.spider.urls.added"
73
+ site: ""
74
+ operator: ">="
75
+ value: 100
76
+ name: "At least 100 URLs found"
77
+ type: "stats"
78
+ - name: "spider logged in" # Name of the test, optional
79
+ type: stats # Specifies that the test is of type 'stats'
80
+ statistic: "stats.auth.success" # Name of an integer / long statistic
81
+ operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
82
+ value: 2 # Value to compare statistic against
83
+ onFail: "info"
84
+ - name: "spider failed" # Name of the test, optional
85
+ type: stats # Specifies that the test is of type 'stats'
86
+ statistic: "stats.auth.failure" # Name of an integer / long statisti
87
+ operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
88
+ value: 2 # Value to compare statistic against
89
+ onFail: "info"
90
+ - parameters: {}
91
+ name: "passiveScan-wait"
92
+ type: "passiveScan-wait"
93
+ - parameters:
94
+ context: "Demo testfire"
95
+ user: "demoadmin"
96
+ policy: ""
97
+ maxRuleDurationInMins: 0
98
+ maxScanDurationInMins: 5
99
+ maxAlertsPerRule: 0
100
+ policyDefinition:
101
+ defaultStrength: "medium"
102
+ defaultThreshold: "medium"
103
+ rules: []
104
+ tests:
105
+ - name: "spider logged in" # Name of the test, optional
106
+ type: stats # Specifies that the test is of type 'stats'
107
+ statistic: "stats.auth.success" # Name of an integer / long statistic
108
+ operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
109
+ value: 2 # Value to compare statistic against
110
+ onFail: "info"
111
+ - name: "spider failed" # Name of the test, optional
112
+ type: stats # Specifies that the test is of type 'stats'
113
+ statistic: "stats.auth.failure" # Name of an integer / long statisti
114
+ operator: ">=" # One of '==', '!=', '>=', '>', '<', '<='
115
+ value: 2 # Value to compare statistic against
116
+ onFail: "info"
117
+ name: "activeScan"
118
+ type: "activeScan"
119
+ - parameters:
120
+ template: "traditional-json"
121
+ reportDir: ""
122
+ reportFile: ""
123
+ reportTitle: "Boman_Scanning_Report"
124
+ reportDescription: ""
125
+ displayReport: false
126
+ risks:
127
+ - "info"
128
+ - "low"
129
+ - "medium"
130
+ - "high"
131
+ confidences:
132
+ - "falsepositive"
133
+ - "low"
134
+ - "medium"
135
+ - "high"
136
+ - "confirmed"
137
+ sites: []
138
+ name: "report"
139
+ type: "report"
@@ -270,6 +270,7 @@ def showSummary():
270
270
  logging.info('----------------------------------------')
271
271
  logging.info('-------- Vulnerabilities Found ---------')
272
272
  logging.info('HIGH : %s | MEDIUM : %s | LOW : %s ', Config.high_count, Config.medium_count, Config.low_count)
273
+ logging.warning("The HIGH, MEDIUM and LOW counts are derived after excluding False Postive, Accepted Risk, Not Applicable and Muted Vulnerabilities")
273
274
  logging.info('----------------------------------------')
274
275
  logging.info('----------------------------------------')
275
276
  logging.info('-------- SAST STATUS ---------')
@@ -1034,3 +1035,104 @@ def copy_file(source_path, dest_path):
1034
1035
  return True
1035
1036
 
1036
1037
  return False # Source file not found
1038
+
1039
+ def reachability_support_check(language):
1040
+ if language in Config.supported_languages_reachability:
1041
+ return language
1042
+ elif language == "nodejs":
1043
+ return "js"
1044
+ return False
1045
+
1046
+
1047
+ def reachability_analysis(bom_file, reachable_slices_file):
1048
+ """Find and print reachable libraries by matching purls."""
1049
+
1050
+ # Load the SBOM and reachable slice JSON files
1051
+
1052
+ sbom_data = load_json(bom_file)
1053
+
1054
+ reachable_slice_data = load_json(reachable_slices_file)
1055
+
1056
+ # print(sbom_data)
1057
+
1058
+ # Extract purls from SBOM
1059
+
1060
+ sbom_purls = {}
1061
+
1062
+ for component in sbom_data.get("components", []):
1063
+
1064
+ purl = component.get("purl")
1065
+
1066
+ if purl:
1067
+ # print(purl)
1068
+ if purl not in sbom_purls.keys():
1069
+
1070
+ package_name = component.get("name")
1071
+ package_version = component.get("version")
1072
+
1073
+ if package_name and package_version:
1074
+ sbom_purls[purl] = f'{package_name}@{package_version}'
1075
+ else:
1076
+ sbom_purls[purl] = f'{package_name}'
1077
+
1078
+
1079
+
1080
+
1081
+ # Check for matches in the reachable slice file
1082
+ reachable_packages = []
1083
+ reachable_dict = {}
1084
+ for reachable in reachable_slice_data.get("reachables", []):
1085
+ # print(reachable)
1086
+ for purl in reachable.get("purls", []):
1087
+ # print(purl)
1088
+ if purl in sbom_purls.keys():
1089
+
1090
+ if purl in reachable_dict.keys():
1091
+
1092
+ reachable_dict[purl].append(reachable.get("flows",[]))
1093
+
1094
+ else:
1095
+ reachable_dict[purl] = []
1096
+
1097
+ # print(reachable_dict.keys())
1098
+ for key in reachable_dict.keys():
1099
+ reachable_packages.append({
1100
+ "package":sbom_purls[key],
1101
+ "purl":key,
1102
+ "flows": reachable_dict[key]
1103
+ })
1104
+ return reachable_packages
1105
+
1106
+ def load_json(file_path):
1107
+
1108
+ """Load a JSON file."""
1109
+
1110
+ with open(file_path, "r") as f:
1111
+
1112
+ return json.load(f)
1113
+
1114
+ def copy_contents(source,destination):
1115
+
1116
+ # Copy the file
1117
+ with open(source, 'rb') as src_file:
1118
+ with open(destination, 'wb') as dest_file:
1119
+ dest_file.write(src_file.read())
1120
+
1121
+ logging.info(f"File copied from {source} to {destination}")
1122
+
1123
+ def get_summary_of_vulns(response):
1124
+ vuln = {"LOW":0,"MEDIUM":0,"HIGH":0,"CRITICAL":0}
1125
+ if response['status'] == True:
1126
+ logging.info('Summary: Analyzing the vulnerabilities found')
1127
+ for data in response['data'] :
1128
+ vuln[data['_id']] = data['count']
1129
+
1130
+ Config.low_count = vuln['LOW']
1131
+ Config.medium_count = vuln['MEDIUM']
1132
+ Config.high_count = vuln['HIGH']
1133
+ Config.critical_count = vuln['CRITICAL']
1134
+
1135
+ logging.info('Summary: Analyzing Vulnerabitlites Done')
1136
+
1137
+ else:
1138
+ logging.info('Summary: Error Analyzing Vulnerabitlites.')
@@ -365,7 +365,19 @@ def tool_configuration_validation():
365
365
  Config.scan_name = json_response['data']['scan_name']
366
366
  Config.con_scan_response = json_response['data']['cs']
367
367
  Config.sbom_response = json_response['data']['sbom']
368
- Config.iac_scan_response = json_response['data']['iac']
368
+ Config.iac_scan_response = json_response['data']['iac']
369
+
370
+ if 'reachability' in json_response['data'].keys():
371
+ Config.reachability_response = json_response['data']['reachability']
372
+ if 'code_language' in json_response.keys():
373
+ Config.reachability_language = json_response['code_language']
374
+ Config.reachability_present = True
375
+ else:
376
+ Config.reachability_present = False
377
+ logging.warning("Reachability: Not Configured!!!! Message: Code language was not found on JSON Response")
378
+ else:
379
+ Config.reachability_present = False
380
+ logging.warning("Reachability: Not Configured!!!! Message: Tool configuration data was not found on JSON Response")
369
381
 
370
382
 
371
383
  # if Config.json_response['data']['advanced_zap_auth']:
@@ -411,19 +423,19 @@ def tool_configuration_validation():
411
423
  logging.error(f'Tool Config: Failed!!! exit code: 4(CONFIG ERROR) Message: SaaS CONFIGURATION ERROR: {ke}')
412
424
  exit(4) #configuration error
413
425
 
414
- Config.sast_build_dir = Config.sca_build_dir = Config.con_scan_build_dir = Config.sbom_build_dir = Config.iac_scan_build_dir = os.getcwd()+'/'
426
+ Config.sast_build_dir = Config.sca_build_dir = Config.con_scan_build_dir = Config.sbom_build_dir = Config.iac_scan_build_dir = Config.reachability_build_dir = os.getcwd()+'/'
415
427
 
416
428
  try:
417
429
  if Config.sast_present:
418
- if Config.sast_response[0]['lang'] != "sonar":
430
+ if (Config.sast_response[0]['lang'] != "sonar") and (Config.sast_response[0]['lang'] != "semgrep-pro"):
419
431
  Config.sast_message = 'SAST is properly configured'
420
432
  Config.sast_ignore = True if Config.sast_configuration['ignore_files'].lower() == "true" else False
421
433
  Config.sast_target = Config.sast_configuration['target']
422
434
  Config.sast_lang = "semgrep"
423
435
  Config.sast_ignore_folders_and_files = Config.sast_configuration['sast_ignore_file_data']
424
436
  else:
425
- Config.sast_lang = "sonar"
426
- logging.warning('Tool Config: SAST was properly configured on SaaS. It will be running Sonar from SaaS')
437
+ Config.sast_lang = Config.sast_response[0]['lang']
438
+ logging.warning(f'Tool Config: SAST was properly configured on SaaS. It will be running {Config.sast_lang} from SaaS')
427
439
  else:
428
440
  Config.sast_message = 'SAST was not properly configured on SaaS'
429
441
  logging.warning('Tool Config: SAST was not properly configured on SaaS')
@@ -472,7 +484,11 @@ def tool_configuration_validation():
472
484
 
473
485
  try:
474
486
  if Config.secret_scan_present:
475
- Config.secret_scan_message = 'Secret Scan is properly configured'
487
+ if Config.secret_scan_response[0]['tool'] != "Semgrep Pro":
488
+ Config.secret_scan_message = 'Secret Scan is properly configured'
489
+ else:
490
+ Config.secret_scan_lang = Config.secret_scan_response[0]['tool'].lower()
491
+ logging.warning(f"Tool Config: Secret Scan was properly configured on SaaS. It will be running {Config.secret_scan_response[0]['tool']} from SaaS")
476
492
  else:
477
493
  Config.secret_scan_message = 'Secret Scan was not properly configured on SaaS'
478
494
  logging.warning('Tool Config: Secret Scan was not properly configured on SaaS')
@@ -483,11 +499,17 @@ def tool_configuration_validation():
483
499
 
484
500
  try:
485
501
  if Config.sca_present:
486
- if Config.sca_response[0]['tool'] != "SNYK":
502
+ if Config.sca_response[0]['tool'] != "SNYK" and Config.sca_response[0]['tool'] != "Semgrep Pro":
487
503
  Config.sca_message = 'SCA is properly configured'
504
+ if Config.reachability_present:
505
+ if Utils.reachability_support_check(Config.reachability_language):
506
+ Config.reachability_language = Utils.reachability_support_check(Config.reachability_language)
507
+ Config.sbom_present = True
508
+ else:
509
+ Config.reachability_present = False
488
510
  else:
489
- Config.sca_lang = "snyk"
490
- logging.warning('Tool Config: SCA was properly configured on SaaS. It will be running SNYK from SaaS')
511
+ Config.sca_lang = Config.sca_response[0]['tool'].lower()
512
+ logging.warning(f"Tool Config: SCA was properly configured on SaaS. It will be running {Config.sca_response[0]['tool']} from SaaS")
491
513
 
492
514
  else:
493
515
  Config.sca_message = 'SCA was not properly configured on SaaS'
@@ -1,8 +1,9 @@
1
1
  [metadata]
2
2
  environment = prod
3
- version = 2.4.7
3
+ version = 2.4.8
4
4
  name = boman-cli
5
- saas_base_url = https://dashboard.boman.ai/
5
+ saas_base_url =
6
+ https = //dashboard.boman.ai/
6
7
 
7
8
  [egg_info]
8
9
  tag_build =
@@ -1,6 +1,6 @@
1
- coloredlogs<=15.0.1
2
1
  docker<=7.0.0
3
- pyfiglet<=1.0.2
4
- pyyaml
5
2
  requests<=2.31.0
3
+ pyyaml
4
+ coloredlogs<=15.0.1
6
5
  xmltodict<=0.13.0
6
+ pyfiglet<=1.0.2
File without changes
File without changes