kotoba-lang/sigma
GitHub: kotoba-lang/sigma
该库以零依赖的可移植 Clojure 将 Sigma 检测规则建模为纯 EDN 数据,并提供校验、YAML 转换与纯事件匹配能力。
Stars: 0 | Forks: 0
# sigma-clj (检测规则)
[](https://github.com/kotoba-lang/sigma/actions/workflows/ci.yml)
在可移植的 Clojure 中 **将 Sigma 检测规则作为 EDN/Clojure 数据处理** —— 每个
命名空间都是 `.cljc`,且 **运行时零第三方依赖**,因此它可以在 JVM、
ClojureScript 和 Clojure-on-WASM 主机 (SCI) 上运行。Sigma 规则是纯数据,你可以
对其进行 `assoc`、`diff`、存储在 Datomic 中,或动态生成;该库在此基础上添加了结构性
校验、YAML 结构 ↔ EDN 转换器,以及一个纯事件匹配器。
它是
[ghosthacker](https://github.com/com-junkawasaki/ghosthacker)(安全工具套件)
的天然搭档,也是本组织中其他可复用 `*-clj` 核心
([bpmn-clj](https://github.com/com-junkawasaki/bpmn-clj)、
[dmn-clj](https://github.com/com-junkawasaki/dmn-clj))的兄弟项目。
## 为什么需要共享库 (组织归属)
根据三组织规则,**可复用的**检测模型位于 **com-junkawasaki** 中;
驱动具体检测流水线的 **公益行为者实例** 位于
**etzhayyim** 中;任何 **商业/私有部署** 则位于 **gftdcojp** 中。sigma-clj
就是那个依赖项 —— 它不包含任何领域规则,也没有引擎绑定(那些都是
由宿主注入的端口)。Sigma 规则本身属于使用该规则的组织。
## 模型:作为 EDN 的 Sigma 规则 (`sigma.model`)
规则是带有命名空间 `:sigma/*` 的 map;selection 使用字符串作为键(保留
包括 `|modifier` 后缀在内的 YAML 字段键格式):
```
{:sigma/id "bf-01"
:sigma/title "Brute Force Login Attempt"
:sigma/level :high
:sigma/logsource {:sigma/product "windows" :sigma/service "security"}
:sigma/detection
{:sigma/selections
{"sel" {"EventID" [4625] "TargetUserName|contains" ["adm"]}
"filt" {"IpAddress" ["127.0.0.1"]}}
:sigma/condition "sel and not filt"}}
```
一个对线程友好的构建器:
```
(require '[sigma.model :as m])
(def rule
(-> (m/rule "bf-01" "Brute Force Login" {:level :high
:logsource {:sigma/product "windows"
:sigma/service "security"}})
(m/add-selection "sel" {"EventID" [4625] "TargetUserName|contains" ["adm"]})
(m/add-selection "filt" {"IpAddress" ["127.0.0.1"]})
(m/set-condition "sel and not filt")))
(m/selection-names rule) ;=> #{"sel" "filt"}
(m/condition rule) ;=> "sel and not filt"
```
## 校验 (`sigma.validate`)
`problems` 返回一个包含 `{:sigma/severity :error|:warn :sigma/code :sigma/id :sigma/msg}` 的 vector;
当且仅当不存在 `:error` 时 `valid?` 为真(警告仅作参考):
```
(require '[sigma.validate :as v])
(v/valid? rule) ;=> true
(v/problems broken) ;=> [{:sigma/severity :error :sigma/code :condition/unknown-selection …}]
```
错误:条件引用了未定义的 selection、未知的字段修饰符。
警告:未知的 `:sigma/level`(参考性建议 —— Sigma 规范会不断演进;当前已知的级别有
`:informational` `:low` `:medium` `:high` `:critical`)、条件中的 glob 没有匹配到任何
已定义的 selection。
## YAML 结构 ↔ EDN (`sigma.yaml`)
`from-data` 将一个 **已解析的** Sigma YAML map(字符串键,类似于
`clj-yaml` 等 YAML 库生成的格式)转换为 `:sigma/*` 模型。`to-data` 执行
反向操作。**这里绝不解析原始 YAML 文本** —— 请注入你所用平台的 YAML 解析器:
```
(require '[sigma.yaml :as y])
;; Host already parsed YAML to a Clojure map with string keys:
(def data {"title" "Brute Force Login" "id" "bf-01" "level" "high"
"logsource" {"product" "windows" "service" "security"}
"detection" {"sel" {"EventID" [4625] "TargetUserName|contains" ["adm"]}
"filt" {"IpAddress" ["127.0.0.1"]}
"condition" "sel and not filt"}})
(def rule (y/from-data data))
(:sigma/level rule) ;=> :high
(= data (y/to-data rule)) ;=> true (round-trip lossless)
```
## 端口 (`sigma.ports`)
宿主通过注入 `IField`,使得 sigma-clj 绝不预设事件 schema:
```
IField extract [field-name event] — String field name + event map → value
```
`default-ports` 会优先尝试字符串键,然后再尝试关键字键 —— 这对于普通 map 来说已经足够。
对于结构化的日志 schema(嵌套 map、schema registry、带类型的字段),请提供
自定义的 `IField` 实现。
## 执行 (`sigma.execute` + `sigma.ports`)
一个 **纯粹的事件匹配器** —— 没有 I/O 操作,也没有副作用。将每个具名 selection 编译
为谓词,然后对条件表达式进行求值:
```
(require '[sigma.execute :as e]
'[sigma.ports :as p])
(def pts (p/default-ports))
(e/matches? pts rule {"EventID" 4625 "TargetUserName" "administrator" "IpAddress" "10.0.0.1"})
;=> true (sel=true, filt=false)
(e/matches? pts rule {"EventID" 4625 "TargetUserName" "administrator" "IpAddress" "127.0.0.1"})
;=> false (filtered: filt=true)
```
**条件表达式** 支持标识符、`and` / `or` / `not`、括号、
`1 of `(任何名称匹配该 glob 的 selection,例如 `sel*`),以及
`all of them`。
**字段修饰符**(字段键上的管道符后缀):
| 修饰符 | 语义 |
|--------------|----------------------------------------------------|
| `contains` | 字段值(作为字符串)包含判定条件 |
| `startswith` | 字段值以判定条件开头 |
| `endswith` | 字段值以判定条件结尾 |
| `re` | 字段值中能 `re-find` 匹配到作为判定条件的正则表达式 |
| `gt` | 字段值(数字)> 判定条件(数字) |
| `lt` | 字段值(数字)< 判定条件(数字) |
| `all` | 对值列表进行 AND 操作,而不是 OR |
修饰符可以组合使用:`"Cmd|contains|all"` 表示该字段必须包含列出的
**所有** 值。`default-ports` 处理普通的以字符串/关键字为键的事件 map;对于结构化
schema,请替换为自定义的 `IField`。
**安全协同**:sigma-clj 可以与
[ghosthacker](https://github.com/com-junkawasaki/ghosthacker) 天然配合 —— ghosthacker 可以
根据其安全工具的输出生成结构化的事件 map,而 sigma-clj
无需任何额外的依赖即可针对这些 map 评估 Sigma 检测规则。
## 测试
```
clojure -X:test
```
标签:Clojure, EDN, Sigma规则, 云计算, 安全工程, 日志检测, 目标导入, 规则引擎, 零依赖