scanner

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	SeverityCritical = "critical"
	SeverityHigh     = "high"
	SeverityMedium   = "medium"
	SeverityLow      = "low"
	SeverityInfo     = "info"
)

Severity levels for findings

Variables

This section is empty.

Functions

func AvailableCategories

func AvailableCategories() []string

func AvailableSeverities

func AvailableSeverities() []string

func DiscoverConfigPath

func DiscoverConfigPath(dir string) (string, error)

func LoadRuleDocs added in v0.4.0

func LoadRuleDocs(fsys fs.FS) error

LoadRuleDocs parses every *.md file in fsys (a directory of rule pages) and populates the registry. Production passes the embedded docs/rules FS; tests pass os.DirFS pointed at the real files. It is safe to call more than once (the last call wins), which keeps test setup simple.

func ParseRepoURL

func ParseRepoURL(input string) (owner, repo string, err error)

ParseRepoURL extracts owner and repo from various GitHub URL formats

func SeverityOrder

func SeverityOrder(severity string) int

SeverityOrder returns a number for sorting (lower = more severe)

Types

type ActionRef

type ActionRef struct {
	// contains filtered or unexported fields
}

ActionRef holds information about an action reference

type ActionVersionPinningRuleOptions

type ActionVersionPinningRuleOptions struct {
	IgnoreSameOwner bool `yaml:"ignore_same_owner"`
}

type ActionsSettingsFacts

type ActionsSettingsFacts struct {
	Permissions                *github.ActionsPermissionsRepository
	AccessFinding              *Finding
	DefaultWorkflowPermissions *github.DefaultWorkflowPermissionRepository
	ForkPRContributorApproval  *github.ContributorApprovalPermissions
}

type Config

type Config struct {
	Rules        ConfigRules       `yaml:"rules"`
	RuleOptions  ConfigRuleOptions `yaml:"rule_options"`
	Overrides    []RuleOverride    `yaml:"overrides"`
	Suppressions []RuleSuppression `yaml:"suppressions"`
}

func LoadConfig

func LoadConfig(path string) (*Config, error)

func LoadConfigFromDir

func LoadConfigFromDir(dir string) (*Config, string, error)

type ConfigRuleOptions

type ConfigRuleOptions struct {
	ActionVersionPinning    ActionVersionPinningRuleOptions    `yaml:"workflows/action-version-pinning"`
	UpdateToolConfiguration UpdateToolConfigurationRuleOptions `yaml:"updates/update-tool-configuration"`
}

type ConfigRules

type ConfigRules struct {
	Include []string `yaml:"include"`
	Exclude []string `yaml:"exclude"`
}

type DebugLogger

type DebugLogger func(repo, msg string)

DebugLogger is a function that receives a single-line debug message. The repo parameter is "owner/repo" and is included so interleaved batch output can be filtered or sorted by repository. When nil, debug output is suppressed entirely.

Fact collection runs concurrently, so a DebugLogger may be called from multiple goroutines at once and must be safe for concurrent use. Each call is expected to emit one whole line so interleaved output stays readable.

type DependabotConfig

type DependabotConfig struct {
	Version int                `yaml:"version"`
	Updates []DependabotUpdate `yaml:"updates"`
}

DependabotConfig represents a dependabot.yml configuration

type DependabotCooldown

type DependabotCooldown struct {
	DefaultDays int `yaml:"default-days"`
}

type DependabotFacts

type DependabotFacts struct {
	Path         string
	Content      string
	Config       *DependabotConfig
	Missing      bool
	Invalid      error
	HasWorkflows bool
	// Unknown is set when presence could not be determined (an indeterminate
	// fetch error, not a clean 404). It is mutually exclusive with Missing:
	// when Unknown, rules must not treat the tool as absent.
	Unknown bool
}

type DependabotUpdate

type DependabotUpdate struct {
	PackageEcosystem string `yaml:"package-ecosystem"`
	Directory        string `yaml:"directory"`
	Schedule         struct {
		Interval string `yaml:"interval"`
	} `yaml:"schedule"`
	Cooldown              *DependabotCooldown `yaml:"cooldown"`
	OpenPullRequestsLimit int                 `yaml:"open-pull-requests-limit"`
}

DependabotUpdate represents a single update configuration

type FactWarning added in v0.4.0

type FactWarning struct {
	Area   string
	Detail string
}

FactWarning records a fact the scanner could not determine because a GitHub request failed for a reason other than a definitive "not found" — a timeout, cancellation, rate limit, 5xx, or network error. These are surfaced on ScanResult.Incomplete so a partial scan is never silently reported as clean.

The distinction matters because the collectors treat a 404 as a meaningful signal ("this config file does not exist"). An indeterminate failure is NOT that signal — we simply don't know — and conflating the two would either invent findings (e.g. "no update tool") or hide them.

func (FactWarning) String added in v0.4.0

func (w FactWarning) String() string

type Finding

type Finding struct {
	ID          string `json:"id"`
	Rule        string `json:"rule,omitempty"`
	Category    string `json:"category,omitempty"`
	Severity    string `json:"severity"`
	Success     bool   `json:"success"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Location    string `json:"location,omitempty"`
	File        string `json:"file,omitempty"`
	Line        int    `json:"line,omitempty"`
	Remediation string `json:"remediation"`
	FixURL      string `json:"fix_url,omitempty"`
	DocURL      string `json:"doc_url,omitempty"`
}

Finding represents a single security issue found during scanning

type RenovateConfig

type RenovateConfig struct {
	Extends         []string              `json:"extends"`
	EnabledManagers []string              `json:"enabledManagers"`
	PinDigests      bool                  `json:"pinDigests"`
	MinReleaseAge   string                `json:"minimumReleaseAge"`
	PackageRules    []RenovatePackageRule `json:"packageRules"`
}

RenovateConfig represents a parsed Renovate configuration file. Only the fields relevant to our security checks are decoded.

type RenovateFacts

type RenovateFacts struct {
	Path         string
	Content      string
	Config       *RenovateConfig
	Missing      bool
	Invalid      error
	HasWorkflows bool
	// Unknown is set when presence could not be determined (an indeterminate
	// fetch error, not a clean 404). It is mutually exclusive with Missing:
	// when Unknown, rules must not treat the tool as absent.
	Unknown bool
}

type RenovatePackageRule

type RenovatePackageRule struct {
	MatchManagers []string `json:"matchManagers"`
	MatchDepTypes []string `json:"matchDepTypes"`
	PinDigests    bool     `json:"pinDigests"`
	MinReleaseAge string   `json:"minimumReleaseAge"`
}

RenovatePackageRule represents one entry in Renovate's packageRules array.

type RuleInfo

type RuleInfo struct {
	Name        string   `json:"name"`
	Aliases     []string `json:"aliases,omitempty"`
	Title       string   `json:"title"`
	Category    string   `json:"category"`
	Severity    string   `json:"severity"`
	Description string   `json:"description"`
	// DocFile is the slug of this rule's page under docs/rules/ (without the
	// .md extension). It is the single source of truth that binds a rule to its
	// in-repo documentation; DocURL() turns it into a link for reports.
	DocFile string `json:"doc_file,omitempty"`
}

func AvailableRules

func AvailableRules() []RuleInfo

func ResolveRuleNames

func ResolveRuleNames(names []string) ([]RuleInfo, error)

func (RuleInfo) DocURL added in v0.4.0

func (r RuleInfo) DocURL() string

DocURL returns the absolute link to this rule's documentation page, or "" if the rule has no DocFile. An absolute URL is used (not a relative path) because the HTML report is a standalone file that may be opened or shared anywhere.

type RuleOverride

type RuleOverride struct {
	Rule     string `yaml:"rule"`
	Severity string `yaml:"severity"`
}

type RuleSuppression

type RuleSuppression struct {
	Rule      string `yaml:"rule"`
	FindingID string `yaml:"finding_id"`
	Path      string `yaml:"path"`
	Reason    string `yaml:"reason"`
}

type ScanFacts

type ScanFacts struct {
	Repository                              *github.Repository
	RepositoryOwner                         string
	DefaultBranch                           string
	Workflows                               []WorkflowFact
	ActionsSettings                         ActionsSettingsFacts
	Dependabot                              DependabotFacts
	Renovate                                RenovateFacts
	ActionVersionPinningIgnoreSameOwner     bool
	UpdateToolConfigurationRequireWorkflows bool

	// Incomplete lists facts that could not be determined because a GitHub
	// request failed for a reason other than a clean 404 (timeout, rate limit,
	// 5xx, …). A non-empty slice means the scan is partial.
	Incomplete []FactWarning
}

type ScanOptions

type ScanOptions struct {
	RuleNames      []string
	Categories     []string
	Severities     []string
	IncludeSuccess bool
	Config         *Config
	DebugLog       DebugLogger
}

type ScanResult

type ScanResult struct {
	RepoFullName string    `json:"repo_full_name"`
	RepoURL      string    `json:"repo_url"`
	Findings     []Finding `json:"findings"`
	ScannedAt    string    `json:"scanned_at"`
	Error        string    `json:"error,omitempty"`
	// Incomplete lists checks that could not be completed (e.g. a timeout or
	// rate limit while fetching a config). A non-empty slice means the findings
	// are partial — the scan must not be read as a clean bill of health. Each
	// entry is "<area>: <cause>".
	Incomplete []string `json:"incomplete,omitempty"`
}

ScanResult holds the complete results for a repository scan

func (*ScanResult) CountBySeverity

func (r *ScanResult) CountBySeverity() map[string]int

CountBySeverity returns counts for each severity level

func (*ScanResult) CountSuccesses

func (r *ScanResult) CountSuccesses() int

type Scanner

type Scanner struct {
	// contains filtered or unexported fields
}

Scanner performs security checks on GitHub repositories

func New

func New() *Scanner

New creates a new Scanner with an unauthenticated GitHub client

func NewWithToken

func NewWithToken(token string) *Scanner

NewWithToken creates a Scanner with an authenticated GitHub client

func (*Scanner) GitHubClient

func (s *Scanner) GitHubClient() *github.Client

GitHubClient returns the underlying github.Client for use in batch operations such as listing repositories for a user or organization.

func (*Scanner) IsAuthenticated

func (s *Scanner) IsAuthenticated() bool

IsAuthenticated reports whether the scanner was constructed with a token.

func (*Scanner) ScanRepo

func (s *Scanner) ScanRepo(ctx context.Context, owner, repo string) (*ScanResult, error)

ScanRepo performs all security checks on a repository.

func (*Scanner) ScanRepoWithOptions

func (s *Scanner) ScanRepoWithOptions(ctx context.Context, owner, repo string, opts ScanOptions) (*ScanResult, error)

type UpdateToolConfigurationRuleOptions

type UpdateToolConfigurationRuleOptions struct {
	RequireWorkflows bool `yaml:"require_workflows"`
}

type WorkflowFact

type WorkflowFact struct {
	Path     string
	Content  string
	Workflow *WorkflowFile
	Valid    bool
}

type WorkflowFile

type WorkflowFile struct {
	Name        string                 `yaml:"name"`
	On          interface{}            `yaml:"on"` // Can be string, []string, or map
	Permissions interface{}            `yaml:"permissions"`
	Jobs        map[string]WorkflowJob `yaml:"jobs"`
}

WorkflowFile represents a parsed GitHub Actions workflow

type WorkflowJob

type WorkflowJob struct {
	Name        string            `yaml:"name"`
	Permissions interface{}       `yaml:"permissions"`
	Steps       []WorkflowStep    `yaml:"steps"`
	RunsOn      interface{}       `yaml:"runs-on"`
	Uses        string            `yaml:"uses"` // For reusable workflows
	Env         map[string]string `yaml:"env"`
}

WorkflowJob represents a job in a workflow

type WorkflowStep

type WorkflowStep struct {
	Name string                 `yaml:"name"`
	Uses string                 `yaml:"uses"`
	Run  string                 `yaml:"run"`
	With map[string]interface{} `yaml:"with"`
	Env  map[string]string      `yaml:"env"`
}

WorkflowStep represents a step in a job

Jump to

Keyboard shortcuts

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