Skip to content

Commit

Permalink
Feat: Add vdr diffing to bom-diff. (#28)
Browse files Browse the repository at this point in the history
Signed-off-by: Caroline Russell <[email protected]>
  • Loading branch information
cerrussell authored Jul 23, 2024
1 parent fccf4a9 commit bf18895
Show file tree
Hide file tree
Showing 7 changed files with 384 additions and 104 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,22 @@ services
- x-trust-boundary

dependencies
- ref
- dependsOn
- ref

vulnerabilities
- advisories
- affects
- bom-ref
- cwes
- description
- detail
- id
- published
- ratings
- references
- source
- updated

The --allow-new-versions option attempts to parse component versions and ascertain if a discrepancy
is attributable to an updated version. Dependency refs and dependents are compared with the version
Expand Down
51 changes: 49 additions & 2 deletions custom_json_diff/bom_diff_template.j2
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
.table_summary2 {
display: grid;
grid-template-columns: 25% 23% 2% 25% 25%;
grid-template-columns: 10% 25% 4% 51% 10%;
}
th, td {
Expand Down Expand Up @@ -96,7 +96,7 @@
<th style="text-align: center; width: 46%">{{ bom_1 }}</th>
<th style="text-align: center; width: 46%">{{ bom_2 }}</th>
</tr>
{% if not (diff_apps_1 or diff_apps_2 or diff_frameworks_1 or diff_frameworks_2 or diff_lib_1 or diff_lib_2 or diff_other_1 or diff_other_2) %}
{% if not (diff_apps_1 or diff_apps_2 or diff_frameworks_1 or diff_frameworks_2 or diff_lib_1 or diff_lib_2 or diff_other_1 or diff_other_2 or diff_vdrs_1 or diff_vdrs_2) %}
<tr>
<td colspan="3" style="text-align: center">No differences found.</td>
</tr>
Expand Down Expand Up @@ -385,6 +385,36 @@
{% endfor %}</td>
</tr>
{% endif %}
{% if diff_vdrs_1 or diff_vdrs_2 %}
<tr>
<th>vulnerabilities<br><br>{{ diff_vdrs_1 | length }}
vs {{ diff_vdrs_2 | length }}</th>
<td>{% for item in diff_vdrs_1 %}
<details>
<summary>{{ item['bom-ref'] }}</summary>
<ul>
{% for key, value in item|items %}
{% if value != "" %}
<li>{{ key }}: {{ value }}</li>
{% endif %}
{% endfor %}
</ul>
</details>
{% endfor %}</td>
<td>{% for item in diff_vdrs_2 %}
<details>
<summary>{{ item['bom-ref'] }}</summary>
<ul>
{% for key, value in item|items %}
{% if value != "" %}
<li>{{ key }}: {{ value }}</li>
{% endif %}
{% endfor %}
</ul>
</details>
{% endfor %}</td>
</tr>
{% endif %}
{% endif %}
</table>
</div>
Expand Down Expand Up @@ -568,6 +598,23 @@
{% endfor %}</td>
</tr>
{% endif %}
{% if common_vdrs %}
<tr>
<th style="width: 8%">vulnerabilities</th>
<td style="width: 41%">{% for item in common_vdrs %}
<details>
<summary>{{ item['bom-ref'] }}</summary>
<ul>
{% for key, value in item|items %}
{% if value != "" %}
<li>{{ key }}: {{ value }}</li>
{% endif %}
{% endfor %}
</ul>
</details>
{% endfor %}</td>
</tr>
{% endif %}
{% endif %}
</table>
</div>
Expand Down
2 changes: 1 addition & 1 deletion custom_json_diff/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def main():
result, j1, j2 = compare_dicts(options)

if args.bom_diff:
result_summary = perform_bom_diff(j1, j2)
result, result_summary = perform_bom_diff(j1, j2)
else:
result_summary = get_diff(j1, j2, options)
report_results(result, result_summary, options, j1, j2)
Expand Down
32 changes: 20 additions & 12 deletions custom_json_diff/custom_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@


def calculate_pcts(diffs: Dict, j1: BomDicts, j2: BomDicts) -> Dict:
j1_counts = j1.generate_counts()
j2_counts = j2.generate_counts()
j1_counts = j1.generate_comp_counts()
j2_counts = j2.generate_comp_counts()
common_counts = generate_counts(diffs["common_summary"])
result = []
for key, value in common_counts.items():
Expand Down Expand Up @@ -73,6 +73,7 @@ def export_html_report(outfile: str, diffs: Dict, j1: BomDicts, j2: BomDicts, op
common_deps=diffs["common_summary"].get("dependencies", []),
common_apps=diffs["common_summary"].get("components", {}).get("applications", []),
common_other=diffs["common_summary"].get("components", {}).get("other_components", []),
common_vdrs=diffs["common_summary"].get("vulnerabilities", []),
diff_lib_1=diffs["diff_summary"].get(options.file_1, {}).get("components", {}).get("libraries", []),
diff_lib_2=diffs["diff_summary"].get(options.file_2, {}).get("components", {}).get("libraries", []),
diff_frameworks_1=diffs["diff_summary"].get(options.file_1, {}).get("components", {}).get("frameworks", []),
Expand All @@ -85,6 +86,8 @@ def export_html_report(outfile: str, diffs: Dict, j1: BomDicts, j2: BomDicts, op
diff_services_2=diffs["diff_summary"].get(options.file_2, {}).get("services", []),
diff_deps_1=diffs["diff_summary"].get(options.file_1, {}).get("dependencies", []),
diff_deps_2=diffs["diff_summary"].get(options.file_2, {}).get("dependencies", []),
diff_vdrs_1=diffs["diff_summary"].get(options.file_1, {}).get("vulnerabilities", []),
diff_vdrs_2=diffs["diff_summary"].get(options.file_2, {}).get("vulnerabilities", []),
bom_1=options.file_1,
bom_2=options.file_2,
stats=stats_summary,
Expand All @@ -109,10 +112,12 @@ def filter_dict(data: Dict, options: Options) -> FlatDicts:

def generate_counts(data: Dict) -> Dict:
return {"libraries": len(data.get("components", {}).get("libraries", [])),
"frameworks": len(data.get("components", {}).get("frameworks", [])),
"applications": len(data.get("components", {}).get("applications", [])),
"other_components": len(data.get("components", {}).get("other_components", [])),
"services": len(data.get("services", [])), "dependencies": len(data.get("dependencies", []))}
"frameworks": len(data.get("components", {}).get("frameworks", [])),
"applications": len(data.get("components", {}).get("applications", [])),
"other_components": len(data.get("components", {}).get("other_components", [])),
"services": len(data.get("services", [])),
"dependencies": len(data.get("dependencies", [])),
"vulnerabilities": len(data.get("vulnerabilities", []))}


def get_diff(j1: FlatDicts, j2: FlatDicts, options: Options) -> Dict:
Expand Down Expand Up @@ -154,13 +159,16 @@ def parse_purls(deps: List[Dict], regex: re.Pattern) -> List[Dict]:
return deps


def perform_bom_diff(bom_1: BomDicts, bom_2: BomDicts) -> Dict:
def perform_bom_diff(bom_1: BomDicts, bom_2: BomDicts) -> Tuple[int, Dict]:
output = (bom_1.intersection(bom_2, "common_summary")).to_summary()
output |= {
"diff_summary": (bom_1 - bom_2).to_summary()
}
output["diff_summary"] |= (bom_2 - bom_1).to_summary()
return output
summary_1 = (bom_1 - bom_2).to_summary()
summary_2 = (bom_2 - bom_1).to_summary()
output["diff_summary"] = summary_1
output["diff_summary"] |= summary_2
status = 0
if summary_1 or summary_2:
status = 1
return status, output


def report_results(status: int, diffs: Dict, options: Options, j1: BomDicts | None = None, j2: BomDicts | None = None) -> None:
Expand Down
Loading

0 comments on commit bf18895

Please sign in to comment.