pysigma-backend-parseable 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. pysigma_backend_parseable-0.1.0/.github/workflows/ci.yml +108 -0
  2. pysigma_backend_parseable-0.1.0/.gitignore +10 -0
  3. pysigma_backend_parseable-0.1.0/CHANGELOG.md +29 -0
  4. pysigma_backend_parseable-0.1.0/LICENSE +21 -0
  5. pysigma_backend_parseable-0.1.0/PKG-INFO +327 -0
  6. pysigma_backend_parseable-0.1.0/README.md +308 -0
  7. pysigma_backend_parseable-0.1.0/examples/placeholder-pipeline.yml +24 -0
  8. pysigma_backend_parseable-0.1.0/examples/placeholder-rule.yml +11 -0
  9. pysigma_backend_parseable-0.1.0/examples/powershell.yml +14 -0
  10. pysigma_backend_parseable-0.1.0/pyproject.toml +42 -0
  11. pysigma_backend_parseable-0.1.0/reports/corpus-baseline.json +14 -0
  12. pysigma_backend_parseable-0.1.0/reports/corpus-report.md +75 -0
  13. pysigma_backend_parseable-0.1.0/scripts/corpus_sweep.py +365 -0
  14. pysigma_backend_parseable-0.1.0/sigma/backends/parseable/__init__.py +3 -0
  15. pysigma_backend_parseable-0.1.0/sigma/backends/parseable/parseable.py +342 -0
  16. pysigma_backend_parseable-0.1.0/sigma/pipelines/parseable/__init__.py +17 -0
  17. pysigma_backend_parseable-0.1.0/sigma/pipelines/parseable/ecs.py +123 -0
  18. pysigma_backend_parseable-0.1.0/sigma/pipelines/parseable/otlp.py +114 -0
  19. pysigma_backend_parseable-0.1.0/sigma/pipelines/parseable/sysmon.py +61 -0
  20. pysigma_backend_parseable-0.1.0/tests/integration/fixtures/cidr-events.json +12 -0
  21. pysigma_backend_parseable-0.1.0/tests/integration/fixtures/events.json +131 -0
  22. pysigma_backend_parseable-0.1.0/tests/integration/fixtures/numeric-keyword-events.json +6 -0
  23. pysigma_backend_parseable-0.1.0/tests/integration/fixtures/pipeline-events.json +24 -0
  24. pysigma_backend_parseable-0.1.0/tests/integration/test_parseable.py +158 -0
  25. pysigma_backend_parseable-0.1.0/tests/integration/test_pipeline_semantics.py +75 -0
  26. pysigma_backend_parseable-0.1.0/tests/test_backend.py +153 -0
  27. pysigma_backend_parseable-0.1.0/tests/test_cidr.py +26 -0
  28. pysigma_backend_parseable-0.1.0/tests/test_corpus_sweep.py +39 -0
  29. pysigma_backend_parseable-0.1.0/tests/test_extended_backend.py +263 -0
  30. pysigma_backend_parseable-0.1.0/tests/test_pipelines.py +173 -0
  31. pysigma_backend_parseable-0.1.0/tests/test_unsupported.py +55 -0
@@ -0,0 +1,108 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ release:
7
+ types: [published]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test:
15
+ name: Python ${{ matrix.python-version }}
16
+ runs-on: ubuntu-latest
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
21
+
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: actions/setup-python@v5
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+ cache: pip
28
+ cache-dependency-path: pyproject.toml
29
+ - name: Install test dependencies
30
+ run: python -m pip install --upgrade pip && python -m pip install -e '.[test]' sigma-cli build
31
+ - name: Lint
32
+ run: ruff check .
33
+ - name: Unit tests
34
+ run: pytest tests -m 'not integration' --cov=sigma.backends.parseable --cov=sigma.pipelines.parseable --cov-report=term-missing
35
+ - name: Sigma CLI discovery and conversion
36
+ run: |
37
+ sigma list targets
38
+ sigma list pipelines parseable
39
+ sigma convert -t parseable -O dataset=windows-events examples/powershell.yml > /tmp/parseable-query.sql
40
+ sigma convert -t parseable -p parseable_otlp -O dataset=otel-events examples/powershell.yml > /tmp/parseable-otlp-query.sql
41
+ sigma convert -t parseable -p parseable_sysmon -p parseable_ecs -O dataset=ecs-events examples/powershell.yml > /tmp/parseable-ecs-query.sql
42
+ test -s /tmp/parseable-query.sql
43
+ test -s /tmp/parseable-otlp-query.sql
44
+ test -s /tmp/parseable-ecs-query.sql
45
+ - name: Build distributions
46
+ if: matrix.python-version == '3.14'
47
+ run: python -m build
48
+
49
+ corpus-regression:
50
+ name: Sigma corpus regression
51
+ runs-on: ubuntu-latest
52
+ env:
53
+ SIGMA_CORPUS_COMMIT: 5c9b21756f4e3ba137c1773ac9ba5a8332188961
54
+
55
+ steps:
56
+ - uses: actions/checkout@v4
57
+ - uses: actions/setup-python@v5
58
+ with:
59
+ python-version: "3.14"
60
+ cache: pip
61
+ cache-dependency-path: pyproject.toml
62
+ - name: Install backend
63
+ run: python -m pip install --upgrade pip && python -m pip install -e '.[test]'
64
+ - name: Fetch pinned Sigma corpus
65
+ run: |
66
+ git init "$RUNNER_TEMP/sigma-corpus"
67
+ git -C "$RUNNER_TEMP/sigma-corpus" remote add origin https://github.com/SigmaHQ/sigma.git
68
+ git -C "$RUNNER_TEMP/sigma-corpus" fetch --depth 1 origin "$SIGMA_CORPUS_COMMIT"
69
+ git -C "$RUNNER_TEMP/sigma-corpus" checkout --detach FETCH_HEAD
70
+ - name: Run compatibility regression gate
71
+ run: |
72
+ python scripts/corpus_sweep.py "$RUNNER_TEMP/sigma-corpus" \
73
+ --baseline reports/corpus-baseline.json \
74
+ --output-dir "$RUNNER_TEMP/corpus-report"
75
+ - name: Upload full corpus report
76
+ if: always()
77
+ uses: actions/upload-artifact@v4
78
+ with:
79
+ name: corpus-report
80
+ path: ${{ runner.temp }}/corpus-report/
81
+ if-no-files-found: error
82
+
83
+ publish:
84
+ name: Publish to PyPI
85
+ needs: [test, corpus-regression]
86
+ runs-on: ubuntu-latest
87
+ if: github.event_name == 'release'
88
+
89
+ steps:
90
+ - uses: actions/checkout@v4
91
+ - uses: actions/setup-python@v5
92
+ with:
93
+ python-version: "3.14"
94
+ - name: Install build tools
95
+ run: python -m pip install --upgrade pip build twine
96
+ - name: Verify release version
97
+ run: |
98
+ package_version="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
99
+ test "v${package_version}" = "${GITHUB_REF_NAME}"
100
+ - name: Build distributions
101
+ run: python -m build
102
+ - name: Check distributions
103
+ run: twine check dist/*
104
+ - name: Publish to PyPI
105
+ env:
106
+ TWINE_USERNAME: __token__
107
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
108
+ run: twine upload dist/* --verbose
@@ -0,0 +1,10 @@
1
+ /.coverage
2
+ /.env
3
+ /.pytest_cache/
4
+ /.ruff_cache/
5
+ /build/
6
+ /dist/
7
+ /reports/corpus-report.json
8
+ *.egg-info/
9
+ __pycache__/
10
+ *.py[cod]
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ ## 0.1.0 - 2026-09-08
6
+
7
+ Initial release.
8
+
9
+ ### Added
10
+
11
+ - Parseable SQL and predicate output formats.
12
+ - Sigma CLI and Python API integration through pySigma plugin discovery.
13
+ - Case-sensitive and case-insensitive string matching, wildcards, lists, comparisons,
14
+ regular expressions, null checks, existence checks, field references, and fieldless
15
+ keyword searches.
16
+ - IPv4 and IPv6 CIDR conversion using Parseable's `ip_in_cidr` SQL function. CIDR queries
17
+ require Parseable v3.2.1 or newer.
18
+ - Built-in processing pipelines for Parseable OTLP, flattened ECS, and native Sysmon schemas.
19
+ - Custom placeholder and field-mapping pipeline support.
20
+ - Unit and live Parseable integration tests.
21
+ - Pinned Sigma corpus compatibility regression checks in GitHub Actions.
22
+
23
+ ### Known limitations
24
+
25
+ - Sigma correlation rules are not supported.
26
+ - Timestamp-part modifiers are not supported.
27
+ - Fieldless regular expressions are not supported.
28
+ - Deployment-specific Sigma placeholders require values supplied through a processing
29
+ pipeline.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Parseable pySigma contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,327 @@
1
+ Metadata-Version: 2.5
2
+ Name: pysigma-backend-parseable
3
+ Version: 0.1.0
4
+ Summary: pySigma backend for Parseable SQL queries
5
+ Project-URL: Homepage, https://github.com/parseablehq/pySigma-backend-parseable
6
+ Project-URL: Issues, https://github.com/parseablehq/pySigma-backend-parseable/issues
7
+ Author: Parseable pySigma contributors
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: parseable,pysigma,security,sigma,sql
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: pysigma<2,>=1.0.0
13
+ Provides-Extra: test
14
+ Requires-Dist: coverage[toml]>=7.8; extra == 'test'
15
+ Requires-Dist: pytest-cov>=6.1; extra == 'test'
16
+ Requires-Dist: pytest>=8.3; extra == 'test'
17
+ Requires-Dist: ruff>=0.12; extra == 'test'
18
+ Description-Content-Type: text/markdown
19
+
20
+ # pySigma backend for Parseable
21
+
22
+ Convert [Sigma](https://github.com/SigmaHQ/sigma) detection rules into SQL accepted by
23
+ [Parseable](https://github.com/parseablehq/parseable).
24
+
25
+ The backend generates SQL only. It does not create alerts, send queries, discover datasets,
26
+ or infer how fields are stored in your Parseable instance.
27
+
28
+ ## Requirements
29
+
30
+ - Python 3.10 or newer
31
+ - pySigma 1.x
32
+ - Parseable v3.2.1 or newer when executing generated CIDR queries
33
+ - A Parseable dataset when executing the generated SQL
34
+
35
+ ## Installation
36
+
37
+ The package is not yet published. Install it from a checkout:
38
+
39
+ ```bash
40
+ python -m venv .venv
41
+ source .venv/bin/activate
42
+ python -m pip install -e .
43
+ ```
44
+
45
+ For the `sigma` command-line interface:
46
+
47
+ ```bash
48
+ python -m pip install sigma-cli
49
+ ```
50
+
51
+ Confirm that the plugin is available:
52
+
53
+ ```bash
54
+ sigma list targets
55
+ sigma list pipelines parseable
56
+ ```
57
+
58
+ ## Quick start
59
+
60
+ Convert a rule into a complete query:
61
+
62
+ ```bash
63
+ sigma convert \
64
+ --target parseable \
65
+ --backend-option dataset=windows-events \
66
+ examples/powershell.yml
67
+ ```
68
+
69
+ Example output:
70
+
71
+ ```sql
72
+ SELECT "Image", "CommandLine", "User"
73
+ FROM "windows-events"
74
+ WHERE LOWER("Image") LIKE '%\\powershell.exe' ESCAPE '\'
75
+ AND LOWER("CommandLine") LIKE '%-encodedcommand%' ESCAPE '\'
76
+ ```
77
+
78
+ The backend quotes dataset and column names, so dotted names such as `service.name` are
79
+ rendered as one SQL identifier: `"service.name"`.
80
+
81
+ ### Python API
82
+
83
+ ```python
84
+ from pathlib import Path
85
+
86
+ from sigma.backends.parseable import ParseableBackend
87
+ from sigma.collection import SigmaCollection
88
+
89
+ rules = SigmaCollection.from_yaml(Path("rule.yml").read_text())
90
+ backend = ParseableBackend(dataset="windows-events")
91
+
92
+ for query in backend.convert(rules):
93
+ print(query)
94
+ ```
95
+
96
+ `convert()` returns a list because a Sigma document may contain multiple rules or
97
+ conditions.
98
+
99
+ ## Output formats
100
+
101
+ The default format produces a complete query and requires a dataset:
102
+
103
+ ```bash
104
+ sigma convert -t parseable -O dataset=windows-events rule.yml
105
+ ```
106
+
107
+ ```sql
108
+ SELECT * FROM "windows-events" WHERE "EventID" = 4625
109
+ ```
110
+
111
+ The `predicate` format produces only the condition for embedding in another query. It does
112
+ not require a dataset:
113
+
114
+ ```bash
115
+ sigma convert -t parseable -f predicate rule.yml
116
+ ```
117
+
118
+ ```sql
119
+ "EventID" = 4625
120
+ ```
121
+
122
+ ## Backend options
123
+
124
+ | Option | Default | Description |
125
+ | ----------------------- | ----------------------------- | ------------------------------------------------------------ |
126
+ | `dataset` | none | Dataset in the `FROM` clause; required for default output |
127
+ | `limit` | none | Positive integer appended as `LIMIT` |
128
+ | `default_search_fields` | `body,message,event.original` | Comma-separated columns searched by fieldless Sigma keywords |
129
+
130
+ Example:
131
+
132
+ ```bash
133
+ sigma convert \
134
+ -t parseable \
135
+ -O dataset=application-logs \
136
+ -O limit=500 \
137
+ -O default_search_fields=body,message,log \
138
+ rule.yml
139
+ ```
140
+
141
+ Every configured search field must exist in the target dataset. DataFusion rejects a query
142
+ that references a missing column.
143
+
144
+ ## Field mapping pipelines
145
+
146
+ Sigma rules use abstract field names. Parseable queries must use the exact columns created
147
+ at ingestion. Select the pipeline matching your stored event schema:
148
+
149
+ | Pipeline | Use when |
150
+ | ------------------ | --------------------------------------------------------------------- |
151
+ | `parseable_otlp` | OTLP log attributes are stored as literal semantic-convention columns |
152
+ | `parseable_ecs` | Nested ECS documents are flattened by Parseable using underscores |
153
+ | `parseable_sysmon` | Events use native Sysmon fields |
154
+
155
+ ### OpenTelemetry
156
+
157
+ ```bash
158
+ sigma convert \
159
+ -t parseable \
160
+ -p parseable_otlp \
161
+ -O dataset=otel-events \
162
+ rule.yml
163
+ ```
164
+
165
+ Representative mappings:
166
+
167
+ | Sigma | Parseable OTLP |
168
+ | ----------------- | ------------------------- |
169
+ | `Image` | `process.executable.path` |
170
+ | `CommandLine` | `process.command_line` |
171
+ | `ProcessId` | `process.pid` |
172
+ | `ParentProcessId` | `process.parent_pid` |
173
+ | `SourceIp` | `source.address` |
174
+ | `DestinationIp` | `destination.address` |
175
+ | `DestinationPort` | `destination.port` |
176
+ | `TargetFilename` | `file.path` |
177
+ | `QueryName` | `dns.question.name` |
178
+ | `Computer` | `host.name` |
179
+
180
+ Mappings are scoped by Sigma log source where field meaning changes. The pipeline does not
181
+ invent fields without a standard OpenTelemetry equivalent.
182
+
183
+ ### ECS
184
+
185
+ ```bash
186
+ sigma convert \
187
+ -t parseable \
188
+ -p parseable_ecs \
189
+ -O dataset=ecs-events \
190
+ rule.yml
191
+ ```
192
+
193
+ Nested ECS input such as `{"source":{"ip":"192.0.2.1"}}` becomes the Parseable column
194
+ `source_ip`.
195
+
196
+ | Sigma | Flattened ECS |
197
+ | ---------------- | --------------------------- |
198
+ | `EventID` | `event_code` |
199
+ | `Channel` | `winlog_channel` |
200
+ | `Image` | `process_executable` |
201
+ | `CommandLine` | `process_command_line` |
202
+ | `ParentImage` | `process_parent_executable` |
203
+ | `User` | `user_name` |
204
+ | `SourceIp` | `source_ip` |
205
+ | `DestinationIp` | `destination_ip` |
206
+ | `TargetFilename` | `file_path` |
207
+ | `QueryName` | `dns_question_name` |
208
+
209
+ Use a custom pipeline if your events contain literal dotted ECS keys or use different column
210
+ names.
211
+
212
+ ### Sysmon
213
+
214
+ ```bash
215
+ sigma convert \
216
+ -t parseable \
217
+ -p parseable_sysmon \
218
+ -O dataset=sysmon-events \
219
+ rule.yml
220
+ ```
221
+
222
+ This pipeline retains native Sysmon fields and adds the appropriate `Channel` and `EventID`
223
+ conditions for generic Windows log sources. For Sysmon normalized to nested ECS before
224
+ ingestion, chain the pipelines:
225
+
226
+ ```bash
227
+ sigma convert \
228
+ -t parseable \
229
+ -p parseable_sysmon \
230
+ -p parseable_ecs \
231
+ -O dataset=ecs-sysmon-events \
232
+ rule.yml
233
+ ```
234
+
235
+ ### Custom schemas
236
+
237
+ Built-in pipelines cannot know organization-specific column names. Define a pySigma pipeline
238
+ for the schema actually present in your dataset:
239
+
240
+ ```yaml
241
+ name: My Parseable field mapping
242
+ priority: 30
243
+ allowed_backends:
244
+ - parseable
245
+ transformations:
246
+ - id: organization_fields
247
+ type: field_name_mapping
248
+ mapping:
249
+ Image: exe_path
250
+ CommandLine: command
251
+ User: username
252
+ SourceIp: client_ip
253
+ ```
254
+
255
+ ```bash
256
+ sigma convert \
257
+ -t parseable \
258
+ -p company-parseable.yml \
259
+ -O dataset=company-events \
260
+ rule.yml
261
+ ```
262
+
263
+ Always compare generated columns with the Parseable dataset schema before deploying rules.
264
+
265
+ ## Supported Sigma features
266
+
267
+ The backend supports:
268
+
269
+ - Case-insensitive Sigma string matching and the `cased` modifier
270
+ - `contains`, `startswith`, `endswith`, `exists`, `fieldref`, and comparison modifiers
271
+ - Sigma `*` and `?` wildcards
272
+ - String and numeric lists
273
+ - Regular expressions bound to a field
274
+ - Null checks and Boolean conditions
275
+ - Fieldless string and numeric keyword searches
276
+ - IPv4 and IPv6 CIDR expressions through Parseable's `ip_in_cidr` SQL function
277
+
278
+ Unsupported constructs fail explicitly with `SigmaFeatureNotSupportedByBackendError`:
279
+
280
+ - Sigma correlation rules
281
+ - Timestamp-part modifiers such as `minute` and `hour`
282
+ - Fieldless regular expressions
283
+
284
+ CIDR conversion requires a Parseable deployment that provides `ip_in_cidr(ip, cidr)`. The
285
+ function correctly parses IPv4 and IPv6 rather than approximating address ranges as text.
286
+
287
+ ## Placeholders
288
+
289
+ Sigma placeholders such as `%Administrators%` are deployment-specific values. Resolve them
290
+ with a processing pipeline before conversion:
291
+
292
+ ```bash
293
+ sigma convert \
294
+ -t parseable \
295
+ -p examples/placeholder-pipeline.yml \
296
+ -O dataset=windows-events \
297
+ examples/placeholder-rule.yml
298
+ ```
299
+
300
+ Do not replace unknown placeholders with wildcards; that changes the detection's meaning.
301
+
302
+ ## Development
303
+
304
+ Install development dependencies and run local checks:
305
+
306
+ ```bash
307
+ python -m pip install -e '.[test]'
308
+ pytest tests -m 'not integration'
309
+ ruff check .
310
+ python -m build
311
+ ```
312
+
313
+ Live tests require `PARSEABLE_URL`, `PARSEABLE_INGESTION_URL`, and `PARSEABLE_API_KEY`.
314
+ They use fixture datasets and are intentionally excluded from the default test command:
315
+
316
+ ```bash
317
+ pytest tests/integration -m integration
318
+ ```
319
+
320
+ The GitHub Actions corpus job checks conversion against a pinned Sigma corpus. Detailed
321
+ results and regression thresholds live in [`reports/`](reports/) rather than this README.
322
+ Successful conversion means valid SQL was generated; it does not prove that a deployment has
323
+ matching columns or representative data.
324
+
325
+ ## License
326
+
327
+ MIT