forked from amjadnatouf/vulnerability_analyzer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
209 lines (175 loc) · 7.28 KB
/
Copy pathmain.py
File metadata and controls
209 lines (175 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env python3
"""
GitHub Advisory Database Downloader and Analyzer.
This tool downloads security advisories and analyzes vulnerability fixes
using PyDriller to extract commit diffs and code modifications.
"""
import logging
import os
import shutil
import stat
import subprocess
import time
import psutil
from analysis import DatasetGenerator
from analysis.advisory_parser import AdvisoryParser
from analysis.repository_metadata_parser import RepositoryMetadataParser
from analysis.repository_test_checker import RepositoryTestChecker
from analysis.test_locator import TestLocator
from analysis.vulnerability_analyzer import VulnerabilityAnalyzer
from core.database import DB
from downloader.advisory_downloader import AdvisoryDownloader
from downloader.repository_metadata_downloader import RepositoryMetadataDownloader
from ui.cli import CLI
from utils import config
# Setup logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("advisory_analyzer.log"), logging.StreamHandler()],
)
logger = logging.getLogger()
def setup_environment():
"""Create necessary directories for the application."""
# Create data directory and subdirectories
os.makedirs(config.DATA_DIR, exist_ok=True)
os.makedirs(config.ADVISORIES_DIR, exist_ok=True)
os.makedirs(config.ANALYSIS_DIR, exist_ok=True)
os.makedirs(config.CWE_CACHE_DIR, exist_ok=True)
os.makedirs(config.DATABASE_DIR, exist_ok=True)
os.makedirs(config.REPOSITORIES_DIR, exist_ok=True)
os.makedirs(config.TEMP_DIR, exist_ok=True)
logger.info("Environment setup complete.")
def close_open_files(directory):
"""Find and close any processes using files in the given directory."""
for proc in psutil.process_iter(["pid", "name"]):
try:
for file in proc.open_files():
if directory in file.path:
logger.info(
f"Closing process {proc.pid} ({proc.name()}) using {file.path}"
)
proc.terminate() # Terminate the process
proc.wait(timeout=5) # Ensure process exits
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue # Ignore processes that have already exited
def force_delete_git_files(directory):
"""Forcefully delete .git folders and make them writable."""
for root, dirs, files in os.walk(directory, topdown=False):
for file in files:
file_path = os.path.join(root, file)
try:
os.chmod(file_path, stat.S_IWRITE) # Ensure writable
os.remove(file_path)
except PermissionError:
logger.warning(f"Could not delete file: {file_path}")
for dir in dirs:
dir_path = os.path.join(root, dir)
try:
os.chmod(dir_path, stat.S_IWRITE)
shutil.rmtree(dir_path, ignore_errors=True)
except PermissionError:
logger.warning(f"Could not delete directory: {dir_path}")
def delete_cloned_repositories():
"""Safely delete the cloned_repositories directory, ensuring no file locks remain."""
cloned_repos_path = config.TEMP_DIR
if os.path.exists(cloned_repos_path):
logger.info("Attempting to delete cloned repositories...")
kill_git_processes() # Kill Git processes before deletion
# Ensure no processes are using files
close_open_files(cloned_repos_path)
for attempt in range(5): # Retry mechanism
try:
# Remove .git files first
force_delete_git_files(cloned_repos_path)
shutil.rmtree(cloned_repos_path) # Delete the directory
logger.info("Successfully deleted cloned repositories.")
break
except PermissionError as e:
logger.warning(
f"Permission error while deleting {cloned_repos_path}: {e}"
)
time.sleep(2) # Wait and retry
except Exception as e:
logger.error(f"Unexpected error deleting {cloned_repos_path}: {e}")
break
def kill_git_processes():
"""Kill all active Git processes to unlock files."""
try:
if os.name == "nt": # Windows
subprocess.run(
["taskkill", "/IM", "git.exe", "/F"],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
else: # Mac/Linux
subprocess.run(
["pkill", "-f", "git"],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
logger.info("Killed Git processes before deletion.")
except Exception as e:
logger.warning(f"Could not kill Git processes: {e}")
def main():
"""Main application flow"""
print("GitHub Advisory Database Downloader and Analyzer")
print("=" * 50)
print("This tool downloads security advisories and analyzes vulnerability fixes")
print("using PyDriller to extract commit diffs and code modifications.")
print("=" * 50)
# Setup environment (create directories)
setup_environment()
# Set the database path
db_path = os.path.join(config.DATABASE_DIR, config.DATABASE_NAME)
# Check if database already exists
if os.path.exists(db_path):
print(f"\nDatabase already exists at: {db_path}")
choice = input(
"Do you want to overwrite the existing database? (y/n): "
).lower()
if choice == "y" or choice == "yes":
logger.info("User chose to overwrite the existing database.")
# TODO: Add a confirmation prompt before overwriting
# For now commented out
# Remove the existing database
# os.remove(db_path)
logger.info("Removed existing database.")
else:
logger.info(
"User chose to add data incrementally to the existing database."
)
db = DB(db_path, echo=False)
db.initialize(logger) # Initialize and return the session
try:
# Create instances of core components
advisory_downloader = AdvisoryDownloader()
advisory_parser = AdvisoryParser(db) # For parsing advisories
vulnerability_analyzer = VulnerabilityAnalyzer(db) # For analyzing repositories and commits
repository_metadata_downloader = RepositoryMetadataDownloader(db)
repository_metadata_parser = RepositoryMetadataParser(db)
repository_test_checker = RepositoryTestChecker(db)
test_locator = TestLocator(db)
dataset_generator = DatasetGenerator()
# Run the CLI menu
cli = CLI(
db,
advisory_downloader,
advisory_parser,
vulnerability_analyzer,
repository_metadata_downloader,
repository_metadata_parser,
repository_test_checker,
test_locator,
dataset_generator,
) # Inject all components
cli.menu()
finally:
# Ensure the database session is closed on exit
db.close_session()
# Delete cloned repositories
# delete_cloned_repositories()
if __name__ == "__main__":
main()