Skip to content

Commit 4f31742

Browse files
committed
Fix .jar analysis via Syft & Grype
1 parent 10b0aec commit 4f31742

File tree

6 files changed

+703
-226
lines changed

6 files changed

+703
-226
lines changed

install_tools.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) 2021 LG Electronics Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
"""
6+
Standalone tool installer for syft and grype.
7+
This script can be run independently without any package dependencies.
8+
"""
9+
10+
import platform
11+
import tarfile
12+
import zipfile
13+
from pathlib import Path
14+
from urllib.request import urlopen, urlretrieve
15+
16+
17+
def get_platform_info():
18+
"""Get platform and architecture information."""
19+
system = platform.system().lower()
20+
machine = platform.machine().lower()
21+
22+
# Normalize architecture names
23+
if machine in ['x86_64', 'amd64']:
24+
arch = 'amd64'
25+
elif machine in ['aarch64', 'arm64']:
26+
arch = 'arm64'
27+
elif machine in ['i386', 'i686']:
28+
arch = '386'
29+
else:
30+
arch = machine
31+
32+
return system, arch
33+
34+
35+
def get_latest_release_url(tool_name, system, arch):
36+
"""Get the latest release download URL for a tool."""
37+
import json
38+
39+
# GitHub API to get latest release
40+
api_url = f"https://api.github.com/repos/anchore/{tool_name}/releases/latest"
41+
42+
try:
43+
with urlopen(api_url) as response:
44+
release_data = json.loads(response.read().decode())
45+
46+
# Find the right asset for our platform
47+
for asset in release_data['assets']:
48+
name = asset['name'].lower()
49+
if system in name and arch in name:
50+
if system == 'windows' and name.endswith('.zip'):
51+
return asset['browser_download_url']
52+
elif system != 'windows' and name.endswith('.tar.gz'):
53+
return asset['browser_download_url']
54+
55+
except Exception as e:
56+
print(f"Failed to get release info for {tool_name}: {e}")
57+
58+
# Fallback to direct URLs
59+
base_urls = {
60+
'syft': f'https://github.com/anchore/syft/releases/latest/download/syft_{system}_{arch}',
61+
'grype': f'https://github.com/anchore/grype/releases/latest/download/grype_{system}_{arch}'
62+
}
63+
64+
if system == 'windows':
65+
return f"{base_urls[tool_name]}.zip"
66+
else:
67+
return f"{base_urls[tool_name]}.tar.gz"
68+
69+
70+
def install_tool(tool_name, install_dir):
71+
"""Install a tool (syft or grype) to the specified directory."""
72+
system, arch = get_platform_info()
73+
74+
print(f"Installing {tool_name} for {system}/{arch}...")
75+
76+
# Create install directory
77+
install_path = Path(install_dir)
78+
install_path.mkdir(parents=True, exist_ok=True)
79+
80+
# Get download URL
81+
download_url = get_latest_release_url(tool_name, system, arch)
82+
83+
# Download file
84+
if system == 'windows':
85+
archive_name = f"{tool_name}.zip"
86+
else:
87+
archive_name = f"{tool_name}.tar.gz"
88+
89+
archive_path = install_path / archive_name
90+
91+
try:
92+
print(f"Downloading {download_url}...")
93+
urlretrieve(download_url, archive_path)
94+
95+
# Extract archive
96+
if system == 'windows':
97+
with zipfile.ZipFile(archive_path, 'r') as zip_ref:
98+
zip_ref.extractall(install_path)
99+
else:
100+
with tarfile.open(archive_path, 'r:gz') as tar_ref:
101+
tar_ref.extractall(install_path)
102+
103+
# Make executable (Unix systems)
104+
if system != 'windows':
105+
tool_binary = install_path / tool_name
106+
if tool_binary.exists():
107+
tool_binary.chmod(0o755)
108+
109+
# Clean up archive
110+
archive_path.unlink()
111+
112+
print(f"✅ {tool_name} installed successfully!")
113+
return True
114+
115+
except Exception as e:
116+
print(f"❌ Failed to install {tool_name}: {e}")
117+
return False
118+
119+
120+
def install_syft_grype():
121+
"""Install both syft and grype tools."""
122+
# Determine install directory
123+
home_dir = Path.home()
124+
install_dir = home_dir / '.local' / 'bin'
125+
126+
print("Installing Syft and Grype tools...")
127+
print(f"Install directory: {install_dir}")
128+
129+
# Install both tools
130+
syft_ok = install_tool('syft', install_dir)
131+
grype_ok = install_tool('grype', install_dir)
132+
133+
if syft_ok and grype_ok:
134+
print("🎉 Both tools installed successfully!")
135+
print(f"Make sure {install_dir} is in your PATH")
136+
return True
137+
else:
138+
print("⚠️ Some tools failed to install")
139+
return False
140+
141+
142+
if __name__ == '__main__':
143+
install_syft_grype()

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

setup.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,40 @@
55
from codecs import open
66
import os
77
import shutil
8+
import subprocess
9+
import sys
810
from setuptools import setup, find_packages
11+
from setuptools.command.install import install
12+
13+
14+
class PostInstallCommand(install):
15+
"""Post-installation for installation mode."""
16+
def run(self):
17+
install.run(self)
18+
19+
# Skip auto-install if explicitly disabled
20+
if os.environ.get('FOSSLIGHT_SKIP_AUTO_INSTALL', '').lower() in ('1', 'true', 'yes'):
21+
print("Auto-install disabled by environment variable")
22+
return
23+
24+
# Install syft and grype using standalone installer
25+
try:
26+
print("Installing syft and grype...")
27+
# Use standalone installer script - no package dependencies!
28+
script_path = os.path.join(os.path.dirname(__file__), 'install_tools.py')
29+
if os.path.exists(script_path):
30+
result = subprocess.run([sys.executable, script_path],
31+
capture_output=True, text=True)
32+
if result.returncode == 0:
33+
print("Syft and grype installation completed.")
34+
else:
35+
print(f"Warning: Tool installation failed: {result.stderr}")
36+
else:
37+
print("Warning: install_tools.py not found, skipping auto-install")
38+
except Exception as e:
39+
print(f"Warning: Failed to auto-install syft/grype: {e}")
40+
print("You can install them manually or they will be installed on first use.")
41+
942

1043
with open('README.md', 'r', 'utf-8') as f:
1144
readme = f.read()
@@ -63,11 +96,19 @@
6396
},
6497
package_data={_PACKAEG_NAME: [os.path.join(_LICENSE_DIR, '*')]},
6598
include_package_data=True,
99+
# Include install_tools.py in the package
100+
data_files=[
101+
('', ['install_tools.py']),
102+
],
103+
cmdclass={
104+
'install': PostInstallCommand,
105+
},
66106
entry_points={
67107
"console_scripts": [
68108
"binary_analysis = fosslight_binary.cli:main",
69109
"fosslight_bin = fosslight_binary.cli:main",
70110
"fosslight_binary = fosslight_binary.cli:main",
111+
"fosslight_install_tools = fosslight_binary.install_cli:main",
71112
]
72113
}
73114
)

0 commit comments

Comments
 (0)