From fb0b6475effcdd31233581240887838d40d36f9f Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Fri, 20 Feb 2026 06:39:39 +0100 Subject: [PATCH] Do not attempt to normalize LUA records. Signed-off-by: Miod Vallat --- pdns/ws-auth.cc | 10 +++++++++- regression-tests.api/test_Zones.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 18543cb333..95901d4c81 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -668,7 +668,15 @@ static void gatherRecords(const Json& container, const DNSName& qname, const QTy validateGatheredRRType(resourceRecord); const auto& items = container["records"].array_items(); for (const auto& record : items) { - string content = normalizeJsonString(stringFromJson(record, "content")); + string content = stringFromJson(record, "content"); + switch (resourceRecord.qtype.getCode()) { + case QType::LUA: + // Keep LUA record contents unmodified + break; + default: + content = normalizeJsonString(content); + break; + } if (record.object_items().count("priority") > 0) { throw std::runtime_error("`priority` element is not allowed in record"); } diff --git a/regression-tests.api/test_Zones.py b/regression-tests.api/test_Zones.py index fa45be59fc..172bed4e84 100644 --- a/regression-tests.api/test_Zones.py +++ b/regression-tests.api/test_Zones.py @@ -1449,6 +1449,34 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( data = self.get_zone(name) self.assertCountEqual(get_rrset(data, name, 'NS')['records'], rrset['records']) + def test_zone_rr_update_lua(self): + # Important to test with LUA records, as their contents should not be + # normalized in any way. + name, payload, zone = self.create_zone() + recname = 'lua.' + name + # do a replace (= update) + rrset = { + 'changetype': 'replace', + 'name': recname, + 'type': 'LUA', + 'ttl': 3600, + 'records': [ + { + "content": "TXT \"; return 'PowerDNS'\"", + "disabled": False + } + ] + } + payload = {'rrsets': [rrset]} + r = self.session.patch( + self.url("/api/v1/servers/localhost/zones/" + name), + data=json.dumps(payload), + headers={'content-type': 'application/json'}) + self.assert_success(r) + # verify that (only) the new record is there + data = self.get_zone(name) + self.assertEqual(get_rrset(data, recname, 'LUA')['records'], rrset['records']) + def test_zone_rr_update_mx(self): # Important to test with MX records, as they have a priority field, which must end up in the content field. name, payload, zone = self.create_zone() -- 2.47.3