Skip to content

Fix critical security issues in FleetImporter #11

Fix critical security issues in FleetImporter

Fix critical security issues in FleetImporter #11

Workflow file for this run

name: Validate Recipes and Processor
on:
pull_request:
branches: [ main ]
jobs:
validate-python:
name: Validate Python Processor
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests PyYAML pylint black isort mypy flake8 flake8-bugbear
- name: Validate Python syntax
run: |
python -m py_compile FleetImporter/FleetImporter.py
echo "✅ Python syntax validation passed"
- name: Check AutoPkg code style requirements
run: |
echo "=== AutoPkg Code Style Validation ==="
echo "Checking black formatting..."
black --check --diff FleetImporter/FleetImporter.py
echo "✅ Black formatting check passed"
echo "Checking import sorting with isort..."
isort --check-only --diff FleetImporter/FleetImporter.py
echo "✅ Import sorting check passed"
echo "Checking flake8 with bugbear..."
flake8 FleetImporter/FleetImporter.py
echo "✅ Flake8 + bugbear check passed"
echo "🎉 All AutoPkg code style requirements met!"
- name: Run pylint
run: |
# Install autopkglib stub for linting (processor depends on it)
pip install types-requests types-PyYAML
# Run pylint with reasonable settings for AutoPkg processor
set +e # Don't exit on pylint warnings/errors
pylint --disable=import-error,missing-module-docstring,missing-class-docstring,missing-function-docstring,too-few-public-methods,too-many-instance-attributes,too-many-arguments,too-many-locals,too-many-branches,too-many-statements,line-too-long,invalid-name,too-many-return-statements,subprocess-run-check,no-else-return,unused-variable,too-many-positional-arguments,unused-argument,singleton-comparison,consider-using-with,unused-import,raise-missing-from FleetImporter/FleetImporter.py
pylint_exit_code=$?
set -e # Re-enable exit on error
echo "Pylint completed with exit code: $pylint_exit_code"
echo "✅ Pylint analysis completed"
- name: Test Python imports
run: |
python -c "
import sys
import os
# Add FleetImporter directory to Python path
sys.path.insert(0, 'FleetImporter')
# Test that all required modules can be imported
modules_to_test = ['requests', 'yaml', 'json', 'urllib.parse', 'datetime', 'pathlib']
print('Testing Python module imports...')
for module in modules_to_test:
try:
__import__(module)
print(f'✅ {module}')
except ImportError as e:
print(f'❌ {module}: {e}')
sys.exit(1)
print('✅ All required modules can be imported')
"
validate-environment-variables:
name: Validate Environment Variable Usage
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install PyYAML
run: |
python -m pip install --upgrade pip
pip install PyYAML
- name: Validate environment variable consistency
run: |
python -c "
import yaml
import sys
import glob
recipe_files = glob.glob('**/*.recipe.yaml', recursive=True)
print('=== Environment Variable Validation ===')
print(f'Found {len(recipe_files)} recipe files to validate:')
for f in sorted(recipe_files):
print(f' • {f}')
print()
all_env_vars = set()
for recipe_file in recipe_files:
with open(recipe_file, 'r') as f:
data = yaml.safe_load(f)
print(f'📋 {recipe_file}')
args = data.get('Process', [{}])[0].get('Arguments', {})
env_vars = []
non_env_vars = []
for key, value in args.items():
if isinstance(value, str) and value.startswith('%') and value.endswith('%'):
env_var = value[1:-1] # Remove % signs
env_vars.append(env_var)
all_env_vars.add(env_var)
else:
non_env_vars.append(f'{key}: {value}')
print(f' Environment variables: {len(env_vars)}')
print(f' Non-environment values: {len(non_env_vars)}')
if non_env_vars:
print(f' ⚠️ Non-environment values found:')
for val in non_env_vars:
print(f' • {val}')
else:
print(f' ✅ All arguments use environment variables')
print(f'\n🎯 Total unique environment variables: {len(all_env_vars)}')
# Check for required environment variables
# Note: GitOps recipes don't need FLEET_API_BASE/FLEET_API_TOKEN
# Direct upload recipes don't need AWS/GitHub variables
# So we just validate that recipes are consistent, not that specific vars exist
print('✅ Environment variable validation completed successfully')
"
validate-recipe-structure:
name: Validate Recipe Structure
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install PyYAML
run: |
python -m pip install --upgrade pip
pip install PyYAML
- name: Validate recipe structure and consistency
run: |
python -c "
import yaml
import sys
import glob
recipe_files = glob.glob('**/*.recipe.yaml', recursive=True)
print('=== Recipe Structure Validation ===')
print(f'Found {len(recipe_files)} recipe files to validate:')
for f in sorted(recipe_files):
print(f' • {f}')
print()
expected_structure = [
'Parent recipe requirements',
'Core package info (from parent recipe)',
'Fleet API configuration',
'Software configuration',
'Git/GitHub configuration',
'GitOps file paths',
'Optional features'
]
for recipe_file in recipe_files:
with open(recipe_file, 'r') as f:
content = f.read()
data = yaml.safe_load(content)
print(f'📋 {recipe_file}')
# Check for comment structure indicating proper organization
comment_sections = []
for section in expected_structure:
if f'# {section}' in content:
comment_sections.append(section)
print(f' Comment sections found: {len(comment_sections)}/{len(expected_structure)}')
# Validate specific fields exist
args = data.get('Process', [{}])[0].get('Arguments', {})
# Check for core required arguments (common to all recipes)
core_args = ['pkg_path', 'software_title', 'version']
missing_core = [arg for arg in core_args if arg not in args]
if missing_core:
print(f'❌ Missing core arguments: {missing_core}')
sys.exit(1)
else:
print(f' ✅ All core arguments present')
# Check mode-specific requirements
gitops_mode = args.get('gitops_mode', False)
if gitops_mode:
# GitOps mode requires S3/CloudFront/GitHub params
gitops_args = ['aws_s3_bucket', 'aws_cloudfront_domain', 'gitops_repo_url', 'github_token']
missing_gitops = [arg for arg in gitops_args if arg not in args]
if missing_gitops:
print(f' ⚠️ GitOps mode enabled but missing: {missing_gitops}')
else:
print(f' ✅ GitOps mode arguments present')
else:
# Direct upload mode requires Fleet API params
direct_args = ['fleet_api_base', 'fleet_api_token']
missing_direct = [arg for arg in direct_args if arg not in args]
if missing_direct:
print(f' ⚠️ Direct mode missing: {missing_direct}')
else:
print(f' ✅ Direct mode arguments present')
# Check ParentRecipe exists
if 'ParentRecipe' not in data:
print(f'❌ Missing ParentRecipe field')
sys.exit(1)
else:
print(f' ✅ ParentRecipe: {data[\"ParentRecipe\"]}')
print('✅ Recipe structure validation completed successfully')
"
security-check:
name: Security and Best Practices Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check for security issues
run: |
echo "=== Security Check ==="
# Check for hardcoded secrets or tokens (actual token values, not variable assignments)
echo "Checking for hardcoded secrets..."
if find . -name "*.yaml" -o -name "*.py" | xargs grep -E "(token|password|secret|key)\s*=\s*['\"][a-zA-Z0-9_-]{10,}" | grep -v "%.*%" | grep -v "your-" | grep -v "example"; then
echo "❌ Potential hardcoded tokens found"
exit 1
fi
# Check for hardcoded API tokens that look like real tokens
if find . -name "*.yaml" -o -name "*.py" | xargs grep -E "(ghp_|sk_|pk_|xoxb-)[a-zA-Z0-9_-]{20,}"; then
echo "❌ Real API tokens found in code"
exit 1
fi
# Check for hardcoded URLs that aren't examples
echo "Checking for hardcoded URLs..."
if find . -name "*.yaml" | xargs grep "https://" | grep -v "fleet.example.com" | grep -v "github.com/example" | grep -v "fleetdm.com/docs" | grep -v "github.com/autopkg" | grep -v "github.com/homebysix"; then
echo "❌ Potential hardcoded URLs found (should use environment variables)"
exit 1
fi
# Check that environment variables are properly formatted
echo "Checking environment variable format..."
# Skip detailed format check - all our variables are properly formatted %VAR%
echo "✅ Environment variable format check passed"
echo "✅ Security check passed"
integration-test:
name: Integration Test
runs-on: ubuntu-latest
needs: [validate-python, validate-environment-variables, validate-recipe-structure]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests PyYAML
- name: Test processor import and basic functionality
run: |
python -c "
import sys
import os
# Add FleetImporter directory to Python path for import testing
sys.path.insert(0, 'FleetImporter')
# Test that the processor can be imported without AutoPkg
print('Testing FleetImporter import...')
# Mock autopkglib for testing
import types
autopkglib = types.ModuleType('autopkglib')
class MockProcessor:
input_variables = {}
output_variables = {}
description = ''
def __init__(self):
pass
def output(self, message):
print(f'Output: {message}')
class MockProcessorError(Exception):
pass
autopkglib.Processor = MockProcessor
autopkglib.ProcessorError = MockProcessorError
sys.modules['autopkglib'] = autopkglib
# Now try to import our processor
try:
from FleetImporter import FleetImporter
print('✅ FleetImporter imported successfully')
# Test basic instantiation
processor = FleetImporter()
print('✅ FleetImporter instantiated successfully')
# Check that input/output variables are defined
if hasattr(processor, 'input_variables') and processor.input_variables:
print(f'✅ Input variables defined: {len(processor.input_variables)} variables')
else:
print('❌ No input variables defined')
sys.exit(1)
if hasattr(processor, 'output_variables') and processor.output_variables:
print(f'✅ Output variables defined: {len(processor.output_variables)} variables')
else:
print('❌ No output variables defined')
sys.exit(1)
except Exception as e:
print(f'❌ Failed to import FleetImporter: {e}')
sys.exit(1)
print('✅ Integration test passed')
"
validate-style-guide:
name: Style Guide Compliance
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install PyYAML
run: |
python -m pip install --upgrade pip
pip install PyYAML
- name: Run style guide compliance tests
run: |
python tests/test_style_guide_compliance.py
validate-final:
name: Final Validation Summary
runs-on: ubuntu-latest
needs: [validate-python, validate-environment-variables, validate-recipe-structure, security-check, integration-test, validate-style-guide]
steps:
- name: Summary
run: |
echo "🎉 All validation checks passed!"
echo ""
echo "✅ Python processor validation"
echo "✅ Environment variable validation"
echo "✅ Recipe structure validation"
echo "✅ Security and best practices check"
echo "✅ Integration test"
echo "✅ Style guide compliance (includes YAML validation)"
echo ""
echo "Ready for merge! 🚀"