-
Notifications
You must be signed in to change notification settings - Fork 97
LCORE-2631: Vulnerability report script: Dependabot data processing part #1961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,9 +5,39 @@ | |
| Rretrieves Dependabot issues, analyses issues, generates graphs, and generates | ||
| HTML page with Vulnerability report. Is is also possible to compare two | ||
| repositories. | ||
|
|
||
| # Usage: | ||
| usage: vulnerability_report.py [-h] [-v] --organization ORGANIZATION | ||
| --repository REPOSITORY [-r] [-g] [-p] | ||
| [-c COMPARISON [COMPARISON ...]] | ||
|
|
||
| Vulnerability report tool | ||
|
|
||
| options: | ||
| -h, --help show this help message and exit | ||
| -v, --verbose make it verbose | ||
| --organization ORGANIZATION | ||
| GitHub organization. | ||
| --repository REPOSITORY | ||
| GitHub repository. | ||
| -r, --retrieve-issues | ||
| Retrieve issues | ||
| -g, --generate-graphs | ||
| Generate graphs with vulnerabilities info | ||
| -p, --generate-page Generate page with vulnerabilities info | ||
| -c, --comparison COMPARISON [COMPARISON ...] | ||
| Compare two or more repositories and generate | ||
| comparison report. Multiple JSON files with Dependabot | ||
| alerts needs to be provided | ||
| """ | ||
|
|
||
| from argparse import ArgumentParser | ||
| from argparse import ArgumentParser, Namespace | ||
| import json | ||
|
|
||
| from typing import Any | ||
|
|
||
| type DependabotAlert = dict[str, Any] | ||
| type DependabotAlerts = list[DependabotAlert] | ||
|
|
||
|
|
||
| def create_argument_parser() -> ArgumentParser: | ||
|
|
@@ -46,35 +76,107 @@ def create_argument_parser() -> ArgumentParser: | |
| parser.add_argument( | ||
| "-r", | ||
| "--retrieve-issues", | ||
| default=True, | ||
| action="store_true", | ||
| default=False, | ||
| help="Retrieve issues", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-g", | ||
| "--generate-graphs", | ||
| default=True, | ||
| action="store_true", | ||
| default=False, | ||
| help="Generate graphs with vulnerabilities info", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-p", | ||
| "--generate-page", | ||
| default=True, | ||
| action="store_true", | ||
| default=False, | ||
| help="Generate page with vulnerabilities info", | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| "-c", | ||
| "--comparison", | ||
| default=False, | ||
| help="Compare two repositories and generate comparison report. " | ||
| "Need to be used with --data1 and --data2 options", | ||
| required=False, | ||
| nargs="+", | ||
| default=[], | ||
| help="Compare two or more repositories and generate comparison report. " | ||
| "Multiple JSON files with Dependabot alerts needs to be provided", | ||
| ) | ||
|
|
||
| return parser | ||
|
|
||
|
|
||
| def dependabot_file_name(args: Namespace) -> str: | ||
| """Construct file name containing Dependabot alerts.""" | ||
| return f"{args.organization}__{args.repository}.json" | ||
|
|
||
|
|
||
| def load_dependabot_file(filename: str) -> Any: | ||
| """Load JSON file containing Dependabot alerts.""" | ||
| with open(filename, "r") as fin: | ||
| return json.load(fin) | ||
|
|
||
|
|
||
| def has_attribute_with_value(item: DependabotAlert, attribute: str, value: str) -> bool: | ||
| """Check if dictionary has attribute with given value.""" | ||
| return bool(item[attribute] == value) | ||
|
|
||
|
|
||
| def has_deep_attribute_with_value( | ||
| item: DependabotAlert, selector: str, attribute: str, value: str | ||
| ) -> bool: | ||
| """Check if dictionary has deep attribute with given value.""" | ||
| return bool(item[selector][attribute] == value) | ||
|
Comment on lines
+118
to
+133
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate Dependabot JSON schema at load time to prevent runtime crashes. Helpers assume keys always exist ( Proposed fix def load_dependabot_file(filename: str) -> Any:
"""Load JSON file containing Dependabot alerts."""
- with open(filename, "r") as fin:
- return json.load(fin)
+ with open(filename, "r", encoding="utf-8") as fin:
+ data = json.load(fin)
+ if not isinstance(data, list):
+ raise ValueError("Dependabot alerts file must contain a JSON array")
+ for idx, item in enumerate(data):
+ if not isinstance(item, dict):
+ raise ValueError(f"Alert at index {idx} must be an object")
+ if "state" not in item:
+ raise ValueError(f"Alert at index {idx} is missing 'state'")
+ sa = item.get("security_advisory")
+ if not isinstance(sa, dict) or "severity" not in sa:
+ raise ValueError(
+ f"Alert at index {idx} is missing 'security_advisory.severity'"
+ )
+ return dataAlso applies to: 136-155 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| def count_attribute_with_value( | ||
| items: DependabotAlerts, attribute: str, value: str | ||
| ) -> int: | ||
| """Count all attributes with given value.""" | ||
| cnt: int = 0 | ||
| for item in items: | ||
| if has_attribute_with_value(item, attribute, value): | ||
| cnt += 1 | ||
| return cnt | ||
|
|
||
|
|
||
| def count_deep_attribute_with_value( | ||
| items: DependabotAlerts, selector: str, attribute: str, value: str | ||
| ) -> int: | ||
| """Count all deep attributes with given value.""" | ||
| cnt: int = 0 | ||
| for item in items: | ||
| if has_deep_attribute_with_value(item, selector, attribute, value): | ||
| cnt += 1 | ||
| return cnt | ||
|
|
||
|
|
||
| def opened_cves(source_data: DependabotAlerts) -> int: | ||
| """Compute how many CVEs are opened.""" | ||
| return count_attribute_with_value(source_data, "state", "open") | ||
|
|
||
|
|
||
| def fixed_cves(source_data: DependabotAlerts) -> int: | ||
| """Compute how many CVEs has been fixed opened.""" | ||
| return count_attribute_with_value(source_data, "state", "fixed") | ||
|
|
||
|
|
||
| def with_severity(severity: str, source_data: DependabotAlerts) -> int: | ||
| """Count number of CVE having specified severity.""" | ||
| return count_deep_attribute_with_value( | ||
| source_data, "security_advisory", "severity", severity | ||
| ) | ||
|
|
||
|
|
||
| def process_dependabot_file(dependabot_file: str, prefix: str) -> dict[str, Any]: | ||
| """Read Dependabot alerts and prepare statistic info.""" | ||
| return {} | ||
|
Comment on lines
+175
to
+177
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Proposed fix def process_dependabot_file(dependabot_file: str, prefix: str) -> dict[str, Any]:
"""Read Dependabot alerts and prepare statistic info."""
- return {}
+ source_data: DependabotAlerts = load_dependabot_file(dependabot_file)
+ return {
+ "prefix": prefix,
+ "opened_cves": opened_cves(source_data),
+ "fixed_cves": fixed_cves(source_data),
+ "critical": with_severity("critical", source_data),
+ "high": with_severity("high", source_data),
+ "medium": with_severity("medium", source_data),
+ "low": with_severity("low", source_data),
+ }
@@
- process_dependabot_file(dependabot_file, prefix)
+ report = process_dependabot_file(dependabot_file, prefix)
+ print(json.dumps(report))
return 0Also applies to: 199-201 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| def main() -> int: | ||
| """ | ||
| CLI entry point that retrieves Dependabot issues and produces Vulnerability report. | ||
|
|
@@ -94,8 +196,9 @@ def main() -> int: | |
| """ | ||
| parser = create_argument_parser() | ||
| args = parser.parse_args() | ||
|
|
||
| print(args) | ||
| dependabot_file = dependabot_file_name(args) | ||
| prefix = args.repository | ||
| process_dependabot_file(dependabot_file, prefix) | ||
| return 0 | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--comparisonis documented/parsed but never used in execution flow.The parser advertises comparison mode, but
main()always processes onlyorganization__repository.json. This is a contract break for the CLI option.Also applies to: 199-201
🤖 Prompt for AI Agents