mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
233 lines
10 KiB
Go
233 lines
10 KiB
Go
package workflowpolicy_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/nezhahq/nezha/integration/agentcompat/internal/workflowpolicy"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPolicy_RejectsSelfHostedRunner(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("self-hosted.yml", workflowpolicy.RuleSelfHostedRunner, "self-hosted"))
|
|
}
|
|
|
|
func TestPolicy_RejectsMatrixSelfHostedRunner(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("matrix-self-hosted.yml", workflowpolicy.RuleSelfHostedRunner, "self-hosted"))
|
|
}
|
|
|
|
func TestPolicy_RejectsCustomRunnerLabel(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("custom-runner.yml", workflowpolicy.RuleSelfHostedRunner, "GitHub-hosted"))
|
|
}
|
|
|
|
func TestPolicy_RejectsMatrixIncludeRunner(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("matrix-include-runner.yml", workflowpolicy.RuleSelfHostedRunner, "include"))
|
|
}
|
|
|
|
func TestPolicy_RejectsComposedCustomRunnerLabel(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("composed-custom-runner.yml", workflowpolicy.RuleSelfHostedRunner, "GitHub-hosted"))
|
|
}
|
|
|
|
func TestPolicy_RejectsDockerExecution(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("docker.yml", workflowpolicy.RuleContainerizedExecution, "docker"))
|
|
}
|
|
|
|
func TestPolicy_RejectsAbsoluteDockerExecution(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("absolute-docker.yml", workflowpolicy.RuleContainerizedExecution, "docker"))
|
|
}
|
|
|
|
func TestPolicy_RejectsAlternateAbsoluteDockerExecution(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("alternate-absolute-docker.yml", workflowpolicy.RuleContainerizedExecution, "docker"))
|
|
}
|
|
|
|
func TestPolicy_RejectsJobContainer(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("container.yml", workflowpolicy.RuleContainerizedExecution, "container"))
|
|
}
|
|
|
|
func TestPolicy_RejectsServiceContainers(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("services.yml", workflowpolicy.RuleContainerizedExecution, "services"))
|
|
}
|
|
|
|
func TestPolicy_RejectsCacheReuse(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("cache.yml", workflowpolicy.RuleReusableExecutable, "cache"))
|
|
}
|
|
|
|
func TestPolicy_RejectsSetupGoDefaultCache(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("setup-go-default-cache.yml", workflowpolicy.RuleReusableExecutable, "cache: false"))
|
|
}
|
|
|
|
func TestPolicy_RejectsArtifactExecutableReuse(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("download-artifact.yml", workflowpolicy.RuleReusableExecutable, "artifact reuse"))
|
|
}
|
|
|
|
func TestPolicy_RejectsWorkspaceExecutableReuse(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("workspace-executable.yml", workflowpolicy.RuleReusableExecutable, "workspace"))
|
|
}
|
|
|
|
func TestPolicy_RejectsLocalActionReuse(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("local-action.yml", workflowpolicy.RuleReusableExecutable, "local action"))
|
|
}
|
|
|
|
func TestPolicy_RejectsUnapprovedAction(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("unapproved-action.yml", workflowpolicy.RuleRepositoryNotAllowed, "action"))
|
|
}
|
|
|
|
func TestPolicy_RejectsMutableActionRef(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("mutable-action-ref.yml", workflowpolicy.RuleOtherRepositoryRef, "approved immutable SHA"))
|
|
}
|
|
|
|
func TestPolicy_RejectsReusableWorkflowJob(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("reusable-workflow-job.yml", workflowpolicy.RuleReusableExecutable, "reusable workflow"))
|
|
}
|
|
|
|
func TestPolicy_RejectsContinueOnError(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("continue-on-error.yml", workflowpolicy.RuleContinueOnError, "continue-on-error"))
|
|
}
|
|
|
|
func TestPolicy_RejectsSwallowedShellFailure(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("swallowed-failure.yml", workflowpolicy.RuleSwallowedFailure, "|| true"))
|
|
}
|
|
|
|
func TestPolicy_RejectsAlternativeSwallowedShellFailures(t *testing.T) {
|
|
for _, fixture := range []string{"or-echo-failure.yml", "or-printf-failure.yml", "or-exit-zero-failure.yml", "set-plus-o-errexit.yml", "set-plus-e-semicolon.yml", "if-not-failure.yml", "if-condition-failure.yml", "and-if-condition-failure.yml", "nested-shell.yml"} {
|
|
t.Run(fixture, func(t *testing.T) {
|
|
assertFixtureRejected(t, rejected(fixture, workflowpolicy.RuleSwallowedFailure, "failure"))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPolicy_RejectsMissingJobTimeout(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("missing-timeout.yml", workflowpolicy.RuleMissingJobTimeout, "timeout-minutes"))
|
|
}
|
|
|
|
func TestPolicy_RejectsMissingConcurrency(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("missing-concurrency.yml", workflowpolicy.RuleMissingConcurrency, "concurrency"))
|
|
}
|
|
|
|
func TestPolicy_RejectsEmptyConcurrency(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("empty-concurrency.yml", workflowpolicy.RuleMissingConcurrency, "concurrency"))
|
|
}
|
|
|
|
func TestPolicy_RejectsArtifactWithoutRedaction(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("artifact-without-redaction.yml", workflowpolicy.RuleArtifactRedaction, "redaction step"))
|
|
}
|
|
|
|
func TestPolicy_RejectsUnredactedArtifactPath(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("unredacted-artifact-path.yml", workflowpolicy.RuleArtifactRedaction, "redacted"))
|
|
}
|
|
|
|
func TestPolicy_RejectsNoOpRedactionStep(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("no-op-redaction.yml", workflowpolicy.RuleArtifactRedaction, "redaction step"))
|
|
}
|
|
|
|
func TestPolicy_RejectsConditionalRedaction(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("conditional-redaction.yml", workflowpolicy.RuleArtifactRedaction, "always()"))
|
|
}
|
|
|
|
func TestPolicy_RejectsRawWriteAfterRedaction(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("raw-after-redaction.yml", workflowpolicy.RuleArtifactRedaction, "immediately follow"))
|
|
}
|
|
|
|
func TestPolicy_RejectsCommandsAppendedToRedaction(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("redaction-command-append.yml", workflowpolicy.RuleArtifactRedaction, "immediately follow"))
|
|
}
|
|
|
|
func TestPolicy_RejectsUntrustedRunExpression(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("untrusted-run-expression.yml", workflowpolicy.RuleUntrustedExpression, "pull_request.title"))
|
|
}
|
|
|
|
func TestPolicy_RejectsDynamicGitRepository(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("dynamic-git-repository.yml", workflowpolicy.RuleRepositoryNotLiteral, "literal"))
|
|
}
|
|
|
|
func TestPolicy_RejectsUnapprovedGitRepository(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("unapproved-git-repository.yml", workflowpolicy.RuleRepositoryNotAllowed, "attacker/fork"))
|
|
}
|
|
|
|
func TestPolicy_RejectsEnvironmentGitRepository(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("environment-git-repository.yml", workflowpolicy.RuleRepositoryNotLiteral, "literal"))
|
|
}
|
|
|
|
func TestPolicy_RejectsExternalGitRepository(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("external-git-repository.yml", workflowpolicy.RuleRepositoryNotAllowed, "repository"))
|
|
}
|
|
|
|
func TestPolicy_RejectsPrefixedGitRepository(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("prefixed-git-repository.yml", workflowpolicy.RuleRepositoryNotLiteral, "literal"))
|
|
}
|
|
|
|
func TestPolicy_RejectsGitGlobalOptionRepository(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("git-global-option-repository.yml", workflowpolicy.RuleRepositoryNotAllowed, "repository"))
|
|
}
|
|
|
|
func TestPolicy_RejectsCommandOptionGitRepository(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("command-option-git-repository.yml", workflowpolicy.RuleRepositoryNotAllowed, "repository"))
|
|
}
|
|
|
|
func TestPolicy_RejectsGitConfigurationEnvironment(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("git-config-environment.yml", workflowpolicy.RuleRepositoryNotLiteral, "GIT_CONFIG_COUNT"))
|
|
}
|
|
|
|
func TestPolicy_RejectsGitHubEnvironmentConfiguration(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("github-environment-git-config.yml", workflowpolicy.RuleRepositoryNotLiteral, "GITHUB_ENV"))
|
|
}
|
|
|
|
func TestPolicy_RejectsIndexedUntrustedExpression(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("indexed-untrusted-expression.yml", workflowpolicy.RuleUntrustedExpression, "github['event']"))
|
|
}
|
|
|
|
func TestPolicy_RejectsDynamicIndexedUntrustedExpression(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("dynamic-indexed-untrusted-expression.yml", workflowpolicy.RuleUntrustedExpression, "github["))
|
|
}
|
|
|
|
func TestPolicy_RejectsNonliteralAction(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("nonliteral-action.yml", workflowpolicy.RuleRepositoryNotLiteral, "literal"))
|
|
}
|
|
|
|
func TestPolicy_RejectsMixedArtifactPaths(t *testing.T) {
|
|
assertFixtureRejected(t, rejected("mixed-artifact-paths.yml", workflowpolicy.RuleArtifactRedaction, "redacted"))
|
|
}
|
|
|
|
func TestPolicy_VerifyFileUsesFreshContents(t *testing.T) {
|
|
// Given
|
|
temporaryDirectory := t.TempDir()
|
|
workflowPath := filepath.Join(temporaryDirectory, "workflow.yml")
|
|
secureWorkflow, err := os.ReadFile(fixturePath(t, "secure-nezha.yml"))
|
|
require.NoError(t, err)
|
|
require.NoError(t, os.WriteFile(workflowPath, secureWorkflow, 0o600))
|
|
require.NoError(t, workflowpolicy.VerifyFile(workflowPath, workflowpolicy.RepositoryNezha))
|
|
maliciousWorkflow, err := os.ReadFile(fixturePath(t, "continue-on-error.yml"))
|
|
require.NoError(t, err)
|
|
require.NoError(t, os.WriteFile(workflowPath, maliciousWorkflow, 0o600))
|
|
|
|
// When
|
|
err = workflowpolicy.VerifyFile(workflowPath, workflowpolicy.RepositoryNezha)
|
|
|
|
// Then
|
|
requireTypedPolicyError(t, err, workflowpolicy.RuleContinueOnError)
|
|
}
|
|
|
|
func TestPolicy_TempWorkflowsReportExactDiagnostics(t *testing.T) {
|
|
// Given
|
|
temporaryDirectory := t.TempDir()
|
|
securePath := filepath.Join(temporaryDirectory, "secure.yml")
|
|
maliciousPath := filepath.Join(temporaryDirectory, "malicious.yml")
|
|
secureWorkflow, err := os.ReadFile(fixturePath(t, "secure-nezha.yml"))
|
|
require.NoError(t, err)
|
|
maliciousWorkflow, err := os.ReadFile(fixturePath(t, "persist-credentials-true.yml"))
|
|
require.NoError(t, err)
|
|
require.NoError(t, os.WriteFile(securePath, secureWorkflow, 0o600))
|
|
require.NoError(t, os.WriteFile(maliciousPath, maliciousWorkflow, 0o600))
|
|
|
|
// When
|
|
secureError := workflowpolicy.VerifyFile(securePath, workflowpolicy.RepositoryNezha)
|
|
maliciousError := workflowpolicy.VerifyFile(maliciousPath, workflowpolicy.RepositoryNezha)
|
|
|
|
// Then
|
|
require.NoError(t, secureError)
|
|
requireTypedPolicyError(t, maliciousError, workflowpolicy.RulePersistCredentials)
|
|
t.Logf("secure workflow: PASS")
|
|
t.Logf("malicious workflow: %v", maliciousError)
|
|
}
|