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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions