Dependabot Security Alerts Analyzer¶
Welcome to the documentation of Dependabot Security Alerts Analyzer!. Come here to visit the project.
Overview¶
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnaaliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
This is blablabla
Blablabla
Modules¶
DB Manager¶
Source code¶
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 | DBNAME = 'mydb'
def record_point(point, bucket, org):
"""It writes a point into a InfluxDB Bucket.
:param point: It represents the Point that will be added inside the bucket. For more information about 'Point' look 'DB Point' module.
:type point: InfluxPoint
:param write_api: It represents the API used to write the point on a bucket.
:type write_api: WriteApi
:param bucket: It contains the name of the bucket were the point will be written.
:type bucket: str
:param org: It contains the organization ID.
:type org: str
"""
host = 'localhost'
port = 8086
print(point)
print("\n ~~ INFO:Writing point into '" + DBNAME + "' ...")
client = InfluxDBClient(host=host, port=port, username=USER, password=PASSWORD, database=DBNAME)
json_body = [
{
"measurement": 'Record',
"time": point.commited_date,
"tags": {
"repo_id": str(point.repo_id),
"commit_hexsha": str(point.commit_hexsha) ,
"commit_author": str(point.commit_author) ,
"repo_name": str(point.repo_name) ,
"repo_owner": str(point.repo_owner) ,
"email": str(point.email) ,
"commit_message": str(point.commit_message) ,
"has_config_file": str(point.has_config_file)
},
"fields": {
"total_commit_dependencies": point.total_commit_dependencies,
"new_critical_vulnerabilities": len(point.critical_severity["new_vulnerabilities"]),
"fixed_critical_vulnerabilities": len(point.critical_severity["fixed_vulnerabilities"]),
"removed_critical_vulnerabilities": len(point.critical_severity["removed_vulnerabilities"]),
"revoked_fixed_critical_vulnerabilities": len(point.critical_severity["revoked_fixed_vulnerabilities"]),
"revoked_removed_critical_vulnerabilities": len(point.critical_severity["revoked_removed_vulnerabilities"]),
"kept_critical_vulnerabilities": len(point.critical_severity["kept_vulnerabilities"]),
"new_high_vulnerabilities": len(point.high_severity["new_vulnerabilities"]),
"fixed_high_vulnerabilities": len(point.high_severity["fixed_vulnerabilities"]),
"removed_high_vulnerabilities": len(point.high_severity["removed_vulnerabilities"]),
"revoked_fixed_high_vulnerabilities": len(point.high_severity["revoked_fixed_vulnerabilities"]),
"revoked_removed_high_vulnerabilities": len(point.high_severity["revoked_removed_vulnerabilities"]),
"kept_high_vulnerabilities": len(point.high_severity["kept_vulnerabilities"]),
"new_moderate_vulnerabilities": len(point.moderate_severity["new_vulnerabilities"]),
"fixed_moderate_vulnerabilities": len(point.moderate_severity["fixed_vulnerabilities"]),
"removed_moderate_vulnerabilities": len(point.moderate_severity["removed_vulnerabilities"]),
"revoked_fixed_moderate_vulnerabilities": len(point.moderate_severity["revoked_fixed_vulnerabilities"]),
"revoked_removed_moderate_vulnerabilities": len(point.moderate_severity["revoked_removed_vulnerabilities"]),
"kept_moderate_vulnerabilities": len(point.moderate_severity["kept_vulnerabilities"]),
"new_low_vulnerabilities": len(point.low_severity["new_vulnerabilities"]),
"fixed_low_vulnerabilities": len(point.low_severity["fixed_vulnerabilities"]),
"removed_low_vulnerabilities": len(point.low_severity["removed_vulnerabilities"]),
"revoked_fixed_low_vulnerabilities": len(point.low_severity["revoked_fixed_vulnerabilities"]),
"revoked_removed_low_vulnerabilities": len(point.low_severity["revoked_removed_vulnerabilities"]),
"kept_low_vulnerabilities": len(point.low_severity["kept_vulnerabilities"]),
"total_new_vulnerabilities": len(point.summary["new_vulnerabilities"]),
"total_fixed_vulnerabilities": len(point.summary["fixed_vulnerabilities"]),
"total_removed_vulnerabilities": len(point.summary["removed_vulnerabilities"]),
"total_revoked_fixed_vulnerabilities": len(point.summary["revoked_fixed_vulnerabilities"]),
"total_revoked_removed_vulnerabilities": len(point.summary["revoked_removed_vulnerabilities"]),
"total_kept_vulnerabilities": len(point.summary["kept_vulnerabilities"]),
"total_commit_vulnerabilities": len(point.summary["total"])
}
}
]
client.write_points(json_body, time_precision='s', protocol='json', database='mydb')
#client_df = DataFrameClient(host=host, port=port, username=USER, password=PASSWORD, database=DBNAME)
#results_df = client_df.query('SELECT * FROM Commit_22')
|
DB Point¶
-
class
db_point.InfluxPoint(repo_id, commited_date, commit_hexsha, commit_author, repo_name, repo_owner, total_commit_dependencies, summary, critical_severity, high_severity, moderate_severity, low_severity, has_config_file, email, commit_message)¶ Bases:
objectConceptual class to represent each point present in a InfluxDB bucket.
Parameters: - repo_id (int) – Id of a repository.
- commited_date (int) – Date when the commit was committed.
- commit_hexsha (int) – It is the hexsha of a commit.
- commit_author (int) – It contains the committed author name.
- repo_name (str) – It represents the name of a repository.
- repo_owner (str) – It represents the owner’s name of a repository.
- total_commit_dependencies (int) – It contains the total number of dependencies present in a commit.
- summary (Dictionary of lists.) – It contains a dictionary with the following keys: ‘new_vulnerabilities’, ‘fixed_vulnerabilities’, ‘removed_vulnerabilities’, ‘revoked_fixed_vulnerabilities’, ‘revoked_removed_vulnerabilities’, ‘kept_vulnerabilities’ and ‘total’. The values are the total number of vulnerabilities that correspond to those types.
- critical_severity (Dictionary of lists.) – It contains a dictionary with the following keys: ‘new_vulnerabilities’, ‘fixed_vulnerabilities’, ‘removed_vulnerabilities’, ‘revoked_fixed_vulnerabilities’, ‘revoked_removed_vulnerabilities’, ‘kept_vulnerabilities’ and ‘total’. The values are critical severity vulnerabilities corresponding to those types.
- high_severity (Dictionary of lists.) – It contains a dictionary with the following keys: ‘new_vulnerabilities’, ‘fixed_vulnerabilities’, ‘removed_vulnerabilities’, ‘revoked_fixed_vulnerabilities’, ‘revoked_removed_vulnerabilities’, ‘kept_vulnerabilities’ and ‘total’. The values are high severity vulnerabilities corresponding to those types.
- moderate_severity (Dictionary of lists.) – It contains a dictionary with the following keys: ‘new_vulnerabilities’, ‘fixed_vulnerabilities’, ‘removed_vulnerabilities’, ‘revoked_fixed_vulnerabilities’, ‘revoked_removed_vulnerabilities’, ‘kept_vulnerabilities’ and ‘total’. The values are moderate severity vulnerabilities corresponding to those types.
- low_severity (int) – It contains a dictionary with the following keys: ‘new_vulnerabilities’, ‘fixed_vulnerabilities’, ‘removed_vulnerabilities’, ‘revoked_fixed_vulnerabilities’, ‘revoked_removed_vulnerabilities’, ‘kept_vulnerabilities’ and ‘total’. The values are low severity vulnerabilities corresponding to those types.
- has_config_file (bool) – It indicates if in the commit it is present the Dependabot configuration file.
- email (str) – It contains the email of the commit author.
- commit_message (str) – It contains the commit message.
Source code¶
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 |
class InfluxPoint:
"""Conceptual class to represent each point present in a InfluxDB bucket.
:param repo_id: Id of a repository.
:type repo_id: int
:param commited_date: Date when the commit was committed.
:type commited_date: int
:param commit_hexsha: It is the hexsha of a commit.
:type commit_hexsha: int
:param commit_author: It contains the committed author name.
:type commit_author: int
:param repo_name: It represents the name of a repository.
:type repo_name: str
:param repo_owner: It represents the owner's name of a repository.
:type repo_owner: str
:param total_commit_dependencies: It contains the total number of dependencies present in a commit.
:type total_commit_dependencies: int
:param summary: It contains a dictionary with the following keys: 'new_vulnerabilities', 'fixed_vulnerabilities', 'removed_vulnerabilities', 'revoked_fixed_vulnerabilities', 'revoked_removed_vulnerabilities', 'kept_vulnerabilities' and 'total'. The values are the total number of vulnerabilities that correspond to those types.
:type summary: Dictionary of lists.
:param critical_severity: It contains a dictionary with the following keys: 'new_vulnerabilities', 'fixed_vulnerabilities', 'removed_vulnerabilities', 'revoked_fixed_vulnerabilities', 'revoked_removed_vulnerabilities', 'kept_vulnerabilities' and 'total'. The values are critical severity vulnerabilities corresponding to those types.
:type critical_severity: Dictionary of lists.
:param high_severity: It contains a dictionary with the following keys: 'new_vulnerabilities', 'fixed_vulnerabilities', 'removed_vulnerabilities', 'revoked_fixed_vulnerabilities', 'revoked_removed_vulnerabilities', 'kept_vulnerabilities' and 'total'. The values are high severity vulnerabilities corresponding to those types.
:type high_severity: Dictionary of lists.
:param moderate_severity: It contains a dictionary with the following keys: 'new_vulnerabilities', 'fixed_vulnerabilities', 'removed_vulnerabilities', 'revoked_fixed_vulnerabilities', 'revoked_removed_vulnerabilities', 'kept_vulnerabilities' and 'total'. The values are moderate severity vulnerabilities corresponding to those types.
:type moderate_severity: Dictionary of lists.
:param low_severity: It contains a dictionary with the following keys: 'new_vulnerabilities', 'fixed_vulnerabilities', 'removed_vulnerabilities', 'revoked_fixed_vulnerabilities', 'revoked_removed_vulnerabilities', 'kept_vulnerabilities' and 'total'. The values are low severity vulnerabilities corresponding to those types.
:type low_severity: int
:param has_config_file: It indicates if in the commit it is present the Dependabot configuration file.
:type has_config_file: bool
:param email: It contains the email of the commit author.
:type email: str
:param commit_message: It contains the commit message.
:type commit_message: str
"""
def __init__(self
, repo_id
, commited_date
, commit_hexsha
, commit_author
, repo_name
, repo_owner
, total_commit_dependencies
, summary
, critical_severity
, high_severity
, moderate_severity
, low_severity
, has_config_file
, email
, commit_message):
"""Constructor method
"""
self.repo_id = repo_id
self.commited_date = commited_date
self.commit_hexsha = commit_hexsha
self.commit_author = commit_author
self.repo_name = repo_name
self.repo_owner = repo_owner
self.total_commit_dependencies = total_commit_dependencies
self.summary = summary
self.critical_severity = critical_severity
self.high_severity = high_severity
self.moderate_severity = moderate_severity
self.low_severity = low_severity
self.has_config_file = has_config_file
self.email = email
self.commit_message = commit_message
def __str__(self):
"""To String method
"""
return " - Commited date:'" + datetime.utcfromtimestamp(self.commited_date).strftime('%Y-%m-%d %H:%M:%S') + "'" + \
"\n - Repository id: '" + str(self.repo_id) + "'" + \
"\n - Commit author: '" + str(self.commit_author) + "'" + \
"\n - Repo name: '" + self.repo_name + "'" + \
"\n - Repo owner: '" + self.repo_owner + "'" + \
"\n - Total number of dependencies: " + str(self.total_commit_dependencies)
# "\n - Dependabot configuration file: '" + str(self.has_config_file) + "'" + \
# "\n - Email address: '" + str(self.email) + "'" + \
# "\n - Commit message: '" + self.commit_message.title() + \
# "\n - Total number of vulnerabilities: " + str(self.summary) + \
# "\n - Critical severity: " + str(self.critical_severity) + \
# "\n - High severity: " + str(self.high_severity) + \
# "\n - Moderate severity: " + str(self.moderate_severity) + \
# "\n - Low severity: " + str(self.low_severity)
|
Dependencies Manager¶
-
dependencies_manager.calculate_summary()¶ It saves total number of different vulnerability ‘status’.
Parameters: vulnerability_id (int) – Vulnerability ID.
-
dependencies_manager.classify_vulnerabilities(vulnerabilities_list, vulnerability_type)¶ It appends each vulnerability to corresponding dictionary depending on severity level.
Parameters: - vulnerabilities_list ([]) – List of vulnerabilities to classify.
- vulnerability_type (str) – Vulnerability type.
-
dependencies_manager.cleaner(version_to_clean)¶ It cleans a dependency version, removing spaces, ‘^’ and ‘~’. Semver does not read version strings that contain those unwanted characters.
Parameters: version_to_clean (str) – Dependency version to clean. Returns: Dependency version cleaned. Return type: str
-
dependencies_manager.clear_dictionaries()¶ It clears ‘summary’, ‘critical_vulnerabilities’, ‘high_vulnerabilities’, ‘moderate_vulnerabilities’ and ‘low_vulnerabilities’. To be ready to proccess next repository.
-
dependencies_manager.get_dependency(line)¶ It returns the dependency present in a line.
Parameters: line (str) – Line where the dependency is searched. Returns: It returns the dependency present in a line. Return type: Dependency
-
dependencies_manager.get_severity_level(vulnerability_id)¶ It return severity level of a vulnerability.
Parameters: vulnerability_id (int) – Vulnerability ID. Returns: Vulnerability severity level. Return type: str
-
dependencies_manager.is_vulnerable(dependency_version, vulnerable_range)¶ It analyzes if a dependency version is vulnerable.
Parameters: - dependency_version (str) – It represents the dependency version.
- vulnerable_range (str) – It represents the vulnerable range.
Returns: It returns ‘True’ if version is within range, if not it returns ‘False’.
Return type: bool
-
dependencies_manager.set_dependencies_and_vulnerabilities(file_path, pattern_1, pattern_2, pattern_3, pattern_4, pattern_5, pattern_6)¶ It returns the total number of dependencies presented in a commit. If one of those dependencies contains a vulnerability it will be set into vulnerable dependencies list.
Parameters: - file_path (str) – Path of npm vulnerable library versions file.
- pattern_1 (str) – Possible pattern of first level dependency.
- pattern_2 (str) – Possible pattern of first level dependency.
- pattern_3 (str) – Possible pattern of first level dependency.
- pattern_4 (str) – Possible pattern of first level dependency.
- pattern_5 (str) – Possible pattern of first level dependency.
- pattern_6 (str) – Possible pattern of first level dependency.
Returns: It returns the total number of dependencies presented in a commit.
Return type: int
-
dependencies_manager.set_new_vulnerability(current_dependency)¶ If the current dependency is vulnerable, it will be classified as new, revoked or kept. At the end, the dependencies log is updated. It is added the current dependency.
param current_dependency: Dependency to classify. type current_dependency: Dependency
-
dependencies_manager.set_npm_and_yarn_dependencies()¶ It saves last commit vulnerabilities and record the new dependencies and vulnerabilities.
Returns: It returns the total number of npm and yarn dependencies. Return type: int
-
dependencies_manager.version_in_range(vulnerable_range, version)¶ It calculates if a version is within range.
Parameters: - vulnerable_range (str) – It represents the range.
- version (str) – It represents the dependency version.
Returns: It return ‘True’ if version is within range, if not it returns ‘False’.
Return type: bool
-
dependencies_manager.vulnerabilities_per_severity_level()¶ It classifies each vulnerability depending on severity level.
Source code¶
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 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | new_vulnerabilities = []
last_commit_vulnerabilities = []
vulnerabilities_log = []
kept_vulnerabilities = []
total_commit_vulnerabilities = []
no_duplicated_vulnerabilities = []
severity_records = []
removed_vulnerabilities = []
fixed_vulnerabilities = []
revoked_fixed_vulnerabilities = []
revoked_removed_vulnerabilities = []
historial_fixes = []
total_dependencies = []
removed_vulnerabilities_historial = []
dependencies_set = []
previous_dependencies_affected = []
critical_vulnerabilities = {
"new_vulnerabilities": [],
"fixed_vulnerabilities": [],
"removed_vulnerabilities": [],
"revoked_fixed_vulnerabilities": [],
"revoked_removed_vulnerabilities": [],
"kept_vulnerabilities": []
}
high_vulnerabilities = {
"new_vulnerabilities": [],
"fixed_vulnerabilities": [],
"removed_vulnerabilities": [],
"revoked_fixed_vulnerabilities": [],
"revoked_removed_vulnerabilities": [],
"kept_vulnerabilities": []
}
moderate_vulnerabilities = {
"new_vulnerabilities": [],
"fixed_vulnerabilities": [],
"removed_vulnerabilities": [],
"revoked_fixed_vulnerabilities": [],
"revoked_removed_vulnerabilities": [],
"kept_vulnerabilities": []
}
low_vulnerabilities = {
"new_vulnerabilities": [],
"fixed_vulnerabilities": [],
"removed_vulnerabilities": [],
"revoked_fixed_vulnerabilities": [],
"revoked_removed_vulnerabilities": [],
"kept_vulnerabilities": []
}
summary = {
"new_vulnerabilities": [],
"fixed_vulnerabilities": [],
"removed_vulnerabilities": [],
"revoked_fixed_vulnerabilities": [],
"revoked_removed_vulnerabilities": [],
"kept_vulnerabilities": [],
"total": []
}
vulnerable_versions_csv = pd.read_csv("../input/vulnerable_versions.csv", sep=';', dtype={"id": int})
def calculate_summary():
"""It saves total number of different vulnerability 'status'.
:param vulnerability_id: Vulnerability ID.
:type vulnerability_id: int
"""
summary["new_vulnerabilities"] = new_vulnerabilities
summary["fixed_vulnerabilities"] = fixed_vulnerabilities
summary["removed_vulnerabilities"] = removed_vulnerabilities
summary["revoked_fixed_vulnerabilities"] = revoked_fixed_vulnerabilities
summary["revoked_removed_vulnerabilities"] = revoked_removed_vulnerabilities
summary["kept_vulnerabilities"] = kept_vulnerabilities
summary["total"] = total_commit_vulnerabilities
def get_severity_level(vulnerability_id):
"""It return severity level of a vulnerability.
:param vulnerability_id: Vulnerability ID.
:type vulnerability_id: int
:return: Vulnerability severity level.
:rtype: str
"""
return vulnerable_versions_csv.loc[vulnerable_versions_csv['id'] == vulnerability_id, "severity"].to_numpy()
def classify_vulnerabilities(vulnerabilities_list, vulnerability_type):
"""It appends each vulnerability to corresponding dictionary depending on severity level.
:param vulnerabilities_list: List of vulnerabilities to classify.
:type vulnerabilities_list: []
:param vulnerability_type: Vulnerability type.
:type vulnerability_type: str
"""
for vul in vulnerabilities_list:
if get_severity_level(vul) == 1:
low_vulnerabilities[vulnerability_type].append(vul)
if get_severity_level(vul) == 2:
moderate_vulnerabilities[vulnerability_type].append(vul)
if get_severity_level(vul) == 3:
high_vulnerabilities[vulnerability_type].append(vul)
if get_severity_level(vul) == 4:
critical_vulnerabilities[vulnerability_type].append(vul)
def vulnerabilities_per_severity_level():
"""It classifies each vulnerability depending on severity level.
"""
classify_vulnerabilities(new_vulnerabilities, "new_vulnerabilities")
classify_vulnerabilities(fixed_vulnerabilities, "fixed_vulnerabilities")
classify_vulnerabilities(removed_vulnerabilities, "removed_vulnerabilities")
classify_vulnerabilities(revoked_fixed_vulnerabilities, "revoked_fixed_vulnerabilities")
classify_vulnerabilities(revoked_removed_vulnerabilities, "revoked_removed_vulnerabilities")
classify_vulnerabilities(kept_vulnerabilities, "kept_vulnerabilities")
def clear_dictionaries():
"""It clears 'summary', 'critical_vulnerabilities', 'high_vulnerabilities', 'moderate_vulnerabilities' and 'low_vulnerabilities'. To be ready to proccess next repository.
"""
for amount in summary:
summary[amount].clear()
for amount in critical_vulnerabilities:
critical_vulnerabilities[amount].clear()
for amount in high_vulnerabilities:
high_vulnerabilities[amount].clear()
for amount in moderate_vulnerabilities:
moderate_vulnerabilities[amount].clear()
for amount in low_vulnerabilities:
low_vulnerabilities[amount].clear()
def cleaner(version_to_clean):
"""It cleans a dependency version, removing spaces, '^' and '~'.
Semver does not read version strings that contain those unwanted characters.
:param version_to_clean: Dependency version to clean.
:type version_to_clean: str
:return: Dependency version cleaned.
:rtype: str
"""
dependency_version_cleaned = version_to_clean.replace(" ", "")
dependency_version_cleaned = dependency_version_cleaned.replace("^", "")
dependency_version_cleaned = dependency_version_cleaned.replace("~", "")
return dependency_version_cleaned
def version_in_range(vulnerable_range, version):
"""It calculates if a version is within range.
:param vulnerable_range: It represents the range.
:type vulnerable_range: str
:param version: It represents the dependency version.
:type version: str
:return: It return 'True' if version is within range, if not it returns 'False'.
:rtype: bool
"""
range_cleaned = cleaner(vulnerable_range)
if ">=" in vulnerable_range:
range_cleaned = range_cleaned.replace(">=", "")
result = semver.compare(range_cleaned, version)
if result is -1 or 0:
return True
elif "<=" in vulnerable_range:
range_cleaned = range_cleaned.replace("<=", "")
result = semver.compare(range_cleaned, version)
if result is 1 or 0:
return True
elif ">" in vulnerable_range:
range_cleaned = range_cleaned.replace(">", "")
result = semver.compare(range_cleaned, version)
if result is -1:
return True
elif "<" in vulnerable_range:
range_cleaned = range_cleaned.replace("<", "")
result = semver.compare(range_cleaned, version)
if result is 1:
return True
elif "=" in vulnerable_range:
range_cleaned = range_cleaned.replace("=", "")
result = semver.compare(range_cleaned, version)
if result is 0:
return True
return False
def is_vulnerable(dependency_version, vulnerable_range):
"""It analyzes if a dependency version is vulnerable.
:param dependency_version: It represents the dependency version.
:type dependency_version: str
:param vulnerable_range: It represents the vulnerable range.
:type vulnerable_range: str
:return: It returns 'True' if version is within range, if not it returns 'False'.
:rtype: bool
"""
if "," in vulnerable_range:
return version_in_range(vulnerable_range.split(',')[0], dependency_version) and version_in_range(
vulnerable_range.split(',')[1], dependency_version)
else:
return version_in_range(vulnerable_range, dependency_version)
def get_dependency(line):
"""It returns the dependency present in a line.
:param line: Line where the dependency is searched.
:type line: str
:return: It returns the dependency present in a line.
:rtype: Dependency
"""
if line.startswith("@"):
line = line[1:]
return dependency.Dependency("@" + line.split("@")[0], line.split("@")[1].replace("extraneous", ""))
else:
return dependency.Dependency(line.split("@")[0], line.split("@")[1].replace("extraneous", ""))
def set_dependencies_and_vulnerabilities(file_path, pattern_1, pattern_2, pattern_3, pattern_4, pattern_5, pattern_6):
"""It returns the total number of dependencies presented in a commit. If one of those dependencies
contains a vulnerability it will be set into vulnerable dependencies list.
:param file_path: Path of npm vulnerable library versions file.
:type file_path: str
:param pattern_1: Possible pattern of first level dependency.
:type pattern_1: str
:param pattern_2: Possible pattern of first level dependency.
:type pattern_2: str
:param pattern_3: Possible pattern of first level dependency.
:type pattern_3: str
:param pattern_4: Possible pattern of first level dependency.
:type pattern_4: str
:param pattern_5: Possible pattern of first level dependency.
:type pattern_5: str
:param pattern_6: Possible pattern of first level dependency.
:type pattern_6: str
:return: It returns the total number of dependencies presented in a commit.
:rtype: int
"""
dependencies_set.clear()
try:
with open('../cloned_git/' + file_path) as file:
if "npm" in file_path:
file_content = file.readlines()[1:]
else:
file_content = file.readlines()
file_content = [x.strip() for x in file_content]
last = file_content[-2]
for line in file_content:
previous_len = len(dependencies_set)
if line.startswith(pattern_1):
dependencies_set.append(get_dependency(line.split(pattern_1, 1)[1]))
elif line.startswith(pattern_2):
dependencies_set.append(get_dependency(line.split(pattern_2, 1)[1]))
elif line.startswith(pattern_3):
dependencies_set.append(get_dependency(line.split(pattern_3, 1)[1]))
elif line.startswith(pattern_6):
dependencies_set.append(get_dependency(line.split(pattern_6, 1)[1]))
elif line is last and line.startswith(pattern_4):
dependencies_set.append(get_dependency(line.split(pattern_4, 1)[1]))
elif line is last and line.startswith(pattern_5):
dependencies_set.append(get_dependency(line.split(pattern_5, 1)[1]))
if previous_len < len(dependencies_set):
set_new_vulnerability(dependencies_set[-1])
file.close()
except:
pass
total_dependencies = dependencies_set
return len(dependencies_set)
def set_npm_and_yarn_dependencies():
"""It saves last commit vulnerabilities and record the new dependencies and vulnerabilities.
:return: It returns the total number of npm and yarn dependencies.
:rtype: int
"""
vulnerable_dependencies = previous_dependencies_affected
previous_commmit_vulnerabilities = last_commit_vulnerabilities
last_commit_vulnerabilities.clear()
previous_dependencies_affected.clear()
for vul in total_commit_vulnerabilities:
last_commit_vulnerabilities.append(vul)
clear_dictionaries()
total_commit_vulnerabilities.clear()
kept_vulnerabilities.clear()
new_vulnerabilities.clear()
revoked_fixed_vulnerabilities.clear()
revoked_removed_vulnerabilities.clear()
removed_vulnerabilities.clear()
npm = set_dependencies_and_vulnerabilities('npm_dependencies.txt', "└── UNMET DEPENDENCY ", "├── UNMET DEPENDENCY "
, "├── ", "└── ", "└── UNMET DEPENDENCY ", "├─┬ UNMET DEPENDENCY ")
yarn = set_dependencies_and_vulnerabilities('yarn_dependencies.txt', "└─ UNMET DEPENDENCY ", "├─ UNMET DEPENDENCY "
, "├─ ", "└─ ", "└─ UNMET DEPENDENCY ", "├─┬ UNMET DEPENDENCY ")
deleted_vulnerabilities = []
for dependency in vulnerable_dependencies:
if dependency.library_name not in dependencies_set:
deleted_vulnerabilities.append(dependency.version)
for vulnerability in previous_commmit_vulnerabilities:
if vulnerability in deleted_vulnerabilities and vulnerability not in total_commit_vulnerabilities and vulnerability not in removed_vulnerabilities_historial:
removed_vulnerabilities.append(vulnerability)
removed_vulnerabilities_historial.append(vulnerability)
elif vulnerability not in kept_vulnerabilities and vulnerability not in historial_fixes:
fixed_vulnerabilities.append(vulnerability)
historial_fixes.append(vulnerability)
calculate_summary()
vulnerabilities_per_severity_level()
return npm + yarn
def set_new_vulnerability(current_dependency):
"""If the current dependency is vulnerable, it will be classified as new, revoked or kept.
At the end, the dependencies log is updated. It is added the current dependency.
:param current_dependency: Dependency to classify.
:type current_dependency: Dependency
"""
try:
vulnerability_position = 0
dependency_version = current_dependency.version
dependency_version_clean = cleaner(dependency_version)
if semver.VersionInfo.isvalid(dependency_version_clean):
matched_vulnerable_version_ranges = vulnerable_versions_csv.loc[
vulnerable_versions_csv['name'] == current_dependency.library_name, "vulnerableVersionRange"].to_numpy()
matched_vulnerable_ids = vulnerable_versions_csv.loc[
vulnerable_versions_csv['name'] == current_dependency.library_name, "id"].to_numpy()
matched_severity_levels = vulnerable_versions_csv.loc[
vulnerable_versions_csv['name'] == current_dependency.library_name, "severity"].to_numpy()
for vulnerable_version_range in matched_vulnerable_version_ranges:
vulnerability_id = matched_vulnerable_ids[vulnerability_position]
if is_vulnerable(dependency_version_clean,
vulnerable_version_range) and vulnerability_id not in no_duplicated_vulnerabilities:
previous_dependencies_affected.append(dependency.Dependency(current_dependency.library_name,vulnerability_id))
if vulnerability_id not in vulnerabilities_log:
if vulnerability_id not in last_commit_vulnerabilities:
new_vulnerabilities.append(vulnerability_id)
elif vulnerability_id in last_commit_vulnerabilities:
kept_vulnerabilities.append(vulnerability_id)
if vulnerability_id in vulnerabilities_log and vulnerability_id not in last_commit_vulnerabilities:
if vulnerability_id not in historial_fixes and vulnerability_id in removed_vulnerabilities_historial:
revoked_removed_vulnerabilities.append(vulnerability_id)
removed_vulnerabilities_historial.remove(vulnerability_id)
elif vulnerability_id in historial_fixes and vulnerability_id not in removed_vulnerabilities_historial:
revoked_fixed_vulnerabilities.append(vulnerability_id)
historial_fixes.remove(vulnerability_id)
if vulnerability_id not in vulnerabilities_log:
vulnerabilities_log.append(vulnerability_id)
no_duplicated_vulnerabilities.append(vulnerability_id)
severity_records.append(str(matched_severity_levels[vulnerability_position]))
for vul_id in new_vulnerabilities:
if vul_id not in total_commit_vulnerabilities:
total_commit_vulnerabilities.append(vul_id)
for vul_id in kept_vulnerabilities:
if vul_id not in total_commit_vulnerabilities:
total_commit_vulnerabilities.append(vul_id)
for vul_id in revoked_fixed_vulnerabilities:
if vul_id not in total_commit_vulnerabilities:
total_commit_vulnerabilities.append(vul_id)
for vul_id in revoked_removed_vulnerabilities:
if vul_id not in total_commit_vulnerabilities:
total_commit_vulnerabilities.append(vul_id)
except:
error = sys.exc_info()[0]
error_details = sys.exc_info()[1]
print("Error: \n%s" % error)
print("Error: \n%s" % error_details)
|
Dependency¶
-
class
dependency.Dependency(library_name, version)¶ Bases:
objectConceptual class to represent each dependency.
Parameters: - library_name (str) – Name of a library.
- version (str) – Version range of a library.
Source code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 | """Conceptual class to represent each dependency.
:param library_name: Name of a library.
:type library_name: str
:param version: Version range of a library.
:type version: str
"""
def __init__(self, library_name, version):
"""Constructor method
"""
self.library_name = library_name
self.version = version
|
Directories Manager¶
-
directories_manager.download_file(repo, commit_hexsha, file_name)¶ To Download file if it is presented in a repository commit.
Parameters: - repo (str) – Repository name that contains the commit.
- commit_hexsha (str) – Commit where the file is searched.
- file_name (str) – Name of the file to download.
Returns: It returns ‘True’ if file exists in the commit and it can be downloaded, if not it returns ‘False’.
Return type: Repo
-
directories_manager.first_level_dependencies_tree(repo, commit)¶ It downloads ‘package.json’, ‘package-lock.json’ and ‘npm-shrinkwrap.json’, ‘yarn.lock’ if it is possible. If’package.json’ was downloaded, it will generate the first level npm dependencies tree. If ‘yarn.lock’ was downloaded, it will create the first level yarn dependencies tree.
param repo: Repository that contains the commit that it is being analyzed. type repo: Repo param commit: Commit which dependencies first level tree is being built. type commit: Commit
-
directories_manager.new_cloned_git_folder()¶ It deletes ‘cloned git’ folder, if possible. It generates a new ‘cloned git’ folder.
Parameters: file_directory (str) – Path where is the wanted to be delete file.
-
directories_manager.remove_file(file_directory)¶ To remove file.
Parameters: file_directory (str) – Path where is the wanted to be delete file.
Source code¶
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 | def download_file(repo, commit_hexsha, file_name):
""" To Download file if it is presented in a repository commit.
:param repo: Repository name that contains the commit.
:type repo: str
:param commit_hexsha: Commit where the file is searched.
:type commit_hexsha: str
:param file_name: Name of the file to download.
:type file_name: str
:return: It returns 'True' if file exists in the commit and it can be downloaded, if not it returns 'False'.
:rtype: Repo
"""
try:
repo.git.checkout(commit_hexsha, file_name)
if path.exists("../cloned_git/" + file_name):
return True
return False
except:
# print(" INFO: It doesn't contain '" + file_name + "'")
return False
pass
def remove_file(file_directory):
""" To remove file.
:param file_directory: Path where is the wanted to be delete file.
:type file_directory: str
"""
try:
os.remove(file_directory)
except:
pass
def new_cloned_git_folder():
""" It deletes 'cloned git' folder, if possible.
It generates a new 'cloned git' folder.
:param file_directory: Path where is the wanted to be delete file.
:type file_directory: str
"""
try:
shutil.rmtree("../cloned_git")
except OSError as e:
print("Error: %s - %s." % (e.filename, e.strerror))
os.mkdir("../cloned_git")
def first_level_dependencies_tree(repo, commit):
""" It downloads 'package.json', 'package-lock.json' and 'npm-shrinkwrap.json',
'yarn.lock' if it is possible. If'package.json' was downloaded, it will generate the
first level npm dependencies tree. If 'yarn.lock' was downloaded, it will create
the first level yarn dependencies tree.
:param repo: Repository that contains the commit that it is being analyzed.
:type repo: Repo
:param commit: Commit which dependencies first level tree is being built.
:type commit: Commit
"""
has_package_file = download_file(repo, commit.hexsha, 'package.json')
download_file(repo, commit.hexsha, 'package-lock.json')
download_file(repo, commit.hexsha, 'npm-shrinkwrap.json')
has_yarn_dependencies = download_file(repo, commit.hexsha, 'yarn.lock')
if has_package_file:
scripts_manager.collect_npm_dependencies()
if has_yarn_dependencies:
scripts_manager.collect_yarn_dependencies()
for file in ["../cloned_git/package.json", "../cloned_git/yarn.lock", "../cloned_git/package-lock.json",
"../cloned_git/package-lock.json"]:
remove_file(file)
|
Git Manager¶
Source code¶
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 |
def diff_in_dependencies(current_commit, previous_commit):
""" It compares the current commit with the previous. To check if'package.json',
'package-lock.json', 'npm-shrinkwrap.json' or 'yarn.lock' were changed.
:param current_commit: Current commit.
:type current_commit: Commit
:param previous_commit: Previous commit.
:type previous_commit: Commit
:return: It returns 'True' if 'package.json', 'package-lock.json', 'npm-shrinkwrap.json' or 'yarn.lock' was modified in the current commit, if not it will return 'False'.
:rtype: bool
"""
diff_index = previous_commit.diff(current_commit)
for diff in diff_index:
if "package.json" in {diff.b_path} or "package.json" in {diff.a_path}:
return True
if "package-lock.json" in {diff.b_path} or "package-lock.json" in {diff.a_path}:
return True
if "npm-shrinkwrap.json" in {diff.b_path} or "npm-shrinkwrap.json" in {diff.a_path}:
return True
if "yarn.lock" in {diff.b_path} or "yarn.lock" in {diff.a_path}:
return True
return False
def clone_repo(name, owner, directory, id):
""" To clone repository into '/cloned_git'
:param name: Repository name.
:type name: str
:param owner: Repository owner.
:type owner: str
:param directory: Directory were the repository is cloned.
:type directory: str
:return: It returns an instance of the cloned repository.
:rtype: Repo
"""
print("\n~~ INFO:Cloning '" + owner + "/" + name + ".git' into '" + directory[:-3] + "cloned_git'")
try:
repo = Repo.clone_from(
url="https://github.com/" + owner + "/" + name,
|
Helpers¶
Source code¶
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 |
from src import dependencies_manager, directories_manager
from src.dependencies_manager import clear_dictionaries
def prepare_repo_environment():
""" It clears 'total commit' and 'log' lists. It replaces the 'output' folder
for a new one. After that the environment is ready for a new repository analysis.
"""
clear_dictionaries()
dependencies_manager.total_commit_vulnerabilities.clear()
dependencies_manager.vulnerabilities_log.clear()
dependencies_manager.historial_fixes.clear()
dependencies_manager.removed_vulnerabilities_historial.clear()
directories_manager.new_cloned_git_folder()
def prepare_commit_environment(repo, commit):
""" It clears 'severity records' and 'no duplicated vulnerabilities' lists.
After that it generates the 'first level dependencies tree' to extract the
dependencies presented on the current commit.
:param repo: Repository to prepare.
:type repo: Repo
:param commit: Current commit.
:type commit: Commit
"""
dependencies_manager.severity_records.clear()
dependencies_manager.no_duplicated_vulnerabilities.clear()
directories_manager.first_level_dependencies_tree(repo, commit)
return False
def has_config_file(repo, commit):
""" It sees if Dependabot's configuration is presented on a specific repository and commit.
:param repo: Repository where the commit is.
:type repo: Repo
:param commit: Commit where it is searched the Dependabot's configuration file.
:type commit: Commit
:return It returns 'True' if Dependabot's configuration file is presented. If not it will return 'False'.
:rtype
"""
if directories_manager.download_file(repo, commit.hexsha, ".github/dependabot.yml"):
return True
return False
def print_current_time(state):
""" It prints current time.
:param state: It indicates if it has started o stopped recording.
:type state: str
"""
now = datetime.now()
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
print(state +" recording - " + current_time)
def write_failed_repos_log(repo_id, repo_url):
""" It records ID and url of repositories that were not downloaded properly.
:param repo_id: Repository ID.
:type repo_id: int
:param repo_url: Repository url.
:type repo_url: str
"""
with open('../output/failed_repos.txt', 'a') as f1:
failed_repo = str(repo_id) + "-" + repo_url
f1.write(failed_repo + os.linesep)
error = sys.exc_info()[0]
|
Scripts Manager¶
-
scripts_manager.collect_npm_dependencies()¶ It calls ‘npm_dependencies.sh’ to collect npm dependencies.
-
scripts_manager.collect_yarn_dependencies()¶ It calls ‘yarn_dependencies.sh’ to collect yarn dependencies.
Source code¶
1 2 3 4 5 6 7 8 9 10 | def collect_npm_dependencies():
""" It calls 'npm_dependencies.sh' to collect npm dependencies.
"""
subprocess.call(['sh', '../scripts/npm_dependencies.sh'], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
def collect_yarn_dependencies():
""" It calls 'yarn_dependencies.sh' to collect yarn dependencies.
"""
subprocess.call(['sh', '../scripts/yarn_dependencies.sh'], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
|