Skip to content

Limit nesting depth of custom variables - #10940

Open
julianbrost wants to merge 1 commit into
masterfrom
custom-var-depth-limit
Open

julianbrost wants to merge 1 commit into
masterfrom
custom-var-depth-limit

Conversation

@julianbrost

@julianbrost julianbrost commented Jul 10, 2026 •

Copy link
Copy Markdown
Member

Recently, with #10908, we've added a limit to how deeply nested data structures can be created from API requests to limit the resulting recursion depth when processing them to prevent stack overflows.

However, this resulted in a slight inconsistency (not relevant for security) where you may create custom variables with different nesting depth limits depending on the exact way to create them. This change now introduces an additional validation for custom variables consistently limiting there overall depth. This was not done yet as part of the security fixes as this also affects existing configuration in a way that might cause existing configuration now fail the validation. The limit of 16 should be large enough so that should affect none or very few users.

Note that the limit of 16 includes the outer vars level, so there is room for 15 user-defined levels.

Also note that while testing, you might run into a small issue described and fixed in #10939 that happens with our handling of modified attributes if the validation fails.

Tests

Current master (8ff3d58)

Nested JSON exceeding JSON depth limit of 24 fails with an error.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-nested-json" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars": {"1": {"2": {"3": {"4": {"5": {"6": {"7": {"8": {"9": {"10": {"11": {"12": {"13": {"14": {"15": {
             "16": {"17": {"18": {"19": {"20": {"21": {"22": {"23": "wtf"}}}}}}}}}}}}}}}}}}}}}}}
  },
  "pretty": true
}
EOF

{"error":400,"status":"Invalid request body: Error: JSON decoding recursion limit reached (path: root[\"attrs\"][\"vars\"][\"1\"][\"2\"][\"3\"][\"4\"][\"5\"][\"6\"][\"7\"][\"8\"][\"9\"][\"10\"][\"11\"][\"12\"][\"13\"][\"14\"][\"15\"][\"16\"][\"17\"][\"18\"][\"19\"][\"20\"][\"21\"][\"22\"])\n"}
Nested JSON just below depth limit creates a nested object in vars with 23 custom labels.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-nested-json" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars": {"1": {"2": {"3": {"4": {"5": {"6": {"7": {"8": {"9": {"10": {"11": {"12": {"13": {"14": {"15": {
             "16": {"17": {"18": {"19": {"20": {"21": {"22": "wtf"}}}}}}}}}}}}}}}}}}}}}}
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 200,
            "status": "Object was created"
        }
    ]
}

$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X GET "https://localhost:5665/v1/objects/hosts/vars-nested-json?pretty=1&attrs=vars"

{
    "results": [
        {
            "attrs": {
                "vars": {
                    "1": {
                        "2": {
                            "3": {
                                "4": {
                                    "5": {
                                        "6": {
                                            "7": {
                                                "8": {
                                                    "9": {
                                                        "10": {
                                                            "11": {
                                                                "12": {
                                                                    "13": {
                                                                        "14": {
                                                                            "15": {
                                                                                "16": {
                                                                                    "17": {
                                                                                        "18": {
                                                                                            "19": {
                                                                                                "20": {
                                                                                                    "21": {
                                                                                                        "22": "wtf"
                                                                                                    }
                                                                                                }
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-nested-json",
            "type": "Host"
        }
    ]
}
vars.d.o.t.t.e.d notation above the limit of 16 returns an error
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-dotted" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16": "wtf"
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 500,
            "errors": [
                "Error: Attribute 'vars.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16' exceeds maximum nesting level of 16.\n"
            ],
            "status": "Object could not be created."
        }
    ]
}
vars.d.o.t.t.e.d notation just below depth limit creates a nested object in vars with 15 custom labels
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-dotted" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15": "wtf"   
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 200,
            "status": "Object was created"
        }
    ]
}

$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X GET "https://localhost:5665/v1/objects/hosts/vars-dotted?pretty=1&attrs=vars"

{
    "results": [
        {
            "attrs": {
                "vars": {
                    "1": {
                        "2": {
                            "3": {
                                "4": {
                                    "5": {
                                        "6": {
                                            "7": {
                                                "8": {
                                                    "9": {
                                                        "10": {
                                                            "11": {
                                                                "12": {
                                                                    "13": {
                                                                        "14": {
                                                                            "15": "wtf"
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-dotted",
            "type": "Host"
        }
    ]
}
Mixing vars.d.o.t.t.e.d with nested JSON allows creating up to 37 custom nested labels
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-mixed" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15": {
             "16": {"17": {"18": {"19": {"20": {"21": {"22": {"23": {"24": {"25": {"26": {"27": {"28": {
             "29": {"30": {"31": {"32": {"33": {"34": {"35": {"36": {"37": "wtf"}}}}}}}}}}}}}}}}}}}}}}
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 200,
            "status": "Object was created"
        }
    ]
}

$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X GET "https://localhost:5665/v1/objects/hosts/vars-mixed?pretty=1&attrs=vars"

{
    "results": [
        {
            "attrs": {
                "vars": {
                    "1": {
                        "2": {
                            "3": {
                                "4": {
                                    "5": {
                                        "6": {
                                            "7": {
                                                "8": {
                                                    "9": {
                                                        "10": {
                                                            "11": {
                                                                "12": {
                                                                    "13": {
                                                                        "14": {
                                                                            "15": {
                                                                                "16": {
                                                                                    "17": {
                                                                                        "18": {
                                                                                            "19": {
                                                                                                "20": {
                                                                                                    "21": {
                                                                                                        "22": {
                                                                                                            "23": {
                                                                                                                "24": {
                                                                                                                    "25": {
                                                                                                                        "26": {
                                                                                                                            "27": {
                                                                                                                                "28": {
                                                                                                                                    "29": {
                                                                                                                                        "30": {
                                                                                                                                            "31": {
                                                                                                                                                "32": {
                                                                                                                                                    "33": {
                                                                                                                                                        "34": {
                                                                                                                                                            "35": {
                                                                                                                                                                "36": {
                                                                                                                                                                    "37": "wtf"
                                                                                                                                                                }
                                                                                                                                                            }
                                                                                                                                                        }
                                                                                                                                                    }
                                                                                                                                                }
                                                                                                                                            }
                                                                                                                                        }
                                                                                                                                    }
                                                                                                                                }
                                                                                                                            }
                                                                                                                        }
                                                                                                                    }
                                                                                                                }
                                                                                                            }
                                                                                                        }
                                                                                                    }
                                                                                                }
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-mixed",
            "type": "Host"
        }
    ]
}

This PR (5440fcb)

With nested JSON, the allowed nesting depth is reduced. 16 custom label levels are now enough for the validation to reject it before the JSON parsing would reject it.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-nested-json" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars": {"1": {"2": {"3": {"4": {"5": {"6": {"7": {"8": {"9": {"10": {"11": {"12": {"13": {"14": {"15": {"16": "wtf"}}}}}}}}}}}}}}}}
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 500,
            "errors": [
                "Error: Validation failed for object 'vars-nested-json' of type 'Host'; Attribute 'vars' -> '1' -> '2' -> '3' -> '4' -> '5' -> '6' -> '7' -> '8' -> '9' -> '10' -> '11' -> '12' -> '13' -> '14' -> '15' -> '16': Variables nested too deep.\nLocation: in /data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf: 3:2-35:2\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(1): object Host \"vars-nested-json\" {\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(2):  check_command = \"dummy\"\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(3):  vars = {\n                                                                                                                     ^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(4):   \"1\" = {\n                                                                                                                    ^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(5):    \"2\" = {\n                                                                                                                    ^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(6):     \"3\" = {\n                                                                                                                    ^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(7):      \"4\" = {\n                                                                                                                    ^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(8):       \"5\" = {\n                                                                                                                    ^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(9):        \"6\" = {\n                                                                                                                    ^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(10):         \"7\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(11):          \"8\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(12):           \"9\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(13):            \"10\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(14):             \"11\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(15):              \"12\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(16):               \"13\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(17):                \"14\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(18):                 \"15\" = {\n                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(19):                  \"16\" = \"wtf\"\n                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(20):                 }\n                                                                                                                     ^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(21):                }\n                                                                                                                     ^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(22):               }\n                                                                                                                     ^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(23):              }\n                                                                                                                     ^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(24):             }\n                                                                                                                     ^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(25):            }\n                                                                                                                     ^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(26):           }\n                                                                                                                     ^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(27):          }\n                                                                                                                     ^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(28):         }\n                                                                                                                     ^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(29):        }\n                                                                                                                     ^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(30):       }\n                                                                                                                     ^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(31):      }\n                                                                                                                     ^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(32):     }\n                                                                                                                     ^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(33):    }\n                                                                                                                     ^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(34):   }\n                                                                                                                     ^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(35):  }\n                                                                                                                     ^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(36):  version = 1783683582.200468\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(37):  zone = \"master\"\n"
            ],
            "status": "Object could not be created."
        }
    ]
}

The error message is quite lengthy and very unreadable if formatted as a JSON string. Decoded, it looks like this:

Error: Validation failed for object 'vars-nested-json' of type 'Host'; Attribute 'vars' -> '1' -> '2' -> '3' -> '4' -> '5' -> '6' -> '7' -> '8' -> '9' -> '10' -> '11' -> '12' -> '13' -> '14' -> '15' -> '16': Variables nested too deep.
Location: in /data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf: 3:2-35:2
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(1): object Host "vars-nested-json" {
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(2):  check_command = "dummy"
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(3):  vars = {
                                                                                                                     ^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(4):   "1" = {
                                                                                                                    ^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(5):    "2" = {
                                                                                                                    ^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(6):     "3" = {
                                                                                                                    ^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(7):      "4" = {
                                                                                                                    ^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(8):       "5" = {
                                                                                                                    ^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(9):        "6" = {
                                                                                                                    ^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(10):         "7" = {
                                                                                                                     ^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(11):          "8" = {
                                                                                                                     ^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(12):           "9" = {
                                                                                                                     ^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(13):            "10" = {
                                                                                                                     ^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(14):             "11" = {
                                                                                                                     ^^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(15):              "12" = {
                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(16):               "13" = {
                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(17):                "14" = {
                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(18):                 "15" = {
                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(19):                  "16" = "wtf"
                                                                                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(20):                 }
                                                                                                                     ^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(21):                }
                                                                                                                     ^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(22):               }
                                                                                                                     ^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(23):              }
                                                                                                                     ^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(24):             }
                                                                                                                     ^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(25):            }
                                                                                                                     ^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(26):           }
                                                                                                                     ^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(27):          }
                                                                                                                     ^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(28):         }
                                                                                                                     ^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(29):        }
                                                                                                                     ^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(30):       }
                                                                                                                     ^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(31):      }
                                                                                                                     ^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(32):     }
                                                                                                                     ^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(33):    }
                                                                                                                     ^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(34):   }
                                                                                                                     ^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(35):  }
                                                                                                                     ^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(36):  version = 1783683582.200468
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-nested-json.conf(37):  zone = "master"
Reducing the nesting level by 1 to 15 nested custom variable labels is still accepted, so that is the new maximum here.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-nested-json" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars": {"1": {"2": {"3": {"4": {"5": {"6": {"7": {"8": {"9": {"10": {"11": {"12": {"13": {"14": {"15": "wtf"}}}}}}}}}}}}}}}
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 200,
            "status": "Object was created"
        }
    ]
}
The limits for vars.d.o.t.t.e.d remain unchanged, 16 is rejected.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-dotted" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16": "wtf"
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 500,
            "errors": [
                "Error: Attribute 'vars.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16' exceeds maximum nesting level of 16.\n"
            ],
            "status": "Object could not be created."
        }
    ]
}
Whereas vars.d.o.t.t.e.d with 15 custom labels after vars is still accepted.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-dotted" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15": "wtf"
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 200,
            "status": "Object was created"
        }
    ]
}
Mixing vars.d.o.t.t.e.d with nested JSON objects so that a total of 16 custom labels after vars is reached is now rejected.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-mixed" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.a1.a2.a3.a4.a5.a6.a7.a8": {
      "b1": {"b2": {"b3": {"b4": {"b5": {"b6": {"b7": {"b8": "wtf"}}}}}}}
    }
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 500,
            "errors": [
                "Error: Validation failed for object 'vars-mixed' of type 'Host'; Attribute 'vars' -> 'a1' -> 'a2' -> 'a3' -> 'a4' -> 'a5' -> 'a6' -> 'a7' -> 'a8' -> 'b1' -> 'b2' -> 'b3' -> 'b4' -> 'b5' -> 'b6' -> 'b7' -> 'b8': Variables nested too deep.\nLocation: in /data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf: 3:2-19:2\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(1): object Host \"vars-mixed\" {\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(2):  check_command = \"dummy\"\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(3):  vars[\"a1\"][\"a2\"][\"a3\"][\"a4\"][\"a5\"][\"a6\"][\"a7\"][\"a8\"] = {\n                                                                                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(4):   b1 = {\n                                                                                                              ^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(5):    b2 = {\n                                                                                                              ^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(6):     b3 = {\n                                                                                                              ^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(7):      b4 = {\n                                                                                                              ^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(8):       b5 = {\n                                                                                                              ^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(9):        b6 = {\n                                                                                                              ^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(10):         b7 = {\n                                                                                                               ^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(11):          b8 = \"wtf\"\n                                                                                                               ^^^^^^^^^^^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(12):         }\n                                                                                                               ^^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(13):        }\n                                                                                                               ^^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(14):       }\n                                                                                                               ^^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(15):      }\n                                                                                                               ^^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(16):     }\n                                                                                                               ^^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(17):    }\n                                                                                                               ^^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(18):   }\n                                                                                                               ^^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(19):  }\n                                                                                                               ^^\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(20):  version = 1783684131.768288\n/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(21):  zone = \"master\"\n"
            ],
            "status": "Object could not be created."
        }
    ]
}

Again, the somewhat more readable error message after JSON decoding:

Error: Validation failed for object 'vars-mixed' of type 'Host'; Attribute 'vars' -> 'a1' -> 'a2' -> 'a3' -> 'a4' -> 'a5' -> 'a6' -> 'a7' -> 'a8' -> 'b1' -> 'b2' -> 'b3' -> 'b4' -> 'b5' -> 'b6' -> 'b7' -> 'b8': Variables nested too deep.
Location: in /data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf: 3:2-19:2
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(1): object Host "vars-mixed" {
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(2):  check_command = "dummy"
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(3):  vars["a1"]["a2"]["a3"]["a4"]["a5"]["a6"]["a7"]["a8"] = {
                                                                                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(4):   b1 = {
                                                                                                              ^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(5):    b2 = {
                                                                                                              ^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(6):     b3 = {
                                                                                                              ^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(7):      b4 = {
                                                                                                              ^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(8):       b5 = {
                                                                                                              ^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(9):        b6 = {
                                                                                                              ^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(10):         b7 = {
                                                                                                               ^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(11):          b8 = "wtf"
                                                                                                               ^^^^^^^^^^^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(12):         }
                                                                                                               ^^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(13):        }
                                                                                                               ^^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(14):       }
                                                                                                               ^^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(15):      }
                                                                                                               ^^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(16):     }
                                                                                                               ^^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(17):    }
                                                                                                               ^^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(18):   }
                                                                                                               ^^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(19):  }
                                                                                                               ^^
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(20):  version = 1783684131.768288
/data/var/lib/icinga2/api/packages/_api/77e78991-06d4-4589-aa0c-ee60e1d7724e/conf.d/hosts/vars-mixed.conf(21):  zone = "master"
Removing one level from vars.d.o.t.t.e.d makes that the object is accepted with 15 custom labels after vars.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-mixed-7-8" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.a1.a2.a3.a4.a5.a6.a7": {
      "b1": {"b2": {"b3": {"b4": {"b5": {"b6": {"b7": {"b8": "wtf"}}}}}}}
    }
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 200,
            "status": "Object was created"
        }
    ]
}
Alternatively, removing one level of nesting from the JSON object also allows the object to be created with the same depth.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X PUT "https://localhost:5665/v1/objects/hosts/vars-mixed-8-7" \
  -d @- <<EOF
{
  "attrs": {
    "check_command": "dummy",
    "vars.a1.a2.a3.a4.a5.a6.a7.a8": {
      "b1": {"b2": {"b3": {"b4": {"b5": {"b6": {"b7": "wtf"}}}}}}
    }
  },
  "pretty": true
}
EOF

{
    "results": [
        {
            "code": 200,
            "status": "Object was created"
        }
    ]
}
This limit is now also enforced in objects defined in config files, 16 levels after vars is rejected.
object Host "vars-nested-dsl" {
  check_command = "dummy"
  vars["1"]["2"]["3"]["4"]["5"]["6"]["7"]["8"]["9"]["10"]["11"]["12"]["13"]["14"]["15"]["16"] = "wtf"
}
[2026-07-10 14:00:32 +0200] critical/config: Error: Validation failed for object 'vars-nested-dsl' of type 'Host'; Attribute 'vars' -> '1' -> '2' -> '3' -> '4' -> '5' -> '6' -> '7' -> '8' -> '9' -> '10' -> '11' -> '12' -> '13' -> '14' -> '15' -> '16': Variables nested too deep.
Location: in /data/etc/icinga2/zones.d/master/hosts.conf: 3:3-3:101
/data/etc/icinga2/zones.d/master/hosts.conf(1): object Host "vars-nested-dsl" {
/data/etc/icinga2/zones.d/master/hosts.conf(2):   check_command = "dummy"
/data/etc/icinga2/zones.d/master/hosts.conf(3):   vars["1"]["2"]["3"]["4"]["5"]["6"]["7"]["8"]["9"]["10"]["11"]["12"]["13"]["14"]["15"]["16"] = "wtf"
                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/data/etc/icinga2/zones.d/master/hosts.conf(4): }
/data/etc/icinga2/zones.d/master/hosts.conf(5): 
[2026-07-10 14:00:32 +0200] critical/config: 1 error
[2026-07-10 14:00:32 +0200] critical/cli: Config validation failed. Re-run with 'icinga2 daemon -C' after fixing the config.
Reducing the nesting level in the config file by one results in a valid object again.
object Host "vars-nested-dsl" {
  check_command = "dummy"
  vars["1"]["2"]["3"]["4"]["5"]["6"]["7"]["8"]["9"]["10"]["11"]["12"]["13"]["14"]["15"] = "wtf"
}
[2026-07-10 14:03:12 +0200] information/cli: Finished validating the configuration file(s).
And finally, querying all those config objects that maxed out the limits show a consistent limit across all methods to create an object.
$ curl -sSku root:icinga \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -X GET "https://localhost:5665/v1/objects/hosts" \
  -d @- <<EOF
{
  "filter": "match(p, host.name)",
  "filter_vars": {"p": "vars-*"},
  "attrs": ["vars"],
  "pretty": true
}
EOF

{
    "results": [
        {
            "attrs": {
                "vars": {
                    "1": {
                        "2": {
                            "3": {
                                "4": {
                                    "5": {
                                        "6": {
                                            "7": {
                                                "8": {
                                                    "9": {
                                                        "10": {
                                                            "11": {
                                                                "12": {
                                                                    "13": {
                                                                        "14": {
                                                                            "15": "wtf"
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-dotted",
            "type": "Host"
        },
        {
            "attrs": {
                "vars": {
                    "a1": {
                        "a2": {
                            "a3": {
                                "a4": {
                                    "a5": {
                                        "a6": {
                                            "a7": {
                                                "b1": {
                                                    "b2": {
                                                        "b3": {
                                                            "b4": {
                                                                "b5": {
                                                                    "b6": {
                                                                        "b7": {
                                                                            "b8": "wtf"
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-mixed-7-8",
            "type": "Host"
        },
        {
            "attrs": {
                "vars": {
                    "a1": {
                        "a2": {
                            "a3": {
                                "a4": {
                                    "a5": {
                                        "a6": {
                                            "a7": {
                                                "a8": {
                                                    "b1": {
                                                        "b2": {
                                                            "b3": {
                                                                "b4": {
                                                                    "b5": {
                                                                        "b6": {
                                                                            "b7": "wtf"
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-mixed-8-7",
            "type": "Host"
        },
        {
            "attrs": {
                "vars": {
                    "1": {
                        "2": {
                            "3": {
                                "4": {
                                    "5": {
                                        "6": {
                                            "7": {
                                                "8": {
                                                    "9": {
                                                        "10": {
                                                            "11": {
                                                                "12": {
                                                                    "13": {
                                                                        "14": {
                                                                            "15": "wtf"
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-nested-dsl",
            "type": "Host"
        },
        {
            "attrs": {
                "vars": {
                    "1": {
                        "2": {
                            "3": {
                                "4": {
                                    "5": {
                                        "6": {
                                            "7": {
                                                "8": {
                                                    "9": {
                                                        "10": {
                                                            "11": {
                                                                "12": {
                                                                    "13": {
                                                                        "14": {
                                                                            "15": "wtf"
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "joins": {},
            "meta": {},
            "name": "vars-nested-json",
            "type": "Host"
        }
    ]
}

Recently, with fb42312, we've added a limit to
how deeply nested data structures can be created from API requests to limit the
resulting recursion depth when processing them to prevent stack overflows.

However, this resulted in a slight inconsistency (not relevant for security)
where you may create custom variables with different nesting depth limits
depending on the exact way to create them. This change now introduces an
additional validation for custom variables consistently limiting there overall
depth. This was not done yet as part of the security fixes as this also affects
existing configuration in a way that might cause existing configuration now
fail the validation. The limit of 16 should be large enough so that should
affect none or very few users.
@julianbrost julianbrost added this to the 2.17.0 milestone Jul 10, 2026
@julianbrost julianbrost added area/configuration DSL, parser, compiler, error handling area/api REST API labels Jul 10, 2026
@cla-bot cla-bot Bot added the cla/signed label Jul 10, 2026
@julianbrost

Copy link
Copy Markdown
Member Author

I almost went crazy while doing and writing down the tests for the PR description. I hope you don't face the same problem when looking at them. 😬

@jschmidt-icinga jschmidt-icinga left a comment •

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.

I have run a couple of the examples in the PR description across this and it works well and makes sense to me. Just a few nitpick comments.

PS: Could also do with a unit-test!

Comment on lines +18 to +20
if (path.size() > VarDepthLimit) {
BOOST_THROW_EXCEPTION(ValidationError(this, path, "Variables nested too deep."));
}

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.

This throws ValidationError while the preexisting checks in ConfigObject::ModifyAttribute and ConfigWriter::EmitScope throw a std::runtime_error. I'd like some consistency here, but either direction is fine with me.

It's a bit annoying because ConfigWriter::EmitScope doesn't have an object so ValidationError might need another constructor for that.

Comment thread lib/base/configwriter.cpp
Comment on lines +84 to +85
// nested data structures that could overflow the stack later on. It is done in addition to the check in
// CustomVarObject::ValidateDepthLimit() which is indirectly called by the validation function below.

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.

I don't know which "validation function below" this is in reference to. Presumably it means CustomVarObject::ValidateDepthLimit, but that isn't "below" (well it is in this GH diff, but not for anyone reading the code in their editor).

Comment on lines +32 to +34
}

if (auto array = dynamic_pointer_cast<Array>(obj); array) {

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.

Suggested change
}
if (auto array = dynamic_pointer_cast<Array>(obj); array) {
} else if (auto array = dynamic_pointer_cast<Array>(obj); array) {

(either that or early return;)

DECLARE_OBJECT(CustomVarObject);

void ValidateVars(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils) final;
void ValidateDepthLimit(const Value& value, std::vector<String>& path);

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.

This should be const and private (or inlined into CustomVarObject::ValidateVars()), or are you anticipating that this will be used in other places in the future?

Making it static (or TU-local) would also be an option, but it uses this for the ValidationError.

Comment on lines 48 to +51
MacroProcessor::ValidateCustomVars(this, lvalue());

std::vector<String> path {"vars"};
ValidateDepthLimit(lvalue(), path);

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.

Maybe could use a temporary Dictionary::Ptr so lvalue() only runs once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api REST API area/configuration DSL, parser, compiler, error handling cla/signed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants