Add nosniff, frame denial, referrer policy, and a self-origin CSP. Clamp human and squad lease TTLs and require author edit rights before a human lease. Include DryRun in replay digests so a dry-run approval cannot authorize apply. Stop Server.Start from swallowing a second Runtime.Start. Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
package events
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ReplayPlan is the application-layer dry-run/apply contract. There is no raw SQL entrypoint.
|
|
type ReplayPlan struct {
|
|
PlanID string
|
|
Operator string
|
|
Approver string
|
|
Environment string
|
|
ProjectID int64
|
|
Consumer string
|
|
EventIDs []string
|
|
Reason string
|
|
ApprovalRef string
|
|
DryRun bool
|
|
}
|
|
|
|
func (p ReplayPlan) Digest() string {
|
|
body, _ := json.Marshal(struct {
|
|
PlanID string `json:"plan_id"`
|
|
Operator string `json:"operator"`
|
|
Approver string `json:"approver"`
|
|
Environment string `json:"environment"`
|
|
ProjectID int64 `json:"project_id"`
|
|
Consumer string `json:"consumer"`
|
|
EventIDs []string `json:"event_ids"`
|
|
Reason string `json:"reason"`
|
|
ApprovalRef string `json:"approval_ref"`
|
|
DryRun bool `json:"dry_run"`
|
|
}{p.PlanID, p.Operator, p.Approver, p.Environment, p.ProjectID, p.Consumer, p.EventIDs, p.Reason, p.ApprovalRef, p.DryRun})
|
|
sum := sha256.Sum256(body)
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func AuthorizeReplayPlan(p ReplayPlan) error {
|
|
if strings.TrimSpace(p.Operator) == "" || strings.TrimSpace(p.Reason) == "" || strings.TrimSpace(p.Consumer) == "" {
|
|
return ErrReplayScope
|
|
}
|
|
if strings.TrimSpace(p.Environment) == "" || p.ProjectID <= 0 {
|
|
return ErrReplayScope
|
|
}
|
|
if len(p.EventIDs) == 0 {
|
|
return ErrReplayScope
|
|
}
|
|
if p.DryRun {
|
|
return nil
|
|
}
|
|
if strings.TrimSpace(p.ApprovalRef) == "" {
|
|
return ErrReplayApply
|
|
}
|
|
if strings.EqualFold(p.Operator, p.Approver) || strings.TrimSpace(p.Approver) == "" {
|
|
return ErrReplayApply
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type ReplayRequest struct {
|
|
Consumer string
|
|
EventID string
|
|
ProjectID int64
|
|
DryRun bool
|
|
Reason string
|
|
Operator string
|
|
}
|
|
|
|
func AuthorizeReplay(req ReplayRequest) error {
|
|
if !req.DryRun {
|
|
return ErrReplayScope
|
|
}
|
|
if strings.TrimSpace(req.Consumer) == "" || strings.TrimSpace(req.Operator) == "" || strings.TrimSpace(req.Reason) == "" {
|
|
return ErrReplayScope
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ApplyReplay(p ReplayPlan, dryRunDigest string) error {
|
|
if p.DryRun {
|
|
return AuthorizeReplayPlan(p)
|
|
}
|
|
if err := AuthorizeReplayPlan(p); err != nil {
|
|
return err
|
|
}
|
|
if dryRunDigest == "" || p.Digest() != dryRunDigest {
|
|
return fmt.Errorf("%w: plan digest mismatch", ErrReplayApply)
|
|
}
|
|
return nil
|
|
}
|