Documentation
¶
Overview ¶
Package secretscan finds credentials committed to a build context or stored as plain environment values, and applies a per-app deploy policy. A finding never carries the matched value, only a short fingerprint.
Index ¶
- Constants
- Variables
- func Describe(reason string, f []Finding) string
- func Fingerprint(ruleID, value string) string
- func RuleDescription(id string) string
- type AllowEntry
- type BlockedError
- type Finding
- type Input
- type Limits
- type Option
- type Policy
- type Result
- type Rule
- type Run
- type SQLStore
- func (s *SQLStore) AddAllow(ctx context.Context, e AllowEntry) error
- func (s *SQLStore) DeleteApp(ctx context.Context, app string) error
- func (s *SQLStore) GetPolicy(ctx context.Context, app string) (Policy, error)
- func (s *SQLStore) GetRun(ctx context.Context, attemptID string) (Run, error)
- func (s *SQLStore) ListAllow(ctx context.Context, app string) ([]AllowEntry, error)
- func (s *SQLStore) RemoveAllow(ctx context.Context, app, fingerprint string) (bool, error)
- func (s *SQLStore) SaveRun(ctx context.Context, r Run) error
- func (s *SQLStore) SetPolicy(ctx context.Context, app string, p Policy) error
- type Service
- func (s *Service) Allow(ctx context.Context, app, fingerprint, reason, actor string) (AllowEntry, error)
- func (s *Service) AllowList(ctx context.Context, app string) ([]AllowEntry, error)
- func (s *Service) BeforeDeploy(ctx context.Context, in Input) error
- func (s *Service) DeleteApp(ctx context.Context, app string) error
- func (s *Service) Limits() Limits
- func (s *Service) Policy(ctx context.Context, app string) (Policy, error)
- func (s *Service) Run(ctx context.Context, attemptID string) (Run, error)
- func (s *Service) ScanEnvNow(ctx context.Context, app string, env map[string]string) (Result, error)
- func (s *Service) SetPolicy(ctx context.Context, app, policy string) (Policy, error)
- func (s *Service) Unallow(ctx context.Context, app, fingerprint string) (bool, error)
- type Store
Constants ¶
const ( RuleURLCredentials = "url-credentials" RuleHighEntropy = "high-entropy-secret" )
RuleURLCredentials and RuleHighEntropy are rules with extra handling.
const ( SourceFile = "file" SourceEnv = "env" )
Sources a finding came from.
const ( ActionSkipped = "skipped" ActionAllow = "allow" ActionWarn = "warn" ActionBlock = "block" )
Gate actions stored on a run.
const ( StatusComplete = "complete" StatusPartial = "partial" StatusError = "error" )
Run statuses.
Variables ¶
var ErrInvalid = errors.New("secretscan: invalid request")
ErrInvalid marks a rejected setting or request.
var ErrRunNotFound = errors.New("secretscan: no scan recorded for this deploy")
ErrRunNotFound means no scan was recorded for the deploy attempt.
var Rules = []Rule{ {ID: "aws-access-key-id", Desc: "AWS access key id", Re: regexp.MustCompile(`\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b`)}, {ID: "aws-secret-access-key", Desc: "AWS secret access key", Re: regexp.MustCompile(`(?i)aws[\w .-]{0,24}secret[\w .-]{0,24}['"=:\s]{1,4}([A-Za-z0-9/+=]{40})`), Group: 1}, {ID: "gcp-api-key", Desc: "Google API key", Re: regexp.MustCompile(`\bAIza[0-9A-Za-z_-]{35}\b`)}, {ID: "azure-storage-key", Desc: "Azure storage account key", Re: regexp.MustCompile(`AccountKey=([A-Za-z0-9+/]{60,}={0,2})`), Group: 1}, {ID: "private-key", Desc: "private key block", Re: regexp.MustCompile(`-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----`)}, {ID: "github-token", Desc: "GitHub token", Re: regexp.MustCompile(`\bgh[pousr]_[A-Za-z0-9]{36,255}\b`)}, {ID: "github-fine-grained-token", Desc: "GitHub fine-grained token", Re: regexp.MustCompile(`\bgithub_pat_[A-Za-z0-9_]{22,255}\b`)}, {ID: "gitlab-token", Desc: "GitLab personal access token", Re: regexp.MustCompile(`\bglpat-[A-Za-z0-9_-]{20,}\b`)}, {ID: "slack-token", Desc: "Slack token", Re: regexp.MustCompile(`\bxox[abprs]-[A-Za-z0-9-]{10,}\b`)}, {ID: "slack-webhook", Desc: "Slack webhook URL", Re: regexp.MustCompile(`https://hooks\.slack\.com/services/T[A-Z0-9]+/B[A-Z0-9]+/[A-Za-z0-9]+`)}, {ID: "stripe-secret-key", Desc: "Stripe live secret key", Re: regexp.MustCompile(`\b[sr]k_live_[0-9A-Za-z]{16,}\b`)}, {ID: "sendgrid-key", Desc: "SendGrid API key", Re: regexp.MustCompile(`\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b`)}, {ID: "npm-token", Desc: "npm access token", Re: regexp.MustCompile(`\bnpm_[A-Za-z0-9]{36}\b`)}, {ID: "jwt", Desc: "JSON web token", Re: regexp.MustCompile(`\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b`)}, {ID: RuleURLCredentials, Desc: "password embedded in a URL", Re: regexp.MustCompile(`\b[a-z][a-z0-9+.-]{1,20}://[^\s:/@'"]{1,64}:([^\s:/@'"]{8,128})@[^\s/'"]+`), Group: 1}, }
Rules is the built-in ruleset.
Functions ¶
func Fingerprint ¶
Fingerprint is a stable short hash of the rule and the matched value, so an allow-list entry survives a file move without exposing the value.
func RuleDescription ¶
RuleDescription is the human label for a rule id, or the id itself when unknown.
Types ¶
type AllowEntry ¶
type AllowEntry struct {
App string `json:"app,omitempty"`
Fingerprint string `json:"fingerprint"`
Reason string `json:"reason"`
Actor string `json:"actor"`
CreatedAt time.Time `json:"created_at"`
}
AllowEntry suppresses one fingerprint for an app, with who and why.
type BlockedError ¶
type BlockedError struct{ Reason string }
BlockedError is returned to the deploy pipeline when the policy blocks a release.
func (*BlockedError) Error ¶
func (e *BlockedError) Error() string
type Finding ¶
type Finding struct {
RuleID string `json:"rule_id"`
Source string `json:"source"`
Path string `json:"path,omitempty"`
Line int `json:"line,omitempty"`
Var string `json:"var,omitempty"`
Fingerprint string `json:"fingerprint"`
Allowed bool `json:"allowed,omitempty"`
}
Finding is one suspected credential. Path is relative to the build context; Var is set for env findings.
type Input ¶
Input is what one deploy hands the scanner. Root is the build context (empty for image deploys) and Env only the plain, non-secret values.
type Limits ¶
type Limits struct {
MaxFiles int
MaxFileBytes int64
MaxTotalBytes int64
MaxFindings int
Timeout time.Duration
}
Limits bound a scan so a huge repo cannot stall a deploy.
func DefaultLimits ¶
func DefaultLimits() Limits
DefaultLimits are the built-in caps, overridable through the environment.
type Option ¶
type Option func(*Service)
Option customizes a Service.
type Policy ¶
type Policy string
Policy is what a scan may do to a release.
Policies. Warn is the default.
type Result ¶
type Result struct {
Findings []Finding `json:"findings"`
FilesScanned int `json:"files_scanned"`
FilesSkipped int `json:"files_skipped"`
Truncated bool `json:"truncated"`
}
Result is a scan's outcome. Truncated means a cap stopped the walk early.
type Rule ¶
Rule is one token format. Group is the capture group holding the secret (0 is the whole match).
type Run ¶
type Run struct {
AttemptID string `json:"deployment_id"`
App string `json:"app"`
Policy Policy `json:"policy"`
Action string `json:"action"`
Reason string `json:"reason,omitempty"`
Status string `json:"status"`
Error string `json:"error,omitempty"`
FilesScanned int `json:"files_scanned"`
Truncated bool `json:"truncated"`
Findings []Finding `json:"findings"`
ScannedAt time.Time `json:"scanned_at"`
}
Run is the persisted outcome of one deploy's scan.
type SQLStore ¶
type SQLStore struct {
// contains filtered or unexported fields
}
SQLStore implements Store over the control plane database.
func (*SQLStore) AddAllow ¶
func (s *SQLStore) AddAllow(ctx context.Context, e AllowEntry) error
AddAllow inserts an entry, keeping the first one's actor and reason on a repeat.
func (*SQLStore) RemoveAllow ¶
RemoveAllow deletes an entry and reports whether it existed.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service scans deploy inputs and applies each app's policy.
func (*Service) Allow ¶
func (s *Service) Allow(ctx context.Context, app, fingerprint, reason, actor string) (AllowEntry, error)
Allow adds a fingerprint to the allow-list; who and why are required.
func (*Service) BeforeDeploy ¶
BeforeDeploy scans in and applies the app's policy. Only a block returns an error. It runs on the deploy job goroutine, bounded by the scan timeout.
func (*Service) ScanEnvNow ¶
func (s *Service) ScanEnvNow(ctx context.Context, app string, env map[string]string) (Result, error)
ScanEnvNow scans an env set without persisting, with the allow-list applied.
type Store ¶
type Store interface {
GetPolicy(ctx context.Context, app string) (Policy, error)
SetPolicy(ctx context.Context, app string, p Policy) error
ListAllow(ctx context.Context, app string) ([]AllowEntry, error)
AddAllow(ctx context.Context, e AllowEntry) error
RemoveAllow(ctx context.Context, app, fingerprint string) (bool, error)
SaveRun(ctx context.Context, r Run) error
GetRun(ctx context.Context, attemptID string) (Run, error)
DeleteApp(ctx context.Context, app string) error
}
Store persists policy, allow-list entries and run records; *SQLStore satisfies it.