security-workspace.sh
flowchart TD gate_esp --> hdr
API
Fhave()
Test whether a command is available in PATH.
gate_pins, gate_sbom, gate_vulnsFhdr()
Print a section header with leading newline, bold text, and color reset.
gate_esp, gate_pins, gate_sbom, gate_vulnsFmissing()
Print a missing-tool error and return failure; a gate that cannot run does not pass.
gate_pins, gate_sbom, gate_vulnsFneed_workspace()
The workspace guard is per-check, not global: esp reads idf_component.yml out of the tracked
tree and has nothing to do with a bootstrap, so it belongs in the fast lane and must not be
blocked by a missing workspace/. The three that genuinely scan the fetched tree call this.
gate_pins, gate_sbom, gate_vulnsFgate_pins()
---- pins ------------------------------------------------------------------
west.yml pins the Nordic add-on to a full SHA, which is correct and is where the reproducibility
claim in its header comes from. But the add-on's OWN manifest is what pins sdk-nrf, Zephyr and
every module under it, and none of that is reviewable from this tree — import: true means the
real dependency set is whatever that commit's manifest said. So it is read back from the
resolved workspace instead, where it is a fact rather than a promise.
A revision that resolves to a branch name is the finding that matters: it means west update
on a different day produces a different tree from the same repository state, and every other
gate in this repo is reasoning about the wrong bytes.
run_one · calls have, hdr, missing, need_workspaceFgate_esp()
---- esp -------------------------------------------------------------------
The ESP component registry is a second package manager nothing in this repo audits. deps reads
bun.lock and the Home Assistant pyproject; idf_component.yml is read by neither, and its default
spec form is a RANGE — version: "~1.0" resolves at build time, on the runner, from a registry.
run_one · calls hdrFgate_sbom()
---- sbom ------------------------------------------------------------------ The one that TOOLING.md rejected, run in the only place it means anything. Both roots are scanned in one syft invocation so the output is a single document: an SBOM split across two files is one nobody consumes.
run_one · calls have, hdr, missing, need_workspaceFgate_vulns()
---- vulns -----------------------------------------------------------------
run_one · calls have, hdr, missing, need_workspaceFrun_one()
---- dispatch --------------------------------------------------------------
gate_esp, gate_pins, gate_sbom, gate_vulns