Documentation
¶
Overview ¶
Package rulepack evaluates deterministic release evidence for signed detection RulePacks.
Index ¶
- Constants
- func Advance(p rulepackdomain.RulePack, d rulepackdomain.RulePackDeployment, in GateInput, ...) (rulepackdomain.RulePackDeployment, error)
- func CollectPurpleEvidence(ctx context.Context, reader PurpleReader, request PurpleRequest) ([]purplecoverage.Coverage, error)
- type EvidenceCollector
- type Failure
- type GateEvidenceRequest
- type GateEvidenceSigner
- type GateInput
- type GatePolicy
- type PurpleReader
- type PurpleRequest
- type QualityMetrics
- type QualitySample
- type Report
- type RetroCase
- type RetroEvidence
- type RetroQueryProvenance
- type RuleCostObservation
- type SignedGateEvidence
- type Stage
- type StageResult
- type TelemetryHunter
Constants ¶
const ( // GateEvidenceAttestationContext domain-separates RulePack release evidence from the repository's // evidence/audit chain-head attestations even when the same infrastructure signer key is reused. GateEvidenceAttestationContext = "synapse-rulepack-gate-evidence:v1" )
Variables ¶
This section is empty.
Functions ¶
func Advance ¶
func Advance(p rulepackdomain.RulePack, d rulepackdomain.RulePackDeployment, in GateInput, next rulepackdomain.DeploymentState) (rulepackdomain.RulePackDeployment, error)
Advance recomputes release evidence at the transition boundary instead of trusting a caller-supplied Report. Candidate->canary needs all pre-canary gates and canary->promoted needs the full release gate. The deployment embedded in GateInput is overwritten with d so evidence cannot be evaluated against a healthier deployment than the one actually being transitioned. Rollback deliberately bypasses gate recomputation: if signed rollback metadata is valid, safety must remain available even when evidence is malformed or the rollout is incompatible.
func CollectPurpleEvidence ¶
func CollectPurpleEvidence(ctx context.Context, reader PurpleReader, request PurpleRequest) ([]purplecoverage.Coverage, error)
CollectPurpleEvidence loads measured purple coverage and returns only rows from the requested run. Empty or cross-scope evidence fails closed: claimed ATT&CK coverage cannot be inferred from a missing run, another engagement, or a row without the asset on which the emulation was measured.
Types ¶
type EvidenceCollector ¶
type EvidenceCollector struct {
// contains filtered or unexported fields
}
EvidenceCollector obtains the release evidence whose provenance matters from the existing authoritative seams, then attests to the deterministic envelope. It performs no persistence itself.
func NewEvidenceCollector ¶
func NewEvidenceCollector(hunter TelemetryHunter, purple PurpleReader, signer GateEvidenceSigner) (*EvidenceCollector, error)
NewEvidenceCollector validates the release-evidence dependencies.
func (*EvidenceCollector) Collect ¶
func (c *EvidenceCollector) Collect(ctx context.Context, p rulepackdomain.RulePack, req GateEvidenceRequest) (SignedGateEvidence, error)
Collect obtains retro-hunt and purple evidence from their authoritative services and returns an attested envelope. A valid-but-failing release (for example a real purple gap) is still attestable; malformed evidence is refused before it can acquire provenance.
type GateEvidenceRequest ¶
type GateEvidenceRequest struct {
Deployment rulepackdomain.RulePackDeployment `json:"deployment"`
Policy GatePolicy `json:"policy"`
Costs []RuleCostObservation `json:"costs"`
RetroCases []RetroCase `json:"retro_cases"`
Purple PurpleRequest `json:"purple_request"`
Evaluation QualitySample `json:"evaluation"`
Canary *QualitySample `json:"canary,omitempty"`
Production *QualitySample `json:"production,omitempty"`
}
GateEvidenceRequest contains operator-owned policy/measurements plus selectors for evidence that must be collected from authoritative telemetry and purple-coverage services. Callers cannot inject raw RetroEvidence or purplecoverage.Coverage into this boundary.
type GateEvidenceSigner ¶
type GateEvidenceSigner interface {
Sign(ctx context.Context, head string) (evidence.Attestation, error)
}
GateEvidenceSigner is satisfied by the existing infrastructure/signing.Ed25519Signer. The composition root must configure it with GateEvidenceAttestationContext before giving it to the collector.
type GateInput ¶
type GateInput struct {
Deployment rulepackdomain.RulePackDeployment `json:"deployment"`
Policy GatePolicy `json:"policy"`
Costs []RuleCostObservation `json:"costs"`
Retro []RetroEvidence `json:"retro"`
Purple []purplecoverage.Coverage `json:"purple"`
Evaluation QualitySample `json:"evaluation"`
Canary *QualitySample `json:"canary,omitempty"`
Production *QualitySample `json:"production,omitempty"`
}
GateInput is all deterministic evidence needed for one RulePack release evaluation. Canary and Production may be omitted while a candidate is only seeking admission to the canary stage.
func VerifyGateEvidence ¶
func VerifyGateEvidence(s SignedGateEvidence, p rulepackdomain.RulePack, trustedPub ed25519.PublicKey) (GateInput, error)
VerifyGateEvidence verifies that s was produced by a trusted evidence collector for this exact RulePack. The attestation's embedded public key is never self-authorizing: it must byte-match the externally pinned trustedPub supplied by the caller.
type GatePolicy ¶
type GatePolicy struct {
MinimumPrecisionBPS int `json:"minimum_precision_bps"`
MaximumFalsePositiveRateBPS int `json:"maximum_false_positive_rate_bps"`
MinimumAnalystDispositionRateBPS int `json:"minimum_analyst_disposition_rate_bps"`
MinimumRequiredFieldAvailabilityBPS int `json:"minimum_required_field_availability_bps"`
MinimumATTACKCoverageBPS int `json:"minimum_attack_coverage_bps"`
MaximumCanaryDetectionsPerHostDayMilli int64 `json:"maximum_canary_detections_per_host_day_milli"`
MaximumProdDetectionsPerHostDayMilli int64 `json:"maximum_prod_detections_per_host_day_milli"`
MinimumReviewedDetections int64 `json:"minimum_reviewed_detections"`
MinimumHostDays int64 `json:"minimum_host_days"`
}
GatePolicy is operator-owned release policy. Rates are integer basis points to keep CI decisions deterministic; detections/host-day is represented in milli-detections to avoid floating point.
func (GatePolicy) Validate ¶
func (p GatePolicy) Validate() error
Validate rejects ambiguous release policy values.
type PurpleReader ¶
type PurpleReader interface {
Trend(ctx context.Context, engagementID shared.ID) ([]purplecoverage.Coverage, error)
}
PurpleReader is satisfied by purplecoverage.Service and keeps the release gate coupled only to the existing usecase seam, not to a persistence adapter.
type PurpleRequest ¶
type PurpleRequest struct {
EngagementID shared.ID `json:"engagement_id"`
RunID shared.ID `json:"run_id"`
}
PurpleRequest selects the exact emulation run whose measured coverage is release evidence.
type QualityMetrics ¶
type QualityMetrics struct {
Detections int64 `json:"detections"`
ReviewedDetections int64 `json:"reviewed_detections"`
TruePositives int64 `json:"true_positives"`
FalsePositives int64 `json:"false_positives"`
SuppressedDetections int64 `json:"suppressed_detections"`
HostDays int64 `json:"host_days"`
PrecisionBPS int `json:"precision_bps"`
FalsePositiveRateBPS int `json:"false_positive_rate_bps"`
SuppressionRateBPS int `json:"suppression_rate_bps"`
AnalystDispositionRateBPS int `json:"analyst_disposition_rate_bps"`
RequiredFieldAvailabilityBPS int `json:"required_field_availability_bps"`
DetectionsPerHostDayMilli int64 `json:"detections_per_host_day_milli"`
}
QualityMetrics is the deterministic metric surface #630 emits for CI and release review.
type QualitySample ¶
type QualitySample struct {
Detections int64 `json:"detections"`
TruePositives int64 `json:"true_positives"`
FalsePositives int64 `json:"false_positives"`
SuppressedDetections int64 `json:"suppressed_detections"`
HostDays int64 `json:"host_days"`
AvailableFields []detection.Field `json:"available_fields"`
}
QualitySample is exact labelled/runtime evidence used to compute detection-quality metrics. Detections are emitted detections; SuppressedDetections are candidates suppressed before emission. True/false positives are analyst dispositions over emitted detections and therefore must not exceed Detections.
type Report ¶
type Report struct {
PackID string `json:"pack_id"`
PackVersion int `json:"pack_version"`
PackDigest string `json:"pack_digest"`
PreCanaryPassed bool `json:"pre_canary_passed"`
CanaryPassed bool `json:"canary_passed"`
Passed bool `json:"passed"`
Stages []StageResult `json:"stages"`
Costs []RuleCostObservation `json:"costs"`
EvaluationMetrics QualityMetrics `json:"evaluation_metrics"`
CanaryMetrics *QualityMetrics `json:"canary_metrics,omitempty"`
ProductionMetrics *QualityMetrics `json:"production_metrics,omitempty"`
ATTACKCoverageBPS int `json:"attack_coverage_bps"`
}
Report is deterministic CI evidence. PreCanaryPassed admits candidate->canary, while Passed requires both canary and production metrics and is required for canary->promoted.
type RetroCase ¶
RetroCase identifies the bounded telemetry window used to prove one candidate rule can be retro-run.
type RetroEvidence ¶
type RetroEvidence struct {
RuleID string `json:"rule_id"`
ContextEvents int `json:"context_events"`
MatchedEvents int `json:"matched_events"`
Complete bool `json:"complete"`
Sampled bool `json:"sampled"`
SequenceGaps int `json:"sequence_gaps"`
Losses int `json:"losses"`
}
RetroEvidence proves one candidate rule was evaluated against a telemetry window from the existing telemetry hunt seam. Complete, unsampled evidence is required for a release claim.
func CollectRetroEvidence ¶
func CollectRetroEvidence(ctx context.Context, p rulepackdomain.RulePack, hunter TelemetryHunter, cases []RetroCase) ([]RetroEvidence, error)
CollectRetroEvidence evaluates each candidate rule over its requested stored-telemetry window. Exactly one case per RulePack rule is required, making missing retro coverage explicit rather than silently omitting a rule from the release gate.
type RetroQueryProvenance ¶
type RetroQueryProvenance struct {
RuleID string `json:"rule_id"`
HostID shared.ID `json:"host_id"`
AssetID shared.ID `json:"asset_id,omitempty"`
Class detection.Class `json:"class"`
Since time.Time `json:"since"`
Until time.Time `json:"until"`
Limit int `json:"limit"`
}
RetroQueryProvenance records the exact bounded telemetry selector used to produce one rule's retro evidence. The gate attestation binds these selectors alongside the aggregate result so a later release review can identify and reproduce the exact host/class/time window that was evaluated.
type RuleCostObservation ¶
type RuleCostObservation struct {
RuleID string `json:"rule_id"`
LatencyMicros int64 `json:"latency_micros"`
CPUMicrosPerHostDay int64 `json:"cpu_micros_per_host_day"`
}
RuleCostObservation is measured release evidence for one rule. It is compared to the signed RulePack's ExpectedCost budget; the gate does not benchmark wall-clock time itself.
type SignedGateEvidence ¶
type SignedGateEvidence struct {
PackID string `json:"pack_id"`
PackVersion int `json:"pack_version"`
PackDigest string `json:"pack_digest"`
RetroQueries []RetroQueryProvenance `json:"retro_queries"`
Input GateInput `json:"input"`
Attestation evidence.Attestation `json:"attestation"`
}
SignedGateEvidence is the immutable release-evidence envelope consumed by synapse-cli rulepack gate. Its attestation covers the exact RulePack identity, the bounded retro-hunt selectors, and canonical GateInput; verification pins the evidence producer key externally, independently of the RulePack content-signing key.
type Stage ¶
type Stage string
Stage is one ordered RulePack release gate.
const ( StageCompatibility Stage = "compatibility" StagePositiveReplay Stage = "positive_replay" StageNegativeReplay Stage = "negative_replay" StagePerformance Stage = "performance" StageRetroHunt Stage = "retro_hunt" StageEmulation Stage = "emulation" StageFPBudget Stage = "false_positive_budget" StageCanary Stage = "canary_metrics" StageProduction Stage = "production_metrics" )
type StageResult ¶
type StageResult struct {
Stage Stage `json:"stage"`
Pass bool `json:"pass"`
Failures []Failure `json:"failures,omitempty"`
}
StageResult records one gate's outcome in fixed release order.
type TelemetryHunter ¶
type TelemetryHunter interface {
Hunt(ctx context.Context, q ports.HuntQuery) (ports.HuntResult, error)
}
TelemetryHunter is satisfied by fleet/telemetry.Service. The candidate RulePack rule is evaluated over the returned window here rather than via telemetry.Service.RetroRunRule, because that existing helper intentionally re-runs the currently shipped detection catalogue and could false-green a candidate rule.