Documentation
¶
Overview ¶
Package poutine provides shared poutine CI/CD security analysis functionality. This package is used by both Kitchen (remote analysis) and Brisket (local analysis).
Index ¶
Constants ¶
const ( CloudProviderAWS = "aws" CloudProviderGCP = "gcp" CloudProviderAzure = "azure" )
CloudProvider constants
Variables ¶
var ExtendedRules = []string{
"debug_enabled",
"excessive_permissions",
}
ExtendedRules adds post-exploitation relevant rules for comprehensive analysis. These are useful for understanding the full attack surface but are not strictly "initial access" vectors.
var OffensiveRules = []string{
"injection",
"pr_runs_on_self_hosted",
"untrusted_checkout_exec",
}
OffensiveRules are poutine rules focused on initial access attack vectors. These rules identify vulnerabilities that can be exploited to gain code execution in CI/CD pipelines from an unauthenticated or low-privileged position.
This is the single source of truth for offensive rules across the codebase.
Functions ¶
This section is empty.
Types ¶
type AnalysisObserver ¶
type AnalysisObserver interface {
OnAnalysisStarted(description string)
OnDiscoveryCompleted(org string, totalCount int)
OnRepoStarted(repo string)
OnRepoCompleted(repo string)
OnRepoError(repo string, err error)
OnRepoSkipped(repo string, reason string)
OnStepCompleted(description string)
OnFinalizeStarted(totalPackages int)
OnFinalizeCompleted()
}
type AnalysisResult ¶
type AnalysisResult struct {
Success bool `json:"success"`
Duration time.Duration `json:"duration"`
// Target metadata
Target string `json:"target"` // org or org/repo (remote) or path (local)
TargetType string `json:"target_type"` // "org", "repo", or "local"
Repository string `json:"repository,omitempty"`
// Findings summary
ReposAnalyzed int `json:"repos_analyzed"`
TotalFindings int `json:"total_findings"`
CriticalFindings int `json:"critical_findings"`
HighFindings int `json:"high_findings"`
MediumFindings int `json:"medium_findings"`
LowFindings int `json:"low_findings"`
// Detailed findings
Findings []Finding `json:"findings"`
// Workflow metadata (for loot scoring)
Workflows []WorkflowMeta `json:"workflows,omitempty"`
// All repos that were analyzed (for tracking private repos with no vulns)
AnalyzedRepos []string `json:"analyzed_repos,omitempty"`
// Secret findings from gitleaks scanning
SecretFindings []SecretFinding `json:"secret_findings,omitempty"`
// Errors (non-fatal issues during analysis)
Errors []string `json:"errors,omitempty"`
}
AnalysisResult represents the result of a poutine CI/CD security analysis. This is the unified result type used by both remote (Kitchen) and local (Brisket) analysis.
func AnalyzeLocal ¶
func AnalyzeLocal(ctx context.Context, path string) (*AnalysisResult, error)
AnalyzeLocal performs analysis on a local filesystem path. This is used by Brisket for on-target scanning.
func AnalyzeRemote ¶
func AnalyzeRemote(ctx context.Context, token, target, targetType string) (*AnalysisResult, error)
AnalyzeRemote performs analysis on a remote GitHub target using the GitHub API. This is used by Kitchen for pre-agent reconnaissance. The token is used ephemerally and NOT persisted.
func AnalyzeRemoteWithObserver ¶
func AnalyzeRemoteWithObserver(ctx context.Context, token, target, targetType string, observer AnalysisObserver) (*AnalysisResult, error)
type AppAction ¶
type AppAction struct {
Action string `json:"action"`
Version string `json:"version"`
PrivateKey string `json:"private_key"`
AppID string `json:"app_id"`
HardcodedAppID string `json:"hardcoded_app_id,omitempty"`
}
AppAction represents a detected GitHub App token action in a workflow.
type CloudAction ¶
type CloudAction struct {
Provider string `json:"provider"` // aws, gcp, azure
Action string `json:"action"` // Full action name (owner/repo)
Version string `json:"version"` // Action version (@v1, @main, etc.)
Inputs map[string]string `json:"inputs"` // Relevant inputs for pivot
}
CloudAction represents a detected cloud provider action in a workflow.
type Finding ¶
type Finding struct {
ID string `json:"id"` // Generated ID (V001, V002, etc.)
Repository string `json:"repository"` // org/repo
Workflow string `json:"workflow"` // .github/workflows/foo.yml
Line int `json:"line,omitempty"` // Line number in workflow
Job string `json:"job,omitempty"` // Job name
Step string `json:"step,omitempty"` // Step name
RuleID string `json:"rule_id"` // poutine rule ID
Title string `json:"title"` // Finding title
Description string `json:"description,omitempty"` // Rule description
Severity string `json:"severity"` // "critical", "high", "medium", "low"
Details string `json:"details,omitempty"` // Additional details
// Injection-specific metadata (for initial access exploitation)
Context string `json:"context,omitempty"` // Injection context: "bash_run", "github_script", etc.
BashContext string `json:"bash_context,omitempty"` // Bash wrapper context when applicable
Trigger string `json:"trigger,omitempty"` // Workflow trigger: "pull_request", "push", etc.
Expression string `json:"expression,omitempty"` // The vulnerable expression (${{ ... }})
InjectionSources []string `json:"injection_sources,omitempty"`
ReferencedSecrets []string `json:"referenced_secrets,omitempty"`
LOTPTool string `json:"lotp_tool,omitempty"`
LOTPAction string `json:"lotp_action,omitempty"`
LOTPTargets []string `json:"lotp_targets,omitempty"`
CachePoisonWriter bool `json:"cache_poison_writer,omitempty"`
CachePoisonReason string `json:"cache_poison_reason,omitempty"`
CachePoisonVictims []cachepoison.VictimCandidate `json:"cache_poison_victims,omitempty"`
// Gate constraints (if: conditions on enclosing job/step)
GateTriggers []string `json:"gate_triggers,omitempty"`
GateRaw string `json:"gate_raw,omitempty"`
GateUnsolvable string `json:"gate_unsolvable,omitempty"`
// Fingerprint for deduplication
Fingerprint string `json:"fingerprint,omitempty"`
}
Finding represents a single vulnerability found during analysis.
type GateConstraint ¶
func ParseGateExpression ¶
func ParseGateExpression(ifExpr, injectionSource string) GateConstraint
type JobMeta ¶
type JobMeta struct {
ID string `json:"id"` // Mandatory job key from YAML
DisplayName string `json:"display_name,omitempty"` // Optional name attribute
Secrets []string `json:"secrets,omitempty"`
CloudActions []CloudAction `json:"cloud_actions,omitempty"`
AppActions []AppAction `json:"app_actions,omitempty"`
SecretTypes map[string]string `json:"secret_types,omitempty"`
HasOIDC bool `json:"has_oidc,omitempty"`
HasWrite bool `json:"has_write,omitempty"`
SelfHosted bool `json:"self_hosted,omitempty"`
GitHubTokenRW bool `json:"github_token_rw,omitempty"`
}
JobMeta contains metadata about a specific job.
type NoopFormatter ¶
type NoopFormatter struct{}
NoopFormatter is a no-op formatter that satisfies the analyze.Formatter interface. We process results directly rather than using poutine's built-in formatters.
func (*NoopFormatter) Format ¶
func (f *NoopFormatter) Format(_ context.Context, _ []*models.PackageInsights) error
Format does nothing - we handle results ourselves.
func (*NoopFormatter) FormatWithPath ¶
func (f *NoopFormatter) FormatWithPath(_ context.Context, _ []*models.PackageInsights, _ map[string][]*models.RepoInfo) error
FormatWithPath does nothing - we handle results ourselves.
type SecretFinding ¶
type SecretFinding struct {
RuleID string `json:"rule_id"`
Description string `json:"description"`
Repository string `json:"repository"`
File string `json:"file"`
StartLine int `json:"start_line"`
Secret string `json:"secret"`
Fingerprint string `json:"fingerprint"`
Entropy float32 `json:"entropy"`
}
SecretFinding represents a secret found by gitleaks during analysis.
type WorkflowMeta ¶
type WorkflowMeta struct {
Repository string `json:"repository"`
Path string `json:"path"`
Secrets []string `json:"secrets,omitempty"`
JobSecrets map[string][]string `json:"job_secrets,omitempty"`
Jobs []JobMeta `json:"jobs,omitempty"`
SecretTypes map[string]string `json:"secret_types,omitempty"`
HardcodedAppIDs []string `json:"hardcoded_app_ids,omitempty"`
HasOIDC bool `json:"has_oidc,omitempty"`
HasWrite bool `json:"has_write,omitempty"`
SelfHosted bool `json:"self_hosted,omitempty"`
CachePoisonVictims []cachepoison.VictimCandidate `json:"cache_poison_victims,omitempty"`
}
WorkflowMeta contains metadata about a workflow for loot potential scoring.