Update Test Rules #10699
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Update Test Rules | |
on: | |
schedule: | |
- cron: '*/10 * * * *' | |
workflow_dispatch: # Allow manual triggering | |
env: | |
PYTHON_VERSION: "3.13" | |
jobs: | |
update-test-rules: | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
checks: write | |
pull-requests: write | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ env.PYTHON_VERSION }} | |
- name: Install dependencies | |
run: pip install requests | |
- name: Create and switch to test-rules branch | |
run: | | |
git fetch --all | |
if git show-ref --quiet refs/remotes/origin/test-rules; then | |
git checkout test-rules | |
git pull origin test-rules | |
else | |
git checkout -b test-rules | |
fi | |
- name: Get latest script from main branch | |
run: | | |
# Checkout the scripts directory from main branch without switching branches | |
git checkout origin/main -- scripts/ | |
# Unstage the script changes - we only want to commit detection rule changes | |
git reset HEAD scripts/ || true | |
- name: Run the script | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
SCRIPT_MODE: 'test-rules' | |
# Configure test-rules specific settings | |
FILTER_BY_ORG_MEMBERSHIP: 'true' | |
ORG_NAME: 'sublime-security' | |
INCLUDE_PRS_WITH_COMMENT: 'true' | |
COMMENT_TRIGGER: '/update-test-rules' | |
# SKIP_FILES pattern and labls are managed within the script | |
SKIP_FILES_WITH_TEXT: 'true' | |
# Skip PRs with too many rules | |
SKIP_BULK_PRS: 'true' | |
MAX_RULES_PER_PR: '10' | |
BULK_PR_LABEL: 'test-rules:excluded:bulk_rules' | |
# Disable adding tags that aren't useful | |
ADD_RULE_STATUS_TAG: 'false' | |
ADD_PR_REFERENCE: 'false' | |
INCLUDE_PR_IN_NAME: 'false' | |
ADD_AUTHOR_TAG: 'false' | |
CREATE_OPEN_PR_TAG: 'false' | |
ADD_TEST_RULES_LABEL: 'true' | |
IN_TEST_RULES_LABEL: 'in-test-rules' | |
AUTHOR_MEMBERSHIP_EXCLUSION_LABEL: 'test-rules:excluded:author_membership' | |
run: python scripts/sync_detection_rules.py | |
- name: Clean up script changes | |
run: | | |
# Revert any script changes back to test-rules branch version | |
# This keeps the working directory clean | |
# this shouldn't be required, but is a good thing to do anyway | |
git checkout HEAD -- scripts/ || true | |
- name: Commit changes | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
git config --global user.name 'github-actions[bot]' | |
git config --global user.email 'github-actions[bot]@users.noreply.github.com' | |
# First check if there are any changes | |
if [[ -z $(git status --porcelain detection-rules) ]]; then | |
echo "No changes to detection rules" | |
exit 0 | |
fi | |
# Get list of modified files | |
FILES=$(git status --porcelain detection-rules | awk '{print $2}') | |
# Track stats for the summary | |
ADDED_COUNT=0 | |
MODIFIED_COUNT=0 | |
DELETED_COUNT=0 | |
UPDATED_PRS=() | |
# Process each file individually | |
for FILE in $FILES; do | |
echo "Processing $FILE" | |
# Skip non-rule files | |
if [[ ! "$FILE" =~ .*\.yml$ ]]; then | |
continue | |
fi | |
BASENAME=$(basename "$FILE") | |
# Extract PR number from filename (format: PR_NUMBER_filename.yml) | |
PR_NUMBER=$(echo "$BASENAME" | grep -o "^[0-9]*") | |
UPDATED_PRS+=("$PR_NUMBER") | |
echo "Processing $BASENAME from $PR_NUMBER for inclusion" | |
# Handle removed files | |
if [[ ! -f "$FILE" ]]; then | |
echo "$BASENAME was deleted, commiting deletion." | |
DELETED_COUNT=$((DELETED_COUNT+1)) | |
git add "$FILE" | |
git commit -m "[PR #${PR_NUMBER}] Delete detection rule" | |
continue | |
fi | |
# Ensure that new files are tracked (otherwise they won't show up in diff) | |
git add -N "$FILE" | |
# Check if only testing_sha field changed | |
DIFF_OUTPUT=$(git diff HEAD -- "$FILE") | |
# Skip files with no changes at all | |
if [[ -z "$DIFF_OUTPUT" ]]; then | |
echo "\tSkipping $FILE: no changes" | |
continue | |
fi | |
# Check if the diff only contains testing_sha changes | |
# Look for lines that are only additions/deletions of testing_sha with hash values | |
# Handle files that may not have trailing newlines | |
# Filter out hunk headers & file paths so we just have +- lines of changed content. | |
LINES_CHANGES_ONLY=$(echo "$DIFF_OUTPUT" | grep -E '^[+-]' | grep -v -E '^[+-]{3}' | grep -v '^\\ No newline at end of file') | |
NON_TESTING_SHA_CHANGES=$(echo $LINES_CHANGES_ONLY | grep -v -E '^[+-]testing_sha: [0-9a-f]+' || true) | |
if [[ -z "$NON_TESTING_SHA_CHANGES" ]]; then | |
echo "\tSkipping $FILE: only testing_sha field changed. Lines changed:" | |
echo "$LINES_CHANGES_ONLY" | |
continue | |
fi | |
# Determine status directly from git | |
GIT_STATUS=$(git status --porcelain "$FILE" | cut -c1-2 | tr -d ' ') | |
if [[ "$GIT_STATUS" == "A" ]]; then | |
ADDED_COUNT=$((ADDED_COUNT+1)) | |
STATUS="added" | |
elif [[ "$GIT_STATUS" == "M" ]]; then | |
MODIFIED_COUNT=$((MODIFIED_COUNT+1)) | |
STATUS="modified" | |
else | |
MODIFIED_COUNT=$((MODIFIED_COUNT+1)) | |
STATUS="changed" | |
fi | |
# Extract rule name and commit | |
RULE_NAME=$(grep -m 1 "name:" "$FILE" | sed 's/name: //' | sed 's/^"//' | sed 's/"$//' | sed "s/^'//" | sed "s/'$//") | |
# Build commit message | |
COMMIT_MSG="[PR #${PR_NUMBER}] ${STATUS} rule: ${RULE_NAME}" | |
# Add file and commit | |
git add "$FILE" | |
git commit -m "$COMMIT_MSG" | |
done | |
# Get unique PR count | |
UNIQUE_PRS=$(printf '%s\n' "${UPDATED_PRS[@]}" | sort -u | wc -l) | |
# Create summary | |
echo "Detection Rules Sync Summary ($(date '+%Y-%m-%d %H:%M:%S'))" | |
echo "- PRs processed: ${UNIQUE_PRS}" | |
echo "- Rules added: ${ADDED_COUNT}" | |
echo "- Rules modified: ${MODIFIED_COUNT}" | |
echo "- Rules deleted: ${DELETED_COUNT}" | |
# Push changes | |
git push |