mirror of
https://github.com/ivuorinen/actions.git
synced 2026-01-26 03:23:59 +00:00
* docs: update documentation * feat: validate-inputs has it's own pyproject * security: mask DOCKERHUB_PASSWORD * chore: add tokens, checkout, recrete docs, integration tests * fix: add `statuses: write` permission to pr-lint
324 lines
11 KiB
Python
324 lines
11 KiB
Python
"""Tests for custom validators in action directories."""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add parent directory to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from validators.registry import ValidatorRegistry
|
|
|
|
|
|
class TestCustomValidators:
|
|
"""Test custom validators for various actions."""
|
|
|
|
def test_sync_labels_custom_validator(self):
|
|
"""Test sync-labels custom validator."""
|
|
registry = ValidatorRegistry()
|
|
validator = registry.get_validator("sync-labels")
|
|
|
|
# Should load the custom validator
|
|
assert validator.__class__.__name__ == "CustomValidator"
|
|
|
|
# Test valid inputs
|
|
inputs = {
|
|
"labels": ".github/labels.yml",
|
|
"token": "${{ github.token }}",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test invalid YAML extension
|
|
validator.clear_errors()
|
|
inputs = {"labels": ".github/labels.txt"}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "Must be a .yml or .yaml file" in str(validator.errors)
|
|
|
|
# Test path traversal
|
|
validator.clear_errors()
|
|
inputs = {"labels": "../../../etc/passwd"}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert validator.has_errors()
|
|
|
|
def test_docker_build_custom_validator(self):
|
|
"""Test docker-build custom validator."""
|
|
registry = ValidatorRegistry()
|
|
validator = registry.get_validator("docker-build")
|
|
|
|
# Should load the custom validator
|
|
assert validator.__class__.__name__ == "CustomValidator"
|
|
|
|
# Test valid inputs
|
|
inputs = {
|
|
"context": ".",
|
|
"dockerfile": "./Dockerfile",
|
|
"architectures": "linux/amd64,linux/arm64",
|
|
"tag": "latest",
|
|
"push": "true",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test missing required tag
|
|
validator.clear_errors()
|
|
inputs = {}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "tag" in str(validator.errors)
|
|
|
|
# Test invalid platform
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"context": ".",
|
|
"tag": "latest",
|
|
"architectures": "invalid/platform",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "Invalid architectures" in str(validator.errors)
|
|
|
|
# Test invalid build args format
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"context": ".",
|
|
"build-args": "INVALID_FORMAT",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "KEY=value format" in str(validator.errors)
|
|
|
|
# Test cache configuration
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"context": ".",
|
|
"tag": "latest",
|
|
"cache-from": "type=gha",
|
|
"cache-to": "type=gha,mode=max",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
def test_codeql_analysis_custom_validator(self):
|
|
"""Test codeql-analysis custom validator."""
|
|
registry = ValidatorRegistry()
|
|
validator = registry.get_validator("codeql-analysis")
|
|
|
|
# Should load the custom validator
|
|
assert validator.__class__.__name__ == "CustomValidator"
|
|
|
|
# Test valid inputs
|
|
inputs = {
|
|
"language": "javascript,python",
|
|
"queries": "security-extended",
|
|
"categories": "/security",
|
|
"threads": "4",
|
|
"ram": "4096",
|
|
"debug": "false",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test missing required language
|
|
validator.clear_errors()
|
|
inputs = {}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "language" in str(validator.errors)
|
|
|
|
# Test invalid language
|
|
validator.clear_errors()
|
|
inputs = {"language": "cobol"}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "Unsupported CodeQL language" in str(validator.errors)
|
|
|
|
# Test valid config file
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"language": "javascript",
|
|
"config-file": ".github/codeql/codeql-config.yml",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test invalid config file extension
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"language": "javascript",
|
|
"config-file": "config.txt",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
err = 'Invalid config-file: "config.txt". Must be a .yml or .yaml file'
|
|
assert err in str(validator.errors)
|
|
|
|
# Test pack validation
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"language": "javascript",
|
|
"packs": "codeql/javascript-queries@1.2.3,github/codeql-go",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test invalid pack format
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"language": "javascript",
|
|
"packs": "invalid-pack-format",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "namespace/pack-name" in str(validator.errors)
|
|
|
|
def test_docker_publish_custom_validator(self):
|
|
"""Test docker-publish custom validator."""
|
|
registry = ValidatorRegistry()
|
|
validator = registry.get_validator("docker-publish")
|
|
|
|
# Should load the custom validator
|
|
assert validator.__class__.__name__ == "CustomValidator"
|
|
|
|
# Test valid inputs
|
|
inputs = {
|
|
"registry": "dockerhub",
|
|
"dockerhub-username": "${{ secrets.DOCKER_USERNAME }}",
|
|
"dockerhub-password": "${{ secrets.DOCKER_PASSWORD }}",
|
|
"platforms": "linux/amd64,linux/arm64",
|
|
"nightly": "false",
|
|
}
|
|
result = validator.validate_inputs(inputs)
|
|
if not result:
|
|
pass
|
|
assert result is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test missing required registry
|
|
validator.clear_errors()
|
|
inputs = {}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert "registry" in str(validator.errors)
|
|
|
|
# Test registry validation
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"registry": "github",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test invalid registry
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"registry": "not-a-valid-registry",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert validator.has_errors()
|
|
|
|
# Test platform validation - only Linux platforms are valid for Docker
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"registry": "dockerhub",
|
|
"platforms": "linux/amd64,linux/arm64,linux/arm/v7",
|
|
}
|
|
result = validator.validate_inputs(inputs)
|
|
if not result:
|
|
pass
|
|
assert result is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test invalid platform OS
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"registry": "dockerhub",
|
|
"platforms": "freebsd/amd64",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert validator.has_errors()
|
|
|
|
# Test scan and sign settings
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"registry": "dockerhub",
|
|
"scan-image": "true",
|
|
"sign-image": "false",
|
|
}
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors()
|
|
|
|
# Test invalid registry value
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"registry": "invalid-registry-123",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
assert validator.has_errors()
|
|
|
|
def test_custom_validator_error_propagation(self):
|
|
"""Test that errors from sub-validators propagate correctly."""
|
|
registry = ValidatorRegistry()
|
|
|
|
# Test sync-labels with invalid token
|
|
validator = registry.get_validator("sync-labels")
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"labels": ".github/labels.yml",
|
|
"token": "invalid-token-format",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
# Should have error from token validator
|
|
assert validator.has_errors()
|
|
|
|
# Test docker-build with injection attempt
|
|
validator = registry.get_validator("docker-build")
|
|
validator.clear_errors()
|
|
inputs = {
|
|
"context": ".",
|
|
"build-args": "ARG1=value1\nARG2=; rm -rf /",
|
|
}
|
|
assert validator.validate_inputs(inputs) is False
|
|
errors = str(validator.errors).lower()
|
|
assert "injection" in errors or "security" in errors
|
|
|
|
def test_custom_validators_github_expressions(self):
|
|
"""Test that custom validators handle GitHub expressions correctly."""
|
|
registry = ValidatorRegistry()
|
|
|
|
# All custom validators should accept GitHub expressions
|
|
test_cases = [
|
|
(
|
|
"sync-labels",
|
|
{
|
|
"labels": "${{ github.workspace }}/.github/labels.yml",
|
|
"token": "${{ secrets.GITHUB_TOKEN }}",
|
|
},
|
|
),
|
|
(
|
|
"docker-build",
|
|
{
|
|
"context": "${{ github.workspace }}",
|
|
"dockerfile": "${{ inputs.dockerfile }}",
|
|
"tag": "${{ steps.meta.outputs.tags }}",
|
|
},
|
|
),
|
|
(
|
|
"codeql-analysis",
|
|
{
|
|
"language": "${{ matrix.language }}",
|
|
"queries": "${{ inputs.queries }}",
|
|
},
|
|
),
|
|
(
|
|
"docker-publish",
|
|
{
|
|
"registry": "${{ vars.REGISTRY }}",
|
|
"platforms": "${{ steps.platforms.outputs.list }}",
|
|
},
|
|
),
|
|
]
|
|
|
|
for action_type, inputs in test_cases:
|
|
validator = registry.get_validator(action_type)
|
|
validator.clear_errors()
|
|
# Add required fields if needed
|
|
if action_type == "docker-build":
|
|
inputs["context"] = inputs.get("context", ".")
|
|
elif action_type == "codeql-analysis":
|
|
inputs["language"] = inputs.get("language", "javascript")
|
|
|
|
assert validator.validate_inputs(inputs) is True
|
|
assert not validator.has_errors(), f"Failed for {action_type}: {validator.errors}"
|