Ask a general AI model to write a YARA-L rule and you will usually get something that looks right and fails in the Rules Editor.
These five mistakes show up again and again:
- metadata.event_type = "LOGIN". That value doesn't exist. The enum is USER_LOGIN.
- target.port = "443". Port is an integer. Quoting it fails type checking.
- Two event variables with no shared placeholder in match. The rule either won't compile or joins everything to everything.
- Raw event fields in outcome inside a rule that has a match section. Every one needs an aggregate like count() or array_distinct().
- Invented UDM fields. The model guesses a field name that sounds plausible and isn't in your parser's output.
None of these are hard problems. They're the kind of thing a senior detection engineer catches in review. The model just doesn't know the rules of the language well enough, and it has no way to check its own work.
So I wrote those rules down and packaged them as an Agent Skill.
Repo: https://github.com/ajaynyayapathi/yara-l-skill
What an Agent Skill is
A skill is a folder with a SKILL.md file in it. The file holds instructions the agent loads only when a task matches its description. You can add reference docs, examples, and scripts alongside it.
Anthropic released the format as an open standard in December 2025. The spec lives at agentskills.io, and Claude Code, GitHub Copilot, OpenAI Codex, Gemini CLI, and Cursor all read it. Write the skill once and it works across all of them.
What this skill does
Give your agent a threat description, an ATT&CK technique, a Sigma rule, or an SPL/KQL query. It follows an 8 step workflow and returns six things:
- The detection logic in one sentence
- A complete YARA-L 2.0 rule with full meta, outcome, and $risk_score
- A list of UDM fields you need to verify against your own parser
- The reference lists and data tables to create, with sample rows
- One test case that should fire and one that should not
- The two most likely false positive sources and how to tune them out
Item 3 matters most. Parsers differ in which UDM fields they fill and which values they write. A failed login might land as BLOCK in one feed and FAIL in another. The skill marks every uncertain field with a // VERIFY: comment instead of guessing, so you know exactly what to check before you deploy.
A real example: device code phishing
In February 2025 Microsoft reported that Storm-2372 had been running device code phishing campaigns against governments, NGOs, and defense, energy, and telecom organizations. The attacker sends a fake Teams meeting invite. The victim enters a code on the real Microsoft login page. The attacker walks away with valid tokens, and MFA never had a chance to stop it.
Here's the rule the skill produces for "alert on device code sign ins from users outside an approved list":
yara
rule entra_device_code_signin_unapproved_user {
meta:
author = "community"
description = "Successful OAuth device code sign in by an account not approved for device code flow."
severity = "HIGH"
priority = "HIGH"
mitre_attack_tactic = "Credential Access"
mitre_attack_technique = "T1528"
reference = "https://attack.mitre.org/techniques/T1528/"
false_positives = "Azure CLI and PowerShell logins with --use-device-code, conference room and IoT devices."
rule_version = "1.0"
yara_version = "YL2.0"
events:
$e.metadata.event_type = "USER_LOGIN"
// VERIFY: confirm log_type for your Entra ID sign in feed.
$e.metadata.log_type = "AZURE_AD"
// VERIFY: the field holding authenticationProtocol differs by parser version.
$e.additional.fields["authenticationProtocol"] = "deviceCode" nocase
$e.security_result.action = "ALLOW"
$e.target.user.userid = $user
not $e.target.user.userid in %device_code_approved_users.userid
match:
$user over 1h
outcome:
$risk_score = 75
$risk_entity_to_score = $user
$signin_count = count($e.metadata.id)
$source_ips = array_distinct($e.principal.ip)
$applications = array_distinct($e.target.application)
$countries = array_distinct($e.principal.location.country_or_region)
condition:
$e
}
Three choices in this rule are worth pointing out:
- The exclusion lives in a data table. Conference rooms and CLI users change every month. Your SOC updates a CSV and never touches the rule.
- match: $user over 1h groups repeat sign ins. One user who signs in five times produces one alert with $signin_count = 5, not five alerts.
- Every outcome field is aggregated. That's required once a match section exists, and it's the mistake models make most often.
The repo ships four example rules, one for each shape you'll write most: single event (certutil downloading a payload), sequence (brute force followed by success), absence (EDR sensor stopped and never came back), and data table lookup (the rule above).
The linter
Instructions only get you so far. The agent still needs a way to check its work before you see it.
The skill includes lint_rule.py, a Python script with no dependencies that catches structural mistakes offline:
$ python3 scripts/lint_rule.py bad_rule.yaral
[FAIL] bad_rule.yaral
error: sections out of order: ['events', 'meta', 'outcome', 'condition']; expected order ['meta', 'events', 'match', 'outcome', 'condition', 'options']
error: unknown metadata.event_type value "LOGIN"
error: integer field ending '.port' compared to a quoted string
error: multiple event variables but no match section
error: absence condition (!$var) requires a match section
warning: no $risk_score in outcome
The skill tells the agent to lint every rule, fix every error, and pass the warnings along to you. When the agent can run code, you get a rule that has already been through one review pass.
A clean lint result doesn't mean the rule compiles. Only the SecOps compiler can tell you that. The linter catches the cheap mistakes so your time goes to the logic.
Install it
Copy one folder.
bash
git clone https://github.com/ajaynyayapathi/yara-l-skill.git
cp -r yara-l-skill/skills/yara-l-authoring ~/.claude/skills/
Agent Personal Project
Claude Code ~/.claude/skills/ .claude/skills/
GitHub Copilot/VS Code ~/.copilot/skills/ .github/skills/
Codex and other clients ~/.agents/skills/ .agents/skills/
For Claude.ai, zip the yara-l-authoring folder and upload it as a custom skill.
Then try a prompt like this:
Write a YARA-L rule for 10 failed logins followed by a success within 30 minutes.
How I'd roll out any rule it writes
- Save the rule with alerting off.
- Run a retrohunt over the last 7 to 30 days.
- If a medium severity rule produces more than 20 detections a day, tune it before you go further.
- Have an analyst review 10 sample detections.
- Turn on alerting.
The skill includes this checklist, so the agent says the same thing every time. AI-written detection content needs the same review gate as human-written content. The skill makes that review faster. It doesn't replace it.
Contribute
The most useful contributions right now:
UDM field corrections from parsers you run in production
New example rules, each with a should fire and a should not fire case
New linter checks for mistakes you've seen in review
Open a PR on the repo. If you run it against your own tenant and a // VERIFY: field turns out wrong for your parser, I want to hear about it.
YARA-L, Google Security Operations, and Chronicle are trademarks of Google LLC. This project is not affiliated with or endorsed by Google.
Top comments (0)