Skip to content

Releases: WRG-11/wrg-sigma-rules

v1.9.0

Choose a tag to compare

@WRG-11 WRG-11 released this 10 Sep 19:23
884b83e

Corpus 294 to 296 rules with two source-honest canonical templates (T1498 Network Denial of Service, T1056.001 Keylogging), each with matching and non-matching sidecar evidence.

  • Sidecars completed for every status:test rule; CI now requires a .sample.json for each.
  • Zero-debt linter gate: broad empty matches, unsafe regex, underspecified logsource, scaffolding, deprecated pipe conditions and placeholder false positives are now blocking.
  • Cursor and any-MCP-client setup documented next to the Claude Code and Codex plugins.
  • README rewritten for a tighter, professional structure.

See CHANGELOG.md for full detail.

v1.8.0 — corpus 278 → 294, sample-match gate

Choose a tag to compare

@WRG-11 WRG-11 released this 05 Sep 18:24
b580e8c

Corpus 278 → 294 rules, seven rule-logic fixes, and a new sample-match gate
wired into CI.

Added

  • Corpus 278 → 294 rules (16 new). Two independent sources, measured
    separately rather than merged blindly:
    • 11 from the sigma_rule_farmer queue. The queue held 36 candidates
      and 25 of them were already in the corpus — the farmer's own INDEX.json
      claimed shipped: 0 and was stale, confirmed by a near-identical diff on
      barracuda_t1567. Only the 11 genuinely new ones were taken and the 25
      overlapping files were left untouched. Actors: Blackwater, Crpxo,
      ShinyHunters, TeamPCP/UNC6780, Unknown-supply-chain-2025-03.
    • 5 from the internal mirror, selected on the same criterion this
      changelog used for [1.7.0]: a rule ships when it brings a new actor or
      a new technique
      , not merely a new actor×technique pair. Of 69 mirror-only
      rules, exactly 5 met it, carrying five techniques the corpus did not have —
      T1021.002, T1036.005, T1199, T1556, T1588. The other 64 map onto actors and
      techniques already covered and stay in the queue, which is the same call
      [1.7.0] made about its own 80 repeats.
  • scripts/sample_match_gate.py — checks that a rule's .sample.json
    sidecar actually matches the rule's detection logic, with gt/lt modifier
    and N of <prefix>* support. Wired into .github/workflows/tests.yml, so it
    runs on every PR instead of waiting to be invoked by hand.
  • scripts/detection_note_gap.py — reports which rules no detection note
    mentions.
  • 162 .sample.json sidecars and 66 detection notes. Every one of the 223
    actor-, CVE- and campaign-bound observed_* rules is now named in at least
    one note.

Fixed

  • Seven real rule-logic defects, each proven by mutation test: the fix was
    reverted, the test went red, the fix was restored.
  • 29 rules with CVSS or status inconsistencies, corrected against live
    gh api queries rather than against memory.

Note on the counts

Every number here was re-measured from disk, not incremented: INDEX.json via
migrate_sigma_corpus.py --regenerate-index, README/DEMO/plugin.json via
readme_stamp.py, and DEMO's quoted ## Summary read back off
coverage_resource.coverage_matrix_body().

Corpus 253 → 278: ten new threat actors

Choose a tag to compare

@WRG-11 WRG-11 released this 23 Aug 16:21

Corpus 253 → 278 rules: ten new threat actors and one new technique.

The generation queue held 105 candidate rules. Measured on two axes against the
live corpus, exactly 25 brought something new — ten actors the corpus did not
carry, and one technique (T1567.002). The other 80 mapped onto actors and
techniques already covered, so they stay in the queue.

New actors

barracuda, emperador, genesis, global_secret_group,
iran_nexus_tortoiseshell, kairos, ms13_089, panzer, securotrop,
shai_hulud_npm_worm.

Verified

  • Full test suite: 794 passed
  • pySigma validation: 25/25
  • Corpus quality linter: 25/25 (≤2 warnings each)
  • INDEX / README / DEMO counts regenerated from disk
  • OPSEC: no victim-level personal data, no real addresses, English only

Also includes the MCP server version-announce fix and Docker startup fix from
the unreleased window.

v1.6.0: corpus 222 to 253, correlation schema migration

Choose a tag to compare

@WRG-11 WRG-11 released this 19 Aug 21:07

31 observed actor rules, each bound to an actor with at least one recorded incident. Test tier 8 -> 39 rules (3.6% -> 15.4%); stable remains 0.

Eight rules moved from deprecated pipe-aggregation to real Sigma correlation documents, so the correlation set is now 18 and Lucene-convertible 235.

Full notes: CHANGELOG.md [1.6.0].

v1.5.0: corpus 100 to 222, AI-runtime + agent-tooling CVE wave

Choose a tag to compare

@WRG-11 WRG-11 released this 12 Aug 00:26

Corpus 100 -> 222 rules across Open WebUI, vLLM, SGLang, Flowise (the corpus's first agentflow/low-code-LLM-builder theme), NLTK, Bedrock AgentCore, NVIDIA Triton, stable-diffusion.cpp, MaxKB, RAGFlow, Gradio, mem0, Ollama, GitLab MCP and the Miasma npm worm cluster. Multi-backend conversion re-measured against the full 222-rule corpus: Splunk and OpenSearch-PPL convert all 222, the Lucene-family targets convert 212 -- the 10 correlation rules are a backend capability gap, not a rule defect. DEMO.md, README.md and plugin.json are all under readme_stamp's reach now, so published counts re-measure instead of drifting. Full entry, including rejected candidates, in CHANGELOG.md.

v1.4.0: corpus 80 to 100, AI-fingerprint detectors, mcp 2.x support

Choose a tag to compare

@WRG-11 WRG-11 released this 05 Aug 01:27

v1.4.0: corpus 80 to 100, AI-fingerprint detectors, mcp 2.x support

Twenty new rules, a class of detector the corpus did not have before, and an
SDK break fixed properly rather than pinned around. The plugin version has read
1.4.0 since this work landed; this tag makes the published release match it.

Added

  • Corpus 80 → 100 rules, in three groups.

    Four AI-fingerprint detectors on the code_review logsource — ai_prose,
    unicode_watermark, ai_provenance, hallucinated_import — taking that
    logsource from 5 rules to 9. They sit outside the ATT&CK tactic taxonomy on
    purpose: they match authorship artifacts in source, not adversary technique,
    which is why code_review is a rule directory but never counted as a tactic.

    Six observed campaigns, each bound to a named, dated incident:

    • UNC1069/WAVESHAPER axios npm compromise
    • TanStack Pwn Request Actions cache poisoning
    • SharePoint CVE-2026-58644 w3wp shell spawn
    • N-able N-central CVE-2026-18577 cloudflared persistence
    • Storm-2949 Azure management-plane credential harvest
    • keyv/cacheable npm worm ETH C2

    Ten canonical templates: T1082, T1083, T1189, T1195.002, T1204, T1485,
    T1546, T1552.004, T1567.001, T1574.

    Nothing was invented to reach a round number. The corpus stopped at 94 when
    cross-verifiable material ran out, and three further candidates were rejected
    for single-source or self-contradictory IOCs. T1071.001 was checked and
    skipped — its frequency in the source corpus is 0.

  • OpenSearch as a fifth conversion target. Lucene and PPL are separate
    targets, and a test asserts they do not silently resolve to the same one.

  • Processing pipelines are applied by convert_rule rather than ignored. An
    unknown pipeline name is an error; a missing pipeline package names itself in
    the returned envelope instead of raising a bare traceback.

  • One new rule for each of the four thinnest ATT&CK tactics, the two correlation
    types the templates had not yet used, and a previously-withheld
    privilege-escalation rule.

  • discovery becomes the thirteenth ATT&CK tactic covered by the corpus, up
    from twelve. The heaviest growth was elsewhere: initial_access 17 → 21 and
    command_and_control 3 → 6.

  • Coverage measurement is gated on again, and the Docker image is built and
    smoke-tested by speaking MCP to the container over stdio rather than assuming
    it starts.

Fixed

  • write_rule_yaml date-regression guard. Found via a real near-miss:
    running the corpus migration overwrote an already-deployed, fresher rule
    with content re-rendered from a stale test fixture, silently backdating it.
    The guard refuses any write that would backdate a deployed rule. A blocked
    write means the source needs refreshing, not that the guard should be
    bypassed.

  • mcp 2.0.0 support, properly this time. 1.3.0 responded to the SDK 2.0
    break by pinning mcp<2. server.py now imports mcp.server.MCPServer on
    2.x and falls back to mcp.server.fastmcp.FastMCP on 1.x, so both majors
    work and CI runs the suite against both instead of excluding one.

  • The Docker image shipped without the rule corpus baked in, so both MCP
    resources that read resources/examples/ answered ok: false inside the
    container even though the same server worked outside it.

  • Placeholder falsepositives: entries (REPLACE_ME and similar) are gone
    from the corpus, and validate_rule flags any that reappear.

Verification

  • pytest -q in CI across six matrix jobs (ubuntu / macos / windows × mcp<2
    and mcp>=2): 419 passed in every one (332 passed, 7 skipped at v1.3.0)
  • Coverage 92% against the 85% floor in .coveragerc
  • readme_stamp.py --check in sync: sigma_rule_count=100,
    test_module_count=16
  • Rule status: breakdown: 8 test, 92 experimental, 0 stable — read
    rule status before
    deploying any of these
  • CodeQL (actions and python), bandit, ruff, dependency-licenses and the Docker
    stdio smoke test all clean

Note

The [1.3.0] changelog section understated its own release: it claimed a
corpus of 76 while the tag it names ships 80. The section written for this
release then continued that 76 instead of measuring, so the two agreed with
each other and disagreed with the repository — which is why both survived
review. Both are corrected here, and CHANGELOG.md now carries the guard
README.md already had: a test asserts the newest section's rule count against
the actual corpus, and that each section starts where the previous one ended.
The GitHub release title for v1.3.0 was correct throughout; only the changelog
was wrong.

v1.3.0: corpus 73 to 80, OpenSearch backend, pipeline support

Choose a tag to compare

@WRG-11 WRG-11 released this 29 Jul 14:18

v1.3.0: corpus 73 to 80, OpenSearch backend, pipeline support

Seven new rule templates, a fifth conversion backend, a validator that catches
templates nobody filled in, and an upper bound on every dependency. The plugin
version has read 1.3.0 since this work started; this tag makes the published
release match it.

Added

  • Seven rule templates, taking the corpus from 73 to 80 across MITRE
    ATT&CK:
    • T1114.001 local email collection (OST/PST access)
    • T1110.003 password spraying by distinct-account count
    • T1486+T1490 ransomware chain, temporal correlation
    • T1021.006 remote services over WinRM
    • T1053.005 scheduled task created from a scripting host
    • T1098.003 AWS IAM wildcard-admin policy created
    • T1548.002 UAC bypass via an auto-elevating binary
  • OpenSearch as a conversion target. pysigma-backend-opensearch joins
    Splunk, Elastic, Kibana and Wazuh. Lucene and PPL are separate targets, and
    a test asserts they do not quietly resolve to the same one.
  • Sysmon and Windows pipelines. convert_rule takes a pipeline name or a
    list of them. An unknown pipeline is an error rather than a silent fallback,
    and a missing pipeline package returns an envelope naming the package to
    install instead of a bare traceback.
  • The validator now catches an unfilled template. A rule still carrying a
    REPLACE_ME marker, or a placeholder entry in falsepositives, is a rule
    that looks complete and detects nothing. validate_rule reports both.

Changed

  • Every dependency has an upper bound now. mcp was unpinned when
    mcp 2.0.0 shipped on 2026-07-28 and moved mcp.server.fastmcp. The next
    scheduled CI run on main would have failed on its own, with or without
    this release. It is pinned to <2; pysigma and the backends are bounded
    the same way.
  • The three skills were updated for the new targets and the pipeline options.

Verification

  • pytest -q on Python 3.12: 332 passed, 7 skipped (302 at v1.2.1)
  • readme_stamp.py --check in sync: sigma_rule_count=80,
    test_module_count=11
  • CodeQL (actions and python) clean, and the OPSEC identifier scan clean

Note

Two things were held back on purpose. The coverage-matrix MCP resource is
written and tested, but its module would ship without the server.py
registration that exposes it, so no client could reach the resource; it waits
for that wiring rather than landing as something the release notes could claim
and users could not use. A mirror-integrity test was also excluded: it asserts
a layout constraint that is false in this repository, where server.py
genuinely exists.

v1.2.1 - internal corpus name removed from published content

Choose a tag to compare

@WRG-11 WRG-11 released this 23 Jul 10:04

v1.2.1 — internal corpus name removed from published content

Wording only. No detection rule, tool logic or schema changed; the corpus stays at 73 rules and the test suite passes unchanged. Eleven places in content published from this repository referred to an internal corpus that is not part of it.

Changed

  • Three claimed provenance from a private asset a reader cannot inspect or verify — the plugin marketplace description, the canonical-patterns index, and the description of the wrg-sigma://patterns/canonical-5 MCP resource (wired into the server in v1.2.0, so that string genuinely reaches MCP clients). They now describe what is actually published: a 73-rule corpus across 12 MITRE ATT&CK tactics.
  • The other eight used the internal name where they meant this corpus — the 73 published rules — in a validation message users see, a skill instruction, two docstrings, a code comment, a test docstring and the pattern index. They now say "this corpus" or "the published corpus": accurate, and unambiguous to a reader outside the project.
  • migrate_sigma_corpus.py's truncation warning pointed readers at a full source that is not published. It now states that plainly instead.

Verification

  • Jargon scan against this checkout: 11 → 0 findings
  • pytest -q302 passed on Python 3.12 (no test asserted any of these strings)
  • readme_stamp.py --check → in sync (sigma_rule_count=73, test_module_count=11)

Note

The changelog entry for this release deliberately does not quote the removed term. Writing it out first put the scan count back to 4 — documenting the removal reintroduced exactly what the release removes.

v1.2.0 - corpus 68 to 73 + correlation-rule migration

Choose a tag to compare

@WRG-11 WRG-11 released this 23 Jul 08:43

v1.2.0 — corpus 68 → 73 + correlation-rule migration

The published corpus grew from 68 to 73 rule files (net +5), the 8 rules still using the deprecated pipe-aggregation condition were migrated to real Sigma correlation-rule syntax, and the MCP server is now wired into the plugin. (Per repo convention a release here is a GitHub tag marking a public-corpus milestone — there is no PyPI artifact, and the detection logic is already live on main.)

Added

  • Photo ZIP campaign Node.js Run-key persistence rule (68 → 69) — real-incident-grounded registry Run-key detection (#30)
  • Four rules via corpus sync (69 → 73): Jellyfin CVE-2026-35033 FFmpeg argument-injection LFI (real observed), plus three MCP database-server SQL-abuse templates — local-file-read, SSRF-to-metadata, read-only write-bypass (#32)
  • MCP server wired into the plugin (.mcp.json) with naming / version / rule-count consistency and a pytest CI gate (#26)
  • Sigma correlation-rule support in convert_rule / validate_rule via SigmaCollection — base-rule + correlation-rule two-document pairs now parse and convert; single-document rules unchanged (#44)
  • deprecated_pipe_condition linter in validate_rule — flags condition: X | count() by Y > N in Zm, which is schema-valid but rejected by every pySigma backend at convert time (#44)
  • --regenerate-index in scripts/migrate_sigma_corpus.py — rebuilds INDEX.json from the rule files on disk, with a regenerate-vs-committed snapshot test so index drift cannot silently re-accumulate (#42)
  • test_module_count as a second self-stamped README metric alongside sigma_rule_count
  • .gitignore — this repository previously had none

Changed

  • Relabeled three synthetic rules observed_template_ (plus the wrg.template tag): these are internal-adversarial-derived scenarios, not real-world-observed incidents, so the prior labelling over-claimed
  • Migrated the 8 remaining pipe-aggregation rules to correlation-rule syntax — a base document plus an event_count correlation document, with id / title / references / tags preserved so id-based consumers do not break. Splunk convert verified per rule; Elastic correctly reports it does not support correlation rules (#44)
  • README — added the persistence tactic (12th ATT&CK category) and corrected the title to "Claude Code Plugin" (a third-party plugin, not an Anthropic product) (#31)
  • convert_rule — a non-empty config argument was accepted and echoed in config_used but never applied to backend construction. It now raises a warning instead of silently pretending the config took effect
  • draft_rule now emits YAML via yaml.safe_dump instead of a hand-rolled emitter (#42)

Fixed

  • INDEX.json drift — regenerated from disk (total_rules 68 → 73). The persistence tactic was completely unindexed, 3 stale observed_template_ renames were still listed under their old names, and 5 new rules were missing. The three stale rule-count siblings (plugin.json, resources/canonical-patterns/INDEX.md, DEMO.md) were corrected in the same pass (#42)
  • Documented-but-unreachable MCP resourceregister_canonical_pattern_resources() was fully implemented and covered by 12 tests, but server.py never called it, so the resource URI that canonical-patterns/INDEX.md documented as a working feature could not be reached by any real MCP client. Now wired in, with a test that imports the real server module and asserts registration (#42)
  • draft_rule — the hand-rolled YAML emitter only quoted problem characters in top-level scalars, so a references entry containing : silently re-parsed as a one-key mapping instead of a string (#42)
  • draft_rule — 80-char title truncation cut mid-word with no ellipsis when the description had no period (silent data loss) (#44)
  • validate_rule — a non-string id field skipped the schema check entirely; now flagged with a distinct error (#44)
  • validate_rule — a multi-document YAML file no longer forces valid=False when the first document is otherwise clean (fixed on the separate _pysigma_validate path too) (#42)
  • validate_rule — UUID regex now accepts v6/v7/v8 (RFC 9562) and the nil UUID (#42)
  • Rule references corrected across six observed_* rules — real sources and accurate MITRE ATT&CK attribution replacing prior placeholders (#33)
  • Doc drift — README claimed "8 Python test modules" against an actual 10; that metric is now self-stamped and has since auto-tracked to 11 on its own

Security

  • validate_rule YAML denial-of-service. A byte-size cap alone does not stop an alias bomb (billion-laughs): PyYAML resolves aliases to shared object references, so parsing stays fast at any nesting depth and the exponential blowup instead hits downstream code that walks the parsed graph without reference-awareness. Anchor/alias syntax is now rejected outright via a PyYAML composer event hook (not a regex) — Sigma rules have no legitimate use for &anchor / *alias. RecursionError is handled for deep but alias-free nesting, and the byte-size cap is kept as a separate guard against plain oversized input (#42)
  • Internal wave-dispatch identifiers and fleet-topology metadata removed from public content, with a regression test blocking them from reaching the public surface (#37, #38, #43)

Known limitations

  • coverage run -m pytest produces false failures on this repository — 94 of 287 when the effect was characterised on Python 3.12, matching CI. Root cause is upstream: pysigma's SigmaYAMLLoader(yaml.CSafeLoader), a C-extension YAML loader subclass in the dependency rather than in this repo's code, breaks specifically under coverage.py's tracer (reproduces with core=ctrace forced; does not reproduce under a bare no-op sys.settrace; branch=True additionally hangs). CI therefore stays on plain pytest deliberately — wiring in coverage run as-is would make CI red for reasons unrelated to code quality.

Maintenance / CI

  • Dependency bumps: actions/checkout 6.0.3 → 7.0.1 (#28, #46), actions/setup-python 6.2.0 → 7.0.0 (#27, #49), github/codeql-action 4.36.2 → 4.37.3 (#39, #40, #41, #47, #48, #50), pysigma ≥ 1.3.3 → ≥ 1.4.0 (#34), mcp ≥ 1.2.0 → ≥ 1.28.1 (#29, #36), plus a pysigma-backend-elasticsearch bump (#35)
  • DEMO.md no longer carries a hard-coded suite pass count — the hand-corrected 286 → 287 fix from this same cycle had already rotted, so the line points at the CI workflow instead. The count cannot be self-stamped the way the rule and module counts are: deriving it requires invoking pytest, and readme_stamp.py is deliberately stdlib-only.

v1.1.1 - metadata / hygiene / CI patch

Choose a tag to compare

@WRG-11 WRG-11 released this 18 Jun 00:14

v1.1.1 — metadata / hygiene / CI patch

Corpus rule count unchanged at 68 — no detection rules added or removed. This is a maintenance, repository-hygiene, and CI/dependency patch; the detection logic was already live on main. (Per repo convention a release here is a GitHub tag marking a public-corpus milestone — there is no PyPI artifact.)

Changed

  • wrg.observed tag added to 4 observed_* rules (token type-confusion, audit-log-gap, scanner-crash defense-evasion, GitHub Actions base64 payload) — tag/metadata only, detection logic unchanged (#12)
  • Rule-description + INDEX context refresh on 7 observed_* rules — no detection: / logsource: changes (#22)
  • README refresh — Status section, Quick example, ecosystem links, dead-link cleanup, DEMO repro-path fix

Added

  • SECURITY.md — private vulnerability disclosure via GitHub Security Advisories (#19)
  • dependabot.yml — dependency monitoring (GitHub Actions + pip)

Removed

  • Internal-only draft and self-audit documents removed from the public corpus; marketplace status corrected (#21)
  • Removed residual internal-tooling references from corpus files (cleanup)

Maintenance / CI

  • Dependency bumps: actions/checkout 4.3.1 → 6.0.3 (#13), github/codeql-action 3 → 4.36.2 (#14, #25), PyYAML ≥ 6.0.3 (#15), pysigma ≥ 1.3.3 plus elasticsearch / splunk backends (#16#18)
  • Pinned codeql-action and checkout refs to commit SHA (#20)