Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "detection_rules"
version = "2.1.6"
version = "2.1.7"
description = "Detection Rules is the home for rules used by Elastic Security. This repository is used for the development, maintenance, testing, validation, and release of rules for Elastic Security’s Detection Engine."
readme = "README.md"
requires-python = ">=3.12"
Expand Down
35 changes: 32 additions & 3 deletions tests/test_all_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
import unittest
import uuid
from collections import defaultdict
from datetime import UTC, date, datetime
from pathlib import Path
from typing import ClassVar

import eql
import kql
import pytoml
from marshmallow import ValidationError
from semver import Version

Expand Down Expand Up @@ -928,7 +930,7 @@ def test_deprecated_rules_modified(self):

@unittest.skipIf(os.getenv("GITHUB_EVENT_NAME") == "push", "Skipping this test when not running on pull requests.")
def test_rule_change_has_updated_date(self):
"""Test to ensure modified rules have updated_date field updated."""
"""Fail when a modified rule lacks an updated_date bump and is not same-day UTC."""

rules_path = get_path(["rules"])
rules_bbr_path = get_path(["rules_building_block"])
Expand All @@ -947,11 +949,38 @@ def test_rule_change_has_updated_date(self):
if result:
modified_rules = [path for path in result.splitlines() if path.endswith(".toml")]
failed_rules = []
today_utc = datetime.now(UTC).date()
for modified_rule_path in modified_rules:
diff_output = detection_rules_git("diff", "origin/main", modified_rule_path)
if not re.search(r"\+\s*updated_date =", diff_output):
# Rule has been modified but updated_date has not been changed, add to list of failed rules
if re.search(r"^\+\s*updated_date\s*=", diff_output, re.MULTILINE):
# updated_date has been modified in this PR
continue

rule_path = get_path([modified_rule_path])
metadata = pytoml.loads(rule_path.read_text(encoding="utf-8")).get("metadata") or {}
if "updated_date" not in metadata:
# Explicit updated_date was not found -> do not require a bump
continue

updated_date = metadata["updated_date"]
if isinstance(updated_date, datetime):
if updated_date.tzinfo is None:
updated_date = updated_date.replace(tzinfo=UTC)

@eric-forte-elastic eric-forte-elastic Aug 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, other places in the code covert the time to non-UTC so this will still could cause an issue if fixed this way. Since in this repo updated date is required I think it needs to be handled a little differently.

Pulling local time:

  • detection_rules/cli_utils.py:62 time.strftime("%Y/%m/%d")
  • detection_rules/cli_utils.py:271 datetime.date.today()
  • detection_rules/devtools.py:1010,1165,1758,2026 time.strftime("%Y/%m/%d")

So cases with something like: US/Pacific, Aug 20 18:00 PDT (= Aug 21 01:00 UTC)

I think the simplest way to address this is to just have our functions in cli_utils.py and devtools.py also be timezone aware to UTC (e.g. datetime.now(UTC).strftime("%Y/%m/%d") ), to address the conflict.

patch to fix:

utc-date-stamping.patch.txt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is the case, agreed, maybe a separate PR to add a globally accessible adjustment.

updated_date = updated_date.astimezone(UTC).date()
elif isinstance(updated_date, date):
pass
elif isinstance(updated_date, str):
updated_date = date.fromisoformat(updated_date.replace("/", "-").split("T")[0])
else:
failed_rules.append(f"{modified_rule_path}")
continue

# Same-day follow-up tunings may leave updated_date unchanged.
# Compare in UTC so evening local edits still match CI runners.
if updated_date == today_utc:
continue

failed_rules.append(f"{modified_rule_path}")

if failed_rules:
fail_msg = """
Expand Down
Loading