ossa-scanner 0.1.2__tar.gz → 0.1.3__tar.gz
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-0.1.2 → ossa_scanner-0.1.3}/PKG-INFO +1 -1
- ossa_scanner-0.1.3/ossa_scanner/__init__.py +1 -0
- ossa_scanner-0.1.3/ossa_scanner/cli.py +50 -0
- ossa_scanner-0.1.3/ossa_scanner/scanner.py +123 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner.egg-info/PKG-INFO +1 -1
- ossa_scanner-0.1.2/ossa_scanner/__init__.py +0 -1
- ossa_scanner-0.1.2/ossa_scanner/cli.py +0 -35
- ossa_scanner-0.1.2/ossa_scanner/scanner.py +0 -113
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/LICENSE +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/README.md +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner/uploader.py +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner/utils/__init__.py +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner/utils/downloader.py +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner/utils/hash_calculator.py +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner/utils/os_detection.py +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner/utils/package_manager.py +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner/utils/swhid_calculator.py +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner.egg-info/SOURCES.txt +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner.egg-info/dependency_links.txt +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner.egg-info/entry_points.txt +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner.egg-info/requires.txt +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/ossa_scanner.egg-info/top_level.txt +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/setup.cfg +0 -0
- {ossa_scanner-0.1.2 → ossa_scanner-0.1.3}/setup.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.1.3"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import argparse
|
2
|
+
import os
|
3
|
+
import shutil
|
4
|
+
from .scanner import Scanner
|
5
|
+
from .uploader import GitHubUploader
|
6
|
+
|
7
|
+
def main():
|
8
|
+
parser = argparse.ArgumentParser(description="OSSA Scanner CLI Tool")
|
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 (required for upload)")
|
12
|
+
parser.add_argument('--repo-name', type=str, help="GitHub repository name (required for upload)")
|
13
|
+
parser.add_argument('--token', type=str, help="GitHub token (required for upload)")
|
14
|
+
parser.add_argument('--repo-dir', type=str, help="Target directory in GitHub repo for results (required for upload)")
|
15
|
+
parser.add_argument('--retain-temp', action='store_true', help="Retain the temporary directory for downloaded and extracted packages")
|
16
|
+
args = parser.parse_args()
|
17
|
+
|
18
|
+
# Define directories
|
19
|
+
reports_dir = os.path.join(os.getcwd(), "ossa_reports")
|
20
|
+
temp_dir = "/tmp/ossa_temp"
|
21
|
+
|
22
|
+
os.makedirs(reports_dir, exist_ok=True)
|
23
|
+
os.makedirs(temp_dir, exist_ok=True)
|
24
|
+
|
25
|
+
try:
|
26
|
+
# Initialize the scanner
|
27
|
+
scanner = Scanner(threads=args.threads, output_dir=reports_dir, temp_dir=temp_dir)
|
28
|
+
|
29
|
+
# Perform scanning
|
30
|
+
results = scanner.scan_packages()
|
31
|
+
|
32
|
+
# Handle GitHub upload if specified
|
33
|
+
if args.upload:
|
34
|
+
if not (args.repo_owner and args.repo_name and args.token and args.repo_dir):
|
35
|
+
raise ValueError("GitHub upload requires --repo-owner, --repo-name, --token, and --repo-dir")
|
36
|
+
|
37
|
+
uploader = GitHubUploader(args.token, args.repo_owner, args.repo_name)
|
38
|
+
for report_file in os.listdir(reports_dir):
|
39
|
+
report_path = os.path.join(reports_dir, report_file)
|
40
|
+
if os.path.isfile(report_path):
|
41
|
+
uploader.upload_file(report_path, os.path.join(args.repo_dir, report_file), "Add OSSA report")
|
42
|
+
|
43
|
+
finally:
|
44
|
+
# Clean up the temporary directory unless the user opts to retain it
|
45
|
+
if not args.retain_temp:
|
46
|
+
print(f"Cleaning up temporary directory: {temp_dir}")
|
47
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
48
|
+
|
49
|
+
if __name__ == "__main__":
|
50
|
+
main()
|
@@ -0,0 +1,123 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import hashlib
|
4
|
+
from datetime import datetime
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
6
|
+
from .utils.os_detection import detect_os
|
7
|
+
from .utils.package_manager import list_packages, get_package_info
|
8
|
+
from .utils.downloader import download_source
|
9
|
+
from .utils.hash_calculator import calculate_file_hash
|
10
|
+
from .utils.swhid_calculator import calculate_swhid
|
11
|
+
|
12
|
+
class Scanner:
|
13
|
+
def __init__(self, threads=4, output_dir="ossa_reports", temp_dir="/tmp/ossa_temp"):
|
14
|
+
self.output_dir = output_dir
|
15
|
+
self.temp_dir = temp_dir
|
16
|
+
self.os_type = detect_os()
|
17
|
+
self.threads = threads
|
18
|
+
os.makedirs(self.temp_dir, exist_ok=True)
|
19
|
+
|
20
|
+
def process_package(self, package):
|
21
|
+
"""
|
22
|
+
Processes a single package: downloads source, extracts, calculates hash and SWHID.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
package (str): Package name to process.
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
dict: Result of the processed package including hash and SWHID.
|
29
|
+
"""
|
30
|
+
try:
|
31
|
+
print(f"Processing package: {package}")
|
32
|
+
package_info = get_package_info(self.os_type, package)
|
33
|
+
print(f"Fetched metadata for {package}")
|
34
|
+
|
35
|
+
# Download the source code to temp_dir
|
36
|
+
source_file = download_source(self.os_type, package, self.temp_dir)
|
37
|
+
print(f"Downloaded source file: {source_file}")
|
38
|
+
|
39
|
+
# Calculate hash of the source file
|
40
|
+
file_hash = calculate_file_hash(source_file)
|
41
|
+
print(f"Hash (SHA256) for {package}: {file_hash}")
|
42
|
+
|
43
|
+
# Extract source code directory in temp_dir
|
44
|
+
source_dir = os.path.join(self.temp_dir, package)
|
45
|
+
os.makedirs(source_dir, exist_ok=True)
|
46
|
+
|
47
|
+
# Calculate SWHID
|
48
|
+
swhid = calculate_swhid(source_dir)
|
49
|
+
print(f"SWHID for {package}: {swhid}")
|
50
|
+
|
51
|
+
# Save report
|
52
|
+
self.save_package_report(package, package_info, file_hash, swhid, source_file)
|
53
|
+
|
54
|
+
except Exception as e:
|
55
|
+
print(f"Error processing package {package}: {e}")
|
56
|
+
|
57
|
+
def scan_packages(self):
|
58
|
+
"""
|
59
|
+
Scans all packages in the repository and processes them in parallel.
|
60
|
+
"""
|
61
|
+
print(f"Detected OS: {self.os_type}")
|
62
|
+
print("Listing available packages...")
|
63
|
+
packages = list_packages(self.os_type)
|
64
|
+
with ThreadPoolExecutor(max_workers=self.threads) as executor:
|
65
|
+
# Submit tasks for parallel processing
|
66
|
+
future_to_package = {
|
67
|
+
executor.submit(self.process_package, package): package
|
68
|
+
for package in packages
|
69
|
+
}
|
70
|
+
|
71
|
+
for future in as_completed(future_to_package):
|
72
|
+
package = future_to_package[future]
|
73
|
+
try:
|
74
|
+
future.result()
|
75
|
+
except Exception as e:
|
76
|
+
print(f"Exception occurred for package {package}: {e}")
|
77
|
+
|
78
|
+
def save_package_report(self, package, package_info, file_hash, swhid, source_file):
|
79
|
+
"""
|
80
|
+
Save the report for a single package.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
package (str): Package name.
|
84
|
+
package_info (dict): Information about the package.
|
85
|
+
file_hash (str): SHA256 hash of the downloaded source.
|
86
|
+
swhid (str): Software Heritage ID of the package.
|
87
|
+
"""
|
88
|
+
# Generate report filename
|
89
|
+
sha1_name = hashlib.sha1(package.encode()).hexdigest()
|
90
|
+
date_str = datetime.now().strftime("%Y%m%d")
|
91
|
+
report_filename = f"ossa-{date_str}-{sha1_name}-{package}.json"
|
92
|
+
report_path = os.path.join(self.output_dir, report_filename)
|
93
|
+
|
94
|
+
# Create the report content
|
95
|
+
report = {
|
96
|
+
"id": f"OSSA-{date_str}-{sha1_name.upper()}",
|
97
|
+
"version": "1.0.0",
|
98
|
+
"severity": "Informational",
|
99
|
+
"title": f"Advisory for {package}",
|
100
|
+
"package_name": package,
|
101
|
+
"publisher": "Generated by OSSA Collector",
|
102
|
+
"last_updated": datetime.now().isoformat(),
|
103
|
+
"approvals": [{"consumption": True, "externalization": True}],
|
104
|
+
"description": f"Automatically generated OSSA for the package {package}.",
|
105
|
+
"purls": [f"pkg:{self.os_type}/{package}"],
|
106
|
+
"regex": [f"^pkg:{self.os_type}/{package}.*"],
|
107
|
+
"affected_versions": ["*.*"],
|
108
|
+
"artifacts": [
|
109
|
+
{
|
110
|
+
"url": f"file://{source_file}",
|
111
|
+
"hashes": {"sha256": file_hash},
|
112
|
+
"swhid": swhid
|
113
|
+
}
|
114
|
+
],
|
115
|
+
"licenses": package_info.get("licenses", []),
|
116
|
+
"aliases": package_info.get("aliases", []),
|
117
|
+
"references": package_info.get("references", [])
|
118
|
+
}
|
119
|
+
|
120
|
+
# Save the report to the output directory
|
121
|
+
with open(report_path, "w") as f:
|
122
|
+
json.dump(report, f, indent=4)
|
123
|
+
print(f"Report saved: {report_path}")
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "0.1.2"
|
@@ -1,35 +0,0 @@
|
|
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()
|
@@ -1,113 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import json
|
3
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
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
|
10
|
-
|
11
|
-
class Scanner:
|
12
|
-
def __init__(self, output_dir, threads=4):
|
13
|
-
self.output_dir = output_dir
|
14
|
-
self.os_type = detect_os()
|
15
|
-
self.threads = threads
|
16
|
-
|
17
|
-
def process_package(self, package):
|
18
|
-
"""
|
19
|
-
Processes a single package: downloads source, extracts, calculates hash and SWHID.
|
20
|
-
|
21
|
-
Args:
|
22
|
-
package (str): Package name to process.
|
23
|
-
|
24
|
-
Returns:
|
25
|
-
dict: Result of the processed package including hash and SWHID.
|
26
|
-
"""
|
27
|
-
try:
|
28
|
-
print(f"Processing package: {package}")
|
29
|
-
package_info = get_package_info(self.os_type, package)
|
30
|
-
print(f"Fetched metadata for {package}")
|
31
|
-
|
32
|
-
# Download the source code
|
33
|
-
source_file = download_source(self.os_type, package, self.output_dir)
|
34
|
-
print(f"Downloaded source file: {source_file}")
|
35
|
-
|
36
|
-
# Calculate hash of the source file
|
37
|
-
file_hash = calculate_file_hash(source_file)
|
38
|
-
print(f"Hash (SHA256) for {package}: {file_hash}")
|
39
|
-
|
40
|
-
# Extract source code directory
|
41
|
-
source_dir = os.path.join(self.output_dir, package)
|
42
|
-
os.makedirs(source_dir, exist_ok=True)
|
43
|
-
|
44
|
-
# Calculate SWHID
|
45
|
-
swhid = calculate_swhid(source_dir)
|
46
|
-
print(f"SWHID for {package}: {swhid}")
|
47
|
-
|
48
|
-
return {
|
49
|
-
"package": package,
|
50
|
-
"info": package_info,
|
51
|
-
"hash": file_hash,
|
52
|
-
"swhid": swhid,
|
53
|
-
}
|
54
|
-
|
55
|
-
except Exception as e:
|
56
|
-
print(f"Error processing package {package}: {e}")
|
57
|
-
return {
|
58
|
-
"package": package,
|
59
|
-
"error": str(e)
|
60
|
-
}
|
61
|
-
|
62
|
-
def scan_packages(self):
|
63
|
-
"""
|
64
|
-
Scans all packages in the repository and processes them in parallel.
|
65
|
-
|
66
|
-
Returns:
|
67
|
-
list: List of results for each package.
|
68
|
-
"""
|
69
|
-
print(f"Detected OS: {self.os_type}")
|
70
|
-
print("Listing available packages...")
|
71
|
-
packages = list_packages(self.os_type)
|
72
|
-
results = []
|
73
|
-
with ThreadPoolExecutor(max_workers=self.threads) as executor:
|
74
|
-
# Submit tasks for parallel processing
|
75
|
-
future_to_package = {
|
76
|
-
executor.submit(self.process_package, package): package
|
77
|
-
for package in packages
|
78
|
-
}
|
79
|
-
|
80
|
-
for future in as_completed(future_to_package):
|
81
|
-
package = future_to_package[future]
|
82
|
-
try:
|
83
|
-
result = future.result()
|
84
|
-
results.append(result)
|
85
|
-
except Exception as e:
|
86
|
-
print(f"Exception occurred for package {package}: {e}")
|
87
|
-
return results
|
88
|
-
|
89
|
-
def save_results(self, results, output_file):
|
90
|
-
"""
|
91
|
-
Save the scan results to a JSON file.
|
92
|
-
|
93
|
-
Args:
|
94
|
-
results (list): List of results for each package.
|
95
|
-
output_file (str): Path to save the JSON file.
|
96
|
-
"""
|
97
|
-
with open(output_file, "w") as f:
|
98
|
-
json.dump(results, f, indent=4)
|
99
|
-
print(f"Results saved to {output_file}")
|
100
|
-
|
101
|
-
def upload_results(self, results_file, github_uploader, repo_dir):
|
102
|
-
"""
|
103
|
-
Uploads the results file to GitHub.
|
104
|
-
|
105
|
-
Args:
|
106
|
-
results_file (str): Local results file path to upload.
|
107
|
-
github_uploader (GitHubUploader): Instance of the GitHubUploader class.
|
108
|
-
repo_dir (str): Path in the GitHub repository where the results will be uploaded.
|
109
|
-
"""
|
110
|
-
print(f"Uploading results to GitHub: {repo_dir}")
|
111
|
-
repo_path = os.path.join(repo_dir, os.path.basename(results_file))
|
112
|
-
github_uploader.upload_file(results_file, repo_path, "Add scanning results")
|
113
|
-
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|