security

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package security implements the opt-in deterministic security gate (spec 05). It scans the operator's own tracked files for accidentally committed secrets, prompt-injection payloads, and typosquatted dependencies. Every scanner is a pure function of tracked file contents + embedded rule data + the allowlist: no network, no LLM, stable finding order (R7).

Index

Constants

View Source
const DepEvidenceSchema = "dep-evidence/v1"

DepEvidenceSchema pins the version of the offline adapter artifact this gate accepts. External vulnerability/provenance facts arrive as a pinned artifact produced out of band (scripts/dep-evidence.sh); the gate itself never touches the network — it validates and projects the pinned bytes (R6.2).

View Source
const PolicyVersion = "1"
View Source
const RegressionSchemaV1 = "security-regression/v1"

Variables

This section is empty.

Functions

func AppendException added in v0.4.0

func AppendException(root string, e Exception) error

func ConfigForPolicy added in v0.4.0

func ConfigForPolicy(cfg core.SecurityConfig, p PolicyV1) core.SecurityConfig

func ExceptionDigest added in v0.4.0

func ExceptionDigest(raw []byte) string

func GateFindings

func GateFindings(result Result) []gates.Finding

GateFindings projects an analysis Result to gate findings: allowlisted and off findings drop out; error/warn map to gate severities.

func LoadExceptions added in v0.4.0

func LoadExceptions(root, revision, environment string, now time.Time) (ExceptionSet, []Finding)

func ManifestDigest added in v0.4.0

func ManifestDigest(root string) (string, error)

ManifestDigest is the sha256 over the concatenated manifest bytes (go.mod then go.sum, each skipped when absent). It matches `cat go.mod go.sum | sha256sum` so scripts/dep-evidence.sh and this gate pin the same manifest state.

func ValidateException added in v0.4.0

func ValidateException(e Exception) error

Types

type DeclaredDep added in v0.4.0

type DeclaredDep struct {
	Reason string `json:"reason"`
	Source string `json:"source"`
}

DeclaredDep is the operator's approved reason/source for one dependency, declared out of band (e.g. .specd/security/dependencies.json). Both fields are required — a dependency without a stated reason and a stated source is treated as unapproved (R6.1).

type DepEvidenceItem added in v0.4.0

type DepEvidenceItem struct {
	Module   string `json:"module"`
	Severity string `json:"severity"`
	Advisory string `json:"advisory"`
	Summary  string `json:"summary"`
}

DepEvidenceItem is one advisory/provenance fact from the pinned artifact.

type DepEvidenceV1 added in v0.4.0

type DepEvidenceV1 struct {
	Schema         string            `json:"schema"`
	GeneratedAt    string            `json:"generated_at"`
	ManifestDigest string            `json:"manifest_digest"`
	Findings       []DepEvidenceItem `json:"findings"`
}

DepEvidenceV1 is the offline dependency-evidence artifact. ManifestDigest ties the artifact to the exact manifests it was produced for: a mismatch means the evidence is stale and fails closed.

type DependencyPolicy added in v0.4.0

type DependencyPolicy struct {
	Profile           string
	RegistryAllowlist []string
	Declared          map[string]DeclaredDep
}

DependencyPolicy governs the manifest-diff dependency gate. Profile selects finding severity (production → error, otherwise warn). RegistryAllowlist lists approved module path prefixes; empty means the host check is not enforced. Declared is the approved baseline — a dependency absent here is "new" and must carry a reason/source before it is allowed.

type Exception added in v0.4.0

type Exception struct {
	Finding             string `json:"finding"`
	Action              string `json:"action"`
	Reason              string `json:"reason"`
	Ticket              string `json:"ticket"`
	Owner               string `json:"owner"`
	Scope               string `json:"scope"`
	Revision            string `json:"revision"`
	Environment         string `json:"environment"`
	IssuedAt            string `json:"issued_at"`
	ExpiresAt           string `json:"expires_at"`
	CompensatingControl string `json:"compensating_control"`
	Approver            string `json:"approver"`
}

Exception is one immutable, governed suppression record. Revocation is a new record with Action="revoke" and the same Finding; prior records remain audit history in the append-only JSONL ledger.

type ExceptionSet added in v0.4.0

type ExceptionSet struct {
	Records []Exception
	Digest  string
	// contains filtered or unexported fields
}

func (ExceptionSet) Allows added in v0.4.0

func (s ExceptionSet) Allows(finding string) bool

type Finding

type Finding struct {
	Scanner     string `json:"scanner"`
	Rule        string `json:"rule"`
	File        string `json:"file"`
	Line        int    `json:"line"`
	Fingerprint string `json:"fingerprint"`
	Excerpt     string `json:"excerpt"`
	// Severity is filled by the gate from config; empty until resolved.
	Severity string `json:"severity,omitempty"`
	// Allowlisted is set by the gate when an allow.json entry suppresses the
	// finding. Recorded (not dropped) so reports show what was waived (R6).
	Allowlisted bool `json:"allowlisted,omitempty"`
}

Finding is one scanner hit before severity resolution. Fingerprint pins the finding to (rule + path + matched content) so a reasoned allowlist entry survives the match moving lines but is invalidated by editing the match (R2). Excerpt is always redacted — a secrets scanner that prints secrets into CI logs creates the leak it exists to prevent (R1).

func ScanDangerous added in v0.4.0

func ScanDangerous(root string, diff scope.Diff, profile string) []Finding

ScanDangerous runs deterministic dangerous-change policies over a normalized diff (R6.3): destructive shell fragments, world-writable / unexpected-exec mode changes, auth/ownership policy changes, generated secret files, and symlink escapes. Profile selects severity (production → error); generated secrets and symlink escapes always fail closed at error. False positives are controlled by scanning only added/changed files, targeted patterns, script extension carve-outs, and the fingerprint allowlist reused by the caller.

func ScanDepEvidence added in v0.4.0

func ScanDepEvidence(artifact []byte, manifestDigest string) []Finding

ScanDepEvidence validates a pinned offline artifact against the current manifest digest and projects its advisories to findings. Malformed, wrong schema, and stale (digest mismatch) all fail closed at error severity — the gate never trusts unvalidated or outdated supply-chain evidence.

func ScanManifest added in v0.4.0

func ScanManifest(files []TrackedFile, policy DependencyPolicy) []Finding

ScanManifest runs the dependency policy over the given manifests/lockfiles.

type Gate

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

Gate is the opt-in security gate registered by `check --security`. It scans tracked files and resolves per-scanner severity from config.

func New

func New(cfg core.SecurityConfig) Gate

New builds the gate with the resolved per-scanner severity config.

func (Gate) Name

func (Gate) Name() string

func (Gate) Run

func (g Gate) Run(ctx gates.CheckCtx) []gates.Finding

Run enumerates tracked files under ctx.Root and returns gate findings. Error findings fail the gate; warn findings print but pass; off scanners are skipped (R5). Allowlisted findings are suppressed from the gate result (recorded separately by the caller for reports, R6).

type PolicyV1 added in v0.4.0

type PolicyV1 struct {
	PolicyVersion     string            `json:"policy_version"`
	Profile           string            `json:"profile"`
	ScannerSeverities map[string]string `json:"scanner_severities"`
	RequiredGates     []string          `json:"required_gates"`
	SandboxRequired   bool              `json:"sandbox_required"`
	NetworkDefault    string            `json:"network_default"`
	PolicyDigest      string            `json:"policy_digest"`
}

func ResolvePolicy added in v0.4.0

func ResolvePolicy(cfg core.SecurityConfig) (PolicyV1, error)

type RegressionCorpusV1 added in v0.4.0

type RegressionCorpusV1 struct {
	SchemaVersion string                 `json:"schema_version"`
	PolicyDigest  string                 `json:"policy_digest"`
	Incidents     []RegressionIncidentV1 `json:"incidents"`
}

func LoadIncidentCorpus added in v0.4.0

func LoadIncidentCorpus(path, policyDigest string) (RegressionCorpusV1, error)

LoadIncidentCorpus validates promoted incident attestations offline. Policy digest mismatch makes the entire corpus stale; partial reuse is forbidden.

func (RegressionCorpusV1) Trend added in v0.4.0

func (c RegressionCorpusV1) Trend() []RegressionTrend

type RegressionExpectedV1 added in v0.4.0

type RegressionExpectedV1 struct {
	Scanner string `json:"scanner"`
	Rule    string `json:"rule"`
}

type RegressionIncidentV1 added in v0.4.0

type RegressionIncidentV1 struct {
	ID         string               `json:"id"`
	Provenance string               `json:"provenance"`
	Input      RegressionInputV1    `json:"input"`
	Expected   RegressionExpectedV1 `json:"expected"`
}

type RegressionInputV1 added in v0.4.0

type RegressionInputV1 struct {
	Path    string   `json:"path"`
	Kind    ScanKind `json:"kind"`
	Trust   string   `json:"trust"`
	Content string   `json:"content"`
}

type RegressionTrend added in v0.4.0

type RegressionTrend struct {
	Rule  string `json:"rule"`
	Count int    `json:"count"`
}

type Result

type Result struct {
	Findings []Finding
}

Result is the full analysis: every scanner finding with severity resolved and allowlist status stamped. Consumed by the gate (for pass/fail) and by the caller (for recording under state.security).

func Analyze

func Analyze(root string, cfg core.SecurityConfig) Result

Analyze runs every non-off scanner over the tracked working tree and returns findings with severity + allowlist status resolved. A load error in the allowlist fails closed (its error finding is included at error severity).

type ScanInputV1 added in v0.4.0

type ScanInputV1 struct {
	Root    string   `json:"root,omitempty"`
	ItemRef string   `json:"item_ref"`
	Path    string   `json:"path"`
	Kind    ScanKind `json:"kind"`
	Trust   string   `json:"trust"`
	Digest  string   `json:"digest"`
	Content []byte   `json:"-"`
}

ScanInputV1 identifies exact bytes crossing the scanner trust boundary.

func NewScanInput added in v0.4.0

func NewScanInput(ref string, kind ScanKind, trust string, content []byte) ScanInputV1

type ScanKind added in v0.4.0

type ScanKind string
const (
	ScanKindSpec            ScanKind = "spec"
	ScanKindSteering        ScanKind = "steering"
	ScanKindRole            ScanKind = "role"
	ScanKindMemory          ScanKind = "memory"
	ScanKindSource          ScanKind = "source"
	ScanKindUntracked       ScanKind = "untracked"
	ScanKindToolResult      ScanKind = "tool_result"
	TrustTrustedInstruction          = "trusted_instruction"
	TrustUntrustedData               = "untrusted_data"
)

type Scanner

type Scanner interface {
	Name() string
	Exclude(input ScanInputV1) bool
	Scan(files []ScanInputV1) []Finding
}

Scanner is the pure contract every detector satisfies. Scan must return findings in a stable order for identical input.

type TrackedFile

type TrackedFile = ScanInputV1

TrackedFile keeps existing scanner fixtures source-compatible.

Jump to

Keyboard shortcuts

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