Skip to content

Commit 8c54a59

Browse files
mikep-sumoclaude
andcommitted
TLAB-2569 Add mypy static type checking to CI/CD
Added mypy type checking configuration and CI integration: - Added mypy ^1.11 to dev dependencies - Configured mypy with lenient settings (python 3.10, namespace packages) - Added type annotations to fix critical errors in schema_loader, confidence, and backend - Added CI step to run mypy before tests - Used type: ignore comments for pySigma framework compatibility issues Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent bd724b1 commit 8c54a59

7 files changed

Lines changed: 1301 additions & 27 deletions

File tree

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jobs:
2323
python-version: ${{ matrix.python-version }}
2424
- name: Install dependencies
2525
run: poetry install
26+
- name: Type check
27+
run: poetry run mypy sigma/
2628
- name: Run tests
2729
run: poetry run pytest --cov=sigma --cov-report term --cov-report xml:cov.xml -vv
2830
- name: Store coverage for badge

poetry.lock

Lines changed: 1273 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ pytest = "^8.0"
2525
pytest-cov = "^4.0"
2626
coverage = "^7.0"
2727
streamlit = "^1.32.0"
28+
mypy = "^1.11"
29+
30+
[tool.mypy]
31+
python_version = "3.10"
32+
warn_return_any = true
33+
warn_unused_configs = true
34+
disallow_untyped_defs = false
35+
ignore_missing_imports = true
36+
namespace_packages = true
37+
explicit_package_bases = true
2838

2939
[build-system]
3040
requires = ["poetry-core>=1.8.1"]

sigma/backends/sumologic/sumologic.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ class SumoLogicCSEBackend(TextQueryBackend):
2626
"""
2727

2828
name: ClassVar[str] = "Sumo Logic Cloud SIEM Backend"
29-
formats: Dict[str, str] = {
29+
formats: Dict[str, str] = { # type: ignore[misc]
3030
"default": "Sumo Logic CSIEM Rule JSON format",
3131
"cse_rule": "CSIEM Rule JSON with full metadata",
3232
}
33-
requires_pipeline: bool = True
33+
requires_pipeline: bool = True # type: ignore[misc]
3434

3535
# Cloud SIEM uses uppercase boolean operators
36-
precedence: ClassVar[Tuple[ConditionItem, ConditionItem, ConditionItem]] = (
36+
precedence: ClassVar[Tuple[ConditionItem, ConditionItem, ConditionItem]] = ( # type: ignore[assignment]
3737
ConditionNOT,
3838
ConditionAND,
3939
ConditionOR,
@@ -64,7 +64,7 @@ class SumoLogicCSEBackend(TextQueryBackend):
6464
wildcard_single: ClassVar[str] = "*"
6565
add_escaped: ClassVar[str] = "" # Don't add extra escaping
6666
filter_chars: ClassVar[str] = ""
67-
bool_values: ClassVar[Dict[bool, str]] = {
67+
bool_values: ClassVar[Dict[bool, str]] = { # type: ignore[assignment]
6868
True: "true",
6969
False: "false",
7070
}
@@ -79,7 +79,7 @@ class SumoLogicCSEBackend(TextQueryBackend):
7979
# Must escape / (delimiter), and common regex metacharacters when used as literals
8080
re_expression: ClassVar[str] = "{field} matches /{regex}/"
8181
re_escape_char: ClassVar[str] = "\\"
82-
re_escape: ClassVar[Tuple[str, ...]] = ("/", ".")
82+
re_escape: ClassVar[Tuple[str, ...]] = ("/", ".") # type: ignore[assignment]
8383
re_escape_escape_char: bool = True
8484
re_flag_prefix: bool = False # CSE doesn't use flag prefixes in regex
8585
re_flags: Dict[SigmaRegularExpressionFlag, str] = {
@@ -88,7 +88,7 @@ class SumoLogicCSEBackend(TextQueryBackend):
8888

8989
# Numeric comparison operators
9090
compare_op_expression: ClassVar[str] = "{field} {operator} {value}"
91-
compare_operators: ClassVar[Dict[SigmaCompareExpression.CompareOperators, str]] = {
91+
compare_operators: ClassVar[Dict[SigmaCompareExpression.CompareOperators, str]] = { # type: ignore[valid-type]
9292
SigmaCompareExpression.CompareOperators.LT: "<",
9393
SigmaCompareExpression.CompareOperators.LTE: "<=",
9494
SigmaCompareExpression.CompareOperators.GT: ">",
@@ -139,7 +139,7 @@ def __init__(
139139
**kwargs,
140140
):
141141
super().__init__(processing_pipeline, collect_errors, **kwargs)
142-
self.rule_metadata = []
142+
self.rule_metadata: List[Dict[str, Any]] = []
143143
self.min_confidence = min_confidence
144144
self.schema_path = schema_path
145145
self.include_confidence_metadata = include_confidence_metadata
@@ -224,7 +224,7 @@ def convert_condition_field_eq_val_num(self, cond, state: ConversionState) -> st
224224

225225
if not isinstance(cond, ConditionFieldEqualsValueExpression):
226226
# Fallback to parent implementation
227-
return super().convert_condition_field_eq_val_num(cond, state)
227+
return super().convert_condition_field_eq_val_num(cond, state) # type: ignore[return-value]
228228

229229
field_name = cond.field
230230
numeric_value = cond.value.to_plain()
@@ -253,7 +253,7 @@ def convert_condition_as_in_expression(self, cond, state: ConversionState) -> st
253253
from typing import Union, cast
254254

255255
if not all(isinstance(arg, ConditionFieldEqualsValueExpression) for arg in cond.args):
256-
return super().convert_condition_as_in_expression(cond, state)
256+
return super().convert_condition_as_in_expression(cond, state) # type: ignore[return-value]
257257

258258
field_name = cast(ConditionFieldEqualsValueExpression, cond.args[0]).field
259259

@@ -1165,7 +1165,7 @@ def _get_entity_selectors(self, logsource: Any) -> List[Dict[str, str]]:
11651165
Returns:
11661166
List of entity selector dictionaries (empty if cannot determine confidently)
11671167
"""
1168-
entity_selectors = []
1168+
entity_selectors: List[Dict[str, Any]] = []
11691169

11701170
if not logsource:
11711171
return entity_selectors

sigma/pipelines/sumologic/confidence.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def compute_data_preservation(
232232
Data preservation score 0.0-1.0
233233
"""
234234
score = 1.0
235-
warnings = []
235+
warnings: list[str] = []
236236

237237
# Check for multi-value collapse
238238
if sigma_field in MULTI_VALUE_FIELDS:

sigma/pipelines/sumologic/schema_loader.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def __init__(self, fields: Dict[str, FieldSchema]):
6464
self.fields = fields
6565

6666
# Build reverse indexes for fast lookup
67-
self._entity_type_index = {}
67+
self._entity_type_index: Dict[str, list] = {}
6868
self._enrichment_fields = set()
6969
self._general_purpose_fields = set()
7070

@@ -106,7 +106,8 @@ def get_related_fields(self, field_name: str) -> Dict[str, str]:
106106

107107
def get_fields_by_entity_type(self, entity_type: str) -> List[str]:
108108
"""Get all fields associated with an entity type (ip, hostname, etc.)."""
109-
return self._entity_type_index.get(entity_type, [])
109+
result = self._entity_type_index.get(entity_type, [])
110+
return list(result)
110111

111112

112113
class SchemaLoader:

sigma/pipelines/sumologic/sumologic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
ExcludeFieldCondition,
1111
)
1212
from sigma.processing.pipeline import ProcessingItem, ProcessingPipeline
13-
from typing import Dict, Any, Optional
13+
from typing import Dict, Any, Optional, cast
1414
from .schema_loader import SchemaIndex, SchemaLoader
1515
from .confidence import compute_confidence, ConfidenceScore
1616

@@ -37,7 +37,7 @@ def __init__(
3737
logsource_category: Sigma logsource category (e.g., "process_creation")
3838
schema: CSE schema index for validation (None if schema not loaded)
3939
"""
40-
super().__init__(mapping)
40+
super().__init__(cast(Dict[str | None, str | list[str]], mapping))
4141
self.logsource_category = logsource_category
4242
self.schema = schema
4343

0 commit comments

Comments
 (0)