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.
- {boman_cli-2.5.15 → boman_cli-2.6.2}/PKG-INFO +16 -13
- {boman_cli-2.5.15 → boman_cli-2.6.2}/README.md +14 -2
- {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/PKG-INFO +16 -13
- {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/SOURCES.txt +1 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/Config.py +18 -10
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/auth.py +13 -7
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/dev_attribution.py +2 -1
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/main.py +64 -28
- boman_cli-2.6.2/bomancli/malware_rules/boman/js/boman-malware-detection.yml +35 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/utils.py +201 -3
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/validation.py +3 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/setup.cfg +3 -2
- {boman_cli-2.5.15 → boman_cli-2.6.2}/setup.py +1 -1
- {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/dependency_links.txt +0 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/entry_points.txt +0 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/requires.txt +0 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/boman_cli.egg-info/top_level.txt +0 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/_init_.py +0 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/base_logger.py +0 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/loc_finder.py +0 -0
- {boman_cli-2.5.15 → boman_cli-2.6.2}/bomancli/sbom_enricher.py +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: boman-cli
|
|
3
|
-
Version: 2.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: boman-cli
|
|
3
|
-
Version: 2.
|
|
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
|
-
|
|
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
|
|
119
|
+
- **SBOM API BUG** - New Feature. Additional sbom columns were added
|
|
117
120
|
|
|
118
121
|
### 2.5.8
|
|
119
122
|
|
|
@@ -121,31 +121,39 @@ class Config:
|
|
|
121
121
|
|
|
122
122
|
log_level = "INFO"
|
|
123
123
|
|
|
124
|
-
version = 'v2.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
150
|
+
"pylock.toml",
|
|
151
|
+
"uv.lock",
|
|
152
|
+
"renv.lock",
|
|
145
153
|
"Gemfile.lock",
|
|
146
|
-
"
|
|
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=
|
|
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 =
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
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=
|
|
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}"
|
|
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
|
-
|
|
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.
|
|
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 ='
|
|
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
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
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
|
-
|
|
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
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
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(
|
|
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=
|
|
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")
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|