Parse the CycloneDX shape Supply-chain SBOM
just walked, build the dependency graph from its dependencies array, and write
find_confusion so it flags an internal-looking name that is already claimable on
the public index — including one that's two hops down, not just a direct dependency. This
mirrors the actual job task: a supply-chain check that only reads the top-level manifest
misses exactly the risk that matters. It runs on your Mac, not in the browser.
src/sec_sbom/sbom.py — load_components(sbom): every component
keyed by its bom-ref, including the root application, with a missing
version defaulting to "".src/sec_sbom/graph.py — build_dependency_graph(sbom): mirror the
dependencies array as {ref: set(dependsOn)}, keeping a leaf's empty
set as a key. reachable_from(graph, root): every ref reachable from
root by any number of hops, excluding root itself.src/sec_sbom/confusion.py — normalize_name(name): fold case and
treat -/_/. as one separator. find_confusion(sbom,
internal_prefixes, public_index): every reachable component whose normalized name
starts with a normalized prefix AND is a key of public_index, sorted by
name.The tests are ordinary pytest and ship in the public folder with the starter — read them first; the names below are the check list. Solutions are not published.
Needs git. uv installs the right Python itself, so nothing else is required.
# once, anywhere on your machine
git clone https://github.com/theDocWho/ai-ml-roadmap.git
cd ai-ml-roadmap
No git? Download the ZIP, unzip it, and cd into the unzipped folder instead.
From the repo root:
# one-time: uv (https://docs.astral.sh/uv/) manages the venv and pins Python ≥ 3.12
cd exercises/sec-sbom && uv sync && uv run pytest -q
Done when uv run pytest -q prints 6 passed. The
untouched starter fails all 6 — every function is ....
test_load_components_reads_root_and_every_declared_componenttest_build_dependency_graph_matches_the_dependencies_arraytest_find_confusion_flags_direct_public_match_and_spares_private_only_nametest_find_confusion_ignores_name_that_merely_contains_the_prefixtest_find_confusion_normalizes_case_and_separators_before_matchingtest_find_confusion_walks_the_full_dependency_graph_not_just_direct_childrenThe last four all run against the same six-component SBOM fixture — the
first is the baseline verdict (a direct public match flagged, an unpublished internal name
spared), and the other three each isolate one way a plausible find_confusion gets
it wrong: a substring match instead of a true prefix, comparing names without normalizing them,
and stopping at the root's direct dependencies instead of walking the whole graph.
This is self-attestation — the site cannot see your terminal, so the box and the button are you telling The Path the suite went green on your machine.
sbom["components"] is flat regardless of
depth; find_confusion must call
reachable_from(build_dependency_graph(sbom), root_ref), not just loop over every
component.internal_prefixes and public_index's keys. Normalizing only one side
is how MyCorp_Telemetry silently stops matching mycorp-telemetry.normalized_name.startswith(prefix), never
prefix in normalized_name. The second over-flags any public package whose name
happens to contain the word anywhere.uv run pytest -x --tb=short stops at the first
failure and prints the assert message, which names the component and the value it computed.