Files
patterns/.github/workflows/update_patterns.yml
Fabrizio Salmi 478c20a83c Generate dense release notes with stats, install snippet, and SHA-256
- New workflow step builds release_notes.md from runtime data: build date,
  resolved CRS tag (queried from upstream API), total OWASP rule count,
  category count, per-backend bot counts, archive sizes (du -h), and
  SHA-256 checksums of every zip.
- Replace deprecated actions/create-release@v1 + 4x upload-release-asset@v1
  with a single softprops/action-gh-release@v2 step that publishes the body
  and all four archives in one go.
- Release body becomes a self-contained, email-friendly summary visible in
  GitHub notification mails: coverage, backends table, quick-install
  one-liner, and supply-chain-verifiable SHA-256 list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:51:10 +02:00

198 lines
7.2 KiB
YAML

name: Update Patterns
permissions:
contents: write # Commit changes, push updates
statuses: write # Update commit statuses
actions: read # Required for checking out the repository
packages: write # For GitHub Packages (if used)
on:
schedule:
- cron: '0 0 * * *' # Daily at midnight UTC
workflow_dispatch: # Manual trigger
jobs:
update-owasp-waf:
runs-on: ubuntu-latest
steps:
- name: 🚚 Checkout Repository
uses: actions/checkout@v3
with:
fetch-depth: 0 # get full git history
- name: ⚙️ Set Up Python 3.11
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 📦 Cache pip dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: 📥 Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: 🕷️ Run OWASP Scraper
run: python owasp2json.py
- name: 🔄 Convert OWASP to Nginx WAF
run: python json2nginx.py
- name: 🔄 Convert OWASP to Apache WAF
run: python json2apache.py
- name: 🔄 Convert OWASP to Traefik WAF
run: python json2traefik.py
- name: 🔄 Convert OWASP to HAProxy WAF
run: python json2haproxy.py
- name: 🔄 Generate Bad Bot Blockers
run: python badbots.py
- name: 🚀 Commit and Push Changes (if any)
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add .
# Check if there are any changes *before* committing.
if ! git diff --quiet --exit-code; then
git commit -m "Update WAF rules [$(date +'%Y-%m-%d')]"
git push
else
echo "No changes to commit."
fi
continue-on-error: true # Continue even if no changes
- name: 📦 Create Zip Archives
run: |
mkdir -p dist
(cd waf_patterns/nginx && zip -r ../../dist/nginx_waf.zip .)
(cd waf_patterns/apache && zip -r ../../dist/apache_waf.zip .)
(cd waf_patterns/traefik && zip -r ../../dist/traefik_waf.zip .)
(cd waf_patterns/haproxy && zip -r ../../dist/haproxy_waf.zip .)
- name: 🗑️ Delete Existing 'latest' Tag and Release (if they exist)
run: |
# Delete local tag
git tag -d latest || true
# Delete remote tag (force)
git push --delete origin latest || true
# Delete release, --yes for confirmation
gh release delete latest --yes || true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 📝 Generate release notes
if: success()
run: |
set -euo pipefail
BUILD_DATE=$(date -u +'%Y-%m-%d')
# Latest CRS tag (falls back to v4.0 if API is unreachable)
CRS_REF=$(curl -sfL https://api.github.com/repos/coreruleset/coreruleset/releases/latest \
| jq -r '.tag_name // "v4.0"' 2>/dev/null || echo "v4.0")
# OWASP source coverage
TOTAL_RULES=$(jq length owasp_rules.json)
CATEGORIES=$(jq -r '.[].category' owasp_rules.json | sort -u | wc -l | tr -d ' ')
# Bot pattern counts per backend
NGINX_BOTS=$(grep -c '^\s*"~' waf_patterns/nginx/bots.conf || echo 0)
APACHE_BOTS=$(grep -c '^SecRule REQUEST_HEADERS' waf_patterns/apache/bots.conf || echo 0)
TRAEFIK_BOTS=$(grep -cE '^\s*"' waf_patterns/traefik/bots.toml || echo 0)
HAPROXY_BOTS=$(grep -c '^acl' waf_patterns/haproxy/bots.acl || echo 0)
# Archive sizes (human-readable)
NGINX_SIZE=$(du -h dist/nginx_waf.zip | cut -f1)
APACHE_SIZE=$(du -h dist/apache_waf.zip | cut -f1)
TRAEFIK_SIZE=$(du -h dist/traefik_waf.zip | cut -f1)
HAPROXY_SIZE=$(du -h dist/haproxy_waf.zip | cut -f1)
# SHA-256 checksums
NGINX_SHA=$(sha256sum dist/nginx_waf.zip | cut -d' ' -f1)
APACHE_SHA=$(sha256sum dist/apache_waf.zip | cut -d' ' -f1)
TRAEFIK_SHA=$(sha256sum dist/traefik_waf.zip | cut -d' ' -f1)
HAPROXY_SHA=$(sha256sum dist/haproxy_waf.zip | cut -d' ' -f1)
cat > release_notes.md <<EOF
**Patterns build · ${BUILD_DATE}**
OWASP Core Rule Set [\`coreruleset/coreruleset@${CRS_REF}\`](https://github.com/coreruleset/coreruleset/releases/tag/${CRS_REF}) converted into native WAF rules for four web servers. Generated automatically; no manual edits.
**Coverage:** ${TOTAL_RULES} patterns across ${CATEGORIES} categories &mdash; SQLi, XSS, RCE, LFI, RFI, plus generic anomaly and protocol-violation rules.
### Backends
| Backend | Format | Bots | Archive | Size |
|----------|-------------------|--------:|-----------------------|---------:|
| Nginx | \`map\` + \`if\` | ${NGINX_BOTS} | \`nginx_waf.zip\` | ${NGINX_SIZE} |
| Apache | ModSecurity | ${APACHE_BOTS} | \`apache_waf.zip\` | ${APACHE_SIZE} |
| Traefik | Middleware TOML | ${TRAEFIK_BOTS} | \`traefik_waf.zip\` | ${TRAEFIK_SIZE} |
| HAProxy | ACL | ${HAPROXY_BOTS} | \`haproxy_waf.zip\` | ${HAPROXY_SIZE} |
### Quick install
\`\`\`bash
curl -LO https://github.com/fabriziosalmi/patterns/releases/latest/download/nginx_waf.zip
unzip nginx_waf.zip -d /etc/nginx/waf_patterns
\`\`\`
Integration guides → [Nginx](https://fabriziosalmi.github.io/patterns/nginx) · [Apache](https://fabriziosalmi.github.io/patterns/apache) · [Traefik](https://fabriziosalmi.github.io/patterns/traefik) · [HAProxy](https://fabriziosalmi.github.io/patterns/haproxy)
### Verify (SHA-256)
\`\`\`text
${NGINX_SHA} nginx_waf.zip
${APACHE_SHA} apache_waf.zip
${TRAEFIK_SHA} traefik_waf.zip
${HAPROXY_SHA} haproxy_waf.zip
\`\`\`
\`\`\`bash
echo "${NGINX_SHA} nginx_waf.zip" | sha256sum -c -
\`\`\`
---
[Documentation](https://fabriziosalmi.github.io/patterns/) · [Source](https://github.com/fabriziosalmi/patterns) · [Issues](https://github.com/fabriziosalmi/patterns/issues)
EOF
echo "Generated release_notes.md ($(wc -l < release_notes.md) lines)"
- name: 🚀 Publish release
if: success()
uses: softprops/action-gh-release@v2
with:
tag_name: latest
name: WAF rules (Nginx, Apache, Traefik, Haproxy)
body_path: release_notes.md
files: |
dist/nginx_waf.zip
dist/apache_waf.zip
dist/traefik_waf.zip
dist/haproxy_waf.zip
make_latest: 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 🧹 Clean Up (Optional)
if: always() # Run cleanup even on failure
run: rm -rf ~/.cache/pip
- name: 🚨 Notify on Failure (Optional)
if: failure()
run: |
echo "🚨 Workflow failed! Please investigate."
# Example: Send a Slack notification (requires a Slack webhook URL)
# curl -X POST -H 'Content-type: application/json' --data '{"text":"WAF update workflow failed!"}' ${{ secrets.SLACK_WEBHOOK }}