Security teams can adopt DevOps principals to standardize how rules are authored, reviewed, tested, and deployed. Here’s I’ve created a mock deployment using GitHub Actions.
This post walks through a complete end to end CI/CD example that mirrors a lightweight Git pipeline. It uses two GitHub workflows, a pull request review gate, and a mock deployment target using RequestBin. Everything runs inside GitHub, and nothing requires external infrastructure.
Overview
The goal is to simulate a DaC pipeline with these phases:
- Edit a rule file in a local repository.
- Push to a feature branch.
- Open a pull request.
- GitHub runs a validation workflow on the PR.
- Approve the PR.
- Merge into main.
- A deployment workflow packages the rule, encodes it, and sends it to a mock endpoint.
Repository Structure
Create a simple repo with a rules/ directory and GitHub workflow definitions under .github/workflows/.
mock-dac-pipeline/
├── rules/
│ └── windows_lateral_movement.yaml
└── .github/
└── workflows/
├── pr-validate.yaml
└── deploy.yaml
A rule file can contain any content for demonstration purposes:
title: windows_lateral_movement
description: test rule
severity: high
This repository acts as the source of truth for all rules.
Step 1: Set Up a Mock Deployment Target
You need an external API endpoint where the workflow can POST the deployed rule. RequestBin is a simple disposable endpoint that shows incoming requests.
Create one at:
It produces an endpoint similar to:
https://asdfasdfasdfasdf.m.pipedream.net
Store this in a GitHub secret so it remains private.
In the repo:
Settings -> Secrets and variables -> Actions -> New repository secret
Name: REQUESTBIN_URL
Value: https://<your-id>.m.pipedream.net
Step 2: Create the PR Validation Workflow
The validation workflow inspects rule changes in a pull request and prints the resulting JSON payload to the logs. This simulates a DaC validation stage.
Create .github/workflows/pr-validate.yaml:
name: PR Validation
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Identify changed rule files
id: diff
run: |
changed=$(git diff --name-only origin/main...HEAD | grep '^rules/' || true)
echo "changed=$changed" >> $GITHUB_OUTPUT
echo "Changed files:"
echo "$changed"
- name: Stop if no rule changes
if: ${{ steps.diff.outputs.changed == '' }}
run: echo "No rule changes in this PR"
- name: Build JSON preview
if: ${{ steps.diff.outputs.changed != '' }}
run: |
files="${{ steps.diff.outputs.changed }}"
json="[]"
for f in $files; do
sha=$(sha256sum "$f" | awk '{print $1}')
name=$(basename "$f")
content_b64=$(base64 -w0 "$f")
json=$(echo "$json" | jq ". += [{\"name\": \"$name\", \"hash\": \"$sha\", \"content_b64\": \"$content_b64\"}]")
done
echo "Preview JSON payload:"
echo "$json"
This workflow verifies rule changes without pushing anything. It gives you visibility into exactly what will be deployed after merge.
Step 3: Set Up Branch Protection and PR Approvals
For a realistic DaC workflow, enforce pull request approvals. In a real engineering environment, this would require sign off from peers or code owners.
In GitHub:
Settings -> Branches -> Add Rule
Enable:
- Require a pull request before merging
- Require approvals
- Required number of approvals: 1
If you want to test solo, add a second GitHub account as a collaborator so you can assign and approve reviews.
Step 4: Create the Deployment Workflow
The deployment workflow runs only on pushes to main. It packages rule files, base64 encodes content, and POSTs the payload to your mock endpoint.
Create .github/workflows/deploy.yaml:
name: Rule Deployment
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Identify changed rule files
id: diff
run: |
echo "Before SHA: ${{ github.event.before }}"
echo "After SHA: ${{ github.sha }}"
changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" | grep '^rules/' || true)
echo "changed=$changed" >> $GITHUB_OUTPUT
- name: Stop if no rule changes
if: ${{ steps.diff.outputs.changed == '' }}
run: echo "No rule changes detected"
- name: Build JSON payload
if: ${{ steps.diff.outputs.changed != '' }}
id: build
run: |
files="${{ steps.diff.outputs.changed }}"
json="[]"
for f in $files; do
sha=$(sha256sum "$f" | awk '{print $1}')
name=$(basename "$f")
content_b64=$(base64 -w0 "$f")
json=$(echo "$json" | jq ". += [{\"name\": \"$name\", \"hash\": \"$sha\", \"content_b64\": \"$content_b64\"}]")
done
{
echo "payload<<EOF"
echo "$json"
echo "EOF"
} >> $GITHUB_OUTPUT
- name: Send to RequestBin
if: ${{ steps.build.outputs.payload != '' }}
run: |
curl -X POST \
-H "Content-Type: application/json" \
-d "${{ steps.build.outputs.payload }}" \
"${{ secrets.REQUESTBIN_URL }}"
The last step sends the rule bundle to the secret URL. The resulting payload appears immediately in RequestBin for inspection.
Step 5: Full Workflow in Action
Create a Feature Branch
git checkout -b test-dac-change
echo "# new test change" >> rules/windows_lateral_movement.yaml
git commit -am "update rule"
git push --set-upstream origin test-dac-change
Open a Pull Request
GitHub automatically triggers the validation workflow. Under the Checks tab you can see:
- Changed files
- SHA256 hash
- Base64 encoded rule contents
- Generated JSON payload
This acts as your DaC review artifact.
Approve and Merge
Your second GitHub account (or collaborator) reviews and approves the PR.
Merge into main.
Deployment Workflow Executes
Under the Actions tab, the deployment workflow runs:
- Detects changed rules
- Encodes and packages them
- Sends to the mock endpoint
You will see the incoming JSON payload in the RequestBin inspector.
Result
You now have:
- A working CI/CD pipeline for mock Detection as Code
- PR based approval gating
- Payload bundling and encoding
- Automated deployment using GitHub Actions
- A fully contained environment suitable for demos and experimentation
This structure mirrors modern DaC implementations used in enterprise security engineering teams.
0 Comments