Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

SBOM: Generate a Software Bill of Materials

By Kokil Thapa | Last reviewed: September 2026

You ship code weekly, but can you list every library inside your production build in under five minutes? That gap is exactly why teams now need to SBOM: Generate a Software Bill of Materials as a standard release artefact. An SBOM is a machine-readable inventory of components, versions, licences, and relationships in your application stack. Regulators, enterprise buyers, and security teams increasingly expect one before they trust your software. This guide walks through practical generation for Laravel and PHP stacks you already run in production.

What Is an SBOM and Why Should You Generate a Software Bill of Materials?

An SBOM is structured metadata about what your software contains. Think of it as a parts list for code. Each entry names a component, its version, its supplier, and often its licence. When a CVE hits a library, you search the SBOM instead of guessing which servers run the vulnerable package.

The U.S. National Telecommunications and Information Administration defines minimum SBOM elements: supplier name, component name, version, other unique identifiers, dependency relationship, author, and timestamp. You do not need a custom spreadsheet. Standard formats like SPDX and CycloneDX encode those fields so scanners and auditors can consume them automatically.

On real client projects I maintain, SBOM output lives beside the build artefact. When a client asks for supply-chain documentation, you hand over a file generated from the same commit that went live. That beats reconstructing dependencies from memory two months later.

SBOM Component InventoryYour ApplicationLaravel 13 / PHP 8.5Composervendor packagesnpm / Vitefrontend depsOS / RuntimePHP-FPM, libsSBOM Output FileSPDX JSON or CycloneDX JSONOne file lists every component for security and compliance audits
SBOM software bill of materials maps application code to every Composer, npm, and runtime dependency

Supply-chain attacks moved SBOM from niche to normal. The NTIA minimum elements guide remains the baseline reference most procurement teams cite. If you sell B2B software or maintain portals for regulated sectors, expect SBOM requests in RFPs.

Who needs an SBOM today

  • Teams shipping Laravel or WordPress apps with dozens of Composer or plugin dependencies
  • Agencies delivering enterprise application development to clients with audit requirements
  • DevOps engineers running CI pipelines that already lint and test each deploy
  • eCommerce platforms where a vulnerable payment or cart library creates direct business risk

How Do You Generate an SBOM for a Laravel or PHP Application?

PHP projects expose dependencies through composer.lock. That lock file is your ground truth. Never generate an SBOM from composer.json alone — it lists version ranges, not pinned releases. The same rule applies to Node: use package-lock.json, not bare package.json.

Start with CycloneDX tooling. The official CLI reads lock files and emits standard JSON. Install it globally or run it in CI with a pinned version.

# Install CycloneDX CLI (Node 26 LTS)
npm install -g @cyclonedx/cyclonedx-npm @cyclonedx/cyclonedx-php-composer

# Generate SBOM from Composer lock
cyclonedx-php-composer make-sbom \
  --output-file=sbom-composer.json \
  --output-format=JSON

# Generate SBOM from npm lock (Vite 8.x frontend)
cyclonedx-npm --output-file sbom-npm.json --output-format JSON

For SPDX output, use syft from Anchore. It scans directories, container images, or lock files and supports multiple formats.

# Install syft (check latest release tag on GitHub)
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# Scan project root — picks up composer.lock and package-lock.json
syft dir:. -o spdx-json=sbom-spdx.json

# Scan a built Docker image after deploy
syft packages your-registry/app:1.4.2 -o cyclonedx-json=sbom-image.json

On a production Laravel application, I run both Composer and npm scans because the attack surface spans backend and frontend. Merge is optional early on. Two files per release is fine until your compliance team asks for one unified document.

Laravel-specific checklist

  1. Commit composer.lock and package-lock.json — never deploy without them
  2. Run SBOM generation after composer install --no-dev on the release build
  3. Include PHP runtime version in metadata (PHP 8.3 minimum for Laravel 13, PHP 8.2 for Laravel 12)
  4. Store SBOM files in your artefact bucket with the git SHA in the filename
  5. Attach SBOM hash to your deploy log for traceability

WordPress and WooCommerce stacks need a different scan target. Scan the wp-content/plugins and themes directories plus core version. For sites I maintain under support and maintenance contracts, a quarterly SBOM diff catches plugins that drifted out of date.

SBOM Generation PipelineGit Pushmain branchCI Buildinstall depsSBOM Scansyft / CycloneDXVuln MatchGrype / OSVSigned SBOM Artefactsbom-{git-sha}.cyclonedx.jsonS3 / GitLabartefact storeDeployer 7release tagAudit Logcompliance
Automated SBOM generation pipeline from git push through vulnerability matching to signed artefact storage

Which SBOM Format Should You Choose: SPDX, CycloneDX, or SWID?

Three formats dominate. Pick one primary format per organisation. Converting between them is possible but lossy. Standardise early so your CI templates stay simple.

FormatBest forLicence metadataTooling ecosystemTypical output
SPDXLegal and licence compliance teamsExcellent — built for licence expressionssyft, SPDX tools, Linux Foundation.spdx, JSON, YAML
CycloneDXSecurity and DevSecOps workflowsGood — improving each releaseCycloneDX CLI, Dependency-Track, Grype.json, .xml
SWIDEnterprise IT asset managementLimited compared to SPDXTagVault, commercial scannersISO/IEC 19770-2 tags

For most web agencies and product teams, CycloneDX JSON is the pragmatic default. It integrates cleanly with OWASP Dependency-Track and vulnerability scanners. SPDX wins when your client's legal team cares deeply about licence classification. The SPDX specification and CycloneDX specification are the authoritative references — read the schema once so you know which fields auditors will ask about.

SWID tags matter mainly for packaged desktop or appliance software. Most Laravel and WordPress deployments never need SWID unless a procurement template explicitly requires ISO/IEC 19770-2 tags.

SBOM Format DecisionWhat is the primary goal?Security / CVEresponseLicence auditcomplianceIT asset mgmtenterpriseCycloneDXSPDXSWID tagsMost PHP and Laravel teams start with CycloneDX JSON
SBOM format decision tree — CycloneDX for security, SPDX for licence audits, SWID for enterprise asset management

How Do You Integrate SBOM Generation Into CI/CD Pipelines?

Manual SBOM creation fails within a month. Someone forgets, or the file drifts from the deployed build. Wire generation into the same pipeline that runs tests. I use GitLab CI on several sister sites sharing a Deployer 7 workflow — the pattern below fits that stack and adapts to GitHub Actions easily.

# .gitlab-ci.yml excerpt
generate_sbom:
  stage: build
  image: node:26-bookworm
  script:
    - curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
    - composer install --no-dev --prefer-dist --no-interaction
    - npm ci
    - syft dir:. -o cyclonedx-json=sbom-${CI_COMMIT_SHORT_SHA}.json
    - grype sbom:${CI_COMMIT_SHORT_SHA}.json --fail-on high
  artifacts:
    paths:
      - sbom-*.json
    expire_in: 1 year
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Pair SBOM output with a vulnerability scanner. Grype reads CycloneDX directly. OSV-Scanner accepts SPDX. Fail the pipeline on critical CVEs only after you have a remediation process. Blocking every low-severity finding on day one creates noise your team will ignore.

Sign the SBOM if your compliance framework requires integrity proof. GPG-sign the JSON file and store the detached signature alongside it. Document the public key in your internal runbook. For smaller agencies, a checksum in the CI log plus immutable artefact storage is often enough.

GitLab CI vs GitHub Actions placement

Run SBOM generation in the build stage, before deploy. The scan must reflect the exact dependency tree that ships. Scanning your dev laptop and deploying different packages from CI defeats the purpose. On projects using Linux system administration with Deployer 7, I attach the SBOM filename to the release tag notes.

Validate JSON output in CI with a schema check. CycloneDX publishes a JSON schema. A one-line ajv validate step catches malformed files before they reach a client inbox. Use the JSON formatter tool locally when debugging schema errors — truncated arrays and missing required fields show up fast with pretty-printed output.

What Are Common SBOM Generation Mistakes in Production Deployments?

Most SBOM programmes fail on process, not tooling. These mistakes show up repeatedly across client audits and internal reviews.

  • Scanning dev dependencies in production SBOMs. Run composer install --no-dev first. Dev tools like PHPUnit should not appear in production SBOMs.
  • Ignoring transitive dependencies. Your SBOM must include nested packages, not just top-level entries. Both syft and CycloneDX resolve the full tree from lock files.
  • Generating once and never updating. An SBOM from six months ago is historical data, not a current inventory. Tie generation to every release.
  • Skipping container base images. If you deploy Docker, scan the image layer. OS packages in the base image carry CVEs too.
  • No linkage to deployed version. Always embed git SHA, build timestamp, and environment in SBOM metadata fields.

A pattern I have seen repeatedly: teams store SBOM files in email threads. Put them in object storage or your CI artefact registry with retention matching your compliance window. For legal-tech portals like Mijar Law Associates or booking systems such as Adventure Third Pole Trek, document retention matters as much as generation.

SBOM Mistakes vs Best PracticeCommon MistakesCorrect ApproachScan composer.json onlyScan composer.lock pinnedOne-time manual exportCI job every releaseEmail SBOM to clientSigned artefact in storageIgnore dev deps in prodcomposer install --no-devProcess discipline matters more than which scanner you pick first
Side-by-side comparison of common SBOM generation mistakes and production-ready best practices

Vulnerability response workflow

When a CVE drops, import the latest SBOM into Dependency-Track or run Grype against it. Identify affected services. Patch, redeploy, regenerate SBOM. Record the cycle in your incident log. This workflow pairs naturally with API security practices and secrets management covered in Ansible Vault for secrets.

For eCommerce systems like Quick And Easy Nepalese Grocery, payment libraries and cart packages deserve priority in your vulnerability triage. Not every CVE is exploitable in your configuration. Document why you accepted or deferred each finding.

How Do You Validate and Share an SBOM With Clients or Auditors?

Generation is half the job. Delivery and validation complete it. Auditors want three things: completeness, accuracy, and traceability to a specific release.

Validate completeness against NTIA minimum elements. Your SBOM should list supplier, component name, version, unique identifier (PURL or CPE), dependency relationship, timestamp, and author. CycloneDX and SPDX both support these fields when generated from lock files.

Share SBOMs through secure channels. A password-protected zip sent over email works for small clients. Larger enterprises prefer SFTP or a vendor portal upload. Include a README explaining format, generator tool, and version scanned.

Cost is modest. Open-source tools are free. Dependency-Track community edition runs on a small VPS — roughly Rs 2,500/month (~USD 19) on a basic cloud instance. The labour cost is pipeline setup, typically four to eight hours for a standard Laravel project. That is cheaper than an emergency audit after a supply-chain incident.

If you build custom platforms under custom software development contracts, add SBOM delivery to your statement of work now. Clients will ask for it. Pricing the work upfront avoids scope arguments later.

Key Takeaways

  • Generate SBOMs from lock files (composer.lock, package-lock.json), not loose manifest version ranges.
  • Standardise on CycloneDX JSON for security workflows or SPDX JSON for licence-heavy audits.
  • Wire SBOM generation and vulnerability scanning into CI so every main-branch release produces a fresh artefact.
  • Store SBOM files with git SHA, timestamp, and optional GPG signature — not in email threads.
  • Run composer install --no-dev before scanning so production SBOMs exclude test tooling.
  • Pair SBOM output with Grype or OSV-Scanner and define clear severity thresholds before failing builds.

People Also Ask

Is an SBOM required by law in 2026?

No universal global mandate exists yet, but U.S. federal software procurement guidelines and EU cyber-resilience proposals strongly encourage SBOM delivery. Enterprise RFPs and security questionnaires increasingly treat SBOMs as expected deliverables regardless of formal regulation.

Can I generate an SBOM without Docker?

Yes. Tools like syft and CycloneDX CLI scan local directories and lock files directly. Docker scanning adds value when you containerise deployments, but bare-metal Laravel apps on Apache and PHP-FPM work fine with directory-based scans.

How often should you regenerate an SBOM?

Regenerate on every production release at minimum. Weekly regeneration helps high-churn teams. The SBOM must always match the deployed build — a stale file is worse than none because it creates false confidence.

What is the difference between an SBOM and a dependency audit?

An SBOM is a structured inventory of all components. A dependency audit evaluates those components for known vulnerabilities, licence conflicts, or outdated versions. Run the audit against the SBOM — they are complementary, not interchangeable.

Build Supply-Chain Visibility Into Your Next Release

You now have the tooling, format choices, and CI patterns to SBOM: Generate a Software Bill of Materials on every release instead of scrambling during an audit. Start with one Laravel or WordPress project, add a syft step to your existing pipeline, and store the output beside your deploy artefacts. Expand to vulnerability gating once the baseline workflow is stable. If you want help wiring SBOM generation into a production stack or testing and optimization pipeline, contact us to discuss your deployment setup. You can also review how we ship secure platforms in our portfolio or explore related DevOps reading on multi-cloud deployment patterns and essential developer tooling.

Frequently Asked Questions

An SBOM is a machine-readable inventory of every component, version, licence, and dependency relationship in your application stack. Think of it as a parts list for code. When a CVE hits a library, you search the SBOM instead of guessing which servers run the vulnerable package. Regulators, enterprise buyers, and security teams increasingly expect one before they trust your software. On production projects I maintain, SBOM output lives beside the build artefact so supply-chain documentation matches the exact commit that went live.

PHP projects expose dependencies through composer.lock — that lock file is your ground truth, never composer.json alone because it lists version ranges, not pinned releases. The same rule applies to Node: use package-lock.json, not bare package.json. Install CycloneDX CLI with npm and run cyclonedx-php-composer make-sbom against your Composer lock, and cyclonedx-npm against your npm lock for Vite frontend assets. Alternatively, install syft from Anchore and scan the project root with syft dir:. to pick up both lock files in one pass. On production Laravel apps I run both Composer and npm scans because the attack surface spans backend and frontend.

Pick one primary format per organisation and standardise early — converting between formats is possible but lossy. CycloneDX JSON is the pragmatic default for most web agencies and product teams because it integrates cleanly with OWASP Dependency-Track and vulnerability scanners like Grype. SPDX wins when your client's legal team cares deeply about licence classification — it has excellent licence metadata built for compliance teams. SWID tags matter mainly for packaged desktop or appliance software under ISO/IEC 19770-2; most Laravel and WordPress deployments never need SWID unless a procurement template explicitly requires it.

Wire generation into the same pipeline that runs tests — manual SBOM creation fails within a month when someone forgets or the file drifts from the deployed build. In GitLab CI, add a build-stage job on a node:26-bookworm image: run composer install --no-dev, npm ci, then syft dir:. to output a CycloneDX JSON file named with the git SHA. Pair output with Grype for vulnerability scanning and store the artefact for one year. Run generation before deploy so the scan reflects the exact dependency tree that ships. Validate JSON output against the CycloneDX schema with an ajv validate step to catch malformed files before they reach a client inbox.

The most repeated failures are process mistakes, not tooling gaps. Scanning before composer install --no-dev pulls PHPUnit and other dev tools into production SBOMs. Using composer.json or package.json instead of lock files misses pinned versions and transitive dependencies. Generating once and never updating produces historical data, not a current inventory — tie generation to every release. Skipping container base images ignores OS-package CVEs when you deploy Docker. Storing SBOM files in email threads instead of object storage or a CI artefact registry breaks traceability. Always embed git SHA, build timestamp, and environment in SBOM metadata fields.

Auditors want three things: completeness, accuracy, and traceability to a specific release. Validate completeness against NTIA minimum elements — supplier, component name, version, unique identifier such as PURL or CPE, dependency relationship, timestamp, and author. CycloneDX and SPDX both support these fields when generated from lock files. Share through secure channels: a password-protected zip works for small clients; larger enterprises prefer SFTP or a vendor portal upload. Include a README explaining format, generator tool, and version scanned. GPG-sign the JSON file if your compliance framework requires integrity proof, and store the detached signature alongside the artefact.

No universal global mandate exists yet, but U.S. federal software procurement guidelines and EU cyber-resilience proposals strongly encourage SBOM delivery. Enterprise RFPs and security questionnaires increasingly treat SBOMs as expected deliverables regardless of formal regulation.

Yes. Tools like syft and CycloneDX CLI scan local directories and lock files directly. Docker scanning adds value when you containerise deployments, but bare-metal Laravel apps on Apache and PHP-FPM work fine with directory-based scans.

Regenerate on every production release at minimum. Weekly regeneration helps high-churn teams. The SBOM must always match the deployed build — a stale file is worse than none because it creates false confidence.

An SBOM is a structured inventory of all components in your stack — names, versions, licences, and relationships encoded in a standard format like CycloneDX or SPDX. A dependency audit evaluates those components for known vulnerabilities, licence conflicts, or outdated versions using tools like Grype or OSV-Scanner. They are complementary, not interchangeable. Generate the SBOM first from lock files, then run the audit against that output. Grype reads CycloneDX directly; OSV-Scanner accepts SPDX. Define clear severity thresholds before failing builds — blocking every low-severity finding on day one creates noise your team will ignore.

Always use composer.lock, never composer.json alone. The lock file is your ground truth — it records exact pinned versions of every direct and transitive dependency installed in your build. composer.json only declares version ranges like ^8.0, which tells an auditor nothing about what actually shipped. The identical rule applies on the frontend: scan package-lock.json, not bare package.json. Both syft and CycloneDX resolve the full transitive tree from lock files automatically. Commit both lock files to version control and never deploy without them. If a lock file is missing from your release branch, fix that before investing in SBOM tooling.

Two open-source toolchains cover most Laravel stacks. CycloneDX CLI provides cyclonedx-php-composer for Composer lock files and cyclonedx-npm for npm lock files, both emitting standard JSON. Install globally with npm on Node 26 LTS or run pinned versions in CI. syft from Anchore scans project directories, lock files, or built Docker images and supports both CycloneDX and SPDX output formats. For vulnerability matching after generation, Grype reads CycloneDX SBOMs directly and OSV-Scanner accepts SPDX. OWASP Dependency-Track ingests either format for ongoing component tracking across releases.

Open-source generation tools — CycloneDX CLI, syft, and Grype — are free. Dependency-Track community edition runs on a small VPS for roughly Rs 2,500/month (~USD 19) on a basic cloud instance if you want a central dashboard. The real cost is labour: pipeline setup typically takes four to eight hours for a standard Laravel project. That upfront investment is cheaper than an emergency audit after a supply-chain incident. If you build custom platforms under development contracts, add SBOM delivery to your statement of work now — clients will ask for it, and pricing the work upfront avoids scope arguments later.

No. Dev tools must not appear in production SBOMs because they inflate the component count and create false audit findings. Run composer install --no-dev --prefer-dist before scanning so only runtime packages enter the inventory. The same discipline applies in CI: the generate_sbom job should install production dependencies first, then run syft or CycloneDX against the resulting tree. Scanning a local dev environment that includes PHPUnit, Laravel Debugbar, or Pest produces an SBOM that does not match what ships. Auditors comparing your SBOM to the deployed build will flag the mismatch immediately.

When a CVE drops, import the latest SBOM into Dependency-Track or run Grype against it to identify affected services and specific component versions. Patch the vulnerable package, redeploy, and regenerate the SBOM from the new lock file so the inventory reflects the fix. Record the full cycle in your incident log — discovery, triage, remediation, and regenerated artefact. Not every CVE is exploitable in your configuration; document why you accepted or deferred each finding. For eCommerce systems, payment libraries and cart packages deserve priority in triage. Pair this workflow with secrets management and API security practices already in your deployment runbook.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: