Documentation
¶
Overview ¶
Package ghasetup wires third-party security scanners into a repository's GitHub Actions workflows.
The catalog is the single source of truth: `vulnetix gha setup` renders workflow jobs from it, and the published documentation examples are generated from it too. A tool documented on the website is therefore, by construction, the same job the command writes and the same job that runs in production.
Index ¶
- Constants
- func IsManaged(content string) bool
- func ManualTools(c *Catalog) []string
- func Render(c *Catalog, ids []string, opt Options) (string, error)
- func RepoRoot(dir string) (string, error)
- func RepoRootOrCwd(root string) string
- func ScheduleCronFor(slug string) string
- func ToolsInWorkflow(c *Catalog, content string) []string
- type Catalog
- type Detect
- type Options
- type Remote
- type RepoSignals
- type Step
- type Tool
Constants ¶
const DefaultJobTimeoutMinutes = 60
DefaultJobTimeoutMinutes caps every scanner job.
Measured over 1,645 jobs on the self-hosted pool: p50 39 seconds, 95.2% under five minutes, p99 29 minutes. Sixty minutes is a little over twice the p99, so it never touches a scan that is merely slow, and it turns a hang from six hours of a held runner — GitHub's own job ceiling, which is what four of these actually hit — into an hour.
const DefaultScheduleCron = "17 3 * * 1"
DefaultScheduleCron is the schedule used when one is asked for and the repository has no slug to derive a slot from. It is a fallback, not a fleet default: every repository that knows its own name gets its own slot from ScheduleCronFor instead.
const WorkflowPath = ".github/workflows/vulnetix-scanners.yml"
WorkflowPath is the file `gha setup` manages, relative to the repository root.
It is deliberately separate from any hand-written workflow: the file is regenerated in full on every `gha setup`, so it must not be somewhere a user keeps their own jobs.
Variables ¶
This section is empty.
Functions ¶
func ManualTools ¶ added in v3.92.0
ManualTools returns the ids --detect will never select, with the reason, so the command can say what it left out rather than silently narrowing the set.
func Render ¶
Render produces the complete workflow for the given tool ids.
The Vulnetix CLI is installed unpinned on purpose. install.sh resolves the latest release, so a workflow written today keeps working without anyone coming back to bump a version — which is exactly how a fleet ends up spread across three different CLI versions. Third-party scanners are pinned, because there the reproducibility of the scan matters more than being current.
func RepoRootOrCwd ¶ added in v3.92.0
RepoRootOrCwd is a convenience for callers that already resolved a root.
func ScheduleCronFor ¶ added in v3.93.2
ScheduleCronFor derives a stable weekly slot from the repository slug.
Deterministic on purpose: the workflow is regenerated in full on every `gha setup`, so a random or clock-derived time would rewrite the schedule (and produce a diff) every single run. Hashing the slug means the same repository always lands in the same slot, and a repository added to the fleet does not move anybody else — which an index-into-a-sorted-list scheme would.
The week is cut into 20-minute slots, three per hour, 504 in all. The CLI runs inside one repository and cannot see the others, so slots are assigned by hash rather than allocated; two repositories colliding is possible and costs only that one of them queues behind the other.
func ToolsInWorkflow ¶
ToolsInWorkflow returns the catalog tool ids already wired into an existing managed workflow, so `gha setup` adds to the set rather than replacing it.
Types ¶
type Catalog ¶
Catalog is the parsed tool catalog.
type Detect ¶ added in v3.92.0
type Detect struct {
// Always marks a tool that works on any repository: the SBOM producers,
// the secret scanners, the multi-language SAST engines.
Always bool `json:"always,omitempty"`
// Files are basenames or directory names that must exist somewhere in the
// tree. A match on any one of them selects the tool.
Files []string `json:"files,omitempty"`
// Extensions are file suffixes, matched the same way.
Extensions []string `json:"extensions,omitempty"`
// Manual keeps a tool out of --detect entirely. These are the ones that
// need something the repository cannot supply: a licence key (Snyk), or a
// live target the operator must nominate (ZAP, Nuclei). Selecting one by
// name still works.
Manual bool `json:"manual,omitempty"`
}
Detect says when `gha setup --detect` should pick a tool for a repository.
Every recipe already refuses to run against a tree it has nothing to say about ("no go.mod found; skipping gosec"), so a wrong guess here costs a no-op job rather than a bad result. What it buys is a workflow that names only the scanners this repository can actually feed, which is the difference between reading a run and scrolling past forty skipped jobs.
func (Detect) Matches ¶ added in v3.92.0
func (d Detect) Matches(sig *RepoSignals) bool
Matches reports whether a tool applies to a repository with these signals.
type Options ¶
type Options struct {
// SelfHostedLabels, when set, replaces `ubuntu-latest` on every job.
SelfHostedLabels []string
// OrgID is written into the publish job's env as a literal only when the
// caller explicitly asks; otherwise the secret reference is used, which is
// what almost every repository wants.
OrgIDLiteral string
// Triggers are the workflow's `on:` events. Empty means push +
// workflow_dispatch, which is what this command has always written.
//
// It is worth choosing deliberately once a repository runs more than a
// handful of scanners: a full detected set is twenty-odd jobs, and on a
// small runner pool "on every push" turns a two-minute build into an hour
// of queue for everyone else.
Triggers []string
// ScheduleCron is the five-field expression used when Triggers includes
// "schedule". Leave it empty and the expression is derived from RepoSlug,
// which is what staggers the fleet; set it only to pin one repository to a
// time somebody actually cares about.
ScheduleCron string
// RepoSlug is "owner/repo", used to derive the schedule when ScheduleCron
// is empty. Empty falls back to DefaultScheduleCron.
RepoSlug string
}
Options controls how the workflow is rendered.
type Remote ¶
type Remote struct {
// URL is the raw origin URL, empty when there is no origin.
URL string
// Host is the parsed host ("github.com", "gitlab.com", …), empty when the
// URL could not be parsed.
Host string
// Slug is "owner/repo" when the URL could be parsed.
Slug string
// IsGitHub reports whether this is github.com or a GitHub Enterprise host.
IsGitHub bool
}
Remote describes the repository's origin.
func DetectRemote ¶
DetectRemote inspects the origin remote of the repository at dir.
A workflow file is only meaningful on GitHub, so the caller warns when this is something else. It is a warning rather than an error: mirrors, forks about to be pushed to GitHub, and Enterprise hosts with unusual names are all legitimate reasons to write the file anyway.
type RepoSignals ¶ added in v3.92.0
RepoSignals is what a repository contains, reduced to the two questions the catalog asks: which files are present, and which extensions appear.
func DetectTools ¶ added in v3.92.0
func DetectTools(c *Catalog, root string) ([]string, *RepoSignals, error)
DetectTools returns the catalog ids that apply to the repository at root, sorted, along with the signals they were chosen from.
func ScanRepo ¶ added in v3.92.0
func ScanRepo(root string) (*RepoSignals, error)
ScanRepo walks a repository and records the signals the catalog matches on.
type Step ¶
type Step struct {
Name string `json:"name,omitempty"`
ID string `json:"id,omitempty"`
// If is a GitHub expression written verbatim into the step's `if:`. A step
// that cannot run everywhere is gated on a preceding detection step rather
// than left to hang the job on a runner that cannot support it.
If string `json:"if,omitempty"`
Uses string `json:"uses,omitempty"`
Run string `json:"run,omitempty"`
With map[string]any `json:"with,omitempty"`
Env map[string]string `json:"env,omitempty"`
// TimeoutMinutes caps one step. The job cap cannot rescue a step that
// blocks on a prompt that never arrives: the job spends its whole budget
// inside setup and dies having run no scanner at all.
TimeoutMinutes int `json:"timeoutMinutes,omitempty"`
}
Step is one workflow step. Exactly one of Uses or Run is set.
type Tool ¶
type Tool struct {
ID string `json:"id"`
Name string `json:"name"`
JobName string `json:"jobName"`
Category string `json:"category"`
Description string `json:"description"`
Artifact string `json:"artifact"`
Paths []string `json:"paths"`
Note string `json:"note,omitempty"`
// TimeoutMinutes overrides DefaultJobTimeoutMinutes for this tool.
//
// Set it only where the measured runtime says the default is wrong: too
// tight and a legitimate slow scan is killed, too loose and a hung one
// holds a runner until GitHub's six-hour ceiling. Zero means "use the
// default", which is the right answer for almost every scanner.
TimeoutMinutes int `json:"timeoutMinutes,omitempty"`
Detect Detect `json:"detect"`
Steps []Step `json:"steps"`
}
Tool is one scanner: the job that runs it and the artifact it leaves behind.
Directories
¶
| Path | Synopsis |
|---|---|
|
Command ghasetupgen renders the third-party scanner documentation from the single source of truth: the embedded catalog (internal/ghasetup/catalog).
|
Command ghasetupgen renders the third-party scanner documentation from the single source of truth: the embedded catalog (internal/ghasetup/catalog). |