ossa-scanner 0.1.0__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.
File without changes
@@ -0,0 +1,120 @@
1
+ import os
2
+ import json
3
+ from concurrent.futures import ThreadPoolExecutor, as_completed
4
+ from ossa_scanner.utils.os_detection import detect_os
5
+ from ossa_scanner.utils.package_manager import list_packages, get_package_info, download_source
6
+ from ossa_scanner.utils.hash_calculator import calculate_file_hash
7
+ from ossa_scanner.utils.swhid_calculator import calculate_swhid
8
+ from ossa_scanner.uploader import GitHubUploader
9
+
10
+ class Scanner:
11
+ 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
+ self.output_dir = output_dir
20
+ self.os_type = detect_os()
21
+ self.threads = threads
22
+
23
+ def process_package(self, package):
24
+ """
25
+ Processes a single package: downloads source, extracts, calculates hash and SWHID.
26
+
27
+ Args:
28
+ package (str): Package name to process.
29
+
30
+ Returns:
31
+ dict: Result of the processed package including hash and SWHID.
32
+ """
33
+ try:
34
+ print(f"Processing package: {package}")
35
+ package_info = get_package_info(self.os_type, package)
36
+ print(f"Fetched metadata for {package}")
37
+
38
+ # Download the source code
39
+ source_file = download_source(self.os_type, package, self.output_dir)
40
+ print(f"Downloaded source file: {source_file}")
41
+
42
+ # Calculate hash of the source file
43
+ file_hash = calculate_file_hash(source_file)
44
+ print(f"Hash (SHA256) for {package}: {file_hash}")
45
+
46
+ # Extract source code directory
47
+ source_dir = os.path.join(self.output_dir, package)
48
+ os.makedirs(source_dir, exist_ok=True)
49
+
50
+ # Calculate SWHID
51
+ swhid = calculate_swhid(source_dir)
52
+ print(f"SWHID for {package}: {swhid}")
53
+
54
+ return {
55
+ "package": package,
56
+ "info": package_info,
57
+ "hash": file_hash,
58
+ "swhid": swhid,
59
+ }
60
+
61
+ except Exception as e:
62
+ print(f"Error processing package {package}: {e}")
63
+ return {
64
+ "package": package,
65
+ "error": str(e)
66
+ }
67
+
68
+ def scan_packages(self):
69
+ """
70
+ Scans all packages in the repository and processes them in parallel.
71
+
72
+ Returns:
73
+ list: List of results for each package.
74
+ """
75
+ print(f"Detected OS: {self.os_type}")
76
+ print("Listing available packages...")
77
+ packages = list_packages(self.os_type)
78
+
79
+ results = []
80
+ with ThreadPoolExecutor(max_workers=self.threads) as executor:
81
+ # Submit tasks for parallel processing
82
+ future_to_package = {
83
+ executor.submit(self.process_package, package): package
84
+ for package in packages
85
+ }
86
+
87
+ for future in as_completed(future_to_package):
88
+ package = future_to_package[future]
89
+ try:
90
+ result = future.result()
91
+ results.append(result)
92
+ except Exception as e:
93
+ print(f"Exception occurred for package {package}: {e}")
94
+ return results
95
+
96
+ def save_results(self, results, output_file):
97
+ """
98
+ Save the scan results to a JSON file.
99
+
100
+ Args:
101
+ results (list): List of results for each package.
102
+ output_file (str): Path to save the JSON file.
103
+ """
104
+ with open(output_file, "w") as f:
105
+ json.dump(results, f, indent=4)
106
+ print(f"Results saved to {output_file}")
107
+
108
+ def upload_results(self, results_file, github_uploader, repo_dir):
109
+ """
110
+ Uploads the results file to GitHub.
111
+
112
+ Args:
113
+ results_file (str): Local results file path to upload.
114
+ github_uploader (GitHubUploader): Instance of the GitHubUploader class.
115
+ repo_dir (str): Path in the GitHub repository where the results will be uploaded.
116
+ """
117
+ print(f"Uploading results to GitHub: {repo_dir}")
118
+ repo_path = os.path.join(repo_dir, os.path.basename(results_file))
119
+ github_uploader.upload_file(results_file, repo_path, "Add scanning results")
120
+
@@ -0,0 +1,68 @@
1
+ import http.client
2
+ import json
3
+ import base64
4
+ import os
5
+
6
+
7
+ class GitHubUploader:
8
+ def __init__(self, token, repo_owner, repo_name):
9
+ self.token = token
10
+ self.repo_owner = repo_owner
11
+ self.repo_name = repo_name
12
+ self.base_url = "api.github.com"
13
+
14
+ def upload_file(self, file_path, repo_path, commit_message="Add scanner results"):
15
+ """
16
+ Uploads a file to a GitHub repository.
17
+
18
+ Args:
19
+ file_path (str): Local file path to upload.
20
+ repo_path (str): Path in the GitHub repository.
21
+ commit_message (str): Commit message for the upload.
22
+ """
23
+ # Read the file and encode it in base64
24
+ with open(file_path, "rb") as f:
25
+ content = f.read()
26
+ encoded_content = base64.b64encode(content).decode("utf-8")
27
+
28
+ # Create the payload
29
+ payload = {
30
+ "message": commit_message,
31
+ "content": encoded_content,
32
+ }
33
+
34
+ # GitHub API endpoint
35
+ endpoint = f"/repos/{self.repo_owner}/{self.repo_name}/contents/{repo_path}"
36
+
37
+ # Make the API request
38
+ conn = http.client.HTTPSConnection(self.base_url)
39
+ headers = {
40
+ "Authorization": f"Bearer {self.token}",
41
+ "User-Agent": "ossa-scanner",
42
+ "Content-Type": "application/json",
43
+ }
44
+
45
+ conn.request("PUT", endpoint, body=json.dumps(payload), headers=headers)
46
+ response = conn.getresponse()
47
+ data = response.read().decode("utf-8")
48
+ conn.close()
49
+
50
+ if response.status == 201:
51
+ print(f"File '{file_path}' successfully uploaded to {repo_path} in {self.repo_name}")
52
+ else:
53
+ print(f"Failed to upload file '{file_path}'. Response: {data}")
54
+ raise Exception(f"GitHub API Error: {response.status}")
55
+
56
+ def upload_results(self, results_dir, repo_dir):
57
+ """
58
+ Uploads all files in a directory to a specified path in the GitHub repo.
59
+
60
+ Args:
61
+ results_dir (str): Local directory containing results to upload.
62
+ repo_dir (str): Target directory in the GitHub repository.
63
+ """
64
+ for root, _, files in os.walk(results_dir):
65
+ for file_name in files:
66
+ local_path = os.path.join(root, file_name)
67
+ repo_path = os.path.join(repo_dir, file_name).replace("\\", "/")
68
+ self.upload_file(local_path, repo_path)
File without changes
@@ -0,0 +1,11 @@
1
+ import subprocess
2
+
3
+ def download_source(package_manager, package_name, output_dir):
4
+ if package_manager == 'apt':
5
+ cmd = ['apt-get', 'source', package_name, '-d', output_dir]
6
+ elif package_manager in ['yum', 'dnf']:
7
+ cmd = ['dnf', 'download', '--source', package_name, '--downloaddir', output_dir]
8
+ else:
9
+ raise ValueError("Unsupported package manager")
10
+
11
+ subprocess.run(cmd)
@@ -0,0 +1,8 @@
1
+ import hashlib
2
+
3
+ def calculate_file_hash(file_path, algorithm='sha256'):
4
+ hash_func = hashlib.new(algorithm)
5
+ with open(file_path, 'rb') as f:
6
+ while chunk := f.read(8192):
7
+ hash_func.update(chunk)
8
+ return hash_func.hexdigest()
@@ -0,0 +1,10 @@
1
+ import platform
2
+
3
+ def detect_os():
4
+ dist, _, _ = platform.linux_distribution(full_distribution_name=False)
5
+ if 'Ubuntu' in dist or 'Debian' in dist:
6
+ return 'apt'
7
+ elif 'Red Hat' in dist or 'CentOS' in dist or 'AlmaLinux' in dist:
8
+ return 'yum'
9
+ else:
10
+ raise ValueError("Unsupported OS")
@@ -0,0 +1,31 @@
1
+ import subprocess
2
+
3
+ def list_packages(package_manager):
4
+ if package_manager == 'apt':
5
+ result = subprocess.run(
6
+ ['apt-cache', 'search', '.'],
7
+ capture_output=True,
8
+ text=True
9
+ )
10
+ elif package_manager in ['yum', 'dnf']:
11
+ result = subprocess.run(
12
+ ['repoquery', '--all'],
13
+ capture_output=True,
14
+ text=True
15
+ )
16
+ else:
17
+ raise ValueError("Unsupported package manager")
18
+
19
+ packages = result.stdout.splitlines()
20
+ return [pkg.split()[0] for pkg in packages]
21
+
22
+ def get_package_info(package_manager, package_name):
23
+ if package_manager == 'apt':
24
+ cmd = ['apt-cache', 'show', package_name]
25
+ elif package_manager in ['yum', 'dnf']:
26
+ cmd = ['repoquery', '--info', package_name]
27
+ else:
28
+ raise ValueError("Unsupported package manager")
29
+
30
+ result = subprocess.run(cmd, capture_output=True, text=True)
31
+ return result.stdout
@@ -0,0 +1,4 @@
1
+ from swh.model.hashutil import hash_directory
2
+
3
+ def calculate_swhid(directory_path):
4
+ return hash_directory(directory_path)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Oscar V
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
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)
@@ -0,0 +1,14 @@
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,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.6.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ ossa_scanner