From 970d26231450975ba7b8767c5a2ce8048807cf5e Mon Sep 17 00:00:00 2001 From: phi-go Date: Mon, 17 Feb 2025 15:22:53 +0100 Subject: [PATCH] per fuzzer coverage plots in overview Signed-off-by: phi-go --- .../app/static/assets/db/oss_fuzz.py | 16 +++++++ .../assets/db/web_db_creator_from_summary.py | 26 ++++++++-- .../app/webapp/__init__.py | 1 + .../app/webapp/models.py | 2 + .../app/webapp/templates/project-profile.html | 48 +++++++++++++++++++ 5 files changed, 88 insertions(+), 5 deletions(-) diff --git a/tools/web-fuzzing-introspection/app/static/assets/db/oss_fuzz.py b/tools/web-fuzzing-introspection/app/static/assets/db/oss_fuzz.py index 56389b31d..fc6e7f7e7 100644 --- a/tools/web-fuzzing-introspection/app/static/assets/db/oss_fuzz.py +++ b/tools/web-fuzzing-introspection/app/static/assets/db/oss_fuzz.py @@ -153,6 +153,10 @@ def get_code_coverage_summary_url(project_name, datestr): project_url = base_url.format(project_name, datestr) return project_url +def get_fuzzer_code_coverage_summary_url(project_name, datestr, fuzzer): + base_url = 'https://storage.googleapis.com/oss-fuzz-coverage/{0}/reports-by-target/{1}/{2}/linux/summary.json' + project_url = base_url.format(project_name, datestr, fuzzer) + return project_url def get_coverage_report_url(project_name, datestr, language): if language == 'java' or language == 'python' or language == 'go': @@ -255,6 +259,18 @@ def get_code_coverage_summary(project_name, datestr): except: return None +def get_fuzzer_code_coverage_summary(project_name, datestr, fuzzer): + cov_summary_url = get_fuzzer_code_coverage_summary_url(project_name, datestr, fuzzer) + try: + coverage_summary_raw = requests.get(cov_summary_url, timeout=20).text + except: + return None + try: + json_dict = json.loads(coverage_summary_raw) + return json_dict + except: + return None + def extract_new_introspector_functions(project_name, date_str): introspector_functions_url = get_introspector_report_url_all_functions( diff --git a/tools/web-fuzzing-introspection/app/static/assets/db/web_db_creator_from_summary.py b/tools/web-fuzzing-introspection/app/static/assets/db/web_db_creator_from_summary.py index 2bf842eae..c71dd70c5 100644 --- a/tools/web-fuzzing-introspection/app/static/assets/db/web_db_creator_from_summary.py +++ b/tools/web-fuzzing-introspection/app/static/assets/db/web_db_creator_from_summary.py @@ -283,9 +283,8 @@ def extract_and_refine_functions(all_function_list, date_str): return refined_proj_list -def extract_code_coverage_data(code_coverage_summary, project_name, date_str, - project_language) -> Optional[Dict[str, Any]]: - """Gets coverage URL and line coverage total of a project""" +def extract_code_coverage_data(code_coverage_summary): + """Extract the coverage data from a loaded coverage summary.json""" # Extract data from the code coverage reports if code_coverage_summary is None: return None @@ -307,6 +306,14 @@ def extract_code_coverage_data(code_coverage_summary, project_name, date_str, except: pass + return line_total_summary + + +def prepare_code_coverage_dict(code_coverage_summary, project_name: str, date_str: str, + project_language: str) -> Optional[Dict[str, Any]]: + """Gets coverage URL and line coverage total of a project""" + line_total_summary = extract_code_coverage_data(code_coverage_summary) + coverage_url = oss_fuzz.get_coverage_report_url(project_name, date_str.replace("-", ""), project_language) @@ -442,7 +449,7 @@ def extract_local_project_data(project_name, oss_fuzz_path, project_name } - code_coverage_data_dict = extract_code_coverage_data( + code_coverage_data_dict = prepare_code_coverage_dict( code_coverage_summary, project_name, '', project_language) if cov_fuzz_stats is not None: @@ -704,20 +711,29 @@ def extract_project_data(project_name, date_str, should_include_details, 'project_name': project_name } - code_coverage_data_dict = extract_code_coverage_data( + code_coverage_data_dict = prepare_code_coverage_dict( code_coverage_summary, project_name, date_str, project_language) + per_fuzzer_cov = {} if cov_fuzz_stats is not None: all_fuzzers = cov_fuzz_stats.split("\n") if all_fuzzers[-1] == '': all_fuzzers = all_fuzzers[0:-1] amount_of_fuzzers = len(all_fuzzers) + for ff in all_fuzzers: + try: + fuzzer_cov = oss_fuzz.get_fuzzer_code_coverage_summary(project_name, date_str.replace("-", ""), ff) + fuzzer_cov_data = extract_code_coverage_data(fuzzer_cov) + per_fuzzer_cov[ff] = fuzzer_cov_data + except: + pass project_timestamp = { "project_name": project_name, "date": date_str, 'language': project_language, 'coverage-data': code_coverage_data_dict, + 'per-fuzzer-coverage-data': per_fuzzer_cov, 'introspector-data': introspector_data_dict, 'fuzzer-count': amount_of_fuzzers, 'project_repository': project_repository, diff --git a/tools/web-fuzzing-introspection/app/webapp/__init__.py b/tools/web-fuzzing-introspection/app/webapp/__init__.py index e72b5d33e..e0d9aa630 100644 --- a/tools/web-fuzzing-introspection/app/webapp/__init__.py +++ b/tools/web-fuzzing-introspection/app/webapp/__init__.py @@ -60,6 +60,7 @@ def load_db() -> None: project_name=project_timestamp['project_name'], language=project_timestamp['language'], coverage_data=project_timestamp['coverage-data'], + per_fuzzer_coverage_data=project_timestamp.get('per-fuzzer-coverage-data', None), introspector_data=project_timestamp['introspector-data'], fuzzer_count=project_timestamp['fuzzer-count'], introspector_url=project_timestamp.get('introspector_url', diff --git a/tools/web-fuzzing-introspection/app/webapp/models.py b/tools/web-fuzzing-introspection/app/webapp/models.py index bc326e5b0..3bf178449 100644 --- a/tools/web-fuzzing-introspection/app/webapp/models.py +++ b/tools/web-fuzzing-introspection/app/webapp/models.py @@ -78,6 +78,7 @@ def __init__(self, date: str, language: str, coverage_data: Optional[Dict[str, Any]], + per_fuzzer_coverage_data: Optional[Dict[str, Dict[str, Any]]], introspector_data: Optional[Dict[str, Any]], fuzzer_count: int, introspector_url: Optional[str] = None, @@ -88,6 +89,7 @@ def __init__(self, self.date = date self.language = language self.coverage_data = coverage_data + self.per_fuzzer_coverage_data = per_fuzzer_coverage_data self.introspector_data = introspector_data self.fuzzer_count = fuzzer_count self.introspector_url = introspector_url diff --git a/tools/web-fuzzing-introspection/app/webapp/templates/project-profile.html b/tools/web-fuzzing-introspection/app/webapp/templates/project-profile.html index 230fea54c..e75c24183 100644 --- a/tools/web-fuzzing-introspection/app/webapp/templates/project-profile.html +++ b/tools/web-fuzzing-introspection/app/webapp/templates/project-profile.html @@ -162,6 +162,9 @@

Historical progression

+ +

Per fuzzer progression

+
{% else %} @@ -255,6 +258,7 @@

const code_coverage_functions_y = []; const code_reachability_y = []; const fuzzer_count_y = []; +const fuzzers = new Map(); max_fuzzer_count = 0; @@ -270,6 +274,17 @@

code_coverage_functions_y.push({{project_timestamp.introspector_data.functions_covered_estimate}}); code_reachability_y.push({{project_timestamp.introspector_data.static_reachability}}); {% endif %} + + {% if project_timestamp.per_fuzzer_coverage_data != None %} + {% for (fuzzer, cov_data) in project_timestamp.per_fuzzer_coverage_data.items() %} + if (fuzzers.get("{{fuzzer}}") === undefined) { + fuzzers.set("{{fuzzer}}", {x: [], y: []}); + } + var vv = fuzzers.get("{{fuzzer}}"); + vv.x.push("{{project_timestamp.date}}"); + vv.y.push({{cov_data.percent}}); + {%endfor%} + {% endif %} {% endif %} {%endfor%} @@ -287,6 +302,39 @@

}; Plotly.newPlot("codeCoverageLinesOverTimePlot", code_coverage_lines_data, code_coverage_lines_layout); +const progress_graph_div = document.getElementById("progress_graphs") + +for (const [fuzzer_name, coverage_percentage] of fuzzers) { + const per_fuzzer_id = 'perFuzzerCoverageLinesOverTimePlot' + fuzzer_name; + + const sg_div = document.createElement('div'); + sg_div.classList.add('single__graph'); + progress_graph_div.appendChild(sg_div); + + const gc_div = document.createElement('div'); + gc_div.classList.add('graph__chart') + sg_div.appendChild(gc_div); + + const the_div = document.createElement('div') + the_div.id = per_fuzzer_id + the_div.style = "width:100%;max-width:500px" + gc_div.appendChild(the_div); + + // Plot for fuzzer counter over time + const per_fuzzer_code_coverage_lines_data = [{ + x: coverage_percentage.x, + y: coverage_percentage.y, + mode:"lines" + }]; + const per_fuzzer_code_coverage_lines_layout = { + xaxis: {title: "Date"}, + yaxis: {title: "Coverage", range: [0.0, 100.0]}, + title: "Code Coverage (lines) %
" + fuzzer_name, + type: "scatter" + }; + Plotly.newPlot(per_fuzzer_id, per_fuzzer_code_coverage_lines_data, per_fuzzer_code_coverage_lines_layout); +} + // Plot for fuzzer counter over time const fuzzer_count_data = [{ x: code_coverage_lines_x,