boman-cli 2.5.15__tar.gz → 2.6.2__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.
Files changed (22) hide show
  1. {boman_cli-2.5.15 → boman_cli-2.6.2}/PKG-INFO +16 -13
  2. {boman_cli-2.5.15 → boman_cli-2.6.2}/README.md +14 -2
  3. {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/PKG-INFO +16 -13
  4. {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/SOURCES.txt +1 -0
  5. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/Config.py +18 -10
  6. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/auth.py +13 -7
  7. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/dev_attribution.py +2 -1
  8. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/main.py +64 -28
  9. boman_cli-2.6.2/bomancli/malware_rules/boman/js/boman-malware-detection.yml +35 -0
  10. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/utils.py +201 -3
  11. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/validation.py +3 -0
  12. {boman_cli-2.5.15 → boman_cli-2.6.2}/setup.cfg +3 -2
  13. {boman_cli-2.5.15 → boman_cli-2.6.2}/setup.py +1 -1
  14. {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/dependency_links.txt +0 -0
  15. {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/entry_points.txt +0 -0
  16. {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/requires.txt +0 -0
  17. {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/top_level.txt +0 -0
  18. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/_init_.py +0 -0
  19. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/base_logger.py +0 -0
  20. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/loc_finder.py +0 -0
  21. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/sbom_enricher.py +0 -0
  22. {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/templates/template_plan.yaml +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: boman-cli
3
- Version: 2.5.15
3
+ Version: 2.6.2
4
4
  Summary: CLI tool of boman.ai
5
5
  Home-page: https://boman.ai
6
6
  Author: Sumeru Software Solutions Pvt. Ltd.
@@ -19,15 +19,6 @@ Requires-Dist: xmltodict<=0.13.0
19
19
  Requires-Dist: pyfiglet<=1.0.2
20
20
  Requires-Dist: aiohttp>=3.8.0
21
21
  Requires-Dist: packageurl-python>=0.11.0
22
- Dynamic: author
23
- Dynamic: author-email
24
- Dynamic: classifier
25
- Dynamic: description
26
- Dynamic: description-content-type
27
- Dynamic: home-page
28
- Dynamic: license
29
- Dynamic: requires-dist
30
- Dynamic: summary
31
22
 
32
23
  # Introduction
33
24
  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.
@@ -110,10 +101,22 @@ Example: boman-cli -a run -zap_session_script ./session.js
110
101
 
111
102
 
112
103
 
113
- ### Release Note:
104
+ # Release Notes:
105
+
106
+ ### 2.6.1
107
+ - **Updated tools** - updated SAST, SCA and Secret scan tools
108
+
109
+ ### 2.5.12
110
+ - **Added Cache Dir for Container Scan** - Added Custom cache dir location for Container Scan to eliminate permission issue
111
+
112
+ ### 2.5.11
113
+ - **Added Cache Dir for SBOM** - Added Custom cache dir location for SBOM to eliminate permission issue
114
+
115
+ ### 2.5.10
116
+ - **Reachability (Depricated)** - Reachability is completely depricated to optimize consumption of CICD minutes
114
117
 
115
118
  ### 2.5.9
116
- - **SBOM API BUg** - New Feature. Additional sbom columns were added
119
+ - **SBOM API BUG** - New Feature. Additional sbom columns were added
117
120
 
118
121
  ### 2.5.8
119
122
 
@@ -79,10 +79,22 @@ Example: boman-cli -a run -zap_session_script ./session.js
79
79
 
80
80
 
81
81
 
82
- ### Release Note:
82
+ # Release Notes:
83
+
84
+ ### 2.6.1
85
+ - **Updated tools** - updated SAST, SCA and Secret scan tools
86
+
87
+ ### 2.5.12
88
+ - **Added Cache Dir for Container Scan** - Added Custom cache dir location for Container Scan to eliminate permission issue
89
+
90
+ ### 2.5.11
91
+ - **Added Cache Dir for SBOM** - Added Custom cache dir location for SBOM to eliminate permission issue
92
+
93
+ ### 2.5.10
94
+ - **Reachability (Depricated)** - Reachability is completely depricated to optimize consumption of CICD minutes
83
95
 
84
96
  ### 2.5.9
85
- - **SBOM API BUg** - New Feature. Additional sbom columns were added
97
+ - **SBOM API BUG** - New Feature. Additional sbom columns were added
86
98
 
87
99
  ### 2.5.8
88
100
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: boman-cli
3
- Version: 2.5.15
3
+ Version: 2.6.2
4
4
  Summary: CLI tool of boman.ai
5
5
  Home-page: https://boman.ai
6
6
  Author: Sumeru Software Solutions Pvt. Ltd.
@@ -19,15 +19,6 @@ Requires-Dist: xmltodict<=0.13.0
19
19
  Requires-Dist: pyfiglet<=1.0.2
20
20
  Requires-Dist: aiohttp>=3.8.0
21
21
  Requires-Dist: packageurl-python>=0.11.0
22
- Dynamic: author
23
- Dynamic: author-email
24
- Dynamic: classifier
25
- Dynamic: description
26
- Dynamic: description-content-type
27
- Dynamic: home-page
28
- Dynamic: license
29
- Dynamic: requires-dist
30
- Dynamic: summary
31
22
 
32
23
  # Introduction
33
24
  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.
@@ -110,10 +101,22 @@ Example: boman-cli -a run -zap_session_script ./session.js
110
101
 
111
102
 
112
103
 
113
- ### Release Note:
104
+ # Release Notes:
105
+
106
+ ### 2.6.1
107
+ - **Updated tools** - updated SAST, SCA and Secret scan tools
108
+
109
+ ### 2.5.12
110
+ - **Added Cache Dir for Container Scan** - Added Custom cache dir location for Container Scan to eliminate permission issue
111
+
112
+ ### 2.5.11
113
+ - **Added Cache Dir for SBOM** - Added Custom cache dir location for SBOM to eliminate permission issue
114
+
115
+ ### 2.5.10
116
+ - **Reachability (Depricated)** - Reachability is completely depricated to optimize consumption of CICD minutes
114
117
 
115
118
  ### 2.5.9
116
- - **SBOM API BUg** - New Feature. Additional sbom columns were added
119
+ - **SBOM API BUG** - New Feature. Additional sbom columns were added
117
120
 
118
121
  ### 2.5.8
119
122
 
@@ -17,4 +17,5 @@ bomancli/main.py
17
17
  bomancli/sbom_enricher.py
18
18
  bomancli/utils.py
19
19
  bomancli/validation.py
20
+ bomancli/malware_rules/boman/js/boman-malware-detection.yml
20
21
  bomancli/templates/template_plan.yaml
@@ -121,31 +121,39 @@ class Config:
121
121
 
122
122
  log_level = "INFO"
123
123
 
124
- version = 'v2.5.15'
124
+ version = 'v2.6.2'
125
125
 
126
126
  boman_config_file = 'boman.yaml'
127
127
 
128
128
  osv_supported_files = [
129
+ "conan.lock",
130
+ "pubspec.lock",
131
+ "mix.lock",
132
+ "go.mod",
133
+ "cabal.project.freeze",
134
+ "stack.yaml.lock",
129
135
  "buildscript-gradle.lockfile",
130
136
  "gradle.lockfile",
137
+ "gradle/verification-metadata.xml",
131
138
  "pom.xml",
132
- "go.mod",
133
- "mix.lock",
134
- "pubspec.lock",
135
- "conan.lock",
139
+ "bun.lock",
136
140
  "package-lock.json",
137
141
  "pnpm-lock.yaml",
138
142
  "yarn.lock",
143
+ "deps.json",
144
+ "packages.config",
139
145
  "composer.lock",
140
146
  "Pipfile.lock",
141
147
  "poetry.lock",
142
148
  "requirements.txt",
143
149
  "pdm.lock",
144
- "Cargo.lock",
150
+ "pylock.toml",
151
+ "uv.lock",
152
+ "renv.lock",
145
153
  "Gemfile.lock",
146
- "renv.lock"
154
+ "gems.locked",
155
+ "Cargo.lock"
147
156
  ]
148
-
149
157
  #Container scan
150
158
  con_scan_present=None
151
159
  con_scan_build_dir=None
@@ -208,7 +216,7 @@ class Config:
208
216
  "c++"
209
217
  ]
210
218
 
211
- reachability_present=None
219
+ reachability_present=False
212
220
  reachability_build_dir=None
213
221
  reachability_message=None
214
222
  reachability_response=None
@@ -219,7 +227,7 @@ class Config:
219
227
 
220
228
  fail_build = False
221
229
  sla_fail_build = False
222
- polling_time = 60
230
+ polling_time = 100
223
231
  polling_frequency = 10
224
232
 
225
233
  ml_success = False
@@ -60,19 +60,21 @@ def new_authorize():
60
60
  Config.sca_target = sca_configuration['target']
61
61
  Config.sca_ignore = True if sca_configuration['ignore_files'].lower() == "true" else False
62
62
  Config.sca_ignore_folders_and_files = sca_configuration['sca_ignore_file_data']
63
+ # logging.info(f"SCA Target for SCA scan is {Config.sca_target}")
63
64
  if Config.sca_type == "directory":
64
65
  file_present = False
65
66
  for filename in Config.osv_supported_files:
66
- if Config.sca_target is None:
67
+ if Config.sca_target is None or Config.sca_target == "":
67
68
  if recursive_file_present_check(Config.sca_build_dir,filename):
68
69
  file_present =True
69
70
  Config.sca_target= ""
70
71
  logging.info(f"New Authorization: Boman has found the dependency file: {filename} in the path: {os.path.join(Config.sca_build_dir,Config.sca_target)}")
71
72
  break
72
73
  else:
73
- if recursive_file_present_check(os.path.join(Config.sca_build_dir,Config.sca_target),filename):
74
+ target = recursive_file_present_check(os.path.join(Config.sca_build_dir,Config.sca_target),filename)
75
+ if target:
74
76
  file_present =True
75
- Config.sca_target= os.path.join(Config.sca_target,filename)
77
+ Config.sca_target= target
76
78
  logging.info(f"New Authorization: Boman has found the dependency file: {filename} in the path: {os.path.join(Config.sca_build_dir,Config.sca_target)}")
77
79
  break
78
80
  if file_present:
@@ -90,6 +92,7 @@ def new_authorize():
90
92
  exit(4)
91
93
 
92
94
  logging.info(f"Boman opted for: {Config.sca_lang} scan.")
95
+ # logging.info(f"SCA Target for SCA scan is {Config.sca_target}")
93
96
 
94
97
 
95
98
 
@@ -103,16 +106,17 @@ def authorize():
103
106
  if Config.sca_type == "directory":
104
107
  file_present = False
105
108
  for filename in Config.osv_supported_files:
106
- if Config.sca_target is None:
109
+ if Config.sca_target is None or Config.sca_target == "":
107
110
  if recursive_file_present_check(Config.sca_build_dir,filename):
108
111
  file_present =True
109
112
  Config.sca_target= ""
110
113
  logging.info(f"Authorization: Boman has found the dependency file: {filename} in the path: {os.path.join(Config.sca_build_dir,Config.sca_target)}")
111
114
  break
112
- else:
113
- if recursive_file_present_check(os.path.join(Config.sca_build_dir,Config.sca_target),filename):
115
+ else:
116
+ target = recursive_file_present_check(os.path.join(Config.sca_build_dir,Config.sca_target),filename)
117
+ if target:
114
118
  file_present =True
115
- Config.sca_target= os.path.join(Config.sca_target,filename)
119
+ Config.sca_target= target
116
120
  logging.info(f"Authorization: Boman has found the dependency file: {filename} in the path: {os.path.join(Config.sca_build_dir,Config.sca_target)}")
117
121
  break
118
122
  if file_present:
@@ -184,6 +188,8 @@ def authorize():
184
188
  Config.reachability_present = False
185
189
  logging.warning("Reachability: Not Configured!!!! Message: Tool configuration data was not found on JSON Response")
186
190
 
191
+ # As to keep reachability logic. Due to reachbility not yielding any result and time consuming in runtime making it false without removeing the logic.
192
+ Config.reachability_present = False
187
193
  return 1
188
194
  except:
189
195
  logging.info("Authorization: Failed!!! exit code: 1 (Server ERROR) Message: Problem occured while authorizing the scan, Please contact boman.ai team")
@@ -4,6 +4,7 @@ import argparse
4
4
  import os
5
5
  import datetime
6
6
  import re
7
+ import sys
7
8
  from docker import errors
8
9
  from bomancli.base_logger import logging
9
10
  from bomancli.Config import Config
@@ -26,7 +27,7 @@ def ensure_full_git_history():
26
27
  logging.info("[INFO] Repository already has full history.")
27
28
 
28
29
  except Exception as e:
29
- logging.error(f"[WARN] Could not determine git history depth: {e}", file=sys.stderr)
30
+ logging.error(f"[WARN] Could not determine git history depth: {e}")
30
31
 
31
32
 
32
33
 
@@ -12,6 +12,7 @@ from pathlib import Path
12
12
  import sys
13
13
  import pyfiglet
14
14
  import atexit
15
+ import shutil
15
16
 
16
17
  ##importing required files
17
18
 
@@ -179,7 +180,9 @@ def runImage(data=None,type=None):
179
180
  try:
180
181
 
181
182
  # logging.info('Semgrep token present')
182
-
183
+ logging.info("copying custom opengrep rules to the build directory")
184
+ Utils.copy_rules_to_base_docker('malware_rules', Config.sast_build_dir)
185
+ command_line = f"{command_line} --config auto --config /src/malware_rules --exclude='/src/malware_rules/**' --exclude='/src/boman_opengrep.json'"
183
186
  if Config.jenkins == 'yes':
184
187
 
185
188
  logging.info('SAST Docker: Preparing %s scan for jenkins env with %s user ', tool_name , userid)
@@ -196,13 +199,16 @@ def runImage(data=None,type=None):
196
199
  container_output = docker.containers.run(docker_image, command_line, volumes={Config.sast_build_dir: {
197
200
  'bind': data['bind']}},user=userid,detach=detach,environment=env_var,remove=True)
198
201
  logging.info('SAST Docker: SUCCESS !!!. Message: %s Scan Completed',tool_name)
199
-
202
+ logging.info('SAST Docker: Deleting custom opengrep rules from the build directory')
203
+ shutil.rmtree('malware_rules')
200
204
  except errors.ContainerError as exc:
201
205
  msg='\n The following error has been recorded while scanning SAST'
202
206
  Utils.logError(msg,str(exc))
203
207
  logging.error(f'SAST Docker: Failed !!!, Message: Some Error recorded while scanning {str(exc)}')
204
208
  Config.sast_scan_status = 'Failed'
205
209
  Config.sast_errors = f'Exit Status {exc.exit_status}:{str(exc)}]'
210
+ logging.info('SAST Docker: Deleting custom opengrep rules from the build directory')
211
+ shutil.rmtree('malware_rules')
206
212
 
207
213
 
208
214
 
@@ -436,8 +442,10 @@ def runImage(data=None,type=None):
436
442
  Config.dast_message ='Error recorded while uploading the report of',tool_name
437
443
 
438
444
  if type == 'SCA':
445
+ sbom_file = "unknown"
439
446
  Utils.checkImageAlreadyExsist(docker_image)
440
447
  logging.info('Running %s',tool_name)
448
+ logging.info("SCA Docker: Target for SCA scan is %s",Config.sca_target)
441
449
  try:
442
450
  if Config.sca_lang=="osv":
443
451
  if Config.sca_type == "directory":
@@ -451,9 +459,24 @@ def runImage(data=None,type=None):
451
459
  target = Utils.remove_leading_slash(Config.sca_target)
452
460
  Config.build_dir = Config.sca_build_dir
453
461
  command_line = "% s" % command_line.format(target ='/src/'+target)
454
- container_output = docker.containers.run(docker_image, command_line, volumes={Config.sca_build_dir: {
462
+ if Config.sca_type == "sbom":
463
+ sbom_file = Utils.normalize_sbom_filename(f"{Config.sca_build_dir}/{target}")
464
+ logging.info('SCA Docker: SBOM file detected %s',sbom_file)
465
+ if sbom_file != "unknown":
466
+ command_line = "% s" % command_line.format(target ='/src/'+sbom_file)
467
+ logging.info('SCA Docker: Command ==> %s',command_line)
468
+ container_output = docker.containers.run(docker_image, command_line, volumes={Config.sca_build_dir: {
455
469
  'bind': data['bind']}}, user=uid, remove=True)
470
+ else:
471
+ raise errors.ContainerError("",-1,"","","Given SBOM file is not in SPDX or CycloneDX format",)
472
+ else:
473
+ container_output = docker.containers.run(docker_image, command_line, volumes={Config.sca_build_dir: {
474
+ 'bind': data['bind']}}, user=uid, remove=True)
456
475
  logging.info('SCA Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
476
+ if sbom_file != "unknown":
477
+ logging.info('sbom file detected %s',sbom_file)
478
+ logging.info('SCA Docker: Deleting SBOM file %s',sbom_file)
479
+ Utils.delete_file_on_error(Config.sca_build_dir + '/' + sbom_file)
457
480
  else:
458
481
  if Config.sca_ignore:
459
482
  Config.sca_exclude_files = Utils.read_bomanignore('.bomanignore')
@@ -472,12 +495,22 @@ def runImage(data=None,type=None):
472
495
  if exc.exit_status==1:
473
496
  Config.sca_message ='SCA scan completed'
474
497
  Config.sca_scan_status ='Completed'
498
+ if sbom_file != "unknown":
499
+ logging.info('sbom file detected %s',sbom_file)
500
+ logging.info('SCA Docker: Deleting SBOM file %s',sbom_file)
501
+ Utils.delete_file_on_error(Config.sca_build_dir + '/' + sbom_file)
475
502
  logging.info('SCA Docker: SUCCESS!!! Message: %s Scan Completed',tool_name)
476
- logging.info(f'OSV: {exc.stderr}')
503
+ logging.error(f'OSV: {exc.stderr}')
504
+ else:
505
+ logging.error('SCA Docker: Failed!!! Message: Some Error recorded while scanning %s',str(exc))
506
+ msg='\n The following error has been recorded while scanning sca'
507
+ Config.sca_scan_status ='Failed'
508
+ Config.sca_errors =f'{str(exc)}]'
509
+ Utils.logError(msg,str(exc))
477
510
  else:
478
511
  logging.error('SCA Docker: Failed!!! Message: Some Error recorded while scanning %s',str(exc))
479
512
  msg='\n The following error has been recorded while scanning sca'
480
- Config.sca_scan_status ='Completed'
513
+ Config.sca_scan_status ='Failed'
481
514
  Config.sca_errors =f'{str(exc)}]'
482
515
  Utils.logError(msg,str(exc))
483
516
 
@@ -836,23 +869,23 @@ def uploadReport(filename,toolname,tool_id,scan_details_id,type):
836
869
  r = requests.post(url,json=values)
837
870
  return 0
838
871
 
839
- try:
840
- logging.info('Removing the result file')
841
- if type != "sbom" and type != "reachability":
842
- logging.info("remvoing file here")
843
- os.remove(path)
844
- elif Config.reachability_present is not True and type == 'sbom':
845
- #print('dmdmd')
846
- os.remove(path)
847
- elif type == "reachability":
848
- os.remove(path)
849
- os.remove(Path(str(Config.sbom_build_dir))/Config.sbom_response[0]['output_file'])
850
- os.remove(path_reachability)
851
- os.remove(path_bom)
872
+ # try:
873
+ # logging.info('Removing the result file')
874
+ # if type != "sbom" and type != "reachability":
875
+ # logging.info("removing file %s",path)
876
+ # os.remove(path)
877
+ # elif Config.reachability_present is not True and type == 'sbom':
878
+ # logging.info("removing file %s",path)
879
+ # os.remove(path)
880
+ # elif type == "reachability":
881
+ # os.remove(path)
882
+ # os.remove(Path(str(Config.sbom_build_dir))/Config.sbom_response[0]['output_file'])
883
+ # os.remove(path_reachability)
884
+ # os.remove(path_bom)
885
+ # logging.info("removing file %s, %s, %s, %s", path, path_reachability, path_bom, Path(str(Config.sbom_build_dir))/Config.sbom_response[0]['output_file'])
852
886
 
853
-
854
- except Exception as e:
855
- logging.error(f"unable to remove the file: {path} ---> Error: {e}")
887
+ # except Exception as e:
888
+ # logging.error(f"unable to remove the file: {path} ---> Error: {e}")
856
889
 
857
890
  tool_output = json.dumps(data, ensure_ascii=False, indent=4)
858
891
 
@@ -907,16 +940,19 @@ def initSecertScan(path,data):
907
940
 
908
941
  try:
909
942
  logging.info('Running Secret Scanning on the repository')
943
+ # logging.info('Command for trufflehog is : %s',command_line_truffle)
910
944
  container = Config.docker_client.containers.run(image_name, command_line_truffle, detach=True,volumes={build_dir: {
911
- 'bind': bind_dir}})
945
+ 'bind': bind_dir}}, remove=True)
912
946
  op = []
913
947
  for iteration_main,line in enumerate(container.logs(stream=True)):
914
948
  try:
949
+ # print(line.strip())
915
950
  op.append(json.loads(line.strip()))
916
- #print(op[iteration_main]['stringsFound'])
917
- for iteration,key in enumerate(op[iteration_main]['stringsFound']):
918
- #print(key)
919
- op[iteration_main]['stringsFound'][iteration] = Utils.masker(key)
951
+ formatted_op = Utils.parse_new_output(op)
952
+ # # print(op[iteration_main]['stringsFound'])
953
+ # for iteration,key in enumerate(op[iteration_main]['stringsFound']):
954
+ # #print(key)
955
+ # op[iteration_main]['stringsFound'][iteration] = Utils.masker(key)
920
956
 
921
957
  except:
922
958
  logging.error('Some Findings from the trufflehog is unrecognisble.Skiping them.')
@@ -940,7 +976,7 @@ def initSecertScan(path,data):
940
976
  Config.build_dir = Config.sast_build_dir
941
977
  path = Config.sast_build_dir+file_name
942
978
  with open(path, 'w', encoding='utf-8') as f:
943
- json.dump(op, f, ensure_ascii=False, indent=4)
979
+ json.dump(formatted_op, f, ensure_ascii=False, indent=4)
944
980
 
945
981
  if uploadReport(file_name,tool_name,tool_id,scan_details_id,'SS'):
946
982
  logging.info('[COMPLETED]: Secret Scan report Uploaded')
@@ -1261,7 +1297,7 @@ def default():
1261
1297
  # parser.add_argument('-uid','--user_id',default='1000:1000',help="[internal] Pass the custom userid:groupid incase the lingu detec function is failed")
1262
1298
  parser.add_argument('-zap_arg','--zap_custom_arg',default=None,help="Pass the custom arguments for zap scanner")
1263
1299
  parser.add_argument('-semgrep_token','--semgrepToken',default='No',help="Pass the semgrep community token here")
1264
- 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 ")
1300
+ parser.add_argument('-pot','--pollTime',default=100,help="Polling Time. Min : 30s, Max: 5min, Default : 1min. Format: input the time in seconds eg: 1min => 60 ")
1265
1301
  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 ")
1266
1302
  parser.add_argument('-std','--setCacheDir',default=False,help="Set the cache directory for the scan. Set true if you use jenkins cicd")
1267
1303
  args = parser.parse_args()
@@ -0,0 +1,35 @@
1
+ rules:
2
+ - id: global-require-capability-capture-obfuscation-potential-malware
3
+ languages: [regex]
4
+ severity: HIGH
5
+ metadata:
6
+ category: security
7
+ technique: capability-smuggling
8
+ confidence: HIGH
9
+ impact: HIGH
10
+ message: >
11
+ Obfuscated malicious code was identified within the source code repository.
12
+ The code appears to have been introduced through a third-party dependency or
13
+ inadvertently committed by a developer. Its presence poses a supply chain security risk
14
+ and may enable unauthorized actions, data exfiltration, or compromise the integrity of
15
+ the application and development environment.
16
+ pattern-regex: '(global(This)?)\s*(\[(?:[^\[\]]|\[[^\[\]]*\]){1,80}\]|\.[A-Za-z0-9_$]+)\s*=\s*require\b'
17
+
18
+ - id: json-file-contains-obfuscated-js-code-potential-malware
19
+ languages: [regex]
20
+ severity: HIGH
21
+ paths:
22
+ include:
23
+ - "*.json"
24
+ metadata:
25
+ category: security
26
+ technique: format-integrity
27
+ confidence: MEDIUM
28
+ impact: HIGH
29
+ message: >
30
+ Obfuscated malicious code was identified within the source code repository.
31
+ The code appears to have been introduced through a third-party dependency or
32
+ inadvertently committed by a developer. Its presence poses a supply chain security risk
33
+ and may enable unauthorized actions, data exfiltration, or compromise the integrity of
34
+ the application and development environment.
35
+ pattern-regex: '(?s)\A(?=.*(?:\brequire\s*\(|\bfunction\s*\(|\beval\s*\(|=>|\b(?:var|let|const)\s+\w+\s*=|;\s*$))'
@@ -4,7 +4,7 @@
4
4
  # import loc_finder as loc
5
5
  #from datetime import datetime
6
6
 
7
-
7
+ from __future__ import annotations
8
8
  from bomancli.base_logger import logging
9
9
  from bomancli.Config import Config
10
10
  from bomancli import loc_finder as loc
@@ -21,7 +21,8 @@ import yaml
21
21
  import time
22
22
  from json import JSONDecodeError
23
23
  from pathlib import Path
24
-
24
+ import sys
25
+ import shutil
25
26
 
26
27
 
27
28
  logging.basicConfig(format='%(asctime)s — %(name)s — %(levelname)s — %(funcName)s:%(lineno)d — %(message)s')
@@ -45,6 +46,8 @@ def checkImageAlreadyExsist(imagename):
45
46
  #print(image.tags)
46
47
  if imagename in image.tags:
47
48
  logging.info('Image is already in the local machine')
49
+ logging.info('Checking if the image is up to date')
50
+ ensure_latest(imagename)
48
51
  return True
49
52
 
50
53
  logging.info('Image is not present in the local machine')
@@ -60,7 +63,27 @@ def checkImageAlreadyExsist(imagename):
60
63
  exit(3) ## docker/system error
61
64
 
62
65
 
66
+ ### ensure docker image is up to date
67
+ def ensure_latest(image_name):
63
68
 
69
+ # Get local image digest
70
+ try:
71
+ local = docker.images.get(image_name)
72
+ local_digest = local.attrs['RepoDigests'][0].split('@')[1] if local.attrs['RepoDigests'] else None
73
+ except docker.errors.ImageNotFound:
74
+ local_digest = None
75
+
76
+ # Get remote digest from registry
77
+ reg_data = docker.images.get_registry_data(image_name)
78
+ remote_digest = reg_data.attrs['Descriptor']['digest']
79
+
80
+ if local_digest != remote_digest:
81
+ logging.info(f"Pulling newer image: {image_name}")
82
+ docker.images.pull(image_name)
83
+ return False
84
+ else:
85
+ logging.info(f"Already up to date: {image_name}")
86
+ return True
64
87
 
65
88
  ### fucntion to check the git is present or not
66
89
  def isGitDirectory(path):
@@ -363,6 +386,19 @@ def showSummary():
363
386
  # logging.info(Config.sca_message)
364
387
  # logging.info(Config.secret_scan_message)
365
388
 
389
+ # opengrep rules file to base docker
390
+ def copy_rules_to_base_docker(opengrep_rules_path, base_docker_path):
391
+ logging.info('Copying the opengrep rules to base docker image')
392
+ try:
393
+ rules_path = pkg_resources.resource_filename(__name__,opengrep_rules_path)
394
+ # os.makedirs(os.path.join(base_docker_path, opengrep_rules_path), exist_ok=True)
395
+ # logging.info(rules_path, os.path.join(base_docker_path, opengrep_rules_path))
396
+ shutil.copytree(rules_path, os.path.join(base_docker_path, opengrep_rules_path))
397
+ logging.info('Copied the opengrep rules to base docker image successfully')
398
+ except Exception as e:
399
+ logging.error('Error while copying the opengrep rules to base docker image: %s', e)
400
+ exit(3) # docker/system error
401
+
366
402
  ## zap config part
367
403
 
368
404
  ##zap plan creation fucntion
@@ -972,6 +1008,7 @@ def load_cloc_json_safe(path="cloc.json"):
972
1008
  return empty_data
973
1009
 
974
1010
  Config.cloc_total_data = data['SUM']['code']
1011
+ os.remove(path) # Remove the file after reading
975
1012
  return data
976
1013
 
977
1014
  except (JSONDecodeError, OSError, ValueError):
@@ -1341,4 +1378,165 @@ def runReachability(data,userid):
1341
1378
 
1342
1379
 
1343
1380
 
1344
- return 1
1381
+ return 1
1382
+
1383
+
1384
+ def _extract_git(new_finding: dict) -> dict:
1385
+ """Safely drill into SourceMetadata.Data.Git."""
1386
+ return (
1387
+ new_finding
1388
+ .get("SourceMetadata", {})
1389
+ .get("Data", {})
1390
+ .get("Git", {})
1391
+ )
1392
+
1393
+
1394
+ def map_new_to_old(new_finding: dict) -> dict:
1395
+ """
1396
+ Convert a single new TruffleHog v3 finding dict to the old format dict.
1397
+
1398
+ Mapping table:
1399
+ commitHash <- SourceMetadata.Data.Git.commit
1400
+ date <- SourceMetadata.Data.Git.timestamp
1401
+ path <- SourceMetadata.Data.Git.file
1402
+ reason <- DetectorDescription
1403
+ stringsFound <- [Raw]
1404
+ branch <- "" (no equivalent in v3)
1405
+ commit <- "" (commit message not present in v3)
1406
+ diff <- "" (not present in v3)
1407
+ printDiff <- "" (not present in v3)
1408
+ """
1409
+ git = _extract_git(new_finding)
1410
+
1411
+ return {
1412
+ "branch": "",
1413
+ "commit": "",
1414
+ "commitHash": git.get("commit", ""),
1415
+ "date": git.get("timestamp", ""),
1416
+ "diff": "",
1417
+ "path": f'{git.get("file", "")}:{git.get("line", "")}',
1418
+ "printDiff": "",
1419
+ "reason": new_finding.get("DetectorDescription", "") if len(new_finding.get("DetectorDescription", "")) else new_finding.get("DetectorDescription", ""),
1420
+ "stringsFound": masker(new_finding["Raw"] if new_finding.get("Raw") else ""),
1421
+ }
1422
+
1423
+
1424
+ def is_log_entry(entry: dict) -> bool:
1425
+ """Log entries (info/warn/error) have a 'level' key; findings do not."""
1426
+ return "level" in entry
1427
+
1428
+
1429
+ def parse_new_output(new_output: list[dict]) -> list[dict]:
1430
+ """
1431
+ Parse a full new TruffleHog v3 output list (mix of log entries + findings)
1432
+ and return a list of dicts in the old format.
1433
+ """
1434
+ return [
1435
+ map_new_to_old(entry)
1436
+ for entry in new_output
1437
+ if not is_log_entry(entry) and "SourceMetadata" in entry
1438
+ ]
1439
+
1440
+
1441
+ # detect sbom format
1442
+ def detect_by_filename(filepath: str) -> str | None:
1443
+ """Quick check based on OSV-Scanner's recognized filename patterns."""
1444
+ name = Path(filepath).name.lower()
1445
+
1446
+ spdx_patterns = (".spdx.json", ".spdx", ".spdx.yml", ".spdx.rdf", ".spdx.rdf.xml")
1447
+ cyclonedx_patterns = ("bom.json", ".cdx.json", "bom.xml", ".cdx.xml")
1448
+
1449
+ if any(name.endswith(p) for p in spdx_patterns):
1450
+ return "SPDX"
1451
+ if name == "bom.json" or name == "bom.xml" or any(name.endswith(p) for p in cyclonedx_patterns):
1452
+ return "CycloneDX"
1453
+ return None
1454
+
1455
+
1456
+ def detect_by_content(filepath: str) -> str | None:
1457
+ """Fallback: inspect file content for identifying fields."""
1458
+ path = Path(filepath)
1459
+
1460
+ try:
1461
+ text = path.read_text(encoding="utf-8", errors="ignore")
1462
+ except Exception as e:
1463
+ logging.error(f"Error reading file: {e}", file=sys.stderr)
1464
+ return None
1465
+
1466
+ # JSON-based check
1467
+ if path.suffix.lower() == ".json" or text.strip().startswith("{"):
1468
+ try:
1469
+ data = json.loads(text)
1470
+ if data.get("bomFormat") == "CycloneDX":
1471
+ return "CycloneDX"
1472
+ if "spdxVersion" in data:
1473
+ return "SPDX"
1474
+ except json.JSONDecodeError:
1475
+ pass
1476
+
1477
+ # XML-based check
1478
+ if "<bom" in text and "cyclonedx" in text.lower():
1479
+ return "CycloneDX"
1480
+ if "SPDXVersion" in text or "spdx.org" in text.lower():
1481
+ return "SPDX"
1482
+
1483
+ # Tag-value SPDX format (plain .spdx files, not JSON/XML)
1484
+ if "SPDXVersion:" in text:
1485
+ return "SPDX"
1486
+
1487
+ return None
1488
+
1489
+
1490
+ def detect_sbom_format(filepath: str) -> str | None:
1491
+ """Combine filename + content checks, filename takes priority."""
1492
+ result = detect_by_filename(filepath)
1493
+ if result:
1494
+ return result
1495
+ return detect_by_content(filepath)
1496
+
1497
+
1498
+ def normalize_sbom_filename(filepath: str) -> str:
1499
+ """
1500
+ Detect SBOM format and copy it to an OSV-Scanner-recognized filename
1501
+ in the same directory as the source file.
1502
+
1503
+ Returns:
1504
+ The resulting filename (not full path) as `out`, or "unknown"
1505
+ if the format could not be determined.
1506
+ """
1507
+ path = Path(filepath)
1508
+ if not path.exists():
1509
+ logging.error(f"Error: file not found: {filepath}")
1510
+ return "unknown"
1511
+
1512
+ fmt = detect_sbom_format(filepath)
1513
+
1514
+ if fmt == "SPDX":
1515
+ out = "boman-bom.spdx.json"
1516
+ elif fmt == "CycloneDX":
1517
+ out = "boman-bom.cdx.json"
1518
+ else:
1519
+ return "unknown"
1520
+
1521
+ dest_path = path.parent / out
1522
+
1523
+ # Avoid pointless copy if source is already correctly named
1524
+ if path.resolve() != dest_path.resolve():
1525
+ shutil.copy2(path, dest_path)
1526
+
1527
+ return out
1528
+
1529
+ def delete_file_on_error(filepath: str) -> bool:
1530
+ """
1531
+ Delete a file, typically used for cleanup when an error occurs.
1532
+ Returns True if deleted (or already gone), False if deletion failed.
1533
+ """
1534
+ try:
1535
+ path = Path(filepath)
1536
+ if path.exists():
1537
+ path.unlink()
1538
+ logging.info(f"Deleted: {filepath}")
1539
+ return True
1540
+ except Exception as e:
1541
+ logging.error(f"Failed to delete {filepath}: {e}")
1542
+ return False
@@ -384,6 +384,9 @@ def tool_configuration_validation():
384
384
  # Config.dast_adv_auth_enabled = True
385
385
  # else:
386
386
  # Config.dast_adv_auth_enabled = False
387
+
388
+ # As to keep reachability logic. Due to reachbility not yielding any result and time consuming in runtime making it false without removeing the logic.
389
+ Config.reachability_present = False
387
390
 
388
391
  except:
389
392
  logging.info("Tool Config: Failed!!! exit code: 1 (Server ERROR) Message: Problem occured while authorizing the scan, Please contact boman.ai team")
@@ -1,8 +1,9 @@
1
1
  [metadata]
2
2
  environment = prod
3
- version = 2.5.15
3
+ version = 2.6.2
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 =
@@ -46,7 +46,7 @@ setup(
46
46
  license='BSD 2-clause',
47
47
  entry_points = entry_points,
48
48
  packages=['bomancli'],
49
- package_data={'bomancli': ['templates/template_plan.yaml']},
49
+ package_data={'bomancli': ['templates/template_plan.yaml', 'malware_rules/**/*', 'malware_rules/**/**/*']},
50
50
  install_requires=['docker<=7.0.0',
51
51
  'requests<=2.31.0',
52
52
  'pyyaml',
File without changes