Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions etc/gensbom/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dist/
index.js
index.js.map
*.log
node_modules/
sboms/
sboms.zip
*.tsbuildinfo
11 changes: 11 additions & 0 deletions etc/gensbom/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"experimentalOperatorPosition": "start",
"printWidth": 79,
"useTabs": false,
"semi": true,
"singleQuote": false,
"quoteProps": "consistent",
"jsxSingleQuote": false,
"trailingComma": "all",
"bracketSameLine": false
}
113 changes: 113 additions & 0 deletions etc/gensbom/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Purpose

This tool generates SBOMs (Software Bill of Materials) from container images using Syft and ingests them into a TPA (Trusted Profile Analyzer) service. It can run as either a standalone script or a containerized application.

## Usage

### Using the Bash Script

```bash
./gensbom.sh images.txt
```

### Using the TypeScript Script

First, install dependencies:

```bash
npm install
```

Build the TypeScript code:

```bash
npm run build
```

Run the compiled script:

```bash
node index.js images.txt
```

Or run directly with tsx (development):

```bash
npm run dev images.txt
```

### Using the Container

Build the container:

```bash
podman build -t gensbom -f Containerfile .
```

Run the container:

```bash
podman run --rm -v "${PWD}":/gensbom:Z -e 'TPA_*' gensbom:latest images.txt
```

The input file should contain one container image per line (see `example-images.txt`).

## Environment Variables

**Required:**

- `TPA_SERVICE_URL` - URL to the running TPA service (e.g., `my.tpa.instance.abc:8765`)

**Optional:**

- `TPA_AUTH_TOKEN` - Authorization token for TPA (e.g., `Bearer XXXXXXXXXX`)
- `NO_COLOR` - Set to disable colored output (automatically set for non-TTY environments)

## Authentication Files

These files must be in the current working directory:

- **config.json** (required for private registries) - Docker-format container registry credentials. For Quay.io: Account Settings → Generate Encrypted Password → Docker Configuration
- **trust.crt** (optional) - Custom trust anchors if TPA uses certificates signed by custom CAs

## Container Build Arguments

When building the container, you can customize:

- `SYFT_REGISTRY` - Container registry for Syft (default: `ghcr.io/anchore`)
- `SYFT_IMAGE` - Syft image name (default: `syft`)
- `SYFT_TAG` - Syft version tag (default: `v1.36.0`)

Example:

```bash
podman build -t gensbom -f Containerfile --build-arg SYFT_TAG=v1.37.0 .
```

## Output

After running, the tool produces:

- `sboms/` directory with individual SBOM JSON files
- `sboms.zip` archive containing all generated SBOMs

Each SBOM is generated in CycloneDX 1.6 format and automatically uploaded to the TPA service.

## Script Behavior

Both `gensbom.sh` and `index.ts` implement the same functionality:

1. Validates environment variables and required files
2. Pings the TPA service to verify connectivity and auth
3. For each image in the input file:
- Generates an SBOM using Syft
- Uploads the SBOM to TPA via POST to `/api/v2/sbom`
- Continues on errors (failed images won't stop processing)
4. Creates a ZIP archive of all successfully generated SBOMs
5. Removes empty/corrupted SBOM files from the archive

The scripts are designed to be run both natively (with Syft installed) and within a container environment.
28 changes: 22 additions & 6 deletions etc/gensbom/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,35 @@ ARG SYFT_REGISTRY=ghcr.io/anchore
ARG SYFT_IMAGE=syft
ARG SYFT_TAG=v1.36.0

FROM registry.access.redhat.com/ubi9/ubi:latest
FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS builder

USER 1001
COPY --chown=1001 . .

RUN pwd && \
npm version && \
npm config ls -l && \
npm ci --verbose --ignore-scripts --no-audit && \
npm run format:check && \
npm run build && \
npm run lint

FROM registry.access.redhat.com/ubi9/nodejs-22-minimal:latest

ARG SYFT_REGISTRY
ARG SYFT_IMAGE
ARG SYFT_TAG

USER 0

COPY --from=${SYFT_REGISTRY}/${SYFT_IMAGE}:${SYFT_TAG} /syft /usr/local/bin/syft
COPY ./gensbom.sh /usr/local/bin/gensbom.sh
COPY --from=builder /opt/app-root/src/package*.json ./
COPY --from=builder /opt/app-root/src/dist ./dist
COPY --from=builder /opt/app-root/src/bin ./bin

RUN dnf -y install gawk zip && \
dnf clean all && \
mkdir -p /gensbom
RUN mkdir -p /gensbom && \
npm install --production

WORKDIR /gensbom

ENTRYPOINT ["/usr/local/bin/gensbom.sh"]
ENTRYPOINT ["/opt/app-root/src/bin/gensbom.js"]
52 changes: 26 additions & 26 deletions etc/gensbom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@

**Prerequisites:**

* `podman`
* [`config.json`](https://github.com/google/go-containerregistry/tree/main/pkg/authn#docker-config-auth)
- `podman`
- [`config.json`](https://github.com/google/go-containerregistry/tree/main/pkg/authn#docker-config-auth)
in the current working directory
* `trust.crt` (optional custom trust anchors) in the current working directory
- `trust.crt` (optional custom trust anchors) in the current working directory
if the running TPA service uses a certificate signed by these trust anchors
* `TPA_SERVICE_URL`, holding the URL to the running TPA service, e.g.
- `TPA_SERVICE_URL`, holding the URL to the running TPA service, e.g.
`my.tpa.instance.abc:8765`
* `TPA_AUTH_TOKEN`, holding the valid authorization token, e.g.
- `TPA_AUTH_TOKEN`, holding the valid authorization token, e.g.
`Bearer XXXXXXXXXX`
* `gensbom` image
* you can either get it from the [GitHub Container Registry](https://github.com/guacsec/trustify/pkgs/container/gensbom):
- `gensbom` image
- you can either get it from the [GitHub Container Registry](https://github.com/guacsec/trustify/pkgs/container/gensbom):

```
podman pull ghcr.io/guacsec/gensbom:latest
```

* or build it [on its own](#building-the-container-image)
- or build it [on its own](#building-the-container-image)

**Running:**

Expand All @@ -34,24 +34,24 @@ podman run --rm -v "${PWD}":/gensbom:Z -e 'TPA_*' gensbom:latest images.txt

**Output:**

* `${PWD}/sboms` directory with the generated SBOMs
* `${PWD}/sboms.zip` archive with the generated SBOMs
- `${PWD}/sboms` directory with the generated SBOMs
- `${PWD}/sboms.zip` archive with the generated SBOMs

## Using the Script

**Prerequisites:**

* `awk`
* Bash
* `curl`
* `sha512sum`
* [`syft`](https://github.com/anchore/syft/releases)
* `zip`
* [`config.json`](https://github.com/google/go-containerregistry/tree/main/pkg/authn#docker-config-auth)
- `awk`
- Bash
- `curl`
- `sha512sum`
- [`syft`](https://github.com/anchore/syft/releases)
- `zip`
- [`config.json`](https://github.com/google/go-containerregistry/tree/main/pkg/authn#docker-config-auth)
in the current working directory
* `TPA_SERVICE_URL`, holding the URL to the running TPA service, e.g.
- `TPA_SERVICE_URL`, holding the URL to the running TPA service, e.g.
`my.tpa.instance.abc:8765`
* `TPA_AUTH_TOKEN`, holding the valid authorization token, e.g.
- `TPA_AUTH_TOKEN`, holding the valid authorization token, e.g.
`Bearer XXXXXXXXXX`

**Running:**
Expand All @@ -66,7 +66,7 @@ podman run --rm -v "${PWD}":/gensbom:Z -e 'TPA_*' gensbom:latest images.txt

**Output:**

* Same as in the previous case
- Same as in the previous case

## Troubleshooting

Expand Down Expand Up @@ -97,13 +97,13 @@ For other container image registries the process may be similar.

The `Containerfile` build arguments:

* `SYFT_REGISTRY`, holding the container registry from which the `syft`
- `SYFT_REGISTRY`, holding the container registry from which the `syft`
container is pulled (default: `ghcr.io/anchore`)
* `SYFT_IMAGE`, holding the `syft` container image name (default: `syft`)
* `SYFT_TAG`, holding the `syft` container image tag (default: `v1.36.0`)
- `SYFT_IMAGE`, holding the `syft` container image name (default: `syft`)
- `SYFT_TAG`, holding the `syft` container image tag (default: `v1.36.0`)

## References

* [Docker Config Auth](https://github.com/google/go-containerregistry/tree/main/pkg/authn#docker-config-auth)
* [Syft](https://github.com/anchore/syft)
* [Syft: Private Registry Authentication](https://github.com/anchore/syft/wiki/private-registry-authentication)
- [Docker Config Auth](https://github.com/google/go-containerregistry/tree/main/pkg/authn#docker-config-auth)
- [Syft](https://github.com/anchore/syft)
- [Syft: Private Registry Authentication](https://github.com/anchore/syft/wiki/private-registry-authentication)
11 changes: 11 additions & 0 deletions etc/gensbom/bin/gensbom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env node

import { exit } from "node:process";
import { handleError } from "../dist/utils.js";
import { main } from "../dist/index.js";

try {
exit(await main());
} catch (err) {
exit(handleError(err));
}
Loading