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.
- ossa_scanner/__init__.py +0 -0
- ossa_scanner/scanner.py +120 -0
- ossa_scanner/uploader.py +68 -0
- ossa_scanner/utils/__init__.py +0 -0
- ossa_scanner/utils/downloader.py +11 -0
- ossa_scanner/utils/hash_calculator.py +8 -0
- ossa_scanner/utils/os_detection.py +10 -0
- ossa_scanner/utils/package_manager.py +31 -0
- ossa_scanner/utils/swhid_calculator.py +4 -0
- ossa_scanner-0.1.0.dist-info/LICENSE +21 -0
- ossa_scanner-0.1.0.dist-info/METADATA +41 -0
- ossa_scanner-0.1.0.dist-info/RECORD +14 -0
- ossa_scanner-0.1.0.dist-info/WHEEL +5 -0
- ossa_scanner-0.1.0.dist-info/top_level.txt +1 -0
ossa_scanner/__init__.py
ADDED
File without changes
|
ossa_scanner/scanner.py
ADDED
@@ -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
|
+
|
ossa_scanner/uploader.py
ADDED
@@ -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,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,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 @@
|
|
1
|
+
ossa_scanner
|