{"id":49936826,"url":"https://github.com/orlovevgeny/zig-build-sbom","last_synced_at":"2026-05-17T07:16:03.891Z","repository":{"id":343006514,"uuid":"1175460193","full_name":"OrlovEvgeny/zig-build-sbom","owner":"OrlovEvgeny","description":"Build-time SBOM generator for Zig projects and bare-metal firmware","archived":false,"fork":false,"pushed_at":"2026-03-08T11:53:06.000Z","size":55,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-08T14:52:10.586Z","etag":null,"topics":["compliance","microzig","sbom","security","zig","zig-package"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OrlovEvgeny.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-07T18:36:41.000Z","updated_at":"2026-03-08T11:53:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/OrlovEvgeny/zig-build-sbom","commit_stats":null,"previous_names":["orlovevgeny/zig-build-sbom"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/OrlovEvgeny/zig-build-sbom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzig-build-sbom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzig-build-sbom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzig-build-sbom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzig-build-sbom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OrlovEvgeny","download_url":"https://codeload.github.com/OrlovEvgeny/zig-build-sbom/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzig-build-sbom/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33130364,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T06:27:06.342Z","status":"ssl_error","status_checked_at":"2026-05-17T06:26:59.432Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compliance","microzig","sbom","security","zig","zig-package"],"created_at":"2026-05-17T07:16:02.354Z","updated_at":"2026-05-17T07:16:03.885Z","avatar_url":"https://github.com/OrlovEvgeny.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zig-build-sbom\n\nBuild-time SBOM generator for Zig projects and bare-metal firmware.\n\n[![Zig](https://img.shields.io/badge/Zig-0.15.0+-f7a41d?logo=zig\u0026logoColor=white)](https://ziglang.org)\n[![Release](https://img.shields.io/github/v/release/OrlovEvgeny/zig-build-sbom)](https://github.com/OrlovEvgeny/zig-build-sbom/releases)\n[![Tests](https://github.com/OrlovEvgeny/zig-build-sbom/actions/workflows/ci.yml/badge.svg)](https://github.com/OrlovEvgeny/zig-build-sbom/actions/workflows/ci.yml)\n\nHooks into `std.Build` to extract the full dependency graph at compile time and produces [CycloneDX 1.6](https://cyclonedx.org/docs/1.6/) or [SPDX 2.3](https://spdx.github.io/spdx-spec/v2.3/) SBOMs with zero runtime overhead. Designed for [EU Cyber Resilience Act](https://digital-strategy.ec.europa.eu/en/policies/cyber-resilience-act) compliance in embedded/IoT products.\n\n## Why\n\nExisting SBOM tools (Syft, cdxgen, FOSSA) work by scanning binaries or lockfiles. Zig has neither. Its build system resolves dependencies via content-addressed hashing and compiles everything from source — there is no `package-lock.json` to scrape and no dynamic linker metadata to read.\n\n`zig-build-sbom` reads the build graph directly. Every Zig package, its transitive dependencies, content hashes, and vendored C sources are captured from the same data structures the compiler uses. No guessing, no heuristics for the Zig portion of the graph.\n\nFor MicroZig firmware it also captures hardware context: chip name, CPU architecture, memory regions — information that CycloneDX's `device` component type was designed for but no existing tool fills in.\n\n## Install\n\n```sh\nzig fetch --save git+https://github.com/OrlovEvgeny/zig-build-sbom\n```\n\n## Usage\n\n### `addSbomStep` — generate on demand\n\nAdds an SBOM step reachable via `zig build sbom`:\n\n```zig\nconst std = @import(\"std\");\nconst sbom = @import(\"zig-build-sbom\");\n\npub fn build(b: *std.Build) void {\n    const target = b.standardTargetOptions(.{});\n    const optimize = b.standardOptimizeOption(.{});\n\n    const exe_mod = b.createModule(.{\n        .root_source_file = b.path(\"src/main.zig\"),\n        .target = target,\n        .optimize = optimize,\n    });\n\n    const exe = b.addExecutable(.{\n        .name = \"my-app\",\n        .root_module = exe_mod,\n    });\n    b.installArtifact(exe);\n\n    // Generate SBOM on `zig build sbom`\n    _ = sbom.addSbomStep(b, exe, .{\n        .format = .cyclonedx_json,\n        .output_path = \"sbom.cdx.json\",\n        .version = \"1.0.0\",\n    });\n}\n```\n\n```sh\nzig build sbom\n```\n\n### `addSbomToInstall` — generate on every build\n\n```zig\n_ = sbom.addSbomToInstall(b, exe, .{\n    .format = .cyclonedx_json,\n    .output_path = \"sbom.cdx.json\",\n    .version = \"1.0.0\",\n});\n```\n\n### `getOutput` — chain the SBOM as a build dependency\n\n```zig\nconst sbom_step = sbom.addSbomStep(b, exe, .{ ... });\nconst sbom_output = sbom_step.getOutput();\n// Use sbom_output as a LazyPath dependency for another step.\n```\n\n## Output formats\n\n| Format | Option | File convention |\n|--------|--------|-----------------|\n| CycloneDX 1.6 JSON | `.cyclonedx_json` | `*.cdx.json` |\n| CycloneDX 1.6 XML | `.cyclonedx_xml` | `*.cdx.xml` |\n| SPDX 2.3 JSON | `.spdx_json` | `*.spdx.json` |\n\n## Options\n\n```zig\n_ = sbom.addSbomStep(b, exe, .{\n    // Output format (default: cyclonedx_json)\n    .format = .cyclonedx_json,\n\n    // Output filename in the build cache\n    .output_path = \"sbom.cdx.json\",\n\n    // Version string for the root component\n    .version = \"1.0.0\",\n\n    // Manufacturer metadata (useful for CRA compliance)\n    .manufacturer = .{\n        .name = \"Acme GmbH\",\n        .url = \"https://acme.de\",\n    },\n\n    // Include vendored C sources as components (default: true)\n    .include_c_sources = true,\n\n    // Walk transitive Zig dependencies (default: true)\n    .include_transitive = true,\n\n    // Infer SPDX license IDs from package names (default: true)\n    .infer_licenses = true,\n\n    // Custom properties added to the root component\n    .custom_properties = \u0026.{},\n});\n```\n\n## MicroZig firmware\n\nFor MicroZig projects, use the `microzig` integration. It adds hardware context: chip name, CPU architecture, memory layout, and a CycloneDX `device` component for the MCU.\n\n```zig\nconst std = @import(\"std\");\nconst sbom = @import(\"zig-build-sbom\");\n\npub fn build(b: *std.Build) void {\n    // ... MicroZig setup, `fw` is a MicroZig Firmware value ...\n\n    _ = sbom.microzig.addFirmwareSbomStep(b, fw, .{\n        .base = .{\n            .format = .cyclonedx_json,\n            .output_path = \"sbom.cdx.json\",\n            .version = \"1.0.0\",\n            .manufacturer = .{ .name = \"Acme IoT GmbH\", .url = \"https://acme-iot.de\" },\n        },\n        .include_hardware_component = true,  // adds device component for the MCU\n        .include_memory_layout = true,       // flash/RAM regions in properties\n        .include_cpu_info = true,            // CPU arch and model in properties\n    });\n}\n```\n\nThe firmware's `fw` value is accepted as `anytype` — it must have `.artifact` (`*std.Build.Step.Compile`) and `.target` (with `.chip.name`, `.chip.cpu.arch`, `.chip.cpu.name`, `.chip.memory_regions`). Standard MicroZig `Firmware` satisfies this contract.\n\n### Example output (RP2040 blinky)\n\n```json\n{\n  \"bomFormat\": \"CycloneDX\",\n  \"specVersion\": \"1.6\",\n  \"version\": 1,\n  \"metadata\": {\n    \"component\": {\n      \"type\": \"firmware\",\n      \"bom-ref\": \"blinky\",\n      \"name\": \"blinky\",\n      \"version\": \"1.0.0\",\n      \"properties\": [\n        { \"name\": \"firmware:cpu.arch\", \"value\": \"thumb\" },\n        { \"name\": \"firmware:cpu.model\", \"value\": \"cortex_m0plus\" },\n        { \"name\": \"firmware:chip.name\", \"value\": \"RP2040\" },\n        { \"name\": \"firmware:memory.region.0.type\", \"value\": \"flash\" },\n        { \"name\": \"firmware:memory.region.0.length\", \"value\": \"0x200000\" },\n        { \"name\": \"firmware:memory.region.1.type\", \"value\": \"ram\" },\n        { \"name\": \"firmware:memory.region.1.length\", \"value\": \"0x42000\" }\n      ]\n    },\n    \"manufacturer\": { \"name\": \"Acme IoT GmbH\" }\n  },\n  \"components\": [\n    {\n      \"type\": \"device\",\n      \"bom-ref\": \"device-RP2040\",\n      \"name\": \"RP2040\",\n      \"description\": \"RP2040 (cortex_m0plus core, 2097152 bytes flash)\",\n      \"properties\": [\n        { \"name\": \"cdx:device:type\", \"value\": \"mcu\" },\n        { \"name\": \"firmware:target.arch\", \"value\": \"thumb\" }\n      ]\n    }\n  ],\n  \"compositions\": [\n    { \"aggregate\": \"complete\", \"assemblies\": [\"blinky\", \"device-RP2040\"] }\n  ]\n}\n```\n\n## Vendored C sources\n\nWhen `include_c_sources` is enabled (the default), the traversal inspects `Step.Compile` link objects for C source files. Files are grouped by directory into logical libraries using a built-in table of known projects:\n\n| Library | Detected path pattern |\n|---------|-----------------------|\n| lwIP | `lwip/` |\n| mbedTLS | `mbedtls/` |\n| FreeRTOS | `freertos/`, `FreeRTOS/` |\n| CMSIS | `cmsis/`, `CMSIS/` |\n| FatFs | `fatfs/` |\n| tinycbor | `tinycbor/` |\n| SQLite | `sqlite/` |\n\nC-sourced components are marked with `compositions.aggregate = \"incomplete\"` — the tool cannot guarantee it found every vendored file through path heuristics alone.\n\n## What goes into the SBOM\n\n| Source | Component type | `bom-ref` | Completeness |\n|--------|---------------|-----------|--------------|\n| Root project | `firmware` | project name | `complete` |\n| Zig packages (direct + transitive) | `library` | `pkg_hash` | `complete` |\n| Vendored C sources | `library` | generated ID | `incomplete` |\n| MCU chip (MicroZig) | `device` | `device-{chip}` | `complete` |\n\n`pkg_hash` (Zig's content-addressed package hash) is used as `bom-ref` for all Zig dependencies. Names can collide in diamond dependency graphs; content hashes cannot.\n\n## CRA compliance notes\n\nThe [EU Cyber Resilience Act](https://digital-strategy.ec.europa.eu/en/policies/cyber-resilience-act) requires technical documentation including an SBOM for products with digital elements (enforcement: September 2026 for reporting, December 2027 for full compliance).\n\n`zig-build-sbom` supports CRA workflows:\n\n- `compositions.aggregate` honestly reports `complete` (Zig packages from the build graph) vs. `incomplete` (C sources from heuristics). Auditors can distinguish proven completeness from best-effort.\n- `manufacturer` metadata maps to CRA Article 13 requirements.\n- CycloneDX `device` components capture hardware identity for IoT/embedded products.\n- Every Zig dependency includes its content hash, providing verifiable provenance without external registries.\n\n## How it works\n\nSBOM generation runs in two phases:\n\n1. **Graph extraction** (build step, runs in the Zig build runner): walks `Step.Compile` modules and their transitive imports, reads `build.zig.zon` metadata via `std.zig.Ast`, detects C sources, and writes an intermediate JSON file.\n\n2. **Serialization** (separate executable via `Step.Run`): reads the intermediate JSON, serializes to CycloneDX or SPDX using [serde.zig](https://github.com/OrlovEvgeny/serde.zig) for standards-compliant field naming and structure.\n\nThe two-phase split exists because `serde.zig` (like any normal Zig dependency) cannot be `@import`ed inside the build runner — the build runner resolves `@import(\"serde\")` to the dependency's `build.zig`, not its library module.\n\n## Requirements\n\n- Zig 0.15.0+\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forlovevgeny%2Fzig-build-sbom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Forlovevgeny%2Fzig-build-sbom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forlovevgeny%2Fzig-build-sbom/lists"}