From 8129760c735acb09ff084e61f969c36a4524741f Mon Sep 17 00:00:00 2001 From: Noel Paton-og <144045623+noelpaton-og@users.noreply.github.com> Date: Thu, 6 Mar 2025 19:36:49 +0800 Subject: [PATCH 01/20] Create noelpatonog.json --- domains/noelpatonog.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 domains/noelpatonog.json diff --git a/domains/noelpatonog.json b/domains/noelpatonog.json new file mode 100644 index 000000000..ffe84597f --- /dev/null +++ b/domains/noelpatonog.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "noelpaton-og", + "email": "noel27938@gmail.com" + }, + "record": { + "CNAME": "noelpaton-og.github.io" + } +} From f775e55954f14ce5d8987a8ac0535b679049c75b Mon Sep 17 00:00:00 2001 From: JustDeveloper <176615419+JustDeveloper1@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:41:48 +0300 Subject: [PATCH 02/20] Update reserved.json --- util/reserved.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/reserved.json b/util/reserved.json index 8ac0501fd..13b954c4f 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -67,6 +67,8 @@ "helper[1-99]", "helpers", "hostmaster", + "http", + "https", "info", "infos", "int", From 18a4b8120a2179a58a7a915a0acfeccfbce905d8 Mon Sep 17 00:00:00 2001 From: JustDeveloper <176615419+JustDeveloper1@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:46:23 +0300 Subject: [PATCH 03/20] https://github.com/is-a-dev/register/pull/20801#issuecomment-2695781465 --- util/reserved.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/reserved.json b/util/reserved.json index 13b954c4f..aae8f4634 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -16,6 +16,7 @@ "api", "apis", "auth", + "auth[1-99]", "authentication", "authorisation", "authorise", @@ -39,6 +40,8 @@ "con", "confirm", "confirmation", + "contribute", + "contributing", "dash", "dashboard", "default", @@ -149,6 +152,8 @@ "teams", "test", "test[1-9999]", + "terms", + "terms[1-99]", "termsofservice", "terms-of-service", "tld", From d59a92a49f3a81d9b69f1404608753b69b24d955 Mon Sep 17 00:00:00 2001 From: JustDeveloper <176615419+JustDeveloper1@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:47:56 +0300 Subject: [PATCH 04/20] Update reserved.json --- util/reserved.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/reserved.json b/util/reserved.json index aae8f4634..7e8dc36a1 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -71,7 +71,9 @@ "helpers", "hostmaster", "http", + "http[1-9999]", "https", + "https[1-9999]", "info", "infos", "int", From aec811cb280a9ac43793bb6a4e4d9c51109b176a Mon Sep 17 00:00:00 2001 From: JustDeveloper <176615419+JustDeveloper1@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:50:22 +0300 Subject: [PATCH 05/20] Update reserved.json --- util/reserved.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/util/reserved.json b/util/reserved.json index 7e8dc36a1..131dc9b70 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -10,6 +10,7 @@ "admin[1-99]", "admins", "administrator", + "administrator[1-99]", "administrators", "alert", "alerts", @@ -39,11 +40,15 @@ "com[1-9]", "con", "confirm", + "confirm[1-99]", "confirmation", "contribute", + "contribute[1-99]", "contributing", "dash", + "dash[1-99]", "dashboard", + "dashboard[1-99]", "default", "dns", "doc", @@ -160,13 +165,16 @@ "terms-of-service", "tld", "token", + "token[1-9999]", "tokens", "tos", + "tos[1-99]", "txt", "txt[1-9999]", "url", "url[1-9999]", "user", + "user[1-9999]", "users", "webmail", "webmaster", From e56139a8be257a8ee33955c7c69204d48e332aba Mon Sep 17 00:00:00 2001 From: Angga Eko Pratama Date: Thu, 6 Mar 2025 19:15:47 +0700 Subject: [PATCH 06/20] Domain for redirection (www) Domain for redirection www.angganix.is-a.dev to angganix.is-a.dev --- domains/www.angganix | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 domains/www.angganix diff --git a/domains/www.angganix b/domains/www.angganix new file mode 100644 index 000000000..e7078cf3b --- /dev/null +++ b/domains/www.angganix @@ -0,0 +1,10 @@ +{ + "description": "Domain for redirection to angganix.is-a.dev", + "owner": { + "username": "angganix", + "email": "angganix@gmail.com" + }, + "record": { + "URL": "https://angganix.is-a.dev" + } +} From 1bcb2b3bff9e938fc7ba021c79a39ccf07df35da Mon Sep 17 00:00:00 2001 From: Omar <180954465+omardotdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:30:01 +0200 Subject: [PATCH 07/20] reserve some more subdomains --- util/reserved.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/reserved.json b/util/reserved.json index 8ac0501fd..a4691a3d6 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -22,6 +22,10 @@ "authorization", "authorize", "aux", + "ad", + "ads", + "advertisment", + "advertisments", "billing", "blog", "calendar", From fe23876f2f0b2d5637df43dd07798615758a63d2 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:33:08 +0800 Subject: [PATCH 08/20] Update ci.yml --- .github/workflows/ci.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 523d63ce8..a880e816b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,13 +47,12 @@ jobs: uses: actions/github-script@v6 with: script: | - const { execSync } = require("child_process") - const { commits } = context.payload.pull_request - const rawFiles = execSync(`git diff --name-only HEAD HEAD~${commits}`).toString() - const files = rawFiles.split("\n").filter(Boolean) - console.log(files) - const jsonArray = JSON.stringify(files) - core.exportVariable('CHANGED_FILES', jsonArray) + const { execSync } = require("child_process"); + const files = execSync(`git diff --name-only HEAD HEAD~${context.payload.pull_request.commits}`) + .toString() + .split("\n") + .filter(Boolean); + core.exportVariable("CHANGED_FILES", JSON.stringify(files)); - name: Run tests - run: npx ava tests/*.test.js + run: npx ava tests/*.test.js --timeout=30s From 6339f26bef0d9dbf56737ffddaca7794cf35bd24 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:38:14 +0800 Subject: [PATCH 09/20] new pr tests + prettier --- tests/domains.test.js | 73 +++++--------- tests/pr.test.js | 47 +++++++++ tests/proxy.test.js | 25 ++--- tests/records.test.js | 220 +++++++++++------------------------------- tests/special.test.js | 29 ++---- util/bypassed.json | 1 - util/reserved.json | 1 + util/trusted.json | 8 ++ 8 files changed, 152 insertions(+), 252 deletions(-) create mode 100644 tests/pr.test.js delete mode 100644 util/bypassed.json create mode 100644 util/trusted.json diff --git a/tests/domains.test.js b/tests/domains.test.js index 90262cdef..f1e4954a3 100644 --- a/tests/domains.test.js +++ b/tests/domains.test.js @@ -3,9 +3,7 @@ const fs = require("fs-extra"); const path = require("path"); const domainsPath = path.resolve("domains"); -const files = fs - .readdirSync(domainsPath) - .filter((file) => file.endsWith(".json")); +const files = fs.readdirSync(domainsPath).filter((file) => file.endsWith(".json")); const domainCache = {}; @@ -15,15 +13,11 @@ function getDomainData(subdomain) { } try { - const data = fs.readJsonSync( - path.join(domainsPath, `${subdomain}.json`), - ); + const data = fs.readJsonSync(path.join(domainsPath, `${subdomain}.json`)); domainCache[subdomain] = data; // Cache the domain data return data; } catch (error) { - throw new Error( - `Failed to read JSON for ${subdomain}: ${error.message}`, - ); + throw new Error(`Failed to read JSON for ${subdomain}: ${error.message}`); } } @@ -52,30 +46,24 @@ t("Nested subdomains should not exist without a parent subdomain", (t) => { const parentSubdomain = getParentSubdomain(subdomain); t.true( parentSubdomain && files.includes(`${parentSubdomain}.json`), - `${file}: Parent subdomain does not exist`, + `${file}: Parent subdomain does not exist` ); } }); }); -t( - "Nested subdomains should not exist if the parent subdomain has NS records", - (t) => { - files.forEach((file) => { - const subdomain = file.replace(/\.json$/, ""); +t("Nested subdomains should not exist if the parent subdomain has NS records", (t) => { + files.forEach((file) => { + const subdomain = file.replace(/\.json$/, ""); - if (subdomain.split(".").length > 1) { - const parentSubdomain = getParentSubdomain(subdomain); - const parentDomain = getDomainData(parentSubdomain); + if (subdomain.split(".").length > 1) { + const parentSubdomain = getParentSubdomain(subdomain); + const parentDomain = getDomainData(parentSubdomain); - t.true( - !parentDomain.record.NS, - `${file}: Parent subdomain has NS records`, - ); - } - }); - }, -); + t.true(!parentDomain.record.NS, `${file}: Parent subdomain has NS records`); + } + }); +}); t("Nested subdomains should be owned by the parent subdomain's owner", (t) => { files.forEach((file) => { @@ -87,9 +75,8 @@ t("Nested subdomains should be owned by the parent subdomain's owner", (t) => { const parentDomain = getDomainData(parentSubdomain); t.true( - data.owner.username.toLowerCase() === - parentDomain.owner.username.toLowerCase(), - `${file}: Owner does not match the parent subdomain`, + data.owner.username.toLowerCase() === parentDomain.owner.username.toLowerCase(), + `${file}: Owner does not match the parent subdomain` ); } }); @@ -103,31 +90,15 @@ t("Subdomains containing an underscore can only have specific records", (t) => { const data = getDomainData(subdomain); const recordKeys = Object.keys(data.record); - if ( - subdomain.startsWith("_acme-challenge.") || - subdomain.includes("._domainkey.") - ) { + if (subdomain.startsWith("_acme-challenge.") || subdomain.includes("._domainkey.")) { t.true( - recordKeys.every((key) => - new Set(["TXT", "CNAME"]).has(key), - ), - `${file}: This type of subdomain can only have TXT or CNAME records`, - ); - } else if ( - subdomain.includes("._tcp.") || - subdomain.includes("._udp.") - ) { - t.deepEqual( - recordKeys, - ["SRV"], - `${file}: This type of subdomain can only have SRV records`, + recordKeys.every((key) => new Set(["TXT", "CNAME"]).has(key)), + `${file}: This type of subdomain can only have TXT or CNAME records` ); + } else if (subdomain.includes("._tcp.") || subdomain.includes("._udp.")) { + t.deepEqual(recordKeys, ["SRV"], `${file}: This type of subdomain can only have SRV records`); } else { - t.deepEqual( - recordKeys, - ["TXT"], - `${file}: Subdomains with underscores can only have TXT records`, - ); + t.deepEqual(recordKeys, ["TXT"], `${file}: Subdomains with underscores can only have TXT records`); } } }); diff --git a/tests/pr.test.js b/tests/pr.test.js new file mode 100644 index 000000000..f2cf47c2d --- /dev/null +++ b/tests/pr.test.js @@ -0,0 +1,47 @@ +const t = require("ava"); +const fs = require("fs-extra"); +const path = require("path"); + +const domainsPath = path.resolve("domains"); +const files = fs.readdirSync(domainsPath).filter((file) => file.endsWith(".json")); + +const changedFiles = JSON.parse(process.env.CHANGED_FILES); +const prAuthor = process.env.PR_AUTHOR.toLowerCase(); +const prLabels = JSON.parse(process.env.PR_LABELS); +const trustedUsers = require("../util/trusted.json").map((u) => u.toLowerCase()); + +function getDomainData(subdomain) { + try { + const data = fs.readJsonSync(path.join(domainsPath, `${subdomain}.json`)); + return data; + } catch (error) { + throw new Error(`Failed to read JSON for ${subdomain}: ${error.message}`); + } +} + +t("Users can only update their own subdomains", (t) => { + if (process.env.PR_AUTHOR && process.env.CHANGED_FILES) { + const changedJSONFiles = changedFiles + .filter((file) => file.startsWith("domains/")) + .map((file) => path.basename(file)) + .forEach((file) => file.replace(/\.json$/, "")); + + if (trustedUsers.includes(prAuthor) || prLabels.includes("bypass-owner-check")) { + t.pass(); + } else { + files + .filter((file) => changedJSONFiles.includes(file)) + .forEach((file) => { + const subdomain = file.replace(/\.json$/, ""); + const data = getDomainData(subdomain); + + t.true( + data.owner.username.toLowerCase() === prAuthor, + `${subdomain}: ${prAuthor} does not own ${subdomain}.is-a.dev` + ); + }); + } + } + + t.pass(); +}); diff --git a/tests/proxy.test.js b/tests/proxy.test.js index 0f1c65039..d7c73bc5b 100644 --- a/tests/proxy.test.js +++ b/tests/proxy.test.js @@ -25,29 +25,22 @@ function validateProxiedRecords(t, data, file) { const recordTypes = Array.from(requiredRecordsToProxy).join(", "); if (data.proxied) { - const hasProxiedRecord = Object.keys(data.record).some((key) => - requiredRecordsToProxy.has(key), - ); + const hasProxiedRecord = Object.keys(data.record).some((key) => requiredRecordsToProxy.has(key)); t.true( hasProxiedRecord, - `${file}: Proxied is true but there are no records that can be proxied (${recordTypes} expected)`, + `${file}: Proxied is true but there are no records that can be proxied (${recordTypes} expected)` ); } } const domainsPath = path.resolve("domains"); -const files = fs - .readdirSync(domainsPath) - .filter((file) => file.endsWith(".json")); +const files = fs.readdirSync(domainsPath).filter((file) => file.endsWith(".json")); -t( - "Domains with proxy enabled must have at least one proxy-able record", - (t) => { - files.forEach((file) => { - const domain = getDomainData(file); +t("Domains with proxy enabled must have at least one proxy-able record", (t) => { + files.forEach((file) => { + const domain = getDomainData(file); - validateProxiedRecords(t, domain, file); - }); - }, -); + validateProxiedRecords(t, domain, file); + }); +}); diff --git a/tests/records.test.js b/tests/records.test.js index 9f05a650b..fbbeee156 100644 --- a/tests/records.test.js +++ b/tests/records.test.js @@ -2,29 +2,14 @@ const t = require("ava"); const fs = require("fs-extra"); const path = require("path"); -const validRecordTypes = new Set([ - "A", - "AAAA", - "CAA", - "CNAME", - "DS", - "MX", - "NS", - "SRV", - "TXT", - "URL", -]); -const hostnameRegex = - /^(?=.{1,253}$)(?:(?:[_a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,63}$/; -const ipv4Regex = - /^(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}$/; +const validRecordTypes = new Set(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NS", "SRV", "TXT", "URL"]); +const hostnameRegex = /^(?=.{1,253}$)(?:(?:[_a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,63}$/; +const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}$/; const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){0,6}::(?:[0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}$/; const domainsPath = path.resolve("domains"); -const files = fs - .readdirSync(domainsPath) - .filter((file) => file.endsWith(".json")); +const files = fs.readdirSync(domainsPath).filter((file) => file.endsWith(".json")); const domainCache = {}; @@ -53,7 +38,7 @@ function expandIPv6(ip) { segments = [ ...nonEmptySegments.slice(0, emptyIndex), ...Array(missingSegments).fill("0000"), - ...nonEmptySegments.slice(emptyIndex), + ...nonEmptySegments.slice(emptyIndex) ]; } @@ -63,11 +48,7 @@ function expandIPv6(ip) { function validateIPv4(ip, proxied) { const parts = ip.split(".").map(Number); - if ( - parts.length !== 4 || - parts.some((part) => isNaN(part) || part < 0 || part > 255) - ) - return false; + if (parts.length !== 4 || parts.some((part) => isNaN(part) || part < 0 || part > 255)) return false; if (ip === "192.0.2.1" && proxied) return true; return !( @@ -113,153 +94,100 @@ function validateRecordValues(t, data, file) { Object.entries(data.record).forEach(([key, value]) => { // General validation for arrays if (["A", "AAAA", "MX", "NS"].includes(key)) { - t.true( - Array.isArray(value), - `${file}: Record value for ${key} should be an array`, - ); + t.true(Array.isArray(value), `${file}: Record value for ${key} should be an array`); value.forEach((record, idx) => { t.true( typeof record === "string", - `${file}: Record value for ${key} should be a string at index ${idx}`, + `${file}: Record value for ${key} should be a string at index ${idx}` ); if (key === "A") { - t.true( - ipv4Regex.test(record), - `${file}: Invalid IPv4 address for ${key} at index ${idx}`, - ); + t.true(ipv4Regex.test(record), `${file}: Invalid IPv4 address for ${key} at index ${idx}`); t.true( validateIPv4(record, data.proxied), - `${file}: Invalid IPv4 address for ${key} at index ${idx}`, + `${file}: Invalid IPv4 address for ${key} at index ${idx}` ); } else if (key === "AAAA") { const expandedIPv6 = expandIPv6(record); - t.true( - ipv6Regex.test(expandedIPv6), - `${file}: Invalid IPv6 address for ${key} at index ${idx}`, - ); - t.true( - validateIPv6(expandedIPv6), - `${file}: Invalid IPv6 address for ${key} at index ${idx}`, - ); + t.true(ipv6Regex.test(expandedIPv6), `${file}: Invalid IPv6 address for ${key} at index ${idx}`); + t.true(validateIPv6(expandedIPv6), `${file}: Invalid IPv6 address for ${key} at index ${idx}`); } else if (["MX", "NS"].includes(key)) { - t.true( - isValidHostname(record), - `${file}: Invalid hostname for ${key} at index ${idx}`, - ); + t.true(isValidHostname(record), `${file}: Invalid hostname for ${key} at index ${idx}`); } }); } // CNAME and URL validations if (["CNAME", "URL"].includes(key)) { - t.true( - typeof value === "string", - `${file}: Record value for ${key} should be a string`, - ); + t.true(typeof value === "string", `${file}: Record value for ${key} should be a string`); if (key === "CNAME") { - t.true( - isValidHostname(value), - `${file}: Invalid hostname for ${key}`, - ); + t.true(isValidHostname(value), `${file}: Invalid hostname for ${key}`); t.true(value !== file, `${file}: CNAME cannot point to itself`); } else if (key === "URL") { t.true( value.startsWith("http://") || value.startsWith("https://"), - `${file}: Record value for ${key} must start with http:// or https://`, - ); - t.notThrows( - () => new URL(value), - `${file}: Invalid URL for ${key}`, + `${file}: Record value for ${key} must start with http:// or https://` ); + t.notThrows(() => new URL(value), `${file}: Invalid URL for ${key}`); const urlHost = new URL(value).host; const isSelfReferencing = - file === "@.json" - ? urlHost === "is-a.dev" - : urlHost === `${subdomain}.is-a.dev`; + file === "@.json" ? urlHost === "is-a.dev" : urlHost === `${subdomain}.is-a.dev`; - t.false( - isSelfReferencing, - `${file}: URL cannot point to itself`, - ); + t.false(isSelfReferencing, `${file}: URL cannot point to itself`); } } // CAA, DS, SRV validations if (["CAA", "DS", "SRV"].includes(key)) { - t.true( - Array.isArray(value), - `${file}: Record value for ${key} should be an array`, - ); + t.true(Array.isArray(value), `${file}: Record value for ${key} should be an array`); value.forEach((record, idx) => { t.true( typeof record === "object", - `${file}: Record value for ${key} should be an object at index ${idx}`, + `${file}: Record value for ${key} should be an object at index ${idx}` ); if (key === "CAA") { t.true( ["issue", "issuewild", "iodef"].includes(record.tag), - `${file}: Invalid tag for CAA at index ${idx}`, - ); - t.true( - typeof record.value === "string", - `${file}: Invalid value for CAA at index ${idx}`, + `${file}: Invalid tag for CAA at index ${idx}` ); + t.true(typeof record.value === "string", `${file}: Invalid value for CAA at index ${idx}`); t.true( isValidHostname(record.value) || record.value === ";", - `${file}: Value must be a hostname or semicolon for CAA at index ${idx}`, + `${file}: Value must be a hostname or semicolon for CAA at index ${idx}` ); } else if (key === "DS") { t.true( - Number.isInteger(record.key_tag) && - record.key_tag >= 0 && - record.key_tag <= 65535, - `${file}: Invalid key_tag for DS at index ${idx}`, + Number.isInteger(record.key_tag) && record.key_tag >= 0 && record.key_tag <= 65535, + `${file}: Invalid key_tag for DS at index ${idx}` ); t.true( - Number.isInteger(record.algorithm) && - record.algorithm >= 0 && - record.algorithm <= 255, - `${file}: Invalid algorithm for DS at index ${idx}`, + Number.isInteger(record.algorithm) && record.algorithm >= 0 && record.algorithm <= 255, + `${file}: Invalid algorithm for DS at index ${idx}` ); t.true( - Number.isInteger(record.digest_type) && - record.digest_type >= 0 && - record.digest_type <= 255, - `${file}: Invalid digest_type for DS at index ${idx}`, - ); - t.true( - isValidHexadecimal(record.digest), - `${file}: Invalid digest for DS at index ${idx}`, + Number.isInteger(record.digest_type) && record.digest_type >= 0 && record.digest_type <= 255, + `${file}: Invalid digest_type for DS at index ${idx}` ); + t.true(isValidHexadecimal(record.digest), `${file}: Invalid digest for DS at index ${idx}`); } else if (key === "SRV") { t.true( - Number.isInteger(record.priority) && - record.priority >= 0 && - record.priority <= 65535, - `${file}: Invalid priority for SRV at index ${idx}`, + Number.isInteger(record.priority) && record.priority >= 0 && record.priority <= 65535, + `${file}: Invalid priority for SRV at index ${idx}` ); t.true( - Number.isInteger(record.weight) && - record.weight >= 0 && - record.weight <= 65535, - `${file}: Invalid weight for SRV at index ${idx}`, + Number.isInteger(record.weight) && record.weight >= 0 && record.weight <= 65535, + `${file}: Invalid weight for SRV at index ${idx}` ); t.true( - Number.isInteger(record.port) && - record.port >= 0 && - record.port <= 65535, - `${file}: Invalid port for SRV at index ${idx}`, - ); - t.true( - isValidHostname(record.target), - `${file}: Invalid target for SRV at index ${idx}`, + Number.isInteger(record.port) && record.port >= 0 && record.port <= 65535, + `${file}: Invalid port for SRV at index ${idx}` ); + t.true(isValidHostname(record.target), `${file}: Invalid target for SRV at index ${idx}`); } }); } @@ -268,60 +196,44 @@ function validateRecordValues(t, data, file) { if (key === "TXT") { const values = Array.isArray(value) ? value : [value]; values.forEach((record, idx) => { - t.true( - typeof record === "string", - `${file}: TXT record value should be a string at index ${idx}`, - ); + t.true(typeof record === "string", `${file}: TXT record value should be a string at index ${idx}`); }); } }); if (data.redirect_config) { - const customPaths = Object.keys( - data.redirect_config.custom_paths || {}, - ); + const customPaths = Object.keys(data.redirect_config.custom_paths || {}); const pathRegex = /^\/[a-zA-Z0-9\-_\.\/]+(? { - const customRedirectURL = - data.redirect_config.custom_paths[customPath]; + const customRedirectURL = data.redirect_config.custom_paths[customPath]; const urlMessage = `${file}: Custom path in redirect_config`; // Validate the custom path t.true( pathRegex.test(customPath), - `${urlMessage} must start with a slash, contain only alphanumeric characters, hyphens, underscores, periods, and slashes, and cannot end with a slash at index ${idx}`, + `${urlMessage} must start with a slash, contain only alphanumeric characters, hyphens, underscores, periods, and slashes, and cannot end with a slash at index ${idx}` ); t.true( customPath.length >= 2 && customPath.length <= 255, - `${urlMessage} should be 2-255 characters long at index ${idx}`, + `${urlMessage} should be 2-255 characters long at index ${idx}` ); // Validate the redirect URL t.true( data.record.URL !== customRedirectURL, - `${urlMessage} should be different from the URL record at index ${idx}`, + `${urlMessage} should be different from the URL record at index ${idx}` ); t.true( - customRedirectURL.startsWith("http://") || - customRedirectURL.startsWith("https://"), - `${urlMessage} must start with http:// or https:// at index ${idx}`, - ); - t.notThrows( - () => new URL(customRedirectURL), - `${urlMessage} contains an invalid URL at index ${idx}`, + customRedirectURL.startsWith("http://") || customRedirectURL.startsWith("https://"), + `${urlMessage} must start with http:// or https:// at index ${idx}` ); + t.notThrows(() => new URL(customRedirectURL), `${urlMessage} contains an invalid URL at index ${idx}`); // Check for self-referencing redirects const urlHost = new URL(customRedirectURL).host; - const isSelfReferencing = - file === "@.json" - ? urlHost === "is-a.dev" - : urlHost === `${subdomain}.is-a.dev`; - t.false( - isSelfReferencing, - `${urlMessage} cannot point to itself at index ${idx}`, - ); + const isSelfReferencing = file === "@.json" ? urlHost === "is-a.dev" : urlHost === `${subdomain}.is-a.dev`; + t.false(isSelfReferencing, `${urlMessage} cannot point to itself at index ${idx}`); }); } } @@ -332,51 +244,35 @@ t("All files should have valid record types", (t) => { const recordKeys = Object.keys(data.record); recordKeys.forEach((key) => { - t.true( - validateRecordType(key), - `${file}: Invalid record type: ${key}`, - ); + t.true(validateRecordType(key), `${file}: Invalid record type: ${key}`); }); // Record type combinations validation if (recordKeys.includes("CNAME") && !data.proxied) { - t.is( - recordKeys.length, - 1, - `${file}: CNAME records cannot be combined with other records unless proxied`, - ); + t.is(recordKeys.length, 1, `${file}: CNAME records cannot be combined with other records unless proxied`); } if (recordKeys.includes("NS")) { t.true( - recordKeys.length === 1 || - (recordKeys.length === 2 && recordKeys.includes("DS")), - `${file}: NS records cannot be combined with other records, except for DS records`, + recordKeys.length === 1 || (recordKeys.length === 2 && recordKeys.includes("DS")), + `${file}: NS records cannot be combined with other records, except for DS records` ); } if (recordKeys.includes("DS")) { - t.true( - recordKeys.includes("NS"), - `${file}: DS records must be combined with NS records`, - ); + t.true(recordKeys.includes("NS"), `${file}: DS records must be combined with NS records`); } if (recordKeys.includes("URL")) { t.true( - !recordKeys.includes("A") && - !recordKeys.includes("AAAA") && - !recordKeys.includes("CNAME"), - `${file}: URL records cannot be combined with A, AAAA, or CNAME records`, + !recordKeys.includes("A") && !recordKeys.includes("AAAA") && !recordKeys.includes("CNAME"), + `${file}: URL records cannot be combined with A, AAAA, or CNAME records` ); } if (data.redirect_config) { t.true( recordKeys.includes("URL") || data.proxied, - `${file}: Redirect config must be combined with a URL record or the domain must be proxied`, + `${file}: Redirect config must be combined with a URL record or the domain must be proxied` ); if (data.redirect_config.redirect_paths) { - t.true( - recordKeys.includes("URL"), - `${file}: redirect_config.redirect_paths requires a URL record`, - ); + t.true(recordKeys.includes("URL"), `${file}: redirect_config.redirect_paths requires a URL record`); } } diff --git a/tests/special.test.js b/tests/special.test.js index 3a5f8aeef..eb8ca52e0 100644 --- a/tests/special.test.js +++ b/tests/special.test.js @@ -3,24 +3,14 @@ const fs = require("fs-extra"); const path = require("path"); const domainsPath = path.resolve("domains"); -const files = fs - .readdirSync(domainsPath) - .filter((file) => file.endsWith(".json")); - -const bypassedUsernames = require("../util/bypassed.json").map((username) => - username.toLowerCase(), -); +const files = fs.readdirSync(domainsPath).filter((file) => file.endsWith(".json")); function getDomainData(subdomain) { try { - const data = fs.readJsonSync( - path.join(domainsPath, `${subdomain}.json`), - ); + const data = fs.readJsonSync(path.join(domainsPath, `${subdomain}.json`)); return data; } catch (error) { - throw new Error( - `Failed to read JSON for ${subdomain}: ${error.message}`, - ); + throw new Error(`Failed to read JSON for ${subdomain}: ${error.message}`); } } @@ -31,20 +21,15 @@ t("Users are limited to one single character subdomain", (t) => { const subdomain = file.replace(/\.json$/, ""); const data = getDomainData(subdomain); - if ( - subdomain.length === 1 && - !bypassedUsernames.includes(data.owner.username.toLowerCase()) - ) { + if (subdomain.length === 1 && data.owner.username.toLowerCase() !== "is-a-dev") { results.push({ subdomain, - owner: data.owner.username.toLowerCase(), + owner: data.owner.username.toLowerCase() }); } }); - const duplicates = results.filter( - (result) => results.filter((r) => r.owner === result.owner).length > 1, - ); + const duplicates = results.filter((result) => results.filter((r) => r.owner === result.owner).length > 1); const output = duplicates.reduce((acc, curr) => { if (!acc[curr.owner]) { acc[curr.owner] = []; @@ -59,7 +44,7 @@ t("Users are limited to one single character subdomain", (t) => { 0, Object.keys(output) .map((owner) => `${owner} - ${output[owner].join(", ")}`) - .join("\n"), + .join("\n") ); t.pass(); diff --git a/util/bypassed.json b/util/bypassed.json deleted file mode 100644 index 31fe6be0d..000000000 --- a/util/bypassed.json +++ /dev/null @@ -1 +0,0 @@ -["is-a-dev", "wdhdev"] diff --git a/util/reserved.json b/util/reserved.json index 8ac0501fd..c87796c32 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -105,6 +105,7 @@ "ops", "org", "organisation", + "organization", "owner", "pay", "payment", diff --git a/util/trusted.json b/util/trusted.json new file mode 100644 index 000000000..4b1b9b611 --- /dev/null +++ b/util/trusted.json @@ -0,0 +1,8 @@ +[ + "DEV-DIBSTER", + "iostpa", + "is-a-dev-bot", + "orangci", + "Stef-00012", + "wdhdev" +] From 9fd9a8ea9503002f7b4ef81bf08b3bf25ae246e5 Mon Sep 17 00:00:00 2001 From: Angga Eko Pratama Date: Thu, 6 Mar 2025 19:45:33 +0700 Subject: [PATCH 10/20] Rename www.angganix to www.angganix.json --- domains/{www.angganix => www.angganix.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename domains/{www.angganix => www.angganix.json} (100%) diff --git a/domains/www.angganix b/domains/www.angganix.json similarity index 100% rename from domains/www.angganix rename to domains/www.angganix.json From 2807153a715399a89e6a7c30bb5c7faf7d995e1c Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:48:06 +0800 Subject: [PATCH 11/20] Update pr.test.js --- tests/pr.test.js | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/tests/pr.test.js b/tests/pr.test.js index f2cf47c2d..8456e1576 100644 --- a/tests/pr.test.js +++ b/tests/pr.test.js @@ -2,17 +2,11 @@ const t = require("ava"); const fs = require("fs-extra"); const path = require("path"); -const domainsPath = path.resolve("domains"); -const files = fs.readdirSync(domainsPath).filter((file) => file.endsWith(".json")); - -const changedFiles = JSON.parse(process.env.CHANGED_FILES); -const prAuthor = process.env.PR_AUTHOR.toLowerCase(); -const prLabels = JSON.parse(process.env.PR_LABELS); const trustedUsers = require("../util/trusted.json").map((u) => u.toLowerCase()); function getDomainData(subdomain) { try { - const data = fs.readJsonSync(path.join(domainsPath, `${subdomain}.json`)); + const data = fs.readJsonSync(path.join(path.resolve("domains"), `${subdomain}.json`)); return data; } catch (error) { throw new Error(`Failed to read JSON for ${subdomain}: ${error.message}`); @@ -21,26 +15,24 @@ function getDomainData(subdomain) { t("Users can only update their own subdomains", (t) => { if (process.env.PR_AUTHOR && process.env.CHANGED_FILES) { + const changedFiles = JSON.parse(process.env.CHANGED_FILES); + const prAuthor = process.env.PR_AUTHOR.toLowerCase(); const changedJSONFiles = changedFiles .filter((file) => file.startsWith("domains/")) - .map((file) => path.basename(file)) - .forEach((file) => file.replace(/\.json$/, "")); + .map((file) => path.basename(file)); - if (trustedUsers.includes(prAuthor) || prLabels.includes("bypass-owner-check")) { - t.pass(); - } else { - files - .filter((file) => changedJSONFiles.includes(file)) - .forEach((file) => { - const subdomain = file.replace(/\.json$/, ""); - const data = getDomainData(subdomain); + if (!changedJSONFiles || trustedUsers.includes(prAuthor)) return t.pass(); + if (process.env.PR_LABELS && process.env.PR_LABELS.includes("bypass-owner-check")) return t.pass(); - t.true( - data.owner.username.toLowerCase() === prAuthor, - `${subdomain}: ${prAuthor} does not own ${subdomain}.is-a.dev` - ); - }); - } + changedJSONFiles.forEach((file) => { + const subdomain = file.replace(/\.json$/, ""); + const data = getDomainData(subdomain); + + t.true( + data.owner.username.toLowerCase() === prAuthor, + `${subdomain}: ${prAuthor} does not own ${subdomain}.is-a.dev` + ); + }); } t.pass(); From 79d807cd016e31e3d198c18cd8a7e7a4da87c0a5 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:52:29 +0800 Subject: [PATCH 12/20] updates --- tests/domains.test.js | 22 ---------------------- util/reserved.json | 16 ++-------------- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/tests/domains.test.js b/tests/domains.test.js index f1e4954a3..cecb2159f 100644 --- a/tests/domains.test.js +++ b/tests/domains.test.js @@ -81,25 +81,3 @@ t("Nested subdomains should be owned by the parent subdomain's owner", (t) => { } }); }); - -t("Subdomains containing an underscore can only have specific records", (t) => { - files.forEach((file) => { - const subdomain = file.replace(/\.json$/, ""); - - if (subdomain.includes("_")) { - const data = getDomainData(subdomain); - const recordKeys = Object.keys(data.record); - - if (subdomain.startsWith("_acme-challenge.") || subdomain.includes("._domainkey.")) { - t.true( - recordKeys.every((key) => new Set(["TXT", "CNAME"]).has(key)), - `${file}: This type of subdomain can only have TXT or CNAME records` - ); - } else if (subdomain.includes("._tcp.") || subdomain.includes("._udp.")) { - t.deepEqual(recordKeys, ["SRV"], `${file}: This type of subdomain can only have SRV records`); - } else { - t.deepEqual(recordKeys, ["TXT"], `${file}: Subdomains with underscores can only have TXT records`); - } - } - }); -}); diff --git a/util/reserved.json b/util/reserved.json index c87796c32..e71a89d3c 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -7,7 +7,6 @@ "account", "accounts", "admin", - "admin[1-99]", "admins", "administrator", "administrators", @@ -30,12 +29,10 @@ "chat", "checkout", "cname", - "cname[1-99]", "co", "codeofconduct", "code-of-conduct", "com", - "com[1-9]", "con", "confirm", "confirmation", @@ -54,7 +51,6 @@ "example", "feedback", "file", - "file[1-9999]", "files", "finance", "forgot", @@ -64,7 +60,6 @@ "help", "helpdesk", "helper", - "helper[1-99]", "helpers", "hostmaster", "info", @@ -75,7 +70,6 @@ "license", "login", "logout", - "lpt[1-9]", "m", "mail", "maintainer", @@ -84,7 +78,6 @@ "media", "mobile", "mod", - "mod[1-99]", "mods", "moderator", "moderators", @@ -96,10 +89,9 @@ "notifications", "notify", "ns", - "ns[1-99]", + "ns[1-9]", "nul", "oauth", - "oauth[1-99]", "official", "operations", "ops", @@ -134,7 +126,6 @@ "signout", "signup", "srv", - "srv[1-99]", "staff", "staging", "statistics", @@ -147,7 +138,6 @@ "system", "teams", "test", - "test[1-9999]", "termsofservice", "terms-of-service", "tld", @@ -155,15 +145,13 @@ "tokens", "tos", "txt", - "txt[1-9999]", "url", - "url[1-9999]", "user", "users", "webmail", "webmaster", "whois", - "www[1-99]", + "www[1-9]", "wwww", "your-domain-name" ] From 0fa85478d265daecf721f5372f14d5c7c8886ba3 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:58:11 +0800 Subject: [PATCH 13/20] Update ci.yml --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a880e816b..bc5bbb110 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,6 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - run: npm install From 7bece280c86bf90549eefcb58ba64bc2de5b771c Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:04:31 +0800 Subject: [PATCH 14/20] fix tests --- .github/workflows/ci.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc5bbb110..7a30fd883 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,8 +24,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - run: npm install @@ -43,15 +41,11 @@ jobs: - name: Get changed files if: github.event_name == 'pull_request' - uses: actions/github-script@v6 - with: - script: | - const { execSync } = require("child_process"); - const files = execSync(`git diff --name-only HEAD HEAD~${context.payload.pull_request.commits}`) - .toString() - .split("\n") - .filter(Boolean); - core.exportVariable("CHANGED_FILES", JSON.stringify(files)); + run: | + FILES=$(gh api --jq '[.files[].filename]' /repos/{owner}/{repo}/pulls/${{ github.event.number }}/files) + echo "CHANGED_FILES=$FILES" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ github.token }} - name: Run tests - run: npx ava tests/*.test.js --timeout=30s + run: npx ava tests/*.test.js --timeout=1m From 645a402f9a81250a21fce61c8d126fa5c6f73618 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:08:07 +0800 Subject: [PATCH 15/20] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a30fd883..1faaef82c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Get changed files if: github.event_name == 'pull_request' run: | - FILES=$(gh api --jq '[.files[].filename]' /repos/{owner}/{repo}/pulls/${{ github.event.number }}/files) + FILES=$(gh api --jq '[.[] | .filename]' /repos/{owner}/{repo}/pulls/${{ github.event.number }}/files) echo "CHANGED_FILES=$FILES" >> $GITHUB_ENV env: GH_TOKEN: ${{ github.token }} From cdcb6e728380f1f811ca12523763ae70ad34e036 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:15:01 +0800 Subject: [PATCH 16/20] Update reserved.json --- util/reserved.json | 62 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/util/reserved.json b/util/reserved.json index d10f6cc31..a64059d4a 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -9,14 +9,12 @@ "admin", "admins", "administrator", - "administrator[1-99]", "administrators", "alert", "alerts", "api", "apis", "auth", - "auth[1-99]", "authentication", "authorisation", "authorise", @@ -37,15 +35,11 @@ "com", "con", "confirm", - "confirm[1-99]", "confirmation", "contribute", - "contribute[1-99]", "contributing", "dash", - "dash[1-99]", "dashboard", - "dashboard[1-99]", "default", "dns", "doc", @@ -63,25 +57,29 @@ "finance", "forgot", "forgot-password", - "gtld", - "guest", "help", "helpdesk", "helper", "helpers", "hostmaster", "http", - "http[1-9999]", "https", - "https[1-9999]", "info", - "infos", "int", "internal", "legal", "license", "login", "logout", + "lpt1", + "lpt2", + "lpt3", + "lpt4", + "lpt5", + "lpt6", + "lpt7", + "lpt8", + "lpt9", "m", "mail", "maintainer", @@ -101,16 +99,25 @@ "notifications", "notify", "ns", - "ns[1-9]", - "nul", + "ns1", + "ns2", + "ns3", + "ns4", + "ns5", + "ns6", + "ns7", + "ns8", + "ns9", "oauth", "official", - "operations", "ops", "org", "organisation", "organization", "owner", + "owners", + "passwordreset", + "password-reset", "pay", "payment", "payments", @@ -123,12 +130,14 @@ "registrar", "registry", "reset", + "resetpassword", "reset-password", "root", "sales", - "schedule", "secure", "security", + "server", + "servers", "service", "services", "session", @@ -150,23 +159,38 @@ "system", "teams", "test", + "test1", + "test2", + "test3", + "test4", + "test5", + "test6", + "test7", + "test8", + "test9", "termsofservice", "terms-of-service", "tld", "token", - "token[1-9999]", "tokens", "tos", - "tos[1-99]", "txt", "url", "user", - "user[1-9999]", "users", "webmail", "webmaster", "whois", - "www[1-9]", + "ww", + "www1", + "www2", + "www3", + "www4", + "www5", + "www6", + "www7", + "www8", + "www9", "wwww", "your-domain-name" ] From 487afe059669744f374f00706b34e10e3278e017 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:30:30 +0800 Subject: [PATCH 17/20] Update reserved.json --- util/reserved.json | 74 +--------------------------------------------- 1 file changed, 1 insertion(+), 73 deletions(-) diff --git a/util/reserved.json b/util/reserved.json index c0a4c5dee..7b7236469 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -1,17 +1,7 @@ [ - "_acme-challenge.www", - "_atproto.www", - "_discord.www", - "_vercel.www", - "acc", "account", "accounts", "admin", - "admins", - "administrator", - "administrators", - "alert", - "alerts", "api", "apis", "auth", @@ -20,49 +10,29 @@ "authorise", "authorization", "authorize", - "aux", "ad", "ads", - "advertisment", - "advertisments", "billing", "blog", - "calendar", - "cart", - "catalog", "chat", "checkout", "cname", "co", - "codeofconduct", - "code-of-conduct", "com", "con", - "confirm", - "confirmation", "contribute", "contributing", "dash", "dashboard", - "default", "dns", "doc", "documentation", "email", - "emails", - "error", - "errors", - "event", - "events", "example", "feedback", "file", "files", - "finance", - "forgot", - "forgot-password", "help", - "helpdesk", "helper", "helpers", "hostmaster", @@ -74,7 +44,6 @@ "legal", "license", "login", - "logout", "lpt1", "lpt2", "lpt3", @@ -88,11 +57,8 @@ "mail", "maintainer", "maintainers", - "marketing", "media", "mobile", - "mod", - "mods", "moderator", "moderators", "net", @@ -101,7 +67,6 @@ "noc", "notification", "notifications", - "notify", "ns", "ns1", "ns2", @@ -113,15 +78,9 @@ "ns8", "ns9", "oauth", - "official", "ops", "org", - "organisation", - "organization", "owner", - "owners", - "passwordreset", - "password-reset", "pay", "payment", "payments", @@ -129,59 +88,28 @@ "postmaster", "prod", "production", - "recovery", "redirect", "registrar", "registry", - "reset", - "resetpassword", - "reset-password", "root", - "sales", "secure", "security", - "server", - "servers", "service", "services", - "session", - "sessions", - "shop", - "signin", - "signout", - "signup", - "srv", "staff", "staging", "statistics", "stats", "status", - "store", - "superuser", "support", "sys", "system", - "teams", "test", - "test1", - "test2", - "test3", - "test4", - "test5", - "test6", - "test7", - "test8", - "test9", - "termsofservice", - "terms-of-service", - "tld", + "terms", "token", "tokens", "tos", - "txt", "url", - "user", - "users", "webmail", "webmaster", "whois", From 1c83072122599571c533c5bfaba0c9496a96f7d6 Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:32:24 +0800 Subject: [PATCH 18/20] Update reserved.json --- util/reserved.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/reserved.json b/util/reserved.json index 7b7236469..6a2199f4a 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -86,6 +86,8 @@ "payments", "portal", "postmaster", + "preview", + "private", "prod", "production", "redirect", @@ -98,6 +100,7 @@ "services", "staff", "staging", + "static", "statistics", "stats", "status", From ff708437a25058a4ab2f5d08879fff85e2b657bf Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:32:55 +0800 Subject: [PATCH 19/20] Update reserved.json --- util/reserved.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/reserved.json b/util/reserved.json index 6a2199f4a..65d342324 100644 --- a/util/reserved.json +++ b/util/reserved.json @@ -4,6 +4,7 @@ "admin", "api", "apis", + "assets", "auth", "authentication", "authorisation", @@ -14,6 +15,7 @@ "ads", "billing", "blog", + "cdn", "chat", "checkout", "cname", From c5a12107f4f2e3c183e586cdd5de9581433f3b5e Mon Sep 17 00:00:00 2001 From: William Harrison <87287585+wdhdev@users.noreply.github.com> Date: Thu, 6 Mar 2025 21:35:16 +0800 Subject: [PATCH 20/20] remove expanded reserved domains --- tests/json.test.js | 45 ++------------------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/tests/json.test.js b/tests/json.test.js index c18ffe1d3..3894f9567 100644 --- a/tests/json.test.js +++ b/tests/json.test.js @@ -36,33 +36,6 @@ const reservedDomains = require("../util/reserved.json"); const domainsPath = path.resolve("domains"); const files = fs.readdirSync(domainsPath); -function expandReservedDomains(reserved) { - const expandedList = [...reserved]; - - reserved.forEach((item) => { - const rangeMatch = item.match(/\[(\d+)-(\d+)\]/); - - if (rangeMatch) { - const prefix = item.split("[")[0]; - const start = parseInt(rangeMatch[1], 10); - const end = parseInt(rangeMatch[2], 10); - - if (start < end) { - for (let i = start; i <= end; i++) { - expandedList.push(prefix + i); - } - expandedList.splice(expandedList.indexOf(item), 1); - } else { - throw new Error(`[util/reserved.json] Invalid range [${start}-${end}] in "${item}"`); - } - } - }); - - return expandedList; -} - -const expandedReservedDomains = expandReservedDomains(reservedDomains); - function findDuplicateKeys(jsonString) { const keyPattern = /"([^"]+)"(?=\s*:)/g; const keys = []; @@ -105,9 +78,9 @@ async function validateFileName(t, file) { hostnameRegex, `${file}: FQDN must be 1-253 characters, use letters, numbers, dots, or hyphens, and not start or end with a hyphen.` ); - t.false(expandedReservedDomains.includes(subdomain), `${file}: Subdomain name is reserved`); + t.false(reservedDomains.includes(subdomain), `${file}: Subdomain name is reserved`); t.true( - !expandedReservedDomains.some((reserved) => subdomain.endsWith(`.${reserved}`)), + !reservedDomains.some((reserved) => subdomain.endsWith(`.${reserved}`)), `${file}: Subdomain name is reserved` ); @@ -173,17 +146,3 @@ t("All files should have valid file names", async (t) => { t("All files should have valid required and optional fields", async (t) => { await Promise.all(files.map((file) => processFile(file, t))); }); - -t("Reserved domains file should be valid", (t) => { - const subdomainRegex = /^_?[a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*(\[\d+-\d+\])?$/; - - expandedReservedDomains.forEach((item, index) => { - t.regex( - item, - subdomainRegex, - `[util/reserved-domains.json] Invalid subdomain name "${item}" at index ${index}` - ); - }); - - t.pass(); -});