ci: require agentcompat stress validation

Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
naiba
2026-07-20 04:55:20 +00:00
co-authored by naiba/CloudCode
parent d501f23473
commit d96b34b0d4
119 changed files with 3552 additions and 23 deletions
@@ -0,0 +1,207 @@
package workflowpolicy
import (
"fmt"
"regexp"
"strconv"
"strings"
"gopkg.in/yaml.v3"
)
var (
fullCommitPattern = regexp.MustCompile(`^[0-9a-fA-F]{40}$`)
dockerCommandPattern = regexp.MustCompile(`(?mi)(?:^|[;&|]\s*|\s)(?:(?:sudo|env)\s+)?(?:/[^\s]+/)?(?:docker|podman|nerdctl|containerd|buildah|runc|crictl)(?:\s|$)`)
gitHubEnvironmentPattern = regexp.MustCompile(`(?is)GIT_[A-Za-z0-9_]*.*GITHUB_ENV|GITHUB_ENV.*GIT_[A-Za-z0-9_]*`)
swallowedFailurePattern = regexp.MustCompile(`(?mi)(?:\|\|\s*(?:true|:|echo\b|printf\b|exit\s+0\b))|(?:^|[;&]\s*)set\s+\+(?:e|o\s+errexit)(?:\s|;|$)|(?:^|[;&]\s*)if\s+|(?:\b(?:bash|sh)\s+-c\b)|(?:^|[;&]\s*)trap\b[^\n]*\bexit\s+0\b|(?:[;&]\s*)(?:true|:)\s*(?:;|$)`)
workspaceCommandPattern = regexp.MustCompile(`(?mi)(?:\$\{\{\s*github\.workspace\s*\}\}|\$GITHUB_WORKSPACE|\$\{GITHUB_WORKSPACE\})(?:/|\\)|(?:^|[;&|]\s*)(?:sudo\s+)?(?:\.\.?/|[A-Za-z0-9_.-]+/)[^\s;&|]+`)
gitRepositoryCommand = regexp.MustCompile(`(?m)(?:^|[;&|]\s*|\s)(?:(?:sudo|command|env)\s+)?(?:/usr/bin/)?git\b[^\n]*(?:clone|ls-remote)\b`)
gitConfigurationPattern = regexp.MustCompile(`(?mi)(?:^|[;&|]\s*)(?:sudo\s+)?git(?:\s+-c\s+url\.[^\s]+\.insteadOf=\S+|\s+config\b)`)
)
func (c *checker) checkJob(name string, job *yaml.Node) {
path := "$.jobs." + name
timeout, exists := mappingValue(job, "timeout-minutes")
if !exists || !positiveInteger(timeout) {
node := job
if exists {
node = timeout
}
c.reject(RuleMissingJobTimeout, at(path+".timeout-minutes", node), "job timeout-minutes must be a positive literal")
}
c.checkRunner(path, job)
if container, exists := mappingValue(job, "container"); exists {
c.reject(RuleContainerizedExecution, at(path+".container", container), "job containers are forbidden")
}
if services, exists := mappingValue(job, "services"); exists {
c.reject(RuleContainerizedExecution, at(path+".services", services), "service containers are forbidden")
}
c.checkPermissions(job, path+".permissions", false)
c.checkContinueOnError(job, path)
if reusableWorkflow, exists := mappingValue(job, "uses"); exists {
c.reject(RuleReusableExecutable, at(path+".uses", reusableWorkflow), "job-level reusable workflows are forbidden")
return
}
steps, exists := mappingValue(job, "steps")
if !exists {
c.reject(RuleWorkflowStructure, at(path+".steps", job), "workflow jobs must define steps")
return
}
if steps.Kind != yaml.SequenceNode {
c.reject(RuleWorkflowStructure, at(path+".steps", steps), "workflow steps must be a sequence")
return
}
c.checkSteps(path, steps)
}
func (c *checker) checkSteps(jobPath string, steps *yaml.Node) {
redactionReady := false
validatedResolvers := make(map[string]Repository)
for index, step := range steps.Content {
path := jobPath + ".steps[" + strconv.Itoa(index) + "]"
if step.Kind != yaml.MappingNode {
c.reject(RuleWorkflowStructure, at(path, step), "workflow step must be a mapping")
redactionReady = false
continue
}
uses, hasUses := mappingValue(step, "uses")
run, hasRun := mappingValue(step, "run")
if !hasUses && !hasRun {
c.reject(RuleWorkflowStructure, at(path, step), "workflow step must define a nonempty uses or run")
redactionReady = false
continue
}
if hasUses && (uses.Kind != yaml.ScalarNode || uses.Tag != "!!str" || strings.TrimSpace(uses.Value) == "") {
c.reject(RuleWorkflowStructure, at(path+".uses", uses), "step uses must be a string action reference")
}
if hasRun && (run.Kind != yaml.ScalarNode || run.Tag != "!!str" || strings.TrimSpace(run.Value) == "") {
c.reject(RuleWorkflowStructure, at(path+".run", run), "step run must be a scalar shell command")
}
c.checkContinueOnError(step, path)
resolver, validResolver := validatedRefResolver(step)
if hasRun {
c.checkRun(path+".run", run, validResolver)
}
if validResolver {
validatedResolvers[resolver.id] = resolver.repository
}
if hasUses {
c.checkUses(path, step, stepCheckState{redactionComplete: redactionReady, validatedResolvers: validatedResolvers})
redactionReady = false
continue
}
redactionReady = c.isRedactionStep(step)
}
}
func (c *checker) checkContinueOnError(mapping *yaml.Node, path string) {
value, exists := mappingValue(mapping, "continue-on-error")
if exists && !explicitFalse(value) {
c.reject(RuleContinueOnError, at(path+".continue-on-error", value), "continue-on-error must not enable failure suppression")
}
}
func (c *checker) checkRun(path string, run *yaml.Node, validatedResolver bool) {
command, exists := scalarString(run)
if !exists {
return
}
if dockerCommandPattern.MatchString(command) {
c.reject(RuleContainerizedExecution, at(path, run), "docker execution is forbidden")
}
if swallowedFailurePattern.MatchString(command) {
c.reject(RuleSwallowedFailure, at(path, run), "shell failure is swallowed by || true or another ignored fallback, exit 0, or disabled errexit")
}
if workspaceCommandPattern.MatchString(command) {
c.reject(RuleReusableExecutable, at(path, run), "executing a binary from the GitHub workspace is forbidden")
}
if gitHubEnvironmentPattern.MatchString(command) {
c.reject(RuleRepositoryNotLiteral, at(path, run), "writing GIT_* configuration through GITHUB_ENV is forbidden")
}
if gitConfigurationPattern.MatchString(command) {
c.reject(RuleRepositoryNotLiteral, at(path, run), "Git configuration mutation is forbidden")
}
if gitRepositoryCommand.MatchString(command) && !validatedResolver {
rule := RuleRepositoryNotLiteral
detail := fmt.Sprintf("git repository operation %q is allowed only in the validated resolver", strings.TrimSpace(command))
if !strings.Contains(command, "$") {
rule = RuleRepositoryNotAllowed
detail = fmt.Sprintf("repository operation %q is forbidden outside the validated resolver", strings.TrimSpace(command))
}
c.reject(rule, at(path, run), detail)
}
}
type stepCheckState struct {
redactionComplete bool
validatedResolvers map[string]Repository
}
func (c *checker) checkUses(path string, step *yaml.Node, state stepCheckState) {
uses, exists := mappingValue(step, "uses")
if !exists {
return
}
action, literal := scalarString(uses)
if !literal {
return
}
if strings.Contains(action, "${{") {
c.reject(RuleRepositoryNotLiteral, at(path+".uses", uses), "action reference must be literal")
return
}
lowerAction := strings.ToLower(action)
if strings.HasPrefix(lowerAction, "docker://") {
c.reject(RuleContainerizedExecution, at(path+".uses", uses), "Docker actions are forbidden")
return
}
if strings.HasPrefix(lowerAction, "./") {
c.reject(RuleReusableExecutable, at(path+".uses", uses), "local action reuse from the workspace is forbidden")
return
}
actionRepository, _, found := strings.Cut(lowerAction, "@")
switch actionRepository {
case "actions/cache", "actions/cache/restore", "actions/cache/save", "actions/download-artifact":
c.reject(RuleReusableExecutable, at(path+".uses", uses), fmt.Sprintf("cache or artifact reuse action %q is forbidden", actionRepository))
return
}
if !found {
c.reject(RuleOtherRepositoryRef, at(path+".uses", uses), "action must use its approved immutable SHA")
return
}
approvedRepository, _, pinned := approvedAction(action)
if approvedRepository == "" {
c.reject(RuleRepositoryNotAllowed, at(path+".uses", uses), "action repository is not approved")
return
}
if !pinned {
c.reject(RuleOtherRepositoryRef, at(path+".uses", uses), "action must use its approved immutable SHA")
return
}
switch approvedRepository {
case "actions/checkout":
c.checkCheckout(path, step, state.validatedResolvers)
case "actions/setup-go":
c.checkRequiredCacheDisabled(path, step)
case "actions/upload-artifact":
c.checkArtifactUpload(path, step, state.redactionComplete)
}
c.checkCacheInputs(path, step)
}
func approvedAction(action string) (string, string, bool) {
repository, ref, found := strings.Cut(strings.ToLower(action), "@")
if !found {
return repository, "", false
}
approvedRefs := map[string]string{
"actions/checkout": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0",
"actions/setup-go": "924ae3a1cded613372ab5595356fb5720e22ba16",
"actions/upload-artifact": "b7c566a772e6b6bfb58ed0dc250532a479d7789f",
}
approvedRef, approved := approvedRefs[repository]
if !approved {
return "", ref, false
}
return repository, ref, ref == approvedRef
}