Skip to content

Commit

Permalink
trivial: Split some more code out of cli.py
Browse files Browse the repository at this point in the history
  • Loading branch information
hughsie committed Dec 10, 2024
1 parent fe475c8 commit a5ea15b
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 232 deletions.
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
"purl.pyi",
"evidence.pyi",
"component.pyi",
"container.pyi",
"container_utils.pyi",
"link.pyi",
"problem.pyi",
"vcs.pyi",
Expand Down
237 changes: 5 additions & 232 deletions uswid/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@
# pylint: disable=wrong-import-position,too-many-locals,too-many-statements,too-many-nested-blocks

from collections import defaultdict
from random import choices, randrange
from datetime import datetime
from typing import Optional, Any, List, Dict, Callable
from typing import Optional, List, Dict, Callable
import argparse
import socket
import json
import os
import sys
import uuid
import string

from importlib import metadata as importlib_metadata
from importlib.metadata import PackageNotFoundError
Expand Down Expand Up @@ -49,6 +46,7 @@
from uswid.format_pe import uSwidFormatPe
from uswid.vcs import uSwidVcs
from uswid.vex_document import uSwidVexDocument
from uswid.container_utils import container_generate, container_roundtrip


def _detect_format(filepath: str) -> Optional[Callable]:
Expand Down Expand Up @@ -184,202 +182,6 @@ def _container_merge_from_filepath(
)


def _roundtrip(container: uSwidContainer, verbose: bool = False) -> None:

# collect for analysis
try:
component: uSwidComponent = container[0]
except IndexError:
print("no default component")
return

# convert to each format and back again
for base in [
uSwidFormatCoswid(),
uSwidFormatIni(),
uSwidFormatCycloneDX(),
uSwidFormatGoswid(),
uSwidFormatPkgconfig(),
uSwidFormatSpdx(),
uSwidFormatSwid(),
uSwidFormatUswid(),
]:

# proxy
base.verbose = verbose

# save
try:
blob: bytes = base.save(container)
except NotImplementedError:
continue

# load
try:
container_new = base.load(blob)
except NotImplementedError:
continue
try:
component_new = container_new[0]
except IndexError:
print(f"no default component for {base.name}")
continue

# compare the old and the new
differences: List[Dict[str, Any]] = []
for key in [
"tag_id",
"tag_version",
"type",
"software_name",
"software_version",
"version_scheme",
"summary",
"product",
"colloquial_version",
"revision",
"edition",
"persistent_id",
"activation_status",
"cpe",
]:
if getattr(component, key) != getattr(component_new, key):
differences.append(
{
"class": "uSwidComponent",
"property": key,
"old": getattr(component, key),
"new": getattr(component_new, key),
}
)

# payloads
for payload in component.payloads:

# check still exists
payload_new = component_new.get_payload_by_name(payload.name)
if not payload_new:
differences.append(
{
"class": "uSwidPayload",
"name": payload.name,
}
)
continue

# check values
for key in [
"name",
"size",
]:
if getattr(payload, key) != getattr(payload_new, key):
differences.append(
{
"class": "uSwidPayload",
"property": key,
"old": getattr(payload, key),
"new": getattr(payload_new, key),
}
)

# entities
for entity in component.entities:

# check still exists
for role in entity.roles:
entity_new = component_new.get_entity_by_role(role)
if not entity_new:
differences.append(
{
"class": "uSwidEntity",
"name": role,
}
)
continue

# check values
for key in [
"name",
"regid",
]:
if getattr(entity, key) != getattr(entity_new, key):
differences.append(
{
"class": "uSwidEntity",
"property": key,
"old": getattr(entity, key),
"new": getattr(entity_new, key),
}
)

# link
for link in component.links:
# check still exists
link_new = component_new.get_link_by_rel(link.rel)
if not link_new:
differences.append(
{
"class": "uSwidLink",
"name": str(link.rel),
}
)
continue

# check values
for key in [
"href",
"rel",
]:
if getattr(link, key) != getattr(link_new, key):
differences.append(
{
"class": "uSwidLink",
"property": key,
"old": getattr(link, key),
"new": getattr(link_new, key),
}
)

# evidence
for evidence in component.evidences:
# check still exists
evidence_new = component_new.get_evidence_by_rel(evidence.rel)
if not evidence_new:
differences.append(
{
"class": "uSwidEvidence",
"name": evidence.rel,
}
)
continue

# check values
for key in [
"date",
"device_id",
]:
if getattr(evidence, key) != getattr(evidence_new, key):
differences.append(
{
"class": "uSwidEvidence",
"property": key,
"old": getattr(evidence, key),
"new": getattr(evidence_new, key),
}
)

# show differences
total: float = 22
print(f"{base.name}: { 100.0 / float(total) * (total - len(differences)):.0f}%")
for dif in differences:
try:
print(
f" - FAILURE {dif['class']}.{dif['property']}: {dif['old']}->{dif['new']}"
)
except KeyError:
print(f" - FAILURE {dif['class']} [{dif['name']}] -> None")


def main():
"""Main entrypoint"""
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -574,38 +376,9 @@ def main():
print("Use uswid --help for command line arguments")
sys.exit(1)

# generate 1000 plausible components, each with:
# - unique tag-id GUID
# - unique software-name of size 4-30 chars
# - colloquial-version from a random selection of 10 SHA-1 hashes
# - edition from a random SHA-1 hash
# - semantic version of size 3-8 chars
# - entity from a random selection of 10 entities
# generate plausible components
if args.generate:
tree_hashes: List[str] = []
entities: List[uSwidEntity] = []
for _ in range(10):
tree_hashes.append("".join(choices("0123456789abcdef", k=40)))
for i in range(10):
entity = uSwidEntity()
entity.name = "Entity#" + str(i)
entity.regid = "com.entity" + str(i)
entity.roles = [uSwidEntityRole.TAG_CREATOR]
entities.append(entity)
for i in range(1000):
component = uSwidComponent()
component.tag_id = str(uuid.uuid4())
component.software_name = "".join(
choices(string.ascii_lowercase, k=randrange(4, 30))
)
component.software_version = "1." + "".join(
choices("123456789", k=randrange(1, 6))
)
component.colloquial_version = tree_hashes[randrange(len(tree_hashes))]
component.edition = "".join(choices("0123456789abcdef", k=40))
component.version_scheme = uSwidVersionScheme.MULTIPARTNUMERIC
component.add_entity(entities[randrange(len(entities))])
container.append(component)
container_generate(container)

# collect data here
for filepath in load_filepaths:
Expand Down Expand Up @@ -704,7 +477,7 @@ def main():

# test the container with different SBOM formats
if args.roundtrip:
_roundtrip(container, verbose=args.verbose)
container_roundtrip(container, verbose=args.verbose)

# add any missing evidence
for component in container:
Expand Down
Loading

0 comments on commit a5ea15b

Please sign in to comment.