Skip to content

YARA schema reference

Modules: zettelforge.yara, zettelforge.yara.parser, zettelforge.yara.entities, zettelforge.yara.tags, zettelforge.yara.ingest

ZettelForge 2.7.0 — Apache-2.0 license

from zettelforge.yara import (
    YaraRule, YaraParseError,
    parse_yara, parse_file,
    rule_to_entities, resolve_yara_tag,
    ingest_rule, ingest_rules_dir,
)

What this reference covers

The YARA subsystem has three layers:

Layer Module Responsibility
Parser yara.parser Wrap plyara, normalize output to a stable dict shape
Entity yara.entities / yara.tags Map a parsed rule dict to YaraRule + KG edges
Ingest yara.ingest Orchestrate parse → entity → remember → persist

This page covers all three layers. The CLI wraps the ingest layer; see CLI.


Vendored CCCS schemas

Two CCCS YARA metadata schema files are vendored in src/zettelforge/yara/schemas/:

File Purpose
CCCS_YARA.yml Field definitions, formats, and optional flags for all CCCS metadata keys
CCCS_YARA_values.yml Allowed value sets for status, sharing, category, malware_type, actor_type, and hash

These files are a clean-room re-implementation; the upstream CCCS validator_functions.py is not vendored. See schemas/NOTICE.md.

ZettelForge loads both files eagerly at import time. A missing or unreadable schema file raises at startup.


Parser

parse_yara(text: str) → list[dict[str, Any]]

Parse YARA source text into a list of normalized rule dicts. A single .yar file may contain multiple rules; one dict per rule.

Uses a module-level plyara.Plyara instance protected by a threading lock (_PARSER_LOCK). The parser is reset before and after each call so state does not leak between invocations.

Each returned dict contains:

Key Type Source
rule_name str plyara
tags list[str] plyara (inline rule tags, e.g. rule Foo : APT)
imports list[str] plyara (YARA import statements)
metadata list[dict] plyara raw (list of single-key dicts)
meta dict added by parser — flattened from metadata, last-write-wins for duplicate keys
strings list[dict] plyara
condition_terms list[str] plyara
raw_condition str plyara
raw_strings str plyara
raw_meta str plyara
raw_rule str added by parser — exact source text carved from start_line/stop_line
start_line int plyara (1-indexed, inclusive)
stop_line int plyara

parse_file(path: str | Path) → list[dict[str, Any]]

Parse a .yar or .yara file. Raises YaraParseError when:

  • the file cannot be stat'd (I/O error),
  • the file exceeds MAX_RULE_FILE_BYTES (1 048 576 bytes, 1 MiB), or
  • plyara raises on a syntax error (re-raised as ValueError).
rules = parse_file("rules/apt41.yar")
for rule in rules:
    print(rule["rule_name"], rule["meta"].get("status"))

YaraParseError

ValueError subclass raised when a rule file is rejected before it reaches plyara (I/O error, oversize). Syntax errors from plyara are re-raised as plain ValueError.

from zettelforge.yara.parser import YaraParseError, parse_file

try:
    rules = parse_file(path)
except YaraParseError as e:
    print(f"rejected: {e}")
except ValueError as e:
    print(f"syntax error: {e}")

CCCS metadata validation

Validation tiers

ZettelForge validates each rule's metadata block against the vendored CCCS schema at ingest time. Three tiers control strictness:

Tier Behaviour
"warn" (default) Required-field failures and value violations are recorded as warnings. accepted is always True.
"strict" Failures on required fields produce errors and set accepted = False; the ingest path drops the rule.
"non_cccs" All checks skipped. Returns (True, [], []) unconditionally.

Required CCCS fields

These fields are optional: No in CCCS_YARA.yml and must be present in strict tier. In warn tier, their absence is recorded as a warning but does not block ingest.

Field Format Validated values
status uppercase string TESTING, RELEASED, DEPRECATED
sharing TLP label TLP:CLEAR, TLP:GREEN, TLP:AMBER, TLP:AMBER+STRICT (each also allows //COMMERCIAL suffix)
source string Any non-empty string; uppercase is convention
author string [A-Za-z0-9_.@+-]+
description string Any non-empty string
category uppercase string INFO, EXPLOIT, TECHNIQUE, TOOL, MALWARE

Auto-generated fields

These fields are optional: Optional in CCCS_YARA.yml. The upstream CCCS validator auto-generates them when absent; ZettelForge does not auto-generate but treats them as required in strict tier to preserve provenance.

Field Format
id Base62 UUID (16+ chars)
fingerprint Hex digest (SHA-1 or SHA-256, 40–64 chars)
version x.y
modified YYYY-MM-DD

Optional CCCS fields

All other fields in CCCS_YARA.yml are optional: Yes. They are validated when present but their absence never produces a warning or error.

Field Type Description
date string (YYYY-MM-DD) Creation date
score integer (0–100) Confidence percentage
minimum_yara string (x.y) Minimum YARA version required
malware_type uppercase string Malware capability (see malware types)
mitre_att string MITRE ATT&CK ID(s): T####, T####.###, TA####, G####, S####; comma- or semicolon-separated list accepted
actor string Threat actor name
actor_type uppercase string APT, CRIMEWARE, or FIN
technique string CCCS technique tag (freeform; distinct from mitre_att)
hash string Sample hash: 32-char (MD5), 40-char (SHA-1), or 64-char (SHA-256) hex
report string Linked report URL or reference
reference string External reference (URL, report, individual)
vol_script string Reverse-engineering team metadata
al_* string Assembly Line internal metadata
credit string Author credit note
original_* string Preserved metadata from rule conversions

validate_metadata(rule_meta, tier)ValidationResult

from zettelforge.yara.cccs_metadata import validate_metadata, ValidationResult

result: ValidationResult = validate_metadata(meta_dict, tier="warn")
# result.accepted  — True unless strict tier produced errors
# result.warnings  — list[str]
# result.errors    — list[str]

Malware type values

Valid values for malware_type (validated when present):

ADWARE, APT, BACKDOOR, BANKER, BOOTKIT, BOT, BROWSER-HIJACKER, BRUTEFORCER, CLICKFRAUD, CRYPTOMINER, DDOS, DOWNLOADER, DROPPER, EXPLOITKIT, FAKEAV, HACKTOOL, INFOSTEALER, KEYLOGGER, LOADER, OBFUSCATOR, POS, PROXY, RAT, RANSOMWARE, REVERSE-PROXY, ROOTKIT, SCANNER, SCAREWARE, SPAMMER, TROJAN, VIRUS, WIPER, WEBSHELL, WORM


YaraRule dataclass

YaraRule extends DetectionRule with YARA-specific fields.

DetectionRule base fields (inherited)

Field Type Description
rule_id str Primary identifier. CCCS id when present; otherwise yara_<content_hash[:16]>
title str Rule name (rule_name from parser)
source_format str Always "yara"
content_sha256 str SHA-256 of the raw rule text (when available) or of rule_name + strings + condition
description str \| None From CCCS description meta
author str \| None From CCCS author meta
date str \| None From CCCS date meta (YYYY-MM-DD)
modified str \| None From CCCS modified meta (YYYY-MM-DD)
references list[str] Populated from meta["report"] when present
tags list[str] Inline YARA tags (grammar-level, e.g. rule Foo : APT MAL)
level str \| None Not populated by YARA ingest (Sigma concept)
status str \| None Lowercased CCCS status value
tlp str \| None From CCCS sharing meta
license str \| None Not populated by YARA ingest
source_repo str \| None Not populated by YARA ingest
source_path str \| None Not populated by YARA ingest
extra dict[str, Any] See extra keys

YaraRule-specific fields

Field Type Description
rule_name str \| None Raw YARA rule name (same as title)
cccs_id str \| None CCCS id meta value
fingerprint str \| None CCCS fingerprint meta (SHA-256 over strings + condition)
category str \| None CCCS category: INFO, EXPLOIT, TECHNIQUE, TOOL, or MALWARE
technique_tag str \| None CCCS technique meta value (freeform; distinct from MITRE IDs)
cccs_version str \| None CCCS version meta value, coerced to string
hash_of_sample list[str] CCCS hash values (one or more sample hashes)
is_private bool True when the rule has the private modifier
is_global bool True when the rule has the global modifier
imports list[str] YARA import module names (e.g. "pe", "hash", "dotnet")
condition str \| None Raw condition expression, stripped of leading/trailing whitespace

Extra keys

Key Type Description
cccs_compliant str Tier the rule was tagged with: "strict", "warn", or "non_cccs"
cccs_warnings list[str] Warnings from metadata validation
cccs_errors list[str] Errors from metadata validation
condition_terms list[str] Individual condition tokens from plyara
source_line_range [int, int] [start_line, stop_line] from plyara
source str CCCS source org (when present)
malware_type str CCCS malware_type (when present)
actor_type str CCCS actor_type (present when actor_type is given but no actor name)

rule_to_entities(rule, *, tier)(YaraRule, list[dict])

Convert a parsed YARA rule dict into a YaraRule entity and a list of KG edges.

from zettelforge.yara.entities import rule_to_entities
from zettelforge.yara.parser import parse_yara

rules = parse_yara(yara_text)
entity, relations = rule_to_entities(rules[0], tier="warn")

KG edge shape

Every relation dict uses the canonical edge shape expected by SQLiteMemoryStore.add_kg_edge:

{
    "from_type": "YaraRule",
    "from_value": entity.rule_id,   # str
    "rel":        str,              # see relation types below
    "to_type":    str,              # entity type of the target
    "to_value":   str,              # primary identifier of the target
    "properties": dict,            # additional relation properties
}

Relation types

Relation to_type Trigger to_value
detects AttackPattern CCCS mitre_att meta (one edge per technique ID) Normalized MITRE ID, e.g. T1218, T1218.001
detects AttackPattern Inline tag matches T#### or attack.T#### MITRE technique ID
attributed_to ThreatActor CCCS actor meta Actor name string
tagged_with YaraTag CCCS technique meta Technique name (namespace "technique")
tagged_with YaraTag Inline tag (category token or freeform) Tag name
references_cve Vulnerability Inline tag matches CVE_YYYY_NNNN or CVE-YYYY-NNNN Normalized CVE-YYYY-NNNN

Tag resolution — resolve_yara_tag(tag)(entity_type, entity_properties)

Inline YARA grammar tags (the colon-separated tokens after the rule name) are resolved before they become KG edges:

Tag pattern Resolved type Example
T#### or attack.T#### (case-insensitive) AttackPattern T1218{"technique_id": "T1218"}
CVE_YYYY_NNNN or CVE-YYYY-NNNN (case-insensitive) Vulnerability CVE_2021_44228{"cve_id": "CVE-2021-44228"}
Known category token YaraTag APT{"namespace": "category", "name": "APT"}
Anything else YaraTag custom_tag{"namespace": "freeform", "name": "custom_tag"}

Known category tokens (case-insensitive match): APT, CRIME, CRIMEWARE, EXPL, HKTL, MAL, MALWARE, PUA, RAT, RANSOM, RANSOMWARE, SUSP, VULN, WEBSHELL.


Ingest API

ingest_rule(rule_source, mm, *, domain, tier, sync)(MemoryNote | None, list[dict])

Ingest a single YARA rule into a MemoryManager instance.

Parameter Type Default Description
rule_source str \| Path \| dict required Path to a .yar file, raw YARA text, or a pre-parsed plyara dict. When a file contains multiple rules, only the first is ingested.
mm MemoryManager required Must not be None; raises ValueError otherwise.
domain str "detection" Memory domain for the note.
tier str "warn" CCCS validation tier.
sync bool True Run MemoryManager enrichment inline.

Returns (None, relations) when strict-tier validation rejects the rule. relations is always a list (empty when the rule has no extractable entities).

Idempotency. Before calling mm.remember, the ingest path checks for an existing note with source_ref = "yara:{rule_id}:{content_sha256[:12]}". If found, the rule is skipped and the existing note is returned.

ingest_rules_dir(path, mm, *, glob, tier, domain, bulk, flush_timeout)dict

Walk a directory tree and ingest every YARA rule file.

result = ingest_rules_dir("rules/", mm=mm, tier="warn", domain="detection")
# result == {"ingested": int, "skipped": int, "errors": list[str]}
Parameter Type Default Description
path str \| Path required Root directory to walk.
mm MemoryManager required Must not be None.
glob str "**/*.yar" Glob pattern for rule files. The default also sweeps for **/*.yara.
tier str "warn" CCCS validation tier.
domain str "detection" Memory domain for notes.
bulk bool False Defer enrichment and flush once after all rules. Pass True for large rule sets.
flush_timeout float \| None None Timeout in seconds passed to mm.flush() when bulk=True.

Security. Symlinks are never followed. Files whose resolved path escapes the root directory are skipped with a warning log.


CLI

python -m zettelforge.yara.ingest <path> [--tier TIER] [--dry-run] [--domain DOMAIN] [--json]
Flag Default Description
path required Path to a .yar file or directory
--tier warn strict, warn, or non_cccs
--dry-run off Parse, validate, and print a summary without writing to memory
--domain detection Memory domain for ingested notes
--json off Emit machine-readable JSON output

Exit codes: 0 on success; 1 on parse errors, strict-tier rejections, or I/O failures.

Dry-run a directory:

python -m zettelforge.yara.ingest tests/fixtures/yara/ --dry-run

Ingest with strict CCCS validation:

python -m zettelforge.yara.ingest rules/ --tier strict --domain detection

The LLM rule explainer is not invoked by this CLI command in v1. Explainer integration with the async enrichment worker is a v1.1 task.