mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat(github_actions): enhance workflow exclusion with full path support
Zizmor doesn't support --exclude flag natively, so implemented exclusion at Prowler level by filtering findings after scan completes. The exclusion now supports both pattern types: - Filename patterns: 'test-*.yml' matches any file named test-*.yml - Full path patterns: '.github/workflows/test-*.yml' matches specific paths - Subdirectory patterns: '**/experimental/*.yml' matches subdirs Changes: - Added fnmatch import for glob pattern matching - Removed invalid --exclude flags from zizmor command - Implemented _should_exclude_workflow() with dual matching logic - Filter findings during processing based on exclusion patterns - Added comprehensive test covering all pattern types - Updated documentation with examples of both pattern types
This commit is contained in:
@@ -98,12 +98,31 @@ Authentication for private repositories can be provided using one of the followi
|
||||
|
||||
### Excluding Workflows
|
||||
|
||||
Exclude specific workflows or patterns from scanning:
|
||||
Exclude specific workflows from the scan results using glob patterns. Prowler filters findings after zizmor completes the scan, matching patterns against both workflow filenames and full paths:
|
||||
|
||||
```bash
|
||||
prowler github_actions --exclude-workflows "test-*.yml" "experimental/*"
|
||||
# Exclude by filename pattern (matches any path)
|
||||
prowler github_actions --exclude-workflows "test-*.yml" "api-*.yml"
|
||||
|
||||
# Exclude by full path pattern
|
||||
prowler github_actions --exclude-workflows ".github/workflows/experimental-*.yml"
|
||||
|
||||
# Exclude entire subdirectories
|
||||
prowler github_actions --exclude-workflows "**/draft/*.yml"
|
||||
|
||||
# Combine multiple pattern types
|
||||
prowler github_actions \
|
||||
--workflow-path .github/workflows \
|
||||
--exclude-workflows "test-*.yml" ".github/workflows/experimental/*"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Patterns can match against either the full path or just the filename:
|
||||
- `test-*.yml` matches `test-deploy.yml` in any directory
|
||||
- `.github/workflows/test-*.yml` matches only workflows in that specific directory
|
||||
- `**/experimental/*.yml` matches workflows in any `experimental` subdirectory
|
||||
</Note>
|
||||
|
||||
### Output Formats
|
||||
|
||||
The GitHub Actions provider supports all standard Prowler output formats:
|
||||
|
||||
@@ -3,6 +3,7 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from fnmatch import fnmatch
|
||||
from os import environ
|
||||
from typing import List
|
||||
|
||||
@@ -132,6 +133,42 @@ class GithubActionsProvider(Provider):
|
||||
"""GitHub Actions provider doesn't need a session since it uses zizmor directly"""
|
||||
return None
|
||||
|
||||
def _should_exclude_workflow(self, workflow_file: str, exclude_patterns: list[str]) -> bool:
|
||||
"""
|
||||
Check if a workflow file should be excluded based on exclude patterns.
|
||||
|
||||
Patterns can match against either:
|
||||
- The full workflow path (e.g., ".github/workflows/test-*.yml")
|
||||
- Just the filename (e.g., "test-*.yml")
|
||||
|
||||
Args:
|
||||
workflow_file: The workflow file path
|
||||
exclude_patterns: List of glob patterns to exclude
|
||||
|
||||
Returns:
|
||||
True if the workflow should be excluded, False otherwise
|
||||
"""
|
||||
if not exclude_patterns:
|
||||
return False
|
||||
|
||||
# Get just the filename from the path
|
||||
from os.path import basename
|
||||
filename = basename(workflow_file)
|
||||
|
||||
# Check if the pattern matches either the full path or just the filename
|
||||
for pattern in exclude_patterns:
|
||||
# Try matching against full path first
|
||||
if fnmatch(workflow_file, pattern):
|
||||
logger.debug(f"Excluding workflow {workflow_file} (matches full path pattern: {pattern})")
|
||||
return True
|
||||
|
||||
# Also try matching against just the filename for convenience
|
||||
if fnmatch(filename, pattern):
|
||||
logger.debug(f"Excluding workflow {workflow_file} (matches filename pattern: {pattern})")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _extract_workflow_file_from_location(self, location: dict) -> str:
|
||||
"""
|
||||
Extract the workflow file path from a location object.
|
||||
@@ -368,6 +405,7 @@ class GithubActionsProvider(Provider):
|
||||
logger.info(f"Running GitHub Actions security scan on {directory} ...")
|
||||
|
||||
# Build zizmor command
|
||||
# Note: zizmor doesn't support --exclude, so we filter findings after scanning
|
||||
zizmor_command = [
|
||||
"zizmor",
|
||||
directory,
|
||||
@@ -375,10 +413,6 @@ class GithubActionsProvider(Provider):
|
||||
"json",
|
||||
]
|
||||
|
||||
# Add exclude patterns if provided
|
||||
for exclude_pattern in exclude_workflows:
|
||||
zizmor_command.extend(["--exclude", exclude_pattern])
|
||||
|
||||
with alive_bar(
|
||||
ctrl_c=False,
|
||||
bar="blocks",
|
||||
@@ -444,6 +478,10 @@ class GithubActionsProvider(Provider):
|
||||
location
|
||||
)
|
||||
if workflow_file:
|
||||
# Check if this workflow should be excluded
|
||||
if self._should_exclude_workflow(workflow_file, exclude_workflows):
|
||||
continue
|
||||
|
||||
report = self._process_zizmor_finding(
|
||||
finding, workflow_file, location
|
||||
)
|
||||
|
||||
@@ -222,3 +222,53 @@ class TestGithubActionsProvider:
|
||||
args = mock_print.call_args[0]
|
||||
assert "https://github.com/test/repo" in str(args[0])
|
||||
assert "test*.yml" in str(args[0])
|
||||
|
||||
def test_should_exclude_workflow(self):
|
||||
"""Test workflow exclusion pattern matching"""
|
||||
with patch.object(GithubActionsProvider, "setup_session", return_value=None):
|
||||
provider = GithubActionsProvider()
|
||||
|
||||
# Test no exclusions
|
||||
assert not provider._should_exclude_workflow(
|
||||
".github/workflows/test.yml", []
|
||||
)
|
||||
|
||||
# Test exact filename match
|
||||
assert provider._should_exclude_workflow(
|
||||
".github/workflows/test.yml", ["test.yml"]
|
||||
)
|
||||
|
||||
# Test wildcard pattern on filename
|
||||
assert provider._should_exclude_workflow(
|
||||
".github/workflows/test-api.yml", ["test-*.yml"]
|
||||
)
|
||||
|
||||
# Test multiple patterns
|
||||
assert provider._should_exclude_workflow(
|
||||
".github/workflows/api-test.yml", ["test-*.yml", "api-*.yml"]
|
||||
)
|
||||
|
||||
# Test no match
|
||||
assert not provider._should_exclude_workflow(
|
||||
".github/workflows/deploy.yml", ["test-*.yml", "api-*.yml"]
|
||||
)
|
||||
|
||||
# Test full path matching
|
||||
assert provider._should_exclude_workflow(
|
||||
".github/workflows/test.yml", [".github/workflows/test.yml"]
|
||||
)
|
||||
|
||||
# Test full path with wildcard
|
||||
assert provider._should_exclude_workflow(
|
||||
".github/workflows/api-tests.yml", [".github/workflows/api-*.yml"]
|
||||
)
|
||||
|
||||
# Test subdirectory patterns
|
||||
assert provider._should_exclude_workflow(
|
||||
".github/workflows/experimental/test.yml", ["**/experimental/*.yml"]
|
||||
)
|
||||
|
||||
# Test that filename pattern works regardless of path
|
||||
assert provider._should_exclude_workflow(
|
||||
"workflows/subdir/test-deploy.yml", ["test-*.yml"]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user