Skip to content

Commit 151d159

Browse files
authored
Merge pull request #156 from fosslight/new_change_dc
Use Dependency-check v12.1.7 to analyze .jar
2 parents 9a72757 + f4d2949 commit 151d159

File tree

4 files changed

+195
-14
lines changed

4 files changed

+195
-14
lines changed

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@ pytz
99
XlsxWriter
1010
PyYAML
1111
fosslight_util>=2.1.13
12-
dependency-check

src/fosslight_binary/__init__.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) 2025 LG Electronics Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
import logging
6+
import os
7+
import stat
8+
import subprocess
9+
import tempfile
10+
import urllib.request
11+
import zipfile
12+
import sys
13+
14+
logger = logging.getLogger(__name__)
15+
DEPENDENCY_CHECK_VERSION = "12.1.7"
16+
17+
18+
def _install_dependency_check():
19+
"""Install OWASP dependency-check"""
20+
try:
21+
# Skip if explicitly disabled
22+
if os.environ.get('FOSSLIGHT_SKIP_AUTO_INSTALL', '').lower() in ('1', 'true', 'yes'):
23+
logger.info("Auto-install disabled by environment variable")
24+
return
25+
26+
env_home = os.environ.get('DEPENDENCY_CHECK_HOME', '').strip()
27+
install_dir = None
28+
forced_env = False
29+
if env_home:
30+
# Normalize
31+
env_home_abs = os.path.abspath(env_home)
32+
# Detect if env_home already the actual extracted root (ends with dependency-check)
33+
candidate_bin_win = os.path.join(env_home_abs, 'bin', 'dependency-check.bat')
34+
candidate_bin_nix = os.path.join(env_home_abs, 'bin', 'dependency-check.sh')
35+
if os.path.exists(candidate_bin_win) or os.path.exists(candidate_bin_nix):
36+
# env points directly to dependency-check root; install_dir is its parent
37+
install_dir = os.path.dirname(env_home_abs)
38+
forced_env = True
39+
else:
40+
# Assume env_home is the base directory where we should extract dependency-check/
41+
install_dir = env_home_abs
42+
43+
if not install_dir:
44+
# Fallback hierarchy: executable dir (if frozen) -> CWD
45+
candidate_base = None
46+
if getattr(sys, 'frozen', False):
47+
exe_dir = os.path.dirname(os.path.abspath(sys.executable))
48+
candidate_base = os.path.join(exe_dir, 'fosslight_dc_bin')
49+
50+
if not os.access(exe_dir, os.W_OK):
51+
candidate_base = None
52+
else:
53+
logger.debug(f"Using executable directory base: {candidate_base}")
54+
if not candidate_base:
55+
candidate_base = os.path.abspath(os.path.join(os.getcwd(), 'fosslight_dc_bin'))
56+
install_dir = candidate_base
57+
else:
58+
logger.debug(f"Resolved install_dir: {install_dir}")
59+
bin_dir = os.path.join(install_dir, 'dependency-check', 'bin')
60+
if sys.platform.startswith('win'):
61+
dc_path = os.path.join(bin_dir, 'dependency-check.bat')
62+
else:
63+
dc_path = os.path.join(bin_dir, 'dependency-check.sh')
64+
65+
# Check if dependency-check already exists
66+
if os.path.exists(dc_path):
67+
try:
68+
result = subprocess.run([dc_path, '--version'], capture_output=True, text=True, timeout=10)
69+
if result.returncode == 0:
70+
logger.debug("dependency-check already installed and working")
71+
# If we detected an existing root via env, retain it, else set home now.
72+
if forced_env:
73+
os.environ['DEPENDENCY_CHECK_HOME'] = env_home_abs
74+
else:
75+
os.environ['DEPENDENCY_CHECK_HOME'] = os.path.join(install_dir, 'dependency-check')
76+
os.environ['DEPENDENCY_CHECK_VERSION'] = DEPENDENCY_CHECK_VERSION
77+
return
78+
except (subprocess.TimeoutExpired, FileNotFoundError) as ex:
79+
logger.debug(f"Exception in dependency-check --version: {ex}")
80+
pass
81+
82+
# Download URL
83+
download_url = (f"https://github.com/dependency-check/DependencyCheck/releases/"
84+
f"download/v{DEPENDENCY_CHECK_VERSION}/"
85+
f"dependency-check-{DEPENDENCY_CHECK_VERSION}-release.zip")
86+
87+
os.makedirs(install_dir, exist_ok=True)
88+
logger.info(f"Downloading dependency-check {DEPENDENCY_CHECK_VERSION} from {download_url} ...")
89+
90+
# Download and extract
91+
with urllib.request.urlopen(download_url) as response:
92+
content = response.read()
93+
94+
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmp_file:
95+
tmp_file.write(content)
96+
tmp_zip_path = tmp_file.name
97+
98+
with zipfile.ZipFile(tmp_zip_path, 'r') as zip_ref:
99+
zip_ref.extractall(install_dir)
100+
os.unlink(tmp_file.name)
101+
102+
# Make shell scripts executable
103+
if os.path.exists(bin_dir):
104+
if sys.platform.startswith('win'):
105+
# Windows: .bat files only
106+
scripts = ["dependency-check.bat"]
107+
else:
108+
# Linux/macOS: .sh files only
109+
scripts = ["dependency-check.sh", "completion-for-dependency-check.sh"]
110+
111+
for script in scripts:
112+
script_path = os.path.join(bin_dir, script)
113+
if os.path.exists(script_path):
114+
st = os.stat(script_path)
115+
os.chmod(script_path, st.st_mode | stat.S_IEXEC)
116+
117+
logger.info("✅ OWASP dependency-check installed successfully!")
118+
logger.info(f"Installed to: {os.path.join(install_dir, 'dependency-check')}")
119+
120+
# Set environment variables after successful installation
121+
os.environ['DEPENDENCY_CHECK_VERSION'] = DEPENDENCY_CHECK_VERSION
122+
os.environ['DEPENDENCY_CHECK_HOME'] = os.path.join(install_dir, 'dependency-check')
123+
124+
return True
125+
126+
except Exception as e:
127+
logger.error(f"Failed to install dependency-check: {e}")
128+
logger.info("dependency-check can be installed manually from: https://github.com/dependency-check/DependencyCheck/releases")
129+
return False
130+
131+
132+
def _auto_install_dependencies():
133+
"""Auto-install required dependencies if not present."""
134+
# Only run this once per session
135+
if hasattr(_auto_install_dependencies, '_already_run'):
136+
return
137+
_auto_install_dependencies._already_run = True
138+
139+
try:
140+
# Install binary version
141+
_install_dependency_check()
142+
143+
logger.info(f"✅ dependency-check setup completed with version {DEPENDENCY_CHECK_VERSION}")
144+
except Exception as e:
145+
logger.warning(f"Auto-install failed: {e}")
146+
147+
148+
# Auto-install on import
149+
_auto_install_dependencies()

src/fosslight_binary/_binary_dao.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import pandas as pd
1010
import socket
1111
from urllib.parse import urlparse
12-
from ._binary import TLSH_CHECKSUM_NULL
12+
from fosslight_binary._binary import TLSH_CHECKSUM_NULL
1313
from fosslight_util.oss_item import OssItem
1414
import fosslight_util.constant as constant
1515

src/fosslight_binary/_jar_analysis.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@
77
import json
88
import os
99
import sys
10+
import subprocess
1011
import fosslight_util.constant as constant
11-
from ._binary import BinaryItem, VulnerabilityItem, is_package_dir
12+
from fosslight_binary._binary import BinaryItem, VulnerabilityItem, is_package_dir
1213
from fosslight_util.oss_item import OssItem
13-
from dependency_check import run as dependency_check_run
14-
1514

1615
logger = logging.getLogger(constant.LOGGER_NAME)
1716

1817

19-
def run_analysis(params, func):
18+
def run_analysis(command):
2019
try:
21-
sys.argv = params
22-
func()
23-
except SystemExit:
24-
pass
20+
result = subprocess.run(command, text=True, timeout=600)
21+
if result.returncode != 0:
22+
logger.error(f"dependency-check failed with return code {result.returncode}")
23+
raise Exception(f"dependency-check failed with return code {result.returncode}")
24+
except subprocess.TimeoutExpired:
25+
logger.error("dependency-check command timed out")
26+
raise
2527
except Exception as ex:
2628
logger.error(f"Run Analysis : {ex}")
29+
raise
2730

2831

2932
def get_oss_ver(version_info):
@@ -185,12 +188,27 @@ def analyze_jar_file(path_to_find_bin, path_to_exclude):
185188
success = True
186189
json_file = ""
187190

188-
command = ['dependency-check', '--scan', f'{path_to_find_bin}', '--out', f'{path_to_find_bin}',
191+
# Use fixed install path: ./fosslight_dc_bin/dependency-check/bin/dependency-check.sh or .bat
192+
if sys.platform.startswith('win'):
193+
depcheck_path = os.path.abspath(os.path.join(os.getcwd(), 'fosslight_dc_bin', 'dependency-check', 'bin', 'dependency-check.bat'))
194+
elif sys.platform.startswith('linux'):
195+
depcheck_path = os.path.abspath(os.path.join(os.getcwd(), 'fosslight_dc_bin', 'dependency-check', 'bin', 'dependency-check.sh'))
196+
elif sys.platform.startswith('darwin'):
197+
depcheck_path = os.path.abspath(os.path.join(os.getcwd(), 'dependency-check'))
198+
199+
if not (os.path.isfile(depcheck_path) and os.access(depcheck_path, os.X_OK)):
200+
logger.error(f'dependency-check script not found or not executable at {depcheck_path}')
201+
success = False
202+
return owasp_items, vulnerability_items, success
203+
204+
command = [depcheck_path, '--scan', f'{path_to_find_bin}', '--out', f'{path_to_find_bin}',
189205
'--disableArchive', '--disableAssembly', '--disableRetireJS', '--disableNodeJS',
190206
'--disableNodeAudit', '--disableNugetconf', '--disableNuspec', '--disableOpenSSL',
191-
'--disableOssIndex', '--disableBundleAudit', '--cveValidForHours', '24', '-f', 'JSON']
207+
'--disableOssIndex', '--disableBundleAudit', '--disableOssIndex', '--nvdValidForHours', '168',
208+
'--nvdDatafeed', 'https://nvd.nist.gov/feeds/json/cve/2.0/nvdcve-2.0-{0}.json.gz', '-f', 'JSON']
209+
192210
try:
193-
run_analysis(command, dependency_check_run)
211+
run_analysis(command)
194212
except Exception as ex:
195213
logger.info(f"Error to analyze .jar file - OSS information for .jar file isn't included in report.\n {ex}")
196214
success = False
@@ -239,7 +257,22 @@ def analyze_jar_file(path_to_find_bin, path_to_exclude):
239257
if not bin_with_path.endswith('.jar'):
240258
bin_with_path = bin_with_path.split('.jar')[0] + '.jar'
241259

242-
file_with_path = os.path.relpath(bin_with_path, path_to_find_bin)
260+
try:
261+
path_to_fild_bin_abs = os.path.abspath(path_to_find_bin)
262+
bin_with_path_abs = os.path.abspath(bin_with_path)
263+
if os.name == 'nt': # Windows
264+
drive_bin = os.path.splitdrive(bin_with_path_abs)[0].lower()
265+
drive_root = os.path.splitdrive(path_to_fild_bin_abs)[0].lower()
266+
# Different drive or UNC root -> fallback to basename
267+
if drive_bin and drive_root and drive_bin != drive_root:
268+
file_with_path = os.path.basename(bin_with_path_abs)
269+
else:
270+
file_with_path = os.path.relpath(bin_with_path_abs, path_to_fild_bin_abs)
271+
else:
272+
file_with_path = os.path.relpath(bin_with_path_abs, path_to_fild_bin_abs)
273+
except Exception as e:
274+
file_with_path = os.path.basename(bin_with_path)
275+
logger.error(f"relpath error: {e}; fallback basename: {file_with_path}")
243276

244277
# First, Get OSS Name and Version info from pkg_info
245278
for pkg_info in all_pkg_info:

0 commit comments

Comments
 (0)