Contribute to an Enterprise Security Repo

Use Git workflows to contribute a detection rule to a security repo.

Introduction

30 Second Summary

Every security team has a repo full of detection rules. Contributing to it means navigating branch naming conventions, commit message formats, CI pipelines, and code review checklists that already exist. Getting your first PR merged without a pile of revision requests is a rite of passage.

In this project, you will contribute a detection rule to an existing enterprise security repo. You will follow the full professional workflow from cloning the repo and reading its conventions, to opening a pull request that passes all automated checks.

What You'll Build

By the end of this project, you will have pushed a feature branch with a working detection rule, survived a CI failure, and opened a PR that a security team reviewer could approve on first pass.

By the end of this project, you'll have:

  • A TOML detection rule for Elastic Security with a proper EQL query and MITRE ATT&CK threat mapping that you wrote, broke, and fixed yourself.
  • A pull request with a filled-out Context section and completed security checklist, following the exact format your team's PR template requires.
  • Custom Claude Code commands (/branch and /commit) that enforce your team's naming and commit conventions on every future contribution.
  • Secret Mission: Write a second rule in ElastAlert YAML format, validate it passes schema checks, and commit it with a multi-line commit body explaining the detection rationale.

Are there any prerequisites?

You need a free GitHub account, Git installed on your machine, VS Code installed, and Python 3.11+ for the validation scripts. No cloud infrastructure or paid tools are required.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters. Contributing a detection rule to an enterprise security repo means navigating real governance, writing commits that reviewers trust, and surviving CI failures without panicking.

Set Up the Enterprise Repo

When you join a security team, the detection rules repo already exists. CODEOWNERS files, PR templates, CI pipelines, and example rules are all in place. Your job is to orient yourself within the existing governance, not build it from scratch.

In this step, you will clone a private enterprise detection rules repo that has all this governance infrastructure already configured. This is the same onboarding experience a junior detection engineer has on day one.

In this step, get ready to:
  • Clone your private GitHub repository with enterprise governance already in place.
  • Verify the existing CI pipeline, validation scripts, and example rules work.
  • Open the project in VS Code for local development.
Clone your enterprise repo

Your repo is a private repository that contains everything an established security team's detection repo would have: ownership rules, a PR template, CI validation, convention documentation, and example detection rules in both TOML and YAML formats. No forking needed since you own it directly.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal and press Enter to open it.
  • Move to your Desktop by running this command:

You should see the repo cloned into a security-detection-rules-template folder on your Desktop.

Why a private repo?

This repo is your private learning sandbox. It simulates an enterprise team's shared detection rules repository with all the governance infrastructure (CODEOWNERS, CI, PR templates) pre-configured. You have full push access, so you can branch, push, and open PRs without needing to fork.

cd ~/Desktop
  • Clone the repo by running this command:
git clone https://github.com/josly-a/security-detection-rules-template.git
  • Move into the repo folder by running:
cd security-detection-rules-template
  • Confirm the governance structure exists by running:
ls .github/ rules/ scripts/

You should see CODEOWNERS, pull_request_template.md, and workflows/ inside .github/. The rules/ directory has network/ and elastalert/ subdirectories with example rules. The scripts/ directory has three Python validation scripts.

Don't see the expected files?

Make sure you cloned the correct repository at josly-a/security-detection-rules-template. If the clone succeeded but the directories are missing, confirm you are inside the security-detection-rules-template folder by running pwd.

help me troubleshoot cloning my enterprise repo

Verify the CI validation works locally

Before you contribute anything, confirm the existing tooling works. The team's CI pipeline runs Python validation scripts against every rule file. You can run these locally to catch errors before pushing.

  • Run the TOML validation script against the existing example rule:
python3 scripts/validate_toml_rule.py rules/network/example_dns_tunneling.toml

You should see PASSED: rules/network/example_dns_tunneling.toml in the output.

Seeing a Python error?

If you see a ModuleNotFoundError for tomllib, your Python version is below 3.11. The tomllib module is built into Python 3.11+.

help me fix my Python version issue for the validation script

  • Open VS Code in this directory by running:
code .

VS Code opens with the full repo structure visible in the Explorer sidebar. You can see the .github/, rules/, scripts/, and sample_logs/ directories that make up this enterprise repo.

Your repo is cloned and the existing governance tooling works. Next up, you will read these conventions like a new team member and create your first feature branch.

Read the Conventions and Create Your Branch

You have cloned an enterprise security repo with all its governance infrastructure in place. Before writing a single line of detection logic, you need to read the existing conventions.

Jumping straight to code without reading CODEOWNERS, the PR template, and example rules is how new contributors get their PRs rejected. Let's make sure that doesn't happen to you.

In this step, get ready to:
  • Read the repo governance files to understand review rules, commit conventions, and rule schema expectations.
  • Create a feature branch following the team's kebab-case naming convention.
  • Verify your branch is active and main is untouched.
Read the repo governance files

Enterprise repos encode their rules into specific files. Your job as a new contributor is to find and follow these patterns, not reinvent them.

  • View the CODEOWNERS file to see who reviews what by running this command:
cat .github/CODEOWNERS

You should see three ownership rules. The one that matters most for your contribution is /rules/ @security-team.

What does CODEOWNERS tell you?

The /rules/ pattern means any file you add under the rules/ directory automatically triggers a review request to @security-team. You do not need to manually request a reviewer.

  • View the team's branching and commit conventions by running:
cat CLAUDE.md

You should see sections for Branching Convention, Commit Convention, and Rule Naming.

What conventions does CLAUDE.md establish?

  • Branches use the format <type>/<kebab-case-description>. Types include feat, fix, chore, and docs.
  • Commits follow Conventional Commits: <type>(<scope>): <description>.
  • TOML rule files use the naming pattern <tactic>_<technique_description>.toml and live in rules/<category>/.
  • Examine the existing example TOML rule to see the schema your team expects by running:
cat rules/network/example_dns_tunneling.toml

You should see a complete rule with [metadata] and [rule] sections, plus a [[rule.threat]] block for MITRE ATT&CK mapping.

What schema patterns should you follow?

  • The [metadata] section needs creation_date, maturity, and updated_date.
  • The [rule] section includes fields like author, risk_score, severity, type, and a full EQL query.
  • The [[rule.threat]] section maps the detection to a MITRE ATT&CK technique and tactic. Notice how the example maps to T1071 (Application Layer Protocol) under TA0011 (Command and Control).
  • Finally, preview the PR template that will auto-fill when you open a pull request later by running:
cat .github/pull_request_template.md

Notice the Context section (what, why, false positive rate, data sources) and the Security Checklist with six items you will need to complete when you open your PR.

Create your feature branch

You know from CLAUDE.md that new feature branches follow the <type>/<kebab-case-description> format. Your rule will detect suspicious RDP tunneling, so the branch name is feat/detect-suspicious-rdp-tunneling.

  • Create and switch to your feature branch by running this command:
git checkout -b feat/detect-suspicious-rdp-tunneling

You should see the message Switched to a new branch 'feat/detect-suspicious-rdp-tunneling'.

Use the /branch command instead

You can also run /branch detect suspicious rdp tunneling inside Claude Code to have it generate the properly formatted branch name for you. The command reads your conventions from CLAUDE.md and creates the branch automatically.

If you see an error that the branch already exists, you may have created it previously. Run git checkout feat/detect-suspicious-rdp-tunneling (without -b) to switch to the existing branch.

Why can't I create my feature branch?

Verify your branch is active and main is clean

Before you write any code, confirm you are on the feature branch and that main has not been accidentally modified.

  • Before you run the next command, predict: which branch name will have the asterisk (*) next to it?
  • Check which branch you are on by running:
git branch

You should see two branches listed. The asterisk (*) marks your active branch:

* feat/detect-suspicious-rdp-tunneling
  main
  • Confirm main is still clean (no uncommitted changes leaked onto it) by running:
git status

You should see nothing to commit, working tree clean. Your feature branch was created from the latest main commit with a clean working directory.

Why work on a feature branch?

Feature branching isolates your changes from the shared main branch. If your detection rule has issues, the team's production rules stay safe. Your branch only merges into main after passing CI and code review.

You are on a properly named feature branch with a clean working tree. Next up, you will examine the sample log data to verify that the fields your detection rule needs actually exist before writing any query logic.

Discover Available Fields in Your Log Data

You have your branch ready and you know the team's conventions. Before writing any detection logic, you need to confirm that the fields your rule depends on actually exist in the data source.

Production detection engineers never write rules blind. A rule that queries a field that does not exist in the index silently returns zero results. You would deploy it, wait days, see no alerts, and have no idea whether the threat is absent or the rule is broken.

In this step, get ready to:
  • Examine sample network event logs from the repo's test data.
  • Identify the fields your RDP tunneling rule needs to query.
  • Verify those fields exist and contain expected values.
Understand what your rule needs to query

Your RDP tunneling rule will look for network events that match a specific pattern. Before you can write the query, you need to know whether the log data contains the right fields.

What fields does RDP tunneling detection require?

  • destination.port — RDP traffic targets port 3389. Your rule filters on this value.
  • source.port — legitimate RDP clients use high ephemeral ports (above 1024). A low source port suggests the traffic is being tunneled through another protocol.
  • source.ip — you need to exclude internal RFC 1918 addresses (10.x, 172.16.x, 192.168.x) to focus on external connections.

The question is: does the sample data in this repo actually contain these fields? Let's find out.

Examine sample network events

The repo includes a sample_logs/ directory with representative network events. These are the same event shapes that flow through the team's Elastic Security cluster in production.

  • View the first 20 lines of the sample log data by running this command:
head -20 sample_logs/network_events.json

You should see JSON objects representing individual network events. Each event has nested objects like destination, source, and event.

What is the event structure telling you?

Each JSON object is one network event as it would appear in an Elasticsearch index. The nested structure (destination.port, source.ip) is exactly how you reference these fields in an EQL query. The field paths you see here are the field paths you will write in your TOML rule.

Make sure you are in the security-detection-rules-template directory (run pwd to check). The sample_logs/ directory is at the root of the repo.

help me find the sample log files in my repo

Verify required fields exist

You saw the general structure. Now confirm that the three specific fields your rule needs are present and contain the values you expect.

  • Search for destination and source fields in the sample data by running:
grep -E '"destination"|"source"' sample_logs/network_events.json | head -30

You should see lines containing nested field structures like this:

"destination": { "port": 3389 }
"source": { "port": 445, "ip": "203.0.113.5" }

What are you confirming here?

  • destination.port exists and some events have the value 3389 (RDP traffic is present in the data).
  • source.port exists and includes values below 1024 (the suspicious pattern your rule will detect).
  • source.ip exists and includes external addresses (not just RFC 1918 internal ranges).
Validate field coverage

One last check: confirm there are enough RDP-related events in the sample data that your rule would have something to detect.

  • Count the number of events targeting RDP port 3389 by running:
grep -c "3389" sample_logs/network_events.json

You should see a number greater than zero. This confirms the sample data contains RDP events that your detection rule can match against.

Why does field coverage matter?

If the count was zero, it would mean either the data source does not capture RDP traffic or the field name is different from what you expect. In production, this discovery step prevents you from deploying a rule that silently matches nothing for weeks.

If grep returns 0, double check the filename is exactly network_events.json (not network-events.json with a hyphen). Run ls sample_logs/ to see what files are available.

help me find RDP events in the sample log data

You have verified that the fields your rule needs exist in the data and that RDP events are present. Now you can write your TOML detection rule with confidence, knowing the query will match real events.

Write Your TOML Detection Rule

You have confirmed that the sample data contains the fields your rule needs: destination.port, source.port, and source.ip. Now it is time to write the actual detection rule you are contributing.

The Elastic Security TOML schema has a specific structure that your CI pipeline validates against. Writing the rule teaches you this schema hands-on. You'll create a rule that detects suspicious RDP tunneling, a technique attackers use to hide remote access inside legitimate protocols.

In this step, get ready to:
  • Create a TOML detection rule with metadata and required fields.
  • Add a MITRE ATT&CK threat mapping for Protocol Tunneling.
  • Commit the rule using a conventional commit message.
Create the rule file with metadata and required fields

Every Elastic Security rule starts with two sections. The [metadata] section tracks when the rule was created and its maturity level. The [rule] section contains the detection logic and required fields that the schema validator checks.

  • Create a new file at rules/network/detect_suspicious_rdp_tunneling.toml in VS Code and paste in the metadata and core rule fields:
[metadata]
creation_date = "2025/07/26"
maturity = "development"
updated_date = "2025/07/26"

[rule]
author = ["Security Team"]
description = """
Detects suspicious RDP tunneling activity by identifying RDP connections
that originate from non-standard source ports or exhibit tunneling
characteristics such as connections routed through uncommon protocols.
"""
false_positives = ["Legitimate remote administration tools using RDP over non-standard ports", "VPN concentrators forwarding RDP traffic"]
from = "now-9m"
index = ["logs-endpoint.events.*", "winlogbeat-*"]
language = "eql"
license = "Elastic License v2"
name = "Suspicious RDP Tunneling via Non-Standard Port"
risk_score = 200
rule_id = "f7e8d9c0-b1a2-3456-cdef-890123456789"
severity = "medium"

What does this code do?

  • [metadata] tells the team this rule is in development maturity, so reviewers know it needs extra scrutiny.
  • description uses triple-quoted strings (""") for multi-line text, matching the pattern in the example DNS rule.
  • risk_score = 200 reflects that RDP tunneling is a high-severity threat. Combined with severity = "medium", this gives SOC analysts clear prioritization.
  • language = "eql" tells Elastic to interpret the query as Event Query Language, which is better suited for network event correlation.
  • Below the severity line, add the tags, query, and timestamp override:
tags = [
    "Domain: Network",
    "OS: Windows",
    "Use Case: Threat Detection",
    "Tactic: Command and Control",
    "Data Source: Elastic Defend"
]
type = "eql"
query = '''
network where destination.port == 3389 and source.port < 1024
  and not source.ip : ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")
'''
timestamp_override = "event.ingested"

What does the EQL query detect?

The query looks for network events where traffic hits RDP port 3389, but originates from a privileged source port (below 1024). Legitimate RDP clients use high ephemeral ports. A low source port suggests the traffic is being tunneled through another protocol.

The not source.ip clause excludes internal RFC 1918 addresses, focusing only on external connections that are more likely malicious.

  • Save detect_suspicious_rdp_tunneling.toml.
  • Confirm the file exists by running:
cat rules/network/detect_suspicious_rdp_tunneling.toml

You should see both sections printed to your terminal: the [metadata] block at the top and the [rule] block with your description, query, and tags.

File not found?

  • Make sure you created the file inside rules/network/, not in the repository root.
  • Check the filename is exactly detect_suspicious_rdp_tunneling.toml with underscores, not hyphens.

Help me find where my TOML file ended up in the directory structure.

Add the MITRE ATT&CK threat mapping

Every detection rule should map to the MITRE ATT&CK framework so analysts can see which adversary behaviour it covers. Your rule detects Protocol Tunneling (T1572), a technique under the Command and Control tactic (TA0011).

  • At the bottom of detect_suspicious_rdp_tunneling.toml, below timestamp_override, add the threat mapping:
[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1572"
name = "Protocol Tunneling"
reference = "https://attack.mitre.org/techniques/T1572/"

[rule.threat.tactic]
id = "TA0011"
name = "Command and Control"
reference = "https://attack.mitre.org/tactics/TA0011/"

What is the double-bracket syntax?

In TOML, [[rule.threat]] creates an array of tables. This means a rule can map to multiple techniques and tactics. The single-bracket [rule.threat.tactic] is a nested table within that array entry.

Compare this to the example DNS rule, which uses the same pattern but maps to T1071 (Application Layer Protocol) with a subtechnique. Your rule is simpler because T1572 has no subtechniques.

  • Save the file.
  • Verify the complete rule structure by running:
cat rules/network/detect_suspicious_rdp_tunneling.toml

You should see the full file with the [[rule.threat]] section at the bottom, including technique T1572 and tactic TA0011.

Seeing a TOML parse error?

  • Make sure you have a blank line between timestamp_override and [[rule.threat]].
  • Check that [[rule.threat]] and [[rule.threat.technique]] use double brackets, while [rule.threat.tactic] uses single brackets.

Help me fix TOML syntax errors in my threat mapping.

✔️ Awesome, I've got everything!

Make sure you have saved rules/network/detect_suspicious_rdp_tunneling.toml with all three sections: metadata, rule fields with the EQL query, and the MITRE threat mapping at the bottom.

ⓧ I'd like to double check the full code

Scroll up and confirm your file contains all three chunks in this order:

  • Chunk 1: [metadata] section (creation_date, maturity, updated_date) followed by [rule] fields from author through severity.
  • Chunk 2: tags array, type, query with the EQL logic, and timestamp_override.
  • Chunk 3: [[rule.threat]] with framework, technique T1572, and tactic TA0011.

Confirm your risk_score is set to 200 and your rule_id is f7e8d9c0-b1a2-3456-cdef-890123456789.

Commit the rule with a conventional commit

Your rule is complete. Time to record it in version control with a commit message that tells reviewers exactly what you added.

  • Stage and commit your new rule by running these commands:
git add rules/network/detect_suspicious_rdp_tunneling.toml
git commit -m "feat(rules): add rdp tunneling detection via protocol analysis"

Why this commit message works

  • feat tells reviewers this is a new feature, not a bug fix or cleanup.
  • (rules) scopes the change to the rules directory so the team knows which area is affected.
  • The description says WHAT (rdp tunneling detection) and HOW (protocol analysis) in imperative mood.

Compare this to weak commits like add rule or new detection. Those tell reviewers nothing about what changed or why.

Before you check your commit history, what do you expect git log to show? How many commits ahead of main should your branch be?

  • Verify your commit by running:
git log --oneline -3

You should see your new commit at the top: feat(rules): add rdp tunneling detection via protocol analysis. Your branch is now one commit ahead of main.

Commit failed with nothing to commit?

  • Run git status to check if the file is untracked or already staged.
  • Make sure you ran git add with the full path including rules/network/.

Help me figure out why git won't commit my detection rule file.

Your detection rule is committed and ready for the team. Next up, you'll push your branch to GitHub and see how the CI pipeline validates your contribution.

Push, Fail CI, and Fix

Your detection rule is committed and your branch is ready to share with the team. The next step in any enterprise workflow is pushing your branch and running the team's validation pipeline to confirm your rule meets the schema before opening a PR.

Reading pipeline output and fixing issues independently is the most important skill for contributing to enterprise repos. In this step, you'll push your branch, run validation, and respond to whatever the pipeline tells you.

In this step, get ready to:
  • Push your feature branch to GitHub.
  • Diagnose a CI validation failure from the pipeline output.
  • Fix the schema error and commit the correction.
Push your branch and run validation

Your branch has one commit ahead of main. Time to push it to GitHub so the code exists on the remote.

  • Push your feature branch to GitHub by running this command:
git push -u origin feat/detect-suspicious-rdp-tunneling

You should see output confirming the branch was pushed to your remote repository.

Now let's run the same validation that the CI/CD pipeline executes when a PR is opened. This is the exact script that GitHub Actions runs against your rule files.

Before you run this, do you think your rule will pass validation? Think about whether every field value in your TOML file is within valid ranges.

  • Run the TOML validation script against your rule by running this command:
python3 scripts/validate_toml_rule.py rules/network/detect_suspicious_rdp_tunneling.toml

The script fails. You'll see output like this:

FAILED: rules/network/detect_suspicious_rdp_tunneling.toml
  - risk_score must be between 1 and 100, got: 200

What just happened?

The validation script checked your rule against the Elastic Security schema and found that risk_score = 200 is outside the valid range of 1-100. This is exactly the kind of error that CI catches before a reviewer ever looks at your code.

On GitHub, this shows up as a red X next to your PR. The skill is reading the output, finding the specific error, and fixing it yourself before requesting review.

Fix the risk_score and commit the correction

The validation output told you exactly what's wrong: the risk_score value is out of range. A medium-severity rule like this one typically uses a score of 47.

  • In rules/network/detect_suspicious_rdp_tunneling.toml, find this line in the [rule] section:
risk_score = 200
  • Replace 200 with 47, so the line now looks like this:
risk_score = 47
  • Save rules/network/detect_suspicious_rdp_tunneling.toml.
  • Confirm the fix passes validation by running the script again:
python3 scripts/validate_toml_rule.py rules/network/detect_suspicious_rdp_tunneling.toml

You should see:

PASSED: rules/network/detect_suspicious_rdp_tunneling.toml

Still seeing FAILED?

Make sure you changed the value to exactly 47 (a plain integer, no quotes). Check that you edited the correct file in rules/network/ and saved it.

Help me debug my TOML validation error.

Now stage and commit the fix. The commit message should tell reviewers exactly what changed and why.

  • Stage the corrected file by running:
git add rules/network/detect_suspicious_rdp_tunneling.toml
  • Commit the fix with a conventional commit message by running:
git commit -m "fix(rules): correct risk_score to valid range (1-100)"

Why a separate commit instead of amending?

Keeping the fix as its own commit preserves the story of your contribution. Reviewers can see that you wrote the rule, then caught and fixed a schema error. This is a sign of a careful contributor who responds to CI feedback.

Compare your two messages: feat(rules): add rdp tunneling detection via protocol analysis introduces the rule, and fix(rules): correct risk_score to valid range (1-100) tells reviewers exactly what was corrected. Each commit is a clear chapter.

Push the fix and verify CI passes
  • Push the updated branch to GitHub by running:
git push

Your branch now has both commits on the remote. When you open a PR in the next step, the CI workflow will run against this corrected version and pass.

Before you check your git log, how many commits do you expect to see ahead of main?

  • Verify your commit history by running:
git log --oneline -2

You should see two commits: your original feat commit and the fix commit.

abc1234 fix(rules): correct risk_score to valid range (1-100)
def5678 feat(rules): add rdp tunneling detection via protocol analysis

What makes these commit messages strong?

Each message has a type (feat or fix), a scope (rules), and a description that says what changed. Compare this to weak messages like fix issues or update rule that force reviewers to open the diff just to understand what happened.

If you only see one commit, you may have amended the original instead of creating a new one. Check that you used git commit -m without the --amend flag.

Help me fix my git commit history.

You just experienced the most common loop in enterprise contributing: push, fail, read, fix, push again. Your branch is clean, validated, and ready for a pull request.

Open the PR and Complete the Security Checklist

Your branch is pushed, CI passes, and your commit history tells a clear story. Now it is time to open the pull request where your contribution actually gets reviewed.

The PR is the single artifact a reviewer reads before deciding whether to merge or request changes. A properly filled Context section and completed security checklist are what separate PRs that get merged in hours from ones that sit in review for days.

In this step, get ready to:
  • Open a pull request on GitHub with the auto-filled PR template.
  • Fill in the Context section with detection rationale and data source requirements.
  • Complete the security checklist and observe the CODEOWNERS review request.
Open a pull request targeting main

Your branch is two commits ahead of main. GitHub shows a prompt to open a PR when a branch is recently pushed.

  • Switch back to your repository page on GitHub.
  • You should see a yellow banner that says your branch had recent pushes. Click Compare & pull request.
  • If the banner is not visible, click the Pull requests tab, then click New pull request.
  • Confirm the base branch is main and the compare branch is feat/detect-suspicious-rdp-tunneling.

The PR body auto-fills with the template from .github/pull_request_template.md. You should see the Context section, Security Checklist, and Commit Quality Check sections appear automatically.

Why does the template auto-fill?

GitHub checks for a file at .github/pull_request_template.md when you open a new PR. If it finds one, it pre-fills the PR description body with the template contents. This ensures every contributor answers the same questions without having to remember the format.

Now fill in each field in the Context section. Replace the blank areas below each bold question with the answers for your RDP tunneling rule.

  • Under What does this change do?, write: Adds an EQL detection rule that identifies RDP connections originating from non-standard source ports or external IPs, which may indicate protocol tunneling for command-and-control.
  • Under Why is this change needed?, write: RDP tunneling (MITRE T1572) is increasingly used by threat actors to hide C2 traffic inside legitimate-looking RDP sessions. Our current ruleset has no coverage for this technique.
  • Under Expected false positive rate:, write: Low-medium. Legitimate remote admin tools using non-standard ports and VPN concentrators may trigger. Both are documented in the rule's false_positives field.
  • Under Required data sources:, write: logs-endpoint.events.* (Elastic Defend) and winlogbeat-* (Windows event logs with network data).

Why does the Context section matter?

Reviewers on a security team evaluate dozens of rule PRs per week. A clear Context section lets them assess the detection logic, threat relevance, and false positive risk without reading the TOML file first. It is the difference between a 5-minute review and a 2-day back-and-forth.

Complete the security checklist

Scroll down to the Security Checklist section in the PR body. Each checkbox item represents a quality gate that your rule must pass before merge.

  • Check No sensitive data (API keys, internal IPs, employee names) in rule logic. Your rule references only port numbers and RFC 1918 ranges.
  • Check MITRE ATT&CK mapping is accurate and specific (not overly broad). You mapped to T1572 (Protocol Tunneling), which is the exact technique, not the broader tactic.
  • Check False positives are documented in the rule's false_positives field or PR description. Your TOML file includes two documented false positive sources.
  • Check Rule has been tested against representative sample data. You validated the rule passes schema checks via CI.
  • Check No expected performance regression (query is scoped to specific indices). Your rule targets only logs-endpoint.events.* and winlogbeat-*.
  • Check Rule name follows team naming convention. Your file is detect_suspicious_rdp_tunneling.toml in the rules/network/ directory.
  • Click Create pull request to submit it.

Check boxes in Markdown

To check a checkbox in the PR body editor, change - [ ] to - [x]. After you submit the PR, you can also click the checkboxes directly in the rendered view.

Observe the CODEOWNERS review request and commit quality

Before you look at the PR sidebar, think about this: your rule file lives inside rules/. Based on what you read in the CODEOWNERS file earlier, who do you expect GitHub to request a review from?

  • Look at the Reviewers section in the right sidebar of your PR.
  • You should see @security-team automatically requested as a reviewer. This happened because CODEOWNERS assigns /rules/ @security-team and your file is in the rules/ directory.

What is CODEOWNERS doing here?

The CODEOWNERS file maps file paths to responsible teams. When a PR modifies files matching a pattern, GitHub automatically requests a review from the designated owners. This ensures no detection rule merges without security team sign-off.

Now scroll down to the Commits tab on your PR page. You should see your two commits listed in order.

  • Click the Commits tab on the PR.
  • Confirm you see: feat(rules): add rdp tunneling detection via protocol analysis followed by fix(rules): correct risk_score to valid range (1-100).

Compare these to the bad examples from the Commit Quality Check section in your template: fix issues, test, update rule. Your commits tell a reviewer exactly what changed and why without opening a single file.

Why do commit messages matter for code review?

A reviewer seeing fix(rules): correct risk_score to valid range (1-100) instantly knows what happened, why, and that CI caught a real schema error that was resolved. A reviewer seeing fix issues has to open the diff, read the TOML, check what changed, and guess the intent.

Strong conventional commits reduce review time because each message is a self-contained explanation. Reviewers can triage PRs by scanning commit titles alone.

  • Switch back to the Conversation tab on your PR.
  • Confirm the CI checks section at the bottom shows all checks passing (green checkmark).

Don't see the review request or CI checks?

If @security-team does not appear as a reviewer, confirm your CODEOWNERS file is in the .github/ directory and uses the pattern /rules/ with a space before the team name. CODEOWNERS review requests also require branch protection rules to be enforced on solo repos.

If CI checks are not appearing, verify your workflow file is at .github/workflows/validate-rules.yml and that the PR modifies files under rules/ (the path trigger in the workflow).

Still stuck? Help me debug why my CODEOWNERS review request or CI workflow isn't triggering on my GitHub pull request.

Secret mission

Write an ElastAlert YAML Rule

Your TOML rule catches individual suspicious RDP connections. But what about high-volume patterns that slip past event-level analysis? Write a second rule in ElastAlert YAML format that uses frequency-based detection to surface tunneling activity, commit it with a multi-line body explaining your rationale, and get both rules passing CI in a single PR.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally with a free GitHub repository, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • GitHub repository (your private repository) containing your detection rules, CI workflow, and PR.
  • Local project folder on your Desktop with all rule files and scripts.

Keep everything running

No action needed. Choose this if you are still actively building or want to reference your detection rules and Claude Code commands later.

  • Your GitHub repository stays online with your PR, commit history, and CI configuration intact.
  • Your local project folder remains on your Desktop with all rule files and scripts available.

Pause - I'll come back to this later

Shut down any running processes to free up memory. Your files and repository remain untouched for when you return.

  • Close VS Code if you have the project open.
  • Close any terminal sessions that are still in the project directory.
  • Your GitHub repository and local files stay exactly where they are.

Delete - I don't want to use this again

Remove all project resources and start fresh. This deletes your local detection rules, commit history, and GitHub repository permanently.

  • Delete your local project folder by running this command in your terminal:

macOS

cd ~/Desktop
rm -rf security-detection-rules-template

Windows

cd ~\Desktop
rmdir /s /q security-detection-rules-template
  • Delete your GitHub repository:
  • Navigate to your repository's main page on GitHub.
  • Click Settings (under the repository name).
  • Scroll down to the Danger Zone section.
  • Click Delete this repository.
  • Click I want to delete this repository, then click I have read and understand these effects.
  • Type your repository name to confirm, then click Delete this repository.

Nice Work!

Nice Work!

You just contributed a detection rule to an enterprise security repo using the exact workflow professional detection engineering teams follow every day.

You've learned how to:

  • Navigate existing enterprise governance (CODEOWNERS, PR templates, CI validation) and write a TOML detection rule for Elastic Security with proper MITRE ATT&CK mapping.
  • Diagnose and fix a CI validation failure by reading pipeline output, identifying the schema error, and committing a fix with a conventional commit that tells reviewers exactly what changed.
  • Set up Claude Code custom commands (/branch, /commit) that enforce your team's kebab-case branching and conventional commit conventions automatically.
  • Secret Mission: Wrote an ElastAlert YAML rule with frequency-based detection and a multi-line commit body explaining the detection rationale.

Ready to quiz yourself?