Repository: StackExchange/dnscontrol
Pull request #888 (2020-10-07 19:57:07)

Fix D_EXTEND matching bug
Fixes https://github.com/StackExchange/dnscontrol/issues/886

* Change js_test.go to use a more standard JSON comparison
* Reformat some of the js/parse_test files
* Fix https://github.com/StackExchange/dnscontrol/issues/886

tlimoncelli: CC: @ad8-bdl 
tlimoncelli: Thanks, @ad8-bdl !  I couldn't have done this without you!  I have limited Javascript skills but I was able to build on top of your code.

Patch:
diff --git a/pkg/js/030-dextenddoc.json b/pkg/js/030-dextenddoc.json
deleted file mode 100644
index c505edf9..00000000
--- a/pkg/js/030-dextenddoc.json
+++ /dev/null
@@ -1,92 +0,0 @@
-{
-  "dns_providers": [
-    {
-      "name": "Cloudflare",
-      "type": "CLOUDFLAREAPI"
-    }
-  ],
-  "domains": [
-    {
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
-      "name": "domain.tld",
-      "records": [
-        {
-          "name": "@",
-          "target": "127.0.0.1",
-          "type": "A"
-        },
-        {
-          "name": "www",
-          "target": "127.0.0.2",
-          "type": "A"
-        },
-        {
-          "name": "a",
-          "target": "b",
-          "type": "CNAME"
-        },
-        {
-          "name": "aaa",
-          "target": "127.0.0.3",
-          "type": "A"
-        },
-        {
-          "name": "c",
-          "target": "d",
-          "type": "CNAME"
-        },
-        {
-          "name": "bbb.sub",
-          "subdomain": "sub",
-          "target": "127.0.0.4",
-          "type": "A"
-        },
-        {
-          "name": "ccc.sub",
-          "subdomain": "sub",
-          "target": "127.0.0.5",
-          "type": "A"
-        },
-        {
-          "name": "e.sub",
-          "subdomain": "sub",
-          "target": "f",
-          "type": "CNAME"
-        },
-        {
-          "name": "ddd.sub.sub",
-          "subdomain": "sub.sub",
-          "target": "127.0.0.6",
-          "type": "A"
-        },
-        {
-          "name": "g.sub.sub",
-          "subdomain": "sub.sub",
-          "target": "h",
-          "type": "CNAME"
-        },
-        {
-          "name": "sub",
-          "subdomain": "sub",
-          "target": "127.0.0.7",
-          "type": "A"
-        },
-        {
-          "name": "i.sub",
-          "subdomain": "sub",
-          "target": "j",
-          "type": "CNAME"
-        }
-      ],
-      "registrar": "Third-Party"
-    }
-  ],
-  "registrars": [
-    {
-      "name": "Third-Party",
-      "type": "NONE"
-    }
-  ]
-}
diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js
index 91cb98db..00b0cc27 100644
--- a/pkg/js/helpers.js
+++ b/pkg/js/helpers.js
@@ -120,20 +120,27 @@ function D_EXTEND(name) {
     conf.domains[domain.id] = domain.obj; // let's overwrite the object.
 }
 
-// _getDomainObject(name): This is a small helper function to get the domain JS object returned.
-// returns the domain object defined for the given name or subdomain thereof
+// _getDomainObject(name): This implements the domain matching
+// algorithm used by D_EXTEND(). Candidate matches are an exact match
+// of the domain's name, or if name is a proper subdomain of the
+// domain's name. The longest match is returned. 
 function _getDomainObject(name) {
-    domain = null;
-    domain_len = 0;
-    for(var i = 0; i < conf.domains.length; i++) {
-        if (name.substr(-conf.domains[i]['name'].length) == conf.domains[i]['name']) {
-            if (conf.domains[i]['name'].length > domain_len) {
-                domain_len = conf.domains[i]['name'].length;
-                domain = {'id': i, 'obj': conf.domains[i]};
-            }
-        }
-    }
-    return domain;
+  domain = null;
+  domain_len = 0;
+  for (var i = 0; i < conf.domains.length; i++) {
+    thisname = conf.domains[i]["name"];
+    desiredsuffix = "." + thisname;
+    foundsuffix = name.substr(-desiredsuffix.length);
+    // If this is an exact match or the suffix matches...
+    if (name == thisname || foundsuffix == desiredsuffix) {
+      // If this match is a longer match than our current best match...
+      if (thisname.length > domain_len) {
+        domain_len = thisname.length;
+        domain = { id: i, obj: conf.domains[i] };
+      }
+    }
+  }
+  return domain;
 }
 
 // DEFAULTS provides a set of default arguments to apply to all future domains.
diff --git a/pkg/js/static.go b/pkg/js/static.go
index 6622ec48..32f5e547 100644
--- a/pkg/js/static.go
+++ b/pkg/js/static.go
@@ -212,128 +212,129 @@ var _escData = map[string]*_escFile{
 	"/helpers.js": {
 		name:    "helpers.js",
 		local:   "pkg/js/helpers.js",
-		size:    27416,
+		size:    27572,
 		modtime: 0,
 		compressed: `
-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
-6diybudx5/8FAAD//7FJb88YawAA
+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
 `,
 	},
 }


Test patch:
diff --git a/pkg/js/js_test.go b/pkg/js/js_test.go
index 1962f889..1139df96 100644
--- a/pkg/js/js_test.go
+++ b/pkg/js/js_test.go
@@ -8,8 +8,7 @@ import (
 	"testing"
 	"unicode"
 
-	"github.com/tdewolff/minify"
-	minjson "github.com/tdewolff/minify/json"
+	testifyrequire "github.com/stretchr/testify/require"
 )
 
 const (
@@ -31,8 +30,6 @@ func TestParsedFiles(t *testing.T) {
 		if filepath.Ext(f.Name()) != ".js" || !unicode.IsNumber(rune(f.Name()[0])) {
 			continue
 		}
-		m := minify.New()
-		m.AddFunc("json", minjson.Minify)
 		t.Run(f.Name(), func(t *testing.T) {
 			conf, err := ExecuteJavascript(string(filepath.Join(testDir, f.Name())), true)
 			if err != nil {
@@ -42,27 +39,14 @@ func TestParsedFiles(t *testing.T) {
 			if err != nil {
 				t.Fatal(err)
 			}
-			actualJSON, err = m.Bytes("json", actualJSON)
-			if err != nil {
-				t.Fatal(err)
-			}
 			expectedFile := filepath.Join(testDir, f.Name()[:len(f.Name())-3]+".json")
-			expectedData, err := ioutil.ReadFile(expectedFile)
-			if err != nil {
-				t.Fatal(err)
-			}
-
-			expectedJSON, err := m.String("json", string(expectedData))
+			expectedJSON, err := ioutil.ReadFile(expectedFile)
 			if err != nil {
 				t.Fatal(err)
 			}
 			es := string(expectedJSON)
 			as := string(actualJSON)
-			if es != as {
-				t.Errorf("Expected and actual json don't match: %s", f.Name())
-				t.Logf("Expected(%v): %s", len(es), es)
-				t.Logf("Actual  (%v): %s", len(as), as)
-			}
+			testifyrequire.JSONEqf(t, es, as, "EXPECTING %s", as)
 		})
 	}
 }
diff --git a/pkg/js/parse_tests/028-dextend.json b/pkg/js/parse_tests/028-dextend.json
index edbb43cc..2bfe8b43 100644
--- a/pkg/js/parse_tests/028-dextend.json
+++ b/pkg/js/parse_tests/028-dextend.json
@@ -1,82 +1,34 @@
 {
-  "registrars": [
-    {
-      "name": "Third-Party",
-      "type": "NONE"
-    }
-  ],
-  "dns_providers": [
-    {
-      "name": "Cloudflare",
-      "type": "CLOUDFLAREAPI"
-    }
-  ],
+  "registrars": [ { "name": "Third-Party", "type": "NONE" } ],
+  "dns_providers": [ { "name": "Cloudflare", "type": "CLOUDFLAREAPI" } ],
   "domains": [
     {
       "name": "foo.com",
       "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "10.1.1.1"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "10.2.2.2"
-        }
+        { "name": "@", "target": "10.1.1.1", "type": "A" },
+        { "name": "www", "target": "10.2.2.2", "type": "A" }
       ]
     },
     {
       "name": "bar.foo.com",
       "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "10.3.3.3"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "10.4.4.4"
-        }
+        { "name": "@", "target": "10.3.3.3", "type": "A" },
+        { "name": "www", "target": "10.4.4.4", "type": "A" }
       ]
     },
     {
       "name": "foo.edu",
       "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "10.5.5.5"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "10.6.6.6"
-        },
-        {
-          "type": "A",
-          "name": "more1",
-          "target": "10.7.7.7"
-        },
-        {
-          "type": "A",
-          "name": "more2",
-          "target": "10.8.8.8"
-        }
+        { "name": "@", "target": "10.5.5.5", "type": "A" },
+        { "name": "www", "target": "10.6.6.6", "type": "A" },
+        { "name": "more1", "target": "10.7.7.7", "type": "A" },
+        { "name": "more2", "target": "10.8.8.8", "type": "A" }
       ]
     }
   ]
diff --git a/pkg/js/parse_tests/029-dextendsub.json b/pkg/js/parse_tests/029-dextendsub.json
index 5c83649b..ee803da3 100644
--- a/pkg/js/parse_tests/029-dextendsub.json
+++ b/pkg/js/parse_tests/029-dextendsub.json
@@ -1,264 +1,94 @@
 {
-  "registrars": [
-    {
-      "name": "Third-Party",
-      "type": "NONE"
-    }
-  ],
-  "dns_providers": [
-    {
-      "name": "Cloudflare",
-      "type": "CLOUDFLAREAPI"
-    }
-  ],
+  "dns_providers": [ { "name": "Cloudflare", "type": "CLOUDFLAREAPI" } ],
   "domains": [
     {
       "name": "foo.net",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "10.1.1.1"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "10.2.2.2"
-        },
-        {
-          "type": "A",
-          "name": "bar",
-          "subdomain": "bar",
-          "target": "10.3.3.3"
-        },
-        {
-          "type": "A",
-          "name": "www.bar",
-          "subdomain": "bar",
-          "target": "10.4.4.4"
-        },
-        {
-          "type": "A",
-          "name": "a.long.path.of.sub.domains",
-          "subdomain": "a.long.path.of.sub.domains",
-          "target": "10.25.25.25"
-        },
-        {
-          "type": "A",
-          "name": "www.a.long.path.of.sub.domains",
-          "subdomain": "a.long.path.of.sub.domains",
-          "target": "10.26.26.26"
-        }
-      ]
+        { "name": "@", "target": "10.1.1.1", "type": "A" },
+        { "name": "www", "target": "10.2.2.2", "type": "A" },
+        { "name": "bar", "subdomain": "bar", "target": "10.3.3.3", "type": "A" },
+        { "name": "www.bar", "subdomain": "bar", "target": "10.4.4.4", "type": "A" },
+        { "name": "a.long.path.of.sub.domains", "subdomain": "a.long.path.of.sub.domains", "target": "10.25.25.25", "type": "A" },
+        { "name": "www.a.long.path.of.sub.domains", "subdomain": "a.long.path.of.sub.domains", "target": "10.26.26.26", "type": "A" }
+      ],
+      "registrar": "Third-Party"
     },
     {
       "name": "foo.tld",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "20.5.5.5"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "20.6.6.6"
-        },
-        {
-          "type": "A",
-          "name": "a",
-          "target": "20.10.10.10"
-        }
-      ]
+        { "name": "@", "target": "20.5.5.5", "type": "A" },
+        { "name": "www", "target": "20.6.6.6", "type": "A" },
+        { "name": "a", "target": "20.10.10.10", "type": "A" }
+      ],
+      "registrar": "Third-Party"
     },
     {
       "name": "bar.foo.tld",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "30.7.7.7"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "30.8.8.8"
-        },
-        {
-          "type": "A",
-          "name": "a",
-          "target": "30.9.9.9"
-        }
-      ]
+        { "name": "@", "target": "30.7.7.7", "type": "A" },
+        { "name": "www", "target": "30.8.8.8", "type": "A" },
+        { "name": "a", "target": "30.9.9.9", "type": "A" }
+      ],
+      "registrar": "Third-Party"
     },
     {
       "name": "foo.help",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "40.12.12.12"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "40.12.12.12"
-        },
-        {
-          "type": "A",
-          "name": "morty",
-          "subdomain": "morty",
-          "target": "40.17.17.17"
-        },
-        {
-          "type": "A",
-          "name": "www.morty",
-          "subdomain": "morty",
-          "target": "40.18.18.18"
-        }
-      ]
+        { "name": "@", "target": "40.12.12.12", "type": "A" },
+        { "name": "www", "target": "40.12.12.12", "type": "A" },
+        { "name": "morty", "subdomain": "morty", "target": "40.17.17.17", "type": "A" },
+        { "name": "www.morty", "subdomain": "morty", "target": "40.18.18.18", "type": "A" }
+      ],
+      "registrar": "Third-Party"
     },
     {
       "name": "bar.foo.help",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "50.13.13.13"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "50.14.14.14"
-        },
-        {
-          "type": "A",
-          "name": "zip",
-          "subdomain": "zip",
-          "target": "50.15.15.15"
-        },
-        {
-          "type": "A",
-          "name": "www.zip",
-          "subdomain": "zip",
-          "target": "50.16.16.16"
-        }
-      ]
+        { "name": "@", "target": "50.13.13.13", "type": "A" },
+        { "name": "www", "target": "50.14.14.14", "type": "A" },
+        { "name": "zip", "subdomain": "zip", "target": "50.15.15.15", "type": "A" },
+        { "name": "www.zip", "subdomain": "zip", "target": "50.16.16.16", "type": "A" }
+      ],
+      "registrar": "Third-Party"
     },
     {
       "name": "foo.here",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "A",
-          "name": "@",
-          "target": "60.19.19.19"
-        },
-        {
-          "type": "A",
-          "name": "www",
-          "target": "60.20.20.20"
-        },
-        {
-          "type": "A",
-          "name": "bar",
-          "subdomain": "bar",
-          "target": "60.21.21.21"
-        },
-        {
-          "type": "A",
-          "name": "www.bar",
-          "subdomain": "bar",
-          "target": "60.22.22.22"
-        },
-        {
-          "type": "A",
-          "name": "baz.bar",
-          "subdomain": "baz.bar",
-          "target": "60.23.23.23"
-        },
-        {
-          "type": "A",
-          "name": "www.baz.bar",
-          "subdomain": "baz.bar",
-          "target": "60.24.24.24"
-        }
-      ]
+        { "name": "@", "target": "60.19.19.19", "type": "A" },
+        { "name": "www", "target": "60.20.20.20", "type": "A" },
+        { "name": "bar", "subdomain": "bar", "target": "60.21.21.21", "type": "A" },
+        { "name": "www.bar", "subdomain": "bar", "target": "60.22.22.22", "type": "A" },
+        { "name": "baz.bar", "subdomain": "baz.bar", "target": "60.23.23.23", "type": "A" },
+        { "name": "www.baz.bar", "subdomain": "baz.bar", "target": "60.24.24.24", "type": "A" }
+      ],
+      "registrar": "Third-Party"
     },
     {
       "name": "example.tld",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "CNAME",
-          "name": "a.sub",
-          "subdomain": "sub",
-          "target": "b"
-        },
-        {
-          "type": "CNAME",
-          "name": "b.sub",
-          "subdomain": "sub",
-          "target": "@"
-        },
-        {
-          "type": "CNAME",
-          "name": "c.sub",
-          "subdomain": "sub",
-          "target": "sub.example.tld."
-        },
-        {
-          "type": "CNAME",
-          "name": "e.sub",
-          "subdomain": "sub",
-          "target": "otherdomain.tld."
-        }
-      ]
+        { "name": "a.sub", "subdomain": "sub", "target": "b", "type": "CNAME" },
+        { "name": "b.sub", "subdomain": "sub", "target": "@", "type": "CNAME" },
+        { "name": "c.sub", "subdomain": "sub", "target": "sub.example.tld.", "type": "CNAME" },
+        { "name": "e.sub", "subdomain": "sub", "target": "otherdomain.tld.", "type": "CNAME" }
+      ],
+      "registrar": "Third-Party"
     },
     {
       "name": "foo.com",
-      "registrar": "Third-Party",
-      "dnsProviders": {
-        "Cloudflare": -1
-      },
+      "dnsProviders": { "Cloudflare": -1 },
       "records": [
-        {
-          "type": "CF_REDIRECT",
-          "name": "@",
-          "target": "test.foo.com,https://goo.com/$1"
-        },
-        {
-          "type": "CF_TEMP_REDIRECT",
-          "name": "@",
-          "target": "test.foo.com,https://goo.com/$1"
-        }
-      ]
+        { "name": "@", "target": "test.foo.com,https://goo.com/$1", "type": "CF_REDIRECT" },
+        { "name": "@", "target": "test.foo.com,https://goo.com/$1", "type": "CF_TEMP_REDIRECT" }
+      ],
+      "registrar": "Third-Party"
     }
-  ]
+  ],
+  "registrars": [ { "name": "Third-Party", "type": "NONE" } ]
 }
diff --git a/pkg/js/parse_tests/030-dextenddoc.json b/pkg/js/parse_tests/030-dextenddoc.json
index 03f95924..0b2a0fcd 100644
--- a/pkg/js/parse_tests/030-dextenddoc.json
+++ b/pkg/js/parse_tests/030-dextenddoc.json
@@ -1,67 +1,25 @@
 {
-  "registrars":
-  [
-    {"name":"Third-Party","type":"NONE"}
-  ],
-  "dns_providers":[{"name":"Cloudflare","type":"CLOUDFLAREAPI"}],
-  "domains":
-  [
+  "dns_providers": [ { "name": "Cloudflare", "type": "CLOUDFLAREAPI" } ],
+  "domains": [
     {
-      "name":"domain.tld",
-      "registrar":"Third-Party",
-      "dnsProviders": {"Cloudflare":-1},
-      "records":
-      [
-        {
-          "type":"A",
-          "name":"@",
-          "target":"127.0.0.1"
-        },
-        {
-          "type":"A",
-          "name":"www",
-          "target":"127.0.0.2"
-        },
-        {
-          "type":"CNAME",
-          "name":"a",
-          "target":"b"
-        },
-        {
-          "type":"A",
-          "name":"aaa",
-          "target":"127.0.0.3"
-        },
-        {
-          "type":"CNAME",
-          "name":"c",
-          "target":"d"
-        },
-        {
-          "type":"A",
-          "name":"bbb.sub",
-          "subdomain":"sub",
-          "target":"127.0.0.4"
-        },
-        {
-          "type":"A","name":"ccc.sub","subdomain":"sub","target":"127.0.0.5"
-        },
-        {
-          "type":"CNAME","name":"e.sub","subdomain":"sub","target":"f"
-        },
-        {
-          "type":"A","name":"ddd.sub.sub","subdomain":"sub.sub","target":"127.0.0.6"
-        },
-        {
-          "type":"CNAME","name":"g.sub.sub","subdomain":"sub.sub","target":"h"
-        },
-        {
-          "type":"A","name":"sub","subdomain":"sub","target":"127.0.0.7"
-        },
-        {
-          "type":"CNAME","name":"i.sub","subdomain":"sub","target":"j"
-        }
-      ]
+      "name": "domain.tld",
+      "dnsProviders": { "Cloudflare": -1 },
+      "records": [
+        { "name": "@", "target": "127.0.0.1", "type": "A" },
+        { "name": "www", "target": "127.0.0.2", "type": "A" },
+        { "name": "a", "target": "b", "type": "CNAME" },
+        { "name": "aaa", "target": "127.0.0.3", "type": "A" },
+        { "name": "c", "target": "d", "type": "CNAME" },
+        { "name": "bbb.sub", "subdomain": "sub", "target": "127.0.0.4", "type": "A" },
+        { "name": "ccc.sub", "subdomain": "sub", "target": "127.0.0.5", "type": "A" },
+        { "name": "e.sub", "subdomain": "sub", "target": "f", "type": "CNAME" },
+        { "name": "ddd.sub.sub", "subdomain": "sub.sub", "target": "127.0.0.6", "type": "A" },
+        { "name": "g.sub.sub", "subdomain": "sub.sub", "target": "h", "type": "CNAME" },
+        { "name": "sub", "subdomain": "sub", "target": "127.0.0.7", "type": "A" },
+        { "name": "i.sub", "subdomain": "sub", "target": "j", "type": "CNAME" }
+      ],
+      "registrar": "Third-Party"
     }
-  ]
+  ],
+  "registrars": [ { "name": "Third-Party", "type": "NONE" } ]
 }
diff --git a/pkg/js/parse_tests/031-dextendnames.json b/pkg/js/parse_tests/031-dextendnames.json
index a4c90ceb..98d94dcf 100644
--- a/pkg/js/parse_tests/031-dextendnames.json
+++ b/pkg/js/parse_tests/031-dextendnames.json
@@ -1,46 +1,43 @@
 {
-  "registrars":
-  [
-    {"name":"Third-Party","type":"NONE"}
+  "dns_providers": [
+    {
+      "name": "Cloudflare",
+      "type": "CLOUDFLAREAPI"
+    }
   ],
-  "dns_providers":
-  [
-    {"name":"Cloudflare","type":"CLOUDFLAREAPI"}],
-    "domains":
-    [
-      {
-        "name":"domain.tld",
-        "registrar":"Third-Party",
-        "dnsProviders":{"Cloudflare":-1},
-        "records":
-        [
-          {"type":"A","name":"@","target":"127.0.0.1"},
-          {"type":"A","name":"a","target":"127.0.0.2"},
-          {"type":"CNAME","name":"b","target":"c"},
-          {"type":"A","name":"@","target":"127.0.0.3"},
-          {"type":"A","name":"d","target":"127.0.0.4"},
-          {"type":"CNAME","name":"e","target":"f"},
-          {"type":"A","name":"ub","subdomain":"ub","target":"127.0.0.5"},
-          {"type":"A","name":"g.ub","subdomain":"ub","target":"127.0.0.6"},
-          {"type":"CNAME","name":"h.ub","subdomain":"ub","target":"i"},
-          {"type":"A","name":"@","subdomain":"ssub","target":"127.0.0.7"},
-          {"type":"A","name":"j","subdomain":"ssub","target":"127.0.0.8"},
-          {"type":"CNAME","name":"k","subdomain":"ssub","target":"l"}
-        ]
-      },
-      {
-        "name":"sub.domain.tld",
-        "registrar":"Third-Party",
-        "dnsProviders":{"Cloudflare":-1},
-        "records":
-        [
-          {"type":"A","name":"@","target":"127.0.1.1"},
-          {"type":"A","name":"aa","target":"127.0.1.2"},
-          {"type":"CNAME","name":"bb","target":"cc"},
-          {"type":"A","name":"@","target":"127.0.1.3"},
-          {"type":"A","name":"dd","target":"127.0.1.4"},
-          {"type":"CNAME","name":"ee","target":"ff"}
-        ]
-      }
-    ]
+  "domains": [
+    {
+      "name": "domain.tld",
+      "dnsProviders": { "Cloudflare": -1 },
+      "records": [
+        { "name": "@", "target": "127.0.0.1", "type": "A" },
+        { "name": "a", "target": "127.0.0.2", "type": "A" },
+        { "name": "b", "target": "c", "type": "CNAME" },
+        { "name": "@", "target": "127.0.0.3", "type": "A" },
+        { "name": "d", "target": "127.0.0.4", "type": "A" },
+        { "name": "e", "target": "f", "type": "CNAME" },
+        { "name": "ub", "subdomain": "ub", "target": "127.0.0.5", "type": "A" },
+        { "name": "g.ub", "subdomain": "ub", "target": "127.0.0.6", "type": "A" },
+        { "name": "h.ub", "subdomain": "ub", "target": "i", "type": "CNAME" },
+        { "name": "ssub", "subdomain": "ssub", "target": "127.0.0.7", "type": "A" },
+        { "name": "j.ssub", "subdomain": "ssub", "target": "127.0.0.8", "type": "A" },
+        { "name": "k.ssub", "subdomain": "ssub", "target": "l", "type": "CNAME" }
+      ],
+      "registrar": "Third-Party"
+    },
+    {
+      "name": "sub.domain.tld",
+      "dnsProviders": { "Cloudflare": -1 },
+      "records": [
+        { "name": "@", "target": "127.0.1.1", "type": "A" },
+        { "name": "aa", "target": "127.0.1.2", "type": "A" },
+        { "name": "bb", "target": "cc", "type": "CNAME" },
+        { "name": "@", "target": "127.0.1.3", "type": "A" },
+        { "name": "dd", "target": "127.0.1.4", "type": "A" },
+        { "name": "ee", "target": "ff", "type": "CNAME" }
+      ],
+      "registrar": "Third-Party"
+    }
+  ],
+  "registrars": [ { "name": "Third-Party", "type": "NONE" } ]
 }