ossa-scanner 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- ossa_scanner/__init__.py +1 -0
- ossa_scanner/cli.py +35 -0
- ossa_scanner/scanner.py +6 -13
- ossa_scanner/utils/downloader.py +44 -8
- ossa_scanner/utils/os_detection.py +7 -4
- ossa_scanner/utils/package_manager.py +102 -5
- ossa_scanner/utils/swhid_calculator.py +1 -2
- ossa_scanner-0.1.2.dist-info/METADATA +28 -0
- ossa_scanner-0.1.2.dist-info/RECORD +16 -0
- ossa_scanner-0.1.2.dist-info/entry_points.txt +2 -0
- ossa_scanner-0.1.0.dist-info/METADATA +0 -41
- ossa_scanner-0.1.0.dist-info/RECORD +0 -14
- {ossa_scanner-0.1.0.dist-info → ossa_scanner-0.1.2.dist-info}/LICENSE +0 -0
- {ossa_scanner-0.1.0.dist-info → ossa_scanner-0.1.2.dist-info}/WHEEL +0 -0
- {ossa_scanner-0.1.0.dist-info → ossa_scanner-0.1.2.dist-info}/top_level.txt +0 -0
ossa_scanner/__init__.py
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.1.2"
|
ossa_scanner/cli.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
import argparse
|
2
|
+
from .scanner import Scanner
|
3
|
+
from .uploader import GitHubUploader
|
4
|
+
|
5
|
+
def main():
|
6
|
+
parser = argparse.ArgumentParser(description="OSSA Scanner CLI Tool")
|
7
|
+
parser.add_argument('--output-dir', type=str, required=True, help="Directory to save downloaded source")
|
8
|
+
parser.add_argument('--results-file', type=str, required=True, help="Path to save the JSON results")
|
9
|
+
parser.add_argument('--threads', type=int, default=4, help="Number of threads for parallel processing")
|
10
|
+
parser.add_argument('--upload', action='store_true', help="Upload results to GitHub")
|
11
|
+
parser.add_argument('--repo-owner', type=str, help="GitHub repository owner")
|
12
|
+
parser.add_argument('--repo-name', type=str, help="GitHub repository name")
|
13
|
+
parser.add_argument('--token', type=str, help="GitHub token")
|
14
|
+
parser.add_argument('--repo-dir', type=str, help="Target directory in GitHub repo for results")
|
15
|
+
args = parser.parse_args()
|
16
|
+
|
17
|
+
# Initialize the scanner
|
18
|
+
scanner = Scanner(output_dir=args.output_dir, threads=args.threads)
|
19
|
+
|
20
|
+
# Perform scanning
|
21
|
+
results = scanner.scan_packages()
|
22
|
+
|
23
|
+
# Save results locally
|
24
|
+
scanner.save_results(results, args.results_file)
|
25
|
+
|
26
|
+
# Upload results to GitHub if specified
|
27
|
+
if args.upload:
|
28
|
+
if not (args.repo_owner and args.repo_name and args.token and args.repo_dir):
|
29
|
+
raise ValueError("GitHub upload requires --repo-owner, --repo-name, --token, and --repo-dir")
|
30
|
+
|
31
|
+
uploader = GitHubUploader(args.token, args.repo_owner, args.repo_name)
|
32
|
+
scanner.upload_results(args.results_file, uploader, args.repo_dir)
|
33
|
+
|
34
|
+
if __name__ == "__main__":
|
35
|
+
main()
|
ossa_scanner/scanner.py
CHANGED
@@ -1,21 +1,15 @@
|
|
1
1
|
import os
|
2
2
|
import json
|
3
3
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
4
|
-
from
|
5
|
-
from
|
6
|
-
from
|
7
|
-
from
|
8
|
-
from
|
4
|
+
from .utils.os_detection import detect_os
|
5
|
+
from .utils.package_manager import list_packages, get_package_info
|
6
|
+
from .utils.downloader import download_source
|
7
|
+
from .utils.hash_calculator import calculate_file_hash
|
8
|
+
from .utils.swhid_calculator import calculate_swhid
|
9
|
+
from .uploader import GitHubUploader
|
9
10
|
|
10
11
|
class Scanner:
|
11
12
|
def __init__(self, output_dir, threads=4):
|
12
|
-
"""
|
13
|
-
Initialize the scanner with the output directory and thread count.
|
14
|
-
|
15
|
-
Args:
|
16
|
-
output_dir (str): Directory to store downloaded files and extracted sources.
|
17
|
-
threads (int): Number of threads for parallel processing.
|
18
|
-
"""
|
19
13
|
self.output_dir = output_dir
|
20
14
|
self.os_type = detect_os()
|
21
15
|
self.threads = threads
|
@@ -75,7 +69,6 @@ class Scanner:
|
|
75
69
|
print(f"Detected OS: {self.os_type}")
|
76
70
|
print("Listing available packages...")
|
77
71
|
packages = list_packages(self.os_type)
|
78
|
-
|
79
72
|
results = []
|
80
73
|
with ThreadPoolExecutor(max_workers=self.threads) as executor:
|
81
74
|
# Submit tasks for parallel processing
|
ossa_scanner/utils/downloader.py
CHANGED
@@ -1,11 +1,47 @@
|
|
1
1
|
import subprocess
|
2
|
+
import os
|
3
|
+
import shutil
|
4
|
+
import glob
|
2
5
|
|
3
6
|
def download_source(package_manager, package_name, output_dir):
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
try:
|
8
|
+
if package_manager == 'apt':
|
9
|
+
cmd = ['apt-get', 'source', package_name, '-d', output_dir]
|
10
|
+
subprocess.run(cmd, check=True)
|
11
|
+
elif package_manager in ['yum', 'dnf']:
|
12
|
+
cmd = ['dnf', 'download', '--source', package_name, '--downloaddir', output_dir]
|
13
|
+
subprocess.run(cmd, check=True)
|
14
|
+
elif package_manager == 'brew':
|
15
|
+
# Fetch the source tarball
|
16
|
+
cmd = ['brew', 'fetch', '--build-from-source', package_name]
|
17
|
+
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
18
|
+
cache_dir = subprocess.run(
|
19
|
+
['brew', '--cache', package_name],
|
20
|
+
capture_output=True,
|
21
|
+
text=True,
|
22
|
+
check=True
|
23
|
+
).stdout.strip()
|
24
|
+
prefixes_to_remove = ['aarch64-elf-', 'arm-none-eabi-', 'other-prefix-']
|
25
|
+
stripped_package_name = package_name
|
26
|
+
for prefix in prefixes_to_remove:
|
27
|
+
if package_name.startswith(prefix):
|
28
|
+
stripped_package_name = package_name[len(prefix):]
|
29
|
+
break
|
30
|
+
cache_folder = os.path.dirname(cache_dir)
|
31
|
+
tarball_pattern = os.path.join(cache_folder, f"*{stripped_package_name}*")
|
32
|
+
matching_files = glob.glob(tarball_pattern)
|
33
|
+
if not matching_files:
|
34
|
+
raise FileNotFoundError(f"Tarball not found for {package_name} in {cache_folder}")
|
35
|
+
tarball_path = matching_files[0]
|
36
|
+
os.makedirs(output_dir, exist_ok=True)
|
37
|
+
target_path = os.path.join(output_dir, os.path.basename(tarball_path))
|
38
|
+
shutil.move(tarball_path, target_path)
|
39
|
+
return target_path
|
40
|
+
else:
|
41
|
+
raise ValueError("Unsupported package manager")
|
42
|
+
except subprocess.CalledProcessError as e:
|
43
|
+
print(f"Command failed: {e}")
|
44
|
+
return None
|
45
|
+
except Exception as e:
|
46
|
+
print(f"Error: {e}")
|
47
|
+
return None
|
@@ -1,10 +1,13 @@
|
|
1
|
-
import
|
1
|
+
import distro
|
2
2
|
|
3
3
|
def detect_os():
|
4
|
-
dist
|
5
|
-
if '
|
4
|
+
dist = distro.id()
|
5
|
+
if 'ubuntu' in dist or 'debian' in dist:
|
6
6
|
return 'apt'
|
7
|
-
elif '
|
7
|
+
elif 'redhat' in dist or 'centos' in dist or 'almalinux' in dist:
|
8
8
|
return 'yum'
|
9
|
+
elif 'darwin' in dist:
|
10
|
+
return 'brew'
|
9
11
|
else:
|
10
12
|
raise ValueError("Unsupported OS")
|
13
|
+
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import subprocess
|
2
2
|
|
3
|
+
|
3
4
|
def list_packages(package_manager):
|
4
5
|
if package_manager == 'apt':
|
5
6
|
result = subprocess.run(
|
@@ -13,19 +14,115 @@ def list_packages(package_manager):
|
|
13
14
|
capture_output=True,
|
14
15
|
text=True
|
15
16
|
)
|
17
|
+
elif package_manager == 'brew':
|
18
|
+
result = subprocess.run(
|
19
|
+
['brew', 'search', '.'],
|
20
|
+
capture_output=True,
|
21
|
+
text=True
|
22
|
+
)
|
16
23
|
else:
|
17
|
-
raise ValueError("Unsupported package manager")
|
24
|
+
raise ValueError("ER1: Unsupported package manager for search")
|
18
25
|
|
19
26
|
packages = result.stdout.splitlines()
|
20
|
-
|
27
|
+
extracted_packages = []
|
28
|
+
max_packages = 5
|
29
|
+
k_packages = 0
|
30
|
+
for line in packages:
|
31
|
+
if not line.strip() or line.startswith("==>"):
|
32
|
+
continue
|
33
|
+
extracted_packages.append(line.split()[0])
|
34
|
+
if k_packages >= max_packages:
|
35
|
+
break
|
36
|
+
k_packages += 1
|
37
|
+
|
38
|
+
return extracted_packages
|
39
|
+
|
21
40
|
|
22
41
|
def get_package_info(package_manager, package_name):
|
23
42
|
if package_manager == 'apt':
|
24
43
|
cmd = ['apt-cache', 'show', package_name]
|
25
44
|
elif package_manager in ['yum', 'dnf']:
|
26
45
|
cmd = ['repoquery', '--info', package_name]
|
46
|
+
elif package_manager == 'brew':
|
47
|
+
cmd = ['brew', 'info', package_name]
|
27
48
|
else:
|
28
|
-
raise ValueError("Unsupported package manager")
|
49
|
+
raise ValueError("ER: Unsupported package manager for info")
|
50
|
+
|
51
|
+
try:
|
52
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
53
|
+
output = result.stdout
|
54
|
+
|
55
|
+
# Parse the output based on the package manager
|
56
|
+
if package_manager == 'brew':
|
57
|
+
return parse_brew_info(output)
|
58
|
+
elif package_manager in ['yum', 'dnf']:
|
59
|
+
return parse_yum_info(output)
|
60
|
+
elif package_manager == 'apt':
|
61
|
+
return parse_apt_info(output)
|
62
|
+
except subprocess.CalledProcessError as e:
|
63
|
+
print(f"Command failed: {e}")
|
64
|
+
return None
|
65
|
+
|
66
|
+
|
67
|
+
def parse_brew_info(output):
|
68
|
+
"""Parses brew info output to extract license, website, and description."""
|
69
|
+
info = {}
|
70
|
+
lines = output.splitlines()
|
71
|
+
info["license"] = "Unknown"
|
72
|
+
info["website"] = "Unknown"
|
73
|
+
info["description"] = "Unknown"
|
74
|
+
|
75
|
+
for i, line in enumerate(lines):
|
76
|
+
if i == 1: # The description is usually on the second line
|
77
|
+
info["description"] = line.strip()
|
78
|
+
elif line.startswith("https://"): # The website URL
|
79
|
+
info["website"] = line.strip()
|
80
|
+
elif line.startswith("License:"): # The license information
|
81
|
+
info["license"] = line.split(":", 1)[1].strip()
|
82
|
+
|
83
|
+
# Ensure all keys are present even if some fields are missing
|
84
|
+
return info
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
def parse_yum_info(output):
|
89
|
+
"""Parses yum repoquery --info output."""
|
90
|
+
info = {}
|
91
|
+
lines = output.splitlines()
|
92
|
+
|
93
|
+
for line in lines:
|
94
|
+
if line.startswith("License"):
|
95
|
+
info["license"] = line.split(":", 1)[1].strip()
|
96
|
+
elif line.startswith("URL"):
|
97
|
+
info["website"] = line.split(":", 1)[1].strip()
|
98
|
+
elif "Copyright" in line:
|
99
|
+
info["copyright"] = line.strip()
|
100
|
+
|
101
|
+
# Ensure all keys are present even if data is missing
|
102
|
+
return {
|
103
|
+
"license": info.get("license", "Unknown"),
|
104
|
+
"copyright": info.get("copyright", "Unknown"),
|
105
|
+
"website": info.get("website", "Unknown"),
|
106
|
+
}
|
107
|
+
|
108
|
+
|
109
|
+
def parse_apt_info(output):
|
110
|
+
"""Parses apt-cache show output."""
|
111
|
+
info = {}
|
112
|
+
lines = output.splitlines()
|
113
|
+
|
114
|
+
for line in lines:
|
115
|
+
if line.startswith("License:") or "License" in line:
|
116
|
+
info["license"] = line.split(":", 1)[1].strip()
|
117
|
+
elif line.startswith("Homepage:"):
|
118
|
+
info["website"] = line.split(":", 1)[1].strip()
|
119
|
+
elif "Copyright" in line:
|
120
|
+
info["copyright"] = line.strip()
|
121
|
+
|
122
|
+
# Ensure all keys are present even if data is missing
|
123
|
+
return {
|
124
|
+
"license": info.get("license", "Unknown"),
|
125
|
+
"copyright": info.get("copyright", "Unknown"),
|
126
|
+
"website": info.get("website", "Unknown"),
|
127
|
+
}
|
29
128
|
|
30
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
31
|
-
return result.stdout
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: ossa_scanner
|
3
|
+
Version: 0.1.2
|
4
|
+
Summary: A Python library for scanning Linux packages, managing metadata, and generating SWHIDs.
|
5
|
+
Home-page: https://github.com/oscarvalenzuelab/ossa_scanner
|
6
|
+
Author: Oscar Valenzuela
|
7
|
+
Author-email: oscar.valenzuela.b@gmail.com
|
8
|
+
License: MIT
|
9
|
+
Keywords: linux packages SWHID open-source compliance
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
11
|
+
Classifier: Intended Audience :: Developers
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
14
|
+
Classifier: Programming Language :: Python :: 3.6
|
15
|
+
Classifier: Programming Language :: Python :: 3.7
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
19
|
+
Classifier: Operating System :: POSIX :: Linux
|
20
|
+
Requires-Python: >=3.6
|
21
|
+
Description-Content-Type: text/markdown
|
22
|
+
License-File: LICENSE
|
23
|
+
Requires-Dist: click
|
24
|
+
Requires-Dist: swh.model
|
25
|
+
Requires-Dist: distro
|
26
|
+
|
27
|
+
# ossa_scanner
|
28
|
+
Open Source Advisory Scanner (Generator)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
ossa_scanner/__init__.py,sha256=YvuYzWnKtqBb-IqG8HAu-nhIYAsgj9Vmc_b9o7vO-js,22
|
2
|
+
ossa_scanner/cli.py,sha256=hyRUOgp9kcwFtQrIeyth5vTxeK7eOlxfn5R9E7HX5sA,1640
|
3
|
+
ossa_scanner/scanner.py,sha256=SAkiBLjAuO3dklbHPgXs0p047buO6Pp51RROq6G7Yq8,4082
|
4
|
+
ossa_scanner/uploader.py,sha256=X8bo7GqfpBjz2NlnvSwDR_rVqNoZDRPF2pnQMaVENbc,2436
|
5
|
+
ossa_scanner/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
ossa_scanner/utils/downloader.py,sha256=3ccwcde9yJ_SEP0mG9TDr2O0MMdA1p-K6hpzqme-KQ4,2081
|
7
|
+
ossa_scanner/utils/hash_calculator.py,sha256=i47KS_HoZNiSbGyd0iP9_TcDwxWS2SrmkIcNF2MWLcA,254
|
8
|
+
ossa_scanner/utils/os_detection.py,sha256=QdRKQ4li4SOHgBofe1qWf8OOcw8XvhM-XWUNu0Cy0a4,315
|
9
|
+
ossa_scanner/utils/package_manager.py,sha256=tWuQwgkFQjTzeisem0Gz8uFvWw5Cxd-Tft5HM8tIQmk,4028
|
10
|
+
ossa_scanner/utils/swhid_calculator.py,sha256=4Z0H2GmECMAJlvH6JBbUmaLXSLRNntyYEdxsS6CTEMQ,63
|
11
|
+
ossa_scanner-0.1.2.dist-info/LICENSE,sha256=9slQ_XNiEkio28l90NwihP7a90fCL2GQ6YhcVXTBls4,1064
|
12
|
+
ossa_scanner-0.1.2.dist-info/METADATA,sha256=dWWsJKRvqN1vdal81dAseom9Cb1OwLjuOZAllrfOoMs,1043
|
13
|
+
ossa_scanner-0.1.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
14
|
+
ossa_scanner-0.1.2.dist-info/entry_points.txt,sha256=UVoAo-wTPxT82g3cfqTs2CmQnazd57TAwhd9VwEKD1c,55
|
15
|
+
ossa_scanner-0.1.2.dist-info/top_level.txt,sha256=uUp5CvhZfJLapXn9DyUXvgH7QK3uzF2ibH943lWN5Bs,13
|
16
|
+
ossa_scanner-0.1.2.dist-info/RECORD,,
|
@@ -1,41 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: ossa-scanner
|
3
|
-
Version: 0.1.0
|
4
|
-
Summary: A CLI tool to scan Linux packages, manage metadata, and upload results to GitHub.
|
5
|
-
Author: Oscar Valenzuela
|
6
|
-
Author-email: Oscar Valenzuela <oscar.valenzuela.b@gmail.com>
|
7
|
-
License: MIT License
|
8
|
-
|
9
|
-
Copyright (c) 2024 Oscar V
|
10
|
-
|
11
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
12
|
-
of this software and associated documentation files (the "Software"), to deal
|
13
|
-
in the Software without restriction, including without limitation the rights
|
14
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
15
|
-
copies of the Software, and to permit persons to whom the Software is
|
16
|
-
furnished to do so, subject to the following conditions:
|
17
|
-
|
18
|
-
The above copyright notice and this permission notice shall be included in all
|
19
|
-
copies or substantial portions of the Software.
|
20
|
-
|
21
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
22
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
23
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
24
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
25
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
26
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
27
|
-
SOFTWARE.
|
28
|
-
|
29
|
-
Keywords: linux,packages,SWHID,GitHub,open-source
|
30
|
-
Classifier: Development Status :: 3 - Alpha
|
31
|
-
Classifier: Intended Audience :: Developers
|
32
|
-
Classifier: License :: OSI Approved :: MIT License
|
33
|
-
Classifier: Programming Language :: Python :: 3
|
34
|
-
Classifier: Operating System :: POSIX :: Linux
|
35
|
-
Requires-Python: >=3.7
|
36
|
-
Description-Content-Type: text/markdown
|
37
|
-
License-File: LICENSE
|
38
|
-
Requires-Dist: swh.model
|
39
|
-
|
40
|
-
# ossa_scanner
|
41
|
-
Open Source Advisory Scanner (Generator)
|
@@ -1,14 +0,0 @@
|
|
1
|
-
ossa_scanner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
ossa_scanner/scanner.py,sha256=nku8pJR1n7Z2Fjs5F3lbxBlv9xFUWrUb3dfucZZcM30,4387
|
3
|
-
ossa_scanner/uploader.py,sha256=X8bo7GqfpBjz2NlnvSwDR_rVqNoZDRPF2pnQMaVENbc,2436
|
4
|
-
ossa_scanner/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
ossa_scanner/utils/downloader.py,sha256=5fV531x-oiFTyh6B17Afi4W72zFC2ejopvMpn1qNpw4,408
|
6
|
-
ossa_scanner/utils/hash_calculator.py,sha256=i47KS_HoZNiSbGyd0iP9_TcDwxWS2SrmkIcNF2MWLcA,254
|
7
|
-
ossa_scanner/utils/os_detection.py,sha256=h2hlS5QHHVyyGSHI5jDwq7aivGZTEHk1v_Ml9lTTxbQ,320
|
8
|
-
ossa_scanner/utils/package_manager.py,sha256=N_noGTVHxxCTfH9q6ftq2GhkACj1ExOt1AXhNdFFgNQ,953
|
9
|
-
ossa_scanner/utils/swhid_calculator.py,sha256=mvjx4wlTDCrWhH3Z_6TitKtt1WsPMC7QYwmWSMck7Ro,126
|
10
|
-
ossa_scanner-0.1.0.dist-info/LICENSE,sha256=9slQ_XNiEkio28l90NwihP7a90fCL2GQ6YhcVXTBls4,1064
|
11
|
-
ossa_scanner-0.1.0.dist-info/METADATA,sha256=VdiOAhrJ6M75qRG6ntG8UGxYFUR8IQoPNvtFX-bB_Nk,1927
|
12
|
-
ossa_scanner-0.1.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
13
|
-
ossa_scanner-0.1.0.dist-info/top_level.txt,sha256=uUp5CvhZfJLapXn9DyUXvgH7QK3uzF2ibH943lWN5Bs,13
|
14
|
-
ossa_scanner-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|