Skip to content

Commit fd7821b

Browse files
committed
Add dependency-check script file
1 parent 8d54992 commit fd7821b

File tree

96 files changed

+994
-148
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+994
-148
lines changed

.github/workflows/publish-release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,21 @@ jobs:
6161
- os: ubuntu-latest
6262
TARGET: ubuntu
6363
CMD_BUILD: >
64-
pyinstaller --onefile cli.py -n cli --additional-hooks-dir=hooks --hidden-import=pkg_resources.extern --add-binary "LICENSE:LICENSES" --add-binary "LICENSES/LicenseRef-3rd_party_licenses.txt:LICENSES" &&
64+
pyinstaller cli.spec && \
6565
mv dist/cli fosslight_bin_ubuntu
6666
OUT_FILE_NAME: fosslight_bin_ubuntu
6767
ASSET_MIME: application/octet-stream
6868
- os: macos-latest
6969
TARGET: macos
7070
CMD_BUILD: >
71-
pyinstaller --onefile cli.py -n cli --additional-hooks-dir=hooks --hidden-import=pkg_resources.extern --add-binary "LICENSE:LICENSES" --add-binary "LICENSES/LicenseRef-3rd_party_licenses.txt:LICENSES" &&
71+
pyinstaller cli.spec && \
7272
mv dist/cli fosslight_bin_macos
7373
OUT_FILE_NAME: fosslight_bin_macos
7474
ASSET_MIME: aapplication/x-mach-binary
7575
- os: windows-latest
7676
TARGET: windows
7777
CMD_BUILD: >
78-
pyinstaller --onefile cli.py -n cli --additional-hooks-dir=hooks --hidden-import=pkg_resources.extern --add-binary "LICENSE;LICENSES" --add-binary "LICENSES\LicenseRef-3rd_party_licenses.txt;LICENSES" &&
78+
pyinstaller cli.spec && \
7979
move dist/cli.exe fosslight_bin_windows.exe
8080
OUT_FILE_NAME: fosslight_bin_windows.exe
8181
ASSET_MIME: application/vnd.microsoft.portable-executable

.github/workflows/pull-request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
python -m pip install --upgrade pip
5050
pip install .
5151
pip install pyinstaller
52-
pyinstaller --onefile cli.py -n cli --additional-hooks-dir=hooks --hidden-import=pkg_resources.extern &&
52+
pyinstaller cli.spec
5353
move dist\cli.exe tests\fosslight_bin_windows.exe
5454
.\tests\fosslight_bin_windows.exe
5555

.reuse/dep5

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,7 @@ License: Apache-2.0
5757
Files: .bumpversion.cfg
5858
Copyright: 2021 LG Electronics
5959
License: Apache-2.0
60+
61+
Files: third_party_licenses/*
62+
Copyright: Copyright (c) 2013 Jeremy Long
63+
License: Apache-2.0

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
include LICENSE
22
include README.md
33
include requirements.txt
4+
recursive-include third_party/dependency-check *

cli.spec

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# -*- mode: python ; coding: utf-8 -*-
2+
3+
4+
a = Analysis(
5+
['cli.py'],
6+
pathex=['src'], # source package path for fosslight_binary
7+
binaries=[],
8+
datas=[
9+
# Use original repository location (no pre-install): third_party/dependency-check
10+
('third_party/dependency-check/bin', 'third_party/dependency-check/bin'),
11+
('third_party/dependency-check/lib', 'third_party/dependency-check/lib'),
12+
('third_party/dependency-check/licenses', 'third_party/dependency-check/licenses'),
13+
('third_party/dependency-check', 'third_party/dependency-check'), # txt/md root files
14+
('LICENSES', 'LICENSES'),
15+
('LICENSE', 'LICENSES'),
16+
],
17+
hiddenimports=[
18+
'pkg_resources.extern',
19+
'fosslight_binary.cli',
20+
'fosslight_binary._jar_analysis',
21+
'fosslight_binary._binary',
22+
],
23+
hookspath=['hooks'],
24+
hooksconfig={},
25+
runtime_hooks=[],
26+
excludes=[],
27+
noarchive=False,
28+
optimize=0,
29+
)
30+
pyz = PYZ(a.pure)
31+
32+
exe = EXE(
33+
pyz,
34+
a.scripts,
35+
a.binaries,
36+
a.datas,
37+
[],
38+
name='cli',
39+
debug=False,
40+
bootloader_ignore_signals=False,
41+
strip=False,
42+
upx=True,
43+
upx_exclude=[],
44+
runtime_tmpdir=None,
45+
console=True,
46+
disable_windowed_traceback=False,
47+
argv_emulation=False,
48+
target_arch=None,
49+
codesign_identity=None,
50+
entitlements_file=None,
51+
)

setup.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,27 @@
1414
install_requires = f.read().splitlines()
1515

1616
_PACKAEG_NAME = 'fosslight_binary'
17+
_TEMP_DC_DIR = os.path.join('src', _PACKAEG_NAME, 'third_party', 'dependency-check')
1718
_LICENSE_FILE = 'LICENSE'
1819
_LICENSE_DIR = 'LICENSES'
1920

2021
if __name__ == "__main__":
2122
dest_path = os.path.join('src', _PACKAEG_NAME, _LICENSE_DIR)
23+
# Temporarily copy dependency-check bundle inside package for wheel build
24+
try:
25+
if os.path.isdir('third_party/dependency-check'):
26+
if os.path.exists(_TEMP_DC_DIR):
27+
shutil.rmtree(_TEMP_DC_DIR)
28+
os.makedirs(_TEMP_DC_DIR, exist_ok=True)
29+
# Copy tree (shallow manual to preserve permissions)
30+
for root, dirs, files in os.walk('third_party/dependency-check'):
31+
rel_root = os.path.relpath(root, 'third_party/dependency-check')
32+
target_root = os.path.join(_TEMP_DC_DIR, rel_root) if rel_root != '.' else _TEMP_DC_DIR
33+
os.makedirs(target_root, exist_ok=True)
34+
for f in files:
35+
shutil.copy2(os.path.join(root, f), os.path.join(target_root, f))
36+
except Exception as e:
37+
print(f'Warning: Fail to stage dependency-check bundle: {e}')
2238
try:
2339
if not os.path.exists(dest_path):
2440
os.mkdir(dest_path)
@@ -61,7 +77,16 @@
6177
'python-magic'
6278
],
6379
},
64-
package_data={_PACKAEG_NAME: [os.path.join(_LICENSE_DIR, '*')]},
80+
package_data={
81+
_PACKAEG_NAME: [
82+
os.path.join(_LICENSE_DIR, '*'),
83+
'third_party/dependency-check/bin/*',
84+
'third_party/dependency-check/lib/*',
85+
'third_party/dependency-check/licenses/*',
86+
'third_party/dependency-check/*.txt',
87+
'third_party/dependency-check/*.md',
88+
]
89+
},
6590
include_package_data=True,
6691
entry_points={
6792
"console_scripts": [
@@ -72,3 +97,4 @@
7297
}
7398
)
7499
shutil.rmtree(dest_path, ignore_errors=True)
100+
shutil.rmtree(_TEMP_DC_DIR, ignore_errors=True)

src/fosslight_binary/__init__.py

Lines changed: 63 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -4,146 +4,76 @@
44
# SPDX-License-Identifier: Apache-2.0
55
import logging
66
import os
7-
import stat
87
import subprocess
9-
import tempfile
10-
import urllib.request
11-
import zipfile
128
import sys
139

1410
logger = logging.getLogger(__name__)
15-
DEPENDENCY_CHECK_VERSION = "12.1.7"
1611

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'):
12+
# Static path always used; environment overrides are ignored now.
13+
_PKG_DIR = os.path.dirname(__file__)
14+
_DC_HOME = os.path.join(_PKG_DIR, 'third_party', 'dependency-check')
15+
16+
# Fallback: project root layout (editable install) or current working directory
17+
if not os.path.isdir(_DC_HOME):
18+
_PROJECT_ROOT = os.path.abspath(os.path.join(_PKG_DIR, '..', '..'))
19+
candidate = os.path.join(_PROJECT_ROOT, 'third_party', 'dependency-check')
20+
if os.path.isdir(candidate):
21+
_DC_HOME = candidate
22+
else:
23+
cwd_candidate = os.path.join(os.getcwd(), 'third_party', 'dependency-check')
24+
if os.path.isdir(cwd_candidate):
25+
_DC_HOME = cwd_candidate
26+
if not os.path.isdir(_DC_HOME) and getattr(sys, 'frozen', False):
27+
# Frozen executable scenario (PyInstaller onefile): check exe dir and _MEIPASS temp dir.
28+
exe_dir = os.path.dirname(os.path.abspath(sys.executable))
29+
exe_candidate = os.path.join(exe_dir, 'third_party', 'dependency-check')
30+
if os.path.isdir(exe_candidate):
31+
_DC_HOME = exe_candidate
32+
else:
33+
tmp_root = getattr(sys, '_MEIPASS', '')
34+
if tmp_root:
35+
tmp_candidate = os.path.join(tmp_root, 'third_party', 'dependency-check')
36+
if os.path.isdir(tmp_candidate):
37+
_DC_HOME = tmp_candidate
38+
39+
40+
def get_dependency_check_script():
41+
"""Return path to static dependency-check CLI script or None if missing."""
42+
bin_dir = os.path.join(_DC_HOME, 'bin')
43+
if sys.platform.startswith('win'):
44+
script = os.path.join(bin_dir, 'dependency-check.bat')
45+
else:
46+
script = os.path.join(bin_dir, 'dependency-check.sh')
47+
return script if os.path.isfile(script) else None
48+
49+
50+
def _set_version_env(script_path):
51+
"""Attempt to run '--version' to populate DEPENDENCY_CHECK_VERSION; ignore errors."""
52+
if not script_path or not os.path.exists(script_path):
13653
return
137-
_auto_install_dependencies._already_run = True
138-
13954
try:
140-
# Install binary version
141-
_install_dependency_check()
55+
result = subprocess.run([script_path, '--version'], capture_output=True, text=True, timeout=8)
56+
if result.returncode == 0:
57+
version_line = (result.stdout or '').strip().splitlines()[-1]
58+
if version_line:
59+
os.environ['DEPENDENCY_CHECK_VERSION'] = version_line
60+
except Exception as ex:
61+
logger.debug(f"Could not obtain dependency-check version: {ex}")
62+
63+
64+
def _init_static_dependency_check():
65+
if not os.path.isdir(_DC_HOME):
66+
logger.info("Dependency-check not found under third_party/dependency-check.")
67+
return
68+
os.environ['DEPENDENCY_CHECK_HOME'] = _DC_HOME
69+
script = get_dependency_check_script()
70+
_set_version_env(script)
71+
logger.debug(f"dependency-check home set to: {_DC_HOME}")
14272

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}")
14673

74+
# Perform lightweight initialization (no network, no extraction)
75+
_init_static_dependency_check()
14776

148-
# Auto-install on import
149-
_auto_install_dependencies()
77+
__all__ = [
78+
'get_dependency_check_script'
79+
]

src/fosslight_binary/_jar_analysis.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import logging
77
import json
88
import os
9-
import sys
109
import subprocess
10+
from fosslight_binary import get_dependency_check_script
1111
import fosslight_util.constant as constant
1212
from fosslight_binary._binary import BinaryItem, VulnerabilityItem, is_package_dir
1313
from fosslight_util.oss_item import OssItem
@@ -188,16 +188,13 @@ def analyze_jar_file(path_to_find_bin, path_to_exclude):
188188
success = True
189189
json_file = ""
190190

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-
191+
depcheck_path = get_dependency_check_script()
192+
if not depcheck_path:
193+
logger.info('dependency-check script not available. JAR OSS info will be skipped.')
194+
success = False
195+
return owasp_items, vulnerability_items, success
199196
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}')
197+
logger.info(f'dependency-check script found but not executable: {depcheck_path}. Skipping.')
201198
success = False
202199
return owasp_items, vulnerability_items, success
203200

0 commit comments

Comments
 (0)