Skip to content

All security_monitoring_rules fail to sync: cases.condition is stripped from the create payload #598

Description

@DaniloGT

Syncing security_monitoring_rules fails to create every non-default rule that has case conditions. There are two compounding bugs:

  1. Masking bug: the 400-handling code does error_obj["errors"], but the API error body uses a different shape, so it raises KeyError: 'errors'. That KeyError replaces the real Datadog message, and the log only shows a useless message.

except CustomClientHTTPError as err:
if err.status_code == 400:
preamble = "400 Bad Request - "
error_json_no_preamble = err.args[0][len(preamble) :]
error_obj = json.loads(error_json_no_preamble)
errors = error_obj["errors"]
for error_message in errors:
if error_message in self.errors_to_skip:
raise SkipResource(_id, self.resource_type, err.args[0])
raise err

Example output:

2026-06-25 16:33:22,449 - ERROR - [security_monitoring_rules - xxx-xxx-xxx] - 'errors'

The body is {"error": {...}}, not the {"errors": [...]} the code assumes. Once I fixed this bug locally, I found the real bug

2026-06-25 16:59:31,955 - ERROR - [security_monitoring_rules - xxx-xxx-xxx] - 400 Bad Request - {"error":{"code":"InvalidArgument","message":"Invalid rule configuration","details":[{"code":"InvalidArgument","message":"Query a must be used at least once in a case","target":"cases"},{"code":"InvalidArgument","message":"Case condition cannot be empty","target":"cases[0].condition"}]}}

  1. Root cause: "cases.condition" is listed in excluded_attributes. Excluded attributes doesn't only affect diffing, but also it is stripped from the create/update payload too. Since condition is required by the rule API, every create is rejected with HTTP 400.

class SecurityMonitoringRules(BaseResource):
"""Security Monitoring Rules inherits from BaseResource"""
resource_type = "security_monitoring_rules"
resource_config = ResourceConfig(
base_path="/api/v2/security_monitoring/rules",
excluded_attributes=[
"createdAt",
"creationAuthorId",
"updateAuthorId",
"updatedAt",
"isPartner",
"isBeta",
"isDeleted",
"isDeprecated",
"defaultTags",
"version",
"options.anomalyDetectionOptions",
"options.impossibleTravelOptions",
"cases.condition",
],

I can bypass this second bug by removing the cases.condition from the excluded attributes, which allows me to create the resources at the new account

Note: simply removing cases.condition from excluded_attributes fixes creation but may reintroduce the spurious diffs that #311 was trying to suppress (the API computes condition for some rule types, e.g. anomaly). The two concerns should be decoupled: keep condition in the payload, but ignore it in the diff via DeepDiff exclude_regex_paths (exclude_paths as used by excluded_attributes doesn't match list indices like root['cases'][0]['condition']).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions