checks

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package checks implements the structural, misleading-sha, and resolver-bound validators run against parsed workflows.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewPrewarmedResolver

func NewPrewarmedResolver(r *resolve.Resolver, live []dep.Dependency) *prewarmedResolver

NewPrewarmedResolver primes the adapter with the live resolution of refs. Pass live==nil when ResolveAllRecursive failed; checks that need a ref will fail open.

Types

type Category

type Category string

Category classifies the state of a workflow or action dependency. The string values are part of the schema surfaced to consumers (SARIF rule IDs, JSON output, doc URL slugs); the frozen-strings test guards against accidental renames.

const (
	// NotPinned means the workflow has action refs but no
	// corresponding dependencies entry in the lockfile.
	NotPinned Category = "not-pinned"
	// ShaAsRef means a dependency is pinned to a bare SHA with no
	// human-readable tag ref alongside it.
	ShaAsRef Category = "sha-as-ref"
	// RefChanged means the workflow uses: ref was manually changed
	// (e.g. v6.2.0 → v6) and the lockfile no longer matches.
	RefChanged Category = "ref-changed"
	// RefMoved means the upstream tag now resolves to a different
	// SHA than what the lockfile has recorded.
	RefMoved Category = "ref-moved"
	// Stale means the pinned SHA no longer matches what the ref
	// resolves to today.
	Stale Category = "stale"
	// MisleadingSHA means a ref looks like a SHA but resolves to a
	// different commit.
	MisleadingSHA Category = "misleading-sha"
	// LockfileForgery means the pinned SHA is not an ancestor of the
	// current ref — the lockfile entry was likely injected or
	// tampered with.
	LockfileForgery Category = "lockfile-forgery"
	// Valid means the dependency is pinned and verified.
	Valid Category = "valid"
	// RunOnly means the workflow has no action refs (only run:
	// steps), so pinning is not applicable.
	RunOnly Category = "run-only"
	// AncestryUnknown means the Compare API couldn't decide whether
	// the pinned SHA is in the ref's history (typically rate-limited
	// or transient error). Non-blocking diagnostic: we know the SHAs
	// differ but can't classify the move as benign-but-known
	// (ref-moved) vs. tampered (lockfile-forgery).
	AncestryUnknown Category = "ancestry-unknown"
	// ReachabilityUnknown means branch_commits couldn't decide
	// whether the pinned SHA is still reachable from any branch in
	// the upstream repo (resolver failure, GraphQL rate limit, etc).
	// Non-blocking diagnostic: surfaced so consumers can retry rather
	// than treating the dep as verified.
	ReachabilityUnknown Category = "reachability-unknown"
	// OnboardingRequired means a `check --no-onboard` run encountered a
	// workflow (or an action within one) that has no existing entry in the
	// lockfile. Under --no-onboard the tool refuses to add new entries: the
	// workflow/action is skipped and surfaced rather than silently pinned.
	// Already-tracked entries are still re-pinned. The operator must onboard
	// explicitly (run `gh actions-lock` without --no-onboard) to add it.
	OnboardingRequired Category = "onboarding-required"
	// VersionRef is an informational nudge: a dependency is pinned with a
	// ref that is not a full semver tag (e.g. v4, v3.1, main). Full semver
	// tags (v4.2.1) each resolve to exactly one commit, making the lock
	// comment durable across re-pins.
	VersionRef Category = "version-ref"
	// LocalAction means the workflow uses at least one local path action
	// (uses: ./some-path). Lockfile onboarding is not supported for
	// workflows that reference local actions — the entire workflow is
	// skipped.
	LocalAction Category = "local-action"
)

func (Category) IsInconclusive

func (c Category) IsInconclusive() bool

IsInconclusive reports whether c represents a diagnostic that couldn't reach a verdict (network/rate-limit fallback). These are surfaced as warnings but are not blocking: consumers (e.g. Dependabot FindingMapper) treat them as "scan inconclusive, retry" rather than "lockfile is bad".

type CheckResolver

type CheckResolver interface {
	// ResolveRef returns the live SHA for owner/repo@ref. ok=false means
	// the resolver could not answer (network failure, unknown ref); checks
	// fail open on that.
	ResolveRef(owner, repo, ref string) (sha string, ok bool)
	// PeelTagObject reports whether a hex SHA names an annotated tag
	// object (or chain of tag-of-tag) and, if so, returns the commit OID
	// it ultimately points at.
	PeelTagObject(ctx context.Context, owner, repo, sha string) (commit string, ok bool)
	// CheckAncestry asks whether candidate is an ancestor of head and
	// returns a short human-readable detail alongside the status — the
	// rate-limit or compare-base detail callers surface to operators.
	CheckAncestry(ctx context.Context, owner, repo, candidate, head string) (resolve.AncestryStatus, string)
}

CheckResolver is the surface the resolver-bound checks need. The production implementation is *prewarmedResolver; tests use stubs.

type Confidence

type Confidence string

Confidence is how certain the producer is the finding is real, modeled on zizmor's audit output.

const (
	// ConfidenceLow marks a signal that could not be fully verified
	// (resolver failure, reachability inconclusive).
	ConfidenceLow Confidence = "low"
	// ConfidenceMedium marks a signal inferred from a fallback
	// (tag-object peel, ancestry unknown due to rate limit).
	ConfidenceMedium Confidence = "medium"
	// ConfidenceHigh marks a signal resting on authoritative data
	// (exact SHA comparison, upstream reachability answer).
	ConfidenceHigh Confidence = "high"
)

type Finding

type Finding struct {
	// WorkflowPath is the path to the workflow file.
	WorkflowPath string
	// Category of the finding.
	Category Category
	// Severity of the finding.
	Severity Severity
	// Confidence of the finding — see the Confidence type docs. Always
	// populated at construction; an empty value is a bug and the
	// no-empty-confidence test will catch it.
	Confidence Confidence
	// ActionRef is the action reference this finding relates to (nil for workflow-level findings).
	ActionRef *parserlock.ActionRef
	// Dependency is the existing pinned dep if any.
	Dependency *dep.Dependency
	// ParentNWO is the dep key of the direct action that pulls in this transitive dep (empty if direct).
	ParentNWO string
	// Detail is a human-readable explanation.
	Detail string
	// Remediation describes what the check command can do about it.
	Remediation string
	// ObservedSHA is the SHA the resolver got at scan time, recorded when
	// it differs from the pinned SHA (e.g. ref-moved, misleading-sha,
	// lockfile-forgery).
	ObservedSHA string
	// DocURL points to docs explaining the finding. Populated by the
	// engine adapter so it's parity-aligned with the editor's
	// codeDescription link; "" when no URL is mapped.
	DocURL string
	// RecommendedTag is the most recent stable tag whose commit is
	// reachable from a branch, populated for unreachable-SHA findings
	// when one can be found. Empty otherwise.
	RecommendedTag string
	// RecommendedSHA is the commit SHA the recommended tag points to.
	RecommendedSHA string
	// RecommendedSearched is true when the release walk ran for this
	// finding (regardless of outcome). Lets renderers distinguish
	// "we didn't look" from "we looked and found nothing."
	RecommendedSearched bool
}

Finding represents a single diagnosed issue (or clean bill) for a workflow.

func RunChecks

RunChecks evaluates all enabled validators against the given parsed workflow and returns findings in catalog order. The lockfile snapshot scopes the structural checks; the resolver enables the resolver-bound checks (misleading-sha, ref-moved, forgery). When r is nil, resolver-bound checks are skipped silently.

Returned findings have their primitive fields populated, plus ActionRef (for direct uses) and Dependency (for ref-tied entries). DocURL and ParentNWO are attached by the caller (diagnoseOneParsed) because they need lookup tables runChecks doesn't carry.

func (*Finding) DepKey

func (f *Finding) DepKey() string

DepKey returns a dependency identifier for display grouping.

func (*Finding) IsValid

func (f *Finding) IsValid() bool

IsValid returns true for findings that don't represent integrity violations.

func (*Finding) IsWarning

func (f *Finding) IsWarning() bool

IsWarning returns true for findings that should render as warnings (not errors).

type InventoryEntry

type InventoryEntry struct {
	Dep    dep.Dependency
	File   string
	Direct bool
	// Parents lists the dep keys of parent composite actions that pull in this
	// transitive dependency. Empty for direct dependencies.
	Parents []string
}

InventoryEntry describes a single dependency with context.

type ParsedWorkflow

type ParsedWorkflow struct {
	Path          string
	Refs          []parserlock.ActionRef
	LocalPaths    []string
	ExistingDeps  []dep.Dependency
	ParseWarnings []string
	LoadErr       error
	DepsErr       error
	// Resolved, when true, instructs DiagnoseParsed to run this
	// workflow's diagnostics with a nil resolver. Network-bound checks
	// (ref-moved) are skipped and the engine relies on
	// purely structural validation against the on-disk lockfile. Caller
	// is asserting "this workflow is already fully resolved" — typically
	// set on the fast path when every direct ref in the workflow is
	// already recorded in the lockfile.
	Resolved bool
}

ParsedWorkflow holds the per-workflow parse result that both phases need. LoadErr / DepsErr capture early failures so DiagnoseParsed can surface them as findings without re-loading the file.

func (ParsedWorkflow) IsFullyRecorded

func (pw ParsedWorkflow) IsFullyRecorded() bool

IsFullyRecorded returns true when every direct ref has a matching lockfile entry — the steady-state happy path.

func (ParsedWorkflow) PartitionRefs

func (pw ParsedWorkflow) PartitionRefs() (recorded, unrecorded []parserlock.ActionRef)

PartitionRefs splits refs into recorded (matching a lockfile entry by NWO@Ref or NWO@SHA) and unrecorded (need network resolution). When an error prevented loading refs or deps, everything is unrecorded.

func (ParsedWorkflow) RecordedDeps

func (pw ParsedWorkflow) RecordedDeps(recorded []parserlock.ActionRef) []dep.Dependency

RecordedDeps returns the subset of ExistingDeps whose NWO@Ref or NWO@SHA matches one of the given recorded refs.

type Report

type Report struct {
	Workflows []WorkflowReport
	// RepoFindings are findings that apply to the repository as a whole
	// (not to any individual workflow).
	RepoFindings []Finding
}

Report aggregates all workflow reports for a check run.

func (*Report) HasInconclusive

func (r *Report) HasInconclusive() bool

HasInconclusive reports whether the report contains any inconclusive findings (reachability-unknown, ancestry-unknown). These are treated as warnings by default, but callers that need a strict gate (e.g. --rescan) can use this to fail when verification couldn't complete.

func (*Report) IsValid

func (r *Report) IsValid() bool

IsValid returns true if all workflows in the report pass validation.

func (*Report) Summary

func (r *Report) Summary() map[Category]int

Summary returns counts by category across all workflows.

func (*Report) WorkflowsNeedingAttention

func (r *Report) WorkflowsNeedingAttention() []WorkflowReport

WorkflowsNeedingAttention returns only workflows with actionable findings.

type Severity

type Severity string

Severity indicates how serious a finding is if it represents a real problem. Pair with Confidence to express how strongly the tool stands behind the call.

const (
	// SeverityOK means the finding represents a clean state — no
	// action needed.
	SeverityOK Severity = "ok"
	// SeverityInfo is purely informational and does not require
	// action.
	SeverityInfo Severity = "info"
	// SeverityWarning indicates a concern worth surfacing but not
	// blocking on.
	SeverityWarning Severity = "warning"
	// SeverityError indicates a blocking issue the operator must
	// resolve.
	SeverityError Severity = "error"
)

type WorkflowReport

type WorkflowReport struct {
	Path     string
	Findings []Finding
	// ActionRefs are all action references found in the workflow.
	ActionRefs []parserlock.ActionRef
	// Deps are the existing pinned dependencies (nil if not pinned).
	Deps []dep.Dependency
	// Inventory lists all dependencies with direct/transitive classification.
	Inventory []InventoryEntry
	// ParseWarnings from ExtractActionRefs (e.g. malformed uses: lines).
	ParseWarnings []string
}

WorkflowReport aggregates all findings for a single workflow file.

func (*WorkflowReport) CountByCategory

func (r *WorkflowReport) CountByCategory(c Category) int

CountByCategory returns the number of findings with the given category.

func (*WorkflowReport) IsValid

func (wr *WorkflowReport) IsValid() bool

IsValid returns true if no findings represent integrity violations.

func (*WorkflowReport) NeedsAttention

func (r *WorkflowReport) NeedsAttention() bool

NeedsAttention returns true if this workflow has any non-OK findings.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL