From 83be40a1c3d7f85b1c29e4eff68ad38579440232 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Fri, 5 Jun 2026 08:29:55 +0200 Subject: [PATCH 01/13] specs(pack.yaml): extended rules usage --- docs/authoring.md | 5 +- docs/packs.md | 2 +- docs/repository.md | 6 +- packs/linux/advanced/pack.yml | 22 ++- .../sigma/file_event_lnx_cron_persistence.yml | 1 + ...le_event_lnx_shell_profile_persistence.yml | 1 + .../file_event_lnx_systemd_persistence.yml | 1 + .../proc_creation_lnx_download_pipe_shell.yml | 1 + ..._creation_lnx_exec_from_world_writable.yml | 1 + packs/linux/essential/pack.yml | 24 ++- packs/linux/essential/rules/ioc/hashes.txt | 3 + .../file_event_lnx_ldso_preload_hijack.yml | 1 + .../file_event_lnx_ssh_authorized_keys.yml | 1 + .../file_event_lnx_sshd_config_tamper.yml | 1 + .../sigma/file_event_lnx_sudoers_tamper.yml | 1 + ...eation_lnx_shell_dev_tcp_reverse_shell.yml | 1 + ...oc_creation_lnx_webserver_spawns_shell.yml | 1 + .../rules/yara/lnx_susp_xmrig_coinminer.yar | 1 + packs/macos/advanced/pack.yml | 24 +-- .../file_event_macos_launch_persistence.yml | 1 + ...creation_macos_download_execute_cradle.yml | 1 + ...reation_macos_exec_from_world_writable.yml | 1 + ...roc_creation_macos_local_admin_account.yml | 1 + packs/macos/essential/pack.yml | 28 ++-- packs/macos/essential/rules/ioc/hashes.txt | 3 + .../proc_creation_macos_gatekeeper_bypass.yml | 1 + ..._creation_macos_keychain_dump_security.yml | 1 + ..._creation_macos_osascript_admin_prompt.yml | 1 + ...tion_macos_shell_dev_tcp_reverse_shell.yml | 1 + .../yara/macos_susp_coinminer_strings.yar | 1 + packs/windows/advanced/pack.yml | 26 +-- ...ion_win_susp_local_admin_account_added.yml | 1 + ...roc_creation_win_susp_rundll32_no_args.yml | 1 + ...proc_creation_win_susp_schtasks_create.yml | 1 + ..._creation_win_susp_wmic_process_create.yml | 1 + ...pt_win_susp_powershell_download_cradle.yml | 1 + ...registry_event_win_run_key_persistence.yml | 1 + ...rvice_creation_win_susp_service_binary.yml | 1 + packs/windows/essential/pack.yml | 40 +++-- packs/windows/essential/rules/ioc/hashes.txt | 3 + ...c_creation_win_susp_encoded_powershell.yml | 1 + .../proc_creation_win_susp_eventlog_clear.yml | 1 + ...eation_win_susp_lsass_comsvcs_minidump.yml | 1 + ...eation_win_susp_mshta_remote_execution.yml | 1 + ..._creation_win_susp_ntds_dit_extraction.yml | 1 + ...reation_win_susp_office_spawning_shell.yml | 1 + ...c_creation_win_susp_registry_hive_dump.yml | 1 + ...ion_win_susp_regsvr32_remote_scriptlet.yml | 1 + ...creation_win_susp_shadow_copy_deletion.yml | 1 + ...eation_win_susp_uac_bypass_autoelevate.yml | 1 + .../registry_event_win_defender_tamper.yml | 1 + ...vent_win_wdigest_cleartext_credentials.yml | 1 + ...reation_win_susp_scheduled_task_action.yml | 1 + .../rules/yara/win_susp_mimikatz_strings.yar | 1 + packs/windows/hunting/pack.yml | 16 +- ...n_win_hunting_lolbin_certutil_download.yml | 1 + pyproject.toml | 2 + schemas/pack.schema.json | 108 +++++++++++- tools/build_packs.py | 1 + tools/lib.py | 158 ++++++++++++++++-- tools/link_packs.py | 158 ++++++++++++++++++ tools/validate.py | 19 ++- uv.lock | 53 +++--- 63 files changed, 627 insertions(+), 117 deletions(-) create mode 120000 packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml create mode 120000 packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml create mode 120000 packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml create mode 120000 packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml create mode 120000 packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml create mode 100644 packs/linux/essential/rules/ioc/hashes.txt create mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml create mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml create mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml create mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml create mode 120000 packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml create mode 120000 packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml create mode 120000 packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar create mode 120000 packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml create mode 120000 packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml create mode 120000 packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml create mode 120000 packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml create mode 100644 packs/macos/essential/rules/ioc/hashes.txt create mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml create mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml create mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml create mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml create mode 120000 packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar create mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml create mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml create mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml create mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml create mode 120000 packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml create mode 120000 packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml create mode 120000 packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml create mode 100644 packs/windows/essential/rules/ioc/hashes.txt create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml create mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml create mode 120000 packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml create mode 120000 packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml create mode 120000 packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml create mode 120000 packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar create mode 120000 packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml create mode 100644 tools/link_packs.py diff --git a/docs/authoring.md b/docs/authoring.md index b4dd128..5fa394c 100644 --- a/docs/authoring.md +++ b/docs/authoring.md @@ -92,8 +92,11 @@ rustinel: Then add the `id` to a pack, e.g. `packs/windows/essential/pack.yml`: ```yaml +schema: v1.0.0 rules: - - 7f3a1c2e-4b5d-4e6f-8a90-1b2c3d4e5f60 # Suspicious Encoded PowerShell Command Line + has: + sigma: + - 7f3a1c2e-4b5d-4e6f-8a90-1b2c3d4e5f60 # Suspicious Encoded PowerShell Command Line ``` --- diff --git a/docs/packs.md b/docs/packs.md index 42caf22..8d98427 100644 --- a/docs/packs.md +++ b/docs/packs.md @@ -18,7 +18,7 @@ Essential ⊂ Advanced ⊂ Hunting | [macOS Essential](#macos-essential) | essential | ❌ | low | experimental | | [macOS Advanced](#macos-advanced) | advanced | ❌ | medium | experimental | -> All packs declare `requires_rustinel: ">=1.0.2"`, `pack_schema_version: 1`, and license +> All packs declare `schema: v1.0.0`, `requires_rustinel: ">=1.0.2"`, `pack_schema_version: 1`, and license > `DRL-1.1`. `status: experimental` reflects the early state of v1 content — expect curation to > tighten as coverage grows. diff --git a/docs/repository.md b/docs/repository.md index d61428c..0705415 100644 --- a/docs/repository.md +++ b/docs/repository.md @@ -53,9 +53,11 @@ Key manifest fields: | `id` / `name` | Stable id (`^[a-z0-9]+(-[a-z0-9]+)*$`) and human name. | | `os` | `windows` \| `linux` \| `macos`. | | `level` | `essential` \| `advanced` \| `hunting`. | +| `schema` | Required pack manifest schema version (`v1.0.0`). | | `default` | Whether this pack is enabled by default. | | `extends` | Pack ids cumulatively included (rules merged, never duplicated). | -| `rules` | Artifact ids added by *this* pack. | +| `rules` | Optional dictionary specifying rules directly in this pack (`has`), or rules to include (`includes`) / exclude (`excludes`) from extended packs or automatic sources. | +| `sources` | Optional dictionary of upstream sources categorized by type (`manual`, `sigma`, `yara`). | | `requires_rustinel` | Engine version constraint, e.g. `">=1.0.2"`. | | `attack_coverage` | ATT&CK technique ids covered (drift-checked against members). | | `telemetry_requirements` | Rustinel telemetry channels the pack needs. | @@ -90,7 +92,7 @@ dist/ What the build does for each pack: -1. **Resolves** the cumulative rule list (`extends` + `rules`, de-duplicated). +1. **Resolves** the cumulative rule list (`extends` + `rules` with `has`/`includes`/`excludes` or pack subfolders, de-duplicated). 2. **Copies** each Sigma/YARA file into the flat `rules/sigma` and `rules/yara` folders. 3. **Flattens** every referenced IOC set into the four per-type files in `VALUE;COMMENT` format, prefixing each line with its source set id (`[ioc-…]`) so provenance survives into alerts. diff --git a/packs/linux/advanced/pack.yml b/packs/linux/advanced/pack.yml index b68f58d..510623b 100644 --- a/packs/linux/advanced/pack.yml +++ b/packs/linux/advanced/pack.yml @@ -1,11 +1,12 @@ name: Linux Advanced id: linux-advanced description: > - Linux Essential plus broader production detections. More false positives may - occur than in Essential (notably from package installs); tune per environment - before relying on by default. + Linux Essential plus broader production detections. More false positives may + occur than in Essential (notably from package installs); tune per environment + before relying on by default. os: linux level: advanced +schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false @@ -27,10 +28,13 @@ telemetry_requirements: - file_scan test_status: none sources: - - https://attack.mitre.org/ + manual: + - https://attack.mitre.org/ rules: - - 3d4e5f60-7182-4394-9da5-2e3f4a5b6c73 # Systemd Unit Persistence (sigma) - - 4e5f6071-8293-44a5-8eb6-3f4a5b6c7d84 # Cron Job Persistence (sigma) - - 5f607182-93a4-45b6-9fc7-4a5b6c7d8e95 # Shell Profile / RC File Persistence (sigma) - - 60718293-a4b5-46c7-8fd8-5b6c7d8e9fa6 # Execution from World-Writable / Temporary Directory (sigma) - - 94fb53c7-debd-4287-99e4-6eb0ba923731 # Linux Download and Execute Piped to Shell (sigma) + has: + sigma: + - 3d4e5f60-7182-4394-9da5-2e3f4a5b6c73 # Systemd Unit Persistence (sigma) + - 4e5f6071-8293-44a5-8eb6-3f4a5b6c7d84 # Cron Job Persistence (sigma) + - 5f607182-93a4-45b6-9fc7-4a5b6c7d8e95 # Shell Profile / RC File Persistence (sigma) + - 60718293-a4b5-46c7-8fd8-5b6c7d8e9fa6 # Execution from World-Writable / Temporary Directory (sigma) + - 94fb53c7-debd-4287-99e4-6eb0ba923731 # Linux Download and Execute Piped to Shell (sigma) diff --git a/packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml b/packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml new file mode 120000 index 0000000..b04ec09 --- /dev/null +++ b/packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/file_event_lnx_cron_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml b/packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml new file mode 120000 index 0000000..c39ab31 --- /dev/null +++ b/packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/file_event_lnx_shell_profile_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml b/packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml new file mode 120000 index 0000000..bcde891 --- /dev/null +++ b/packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/file_event_lnx_systemd_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml b/packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml new file mode 120000 index 0000000..499fe12 --- /dev/null +++ b/packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/proc_creation_lnx_download_pipe_shell.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml b/packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml new file mode 120000 index 0000000..3d47e05 --- /dev/null +++ b/packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/proc_creation_lnx_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/linux/essential/pack.yml b/packs/linux/essential/pack.yml index 1aae8df..0f83588 100644 --- a/packs/linux/essential/pack.yml +++ b/packs/linux/essential/pack.yml @@ -3,6 +3,7 @@ id: linux-essential description: Low-noise, high-confidence Linux detections for Rustinel. Safe default pack. os: linux level: essential +schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: true @@ -25,13 +26,18 @@ telemetry_requirements: - file_scan test_status: none sources: - - https://attack.mitre.org/ + manual: + - https://attack.mitre.org/ rules: - - 0a1b2c3d-4e5f-4061-8a72-9b0c1d2e3f40 # Dynamic Linker Hijacking via ld.so.preload (sigma) - - 1b2c3d4e-5f60-4172-9b83-0c1d2e3f4a51 # SSH authorized_keys Created or Replaced (sigma) - - 2c3d4e5f-6071-4283-8c94-1d2e3f4a5b62 # Sudoers Configuration Tampering (sigma) - - a3c5c821-004a-4e52-8684-0f7f9ea0404c # Linux Reverse Shell via /dev/tcp (sigma) - - 8fd4d1d9-38cf-4704-8009-00e41a009c98 # Linux Web Server Spawning Interactive Shell (sigma) - - 9ac8732e-1818-4cda-89ac-01ca08a7b836 # SSH Daemon Configuration Tampering (sigma) - - yara-lnx-xmrig-coinminer # XMRig / coinminer strings in Linux ELF binaries (yara) - - ioc-eicar-test # EICAR safe end-to-end IOC test set (ioc) + has: + sigma: + - 0a1b2c3d-4e5f-4061-8a72-9b0c1d2e3f40 # Dynamic Linker Hijacking via ld.so.preload (sigma) + - 1b2c3d4e-5f60-4172-9b83-0c1d2e3f4a51 # SSH authorized_keys Created or Replaced (sigma) + - 2c3d4e5f-6071-4283-8c94-1d2e3f4a5b62 # Sudoers Configuration Tampering (sigma) + - a3c5c821-004a-4e52-8684-0f7f9ea0404c # Linux Reverse Shell via /dev/tcp (sigma) + - 8fd4d1d9-38cf-4704-8009-00e41a009c98 # Linux Web Server Spawning Interactive Shell (sigma) + - 9ac8732e-1818-4cda-89ac-01ca08a7b836 # SSH Daemon Configuration Tampering (sigma) + yara: + - yara-lnx-xmrig-coinminer # XMRig / coinminer strings in Linux ELF binaries (yara) + ioc: + - ioc-eicar-test # EICAR safe end-to-end IOC test set (ioc) # EICAR safe end-to-end IOC test set (ioc) # EICAR safe end-to-end IOC test set (ioc) diff --git a/packs/linux/essential/rules/ioc/hashes.txt b/packs/linux/essential/rules/ioc/hashes.txt new file mode 100644 index 0000000..6bcbf17 --- /dev/null +++ b/packs/linux/essential/rules/ioc/hashes.txt @@ -0,0 +1,3 @@ +44d88612fea8a8f36de82e1278abb02f;rule=ioc-eicar-test severity=low comment=EICAR test file (MD5) +3395856ce81f2b7382dee72602f798b642f14140;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA1) +275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA256) \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml b/packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml new file mode 120000 index 0000000..1f3e79d --- /dev/null +++ b/packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/file_event_lnx_ldso_preload_hijack.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml b/packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml new file mode 120000 index 0000000..a0ebdea --- /dev/null +++ b/packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/file_event_lnx_ssh_authorized_keys.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml b/packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml new file mode 120000 index 0000000..853c59d --- /dev/null +++ b/packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/file_event_lnx_sshd_config_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml b/packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml new file mode 120000 index 0000000..26fc414 --- /dev/null +++ b/packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/file_event_lnx_sudoers_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml b/packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml new file mode 120000 index 0000000..00e1598 --- /dev/null +++ b/packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml b/packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml new file mode 120000 index 0000000..052a209 --- /dev/null +++ b/packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/linux/proc_creation_lnx_webserver_spawns_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar b/packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar new file mode 120000 index 0000000..51a2561 --- /dev/null +++ b/packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar @@ -0,0 +1 @@ +../../../../../rules/yara/linux/lnx_susp_xmrig_coinminer.yar \ No newline at end of file diff --git a/packs/macos/advanced/pack.yml b/packs/macos/advanced/pack.yml index 7250ef0..4750642 100644 --- a/packs/macos/advanced/pack.yml +++ b/packs/macos/advanced/pack.yml @@ -1,13 +1,14 @@ name: macOS Advanced id: macos-advanced description: > - macOS Essential plus broader detections: launch-item persistence, shell - download cradles, privileged account creation and execution from temporary - paths. More false positives may occur — notably from application installers — - so tune per environment before relying on by default. Experimental and - post-v1; disabled by default. + macOS Essential plus broader detections: launch-item persistence, shell + download cradles, privileged account creation and execution from temporary + paths. More false positives may occur — notably from application installers — + so tune per environment before relying on by default. Experimental and + post-v1; disabled by default. os: macos level: advanced +schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false @@ -29,9 +30,12 @@ telemetry_requirements: - file_event test_status: none sources: - - https://attack.mitre.org/ + manual: + - https://attack.mitre.org/ rules: - - 4d4f5162-7e80-4193-9da3-4e5f60712304 # Launch Agent or Daemon Persistence Plist Created (sigma) - - 3e506273-8f91-42a4-8eb4-5f6071823405 # Shell Download-and-Execute Pipe Cradle (sigma) - - 2f617384-9012-43b5-9fc5-60718293a416 # Local Admin Account Created via Directory Services (sigma) - - 1a728495-a123-44c6-8ad6-718293a4b527 # Execution from World-Writable or Temporary Directory (sigma) + has: + sigma: + - 4d4f5162-7e80-4193-9da3-4e5f60712304 # Launch Agent or Daemon Persistence Plist Created (sigma) + - 3e506273-8f91-42a4-8eb4-5f6071823405 # Shell Download-and-Execute Pipe Cradle (sigma) + - 2f617384-9012-43b5-9fc5-60718293a416 # Local Admin Account Created via Directory Services (sigma) + - 1a728495-a123-44c6-8ad6-718293a4b527 # Execution from World-Writable or Temporary Directory (sigma) diff --git a/packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml b/packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml new file mode 120000 index 0000000..0ecced2 --- /dev/null +++ b/packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/file_event_macos_launch_persistence.yml \ No newline at end of file diff --git a/packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml b/packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml new file mode 120000 index 0000000..213f656 --- /dev/null +++ b/packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/proc_creation_macos_download_execute_cradle.yml \ No newline at end of file diff --git a/packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml b/packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml new file mode 120000 index 0000000..c2e8400 --- /dev/null +++ b/packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/proc_creation_macos_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml b/packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml new file mode 120000 index 0000000..9dbf442 --- /dev/null +++ b/packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/proc_creation_macos_local_admin_account.yml \ No newline at end of file diff --git a/packs/macos/essential/pack.yml b/packs/macos/essential/pack.yml index 0c94b52..1019171 100644 --- a/packs/macos/essential/pack.yml +++ b/packs/macos/essential/pack.yml @@ -1,12 +1,13 @@ name: macOS Essential id: macos-essential description: > - Low-noise, high-confidence macOS detections for Rustinel, focused on the - dominant macOS threats: infostealers, keychain theft, Gatekeeper bypass and - cryptominers. Experimental and post-v1 — macOS packs are not yet - production-ready, so this pack is disabled by default. + Low-noise, high-confidence macOS detections for Rustinel, focused on the + dominant macOS threats: infostealers, keychain theft, Gatekeeper bypass and + cryptominers. Experimental and post-v1 — macOS packs are not yet + production-ready, so this pack is disabled by default. os: macos level: essential +schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false @@ -28,11 +29,16 @@ telemetry_requirements: - file_scan test_status: none sources: - - https://attack.mitre.org/ + manual: + - https://attack.mitre.org/ rules: - - 7a1c2e3f-4b5d-4e6f-8a90-1b2c3d4e5f01 # Keychain Credential Dump via security (sigma) - - 6b2d3f40-5c6e-4f71-9b81-2c3d4e5f6012 # osascript Credential Prompt or Suspicious Admin Shell (sigma) - - 5c3e4051-6d7f-4082-8c92-3d4e5f601203 # Gatekeeper or Quarantine Protection Disabled (sigma) - - be06a6b0-fa9f-4df4-9d4a-9f8794037508 # macOS Reverse Shell via /dev/tcp (sigma) - - yara-macos-coinminer-strings # Cryptominer Mach-O strings (yara) - - ioc-eicar-test # EICAR safe end-to-end IOC test set (ioc) + has: + sigma: + - 7a1c2e3f-4b5d-4e6f-8a90-1b2c3d4e5f01 # Keychain Credential Dump via security (sigma) + - 6b2d3f40-5c6e-4f71-9b81-2c3d4e5f6012 # osascript Credential Prompt or Suspicious Admin Shell (sigma) + - 5c3e4051-6d7f-4082-8c92-3d4e5f601203 # Gatekeeper or Quarantine Protection Disabled (sigma) + - be06a6b0-fa9f-4df4-9d4a-9f8794037508 # macOS Reverse Shell via /dev/tcp (sigma) + yara: + - yara-macos-coinminer-strings # Cryptominer Mach-O strings (yara) + ioc: + - ioc-eicar-test # EICAR safe end-to-end IOC test set (ioc) # EICAR safe end-to-end IOC test set (ioc) # EICAR safe end-to-end IOC test set (ioc) diff --git a/packs/macos/essential/rules/ioc/hashes.txt b/packs/macos/essential/rules/ioc/hashes.txt new file mode 100644 index 0000000..6bcbf17 --- /dev/null +++ b/packs/macos/essential/rules/ioc/hashes.txt @@ -0,0 +1,3 @@ +44d88612fea8a8f36de82e1278abb02f;rule=ioc-eicar-test severity=low comment=EICAR test file (MD5) +3395856ce81f2b7382dee72602f798b642f14140;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA1) +275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA256) \ No newline at end of file diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml new file mode 120000 index 0000000..17c943c --- /dev/null +++ b/packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/proc_creation_macos_gatekeeper_bypass.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml new file mode 120000 index 0000000..03b9b46 --- /dev/null +++ b/packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/proc_creation_macos_keychain_dump_security.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml new file mode 120000 index 0000000..4614e8d --- /dev/null +++ b/packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/proc_creation_macos_osascript_admin_prompt.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml new file mode 120000 index 0000000..971d104 --- /dev/null +++ b/packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/macos/proc_creation_macos_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar b/packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar new file mode 120000 index 0000000..b9d4c18 --- /dev/null +++ b/packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar @@ -0,0 +1 @@ +../../../../../rules/yara/macos/macos_susp_coinminer_strings.yar \ No newline at end of file diff --git a/packs/windows/advanced/pack.yml b/packs/windows/advanced/pack.yml index 0ec4825..3dab849 100644 --- a/packs/windows/advanced/pack.yml +++ b/packs/windows/advanced/pack.yml @@ -1,10 +1,11 @@ name: Windows Advanced id: windows-advanced description: > - Windows Essential plus broader production detections. More false positives may - occur than in Essential; tune per environment before relying on by default. + Windows Essential plus broader production detections. More false positives may + occur than in Essential; tune per environment before relying on by default. os: windows level: advanced +schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false @@ -30,13 +31,16 @@ telemetry_requirements: - service_creation test_status: manual sources: - - https://attack.mitre.org/ - - https://lolbas-project.github.io/ + manual: + - https://attack.mitre.org/ + - https://lolbas-project.github.io/ rules: - - 9c8b7a6e-1d2f-4a3b-9c0d-2e3f4a5b6c71 # Rundll32 Execution Without Standard Arguments - - 6f0d2a91-3b7c-4e58-9a16-8c4e1d7b2a09 # Local Account Created or Added to Administrators - - a1b2c3d4-7e8f-4012-9a3b-4c5d6e7f0a10 # Registry Run Key Persistence - - c3d4e5f6-9012-4234-9c5d-6e7f8a901a12 # PowerShell Download-and-Execute Cradle - - d4e5f6a7-0123-4345-9d6e-7f8a9b012a13 # Suspicious Service Binary Path - - 4a596825-4674-4db3-b360-842435edf11a # Scheduled Task Creation via Schtasks - - 2a3c3182-80b8-42d4-bd39-83c55582ef70 # WMI Process Execution via WMIC + has: + sigma: + - 9c8b7a6e-1d2f-4a3b-9c0d-2e3f4a5b6c71 # Rundll32 Execution Without Standard Arguments + - 6f0d2a91-3b7c-4e58-9a16-8c4e1d7b2a09 # Local Account Created or Added to Administrators + - a1b2c3d4-7e8f-4012-9a3b-4c5d6e7f0a10 # Registry Run Key Persistence + - c3d4e5f6-9012-4234-9c5d-6e7f8a901a12 # PowerShell Download-and-Execute Cradle + - d4e5f6a7-0123-4345-9d6e-7f8a9b012a13 # Suspicious Service Binary Path + - 4a596825-4674-4db3-b360-842435edf11a # Scheduled Task Creation via Schtasks + - 2a3c3182-80b8-42d4-bd39-83c55582ef70 # WMI Process Execution via WMIC diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml new file mode 120000 index 0000000..4ef1112 --- /dev/null +++ b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_local_admin_account_added.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml new file mode 120000 index 0000000..0c318d6 --- /dev/null +++ b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_rundll32_no_args.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml new file mode 120000 index 0000000..3345751 --- /dev/null +++ b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_schtasks_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml new file mode 120000 index 0000000..6db059e --- /dev/null +++ b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_wmic_process_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml b/packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml new file mode 120000 index 0000000..582d9f7 --- /dev/null +++ b/packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/ps_script_win_susp_powershell_download_cradle.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml b/packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml new file mode 120000 index 0000000..e433eaf --- /dev/null +++ b/packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/registry_event_win_run_key_persistence.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml b/packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml new file mode 120000 index 0000000..7dba46e --- /dev/null +++ b/packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/service_creation_win_susp_service_binary.yml \ No newline at end of file diff --git a/packs/windows/essential/pack.yml b/packs/windows/essential/pack.yml index 9ec1398..a284a5f 100644 --- a/packs/windows/essential/pack.yml +++ b/packs/windows/essential/pack.yml @@ -3,6 +3,7 @@ id: windows-essential description: Low-noise, high-confidence Windows detections for Rustinel. Safe default pack. os: windows level: essential +schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: true @@ -33,21 +34,26 @@ telemetry_requirements: - file_scan test_status: mixed sources: - - https://attack.mitre.org/ - - https://lolbas-project.github.io/ + manual: + - https://attack.mitre.org/ + - https://lolbas-project.github.io/ rules: - - 7f3a1c2e-4b5d-4e6f-8a90-1b2c3d4e5f60 # Suspicious Encoded PowerShell Command Line (sigma) - - 7d2a8f31-4b6c-49e0-a1f8-3c5d9e0b2a05 # LSASS Memory Dump via comsvcs.dll MiniDump (sigma) - - 1e9b4d68-2c7a-4f93-8b15-6d0a2e4c1b04 # Volume Shadow Copy Deletion (sigma) - - 9a0c3e75-6d8b-4f12-bc34-1e7a5d9c0b06 # Regsvr32 Remote Scriptlet Execution / Squiblydoo (sigma) - - 3c6f9b22-7e1a-4d85-9c0b-2a8d4e6f1b07 # Mshta Remote or Inline Script Execution (sigma) - - c4a7f2e9-5d3b-4810-a6c1-9e0f3b7d2a03 # Office Application Spawning a Command Shell (sigma) - - 5e8b1d44-9c2a-4f67-8d03-7b1a6e3c9d08 # Windows Event Log Cleared via Command Line (sigma) - - b2c3d4e5-8f90-4123-8b4c-5d6e7f801a11 # Microsoft Defender Tampering via Registry (sigma) - - e7a1b9c2-4d6f-4a83-b2e5-1c0d9f3a4b20 # Registry Hive Dump via reg.exe save (sigma) - - f2b8c4d1-6e90-4a27-9d3b-5a1c2e7f8b21 # UAC Bypass via Auto-Elevating LOLBin (sigma) - - a3c7e9b4-2f81-4d56-8c0a-6b2d1f4e9c22 # Active Directory Database (NTDS.dit) Extraction (sigma) - - b4d8f0c5-3a92-4e67-9d1b-7c3e2f5a0d23 # WDigest Cleartext Credential Caching Enabled (sigma) - - 3a1cdc64-a2a1-444b-a04b-32a3f1d09a99 # Scheduled Task Created With Suspicious Action (sigma) - - yara-win-mimikatz-strings # Mimikatz credential-dumping strings (yara) - - ioc-eicar-test # EICAR safe end-to-end IOC test set (ioc) + has: + sigma: + - 7f3a1c2e-4b5d-4e6f-8a90-1b2c3d4e5f60 # Suspicious Encoded PowerShell Command Line (sigma) # Suspicious Encoded PowerShell Command Line + - 7d2a8f31-4b6c-49e0-a1f8-3c5d9e0b2a05 # LSASS Memory Dump via comsvcs.dll MiniDump (sigma) + - 1e9b4d68-2c7a-4f93-8b15-6d0a2e4c1b04 # Volume Shadow Copy Deletion (sigma) + - 9a0c3e75-6d8b-4f12-bc34-1e7a5d9c0b06 # Regsvr32 Remote Scriptlet Execution / Squiblydoo (sigma) + - 3c6f9b22-7e1a-4d85-9c0b-2a8d4e6f1b07 # Mshta Remote or Inline Script Execution (sigma) + - c4a7f2e9-5d3b-4810-a6c1-9e0f3b7d2a03 # Office Application Spawning a Command Shell (sigma) + - 5e8b1d44-9c2a-4f67-8d03-7b1a6e3c9d08 # Windows Event Log Cleared via Command Line (sigma) + - b2c3d4e5-8f90-4123-8b4c-5d6e7f801a11 # Microsoft Defender Tampering via Registry (sigma) + - e7a1b9c2-4d6f-4a83-b2e5-1c0d9f3a4b20 # Registry Hive Dump via reg.exe save (sigma) + - f2b8c4d1-6e90-4a27-9d3b-5a1c2e7f8b21 # UAC Bypass via Auto-Elevating LOLBin (sigma) + - a3c7e9b4-2f81-4d56-8c0a-6b2d1f4e9c22 # Active Directory Database (NTDS.dit) Extraction (sigma) + - b4d8f0c5-3a92-4e67-9d1b-7c3e2f5a0d23 # WDigest Cleartext Credential Caching Enabled (sigma) + - 3a1cdc64-a2a1-444b-a04b-32a3f1d09a99 # Scheduled Task Created With Suspicious Action (sigma) + yara: + - yara-win-mimikatz-strings # Mimikatz credential-dumping strings (yara) + ioc: + - ioc-eicar-test # EICAR safe end-to-end IOC test set (ioc) # EICAR safe end-to-end IOC test set (ioc) # EICAR safe end-to-end IOC test set (ioc) diff --git a/packs/windows/essential/rules/ioc/hashes.txt b/packs/windows/essential/rules/ioc/hashes.txt new file mode 100644 index 0000000..6bcbf17 --- /dev/null +++ b/packs/windows/essential/rules/ioc/hashes.txt @@ -0,0 +1,3 @@ +44d88612fea8a8f36de82e1278abb02f;rule=ioc-eicar-test severity=low comment=EICAR test file (MD5) +3395856ce81f2b7382dee72602f798b642f14140;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA1) +275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA256) \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml new file mode 120000 index 0000000..1d6e08f --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_encoded_powershell.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml new file mode 120000 index 0000000..6ca1d9a --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_eventlog_clear.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml new file mode 120000 index 0000000..1092aaf --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_lsass_comsvcs_minidump.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml new file mode 120000 index 0000000..90caeea --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_mshta_remote_execution.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml new file mode 120000 index 0000000..f1d4e8e --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_ntds_dit_extraction.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml new file mode 120000 index 0000000..3e9b3cf --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_office_spawning_shell.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml new file mode 120000 index 0000000..ae26533 --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_registry_hive_dump.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml new file mode 120000 index 0000000..2eaf7ca --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_regsvr32_remote_scriptlet.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml new file mode 120000 index 0000000..3d34cbd --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_shadow_copy_deletion.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml new file mode 120000 index 0000000..6b7d9e0 --- /dev/null +++ b/packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_susp_uac_bypass_autoelevate.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml b/packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml new file mode 120000 index 0000000..0ade0a8 --- /dev/null +++ b/packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/registry_event_win_defender_tamper.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml b/packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml new file mode 120000 index 0000000..6c28587 --- /dev/null +++ b/packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/registry_event_win_wdigest_cleartext_credentials.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml b/packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml new file mode 120000 index 0000000..e127a9c --- /dev/null +++ b/packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/task_creation_win_susp_scheduled_task_action.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar b/packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar new file mode 120000 index 0000000..c7a2105 --- /dev/null +++ b/packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar @@ -0,0 +1 @@ +../../../../../rules/yara/windows/win_susp_mimikatz_strings.yar \ No newline at end of file diff --git a/packs/windows/hunting/pack.yml b/packs/windows/hunting/pack.yml index c0530c9..366c333 100644 --- a/packs/windows/hunting/pack.yml +++ b/packs/windows/hunting/pack.yml @@ -1,11 +1,12 @@ name: Windows Hunting id: windows-hunting description: > - Windows Advanced plus broad, noisier hunting content intended for analyst-driven - investigation. Not enabled by default and not suitable as a standing alert source - without tuning. + Windows Advanced plus broad, noisier hunting content intended for analyst-driven + investigation. Not enabled by default and not suitable as a standing alert source + without tuning. os: windows level: hunting +schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false @@ -20,7 +21,10 @@ telemetry_requirements: - process_creation test_status: none sources: - - https://attack.mitre.org/ - - https://lolbas-project.github.io/ + manual: + - https://attack.mitre.org/ + - https://lolbas-project.github.io/ rules: - - 3e2d1c0b-5a4f-4938-8271-6a5b4c3d2e10 # Certutil Used to Download Remote Content (Hunting) + has: + sigma: + - 3e2d1c0b-5a4f-4938-8271-6a5b4c3d2e10 # Certutil Used to Download Remote Content (Hunting) diff --git a/packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml b/packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml new file mode 120000 index 0000000..3779e26 --- /dev/null +++ b/packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml @@ -0,0 +1 @@ +../../../../../rules/sigma/windows/proc_creation_win_hunting_lolbin_certutil_download.yml \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index f0d53ba..b1eb717 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ "pyyaml>=6.0", "jsonschema>=4.0", "yara-x>=1.0", + "ruamel.yaml>=0.18.0", ] [dependency-groups] @@ -21,6 +22,7 @@ dev = [ # backend, no install of this project itself). [tool.uv] package = false +exclude-newer = "3 days" # Prevent supply-chain attacks by excluding recently published versions of dependencies. [tool.ruff] line-length = 100 diff --git a/schemas/pack.schema.json b/schemas/pack.schema.json index 6b1bf5a..ef7cfdc 100644 --- a/schemas/pack.schema.json +++ b/schemas/pack.schema.json @@ -11,12 +11,12 @@ "description", "os", "level", + "schema", "pack_schema_version", "requires_rustinel", "default", "status", - "extends", - "rules" + "extends" ], "properties": { "name": { @@ -41,6 +41,11 @@ "type": "string", "enum": ["essential", "advanced", "hunting"] }, + "schema": { + "type": "string", + "description": "Pack manifest schema version.", + "const": "v1.0.0" + }, "pack_schema_version": { "type": "integer", "const": 1 @@ -69,15 +74,102 @@ "uniqueItems": true }, "rules": { - "type": "array", - "description": "Rule ids (Sigma UUIDs or YARA rule names) added by THIS pack on top of any it extends.", - "items": { "type": "string" }, - "uniqueItems": true + "type": "object", + "description": "Rules included, inherited, or excluded by this pack.", + "additionalProperties": false, + "properties": { + "has": { + "type": "object", + "description": "Rules directly included by this pack.", + "additionalProperties": false, + "properties": { + "sigma": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "yara": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "ioc": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + } + } + }, + "includes": { + "type": "object", + "description": "Rules that should be included inside this pack from the packs that it is extending.", + "additionalProperties": false, + "properties": { + "sigma": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "yara": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "ioc": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + } + } + }, + "excludes": { + "type": "object", + "description": "Rules that should not be taken from an extended pack, or ignored if loading from automatic sources.", + "additionalProperties": false, + "properties": { + "sigma": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "yara": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "ioc": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + } + } + } + } }, "sources": { - "type": "array", + "type": "object", "description": "Upstream / CTI references that informed this pack.", - "items": { "type": "string" } + "additionalProperties": false, + "properties": { + "manual": { + "type": "array", + "description": "Rules/sources that were extracted manually.", + "items": { "type": "string" }, + "uniqueItems": true + }, + "sigma": { + "type": "array", + "description": "Sigma sources that can be cloned with git and copied automatically.", + "items": { "type": "string" }, + "uniqueItems": true + }, + "yara": { + "type": "array", + "description": "YARA sources that can be cloned with git and copied automatically.", + "items": { "type": "string" }, + "uniqueItems": true + } + } }, "license": { "type": "string", diff --git a/tools/build_packs.py b/tools/build_packs.py index 737e7d4..9a167cf 100644 --- a/tools/build_packs.py +++ b/tools/build_packs.py @@ -134,6 +134,7 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: "version": version, "default": manifest.get("default", False), "requires_rustinel": manifest.get("requires_rustinel"), + "schema": manifest.get("schema"), "pack_schema_version": manifest.get("pack_schema_version"), "status": manifest.get("status"), "expected_false_positive_level": manifest.get("expected_false_positive_level"), diff --git a/tools/lib.py b/tools/lib.py index 0d5a105..9186e6a 100644 --- a/tools/lib.py +++ b/tools/lib.py @@ -188,27 +188,163 @@ def packs_by_id() -> dict[str, dict]: return {p["id"]: p for p in load_packs() if "id" in p} +def _get_rule_ids_from_dict(sub_dict: dict | list | None) -> list[str]: + if not sub_dict: + return [] + if isinstance(sub_dict, list): + return [str(item) for item in sub_dict] + ids = [] + for category in ("sigma", "yara", "ioc"): + for item in sub_dict.get(category) or []: + ids.append(str(item)) + return ids + + +def get_pack_subfolder_rules(pack: dict) -> dict[str, list[str]]: + """Scan the pack's 'rules' subfolder and return a dict of {category: [rule_ids]}.""" + result = {"sigma": [], "yara": [], "ioc": []} + pack_path_str = pack.get("__path__") + if not pack_path_str: + return result + pack_dir = Path(pack_path_str).parent + rules_dir = pack_dir / "rules" + if not rules_dir.is_dir(): + return result + + # Build a map of filename to artifact ID from canonical rules + filename_to_id = {} + for art in load_all_artifacts(): + if art.id: + filename_to_id[(art.kind, art.path.name)] = art.id + + # 1. Sigma + sigma_dir = rules_dir / "sigma" + if sigma_dir.is_dir(): + for file in sorted(sigma_dir.glob("*")): + if file.is_file() and file.suffix in (".yml", ".yaml"): + if ("sigma", file.name) in filename_to_id: + result["sigma"].append(filename_to_id[("sigma", file.name)]) + else: + try: + doc = yaml.safe_load(file.read_text(encoding="utf-8")) or {} + rule_id = str(doc.get("id", "")).strip() + if rule_id: + result["sigma"].append(rule_id) + except Exception: + pass + + # 2. Yara + yara_dir = rules_dir / "yara" + if yara_dir.is_dir(): + for file in sorted(yara_dir.glob("*")): + if file.is_file() and file.suffix in (".yar", ".yara"): + if ("yara", file.name) in filename_to_id: + result["yara"].append(filename_to_id[("yara", file.name)]) + else: + try: + raw = file.read_text(encoding="utf-8") + meta_id = _YARA_META_ID_RE.search(raw) + name = _YARA_RULE_RE.search(raw) + rule_id = ( + meta_id.group(1) if meta_id else name.group(1) if name else "" + ).strip() + if rule_id: + result["yara"].append(rule_id) + except Exception: + pass + + # 3. IOC + ioc_dir = rules_dir / "ioc" + if ioc_dir.is_dir(): + ioc_rule_ids = set() + for file in ioc_dir.glob("*.txt"): + try: + content = file.read_text(encoding="utf-8") + for match in re.finditer(r"\brule=([a-zA-Z0-9_-]+)", content): + ioc_rule_ids.add(match.group(1)) + except Exception: + pass + result["ioc"] = sorted(list(ioc_rule_ids)) + + return result + + def resolve_pack_rules(pack: dict, by_id: dict[str, dict]) -> list[str]: """Return the ordered, de-duplicated artifact ids for a pack, including all transitively extended packs. Lower-level packs come first. + Applies the dictionary-based 'includes' and 'excludes' filtering. + Authoritative rules are loaded from the pack's rules subfolder if present, + otherwise falling back to 'has' under rules in the pack manifest. + Raises ValueError on a missing extends target or an extends cycle. """ - ordered: list[str] = [] - seen: set = set() - def visit(pack_id: str, stack: list[str]): + def visit(pack_id: str, stack: list[str]) -> list[str]: if pack_id in stack: raise ValueError(f"extends cycle: {' -> '.join(stack + [pack_id])}") if pack_id not in by_id: raise ValueError(f"unknown pack in extends: {pack_id}") node = by_id[pack_id] + + # 1. Resolve parent rules recursively + extended_rules = [] + extended_seen = set() for parent in node.get("extends", []) or []: - visit(parent, stack + [pack_id]) - for rule_id in node.get("rules", []) or []: - if rule_id not in seen: - seen.add(rule_id) - ordered.append(rule_id) - - visit(pack["id"], []) - return ordered + parent_resolved = visit(parent, stack + [pack_id]) + for r in parent_resolved: + if r not in extended_seen: + extended_seen.add(r) + extended_rules.append(r) + + # 2. Extract rules dictionary from manifest + rules_dict = node.get("rules") or {} + + # If rules is a list (old format), treat it as 'has' list of rules + if isinstance(rules_dict, list): + has_dict_list = rules_dict + includes_list = [] + excludes_list = [] + else: + has_dict_list = _get_rule_ids_from_dict(rules_dict.get("has")) + includes_list = _get_rule_ids_from_dict(rules_dict.get("includes")) + excludes_list = _get_rule_ids_from_dict(rules_dict.get("excludes")) + + # 3. Filter extended rules using 'includes' and 'excludes' + # If 'includes' key is specified (or rules is list, where we don't have includes), + # filter extended rules to only those in the include list. + if not isinstance(rules_dict, list) and "includes" in rules_dict: + include_ids = set(includes_list) + filtered_extended = [r for r in extended_rules if r in include_ids] + else: + filtered_extended = list(extended_rules) + + # If 'excludes' key is specified (or in rules), filter them out. + exclude_ids = set(excludes_list) + filtered_extended = [r for r in filtered_extended if r not in exclude_ids] + + # 4. Get 'has' rules. + # Check subfolders (authoritative) first if any rules are there, + # otherwise use manifest's 'has'. + subfolder_rules = get_pack_subfolder_rules(node) + sub_rule_ids = _get_rule_ids_from_dict(subfolder_rules) + if sub_rule_ids: + has_rules = sub_rule_ids + else: + has_rules = has_dict_list + + has_rules = [r for r in has_rules if r not in exclude_ids] + + # Combine everything + combined = filtered_extended + has_rules + + # De-duplicate preserving order + resolved = [] + seen = set() + for r in combined: + if r not in seen: + seen.add(r) + resolved.append(r) + return resolved + + return visit(pack["id"], []) diff --git a/tools/link_packs.py b/tools/link_packs.py new file mode 100644 index 0000000..7b0ab30 --- /dev/null +++ b/tools/link_packs.py @@ -0,0 +1,158 @@ +""" +Converts old pack.yml format into the new one and symlinks all +rules of the pack into the pack itself for easier zipping and usage. +""" + +from collections.abc import Iterable +from io import StringIO +from itertools import chain +from os.path import relpath +from pathlib import Path +from typing import TypedDict + +from ruamel.yaml import YAML +from yaml import safe_load + +DIR_ROOT = Path(__file__).parent.parent +DIR_PACKS = DIR_ROOT / "packs" +DIR_RULES = DIR_ROOT / "rules" + + +class Rules(TypedDict): + sigma: list[str] + yara: list[str] + ioc: list[str] + + +def find_by_content(root: Path, content: str, glob: str = "*") -> Iterable[Path]: + for file in root.rglob(glob): + if file.is_file(): + try: + text = file.read_text() + if content in text: + yield file + except Exception as e: + print(f"Error reading {file}: {e}") + + +def symlink(original: Path, target: Path): + if target.is_symlink(): + target.unlink() + target.parent.mkdir(parents=True, exist_ok=True) + link = Path(relpath(original.absolute(), start=target.parent.absolute())) + target.symlink_to(link) + + +def main(): + ryaml = YAML() + ryaml.preserve_quotes = True + ryaml.width = 120 + ryaml.indent(mapping=4, sequence=2, offset=2) + + for file_pack in DIR_PACKS.rglob("pack.yml"): + print(f"[*] Checking {file_pack}") + pack = ryaml.load(file_pack.read_text(encoding="utf-8")) + + modified = False + + rules = pack.get("rules") + if isinstance(rules, list): + print("[*] Converting rules list to dict") + rules_ids = [str(x) for x in rules] + rules_by_category: dict[str, list[str]] = { + "sigma": [], + "yara": [], + "ioc": [], + } + for rule_id in rules_ids: + rule_path = next( + chain( + find_by_content(DIR_RULES, rule_id, glob="*.yml"), + find_by_content(DIR_RULES, rule_id, glob="*.yaml"), + find_by_content(DIR_RULES, rule_id, glob="*.yar"), + ) + ) + print(f" [*] Found rule {rule_id} in {rule_path}") + rule_type = rule_path.relative_to(DIR_RULES).parts[0] + rules_by_category[rule_type].append(rule_id) + for key in [x for x in rules_by_category if not rules_by_category[x]]: + del rules_by_category[key] + pack["rules"] = {"has": rules_by_category} + modified = True + + sources = pack.get("sources") + if isinstance(sources, list): + print("[*] Converting sources list to dict") + pack["sources"] = {"manual": list(sources)} + modified = True + + if "schema" not in pack: + pack["schema"] = "v1.0.0" + modified = True + + if modified: + out = StringIO() + ryaml.dump(pack, out) + dumped = out.getvalue() + dumped_lines = [line.rstrip() for line in dumped.splitlines()] + file_pack.write_text("\n".join(dumped_lines) + "\n", encoding="utf-8") + pack = ryaml.load(file_pack.read_text(encoding="utf-8")) + + rules_dict = pack.get("rules") or {} + has_dict = rules_dict.get("has") or {} + + for category, rule_ids in has_dict.items(): + if category == "sigma": + for rule_id in rule_ids: + path_rule = next( + chain( + find_by_content(DIR_RULES, rule_id, glob="*.yml"), + find_by_content(DIR_RULES, rule_id, glob="*.yaml"), + ) + ) + path_rule_link = file_pack.parent / "rules" / category / path_rule.name + symlink(path_rule, path_rule_link) + print(f" [*] Linked {rule_id} to {path_rule_link}") + elif category == "yara": + for rule_id in rule_ids: + path_rule = next(chain(find_by_content(DIR_RULES, rule_id, glob="*.yar"))) + path_rule_link = file_pack.parent / "rules" / category / path_rule.name + symlink(path_rule, path_rule_link) + print(f" [*] Linked {rule_id} to {path_rule_link}") + elif category == "ioc": + iocs: dict[str, list[str]] = {} + for rule_id in rule_ids: + path_rule = next( + chain( + find_by_content(DIR_RULES, rule_id, glob="*.yml"), + find_by_content(DIR_RULES, rule_id, glob="*.yaml"), + ) + ) + rule = safe_load(path_rule.read_text()) + rule_severity = rule.get("severity") + for indicator_type, indicators in rule["indicators"].items(): + if indicator_type not in iocs: + iocs[indicator_type] = [] + for indicator in indicators: + if isinstance(indicator, dict): + ioc_value = indicator["value"] + ioc_comment = indicator.get("comment") + else: + ioc_value = indicator + ioc_comment = None + ioc_comment_parts: list[str] = [f"rule={rule_id}"] + if rule_severity: + ioc_comment_parts.append(f"severity={rule_severity}") + if ioc_comment: + ioc_comment_parts.append(f"comment={ioc_comment}") + ioc_comment_str = " ".join(ioc_comment_parts) + iocs[indicator_type].append(f"{ioc_value};{ioc_comment_str}") + for ioc_type, ioc_values in iocs.items(): + path_rule = file_pack.parent / "rules" / category / f"{ioc_type}.txt" + path_rule.parent.mkdir(parents=True, exist_ok=True) + path_rule.write_text("\n".join(ioc_values)) + print(f" [*] Written {len(ioc_values)} {ioc_type} IOCs to {path_rule}") + + +if __name__ == "__main__": + main() diff --git a/tools/validate.py b/tools/validate.py index 25a631d..cc81af3 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -231,12 +231,12 @@ def check_ioc_set(art, rep: Report, schema_validate): "description", "os", "level", + "schema", "pack_schema_version", "requires_rustinel", "default", "status", "extends", - "rules", ] @@ -255,6 +255,8 @@ def check_packs(packs, artifacts, rep: Report): for field in REQUIRED_PACK_FIELDS: if field not in pack: rep.error(where, f"missing required field '{field}'") + if pack.get("schema") != "v1.0.0": + rep.error(where, "schema must be 'v1.0.0' for v1") if pack.get("pack_schema_version") != 1: rep.error(where, "pack_schema_version must be 1 for v1") if not pack.get("license"): @@ -264,7 +266,20 @@ def check_packs(packs, artifacts, rep: Report): schema_validate(pack, where, rep) # Referential integrity: rules and extends must resolve. - for rule_id in pack.get("rules", []) or []: + rules_dict = pack.get("rules") or {} + rule_ids_to_check = [] + if isinstance(rules_dict, list): + rule_ids_to_check = rules_dict + elif isinstance(rules_dict, dict): + for key in ("has", "includes", "excludes"): + sub = rules_dict.get(key) or {} + if isinstance(sub, dict): + for cat in ("sigma", "yara", "ioc"): + rule_ids_to_check.extend(sub.get(cat) or []) + elif isinstance(sub, list): + rule_ids_to_check.extend(sub) + + for rule_id in rule_ids_to_check: if rule_id not in artifact_index: rep.error(where, f"references unknown artifact id '{rule_id}'") try: diff --git a/uv.lock b/uv.lock index c09fd56..fcb91e8 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,10 @@ version = 1 revision = 3 requires-python = ">=3.12" +[options] +exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer-span = "P3D" + [[package]] name = "attrs" version = "26.1.0" @@ -208,6 +212,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/cb/966040123eb102371559746908ef2c9471f4d43e17ec9a645a2258dab64b/rpds_py-2026.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:90bd6630002a1c7f09e7843dd79f0d24f3d2897cc25a753480917865d14f15b3", size = 225441, upload-time = "2026-05-28T12:01:51.408Z" }, ] +[[package]] +name = "ruamel-yaml" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" }, +] + [[package]] name = "ruff" version = "0.15.15" @@ -240,6 +253,7 @@ source = { virtual = "." } dependencies = [ { name = "jsonschema" }, { name = "pyyaml" }, + { name = "ruamel-yaml" }, { name = "yara-x" }, ] @@ -253,6 +267,7 @@ dev = [ requires-dist = [ { name = "jsonschema", specifier = ">=4.0" }, { name = "pyyaml", specifier = ">=6.0" }, + { name = "ruamel-yaml", specifier = ">=0.18.0" }, { name = "yara-x", specifier = ">=1.0" }, ] @@ -264,27 +279,27 @@ dev = [ [[package]] name = "ty" -version = "0.0.43" +version = "0.0.42" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/37/4ec04de0659b93be37d956dfceca13b1ecab9c959f28d8a1d5e514603f36/ty-0.0.43.tar.gz", hash = "sha256:ea4cff50548f2a1877e848d3abe9e293cde8ab94757a7eb93fc0d4013f98be8e", size = 5798429, upload-time = "2026-06-04T00:52:10.013Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/91/5b5ec4ed8721c18be8d9611778d7c07723cd755676f03b41bf0ea0caa5d3/ty-0.0.42.tar.gz", hash = "sha256:70f5553ac678fc63558d4d77b08a18a68a228f44be2a2fe1afc3f5988db662e7", size = 5769116, upload-time = "2026-06-01T19:40:32.869Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/74/1916026a78f20019a2f03adbd6fb4430ddb7ce1e52c2e17a90856a6d192e/ty-0.0.43-py3-none-linux_armv6l.whl", hash = "sha256:3bf70f5446480562bf6c9f639df4b5cb60716b8f8d1a6b8e5811d5c7eccd8bf2", size = 11598153, upload-time = "2026-06-04T00:52:20.646Z" }, - { url = "https://files.pythonhosted.org/packages/b9/af/58bb0089d2635216c8fa6612dd486a3f986d0ab1c46a41527ab95e57f0e3/ty-0.0.43-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7184741f8b15425a1bc64b950ad005cb353573288ac0e8a04f5481ceb3832596", size = 11357811, upload-time = "2026-06-04T00:52:24.683Z" }, - { url = "https://files.pythonhosted.org/packages/d6/9c/32c6b14f3feddf87b59c7a50709e2b3da408258f2f583f05575f77bc8f7b/ty-0.0.43-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8c306379ca9a35f6ae5270fe9bda7af4b46d91822725a2586d78c8b9b5493b62", size = 10772024, upload-time = "2026-06-04T00:52:14.312Z" }, - { url = "https://files.pythonhosted.org/packages/09/fa/98aa4a74bd00cd5efc424923cd1daffbf1e40a0338041cafb203379d746f/ty-0.0.43-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d624b884c9c1fd244ad2a5f026364e7162a22b3f537025941ada2e363e676414", size = 11291034, upload-time = "2026-06-04T00:52:37.249Z" }, - { url = "https://files.pythonhosted.org/packages/b5/db/4de086c38ce96dcada2bd451f43171d2c237f96d8ed19a1ea8fe51bb8ef4/ty-0.0.43-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:281fc4c00fbc196045141faa085055bddc58846b04a2800204701415a1b9c6aa", size = 11364724, upload-time = "2026-06-04T00:52:33.138Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d3/e3cd8e3233a6fd8362a49aa025b79e9f40151a2a86d811ace154c6eb7445/ty-0.0.43-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f57d6cc28de89024b48d1788e4758c05299d5749d4a51c02e71ac655ec23d9a5", size = 11890555, upload-time = "2026-06-04T00:52:22.711Z" }, - { url = "https://files.pythonhosted.org/packages/80/7b/6f46d444e8241606bbde098df3dca93f2ec0b834a42055db85ee7d33646f/ty-0.0.43-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a1d6ad6c5e7792c7eac0a01e550f2c2004462e01a64a91ea1636aba6fef6e71", size = 12450968, upload-time = "2026-06-04T00:52:28.94Z" }, - { url = "https://files.pythonhosted.org/packages/4a/e1/79fbe51f2e4b9d8347f2013cd7ed0b63f3b499038c02dc0357e9b28a3a47/ty-0.0.43-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:66d474395d7635fb618bdbb58b4e3360259a2056d0a5621b82754b9da2cd8a04", size = 12064187, upload-time = "2026-06-04T00:52:12.039Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3f/c758a3a8df5b90d331f2b60c8f16021ee64d75e78f99d67cc4efc9bf5f4b/ty-0.0.43-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2663a0003a8b60fb98db7f6f6e673df80b21d0fe3a9868a26fb06b4e049b6fc4", size = 11943208, upload-time = "2026-06-04T00:52:31.14Z" }, - { url = "https://files.pythonhosted.org/packages/54/5f/f516442749cf1b45ca6720a5d41df2738a486ed9ace774c03d515db89084/ty-0.0.43-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d5a6c352d374d889189d5ec82b54b26a5885f769f7b7787f7f875500dcb8673e", size = 12143572, upload-time = "2026-06-04T00:52:18.457Z" }, - { url = "https://files.pythonhosted.org/packages/b7/bf/0d83c7f43bf4c10f3678bfe7d938e51c445298c7b923f155c5204730c2df/ty-0.0.43-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e7dbbeedfad3ca250d74fcc355fa9ab6b38d2a17f22d6304f615716939dbbb27", size = 11279355, upload-time = "2026-06-04T00:52:26.726Z" }, - { url = "https://files.pythonhosted.org/packages/3e/de/a6c978bef6d9e949f79f4782d9e4ee4df0893713e73b055d84c1a5116b9a/ty-0.0.43-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:24b18a0273ee46154996cfcfa27438f851f440c925587ec200df6f98dffe67d3", size = 11408412, upload-time = "2026-06-04T00:52:35.282Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b1/d13857c23867f0f76b92e38e5841c64ca5e76dc5d4bf27f52cb81d8ab685/ty-0.0.43-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2ef681951520d692b7e9c0b5e56aacf4f98ccae47cf6ffccaf2c7b6b33dc226e", size = 11541709, upload-time = "2026-06-04T00:52:16.451Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f1/cd6afc6f6a687e238bf5e12189f7920e81a0bdef6c3dba4c784ef140f7d9/ty-0.0.43-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2af105de7437143aa4676b28016b5bee661aaaa4eff52be5867fb25119641ceb", size = 12041266, upload-time = "2026-06-04T00:52:43.541Z" }, - { url = "https://files.pythonhosted.org/packages/bd/ba/51ca7c3335da2b8d0a3e477fa4986be9f4a53b05bfab862967d8d2e6ca60/ty-0.0.43-py3-none-win32.whl", hash = "sha256:e4773115b0d6486ee30f1657fc8bdffe7e3a3f5300ab77ef2495da6e83e4694f", size = 10858724, upload-time = "2026-06-04T00:52:07.843Z" }, - { url = "https://files.pythonhosted.org/packages/9f/29/5d80453e5f7c520145fa058851da87230dbd7ca761a7675447a9fe504e0b/ty-0.0.43-py3-none-win_amd64.whl", hash = "sha256:48d3545094a4ae6395492c7e6ac90550fce969e0ed2815fbf8c5da9756676b7d", size = 11976157, upload-time = "2026-06-04T00:52:41.438Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ed/befe5a543e5b95e754ed38ee95239e44efda9bc5f578db4ac1bc8dd758d6/ty-0.0.43-py3-none-win_arm64.whl", hash = "sha256:740ca33d7f75f655a4e7d475bc42dfb825c13219bb073fad30fcc04d35790c74", size = 11308680, upload-time = "2026-06-04T00:52:39.233Z" }, + { url = "https://files.pythonhosted.org/packages/d4/7c/2df5136ad7c0db69a3973b0b19da8f52bfacdc453c7dffc832d1bf7d23ff/ty-0.0.42-py3-none-linux_armv6l.whl", hash = "sha256:c08a0066610c13627b7d7ad758adb96ca99685791e641eb26837e20803851c53", size = 11544141, upload-time = "2026-06-01T19:40:41.065Z" }, + { url = "https://files.pythonhosted.org/packages/0a/82/96cf406d39d8976e825361a27e332224445812793060ac9506d8a5d32b39/ty-0.0.42-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3e944ee4e3d5cdaf70e4ea87f9dd474cc3db612837b50a3ce57afa8da400ecc2", size = 11283538, upload-time = "2026-06-01T19:40:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/dc/fe/813b60b9332df835c16c05859ff5aac1896593d01b638ba0e461ede415ac/ty-0.0.42-py3-none-macosx_11_0_arm64.whl", hash = "sha256:603085306e4aac2ce592b39119a4b49ebf8b780cd394e2cfc7dbf3fd8228f954", size = 10711874, upload-time = "2026-06-01T19:40:28.77Z" }, + { url = "https://files.pythonhosted.org/packages/07/64/1f609265be0302ce0f51aa03a27636d018947a76100ff1405258d8445e6c/ty-0.0.42-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58f17834d7f078c49326a01111a5aac16c979a774b98cdfd8e2350068316676", size = 11213021, upload-time = "2026-06-01T19:40:45.01Z" }, + { url = "https://files.pythonhosted.org/packages/88/c0/f147b2fde7cd01b5f77682937e85a5cdfe35f48b3f1d0f41021024cbd927/ty-0.0.42-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e6ed1027f313202c5c74e376007d1eb5d214494299beb0ea047078b8ad307d40", size = 11321604, upload-time = "2026-06-01T19:40:55.164Z" }, + { url = "https://files.pythonhosted.org/packages/a9/72/74a5e68a9bd194681f15c4aac7a0dfab378e76e7a107e8ecd93971a22377/ty-0.0.42-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:063838d2360c1d2c065b45ca76a56ecd6df07fff6570813e74183236559e16d9", size = 11802178, upload-time = "2026-06-01T19:40:19.558Z" }, + { url = "https://files.pythonhosted.org/packages/3c/9f/06a31dc9cc91faa2cb8a4bf2f0ba5f7a9a96a4828fb8434338682954ca86/ty-0.0.42-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3f9ed508dfae4cbc943d7766324dd9c57ac8302c8543505fc29cae8ed425fe9", size = 12358436, upload-time = "2026-06-01T19:40:48.968Z" }, + { url = "https://files.pythonhosted.org/packages/06/33/a5bb1afcb671e0b9197f007264682b22f1063bf0e83c151eeaf9958f9047/ty-0.0.42-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fe1eb7d98472ac56ac19fec51c6ed8fe56d86ea0d232a11a127e8c62c882a66", size = 11997849, upload-time = "2026-06-01T19:40:42.951Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8d/560e4ec4c2f69e68fa094bb93cd19b5eb92c9732d2e0f5e7cc3accde84c4/ty-0.0.42-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de75f9e78bfa81209f2f297528758977cfd4518ba35ef45a0acb516c892a27a5", size = 11869087, upload-time = "2026-06-01T19:40:24.38Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/3794db15199c03eda60961690046a56c4fbf5d8ef073c82fe2402c851b8c/ty-0.0.42-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c63281f2f1d4df339117fcd4a6dfe17cb999f84eafe707b30e9ebbe26f0bb54a", size = 12059000, upload-time = "2026-06-01T19:40:34.982Z" }, + { url = "https://files.pythonhosted.org/packages/98/9a/02b61cc65ecbd90f18bd02178845c5b756c78fb92643571e77df52c6eed8/ty-0.0.42-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:19e3856477f25255f772851fa7f16f5356c4e1927324d074d49c7bc9a9b211e1", size = 11195698, upload-time = "2026-06-01T19:40:37.109Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d6/3927335c956b06a806269dcd2e5b46bc4284b0a95ecc6b0246094a1de28c/ty-0.0.42-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2f0f4acac9028264cee5ea0b88229df0b9b2586fc917dbadb6ee35a0e99e8b06", size = 11353487, upload-time = "2026-06-01T19:40:47.035Z" }, + { url = "https://files.pythonhosted.org/packages/d6/87/79e7ae4f5f9fb3bea5f3cfac2b4f8c60e905f13962e3c0d97f8b51a5bff6/ty-0.0.42-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ae244c84e30fdf2bb1a3cbf2b973da8aa535e57c701f12db44b2939604586c04", size = 11463474, upload-time = "2026-06-01T19:40:30.812Z" }, + { url = "https://files.pythonhosted.org/packages/49/f9/73305ead1b3ccc3d79c04258877db2cd7908c6a6c2060d4e598deca384d2/ty-0.0.42-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2430434f4a52bec0da552ff6a061dcc1c5d11973259248679a1146d776c12f37", size = 11961710, upload-time = "2026-06-01T19:40:39.056Z" }, + { url = "https://files.pythonhosted.org/packages/e1/04/52b7325dad8d1a86653f90240208f3e3657bed5e3c8144eed367a0f736b0/ty-0.0.42-py3-none-win32.whl", hash = "sha256:984a55c2fe63b40dac03f5a144b99033c7ed720eb7611787e3f0bd49af8dcf12", size = 10783897, upload-time = "2026-06-01T19:40:22.189Z" }, + { url = "https://files.pythonhosted.org/packages/94/d2/3d2d61255c76c0843766f00b39290115c23cc1cd4fcb0471d84a48f482f1/ty-0.0.42-py3-none-win_amd64.whl", hash = "sha256:f7afd81b10b377d9d4ce6aad355a4f47fd37d47f443118c01ca6e79d46fe6608", size = 11878640, upload-time = "2026-06-01T19:40:50.903Z" }, + { url = "https://files.pythonhosted.org/packages/b4/91/1eb0c1e3d558707ead7424f8bfd89b58f42e576714cbed7ad46dcceef34a/ty-0.0.42-py3-none-win_arm64.whl", hash = "sha256:4068c24b0b264fc9f1901e06b97988a041fcaa36c90f18d7747f05124701c7b3", size = 11202335, upload-time = "2026-06-01T19:40:53.155Z" }, ] [[package]] From 60c659514a68fb7cb64ac2607218f4535600fe82 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Fri, 5 Jun 2026 09:56:22 +0200 Subject: [PATCH 02/13] feat(build): make the zip match the rules folder structure --- pyproject.toml | 3 ++ tools/build_packs.py | 20 +++---- tools/upload_ragest.py | 87 ++++++++++++++++++++++++++++++ uv.lock | 119 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 tools/upload_ragest.py diff --git a/pyproject.toml b/pyproject.toml index b1eb717..139958f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,9 @@ dev = [ "ruff>=0.15", "ty>=0.0.1", ] +radegast = [ + "requests>=2.34.2", +] # This repo ships detection content plus helper scripts under tools/; it is not a # distributable Python package, so uv treats it as a non-package project (no build diff --git a/tools/build_packs.py b/tools/build_packs.py index 9a167cf..905207f 100644 --- a/tools/build_packs.py +++ b/tools/build_packs.py @@ -4,12 +4,12 @@ For each pack manifest this produces, under dist/: - dist// flat materialized pack folder == what the pack.yml Rustinel engine loads directly: - rules/sigma/.yml -> scanner.sigma_rules_path - rules/yara/.yar -> scanner.yara_rules_path - rules/ioc/hashes.txt -> [ioc].hashes_path - rules/ioc/ips.txt -> [ioc].ips_path - rules/ioc/domains.txt -> [ioc].domains_path - rules/ioc/paths_regex.txt -> [ioc].paths_regex_path + sigma/.yml -> scanner.sigma_rules_path + yara/.yar -> scanner.yara_rules_path + ioc/hashes.txt -> [ioc].hashes_path + ioc/ips.txt -> [ioc].ips_path + ioc/domains.txt -> [ioc].domains_path + ioc/paths_regex.txt -> [ioc].paths_regex_path - dist/-.zip zipped pack (one folder = one pack = one zip) - dist/index.json catalog of all packs + checksums + engine paths @@ -77,8 +77,8 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: out_dir = lib.DIST_DIR / pack_id if out_dir.exists(): shutil.rmtree(out_dir) - (out_dir / "rules" / "sigma").mkdir(parents=True, exist_ok=True) - (out_dir / "rules" / "yara").mkdir(parents=True, exist_ok=True) + (out_dir / "sigma").mkdir(parents=True, exist_ok=True) + (out_dir / "yara").mkdir(parents=True, exist_ok=True) resolved_ids = lib.resolve_pack_rules(pack, by_id) copied = [] @@ -92,7 +92,7 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: ) if art.kind in ("sigma", "yara"): - dest = out_dir / "rules" / art.kind / art.path.name + dest = out_dir / art.kind / art.path.name shutil.copy2(art.path, dest) copied.append({"id": artifact_id, "kind": art.kind, "file": art.path.name}) elif art.kind == "ioc": @@ -105,7 +105,7 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: count += 1 copied.append({"id": artifact_id, "kind": "ioc", "indicators": count}) - ioc_counts = write_ioc_files(out_dir / "rules" / "ioc", ioc_acc) + ioc_counts = write_ioc_files(out_dir / "ioc", ioc_acc) # Write a clean manifest (drop internal __path__) plus build metadata. manifest = {k: v for k, v in pack.items() if not k.startswith("__")} diff --git a/tools/upload_ragest.py b/tools/upload_ragest.py new file mode 100644 index 0000000..bc6cb73 --- /dev/null +++ b/tools/upload_ragest.py @@ -0,0 +1,87 @@ +""" +Uploads built packages to Radegast EDR via API +""" + +from json import loads +from typing import TypedDict +from os import environ +from pathlib import Path + +from requests import get, post + +RADEGAST_URL = environ.get("RADEGAST_URL", "https://console.radegast.app") +RADEGAST_API_KEY = environ["RADEGAST_API_KEY"] +DIR_ROOT = Path(__file__).parent.parent +DIR_DIST = DIR_ROOT / "dist" +FILE_INDEX = DIR_DIST / "index.json" + + +class RadegastPack(TypedDict): + id: int + pack_id: str + newest_version: str | None + + +def fetch_radegast_packs() -> list[RadegastPack]: + r = get(f"{RADEGAST_URL}/api/v1/packs/") + r.raise_for_status() + packs: list[RadegastPack] = [RadegastPack( + id=x["id"], + pack_id=x["pack_id"], + newest_version=None + ) for x in r.json()] + for pack in packs: + r = get(f"{RADEGAST_URL}/api/v1/packs/{pack['id']}/versions", headers={"X-API-Key": RADEGAST_API_KEY}) + r.raise_for_status() + versions = r.json() + for version in versions: + pack["newest_version"] = version["version"] + break + return packs + + +def main() -> None: + remote_packs = fetch_radegast_packs() + print(f"[*] Fetched {len(remote_packs)} Radegast packs") + + packs = loads(FILE_INDEX.read_text())['packs'] + for pack in packs: + pack_id = f"rustinel-{pack['id']}" + pack_name = f"Rustinel: {pack['name']}" + pack_version = pack['version'] + file_pack = DIR_DIST / f"{pack['artifact']}" + assert file_pack.exists(), f"Pack file not found: {file_pack}" + + remote_pack = next((p for p in remote_packs if p['pack_id'] == pack_id), None) + if remote_pack is None: + print(f"[*] Creating new pack {pack_id} -- {pack_name}") + r = post( + f"{RADEGAST_URL}/api/v1/packs/", + headers={"X-API-Key": RADEGAST_API_KEY}, + json={ + "pack_id": pack_id, + "name": pack_name, + "description": f"Rustinel pack from https://github.com/Karib0u/rustinel-rules/", + } + ) + r.raise_for_status() + remote_pack = RadegastPack( + id=r.json()["id"], + pack_id=r.json()["pack_id"], + newest_version=None + ) + + if remote_pack["newest_version"] != pack_version: + print(f"[*] Uploading pack {pack_id} version {pack_version} to Radegast") + with file_pack.open("rb") as f: + r = post( + f"{RADEGAST_URL}/api/v1/packs/{remote_pack['id']}/versions", + headers={"X-API-Key": RADEGAST_API_KEY}, + files={"file": (file_pack.name, f, "application/zip")}, + params={"version": pack_version}, + ) + r.raise_for_status() + + +if __name__ == "__main__": + main() diff --git a/uv.lock b/uv.lock index fcb91e8..c4eb308 100644 --- a/uv.lock +++ b/uv.lock @@ -15,6 +15,97 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "idna" +version = "3.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, +] + [[package]] name = "jsonschema" version = "4.26.0" @@ -102,6 +193,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + [[package]] name = "rpds-py" version = "2026.5.1" @@ -262,6 +368,9 @@ dev = [ { name = "ruff" }, { name = "ty" }, ] +radegast = [ + { name = "requests" }, +] [package.metadata] requires-dist = [ @@ -276,6 +385,7 @@ dev = [ { name = "ruff", specifier = ">=0.15" }, { name = "ty", specifier = ">=0.0.1" }, ] +radegast = [{ name = "requests", specifier = ">=2.34.2" }] [[package]] name = "ty" @@ -311,6 +421,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + [[package]] name = "yara-x" version = "1.17.0" From aa8b0c6b07d79a98bc13774815fc0385c80eb50b Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Fri, 5 Jun 2026 11:23:14 +0200 Subject: [PATCH 03/13] chore: flatten rules structure --- .../rules/sigma/file_event_lnx_cron_persistence.yml | 1 - .../sigma/file_event_lnx_shell_profile_persistence.yml | 1 - .../rules/sigma/file_event_lnx_systemd_persistence.yml | 1 - .../rules/sigma/proc_creation_lnx_download_pipe_shell.yml | 1 - .../sigma/proc_creation_lnx_exec_from_world_writable.yml | 1 - .../advanced/sigma/file_event_lnx_cron_persistence.yml | 1 + .../sigma/file_event_lnx_shell_profile_persistence.yml | 1 + .../advanced/sigma/file_event_lnx_systemd_persistence.yml | 1 + .../sigma/proc_creation_lnx_download_pipe_shell.yml | 1 + .../sigma/proc_creation_lnx_exec_from_world_writable.yml | 1 + packs/linux/essential/{rules => }/ioc/hashes.txt | 0 .../rules/sigma/file_event_lnx_ldso_preload_hijack.yml | 1 - .../rules/sigma/file_event_lnx_ssh_authorized_keys.yml | 1 - .../rules/sigma/file_event_lnx_sshd_config_tamper.yml | 1 - .../essential/rules/sigma/file_event_lnx_sudoers_tamper.yml | 1 - .../sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml | 1 - .../sigma/proc_creation_lnx_webserver_spawns_shell.yml | 1 - .../linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar | 1 - .../essential/sigma/file_event_lnx_ldso_preload_hijack.yml | 1 + .../essential/sigma/file_event_lnx_ssh_authorized_keys.yml | 1 + .../essential/sigma/file_event_lnx_sshd_config_tamper.yml | 1 + .../linux/essential/sigma/file_event_lnx_sudoers_tamper.yml | 1 + .../sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml | 1 + .../sigma/proc_creation_lnx_webserver_spawns_shell.yml | 1 + packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar | 1 + .../rules/sigma/file_event_macos_launch_persistence.yml | 1 - .../sigma/proc_creation_macos_download_execute_cradle.yml | 1 - .../sigma/proc_creation_macos_exec_from_world_writable.yml | 1 - .../rules/sigma/proc_creation_macos_local_admin_account.yml | 1 - .../advanced/sigma/file_event_macos_launch_persistence.yml | 1 + .../sigma/proc_creation_macos_download_execute_cradle.yml | 1 + .../sigma/proc_creation_macos_exec_from_world_writable.yml | 1 + .../sigma/proc_creation_macos_local_admin_account.yml | 1 + packs/macos/essential/{rules => }/ioc/hashes.txt | 0 .../rules/sigma/proc_creation_macos_gatekeeper_bypass.yml | 1 - .../sigma/proc_creation_macos_keychain_dump_security.yml | 1 - .../sigma/proc_creation_macos_osascript_admin_prompt.yml | 1 - .../proc_creation_macos_shell_dev_tcp_reverse_shell.yml | 1 - .../essential/rules/yara/macos_susp_coinminer_strings.yar | 1 - .../sigma/proc_creation_macos_gatekeeper_bypass.yml | 1 + .../sigma/proc_creation_macos_keychain_dump_security.yml | 1 + .../sigma/proc_creation_macos_osascript_admin_prompt.yml | 1 + .../proc_creation_macos_shell_dev_tcp_reverse_shell.yml | 1 + packs/macos/essential/yara/macos_susp_coinminer_strings.yar | 1 + .../proc_creation_win_susp_local_admin_account_added.yml | 1 - .../rules/sigma/proc_creation_win_susp_rundll32_no_args.yml | 1 - .../rules/sigma/proc_creation_win_susp_schtasks_create.yml | 1 - .../sigma/proc_creation_win_susp_wmic_process_create.yml | 1 - .../sigma/ps_script_win_susp_powershell_download_cradle.yml | 1 - .../rules/sigma/registry_event_win_run_key_persistence.yml | 1 - .../sigma/service_creation_win_susp_service_binary.yml | 1 - .../proc_creation_win_susp_local_admin_account_added.yml | 1 + .../sigma/proc_creation_win_susp_rundll32_no_args.yml | 1 + .../sigma/proc_creation_win_susp_schtasks_create.yml | 1 + .../sigma/proc_creation_win_susp_wmic_process_create.yml | 1 + .../sigma/ps_script_win_susp_powershell_download_cradle.yml | 1 + .../sigma/registry_event_win_run_key_persistence.yml | 1 + .../sigma/service_creation_win_susp_service_binary.yml | 1 + packs/windows/essential/{rules => }/ioc/hashes.txt | 0 .../sigma/proc_creation_win_susp_encoded_powershell.yml | 1 - .../rules/sigma/proc_creation_win_susp_eventlog_clear.yml | 1 - .../sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml | 1 - .../sigma/proc_creation_win_susp_mshta_remote_execution.yml | 1 - .../sigma/proc_creation_win_susp_ntds_dit_extraction.yml | 1 - .../sigma/proc_creation_win_susp_office_spawning_shell.yml | 1 - .../sigma/proc_creation_win_susp_registry_hive_dump.yml | 1 - .../proc_creation_win_susp_regsvr32_remote_scriptlet.yml | 1 - .../sigma/proc_creation_win_susp_shadow_copy_deletion.yml | 1 - .../sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml | 1 - .../rules/sigma/registry_event_win_defender_tamper.yml | 1 - .../registry_event_win_wdigest_cleartext_credentials.yml | 1 - .../sigma/task_creation_win_susp_scheduled_task_action.yml | 1 - .../essential/rules/yara/win_susp_mimikatz_strings.yar | 1 - .../sigma/proc_creation_win_susp_encoded_powershell.yml | 1 + .../sigma/proc_creation_win_susp_eventlog_clear.yml | 1 + .../sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml | 1 + .../sigma/proc_creation_win_susp_mshta_remote_execution.yml | 1 + .../sigma/proc_creation_win_susp_ntds_dit_extraction.yml | 1 + .../sigma/proc_creation_win_susp_office_spawning_shell.yml | 1 + .../sigma/proc_creation_win_susp_registry_hive_dump.yml | 1 + .../proc_creation_win_susp_regsvr32_remote_scriptlet.yml | 1 + .../sigma/proc_creation_win_susp_shadow_copy_deletion.yml | 1 + .../sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml | 1 + .../essential/sigma/registry_event_win_defender_tamper.yml | 1 + .../registry_event_win_wdigest_cleartext_credentials.yml | 1 + .../sigma/task_creation_win_susp_scheduled_task_action.yml | 1 + packs/windows/essential/yara/win_susp_mimikatz_strings.yar | 1 + .../proc_creation_win_hunting_lolbin_certutil_download.yml | 1 - .../proc_creation_win_hunting_lolbin_certutil_download.yml | 1 + tools/link_packs.py | 6 +++--- 90 files changed, 46 insertions(+), 46 deletions(-) delete mode 120000 packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml delete mode 120000 packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml delete mode 120000 packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml delete mode 120000 packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml delete mode 120000 packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml create mode 120000 packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml create mode 120000 packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml create mode 120000 packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml create mode 120000 packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml create mode 120000 packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml rename packs/linux/essential/{rules => }/ioc/hashes.txt (100%) delete mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml delete mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml delete mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml delete mode 120000 packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml delete mode 120000 packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml delete mode 120000 packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml delete mode 120000 packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar create mode 120000 packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml create mode 120000 packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml create mode 120000 packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml create mode 120000 packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml create mode 120000 packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml create mode 120000 packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml create mode 120000 packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar delete mode 120000 packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml delete mode 120000 packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml delete mode 120000 packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml delete mode 120000 packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml create mode 120000 packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml create mode 120000 packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml create mode 120000 packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml create mode 120000 packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml rename packs/macos/essential/{rules => }/ioc/hashes.txt (100%) delete mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml delete mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml delete mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml delete mode 120000 packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml delete mode 120000 packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar create mode 120000 packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml create mode 120000 packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml create mode 120000 packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml create mode 120000 packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml create mode 120000 packs/macos/essential/yara/macos_susp_coinminer_strings.yar delete mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml delete mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml delete mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml delete mode 120000 packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml delete mode 120000 packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml delete mode 120000 packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml delete mode 120000 packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml create mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml create mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml create mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml create mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml create mode 120000 packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml create mode 120000 packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml create mode 120000 packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml rename packs/windows/essential/{rules => }/ioc/hashes.txt (100%) delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml delete mode 120000 packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml delete mode 120000 packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml delete mode 120000 packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml delete mode 120000 packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml delete mode 120000 packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml create mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml create mode 120000 packs/windows/essential/sigma/registry_event_win_defender_tamper.yml create mode 120000 packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml create mode 120000 packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml create mode 120000 packs/windows/essential/yara/win_susp_mimikatz_strings.yar delete mode 120000 packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml create mode 120000 packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml diff --git a/packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml b/packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml deleted file mode 120000 index b04ec09..0000000 --- a/packs/linux/advanced/rules/sigma/file_event_lnx_cron_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/file_event_lnx_cron_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml b/packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml deleted file mode 120000 index c39ab31..0000000 --- a/packs/linux/advanced/rules/sigma/file_event_lnx_shell_profile_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/file_event_lnx_shell_profile_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml b/packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml deleted file mode 120000 index bcde891..0000000 --- a/packs/linux/advanced/rules/sigma/file_event_lnx_systemd_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/file_event_lnx_systemd_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml b/packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml deleted file mode 120000 index 499fe12..0000000 --- a/packs/linux/advanced/rules/sigma/proc_creation_lnx_download_pipe_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/proc_creation_lnx_download_pipe_shell.yml \ No newline at end of file diff --git a/packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml b/packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml deleted file mode 120000 index 3d47e05..0000000 --- a/packs/linux/advanced/rules/sigma/proc_creation_lnx_exec_from_world_writable.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/proc_creation_lnx_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml b/packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml new file mode 120000 index 0000000..7ec2e51 --- /dev/null +++ b/packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/file_event_lnx_cron_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml b/packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml new file mode 120000 index 0000000..6b51dea --- /dev/null +++ b/packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/file_event_lnx_shell_profile_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml b/packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml new file mode 120000 index 0000000..8c43806 --- /dev/null +++ b/packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/file_event_lnx_systemd_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml b/packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml new file mode 120000 index 0000000..07b3b11 --- /dev/null +++ b/packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/proc_creation_lnx_download_pipe_shell.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml b/packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml new file mode 120000 index 0000000..c91618a --- /dev/null +++ b/packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/proc_creation_lnx_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/ioc/hashes.txt b/packs/linux/essential/ioc/hashes.txt similarity index 100% rename from packs/linux/essential/rules/ioc/hashes.txt rename to packs/linux/essential/ioc/hashes.txt diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml b/packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml deleted file mode 120000 index 1f3e79d..0000000 --- a/packs/linux/essential/rules/sigma/file_event_lnx_ldso_preload_hijack.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/file_event_lnx_ldso_preload_hijack.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml b/packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml deleted file mode 120000 index a0ebdea..0000000 --- a/packs/linux/essential/rules/sigma/file_event_lnx_ssh_authorized_keys.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/file_event_lnx_ssh_authorized_keys.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml b/packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml deleted file mode 120000 index 853c59d..0000000 --- a/packs/linux/essential/rules/sigma/file_event_lnx_sshd_config_tamper.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/file_event_lnx_sshd_config_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml b/packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml deleted file mode 120000 index 26fc414..0000000 --- a/packs/linux/essential/rules/sigma/file_event_lnx_sudoers_tamper.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/file_event_lnx_sudoers_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml b/packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml deleted file mode 120000 index 00e1598..0000000 --- a/packs/linux/essential/rules/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml b/packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml deleted file mode 120000 index 052a209..0000000 --- a/packs/linux/essential/rules/sigma/proc_creation_lnx_webserver_spawns_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/linux/proc_creation_lnx_webserver_spawns_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar b/packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar deleted file mode 120000 index 51a2561..0000000 --- a/packs/linux/essential/rules/yara/lnx_susp_xmrig_coinminer.yar +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/yara/linux/lnx_susp_xmrig_coinminer.yar \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml b/packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml new file mode 120000 index 0000000..729935a --- /dev/null +++ b/packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/file_event_lnx_ldso_preload_hijack.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml b/packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml new file mode 120000 index 0000000..1309e0b --- /dev/null +++ b/packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/file_event_lnx_ssh_authorized_keys.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml b/packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml new file mode 120000 index 0000000..98ac5b3 --- /dev/null +++ b/packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/file_event_lnx_sshd_config_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml b/packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml new file mode 120000 index 0000000..30192b0 --- /dev/null +++ b/packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/file_event_lnx_sudoers_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml b/packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml new file mode 120000 index 0000000..02a965f --- /dev/null +++ b/packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml b/packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml new file mode 120000 index 0000000..89f700f --- /dev/null +++ b/packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml @@ -0,0 +1 @@ +../../../../rules/sigma/linux/proc_creation_lnx_webserver_spawns_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar b/packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar new file mode 120000 index 0000000..c6485ee --- /dev/null +++ b/packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar @@ -0,0 +1 @@ +../../../../rules/yara/linux/lnx_susp_xmrig_coinminer.yar \ No newline at end of file diff --git a/packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml b/packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml deleted file mode 120000 index 0ecced2..0000000 --- a/packs/macos/advanced/rules/sigma/file_event_macos_launch_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/file_event_macos_launch_persistence.yml \ No newline at end of file diff --git a/packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml b/packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml deleted file mode 120000 index 213f656..0000000 --- a/packs/macos/advanced/rules/sigma/proc_creation_macos_download_execute_cradle.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/proc_creation_macos_download_execute_cradle.yml \ No newline at end of file diff --git a/packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml b/packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml deleted file mode 120000 index c2e8400..0000000 --- a/packs/macos/advanced/rules/sigma/proc_creation_macos_exec_from_world_writable.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/proc_creation_macos_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml b/packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml deleted file mode 120000 index 9dbf442..0000000 --- a/packs/macos/advanced/rules/sigma/proc_creation_macos_local_admin_account.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/proc_creation_macos_local_admin_account.yml \ No newline at end of file diff --git a/packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml b/packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml new file mode 120000 index 0000000..9e04019 --- /dev/null +++ b/packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/file_event_macos_launch_persistence.yml \ No newline at end of file diff --git a/packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml b/packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml new file mode 120000 index 0000000..a653ec2 --- /dev/null +++ b/packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/proc_creation_macos_download_execute_cradle.yml \ No newline at end of file diff --git a/packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml b/packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml new file mode 120000 index 0000000..26a850b --- /dev/null +++ b/packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/proc_creation_macos_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml b/packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml new file mode 120000 index 0000000..ac8e8b3 --- /dev/null +++ b/packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/proc_creation_macos_local_admin_account.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/ioc/hashes.txt b/packs/macos/essential/ioc/hashes.txt similarity index 100% rename from packs/macos/essential/rules/ioc/hashes.txt rename to packs/macos/essential/ioc/hashes.txt diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml deleted file mode 120000 index 17c943c..0000000 --- a/packs/macos/essential/rules/sigma/proc_creation_macos_gatekeeper_bypass.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/proc_creation_macos_gatekeeper_bypass.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml deleted file mode 120000 index 03b9b46..0000000 --- a/packs/macos/essential/rules/sigma/proc_creation_macos_keychain_dump_security.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/proc_creation_macos_keychain_dump_security.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml deleted file mode 120000 index 4614e8d..0000000 --- a/packs/macos/essential/rules/sigma/proc_creation_macos_osascript_admin_prompt.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/proc_creation_macos_osascript_admin_prompt.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml b/packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml deleted file mode 120000 index 971d104..0000000 --- a/packs/macos/essential/rules/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/macos/proc_creation_macos_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar b/packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar deleted file mode 120000 index b9d4c18..0000000 --- a/packs/macos/essential/rules/yara/macos_susp_coinminer_strings.yar +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/yara/macos/macos_susp_coinminer_strings.yar \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml b/packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml new file mode 120000 index 0000000..365b749 --- /dev/null +++ b/packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/proc_creation_macos_gatekeeper_bypass.yml \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml b/packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml new file mode 120000 index 0000000..5c8bc62 --- /dev/null +++ b/packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/proc_creation_macos_keychain_dump_security.yml \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml b/packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml new file mode 120000 index 0000000..388946f --- /dev/null +++ b/packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/proc_creation_macos_osascript_admin_prompt.yml \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml b/packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml new file mode 120000 index 0000000..fd21d85 --- /dev/null +++ b/packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml @@ -0,0 +1 @@ +../../../../rules/sigma/macos/proc_creation_macos_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/macos/essential/yara/macos_susp_coinminer_strings.yar b/packs/macos/essential/yara/macos_susp_coinminer_strings.yar new file mode 120000 index 0000000..b596ad2 --- /dev/null +++ b/packs/macos/essential/yara/macos_susp_coinminer_strings.yar @@ -0,0 +1 @@ +../../../../rules/yara/macos/macos_susp_coinminer_strings.yar \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml deleted file mode 120000 index 4ef1112..0000000 --- a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_local_admin_account_added.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_local_admin_account_added.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml deleted file mode 120000 index 0c318d6..0000000 --- a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_rundll32_no_args.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_rundll32_no_args.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml deleted file mode 120000 index 3345751..0000000 --- a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_schtasks_create.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_schtasks_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml b/packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml deleted file mode 120000 index 6db059e..0000000 --- a/packs/windows/advanced/rules/sigma/proc_creation_win_susp_wmic_process_create.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_wmic_process_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml b/packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml deleted file mode 120000 index 582d9f7..0000000 --- a/packs/windows/advanced/rules/sigma/ps_script_win_susp_powershell_download_cradle.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/ps_script_win_susp_powershell_download_cradle.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml b/packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml deleted file mode 120000 index e433eaf..0000000 --- a/packs/windows/advanced/rules/sigma/registry_event_win_run_key_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/registry_event_win_run_key_persistence.yml \ No newline at end of file diff --git a/packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml b/packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml deleted file mode 120000 index 7dba46e..0000000 --- a/packs/windows/advanced/rules/sigma/service_creation_win_susp_service_binary.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/service_creation_win_susp_service_binary.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml new file mode 120000 index 0000000..203ae30 --- /dev/null +++ b/packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_local_admin_account_added.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml new file mode 120000 index 0000000..e0d124e --- /dev/null +++ b/packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_rundll32_no_args.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml new file mode 120000 index 0000000..8c622ce --- /dev/null +++ b/packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_schtasks_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml new file mode 120000 index 0000000..06a155d --- /dev/null +++ b/packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_wmic_process_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml b/packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml new file mode 120000 index 0000000..53f82d7 --- /dev/null +++ b/packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/ps_script_win_susp_powershell_download_cradle.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml b/packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml new file mode 120000 index 0000000..4a256fc --- /dev/null +++ b/packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/registry_event_win_run_key_persistence.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml b/packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml new file mode 120000 index 0000000..f6bc5d4 --- /dev/null +++ b/packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/service_creation_win_susp_service_binary.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/ioc/hashes.txt b/packs/windows/essential/ioc/hashes.txt similarity index 100% rename from packs/windows/essential/rules/ioc/hashes.txt rename to packs/windows/essential/ioc/hashes.txt diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml deleted file mode 120000 index 1d6e08f..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_encoded_powershell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_encoded_powershell.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml deleted file mode 120000 index 6ca1d9a..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_eventlog_clear.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_eventlog_clear.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml deleted file mode 120000 index 1092aaf..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_lsass_comsvcs_minidump.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml deleted file mode 120000 index 90caeea..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_mshta_remote_execution.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_mshta_remote_execution.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml deleted file mode 120000 index f1d4e8e..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_ntds_dit_extraction.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_ntds_dit_extraction.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml deleted file mode 120000 index 3e9b3cf..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_office_spawning_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_office_spawning_shell.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml deleted file mode 120000 index ae26533..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_registry_hive_dump.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_registry_hive_dump.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml deleted file mode 120000 index 2eaf7ca..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_regsvr32_remote_scriptlet.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml deleted file mode 120000 index 3d34cbd..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_shadow_copy_deletion.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_shadow_copy_deletion.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml b/packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml deleted file mode 120000 index 6b7d9e0..0000000 --- a/packs/windows/essential/rules/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_susp_uac_bypass_autoelevate.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml b/packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml deleted file mode 120000 index 0ade0a8..0000000 --- a/packs/windows/essential/rules/sigma/registry_event_win_defender_tamper.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/registry_event_win_defender_tamper.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml b/packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml deleted file mode 120000 index 6c28587..0000000 --- a/packs/windows/essential/rules/sigma/registry_event_win_wdigest_cleartext_credentials.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/registry_event_win_wdigest_cleartext_credentials.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml b/packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml deleted file mode 120000 index e127a9c..0000000 --- a/packs/windows/essential/rules/sigma/task_creation_win_susp_scheduled_task_action.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/task_creation_win_susp_scheduled_task_action.yml \ No newline at end of file diff --git a/packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar b/packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar deleted file mode 120000 index c7a2105..0000000 --- a/packs/windows/essential/rules/yara/win_susp_mimikatz_strings.yar +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/yara/windows/win_susp_mimikatz_strings.yar \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml b/packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml new file mode 120000 index 0000000..f18d339 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_encoded_powershell.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml b/packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml new file mode 120000 index 0000000..044d5e6 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_eventlog_clear.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml b/packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml new file mode 120000 index 0000000..ea81556 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_lsass_comsvcs_minidump.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml b/packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml new file mode 120000 index 0000000..a58b3f4 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_mshta_remote_execution.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml b/packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml new file mode 120000 index 0000000..6174ef5 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_ntds_dit_extraction.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml b/packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml new file mode 120000 index 0000000..5fef58a --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_office_spawning_shell.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml b/packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml new file mode 120000 index 0000000..7fa8725 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_registry_hive_dump.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml b/packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml new file mode 120000 index 0000000..f34d116 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_regsvr32_remote_scriptlet.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml b/packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml new file mode 120000 index 0000000..ae20964 --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_shadow_copy_deletion.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml b/packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml new file mode 120000 index 0000000..3bf5a2d --- /dev/null +++ b/packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_susp_uac_bypass_autoelevate.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/registry_event_win_defender_tamper.yml b/packs/windows/essential/sigma/registry_event_win_defender_tamper.yml new file mode 120000 index 0000000..25b9d34 --- /dev/null +++ b/packs/windows/essential/sigma/registry_event_win_defender_tamper.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/registry_event_win_defender_tamper.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml b/packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml new file mode 120000 index 0000000..4ae0037 --- /dev/null +++ b/packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/registry_event_win_wdigest_cleartext_credentials.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml b/packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml new file mode 120000 index 0000000..cf4bc90 --- /dev/null +++ b/packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/task_creation_win_susp_scheduled_task_action.yml \ No newline at end of file diff --git a/packs/windows/essential/yara/win_susp_mimikatz_strings.yar b/packs/windows/essential/yara/win_susp_mimikatz_strings.yar new file mode 120000 index 0000000..81cbd18 --- /dev/null +++ b/packs/windows/essential/yara/win_susp_mimikatz_strings.yar @@ -0,0 +1 @@ +../../../../rules/yara/windows/win_susp_mimikatz_strings.yar \ No newline at end of file diff --git a/packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml b/packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml deleted file mode 120000 index 3779e26..0000000 --- a/packs/windows/hunting/rules/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../../rules/sigma/windows/proc_creation_win_hunting_lolbin_certutil_download.yml \ No newline at end of file diff --git a/packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml b/packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml new file mode 120000 index 0000000..2d63771 --- /dev/null +++ b/packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml @@ -0,0 +1 @@ +../../../../rules/sigma/windows/proc_creation_win_hunting_lolbin_certutil_download.yml \ No newline at end of file diff --git a/tools/link_packs.py b/tools/link_packs.py index 7b0ab30..609783c 100644 --- a/tools/link_packs.py +++ b/tools/link_packs.py @@ -110,13 +110,13 @@ def main(): find_by_content(DIR_RULES, rule_id, glob="*.yaml"), ) ) - path_rule_link = file_pack.parent / "rules" / category / path_rule.name + path_rule_link = file_pack.parent / category / path_rule.name symlink(path_rule, path_rule_link) print(f" [*] Linked {rule_id} to {path_rule_link}") elif category == "yara": for rule_id in rule_ids: path_rule = next(chain(find_by_content(DIR_RULES, rule_id, glob="*.yar"))) - path_rule_link = file_pack.parent / "rules" / category / path_rule.name + path_rule_link = file_pack.parent / category / path_rule.name symlink(path_rule, path_rule_link) print(f" [*] Linked {rule_id} to {path_rule_link}") elif category == "ioc": @@ -148,7 +148,7 @@ def main(): ioc_comment_str = " ".join(ioc_comment_parts) iocs[indicator_type].append(f"{ioc_value};{ioc_comment_str}") for ioc_type, ioc_values in iocs.items(): - path_rule = file_pack.parent / "rules" / category / f"{ioc_type}.txt" + path_rule = file_pack.parent / category / f"{ioc_type}.txt" path_rule.parent.mkdir(parents=True, exist_ok=True) path_rule.write_text("\n".join(ioc_values)) print(f" [*] Written {len(ioc_values)} {ioc_type} IOCs to {path_rule}") From 2ff8415c26ec99b7e7450d79d22cc600ac62e736 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Fri, 5 Jun 2026 11:42:51 +0200 Subject: [PATCH 04/13] chore: do not duplicate pack schema version --- docs/authoring.md | 1 - docs/packs.md | 2 +- docs/repository.md | 2 +- packs/linux/advanced/pack.yml | 1 - packs/linux/essential/pack.yml | 1 - packs/macos/advanced/pack.yml | 1 - packs/macos/essential/pack.yml | 1 - packs/windows/advanced/pack.yml | 1 - packs/windows/essential/pack.yml | 1 - packs/windows/hunting/pack.yml | 1 - schemas/pack.schema.json | 6 ------ tools/link_packs.py | 4 ---- tools/validate.py | 3 --- 13 files changed, 2 insertions(+), 23 deletions(-) diff --git a/docs/authoring.md b/docs/authoring.md index 5fa394c..356b1bd 100644 --- a/docs/authoring.md +++ b/docs/authoring.md @@ -92,7 +92,6 @@ rustinel: Then add the `id` to a pack, e.g. `packs/windows/essential/pack.yml`: ```yaml -schema: v1.0.0 rules: has: sigma: diff --git a/docs/packs.md b/docs/packs.md index 8d98427..1e90f7a 100644 --- a/docs/packs.md +++ b/docs/packs.md @@ -18,7 +18,7 @@ Essential ⊂ Advanced ⊂ Hunting | [macOS Essential](#macos-essential) | essential | ❌ | low | experimental | | [macOS Advanced](#macos-advanced) | advanced | ❌ | medium | experimental | -> All packs declare `schema: v1.0.0`, `requires_rustinel: ">=1.0.2"`, `pack_schema_version: 1`, and license +> All packs declare `pack_schema_version: 1`, `requires_rustinel: ">=1.0.2"`, and license > `DRL-1.1`. `status: experimental` reflects the early state of v1 content — expect curation to > tighten as coverage grows. diff --git a/docs/repository.md b/docs/repository.md index 0705415..56ef22c 100644 --- a/docs/repository.md +++ b/docs/repository.md @@ -53,7 +53,7 @@ Key manifest fields: | `id` / `name` | Stable id (`^[a-z0-9]+(-[a-z0-9]+)*$`) and human name. | | `os` | `windows` \| `linux` \| `macos`. | | `level` | `essential` \| `advanced` \| `hunting`. | -| `schema` | Required pack manifest schema version (`v1.0.0`). | +| `pack_schema_version` | Required pack manifest schema version (must be `1`). | | `default` | Whether this pack is enabled by default. | | `extends` | Pack ids cumulatively included (rules merged, never duplicated). | | `rules` | Optional dictionary specifying rules directly in this pack (`has`), or rules to include (`includes`) / exclude (`excludes`) from extended packs or automatic sources. | diff --git a/packs/linux/advanced/pack.yml b/packs/linux/advanced/pack.yml index 510623b..a913818 100644 --- a/packs/linux/advanced/pack.yml +++ b/packs/linux/advanced/pack.yml @@ -6,7 +6,6 @@ description: > before relying on by default. os: linux level: advanced -schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false diff --git a/packs/linux/essential/pack.yml b/packs/linux/essential/pack.yml index 0f83588..5c3a5ff 100644 --- a/packs/linux/essential/pack.yml +++ b/packs/linux/essential/pack.yml @@ -3,7 +3,6 @@ id: linux-essential description: Low-noise, high-confidence Linux detections for Rustinel. Safe default pack. os: linux level: essential -schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: true diff --git a/packs/macos/advanced/pack.yml b/packs/macos/advanced/pack.yml index 4750642..c2f943d 100644 --- a/packs/macos/advanced/pack.yml +++ b/packs/macos/advanced/pack.yml @@ -8,7 +8,6 @@ description: > post-v1; disabled by default. os: macos level: advanced -schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false diff --git a/packs/macos/essential/pack.yml b/packs/macos/essential/pack.yml index 1019171..bbfaa35 100644 --- a/packs/macos/essential/pack.yml +++ b/packs/macos/essential/pack.yml @@ -7,7 +7,6 @@ description: > production-ready, so this pack is disabled by default. os: macos level: essential -schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false diff --git a/packs/windows/advanced/pack.yml b/packs/windows/advanced/pack.yml index 3dab849..353246f 100644 --- a/packs/windows/advanced/pack.yml +++ b/packs/windows/advanced/pack.yml @@ -5,7 +5,6 @@ description: > occur than in Essential; tune per environment before relying on by default. os: windows level: advanced -schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false diff --git a/packs/windows/essential/pack.yml b/packs/windows/essential/pack.yml index a284a5f..7b0e41b 100644 --- a/packs/windows/essential/pack.yml +++ b/packs/windows/essential/pack.yml @@ -3,7 +3,6 @@ id: windows-essential description: Low-noise, high-confidence Windows detections for Rustinel. Safe default pack. os: windows level: essential -schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: true diff --git a/packs/windows/hunting/pack.yml b/packs/windows/hunting/pack.yml index 366c333..73be129 100644 --- a/packs/windows/hunting/pack.yml +++ b/packs/windows/hunting/pack.yml @@ -6,7 +6,6 @@ description: > without tuning. os: windows level: hunting -schema: v1.0.0 pack_schema_version: 1 requires_rustinel: ">=1.0.2" default: false diff --git a/schemas/pack.schema.json b/schemas/pack.schema.json index ef7cfdc..e94e498 100644 --- a/schemas/pack.schema.json +++ b/schemas/pack.schema.json @@ -11,7 +11,6 @@ "description", "os", "level", - "schema", "pack_schema_version", "requires_rustinel", "default", @@ -41,11 +40,6 @@ "type": "string", "enum": ["essential", "advanced", "hunting"] }, - "schema": { - "type": "string", - "description": "Pack manifest schema version.", - "const": "v1.0.0" - }, "pack_schema_version": { "type": "integer", "const": 1 diff --git a/tools/link_packs.py b/tools/link_packs.py index 609783c..4a616b0 100644 --- a/tools/link_packs.py +++ b/tools/link_packs.py @@ -86,10 +86,6 @@ def main(): pack["sources"] = {"manual": list(sources)} modified = True - if "schema" not in pack: - pack["schema"] = "v1.0.0" - modified = True - if modified: out = StringIO() ryaml.dump(pack, out) diff --git a/tools/validate.py b/tools/validate.py index cc81af3..8aa5492 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -231,7 +231,6 @@ def check_ioc_set(art, rep: Report, schema_validate): "description", "os", "level", - "schema", "pack_schema_version", "requires_rustinel", "default", @@ -255,8 +254,6 @@ def check_packs(packs, artifacts, rep: Report): for field in REQUIRED_PACK_FIELDS: if field not in pack: rep.error(where, f"missing required field '{field}'") - if pack.get("schema") != "v1.0.0": - rep.error(where, "schema must be 'v1.0.0' for v1") if pack.get("pack_schema_version") != 1: rep.error(where, "pack_schema_version must be 1 for v1") if not pack.get("license"): From 368680f3356802fd7481c8d8c250e6cc22e6e7d6 Mon Sep 17 00:00:00 2001 From: Aris Sarris Date: Fri, 5 Jun 2026 15:58:29 +0300 Subject: [PATCH 05/13] added download_sigma.py --- tools/download_sigma.py | 361 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 tools/download_sigma.py diff --git a/tools/download_sigma.py b/tools/download_sigma.py new file mode 100644 index 0000000..687fad4 --- /dev/null +++ b/tools/download_sigma.py @@ -0,0 +1,361 @@ +"""Download Sigma rules from SigmaHQ/sigma into rules/sigma/. + +Fetches rules from the windows/, linux/, and macos/ subfolders of +https://github.com/SigmaHQ/sigma/tree/master/rules and writes them into +the corresponding rules/sigma/{os}/ folders in this repo, preserving the +upstream subfolder structure. + +Usage: + python tools/download_sigma.py # download all + python tools/download_sigma.py --os windows linux + python tools/download_sigma.py --dry-run + python tools/download_sigma.py --token ghp_... + python tools/download_sigma.py --diff # show missing rules by ID + python tools/download_sigma.py --diff --os linux # diff a single OS + +Set GITHUB_TOKEN env var or pass --token to avoid API rate limits (60 req/h +unauthenticated vs 5 000 req/h authenticated). The tree listing and zip +download each count as one request, so rate limits are rarely an issue here. +""" + +from __future__ import annotations + +import argparse +import io +import json +import os +import sys +import time +import urllib.error +import urllib.request +import zipfile +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parent.parent +SIGMA_DEST = REPO_ROOT / "rules" / "sigma" + +UPSTREAM_REPO = "SigmaHQ/sigma" +UPSTREAM_BRANCH = "master" +UPSTREAM_RULES_PREFIX = "rules" + +GITHUB_API_TREE = ( + f"https://api.github.com/repos/{UPSTREAM_REPO}/git/trees/{UPSTREAM_BRANCH}?recursive=1" +) +GITHUB_RAW_BASE = f"https://raw.githubusercontent.com/{UPSTREAM_REPO}/{UPSTREAM_BRANCH}" +GITHUB_ZIP_URL = ( + f"https://github.com/{UPSTREAM_REPO}/archive/refs/heads/{UPSTREAM_BRANCH}.zip" +) + +ALL_TARGET_OS: tuple[str, ...] = ("windows", "linux", "macos") + + +# --------------------------------------------------------------------------- # +# HTTP helpers +# --------------------------------------------------------------------------- # + + +def _make_request(url: str, token: str | None) -> urllib.request.Request: + req = urllib.request.Request(url) + req.add_header("Accept", "application/vnd.github+json") + req.add_header("X-GitHub-Api-Version", "2022-11-28") + if token: + req.add_header("Authorization", f"Bearer {token}") + return req + + +def fetch_json(url: str, token: str | None = None) -> dict: + req = _make_request(url, token) + try: + with urllib.request.urlopen(req, timeout=30) as resp: + return json.loads(resp.read().decode()) + except urllib.error.HTTPError as exc: + sys.exit(f"GitHub API error {exc.code} for {url}: {exc.reason}") + + +def fetch_raw(url: str, token: str | None = None) -> bytes: + req = _make_request(url, token) + try: + with urllib.request.urlopen(req, timeout=60) as resp: + return resp.read() + except urllib.error.HTTPError as exc: + sys.exit(f"Download error {exc.code} for {url}: {exc.reason}") + + +# --------------------------------------------------------------------------- # +# Download +# --------------------------------------------------------------------------- # + + +def get_rule_entries(token: str | None, target_os: tuple[str, ...]) -> list[dict]: + """Return tree entries for .yml files under rules/{os}/ for each target OS.""" + print(f"Fetching file tree from {UPSTREAM_REPO}…") + tree_data = fetch_json(GITHUB_API_TREE, token) + + if tree_data.get("truncated"): + print("Warning: upstream tree was truncated by GitHub — some files may be missing.") + + entries = [] + for item in tree_data.get("tree", []): + if item.get("type") != "blob": + continue + path: str = item["path"] + parts = path.split("/") + if ( + len(parts) >= 3 + and parts[0] == UPSTREAM_RULES_PREFIX + and parts[1] in target_os + and path.endswith(".yml") + ): + entries.append(item) + + return entries + + +def dest_path(upstream_path: str) -> Path: + """Map upstream path (rules/windows/foo/bar.yml) -> local path under SIGMA_DEST.""" + rel = Path(upstream_path).relative_to(UPSTREAM_RULES_PREFIX) + return SIGMA_DEST / rel + + +def download_rules( + entries: list[dict], + token: str | None, + dry_run: bool, + force: bool, +) -> tuple[int, int, int]: + """Download entries. Returns (downloaded, skipped, total).""" + total = len(entries) + downloaded = 0 + skipped = 0 + + for i, entry in enumerate(entries, 1): + upstream_path: str = entry["path"] + local = dest_path(upstream_path) + label = f"[{i}/{total}] {upstream_path}" + + if local.exists() and not force: + print(f" skip {label}") + skipped += 1 + continue + + raw_url = f"{GITHUB_RAW_BASE}/{upstream_path}" + + if dry_run: + print(f" would download {label}") + downloaded += 1 + continue + + print(f" fetch {label}") + content = fetch_raw(raw_url, token) + local.parent.mkdir(parents=True, exist_ok=True) + local.write_bytes(content) + downloaded += 1 + + # Polite delay to avoid hammering raw.githubusercontent.com + if i % 50 == 0: + time.sleep(0.5) + + return downloaded, skipped, total + + +# --------------------------------------------------------------------------- # +# Diff helpers +# --------------------------------------------------------------------------- # + + +def load_local_ids(target_os: tuple[str, ...]) -> dict[str, dict[str, str]]: + """Read local rules and return {os: {rule_id: rel_path}}. + + Skips files without a parseable `id` field and emits a warning. + """ + result: dict[str, dict[str, str]] = {os_name: {} for os_name in target_os} + for os_name in target_os: + os_dir = SIGMA_DEST / os_name + if not os_dir.is_dir(): + continue + for path in sorted(os_dir.rglob("*.yml")): + try: + doc = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + except yaml.YAMLError: + print(f" Warning: could not parse {path.relative_to(REPO_ROOT)}") + continue + rule_id = str(doc.get("id", "")).strip() + if not rule_id: + print(f" Warning: no id in {path.relative_to(REPO_ROOT)}") + continue + result[os_name][rule_id] = str(path.relative_to(REPO_ROOT)) + return result + + +def fetch_upstream_ids(target_os: tuple[str, ...]) -> dict[str, dict[str, str]]: + """Download the upstream repo zip and return {os: {rule_id: upstream_path}}. + + Uses a single zip download instead of per-file requests. The zip is + processed in memory; no files are written to disk. + """ + print(f"Downloading upstream repo zip from {GITHUB_ZIP_URL} …") + print("(This is a one-time download of the full repo — usually 30–80 MB.)") + data = fetch_raw(GITHUB_ZIP_URL, token=None) + print(f"Downloaded {len(data) / 1_048_576:.1f} MB. Parsing rules…") + + result: dict[str, dict[str, str]] = {os_name: {} for os_name in target_os} + skipped_no_id = 0 + + with zipfile.ZipFile(io.BytesIO(data)) as zf: + for member in zf.namelist(): + if not member.endswith(".yml"): + continue + + # Zip entries look like "sigma-master/rules/windows/proc_creation/foo.yml" + # Find the "rules" segment regardless of the leading archive prefix. + parts = member.split("/") + try: + rules_idx = parts.index(UPSTREAM_RULES_PREFIX) + except ValueError: + continue + + if rules_idx + 2 >= len(parts): + continue + + os_name = parts[rules_idx + 1] + if os_name not in target_os: + continue + + try: + content = zf.read(member).decode("utf-8", errors="replace") + doc = yaml.safe_load(content) or {} + except (yaml.YAMLError, Exception): + continue + + rule_id = str(doc.get("id", "")).strip() + if not rule_id: + skipped_no_id += 1 + continue + + # Reconstruct the canonical upstream path: rules/{os}/... + upstream_path = "/".join(parts[rules_idx:]) + result[os_name][rule_id] = upstream_path + + if skipped_no_id: + print(f" (skipped {skipped_no_id} upstream files with no id field)") + + return result + + +def show_diff(target_os: tuple[str, ...]) -> None: + """Compare local rule IDs against upstream and report what is missing.""" + local = load_local_ids(target_os) + upstream = fetch_upstream_ids(target_os) + + print() + any_missing = False + + for os_name in target_os: + local_ids = local[os_name] + upstream_ids = upstream[os_name] + + missing = { + rule_id: path + for rule_id, path in upstream_ids.items() + if rule_id not in local_ids + } + + print(f"── {os_name} ────────────────────────────────────────────────") + print(f" local: {len(local_ids):>5} rules upstream: {len(upstream_ids):>5} rules missing: {len(missing):>5}") + + if missing: + any_missing = True + print() + for rule_id, upstream_path in sorted(missing.items(), key=lambda kv: kv[1]): + print(f" MISSING {rule_id} ({upstream_path})") + else: + print(" All upstream rules are present locally.") + print() + + if any_missing: + print("Tip: run without --diff to download all missing rules.") + else: + print("Local rules are in sync with upstream for the selected OS targets.") + + +# --------------------------------------------------------------------------- # +# CLI +# --------------------------------------------------------------------------- # + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Download or diff Sigma rules from SigmaHQ/sigma into rules/sigma/." + ) + parser.add_argument( + "--os", + nargs="+", + choices=list(ALL_TARGET_OS), + default=list(ALL_TARGET_OS), + metavar="OS", + help=f"Which OS rule sets to target (default: all). Choices: {', '.join(ALL_TARGET_OS)}", + ) + parser.add_argument( + "--token", + default=os.environ.get("GITHUB_TOKEN"), + help="GitHub personal access token (default: $GITHUB_TOKEN env var)", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="(download mode) Print what would be downloaded without writing files", + ) + parser.add_argument( + "--force", + action="store_true", + help="(download mode) Overwrite files that already exist locally", + ) + parser.add_argument( + "--diff", + action="store_true", + help=( + "Compare local rule IDs against upstream and report which rules are missing. " + "Downloads the upstream repo as a single zip — no files are written." + ), + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + target_os = tuple(args.os) + + if args.diff: + show_diff(target_os) + return + + if not args.token: + print( + "No GitHub token found. Unauthenticated requests are limited to 60/hour.\n" + "Set GITHUB_TOKEN or pass --token to increase to 5 000/hour." + ) + + entries = get_rule_entries(args.token, target_os) + print(f"Found {len(entries)} rules across: {', '.join(target_os)}\n") + + if not entries: + print("Nothing to download.") + return + + downloaded, skipped, total = download_rules( + entries, + token=args.token, + dry_run=args.dry_run, + force=args.force, + ) + + action = "would download" if args.dry_run else "downloaded" + print(f"\nDone. {action} {downloaded}, skipped {skipped} (already exist), total {total}.") + if args.dry_run: + print("Re-run without --dry-run to write files.") + + +if __name__ == "__main__": + main() From bd7a83fbc73aa1de9765fa33e1ae2acea0481f15 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Sat, 6 Jun 2026 13:07:13 +0200 Subject: [PATCH 06/13] fix: typo --- tools/{upload_ragest.py => upload_radegast.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tools/{upload_ragest.py => upload_radegast.py} (98%) diff --git a/tools/upload_ragest.py b/tools/upload_radegast.py similarity index 98% rename from tools/upload_ragest.py rename to tools/upload_radegast.py index bc6cb73..7beb5b4 100644 --- a/tools/upload_ragest.py +++ b/tools/upload_radegast.py @@ -61,7 +61,7 @@ def main() -> None: json={ "pack_id": pack_id, "name": pack_name, - "description": f"Rustinel pack from https://github.com/Karib0u/rustinel-rules/", + "description": f"Rustinel pack from https://github.com/Karib0u/rustinel-rules/.", } ) r.raise_for_status() From 9f51f4f5b0666e577c6e3bddf8429237972660d0 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Sat, 6 Jun 2026 13:35:10 +0200 Subject: [PATCH 07/13] chore: remove unused script --- tools/download_sigma.py | 361 ---------------------------------------- 1 file changed, 361 deletions(-) delete mode 100644 tools/download_sigma.py diff --git a/tools/download_sigma.py b/tools/download_sigma.py deleted file mode 100644 index 687fad4..0000000 --- a/tools/download_sigma.py +++ /dev/null @@ -1,361 +0,0 @@ -"""Download Sigma rules from SigmaHQ/sigma into rules/sigma/. - -Fetches rules from the windows/, linux/, and macos/ subfolders of -https://github.com/SigmaHQ/sigma/tree/master/rules and writes them into -the corresponding rules/sigma/{os}/ folders in this repo, preserving the -upstream subfolder structure. - -Usage: - python tools/download_sigma.py # download all - python tools/download_sigma.py --os windows linux - python tools/download_sigma.py --dry-run - python tools/download_sigma.py --token ghp_... - python tools/download_sigma.py --diff # show missing rules by ID - python tools/download_sigma.py --diff --os linux # diff a single OS - -Set GITHUB_TOKEN env var or pass --token to avoid API rate limits (60 req/h -unauthenticated vs 5 000 req/h authenticated). The tree listing and zip -download each count as one request, so rate limits are rarely an issue here. -""" - -from __future__ import annotations - -import argparse -import io -import json -import os -import sys -import time -import urllib.error -import urllib.request -import zipfile -from pathlib import Path - -import yaml - -REPO_ROOT = Path(__file__).resolve().parent.parent -SIGMA_DEST = REPO_ROOT / "rules" / "sigma" - -UPSTREAM_REPO = "SigmaHQ/sigma" -UPSTREAM_BRANCH = "master" -UPSTREAM_RULES_PREFIX = "rules" - -GITHUB_API_TREE = ( - f"https://api.github.com/repos/{UPSTREAM_REPO}/git/trees/{UPSTREAM_BRANCH}?recursive=1" -) -GITHUB_RAW_BASE = f"https://raw.githubusercontent.com/{UPSTREAM_REPO}/{UPSTREAM_BRANCH}" -GITHUB_ZIP_URL = ( - f"https://github.com/{UPSTREAM_REPO}/archive/refs/heads/{UPSTREAM_BRANCH}.zip" -) - -ALL_TARGET_OS: tuple[str, ...] = ("windows", "linux", "macos") - - -# --------------------------------------------------------------------------- # -# HTTP helpers -# --------------------------------------------------------------------------- # - - -def _make_request(url: str, token: str | None) -> urllib.request.Request: - req = urllib.request.Request(url) - req.add_header("Accept", "application/vnd.github+json") - req.add_header("X-GitHub-Api-Version", "2022-11-28") - if token: - req.add_header("Authorization", f"Bearer {token}") - return req - - -def fetch_json(url: str, token: str | None = None) -> dict: - req = _make_request(url, token) - try: - with urllib.request.urlopen(req, timeout=30) as resp: - return json.loads(resp.read().decode()) - except urllib.error.HTTPError as exc: - sys.exit(f"GitHub API error {exc.code} for {url}: {exc.reason}") - - -def fetch_raw(url: str, token: str | None = None) -> bytes: - req = _make_request(url, token) - try: - with urllib.request.urlopen(req, timeout=60) as resp: - return resp.read() - except urllib.error.HTTPError as exc: - sys.exit(f"Download error {exc.code} for {url}: {exc.reason}") - - -# --------------------------------------------------------------------------- # -# Download -# --------------------------------------------------------------------------- # - - -def get_rule_entries(token: str | None, target_os: tuple[str, ...]) -> list[dict]: - """Return tree entries for .yml files under rules/{os}/ for each target OS.""" - print(f"Fetching file tree from {UPSTREAM_REPO}…") - tree_data = fetch_json(GITHUB_API_TREE, token) - - if tree_data.get("truncated"): - print("Warning: upstream tree was truncated by GitHub — some files may be missing.") - - entries = [] - for item in tree_data.get("tree", []): - if item.get("type") != "blob": - continue - path: str = item["path"] - parts = path.split("/") - if ( - len(parts) >= 3 - and parts[0] == UPSTREAM_RULES_PREFIX - and parts[1] in target_os - and path.endswith(".yml") - ): - entries.append(item) - - return entries - - -def dest_path(upstream_path: str) -> Path: - """Map upstream path (rules/windows/foo/bar.yml) -> local path under SIGMA_DEST.""" - rel = Path(upstream_path).relative_to(UPSTREAM_RULES_PREFIX) - return SIGMA_DEST / rel - - -def download_rules( - entries: list[dict], - token: str | None, - dry_run: bool, - force: bool, -) -> tuple[int, int, int]: - """Download entries. Returns (downloaded, skipped, total).""" - total = len(entries) - downloaded = 0 - skipped = 0 - - for i, entry in enumerate(entries, 1): - upstream_path: str = entry["path"] - local = dest_path(upstream_path) - label = f"[{i}/{total}] {upstream_path}" - - if local.exists() and not force: - print(f" skip {label}") - skipped += 1 - continue - - raw_url = f"{GITHUB_RAW_BASE}/{upstream_path}" - - if dry_run: - print(f" would download {label}") - downloaded += 1 - continue - - print(f" fetch {label}") - content = fetch_raw(raw_url, token) - local.parent.mkdir(parents=True, exist_ok=True) - local.write_bytes(content) - downloaded += 1 - - # Polite delay to avoid hammering raw.githubusercontent.com - if i % 50 == 0: - time.sleep(0.5) - - return downloaded, skipped, total - - -# --------------------------------------------------------------------------- # -# Diff helpers -# --------------------------------------------------------------------------- # - - -def load_local_ids(target_os: tuple[str, ...]) -> dict[str, dict[str, str]]: - """Read local rules and return {os: {rule_id: rel_path}}. - - Skips files without a parseable `id` field and emits a warning. - """ - result: dict[str, dict[str, str]] = {os_name: {} for os_name in target_os} - for os_name in target_os: - os_dir = SIGMA_DEST / os_name - if not os_dir.is_dir(): - continue - for path in sorted(os_dir.rglob("*.yml")): - try: - doc = yaml.safe_load(path.read_text(encoding="utf-8")) or {} - except yaml.YAMLError: - print(f" Warning: could not parse {path.relative_to(REPO_ROOT)}") - continue - rule_id = str(doc.get("id", "")).strip() - if not rule_id: - print(f" Warning: no id in {path.relative_to(REPO_ROOT)}") - continue - result[os_name][rule_id] = str(path.relative_to(REPO_ROOT)) - return result - - -def fetch_upstream_ids(target_os: tuple[str, ...]) -> dict[str, dict[str, str]]: - """Download the upstream repo zip and return {os: {rule_id: upstream_path}}. - - Uses a single zip download instead of per-file requests. The zip is - processed in memory; no files are written to disk. - """ - print(f"Downloading upstream repo zip from {GITHUB_ZIP_URL} …") - print("(This is a one-time download of the full repo — usually 30–80 MB.)") - data = fetch_raw(GITHUB_ZIP_URL, token=None) - print(f"Downloaded {len(data) / 1_048_576:.1f} MB. Parsing rules…") - - result: dict[str, dict[str, str]] = {os_name: {} for os_name in target_os} - skipped_no_id = 0 - - with zipfile.ZipFile(io.BytesIO(data)) as zf: - for member in zf.namelist(): - if not member.endswith(".yml"): - continue - - # Zip entries look like "sigma-master/rules/windows/proc_creation/foo.yml" - # Find the "rules" segment regardless of the leading archive prefix. - parts = member.split("/") - try: - rules_idx = parts.index(UPSTREAM_RULES_PREFIX) - except ValueError: - continue - - if rules_idx + 2 >= len(parts): - continue - - os_name = parts[rules_idx + 1] - if os_name not in target_os: - continue - - try: - content = zf.read(member).decode("utf-8", errors="replace") - doc = yaml.safe_load(content) or {} - except (yaml.YAMLError, Exception): - continue - - rule_id = str(doc.get("id", "")).strip() - if not rule_id: - skipped_no_id += 1 - continue - - # Reconstruct the canonical upstream path: rules/{os}/... - upstream_path = "/".join(parts[rules_idx:]) - result[os_name][rule_id] = upstream_path - - if skipped_no_id: - print(f" (skipped {skipped_no_id} upstream files with no id field)") - - return result - - -def show_diff(target_os: tuple[str, ...]) -> None: - """Compare local rule IDs against upstream and report what is missing.""" - local = load_local_ids(target_os) - upstream = fetch_upstream_ids(target_os) - - print() - any_missing = False - - for os_name in target_os: - local_ids = local[os_name] - upstream_ids = upstream[os_name] - - missing = { - rule_id: path - for rule_id, path in upstream_ids.items() - if rule_id not in local_ids - } - - print(f"── {os_name} ────────────────────────────────────────────────") - print(f" local: {len(local_ids):>5} rules upstream: {len(upstream_ids):>5} rules missing: {len(missing):>5}") - - if missing: - any_missing = True - print() - for rule_id, upstream_path in sorted(missing.items(), key=lambda kv: kv[1]): - print(f" MISSING {rule_id} ({upstream_path})") - else: - print(" All upstream rules are present locally.") - print() - - if any_missing: - print("Tip: run without --diff to download all missing rules.") - else: - print("Local rules are in sync with upstream for the selected OS targets.") - - -# --------------------------------------------------------------------------- # -# CLI -# --------------------------------------------------------------------------- # - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Download or diff Sigma rules from SigmaHQ/sigma into rules/sigma/." - ) - parser.add_argument( - "--os", - nargs="+", - choices=list(ALL_TARGET_OS), - default=list(ALL_TARGET_OS), - metavar="OS", - help=f"Which OS rule sets to target (default: all). Choices: {', '.join(ALL_TARGET_OS)}", - ) - parser.add_argument( - "--token", - default=os.environ.get("GITHUB_TOKEN"), - help="GitHub personal access token (default: $GITHUB_TOKEN env var)", - ) - parser.add_argument( - "--dry-run", - action="store_true", - help="(download mode) Print what would be downloaded without writing files", - ) - parser.add_argument( - "--force", - action="store_true", - help="(download mode) Overwrite files that already exist locally", - ) - parser.add_argument( - "--diff", - action="store_true", - help=( - "Compare local rule IDs against upstream and report which rules are missing. " - "Downloads the upstream repo as a single zip — no files are written." - ), - ) - return parser.parse_args() - - -def main() -> None: - args = parse_args() - target_os = tuple(args.os) - - if args.diff: - show_diff(target_os) - return - - if not args.token: - print( - "No GitHub token found. Unauthenticated requests are limited to 60/hour.\n" - "Set GITHUB_TOKEN or pass --token to increase to 5 000/hour." - ) - - entries = get_rule_entries(args.token, target_os) - print(f"Found {len(entries)} rules across: {', '.join(target_os)}\n") - - if not entries: - print("Nothing to download.") - return - - downloaded, skipped, total = download_rules( - entries, - token=args.token, - dry_run=args.dry_run, - force=args.force, - ) - - action = "would download" if args.dry_run else "downloaded" - print(f"\nDone. {action} {downloaded}, skipped {skipped} (already exist), total {total}.") - if args.dry_run: - print("Re-run without --dry-run to write files.") - - -if __name__ == "__main__": - main() From 0ea4f5dd54cfbcda258e6301eb48621329a29b75 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Wed, 10 Jun 2026 12:27:28 +0200 Subject: [PATCH 08/13] chore: delete symlinks --- .../sigma/file_event_lnx_cron_persistence.yml | 1 - ...le_event_lnx_shell_profile_persistence.yml | 1 - .../file_event_lnx_systemd_persistence.yml | 1 - .../proc_creation_lnx_download_pipe_shell.yml | 1 - ..._creation_lnx_exec_from_world_writable.yml | 1 - packs/linux/essential/ioc/hashes.txt | 3 - .../file_event_lnx_ldso_preload_hijack.yml | 1 - .../file_event_lnx_ssh_authorized_keys.yml | 1 - .../file_event_lnx_sshd_config_tamper.yml | 1 - .../sigma/file_event_lnx_sudoers_tamper.yml | 1 - ...eation_lnx_shell_dev_tcp_reverse_shell.yml | 1 - ...oc_creation_lnx_webserver_spawns_shell.yml | 1 - .../yara/lnx_susp_xmrig_coinminer.yar | 1 - .../file_event_macos_launch_persistence.yml | 1 - ...creation_macos_download_execute_cradle.yml | 1 - ...reation_macos_exec_from_world_writable.yml | 1 - ...roc_creation_macos_local_admin_account.yml | 1 - packs/macos/essential/ioc/hashes.txt | 3 - .../proc_creation_macos_gatekeeper_bypass.yml | 1 - ..._creation_macos_keychain_dump_security.yml | 1 - ..._creation_macos_osascript_admin_prompt.yml | 1 - ...tion_macos_shell_dev_tcp_reverse_shell.yml | 1 - .../yara/macos_susp_coinminer_strings.yar | 1 - ...ion_win_susp_local_admin_account_added.yml | 1 - ...roc_creation_win_susp_rundll32_no_args.yml | 1 - ...proc_creation_win_susp_schtasks_create.yml | 1 - ..._creation_win_susp_wmic_process_create.yml | 1 - ...pt_win_susp_powershell_download_cradle.yml | 1 - ...registry_event_win_run_key_persistence.yml | 1 - ...rvice_creation_win_susp_service_binary.yml | 1 - packs/windows/essential/ioc/hashes.txt | 3 - ...c_creation_win_susp_encoded_powershell.yml | 1 - .../proc_creation_win_susp_eventlog_clear.yml | 1 - ...eation_win_susp_lsass_comsvcs_minidump.yml | 1 - ...eation_win_susp_mshta_remote_execution.yml | 1 - ..._creation_win_susp_ntds_dit_extraction.yml | 1 - ...reation_win_susp_office_spawning_shell.yml | 1 - ...c_creation_win_susp_registry_hive_dump.yml | 1 - ...ion_win_susp_regsvr32_remote_scriptlet.yml | 1 - ...creation_win_susp_shadow_copy_deletion.yml | 1 - ...eation_win_susp_uac_bypass_autoelevate.yml | 1 - .../registry_event_win_defender_tamper.yml | 1 - ...vent_win_wdigest_cleartext_credentials.yml | 1 - ...reation_win_susp_scheduled_task_action.yml | 1 - .../yara/win_susp_mimikatz_strings.yar | 1 - ...n_win_hunting_lolbin_certutil_download.yml | 1 - tools/link_packs.py | 154 ------------------ 47 files changed, 206 deletions(-) delete mode 120000 packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml delete mode 120000 packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml delete mode 120000 packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml delete mode 120000 packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml delete mode 120000 packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml delete mode 100644 packs/linux/essential/ioc/hashes.txt delete mode 120000 packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml delete mode 120000 packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml delete mode 120000 packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml delete mode 120000 packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml delete mode 120000 packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml delete mode 120000 packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml delete mode 120000 packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar delete mode 120000 packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml delete mode 120000 packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml delete mode 120000 packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml delete mode 120000 packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml delete mode 100644 packs/macos/essential/ioc/hashes.txt delete mode 120000 packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml delete mode 120000 packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml delete mode 120000 packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml delete mode 120000 packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml delete mode 120000 packs/macos/essential/yara/macos_susp_coinminer_strings.yar delete mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml delete mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml delete mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml delete mode 120000 packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml delete mode 120000 packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml delete mode 120000 packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml delete mode 120000 packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml delete mode 100644 packs/windows/essential/ioc/hashes.txt delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml delete mode 120000 packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml delete mode 120000 packs/windows/essential/sigma/registry_event_win_defender_tamper.yml delete mode 120000 packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml delete mode 120000 packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml delete mode 120000 packs/windows/essential/yara/win_susp_mimikatz_strings.yar delete mode 120000 packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml delete mode 100644 tools/link_packs.py diff --git a/packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml b/packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml deleted file mode 120000 index 7ec2e51..0000000 --- a/packs/linux/advanced/sigma/file_event_lnx_cron_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/file_event_lnx_cron_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml b/packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml deleted file mode 120000 index 6b51dea..0000000 --- a/packs/linux/advanced/sigma/file_event_lnx_shell_profile_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/file_event_lnx_shell_profile_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml b/packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml deleted file mode 120000 index 8c43806..0000000 --- a/packs/linux/advanced/sigma/file_event_lnx_systemd_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/file_event_lnx_systemd_persistence.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml b/packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml deleted file mode 120000 index 07b3b11..0000000 --- a/packs/linux/advanced/sigma/proc_creation_lnx_download_pipe_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/proc_creation_lnx_download_pipe_shell.yml \ No newline at end of file diff --git a/packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml b/packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml deleted file mode 120000 index c91618a..0000000 --- a/packs/linux/advanced/sigma/proc_creation_lnx_exec_from_world_writable.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/proc_creation_lnx_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/linux/essential/ioc/hashes.txt b/packs/linux/essential/ioc/hashes.txt deleted file mode 100644 index 6bcbf17..0000000 --- a/packs/linux/essential/ioc/hashes.txt +++ /dev/null @@ -1,3 +0,0 @@ -44d88612fea8a8f36de82e1278abb02f;rule=ioc-eicar-test severity=low comment=EICAR test file (MD5) -3395856ce81f2b7382dee72602f798b642f14140;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA1) -275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA256) \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml b/packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml deleted file mode 120000 index 729935a..0000000 --- a/packs/linux/essential/sigma/file_event_lnx_ldso_preload_hijack.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/file_event_lnx_ldso_preload_hijack.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml b/packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml deleted file mode 120000 index 1309e0b..0000000 --- a/packs/linux/essential/sigma/file_event_lnx_ssh_authorized_keys.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/file_event_lnx_ssh_authorized_keys.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml b/packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml deleted file mode 120000 index 98ac5b3..0000000 --- a/packs/linux/essential/sigma/file_event_lnx_sshd_config_tamper.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/file_event_lnx_sshd_config_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml b/packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml deleted file mode 120000 index 30192b0..0000000 --- a/packs/linux/essential/sigma/file_event_lnx_sudoers_tamper.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/file_event_lnx_sudoers_tamper.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml b/packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml deleted file mode 120000 index 02a965f..0000000 --- a/packs/linux/essential/sigma/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/proc_creation_lnx_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml b/packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml deleted file mode 120000 index 89f700f..0000000 --- a/packs/linux/essential/sigma/proc_creation_lnx_webserver_spawns_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/linux/proc_creation_lnx_webserver_spawns_shell.yml \ No newline at end of file diff --git a/packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar b/packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar deleted file mode 120000 index c6485ee..0000000 --- a/packs/linux/essential/yara/lnx_susp_xmrig_coinminer.yar +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/yara/linux/lnx_susp_xmrig_coinminer.yar \ No newline at end of file diff --git a/packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml b/packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml deleted file mode 120000 index 9e04019..0000000 --- a/packs/macos/advanced/sigma/file_event_macos_launch_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/file_event_macos_launch_persistence.yml \ No newline at end of file diff --git a/packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml b/packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml deleted file mode 120000 index a653ec2..0000000 --- a/packs/macos/advanced/sigma/proc_creation_macos_download_execute_cradle.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/proc_creation_macos_download_execute_cradle.yml \ No newline at end of file diff --git a/packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml b/packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml deleted file mode 120000 index 26a850b..0000000 --- a/packs/macos/advanced/sigma/proc_creation_macos_exec_from_world_writable.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/proc_creation_macos_exec_from_world_writable.yml \ No newline at end of file diff --git a/packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml b/packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml deleted file mode 120000 index ac8e8b3..0000000 --- a/packs/macos/advanced/sigma/proc_creation_macos_local_admin_account.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/proc_creation_macos_local_admin_account.yml \ No newline at end of file diff --git a/packs/macos/essential/ioc/hashes.txt b/packs/macos/essential/ioc/hashes.txt deleted file mode 100644 index 6bcbf17..0000000 --- a/packs/macos/essential/ioc/hashes.txt +++ /dev/null @@ -1,3 +0,0 @@ -44d88612fea8a8f36de82e1278abb02f;rule=ioc-eicar-test severity=low comment=EICAR test file (MD5) -3395856ce81f2b7382dee72602f798b642f14140;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA1) -275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA256) \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml b/packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml deleted file mode 120000 index 365b749..0000000 --- a/packs/macos/essential/sigma/proc_creation_macos_gatekeeper_bypass.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/proc_creation_macos_gatekeeper_bypass.yml \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml b/packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml deleted file mode 120000 index 5c8bc62..0000000 --- a/packs/macos/essential/sigma/proc_creation_macos_keychain_dump_security.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/proc_creation_macos_keychain_dump_security.yml \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml b/packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml deleted file mode 120000 index 388946f..0000000 --- a/packs/macos/essential/sigma/proc_creation_macos_osascript_admin_prompt.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/proc_creation_macos_osascript_admin_prompt.yml \ No newline at end of file diff --git a/packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml b/packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml deleted file mode 120000 index fd21d85..0000000 --- a/packs/macos/essential/sigma/proc_creation_macos_shell_dev_tcp_reverse_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/macos/proc_creation_macos_shell_dev_tcp_reverse_shell.yml \ No newline at end of file diff --git a/packs/macos/essential/yara/macos_susp_coinminer_strings.yar b/packs/macos/essential/yara/macos_susp_coinminer_strings.yar deleted file mode 120000 index b596ad2..0000000 --- a/packs/macos/essential/yara/macos_susp_coinminer_strings.yar +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/yara/macos/macos_susp_coinminer_strings.yar \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml deleted file mode 120000 index 203ae30..0000000 --- a/packs/windows/advanced/sigma/proc_creation_win_susp_local_admin_account_added.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_local_admin_account_added.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml deleted file mode 120000 index e0d124e..0000000 --- a/packs/windows/advanced/sigma/proc_creation_win_susp_rundll32_no_args.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_rundll32_no_args.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml deleted file mode 120000 index 8c622ce..0000000 --- a/packs/windows/advanced/sigma/proc_creation_win_susp_schtasks_create.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_schtasks_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml b/packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml deleted file mode 120000 index 06a155d..0000000 --- a/packs/windows/advanced/sigma/proc_creation_win_susp_wmic_process_create.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_wmic_process_create.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml b/packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml deleted file mode 120000 index 53f82d7..0000000 --- a/packs/windows/advanced/sigma/ps_script_win_susp_powershell_download_cradle.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/ps_script_win_susp_powershell_download_cradle.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml b/packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml deleted file mode 120000 index 4a256fc..0000000 --- a/packs/windows/advanced/sigma/registry_event_win_run_key_persistence.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/registry_event_win_run_key_persistence.yml \ No newline at end of file diff --git a/packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml b/packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml deleted file mode 120000 index f6bc5d4..0000000 --- a/packs/windows/advanced/sigma/service_creation_win_susp_service_binary.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/service_creation_win_susp_service_binary.yml \ No newline at end of file diff --git a/packs/windows/essential/ioc/hashes.txt b/packs/windows/essential/ioc/hashes.txt deleted file mode 100644 index 6bcbf17..0000000 --- a/packs/windows/essential/ioc/hashes.txt +++ /dev/null @@ -1,3 +0,0 @@ -44d88612fea8a8f36de82e1278abb02f;rule=ioc-eicar-test severity=low comment=EICAR test file (MD5) -3395856ce81f2b7382dee72602f798b642f14140;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA1) -275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f;rule=ioc-eicar-test severity=low comment=EICAR test file (SHA256) \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml b/packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml deleted file mode 120000 index f18d339..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_encoded_powershell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_encoded_powershell.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml b/packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml deleted file mode 120000 index 044d5e6..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_eventlog_clear.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_eventlog_clear.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml b/packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml deleted file mode 120000 index ea81556..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_lsass_comsvcs_minidump.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_lsass_comsvcs_minidump.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml b/packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml deleted file mode 120000 index a58b3f4..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_mshta_remote_execution.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_mshta_remote_execution.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml b/packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml deleted file mode 120000 index 6174ef5..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_ntds_dit_extraction.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_ntds_dit_extraction.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml b/packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml deleted file mode 120000 index 5fef58a..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_office_spawning_shell.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_office_spawning_shell.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml b/packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml deleted file mode 120000 index 7fa8725..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_registry_hive_dump.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_registry_hive_dump.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml b/packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml deleted file mode 120000 index f34d116..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_regsvr32_remote_scriptlet.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_regsvr32_remote_scriptlet.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml b/packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml deleted file mode 120000 index ae20964..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_shadow_copy_deletion.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_shadow_copy_deletion.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml b/packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml deleted file mode 120000 index 3bf5a2d..0000000 --- a/packs/windows/essential/sigma/proc_creation_win_susp_uac_bypass_autoelevate.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_susp_uac_bypass_autoelevate.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/registry_event_win_defender_tamper.yml b/packs/windows/essential/sigma/registry_event_win_defender_tamper.yml deleted file mode 120000 index 25b9d34..0000000 --- a/packs/windows/essential/sigma/registry_event_win_defender_tamper.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/registry_event_win_defender_tamper.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml b/packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml deleted file mode 120000 index 4ae0037..0000000 --- a/packs/windows/essential/sigma/registry_event_win_wdigest_cleartext_credentials.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/registry_event_win_wdigest_cleartext_credentials.yml \ No newline at end of file diff --git a/packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml b/packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml deleted file mode 120000 index cf4bc90..0000000 --- a/packs/windows/essential/sigma/task_creation_win_susp_scheduled_task_action.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/task_creation_win_susp_scheduled_task_action.yml \ No newline at end of file diff --git a/packs/windows/essential/yara/win_susp_mimikatz_strings.yar b/packs/windows/essential/yara/win_susp_mimikatz_strings.yar deleted file mode 120000 index 81cbd18..0000000 --- a/packs/windows/essential/yara/win_susp_mimikatz_strings.yar +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/yara/windows/win_susp_mimikatz_strings.yar \ No newline at end of file diff --git a/packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml b/packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml deleted file mode 120000 index 2d63771..0000000 --- a/packs/windows/hunting/sigma/proc_creation_win_hunting_lolbin_certutil_download.yml +++ /dev/null @@ -1 +0,0 @@ -../../../../rules/sigma/windows/proc_creation_win_hunting_lolbin_certutil_download.yml \ No newline at end of file diff --git a/tools/link_packs.py b/tools/link_packs.py deleted file mode 100644 index 4a616b0..0000000 --- a/tools/link_packs.py +++ /dev/null @@ -1,154 +0,0 @@ -""" -Converts old pack.yml format into the new one and symlinks all -rules of the pack into the pack itself for easier zipping and usage. -""" - -from collections.abc import Iterable -from io import StringIO -from itertools import chain -from os.path import relpath -from pathlib import Path -from typing import TypedDict - -from ruamel.yaml import YAML -from yaml import safe_load - -DIR_ROOT = Path(__file__).parent.parent -DIR_PACKS = DIR_ROOT / "packs" -DIR_RULES = DIR_ROOT / "rules" - - -class Rules(TypedDict): - sigma: list[str] - yara: list[str] - ioc: list[str] - - -def find_by_content(root: Path, content: str, glob: str = "*") -> Iterable[Path]: - for file in root.rglob(glob): - if file.is_file(): - try: - text = file.read_text() - if content in text: - yield file - except Exception as e: - print(f"Error reading {file}: {e}") - - -def symlink(original: Path, target: Path): - if target.is_symlink(): - target.unlink() - target.parent.mkdir(parents=True, exist_ok=True) - link = Path(relpath(original.absolute(), start=target.parent.absolute())) - target.symlink_to(link) - - -def main(): - ryaml = YAML() - ryaml.preserve_quotes = True - ryaml.width = 120 - ryaml.indent(mapping=4, sequence=2, offset=2) - - for file_pack in DIR_PACKS.rglob("pack.yml"): - print(f"[*] Checking {file_pack}") - pack = ryaml.load(file_pack.read_text(encoding="utf-8")) - - modified = False - - rules = pack.get("rules") - if isinstance(rules, list): - print("[*] Converting rules list to dict") - rules_ids = [str(x) for x in rules] - rules_by_category: dict[str, list[str]] = { - "sigma": [], - "yara": [], - "ioc": [], - } - for rule_id in rules_ids: - rule_path = next( - chain( - find_by_content(DIR_RULES, rule_id, glob="*.yml"), - find_by_content(DIR_RULES, rule_id, glob="*.yaml"), - find_by_content(DIR_RULES, rule_id, glob="*.yar"), - ) - ) - print(f" [*] Found rule {rule_id} in {rule_path}") - rule_type = rule_path.relative_to(DIR_RULES).parts[0] - rules_by_category[rule_type].append(rule_id) - for key in [x for x in rules_by_category if not rules_by_category[x]]: - del rules_by_category[key] - pack["rules"] = {"has": rules_by_category} - modified = True - - sources = pack.get("sources") - if isinstance(sources, list): - print("[*] Converting sources list to dict") - pack["sources"] = {"manual": list(sources)} - modified = True - - if modified: - out = StringIO() - ryaml.dump(pack, out) - dumped = out.getvalue() - dumped_lines = [line.rstrip() for line in dumped.splitlines()] - file_pack.write_text("\n".join(dumped_lines) + "\n", encoding="utf-8") - pack = ryaml.load(file_pack.read_text(encoding="utf-8")) - - rules_dict = pack.get("rules") or {} - has_dict = rules_dict.get("has") or {} - - for category, rule_ids in has_dict.items(): - if category == "sigma": - for rule_id in rule_ids: - path_rule = next( - chain( - find_by_content(DIR_RULES, rule_id, glob="*.yml"), - find_by_content(DIR_RULES, rule_id, glob="*.yaml"), - ) - ) - path_rule_link = file_pack.parent / category / path_rule.name - symlink(path_rule, path_rule_link) - print(f" [*] Linked {rule_id} to {path_rule_link}") - elif category == "yara": - for rule_id in rule_ids: - path_rule = next(chain(find_by_content(DIR_RULES, rule_id, glob="*.yar"))) - path_rule_link = file_pack.parent / category / path_rule.name - symlink(path_rule, path_rule_link) - print(f" [*] Linked {rule_id} to {path_rule_link}") - elif category == "ioc": - iocs: dict[str, list[str]] = {} - for rule_id in rule_ids: - path_rule = next( - chain( - find_by_content(DIR_RULES, rule_id, glob="*.yml"), - find_by_content(DIR_RULES, rule_id, glob="*.yaml"), - ) - ) - rule = safe_load(path_rule.read_text()) - rule_severity = rule.get("severity") - for indicator_type, indicators in rule["indicators"].items(): - if indicator_type not in iocs: - iocs[indicator_type] = [] - for indicator in indicators: - if isinstance(indicator, dict): - ioc_value = indicator["value"] - ioc_comment = indicator.get("comment") - else: - ioc_value = indicator - ioc_comment = None - ioc_comment_parts: list[str] = [f"rule={rule_id}"] - if rule_severity: - ioc_comment_parts.append(f"severity={rule_severity}") - if ioc_comment: - ioc_comment_parts.append(f"comment={ioc_comment}") - ioc_comment_str = " ".join(ioc_comment_parts) - iocs[indicator_type].append(f"{ioc_value};{ioc_comment_str}") - for ioc_type, ioc_values in iocs.items(): - path_rule = file_pack.parent / category / f"{ioc_type}.txt" - path_rule.parent.mkdir(parents=True, exist_ok=True) - path_rule.write_text("\n".join(ioc_values)) - print(f" [*] Written {len(ioc_values)} {ioc_type} IOCs to {path_rule}") - - -if __name__ == "__main__": - main() From 510256f35ba1a4483a56cc160e53b6de35b7e0d4 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Wed, 10 Jun 2026 12:50:12 +0200 Subject: [PATCH 09/13] feat: flatten pack structure --- tools/build_packs.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tools/build_packs.py b/tools/build_packs.py index 905207f..992178e 100644 --- a/tools/build_packs.py +++ b/tools/build_packs.py @@ -79,6 +79,7 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: shutil.rmtree(out_dir) (out_dir / "sigma").mkdir(parents=True, exist_ok=True) (out_dir / "yara").mkdir(parents=True, exist_ok=True) + (out_dir / "ioc").mkdir(parents=True, exist_ok=True) resolved_ids = lib.resolve_pack_rules(pack, by_id) copied = [] @@ -91,11 +92,12 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: f"[build] pack '{pack_id}' references unknown artifact '{artifact_id}'" ) - if art.kind in ("sigma", "yara"): - dest = out_dir / art.kind / art.path.name - shutil.copy2(art.path, dest) - copied.append({"id": artifact_id, "kind": art.kind, "file": art.path.name}) - elif art.kind == "ioc": + dest = out_dir / art.kind / art.path.name + dest.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(art.path, dest) + copied.append({"id": artifact_id, "kind": art.kind, "file": art.path.name}) + + if art.kind == "ioc": count = 0 for ioc_type, entries in art.indicators.items(): for value, comment in entries: @@ -147,12 +149,12 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: "sha256": sha256_file(zip_path), # Drop-in paths for Rustinel config.toml (relative to dist/). "engine": { - "sigma_rules_path": f"{pack_id}/rules/sigma", - "yara_rules_path": f"{pack_id}/rules/yara", - "hashes_path": f"{pack_id}/rules/ioc/hashes.txt", - "ips_path": f"{pack_id}/rules/ioc/ips.txt", - "domains_path": f"{pack_id}/rules/ioc/domains.txt", - "paths_regex_path": f"{pack_id}/rules/ioc/paths_regex.txt", + "sigma_rules_path": f"{pack_id}/sigma", + "yara_rules_path": f"{pack_id}/yara", + "hashes_path": f"{pack_id}/ioc/hashes.txt", + "ips_path": f"{pack_id}/ioc/ips.txt", + "domains_path": f"{pack_id}/ioc/domains.txt", + "paths_regex_path": f"{pack_id}/ioc/paths_regex.txt", }, } From 51d6cb8759908548a18a346cf02b61ed95853674 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Wed, 10 Jun 2026 12:54:04 +0200 Subject: [PATCH 10/13] chore: pass ruff --- tools/upload_radegast.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/upload_radegast.py b/tools/upload_radegast.py index 7beb5b4..5186c02 100644 --- a/tools/upload_radegast.py +++ b/tools/upload_radegast.py @@ -3,9 +3,9 @@ """ from json import loads -from typing import TypedDict from os import environ from pathlib import Path +from typing import TypedDict from requests import get, post @@ -31,7 +31,10 @@ def fetch_radegast_packs() -> list[RadegastPack]: newest_version=None ) for x in r.json()] for pack in packs: - r = get(f"{RADEGAST_URL}/api/v1/packs/{pack['id']}/versions", headers={"X-API-Key": RADEGAST_API_KEY}) + r = get( + f"{RADEGAST_URL}/api/v1/packs/{pack['id']}/versions", + headers={"X-API-Key": RADEGAST_API_KEY} + ) r.raise_for_status() versions = r.json() for version in versions: @@ -61,7 +64,7 @@ def main() -> None: json={ "pack_id": pack_id, "name": pack_name, - "description": f"Rustinel pack from https://github.com/Karib0u/rustinel-rules/.", + "description": "Rustinel pack from https://github.com/Karib0u/rustinel-rules/.", } ) r.raise_for_status() From 6b4d2fb629f7a750287bf7cc3662c520c3fa0d37 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Fri, 12 Jun 2026 20:15:45 +0200 Subject: [PATCH 11/13] chore: flatten docs # Conflicts: # docs/usage.md --- README.md | 12 ++-- docs/repository.md | 21 ++++--- docs/usage.md | 29 +++++----- pyproject.toml | 3 - tools/build_packs.py | 8 ++- tools/lib.py | 17 +++--- tools/upload_radegast.py | 90 ----------------------------- uv.lock | 119 --------------------------------------- 8 files changed, 42 insertions(+), 257 deletions(-) delete mode 100644 tools/upload_radegast.py diff --git a/README.md b/README.md index a538401..b66be1d 100644 --- a/README.md +++ b/README.md @@ -49,14 +49,14 @@ unzip windows-essential-0.2.0.zip ```toml [scanner] -sigma_rules_path = "windows-essential/rules/sigma" -yara_rules_path = "windows-essential/rules/yara" +sigma_rules_path = "windows-essential/sigma" +yara_rules_path = "windows-essential/yara" [ioc] -hashes_path = "windows-essential/rules/ioc/hashes.txt" -ips_path = "windows-essential/rules/ioc/ips.txt" -domains_path = "windows-essential/rules/ioc/domains.txt" -paths_regex_path = "windows-essential/rules/ioc/paths_regex.txt" +hashes_path = "windows-essential/ioc/hashes.txt" +ips_path = "windows-essential/ioc/ips.txt" +domains_path = "windows-essential/ioc/domains.txt" +paths_regex_path = "windows-essential/ioc/paths_regex.txt" ``` **3. Confirm it works.** The Essential packs ship the **EICAR** test IOC set — drop a standard EICAR test file on disk and Rustinel raises an IOC alert in `logs/alerts.json.`. diff --git a/docs/repository.md b/docs/repository.md index 74d2fe0..88cbb74 100644 --- a/docs/repository.md +++ b/docs/repository.md @@ -84,10 +84,9 @@ dist/ ├── catalog.json # Website catalog: rules, ATT&CK techniques, packs ├── windows-essential/ # Materialized flat pack folder (engine drop-in) │ ├── pack.yml # Cleaned manifest + build metadata (version, counts) -│ └── rules/ -│ ├── sigma/ # .yml -> scanner.sigma_rules_path -│ ├── yara/ # .yar -> scanner.yara_rules_path -│ └── ioc/ # hashes.txt / ips.txt / domains.txt / paths_regex.txt +│ ├── sigma/ # .yml -> scanner.sigma_rules_path +│ ├── yara/ # .yar -> scanner.yara_rules_path +│ └── ioc/ # hashes.txt / ips.txt / domains.txt / paths_regex.txt ├── windows-essential-.zip # Rustinel-compatible artifact └── ... ``` @@ -95,7 +94,7 @@ dist/ What the build does for each pack: 1. **Resolves** the cumulative rule list (`extends` + `rules` with `has`/`includes`/`excludes` or pack subfolders, de-duplicated). -2. **Copies** each Sigma/YARA file into the flat `rules/sigma` and `rules/yara` folders. +2. **Copies** each Sigma/YARA file into the flat `sigma` and `yara` folders. 3. **Flattens** every referenced IOC set into the four per-type files in `VALUE;COMMENT` format, prefixing each line with its source set id (`[ioc-…]`) so provenance survives into alerts. 4. **Writes** a cleaned `pack.yml` with build metadata and zips the folder. @@ -133,12 +132,12 @@ automatically: "sha256": "…", "artifact": "windows-essential-0.2.0.zip", "engine": { - "sigma_rules_path": "windows-essential/rules/sigma", - "yara_rules_path": "windows-essential/rules/yara", - "hashes_path": "windows-essential/rules/ioc/hashes.txt", - "ips_path": "windows-essential/rules/ioc/ips.txt", - "domains_path": "windows-essential/rules/ioc/domains.txt", - "paths_regex_path": "windows-essential/rules/ioc/paths_regex.txt" + "sigma_rules_path": "windows-essential/sigma", + "yara_rules_path": "windows-essential/yara", + "hashes_path": "windows-essential/ioc/hashes.txt", + "ips_path": "windows-essential/ioc/ips.txt", + "domains_path": "windows-essential/ioc/domains.txt", + "paths_regex_path": "windows-essential/ioc/paths_regex.txt" } } ``` diff --git a/docs/usage.md b/docs/usage.md index efabd3e..c490fb6 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -18,11 +18,13 @@ dist/ ├── index.json ├── windows-essential/ # <- drop-in folder │ ├── pack.yml -│ └── rules/{sigma,yara,ioc}/ -└── windows-essential-0.2.0.zip # <- distributable artifact +│ ├── sigma/ +│ ├── yara/ +│ └── ioc/ +└── windows-essential-0.1.0.zip # <- distributable artifact ``` -Pass `--version X.Y.Z` to `build_packs.py` to stamp a release version (default `0.2.0`). +Pass `--version X.Y.Z` to `build_packs.py` to stamp a release version (default `0.1.0`). ## 2. Point `config.toml` at the pack @@ -32,16 +34,16 @@ Use the paths from the pack's `engine` block in `dist/index.json` (shown here fo ```toml [scanner] sigma_enabled = true -sigma_rules_path = "windows-essential/rules/sigma" +sigma_rules_path = "windows-essential/sigma" yara_enabled = true -yara_rules_path = "windows-essential/rules/yara" +yara_rules_path = "windows-essential/yara" [ioc] enabled = true -hashes_path = "windows-essential/rules/ioc/hashes.txt" -ips_path = "windows-essential/rules/ioc/ips.txt" -domains_path = "windows-essential/rules/ioc/domains.txt" -paths_regex_path = "windows-essential/rules/ioc/paths_regex.txt" +hashes_path = "windows-essential/ioc/hashes.txt" +ips_path = "windows-essential/ioc/ips.txt" +domains_path = "windows-essential/ioc/domains.txt" +paths_regex_path = "windows-essential/ioc/paths_regex.txt" ``` > Swap `windows-essential` for any other pack id (`windows-advanced`, `linux-essential`, …). Because @@ -67,11 +69,8 @@ Rebuild a pack into the same location and the engine picks it up without a resta ## `config.toml` reference (rule-relevant sections) -This is the pack-installer's subset of the engine config — only the keys that affect loading and -matching detection content. The **full, canonical reference** (logging, alerts, network -aggregation, every default) lives in the engine docs: -[Configuration](https://docs.rustinel.io/configuration/). Defaults shown here are the engine -defaults; only the paths above are required to load a pack, the rest are tuning knobs. +Defaults shown are the engine defaults. Only the paths above are required to load a pack; the rest +are tuning knobs. ### `[scanner]` — Sigma & YARA @@ -115,7 +114,6 @@ defaults; only the paths above are required to load a pack, the rest are tuning | --- | ------- | ------- | | `paths` | OS-shipped dirs (e.g. `C:\Windows\`, `/usr/bin/`, `/System/`) | Trusted prefixes applied to YARA, hash IOC, and response. Per-module allowlists fall back to this. | - ### `[response]` — optional active response | Key | Default | Meaning | @@ -145,3 +143,4 @@ defaults; only the paths above are required to load a pack, the rest are tuning For the engine itself (install, run as a service/daemon, telemetry setup), see the [Rustinel documentation](https://docs.rustinel.io/). + diff --git a/pyproject.toml b/pyproject.toml index 5a3bfea..cc07181 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,9 +16,6 @@ dev = [ "ruff>=0.15.16", "ty>=0.0.45", ] -radegast = [ - "requests>=2.34.2", -] # This repo ships detection content plus helper scripts under tools/; it is not a # distributable Python package, so uv treats it as a non-package project (no build diff --git a/tools/build_packs.py b/tools/build_packs.py index 35c9b7e..5e4e0a0 100644 --- a/tools/build_packs.py +++ b/tools/build_packs.py @@ -95,8 +95,6 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: dest = out_dir / art.kind / art.path.name dest.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(art.path, dest) - copied.append({"id": artifact_id, "kind": art.kind, "file": art.path.name}) - if art.kind == "ioc": count = 0 for ioc_type, entries in art.indicators.items(): @@ -105,7 +103,11 @@ def materialize_pack(pack: dict, by_id, artifact_index, version: str) -> dict: full = f"[{art.id}] {comment}" if comment else f"[{art.id}]" ioc_acc[ioc_type].append((value, full)) count += 1 - copied.append({"id": artifact_id, "kind": "ioc", "indicators": count}) + copied.append( + {"id": artifact_id, "kind": "ioc", "file": art.path.name, "indicators": count} + ) + else: + copied.append({"id": artifact_id, "kind": art.kind, "file": art.path.name}) ioc_counts = write_ioc_files(out_dir / "ioc", ioc_acc) diff --git a/tools/lib.py b/tools/lib.py index 9186e6a..afc8d9d 100644 --- a/tools/lib.py +++ b/tools/lib.py @@ -9,9 +9,9 @@ content. A pack materializes (see ``build_packs.py``) into exactly the layout the Rustinel engine loads: - rules/sigma/ recursive dir of Sigma .yml -> scanner.sigma_rules_path - rules/yara/ recursive dir of YARA .yar -> scanner.yara_rules_path - rules/ioc/hashes.txt ips.txt domains.txt paths_regex.txt + sigma/ recursive dir of Sigma .yml -> scanner.sigma_rules_path + yara/ recursive dir of YARA .yar -> scanner.yara_rules_path + ioc/hashes.txt ips.txt domains.txt paths_regex.txt -> [ioc].*_path Requires PyYAML (see tools/requirements.txt). @@ -201,15 +201,12 @@ def _get_rule_ids_from_dict(sub_dict: dict | list | None) -> list[str]: def get_pack_subfolder_rules(pack: dict) -> dict[str, list[str]]: - """Scan the pack's 'rules' subfolder and return a dict of {category: [rule_ids]}.""" + """Scan the pack's directories and return a dict of {category: [rule_ids]}.""" result = {"sigma": [], "yara": [], "ioc": []} pack_path_str = pack.get("__path__") if not pack_path_str: return result pack_dir = Path(pack_path_str).parent - rules_dir = pack_dir / "rules" - if not rules_dir.is_dir(): - return result # Build a map of filename to artifact ID from canonical rules filename_to_id = {} @@ -218,7 +215,7 @@ def get_pack_subfolder_rules(pack: dict) -> dict[str, list[str]]: filename_to_id[(art.kind, art.path.name)] = art.id # 1. Sigma - sigma_dir = rules_dir / "sigma" + sigma_dir = pack_dir / "sigma" if sigma_dir.is_dir(): for file in sorted(sigma_dir.glob("*")): if file.is_file() and file.suffix in (".yml", ".yaml"): @@ -234,7 +231,7 @@ def get_pack_subfolder_rules(pack: dict) -> dict[str, list[str]]: pass # 2. Yara - yara_dir = rules_dir / "yara" + yara_dir = pack_dir / "yara" if yara_dir.is_dir(): for file in sorted(yara_dir.glob("*")): if file.is_file() and file.suffix in (".yar", ".yara"): @@ -254,7 +251,7 @@ def get_pack_subfolder_rules(pack: dict) -> dict[str, list[str]]: pass # 3. IOC - ioc_dir = rules_dir / "ioc" + ioc_dir = pack_dir / "ioc" if ioc_dir.is_dir(): ioc_rule_ids = set() for file in ioc_dir.glob("*.txt"): diff --git a/tools/upload_radegast.py b/tools/upload_radegast.py deleted file mode 100644 index 5186c02..0000000 --- a/tools/upload_radegast.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Uploads built packages to Radegast EDR via API -""" - -from json import loads -from os import environ -from pathlib import Path -from typing import TypedDict - -from requests import get, post - -RADEGAST_URL = environ.get("RADEGAST_URL", "https://console.radegast.app") -RADEGAST_API_KEY = environ["RADEGAST_API_KEY"] -DIR_ROOT = Path(__file__).parent.parent -DIR_DIST = DIR_ROOT / "dist" -FILE_INDEX = DIR_DIST / "index.json" - - -class RadegastPack(TypedDict): - id: int - pack_id: str - newest_version: str | None - - -def fetch_radegast_packs() -> list[RadegastPack]: - r = get(f"{RADEGAST_URL}/api/v1/packs/") - r.raise_for_status() - packs: list[RadegastPack] = [RadegastPack( - id=x["id"], - pack_id=x["pack_id"], - newest_version=None - ) for x in r.json()] - for pack in packs: - r = get( - f"{RADEGAST_URL}/api/v1/packs/{pack['id']}/versions", - headers={"X-API-Key": RADEGAST_API_KEY} - ) - r.raise_for_status() - versions = r.json() - for version in versions: - pack["newest_version"] = version["version"] - break - return packs - - -def main() -> None: - remote_packs = fetch_radegast_packs() - print(f"[*] Fetched {len(remote_packs)} Radegast packs") - - packs = loads(FILE_INDEX.read_text())['packs'] - for pack in packs: - pack_id = f"rustinel-{pack['id']}" - pack_name = f"Rustinel: {pack['name']}" - pack_version = pack['version'] - file_pack = DIR_DIST / f"{pack['artifact']}" - assert file_pack.exists(), f"Pack file not found: {file_pack}" - - remote_pack = next((p for p in remote_packs if p['pack_id'] == pack_id), None) - if remote_pack is None: - print(f"[*] Creating new pack {pack_id} -- {pack_name}") - r = post( - f"{RADEGAST_URL}/api/v1/packs/", - headers={"X-API-Key": RADEGAST_API_KEY}, - json={ - "pack_id": pack_id, - "name": pack_name, - "description": "Rustinel pack from https://github.com/Karib0u/rustinel-rules/.", - } - ) - r.raise_for_status() - remote_pack = RadegastPack( - id=r.json()["id"], - pack_id=r.json()["pack_id"], - newest_version=None - ) - - if remote_pack["newest_version"] != pack_version: - print(f"[*] Uploading pack {pack_id} version {pack_version} to Radegast") - with file_pack.open("rb") as f: - r = post( - f"{RADEGAST_URL}/api/v1/packs/{remote_pack['id']}/versions", - headers={"X-API-Key": RADEGAST_API_KEY}, - files={"file": (file_pack.name, f, "application/zip")}, - params={"version": pack_version}, - ) - r.raise_for_status() - - -if __name__ == "__main__": - main() diff --git a/uv.lock b/uv.lock index 1206295..14fa884 100644 --- a/uv.lock +++ b/uv.lock @@ -15,97 +15,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "certifi" -version = "2026.5.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, -] - -[[package]] -name = "idna" -version = "3.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, -] - [[package]] name = "jsonschema" version = "4.26.0" @@ -193,21 +102,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] -[[package]] -name = "requests" -version = "2.34.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, -] - [[package]] name = "rpds-py" version = "2026.5.1" @@ -368,9 +262,6 @@ dev = [ { name = "ruff" }, { name = "ty" }, ] -radegast = [ - { name = "requests" }, -] [package.metadata] requires-dist = [ @@ -385,7 +276,6 @@ dev = [ { name = "ruff", specifier = ">=0.15.16" }, { name = "ty", specifier = ">=0.0.45" }, ] -radegast = [{ name = "requests", specifier = ">=2.34.2" }] [[package]] name = "ty" @@ -421,15 +311,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] -[[package]] -name = "urllib3" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, -] - [[package]] name = "yara-x" version = "1.17.0" From 55b1a8d4ae2cb5d3aedcc018506234302f72fc95 Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Fri, 12 Jun 2026 20:28:50 +0200 Subject: [PATCH 12/13] chore: bump schema version --- README.md | 2 +- docs/packs.md | 2 +- docs/repository.md | 2 +- packs/linux/advanced/pack.yml | 2 +- packs/linux/essential/pack.yml | 2 +- packs/macos/advanced/pack.yml | 2 +- packs/macos/essential/pack.yml | 2 +- packs/windows/advanced/pack.yml | 2 +- packs/windows/essential/pack.yml | 2 +- packs/windows/hunting/pack.yml | 2 +- schemas/pack.schema.json | 4 ++-- tools/validate.py | 4 ++-- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b66be1d..6e1403f 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Full catalog and per-pack rule inventory: **[docs/packs.md](docs/packs.md)**. `rustinel-rules` is versioned **independently** from the engine — detection content evolves faster. Each pack manifest declares the engine version it needs: ```yaml -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" ``` diff --git a/docs/packs.md b/docs/packs.md index c192ce3..0fa0e5e 100644 --- a/docs/packs.md +++ b/docs/packs.md @@ -18,7 +18,7 @@ Essential ⊂ Advanced ⊂ Hunting | [macOS Essential](#macos-essential) | essential | ❌ | low | experimental | | [macOS Advanced](#macos-advanced) | advanced | ❌ | medium | experimental | -> All packs declare `pack_schema_version: 1`, `requires_rustinel: ">=1.0.2"`, and license +> All packs declare `pack_schema_version: 2`, `requires_rustinel: ">=1.0.2"`, and license > `DRL-1.1`. `status: experimental` reflects the early state of v1 content — expect curation to > tighten as coverage grows. diff --git a/docs/repository.md b/docs/repository.md index 88cbb74..294e03c 100644 --- a/docs/repository.md +++ b/docs/repository.md @@ -53,7 +53,7 @@ Key manifest fields: | `id` / `name` | Stable id (`^[a-z0-9]+(-[a-z0-9]+)*$`) and human name. | | `os` | `windows` \| `linux` \| `macos`. | | `level` | `essential` \| `advanced` \| `hunting`. | -| `pack_schema_version` | Required pack manifest schema version (must be `1`). | +| `pack_schema_version` | Required pack manifest schema version (must be `2`). | | `default` | Whether this pack is enabled by default. | | `extends` | Pack ids cumulatively included (rules merged, never duplicated). | | `rules` | Optional dictionary specifying rules directly in this pack (`has`), or rules to include (`includes`) / exclude (`excludes`) from extended packs or automatic sources. | diff --git a/packs/linux/advanced/pack.yml b/packs/linux/advanced/pack.yml index a913818..c5b9457 100644 --- a/packs/linux/advanced/pack.yml +++ b/packs/linux/advanced/pack.yml @@ -6,7 +6,7 @@ description: > before relying on by default. os: linux level: advanced -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" default: false expected_false_positive_level: medium diff --git a/packs/linux/essential/pack.yml b/packs/linux/essential/pack.yml index 5c3a5ff..0cbc94a 100644 --- a/packs/linux/essential/pack.yml +++ b/packs/linux/essential/pack.yml @@ -3,7 +3,7 @@ id: linux-essential description: Low-noise, high-confidence Linux detections for Rustinel. Safe default pack. os: linux level: essential -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" default: true expected_false_positive_level: low diff --git a/packs/macos/advanced/pack.yml b/packs/macos/advanced/pack.yml index c2f943d..35d6214 100644 --- a/packs/macos/advanced/pack.yml +++ b/packs/macos/advanced/pack.yml @@ -8,7 +8,7 @@ description: > post-v1; disabled by default. os: macos level: advanced -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" default: false expected_false_positive_level: medium diff --git a/packs/macos/essential/pack.yml b/packs/macos/essential/pack.yml index bbfaa35..31f9214 100644 --- a/packs/macos/essential/pack.yml +++ b/packs/macos/essential/pack.yml @@ -7,7 +7,7 @@ description: > production-ready, so this pack is disabled by default. os: macos level: essential -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" default: false expected_false_positive_level: low diff --git a/packs/windows/advanced/pack.yml b/packs/windows/advanced/pack.yml index 353246f..de16f60 100644 --- a/packs/windows/advanced/pack.yml +++ b/packs/windows/advanced/pack.yml @@ -5,7 +5,7 @@ description: > occur than in Essential; tune per environment before relying on by default. os: windows level: advanced -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" default: false expected_false_positive_level: medium diff --git a/packs/windows/essential/pack.yml b/packs/windows/essential/pack.yml index 7b0e41b..d67481c 100644 --- a/packs/windows/essential/pack.yml +++ b/packs/windows/essential/pack.yml @@ -3,7 +3,7 @@ id: windows-essential description: Low-noise, high-confidence Windows detections for Rustinel. Safe default pack. os: windows level: essential -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" default: true expected_false_positive_level: low diff --git a/packs/windows/hunting/pack.yml b/packs/windows/hunting/pack.yml index 73be129..330e443 100644 --- a/packs/windows/hunting/pack.yml +++ b/packs/windows/hunting/pack.yml @@ -6,7 +6,7 @@ description: > without tuning. os: windows level: hunting -pack_schema_version: 1 +pack_schema_version: 2 requires_rustinel: ">=1.0.2" default: false expected_false_positive_level: high diff --git a/schemas/pack.schema.json b/schemas/pack.schema.json index e94e498..3cf6f1c 100644 --- a/schemas/pack.schema.json +++ b/schemas/pack.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://github.com/rustinel-rules/schemas/pack.schema.json", - "title": "Rustinel pack.yml (v1)", + "title": "Rustinel pack.yml (v2)", "description": "Schema for a rustinel-rules pack manifest. Packs reference canonical rules by id and may extend lower-level packs cumulatively (Essential ⊂ Advanced ⊂ Hunting).", "type": "object", "additionalProperties": false, @@ -42,7 +42,7 @@ }, "pack_schema_version": { "type": "integer", - "const": 1 + "const": 2 }, "requires_rustinel": { "type": "string", diff --git a/tools/validate.py b/tools/validate.py index 8aa5492..5c4cff1 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -254,8 +254,8 @@ def check_packs(packs, artifacts, rep: Report): for field in REQUIRED_PACK_FIELDS: if field not in pack: rep.error(where, f"missing required field '{field}'") - if pack.get("pack_schema_version") != 1: - rep.error(where, "pack_schema_version must be 1 for v1") + if pack.get("pack_schema_version") != 2: + rep.error(where, "pack_schema_version must be 2 for v2") if not pack.get("license"): rep.warn(where, "no 'license' field on pack") From cc743bbc3d7222c6736935edc71e7203ae0e744e Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Fri, 12 Jun 2026 20:37:42 +0200 Subject: [PATCH 13/13] fix(atomic): rules path --- tests/atomic/run_atomics.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/atomic/run_atomics.py b/tests/atomic/run_atomics.py index 75dc9a7..967cda6 100644 --- a/tests/atomic/run_atomics.py +++ b/tests/atomic/run_atomics.py @@ -222,16 +222,16 @@ def setup_engine(engine_dir: Path, dist_dir: Path, pack: dict) -> Path: config = f"""# Generated by run_atomics.py - points the engine at pack '{pack_id}'. [scanner] sigma_enabled = true -sigma_rules_path = "{pack_id}/rules/sigma" +sigma_rules_path = "{pack_id}/sigma" yara_enabled = true -yara_rules_path = "{pack_id}/rules/yara" +yara_rules_path = "{pack_id}/yara" [ioc] enabled = true -hashes_path = "{pack_id}/rules/ioc/hashes.txt" -ips_path = "{pack_id}/rules/ioc/ips.txt" -domains_path = "{pack_id}/rules/ioc/domains.txt" -paths_regex_path = "{pack_id}/rules/ioc/paths_regex.txt" +hashes_path = "{pack_id}/ioc/hashes.txt" +ips_path = "{pack_id}/ioc/ips.txt" +domains_path = "{pack_id}/ioc/domains.txt" +paths_regex_path = "{pack_id}/ioc/paths_regex.txt" [logging] level = "info"