Documentation
¶
Overview ¶
Package pbom generates the Pipeline Bill of Materials: the CycloneDX and native documents that inventory a pipeline's includes, components, images and actions together with their compliance verdicts.
Package pbom provides Pipeline Bill of Materials (PBOM) generation.
A PBOM is an inventory of all dependencies used in a CI/CD pipeline, including container images and includes (components, templates, remote files). Unlike an SBOM (Software Bill of Materials) which tracks application dependencies, a PBOM tracks pipeline infrastructure dependencies.
Index ¶
- Constants
- type Capability
- type ContainerImage
- type ContainerImageRef
- type CycloneDX
- type CycloneDXComponent
- type CycloneDXMetadata
- type CycloneDXProperty
- type CycloneDXTool
- type Generator
- func (g *Generator) Generate(imageData *gitlab.GitlabPipelineImageData, ...) *PBOM
- func (g *Generator) GenerateFromGitHubIR(pipeline *ir.NormalizedPipeline) *PBOM
- func (g *Generator) WithCapabilities(matches []capability.Match) *Generator
- func (g *Generator) WithCommit(sha, ref string) *Generator
- func (g *Generator) WithComplianceData(data *ImageComplianceData) *Generator
- func (g *Generator) WithGitHubComplianceData(data *GitHubComplianceData) *Generator
- func (g *Generator) WithIncludeOverrideData(data *IncludeOverrideData) *Generator
- func (g *Generator) WithJobResources(jobs []ir.Job) *Generator
- func (g *Generator) WithoutComplianceVerdicts() *Generator
- type GitHubComplianceData
- type ImageComplianceData
- type Include
- type IncludeOverrideData
- type JobResources
- type PBOM
- type PlatformGlobalScore
- type PlatformSummary
- type PlumberScoreCounts
- type PlumberScoreSummary
- type PolicyScore
- type ProjectInfo
- type Summary
Constants ¶
const CycloneDXSpecVersion = "1.5"
CycloneDXSpecVersion is the CycloneDX specification version the PBOM is generated as.
const Version = "1.0.0"
Version is the current PBOM specification version
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Capability ¶ added in v0.7.5
type Capability struct {
Kind string `json:"kind"`
Job string `json:"job"`
Tool string `json:"tool"`
Evidence string `json:"evidence"`
}
Capability is one recognized capability of one job (package capability's Match, in the document's own shape). Kind is the declared capability name (the effective list's, spec 4.2), not a closed vocabulary; Tool is the recognizer's declared tool, else the pattern that matched; Evidence is the closed set of the recognizer's field names: images, commands, wrapper_script, gitlab_report_types, gitlab_include_template, gitlab_include_component, gitlab_include_project.
type ContainerImage ¶
type ContainerImage struct {
// Full image reference (e.g., "docker.io/library/golang:1.22-alpine")
Image string `json:"image"`
// Parsed components
Registry string `json:"registry"`
Name string `json:"name"`
Tag string `json:"tag,omitempty"`
// Usage context
Jobs []string `json:"jobs"`
// Unresolved reports that the reference still held a $VARIABLE after
// substitution, so Registry, Name and Tag above were parsed out of a
// placeholder rather than out of an image reference. The collector knows
// this and the compliance enrichment already honours it (an unresolved
// image claims no verdict); carrying it here lets a consumer that keys a
// resource on the reference refuse to key one on a variable name.
//
// The inventory still LISTS the entry: the pipeline does declare it, and
// the document's job is to say what the pipeline declares.
Unresolved bool `json:"unresolved,omitempty"`
// Nested reports a GitHub image no job runs itself: a `uses: docker://`
// step a composite action pulls in. An image some job also runs directly
// is not nested.
Nested bool `json:"nested,omitempty"`
// Via is the chain a nested image was pulled in through, outermost
// first, the first one seen when it is reached on several paths (as for
// Include.Via). Empty on a direct image.
Via []string `json:"via,omitempty"`
// Compliance status (from analysis, if available)
Authorized *bool `json:"authorized,omitempty"`
ForbiddenTag *bool `json:"forbiddenTag,omitempty"`
}
ContainerImage represents a container image used in the pipeline
type ContainerImageRef ¶ added in v0.5.7
type ContainerImageRef struct {
Image string `json:"image"`
Registry string `json:"registry,omitempty"`
Name string `json:"name"`
Tag string `json:"tag,omitempty"`
Digest string `json:"digest,omitempty"`
// Unresolved reports that the reference still held a $VARIABLE when it
// was parsed, so the fields above describe a placeholder rather than an
// image. See ContainerImage.Unresolved: same signal, same reason.
Unresolved bool `json:"unresolved,omitempty"`
}
ContainerImageRef is a container image named without any compliance verdict attached: the inventory fact alone. ContainerImage below is the pipeline-level entry, which additionally carries the jobs that use the image and the flags the controls produced for it.
The split into registry, name, tag and digest is the pipeline model's own. The CLI parses an image reference exactly once, at collection (invariant I1); Image is those parts joined back into one string for a consumer that wants the reference whole.
type CycloneDX ¶
type CycloneDX struct {
BOMFormat string `json:"bomFormat"`
SpecVersion string `json:"specVersion"`
Version int `json:"version"`
SerialNumber string `json:"serialNumber"`
Metadata CycloneDXMetadata `json:"metadata"`
Components []CycloneDXComponent `json:"components"`
}
CycloneDX represents a CycloneDX SBOM Spec: https://cyclonedx.org/docs/1.5/json/ Struct field order matches a natural read path: BOM header, identifiers, metadata (project/tool), dependency components last.
type CycloneDXComponent ¶
type CycloneDXComponent struct {
Type string `json:"type"`
BOMRef string `json:"bom-ref,omitempty"`
Name string `json:"name"`
Version string `json:"version,omitempty"`
Description string `json:"description,omitempty"`
Purl string `json:"purl,omitempty"`
Properties []CycloneDXProperty `json:"properties,omitempty"`
}
CycloneDXComponent represents a component in the BOM
type CycloneDXMetadata ¶
type CycloneDXMetadata struct {
Timestamp string `json:"timestamp"`
Tools []CycloneDXTool `json:"tools,omitempty"`
Component *CycloneDXComponent `json:"component,omitempty"`
Properties []CycloneDXProperty `json:"properties,omitempty"`
}
CycloneDXMetadata contains metadata about the BOM
type CycloneDXProperty ¶
CycloneDXProperty represents a name-value property
type CycloneDXTool ¶
type CycloneDXTool struct {
Vendor string `json:"vendor"`
Name string `json:"name"`
Version string `json:"version"`
}
CycloneDXTool describes a tool used to create the BOM
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator creates PBOMs from pipeline analysis data
func NewGenerator ¶
NewGenerator creates a new PBOM generator
func NewGitHubGenerator ¶ added in v0.3.0
NewGitHubGenerator creates a PBOM generator pre-configured for the GitHub provider. URL is the GitHub host (`github.com` or a GHES host); branch is informational, used to stamp the project section.
func (*Generator) Generate ¶
func (g *Generator) Generate( imageData *gitlab.GitlabPipelineImageData, originData *gitlab.GitlabPipelineOriginData, ) *PBOM
Generate creates a PBOM from pipeline data collections
func (*Generator) GenerateFromGitHubIR ¶ added in v0.3.0
func (g *Generator) GenerateFromGitHubIR(pipeline *ir.NormalizedPipeline) *PBOM
GenerateFromGitHubIR builds a PBOM from the normalized GitHub pipeline IR. Container images are extracted from each job's `image:` and `services:` blocks; the GitHub analogue of GitLab's "include" list is the merged set of third-party action references (`uses: owner/repo@ref`) and reusable-workflow calls (`jobs.<name>.uses: …/.github/workflows/x.yml@ref`).
func (*Generator) WithCapabilities ¶ added in v0.7.5
func (g *Generator) WithCapabilities(matches []capability.Match) *Generator
WithCapabilities attaches what package capability recognized on the analyzed pipeline. The generator copies the matches verbatim: recognizing is the capability package's job, the document only carries the result.
func (*Generator) WithCommit ¶ added in v0.4.52
WithCommit attaches the resolved analyzed commit and its branch/tag so the PBOM names the exact code it describes (#443).
func (*Generator) WithComplianceData ¶
func (g *Generator) WithComplianceData(data *ImageComplianceData) *Generator
func (*Generator) WithGitHubComplianceData ¶ added in v0.3.0
func (g *Generator) WithGitHubComplianceData(data *GitHubComplianceData) *Generator
WithGitHubComplianceData attaches per-image and per-action compliance lookups so the resulting PBOM marks each image with its authorized/forbiddenTag flags and each action with a custom property for unpinned status.
func (*Generator) WithIncludeOverrideData ¶ added in v0.1.47
func (g *Generator) WithIncludeOverrideData(data *IncludeOverrideData) *Generator
WithIncludeOverrideData attaches override detection results so the PBOM marks overridden includes
func (*Generator) WithJobResources ¶ added in v0.5.7
WithJobResources attaches the analyzed pipeline's jobs so the document can name each job's service images and runner tags.
The jobs come from the normalized pipeline model, already parsed: the CLI is the one parser of CI configuration (invariant I1), and nothing here reads YAML or splits an image reference a second time.
func (*Generator) WithoutComplianceVerdicts ¶ added in v0.4.48
WithoutComplianceVerdicts suppresses the include fields that state a CONTROL'S CONCLUSION rather than what was collected. It backs --no-controls.
`UpToDate` is the one that matters: unlike the image flags it is computed during data collection (the origin collector probes the upstream ref), so it survives even when no policy is evaluated, and it is exactly what includesMustBeUpToDate reports. `LatestVersion` is kept: the upstream version is a collected fact and asserts nothing on its own.
type GitHubComplianceData ¶ added in v0.3.0
type GitHubComplianceData struct {
// ForbiddenTagImages keys on the full image reference
// (`docker.io/golang:1.22`).
ForbiddenTagImages map[string]bool
// ImagesPinnedByDigest keys on the full image reference; true
// when the image carries an `@sha256:…` suffix.
ImagesPinnedByDigest map[string]bool
// UnpinnedActions keys on the full action ref (`actions/checkout@v4`);
// true when the ref is not a 40-char SHA.
UnpinnedActions map[string]bool
// ArchivedActions keys on the full action ref; true when the
// upstream repository is archived (ISSUE-702). Sourced from the
// per-action GitHub API metadata enrichment.
ArchivedActions map[string]bool
// VulnerableActions keys on the full action ref; true when the
// upstream repository has at least one published GitHub Advisory
// (ISSUE-703). Same metadata enrichment path as ArchivedActions.
VulnerableActions map[string]bool
// ActionAdvisories keys on the full action ref, value is the
// list of GHSA IDs published against the action's repository.
// Populated alongside VulnerableActions and surfaced into the
// Include / CDX layer so consumers see which advisories triggered
// the finding.
ActionAdvisories map[string][]string
}
GitHubComplianceData carries the GitHub-side compliance lookups the PBOM enriches container images and actions with. Mirrors ImageComplianceData on the GitLab side; kept separate because the keys (action ref vs include path) and providers differ.
func BuildGitHubPBOMCompliance ¶ added in v0.3.65
func BuildGitHubPBOMCompliance(result *control.AnalysisResult) *GitHubComplianceData
BuildGitHubPBOMCompliance walks Findings and the IR to assemble the per-image / per-action compliance lookup the PBOM generator uses to enrich entries.
type ImageComplianceData ¶
type ImageComplianceData struct {
// ForbiddenTagImages maps image links to true if they use a forbidden tag
ForbiddenTagImages map[string]bool
UnauthorizedImages map[string]bool
}
ImageComplianceData holds compliance results for images to enrich PBOM output
func BuildImageComplianceData ¶ added in v0.3.65
func BuildImageComplianceData(result *control.AnalysisResult) *ImageComplianceData
BuildImageComplianceData extracts per-image compliance flags from an AnalysisResult into the lookup map consumed by the PBOM generator.
func ImageComplianceFor ¶ added in v0.5.7
func ImageComplianceFor(result *control.AnalysisResult, noControls bool, platform *PlatformSummary) *ImageComplianceData
ImageComplianceFor returns the per-image compliance flags, or nil when this run may not claim them. The flags are derived from findings, not from the score, so an empty findings slice would otherwise mark every image forbiddenTag:false / authorized:true and assert the very image checks nobody ran, inside the document the flag exists to produce. With nil the document records the inventory and claims nothing.
Two runs may not claim them. noControls (--no-controls) evaluated nothing at all. And a platform-mode run whose resolved policies do not enable an image control evaluated it for no policy, which is the same absence of a verdict; the findings it does have come from the policy runs' union, so the flags it CAN claim are the policies' own.
The answer is per control, not per pair: the forbidden-tag and the authorized-source controls are independent, and a policy that pins tags without restricting registries would otherwise publish "authorized" for every image on the strength of the OTHER control having run. Dropping the map of a control nobody enabled leaves that field out of every image (the generator's existing tri-state), while the other one is still published.
It lives here rather than beside one writer because both the PBOM artifacts and the platform push's bill of materials are enriched from it, and the two must never disagree about which verdict this run may claim.
type Include ¶
type Include struct {
// Type of include: "component", "project", "local", "remote",
// "template" (GitLab) or "action", "reusableWorkflow" (GitHub).
Type string `json:"type"`
// Location/path of the include
Location string `json:"location"`
// For project includes
Project string `json:"project,omitempty"`
// Version information
Version string `json:"version,omitempty"`
LatestVersion string `json:"latestVersion,omitempty"`
UpToDate *bool `json:"upToDate,omitempty"`
// For components from GitLab CI/CD Catalog
ComponentName string `json:"componentName,omitempty"`
FromCatalog bool `json:"fromCatalog,omitempty"`
// Whether this is a nested include (included by another include)
Nested bool `json:"nested,omitempty"`
// Via is the chain a nested GitHub include was pulled in through,
// outermost first (spec 2026-10-16 section 8). Empty on a direct include.
// When the include is reached on several paths, the chain of the first
// one seen (the findings' identities tell the paths apart, the bill
// does not). The platform push keeps its outermost 8 entries
// (review of PR 549), the full chain stays here.
Via []string `json:"via,omitempty"`
// Override information (populated from control results)
Overridden bool `json:"overridden,omitempty"`
OverriddenJobs []utils.OverriddenJobDetail `json:"overriddenJobs,omitempty"`
// GitHub-only compliance enrichments populated from GitHubComplianceData.
// Archived is true when the action's upstream repository is archived
// (ISSUE-702). HasCVE is true when the action's upstream carries at
// least one published GitHub Advisory (ISSUE-703). Advisories is the
// list of GHSA IDs that triggered HasCVE.
Archived *bool `json:"archived,omitempty"`
HasCVE *bool `json:"hasCve,omitempty"`
Advisories []string `json:"advisories,omitempty"`
}
Include represents an include/component/template used in the pipeline. On GitHub, the analogue of GitLab's "include" types are:
- "action" — third-party `uses: owner/repo@ref` step
- "reusableWorkflow" — `uses:` at the job level pointing at a reusable-workflow file
type IncludeOverrideData ¶ added in v0.1.47
type IncludeOverrideData struct {
// Overrides maps a clean include path to its overridden job details
Overrides map[string][]utils.OverriddenJobDetail
}
IncludeOverrideData holds override detection results for includes. Key is the clean include location path (without version/instance prefix).
func BuildIncludeOverrideData ¶ added in v0.3.65
func BuildIncludeOverrideData(result *control.AnalysisResult) *IncludeOverrideData
BuildIncludeOverrideData extracts include-override detection results from an AnalysisResult into the lookup map consumed by the PBOM generator.
type JobResources ¶ added in v0.5.7
type JobResources struct {
Name string `json:"name"`
Services []ContainerImageRef `json:"services,omitempty"`
RunnerTags []string `json:"runnerTags,omitempty"`
}
JobResources is one job's share of the bill of materials. Name is the job name as the pipeline declares it; nothing about it is re-authored.
type PBOM ¶
type PBOM struct {
PBOMVersion string `json:"pbomVersion"`
GeneratedAt time.Time `json:"generatedAt"`
Project ProjectInfo `json:"project"`
Summary Summary `json:"summary"`
PlumberScore *PlumberScoreSummary `json:"plumberScore,omitempty"`
// Policies and PlatformGlobalScore are the platform-mode score block
// (spec 2026-09-10-cli-platform-mode-policies-only s5). In that mode
// there is no run-level score to put in PlumberScore: every verdict
// belongs to one of the platform's policies, and the single global
// figure is the platform's own, present only when the push returned one.
// Both are absent from a standalone PBOM.
Policies []PolicyScore `json:"policies,omitempty"`
PlatformGlobalScore *PlatformGlobalScore `json:"platformGlobalScore,omitempty"`
ContainerImages []ContainerImage `json:"containerImages"`
Includes []Include `json:"includes"`
// Jobs names the per-job resources the pipeline asks a runner for: the
// service images started alongside the job, and the runner tags that
// decide which runner picks it up. Only the jobs carrying at least one
// of the two are listed, sorted by name.
//
// Absent, not empty, when the generator was never handed the pipeline
// model: an empty list would read as "this pipeline has no service and
// no runner tag" for a run that simply never looked.
Jobs []JobResources `json:"jobs,omitempty"`
// Capabilities lists what the pipeline's jobs do, as the CLI recognized it (package
// capability): one entry per job and capability, with the tool and the evidence the
// match rested on. Absent when nothing was recognized.
Capabilities []Capability `json:"capabilities,omitempty"`
}
PBOM represents a Pipeline Bill of Materials - an inventory of all dependencies used in a CI/CD pipeline. JSON field order follows encode/json struct order: version stamp, project context, aggregate summary, score, then inventories (read top-to-bottom).
func (*PBOM) ApplyPlatformSummary ¶ added in v0.4.60
func (p *PBOM) ApplyPlatformSummary(s *PlatformSummary)
ApplyPlatformSummary stamps the platform-mode score block onto a document: the per-policy verdicts and the platform's own global score. A no-op for a standalone run (nil summary), which is what keeps those documents byte-identical.
It is the counterpart of BuildPlumberScoreSummary and deliberately does not touch PlumberScore: in platform mode there is no run-level score to write there, and the caller has already passed a nil one.
func (*PBOM) ToCycloneDX ¶
ToCycloneDX converts a PBOM to CycloneDX format
type PlatformGlobalScore ¶ added in v0.4.60
type PlatformGlobalScore struct {
Letter string `json:"letter,omitempty"`
Points int `json:"points"`
}
PlatformGlobalScore is the platform's own displayed score for the run, as the push response returned it. It is never computed CLI-side.
type PlatformSummary ¶ added in v0.4.60
type PlatformSummary struct {
Policies []PolicyScore
GlobalScore *PlatformGlobalScore
ForbiddenTagEvaluated bool
AuthorizedSourceEvaluated bool
}
PlatformSummary is what a platform-mode run hands the PBOM writers: the per-policy verdicts and the platform's global score (nil when the push returned none). nil itself outside platform mode.
ForbiddenTagEvaluated and AuthorizedSourceEvaluated are false when no resolved policy enables the control behind that per-image boolean. The booleans are a POSITIVE claim - an image no finding names is published as authorized, with no forbidden tag - so with no policy asking for a control the writers drop ITS boolean and the image is reported as inventory alone on that axis. Both fields are already *bool, so dropping one is the existing tri-state, not a schema change.
They are two flags rather than one because the two controls are independent: a policy commonly pins tags without also restricting registries. Answering for both at once published the other control's positive verdict, which is exactly the claim-nobody-evaluated failure these flags exist to prevent.
type PlumberScoreCounts ¶ added in v0.1.83
type PlumberScoreCounts struct {
Critical int `json:"critical"`
High int `json:"high"`
Medium int `json:"medium"`
Low int `json:"low"`
}
PlumberScoreCounts is the number of issues per severity bucket.
type PlumberScoreSummary ¶ added in v0.1.83
type PlumberScoreSummary struct {
ProfileID string `json:"profileId"`
RawPoints float64 `json:"rawPoints"`
FinalPoints float64 `json:"finalPoints"`
Score string `json:"score,omitempty"`
CriticalMalusApplied bool `json:"criticalMalusApplied,omitempty"`
CriticalMalusMax float64 `json:"criticalMalusMax,omitempty"`
Counts PlumberScoreCounts `json:"counts"`
// PathCounts is the scoring-v4 attack-path count per tier, non-zero
// tiers only; absent under scoring-v3.
PathCounts map[string]int `json:"pathCounts,omitempty"`
}
PlumberScoreSummary mirrors control.PlumberScoreResult for JSON consumers (PBOM / SBOM).
func BuildPlumberScoreSummary ¶ added in v0.3.65
func BuildPlumberScoreSummary(score *control.PlumberScoreResult, scoreMode bool) *PlumberScoreSummary
BuildPlumberScoreSummary converts a PlumberScoreResult into the PBOM PlumberScoreSummary shape. Returns nil when scoreMode is false or score is nil (no score in output).
type PolicyScore ¶ added in v0.4.60
type PolicyScore struct {
Name string `json:"name"`
Enforcement string `json:"enforcement"`
Score string `json:"score,omitempty"`
FinalPoints *int `json:"finalPoints,omitempty"`
Applied bool `json:"applied"`
Reason string `json:"reason,omitempty"`
}
PolicyScore is one resolved platform policy's own verdict over its own control set. Score and FinalPoints are omitted for a policy the CLI could not evaluate (Applied false, Reason says why): a letter-less zero would read as a policy that scored badly rather than one that ran nothing.
type ProjectInfo ¶
type ProjectInfo struct {
Path string `json:"path"`
ID int `json:"id,omitempty"`
Provider string `json:"provider,omitempty"`
URL string `json:"url,omitempty"`
GitLabURL string `json:"gitlabUrl,omitempty"`
Branch string `json:"branch,omitempty"`
// CommitSHA and Ref are the analyzed commit and its branch/tag (#443).
// Empty when the commit could not be resolved.
CommitSHA string `json:"commitSHA,omitempty"`
Ref string `json:"ref,omitempty"`
}
ProjectInfo contains information about the analyzed project. Provider names which CI platform produced this PBOM ("gitlab" or "github"). URL is the platform host used by the analysis (full URL on GitLab, host or host/api/v3 on GitHub). GitLabURL is kept for backward compatibility with v0.2.x consumers that key on it; new readers should prefer Provider + URL.
type Summary ¶
type Summary struct {
// Image counts
TotalImages int `json:"totalImages"`
UniqueRegistries int `json:"uniqueRegistries"`
// Include counts
TotalIncludes int `json:"totalIncludes"`
Components int `json:"components"`
ProjectIncludes int `json:"projectIncludes"`
LocalIncludes int `json:"localIncludes"`
RemoteIncludes int `json:"remoteIncludes"`
Templates int `json:"templates"`
// GitHub-specific include counts. Always emitted (matches the
// pattern the GitLab-side counters above use — zero is meaningful
// and gets serialised as `0`, not dropped).
Actions int `json:"actions"`
ReusableWorkflows int `json:"reusableWorkflows"`
}
Summary provides aggregate statistics about the pipeline dependencies