Skip to content

Commit

Permalink
levels of urgency
Browse files Browse the repository at this point in the history
  • Loading branch information
kaiobendrauf committed May 14, 2024
1 parent 4c3db77 commit dede9e4
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 47 deletions.
56 changes: 31 additions & 25 deletions configuration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,36 @@
update_to : "latest"
- target : "12"
update_to : "13"
cve_urgency_conditions:
max_baseScore : 10
average_baseScore : 8
max_exploitabilityScore : 10
average_exploitabilityScore : 8
max_impactScore : 10
average_impactScore : 8
number_CVEs : 10
number_actively_exploited_CVEs : 5
fraction_actively_exploited_CVEs : 0.7
formulas:
- comparison : "average"
formula : "baseScore * exploitabilityScore * impactScore"
threshhold : 500
- comparison : "max"
formula : "baseScore * exploitabilityScore * impactScore * is_actively_exploited"
threshhold : 200
- comparison : "sum"
formula : "baseScore * impactScore * is_actively_exploited"
threshhold : 300
- comparison : "n_above"
formula : "baseScore * impactScore * is_actively_exploited"
n : 2
threshhold : 300
cve_urgency_levels:
- cve_urgency_conditions:
max_baseScore : 20
deadline_days: 2
name: "critical"
- cve_urgency_conditions:
max_baseScore : 10
average_baseScore : 8
max_exploitabilityScore : 10
average_exploitabilityScore : 8
max_impactScore : 10
average_impactScore : 8
number_CVEs : 10
number_actively_exploited_CVEs : 5
fraction_actively_exploited_CVEs : 0.7
formulas:
- comparison : "average"
formula : "baseScore * exploitabilityScore * impactScore"
threshhold : 500
- comparison : "max"
formula : "baseScore * exploitabilityScore * impactScore * is_actively_exploited"
threshhold : 200
- comparison : "sum"
formula : "baseScore * impactScore * is_actively_exploited"
threshhold : 300
- comparison : "n_above"
formula : "baseScore * impactScore * is_actively_exploited"
n : 2
threshhold : 300
deadline_days : 7
default_deadline_days : 14
urgent_deadline_days : 7


69 changes: 47 additions & 22 deletions nudge-auto-updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@

HEADERS = {'accept': 'application/json', 'User-Agent': 'nudge-auto-updater/1.0'}
DEFAULT_CONFIG = {
"targets" : [{"target":"default", "update_to":"latest"}],
"cve_urgency_conditions" : { "fraction_actively_exploited_CVEs" : 0.75 },
"targets" : [{"target": "default", "update_to": "latest"}],
"cve_urgency_levels": [{"cve_urgency_conditions": { "fraction_actively_exploited_CVEs": 0.75 }, "deadline_days": 2, "name": "urgent"}],
"default_deadline_days" : 14,
"urgent_deadline_days" : 7
}

# ----------------------------------------
Expand Down Expand Up @@ -363,10 +362,10 @@ def brackets_subformula(match):
# ----------------------------------------
# Check CVE Conditions
# ----------------------------------------
def is_deadline_urgent(conditions, cves_scores, cves):
return check_cve_scores(conditions, cves_scores) or check_cve_numbers(conditions, cves)
def is_deadline_urgent(conditions, cves_scores, cves, name, days):
return check_cve_scores(conditions, cves_scores, name, days) or check_cve_numbers(conditions, cves, name, days)

def check_cve_scores(conditions, cves):
def check_cve_scores(conditions, cves, name, days):
if len(cves) < 1:
return False
for score in ["baseScore", "exploitabilityScore", "impactScore"]:
Expand All @@ -376,14 +375,16 @@ def check_cve_scores(conditions, cves):
l.append(cves[cve][score])
l.sort(reverse=True)
if l[0] >= conditions[f"max_{score}"]:
logging.info(f'CVE urgency condition met! Max {score} of {l[0]} is higher than or euqal to threshhold {conditions[f"max_{score}"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' Max {score} of {l[0]} is higher than or equal to threshhold {conditions[f"max_{score}"]}.')
return True
if f"average_{score}" in conditions:
l = []
for cve in cves:
l.append(cves[cve][score])
if (sum(l) / len(l)) >= conditions[f"average_{score}"]:
logging.info(f'CVE urgency condition met! Average {score} of {(sum(l) / len(l))} is higher or euqal to than threshhold {conditions[f"average_{score}"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' Average {score} of {(sum(l) / len(l))} is higher or equal to than threshhold {conditions[f"average_{score}"]}.')
return True
if "formulas" in conditions:
for formula in conditions["formulas"]:
Expand All @@ -392,38 +393,45 @@ def check_cve_scores(conditions, cves):
l.append(read_formula(formula["formula"], cve, cves[cve]))
if formula["comparison"] == "average":
if (sum(l) / len(l)) >= formula["threshhold"]:
logging.info(f'CVE urgency condition met! CVEs had an average score for formula {formula["formula"]} ({(sum(l) / len(l))}) higher than or euqal to threshold {formula["threshhold"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' CVEs had an average score for formula {formula["formula"]} ({(sum(l) / len(l))}) higher than or equal to threshold {formula["threshhold"]}.')
return True
if formula["comparison"] == "max":
l.sort(reverse=True)
if l[0] >= formula["threshhold"]:
logging.info(f'CVE urgency condition met! CVEs had an max score for formula {formula["formula"]} ({l[0]}) higher than or euqal to threshold {formula["threshhold"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' CVEs had an max score for formula {formula["formula"]} ({l[0]}) higher than or equal to threshold {formula["threshhold"]}.')
return True
if formula["comparison"] == "sum":
if sum(l) >= formula["threshhold"]:
logging.info(f'CVE urgency condition met! CVEs had an summed score for formula {formula["formula"]} ({sum(l)}) higher than or euqal to threshold {formula["threshhold"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' CVEs had an summed score for formula {formula["formula"]} ({sum(l)}) higher than or equal to threshold {formula["threshhold"]}.')
return True
if formula["comparison"] == "n_above":
n = formula["n"]
if len(l) >= n:
l.sort(reverse=True)
if l[n-1] > formula["threshhold"]:
logging.info(f'CVE urgency condition met! At least {n} CVEs had a score for formula {formula["formula"]} higher than or euqal to the threshold {formula["threshhold"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' At least {n} CVEs had a score for formula {formula["formula"]} higher than or equal to the threshold {formula["threshhold"]}.')
return True
return False

def check_cve_numbers(conditions, cves):
def check_cve_numbers(conditions, cves, name, days):
if "number_CVEs" in conditions:
if len(cves) >= conditions["number_CVEs"]:
logging.info(f'CVE urgency condition met! Number of CVEs ({len(cves)}) is higher than or euqal to threshhold {conditions["number_CVEs"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' Number of CVEs ({len(cves)}) is higher than or equal to threshhold {conditions["number_CVEs"]}.')
return True
if "number_actively_exploited_CVEs" in conditions:
if sum(cves.values()) >= conditions[f"number_actively_exploited_CVEs"]:
logging.info(f'CVE urgency condition met! Number of actively exploited CVEs ({sum(l)}) is higher than or euqal to threshhold {conditions["number_actively_exploited_CVEs"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' Number of actively exploited CVEs ({sum(l)}) is higher than or equal to threshhold {conditions["number_actively_exploited_CVEs"]}.')
return True
if "fraction_actively_exploited_CVEs" in conditions:
if (sum(cves.values()) / len(cves)) >= conditions["fraction_actively_exploited_CVEs"]:
logging.info(f'CVE urgency condition met! Fraction of actively exploited CVEs ({(sum(l) / len(l))}) is higher than or euqal to threshold {conditions["fraction_actively_exploited_CVEs"]}.')
logging.info(f'CVE urgency is {name}! Installation will be required in {days} day(s).')
logging.info(f' Fraction of actively exploited CVEs ({(sum(l) / len(l))}) is higher than or equal to threshold {conditions["fraction_actively_exploited_CVEs"]}.')
return True
return False

Expand Down Expand Up @@ -475,7 +483,10 @@ def main():

# check per configuration if it needs to be updates
for target in config["targets"]:
if target["target"] in nudge_requirements:
if not target["target"] in nudge_requirements:
logging.warning(f"No nudge configuration exists for target \"{target['target']}\", despite this target being specified in {config_file_name}.")
logging.warning(f"Skipping \"{target['target']}\"")
else:
# nudge requirement needs to be checked
if target["update_to"] == "latest":
# nudge requirement needs to be checked against latest macOS
Expand All @@ -490,32 +501,46 @@ def main():
is_uptodate = True
for macos_release in latest_macos_releases:
if macos_release < config_version_gt and macos_release > nudge_requirements[target["target"]]["version"]:
logging.info(f"Nudge configuration for target {target['target']} needs to be updated from {nudge_requirements[target['target']]['version']} to {macos_release})")
logging.info(f"Nudge configuration for target \"{target['target']}\"needs to be updated from {nudge_requirements[target['target']]['version']} to {macos_release})")
is_uptodate = False
new_macos_release = macos_release
break
if is_uptodate:
logging.info(f"Nudge configuration for target \"{target['target']}\" is already up to date.")
else:
# nudge is not up to date! Is the new update urgent?
# nudge is not up to date! How urgent is the new update?
# get security metrics
security_release_cves_scores = dict()
security_release_cves = cves[str(new_macos_release)]
for cve in security_release_cves:
cve_scores = get_CVE_scores(cve, security_release_cves[cve], api_key)
if cve_scores:
security_release_cves_scores[cve] = cve_scores
if is_deadline_urgent(config["cve_urgency_conditions"], security_release_cves_scores, security_release_cves):
days = config["urgent_deadline_days"]
else:
# check urgency levels to determine deadline
days = config["default_deadline_days"]
urgency_condition_met = False
for i, cve_urgency_level in enumerate(config["cve_urgency_levels"]):
name = f"level {i}"
if "name" in cve_urgency_level:
name = cve_urgency_level["name"]
if "deadline_days" not in cve_urgency_level:
logging.error(f"Target \"{target['target']}\" is missing value \'deadline_days\'. Please add this value to {config_file_name}")
sys.exit(1)
days = cve_urgency_level["deadline_days"]
if is_deadline_urgent(cve_urgency_level["cve_urgency_conditions"], security_release_cves_scores, security_release_cves, name, days):
urgency_condition_met = True
if not urgency_condition_met:
logging.info("No CVE urgency condition met.")
days = config["default_deadline_days"]
# update target
nudge_file_dict = update_nudge_file_dict(nudge_file_dict, target["target"], new_macos_release, urls[str(new_macos_release)], days)
nudge_file_needs_updating = True
# if nudge dict has changed rewrite it
if nudge_file_needs_updating:
write_nudge_config(nudge_file_name, nudge_file_dict)
logging.info("Nudge configuration updated.")
else:
logging.info("Nudge configuration does not need updating.")

if __name__ == '__main__':
main()

0 comments on commit dede9e4

Please sign in to comment.