Skip to content

Commit

Permalink
Merge branch 'main' into release/6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
RobPasMue committed May 7, 2024
2 parents eb5df84 + cd8e715 commit 65a06bc
Show file tree
Hide file tree
Showing 4 changed files with 395 additions and 293 deletions.
296 changes: 3 additions & 293 deletions check-vulnerabilities/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ runs:
shell: bash
run: |
python -m pip install --upgrade pip
pip install "pygithub>=1.59,<2" "bandit>=1.7,<2" "safety>=2.3,<4"
pip install -r ${{ github.action_path }}/requirements.txt
- name: "Install library"
shell: bash
Expand All @@ -219,303 +219,13 @@ runs:
safety check -o bare --save-json info_safety.json --continue-on-error $ignored_vulnerabilities
bandit -r ${{ inputs.source-directory }} -o info_bandit.json -f json --exit-zero
- name: "Declare Python script"
shell: bash
run: |
cat > dependency-check.py << 'EOF'
"""
Security vulnerabilities script.
Notes
-----
Script for detecting vulnerabilities on a given repo and creating
associated security vulnerability advisories.
"""
import hashlib
import json
import os
import sys
from typing import Any, Dict
import github
TOKEN = os.environ.get("DEPENDENCY_CHECK_TOKEN", None)
PACKAGE = os.environ.get("DEPENDENCY_CHECK_PACKAGE_NAME", None)
REPOSITORY = os.environ.get("DEPENDENCY_CHECK_REPOSITORY", None)
DRY_RUN = True if os.environ.get("DEPENDENCY_CHECK_DRY_RUN", None) else False
ERROR_IF_NEW_ADVISORY = True if os.environ.get("DEPENDENCY_CHECK_ERROR_EXIT", None) else False
CREATE_ISSUES = True if os.environ.get("DEPENDENCY_CHECK_CREATE_ISSUES", None) else False
def dict_hash(dictionary: Dict[str, Any]) -> str:
"""MD5 hash of a dictionary."""
dhash = hashlib.md5()
# We need to sort arguments so {'a': 1, 'b': 2} is
# the same as {'b': 2, 'a': 1}
encoded = json.dumps(dictionary, sort_keys=True).encode()
dhash.update(encoded)
return dhash.hexdigest()
def check_vulnerabilities():
"""Check library and third-party vulnerabilities."""
new_advisory_detected = False
# Check that the needed environment variables are provided
if any([v is None for v in [TOKEN, REPOSITORY, PACKAGE]]):
raise RuntimeError(
"Required environment variables are not defined. Enter value for ",
"'DEPENDENCY_CHECK_TOKEN', 'DEPENDENCY_CHECK_PACKAGE_NAME', ",
"'DEPENDENCY_CHECK_REPOSITORY'.",
)
# Check if DRY_RUN or not
if DRY_RUN:
print("Dry run... not creating advisories and issues.")
print("Information will be presented on screen.\n")
# Load the security checks
safety_results = {}
with open("info_safety.json", "r") as json_file:
safety_results = json.loads(json_file.read())
# If the security checks have not been loaded... problem ahead!
if not safety_results:
raise RuntimeError(
"Safety results have not been generated... Something went wrong during",
"the execution of 'safety check -o bare --save-json info_safety.json'. ",
"Verify workflow logs.",
)
# Connect to the repository
g = github.Github(auth=github.Auth.Token(TOKEN))
# Get the repository
repo = g.get_repo(REPOSITORY)
# Get the available security advisories
existing_advisories = {}
pl_advisories = repo.get_repository_advisories()
for advisory in pl_advisories:
existing_advisories[advisory.summary] = advisory
###############################################################################
# THIRD PARTY SECURITY ADVISORIES
###############################################################################
# Process the detected advisories by Safety
safety_results_reported = 0
vulnerability: dict
for vulnerability in safety_results["vulnerabilities"]:
# Retrieve the needed values
v_id = vulnerability.get("vulnerability_id")
v_package = vulnerability.get("package_name")
v_cve = vulnerability.get("CVE")
v_url = vulnerability.get("more_info_url")
v_desc = vulnerability.get("advisory")
v_affected_versions = vulnerability.get("vulnerable_spec")
v_fixed_versions = vulnerability.get("fixed_versions")
# Advisory info
summary = f"Safety vulnerability {v_id} for package '{v_package}'"
vuln_adv = {
"package": {"name": f"{v_package}", "ecosystem": "pip"},
"vulnerable_version_range": f"{v_affected_versions}",
"patched_versions": f"{v_fixed_versions}",
"vulnerable_functions": [],
}
desc = f"""
{v_desc}
#### More information
Visit {v_url} to find out more information.
"""
# Check if the advisory already exists
if existing_advisories.get(summary):
continue
elif not DRY_RUN:
# New safety advisory detected
safety_results_reported += 1
new_advisory_detected = True
# Create the advisory but do not publish it
advisory = repo.create_repository_advisory(
summary=summary,
description=desc,
severity_or_cvss_vector_string="medium",
cve_id=v_cve,
vulnerabilities=[vuln_adv],
)
# Create an issue
if CREATE_ISSUES:
issue_body = f"""
A new security advisory was open in this repository. See {advisory.html_url}.
---
**NOTE**
Please update the security advisory status after evaluating. Publish the advisory
once it has been verified (since it has been created in draft mode).
---
#### Description
{desc}
"""
repo.create_issue(title=summary, body=issue_body, labels=["security"])
else:
# New safety advisory detected
safety_results_reported += 1
new_advisory_detected = True
print("===========================================\n")
print(f"{summary}")
print(f"{desc}")
###############################################################################
# LIBRARY SECURITY ADVISORIES
###############################################################################
# Load the bandit checks
bandit_results = {}
with open("info_bandit.json", "r") as json_file:
bandit_results = json.loads(json_file.read())
# If the bandit results have not been loaded... problem ahead!
if not bandit_results:
raise RuntimeError(
"Bandit results have not been generated... Something went wrong during",
"the execution of 'bandit -r <source-directory> -o info_bandit.json -f json'. ",
"Verify workflow logs.",
)
# Process the detected advisories by Bandit
bandit_results_reported = 0
vulnerability: dict
for vulnerability in bandit_results["results"]:
# Retrieve the needed values
v_hash = dict_hash(vulnerability)
v_test_id = vulnerability.get("test_id")
v_test_name = vulnerability.get("test_name")
v_severity_level = vulnerability.get("issue_severity", "medium").lower()
v_filename = vulnerability.get("filename")
v_code = vulnerability.get("code")
v_package = PACKAGE
v_cwe = vulnerability.get("issue_cwe", {"id": "", "link": ""})
v_url = vulnerability.get("more_info")
v_desc = vulnerability.get("issue_text")
# Advisory info
summary = f"Bandit [{v_test_id}:{v_test_name}] on {v_filename} - Hash: {v_hash}"
vuln_adv = {
"package": {"name": f"{v_package}", "ecosystem": "pip"},
"vulnerable_functions": [],
"vulnerable_version_range": None,
"patched_versions": None,
}
desc = f"""
{v_desc}
#### Code
On file {v_filename}:
```
{v_code}
```
#### CWE - {v_cwe['id']}
For more information see {v_cwe['link']}
#### More information
Visit {v_url} to find out more information.
"""
# Check if the advisory already exists
if existing_advisories.get(summary):
continue
elif not DRY_RUN:
# New bandit advisory detected
bandit_results_reported += 1
new_advisory_detected = True
# Create the advisory but do not publish it
advisory = repo.create_repository_advisory(
summary=summary,
description=desc,
severity_or_cvss_vector_string=v_severity_level,
vulnerabilities=[vuln_adv],
cwe_ids=[f"CWE-{v_cwe['id']}"],
)
# Create an issue
if CREATE_ISSUES:
issue_body = f"""
A new security advisory was open in this repository. See {advisory.html_url}.
---
**NOTE**
Please update the security advisory status after evaluating. Publish the advisory
once it has been verified (since it has been created in draft mode).
---
#### Description
{desc}
"""
repo.create_issue(title=summary, body=issue_body, labels=["security"])
else:
# New bandit advisory detected
bandit_results_reported += 1
new_advisory_detected = True
print("===========================================\n")
print(f"{summary}")
print(f"{desc}")
# Print out information
safety_entries = len(safety_results["vulnerabilities"])
bandit_entries = len(bandit_results["results"])
print("\n*******************************************")
print(f"Total 'safety' advisories detected: {safety_entries}")
print(f"Total 'safety' advisories reported: {safety_results_reported}")
print(f"Total 'bandit' advisories detected: {bandit_entries}")
print(f"Total 'bandit' advisories reported: {bandit_results_reported}")
print("*******************************************")
print(f"Total advisories detected: {safety_entries + bandit_entries}")
print(f"Total advisories reported: {safety_results_reported + bandit_results_reported}")
print("*******************************************")
# Return whether new advisories have been created or not
return new_advisory_detected
if __name__ == "__main__":
new_advisory_detected = check_vulnerabilities()
if new_advisory_detected and ERROR_IF_NEW_ADVISORY:
# New advisories detected - exit with error
sys.exit(1)
else:
# No new advisories detected or no failure requested
pass
EOF
cat dependency-check.py
- name: "Run safety advisory checks"
shell: bash
run: |
if [[ ${{ inputs.hide-log }} == 'true' ]]; then
python dependency-check.py > /dev/null 2>&1
python ${{ github.action_path }}/check_vulnerabilities.py > /dev/null 2>&1
else
python dependency-check.py
python ${{ github.action_path }}/check_vulnerabilities.py
fi
- name: "Uploading safety and bandit results"
Expand Down
Loading

0 comments on commit 65a06bc

Please sign in to comment.