caa 11.0.0 → 11.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # caa
2
2
  [![](https://img.shields.io/npm/v/caa.svg?style=flat)](https://www.npmjs.org/package/caa) [![](https://img.shields.io/npm/dm/caa.svg)](https://www.npmjs.org/package/caa) [![](https://packagephobia.com/badge?p=caa)](https://packagephobia.com/result?p=caa)
3
3
 
4
- > [rfc6844](https://tools.ietf.org/html/rfc6844)-conform CAA record lookup and validation
4
+ > [rfc8659](https://www.rfc-editor.org/rfc/rfc8659)-conform CAA record lookup and validation
5
5
 
6
6
  ## Usage
7
7
 
package/dist/index.d.ts CHANGED
@@ -7,13 +7,12 @@ type CaaOpts = {
7
7
  servers?: Array<string>;
8
8
  dnsSocket?: any;
9
9
  };
10
- type CaaRecord = {
10
+ export type CaaRecord = {
11
11
  flags: number;
12
12
  tag: string;
13
13
  value: string;
14
14
  issuerCritical: boolean;
15
15
  };
16
- declare function caa(name: string, opts?: CaaOpts): Promise<Array<CaaRecord>>;
17
- declare function caaMatches(name: string, ca: string, opts?: CaaOpts): Promise<boolean>;
18
- //#endregion
19
- export { CaaRecord, caa, caaMatches };
16
+ export declare function caa(name: string, opts?: CaaOpts): Promise<Array<CaaRecord>>;
17
+ export declare function caaMatches(name: string, ca: string, opts?: CaaOpts): Promise<boolean>;
18
+ //#endregion
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { getServers } from "node:dns";
2
2
  import { promisify } from "node:util";
3
+ import { domainToASCII } from "node:url";
3
4
  import dnsSocket from "dns-socket";
4
5
 
5
6
  //#region node_modules/.pnpm/tlds@1.261.0/node_modules/tlds/index.json
@@ -1451,130 +1452,78 @@ const defaults = {
1451
1452
  recursions: 50,
1452
1453
  retries: 12,
1453
1454
  port: 53,
1454
- servers: ["8.8.8.8", "8.8.4.4"],
1455
- dnsSocket: void 0
1455
+ servers: ["8.8.8.8", "8.8.4.4"]
1456
1456
  };
1457
1457
  const tldSet = new Set(tlds_default);
1458
+ const knownTags = /* @__PURE__ */ new Set([
1459
+ "issue",
1460
+ "issuewild",
1461
+ "iodef",
1462
+ "contactemail",
1463
+ "contactphone",
1464
+ "accounturi",
1465
+ "validationmethods"
1466
+ ]);
1467
+ const okRcodes = /* @__PURE__ */ new Set(["NXDOMAIN", "NOERROR"]);
1458
1468
  const isTLD = (name) => tldSet.has(name);
1459
- const isWildcard = (name) => name.includes("*");
1460
- const parent = (name) => name.split(".").splice(1).join(".");
1461
- function selectServer(servers, retries, tries) {
1462
- return servers[(tries - retries) % servers.length];
1463
- }
1469
+ const isWildcard = (name) => name.startsWith("*.");
1470
+ const parent = (name) => name.split(".").slice(1).join(".");
1464
1471
  function normalizeName(name = "") {
1465
- name = name.toLowerCase();
1466
- return name.endsWith(".") && name.length > 1 ? name.substring(0, name.length - 1) : name;
1472
+ const wildcard = isWildcard(name);
1473
+ const base = wildcard ? name.slice(2) : name;
1474
+ let ascii = domainToASCII(base) || base.toLowerCase();
1475
+ if (ascii.endsWith(".") && ascii.length > 1) ascii = ascii.slice(0, -1);
1476
+ return wildcard ? `*.${ascii}` : ascii;
1467
1477
  }
1468
- const resolve = async ({ name, query, servers, port, recursions, retries, tries, ignoreTLDs }) => {
1469
- name = normalizeName(name);
1470
- if (!name) return [];
1471
- if (ignoreTLDs && isTLD(name)) return [];
1472
- if (recursions <= 0 || retries <= 0) return [];
1473
- name = name.replace(/^\*\./, "");
1474
- let res;
1475
- try {
1476
- res = await query({ questions: [{
1477
- name,
1478
- type: "CAA"
1479
- }] }, port, selectServer(servers, retries, tries));
1480
- } catch {
1481
- if (retries <= 0) return [];
1482
- retries -= 1;
1483
- return await resolve({
1484
- name,
1485
- query,
1486
- servers,
1487
- port,
1488
- recursions,
1489
- retries,
1490
- tries,
1491
- ignoreTLDs
1492
- });
1493
- }
1494
- if (!res || !res.answers && !["NXDOMAIN", "NOERROR"].includes(res.rcode)) {
1495
- if (retries <= 0) return [];
1496
- retries -= 1;
1497
- return await resolve({
1498
- name,
1499
- query,
1500
- servers,
1501
- port,
1502
- recursions,
1503
- retries,
1504
- tries,
1505
- ignoreTLDs
1506
- });
1507
- }
1508
- const records = {};
1509
- if (res?.answers?.length) for (const { name, type, data } of res.answers || {}) {
1510
- if (!name || !type || !data) continue;
1511
- if (!records[type]) records[type] = [];
1512
- records[type].push({
1513
- name,
1514
- data
1515
- });
1516
- }
1517
- if (records.CAA?.length) {
1518
- const caas = records.CAA.filter((record) => record.name === name).map((record) => record.data);
1519
- if (caas.length) return caas;
1520
- }
1521
- let alias;
1522
- if (records.CNAME?.length) {
1523
- const dest = records.CNAME.find((record) => record.name === name);
1524
- if (dest) alias = dest.data;
1525
- } else if (records.DNAME?.length) {
1526
- const dest = records.DNAME.find((record) => record.name === name);
1527
- if (dest) alias = name.replace(dest.name, dest.data);
1528
- }
1529
- if (alias && records.CAA?.length) return records.CAA.filter((record) => record.name === alias && record.data).map((record) => record.data);
1530
- if (!isTLD(name)) {
1531
- recursions -= 1;
1532
- return await resolve({
1533
- name: parent(name),
1534
- query,
1535
- servers,
1536
- port,
1537
- recursions,
1538
- retries,
1539
- tries,
1540
- ignoreTLDs
1541
- });
1542
- } else return [];
1543
- };
1544
1478
  async function caa(name, opts = {}) {
1545
- name = normalizeName(name);
1546
- if (!opts.servers) {
1547
- const systemServers = getServers();
1548
- opts.servers = systemServers?.length ? systemServers : defaults.servers;
1479
+ const ignoreTLDs = opts.ignoreTLDs ?? defaults.ignoreTLDs;
1480
+ const recursions = opts.recursions ?? defaults.recursions;
1481
+ const retries = opts.retries ?? defaults.retries;
1482
+ const port = opts.port ?? defaults.port;
1483
+ let servers;
1484
+ if (opts.servers?.length) servers = opts.servers;
1485
+ else {
1486
+ const sys = getServers();
1487
+ servers = sys.length ? sys : defaults.servers;
1549
1488
  }
1550
- opts = {
1551
- ...defaults,
1552
- ...opts
1553
- };
1554
1489
  const socket = opts.dnsSocket || dnsSocket();
1555
1490
  const query = promisify(socket.query.bind(socket));
1556
- const caa = await resolve({
1557
- name,
1558
- query,
1559
- servers: opts.servers,
1560
- port: opts.port,
1561
- recursions: opts.recursions,
1562
- retries: opts.retries,
1563
- tries: opts.retries,
1564
- ignoreTLDs: opts.ignoreTLDs
1565
- });
1491
+ const climb = async (name, recursionsLeft, retriesLeft) => {
1492
+ if (!name) return [];
1493
+ if (ignoreTLDs && isTLD(name)) return [];
1494
+ if (recursionsLeft <= 0 || retriesLeft <= 0) return [];
1495
+ const server = servers[(retries - retriesLeft) % servers.length];
1496
+ let res;
1497
+ try {
1498
+ res = await query({ questions: [{
1499
+ name,
1500
+ type: "CAA"
1501
+ }] }, port, server);
1502
+ } catch {
1503
+ res = null;
1504
+ }
1505
+ if (!res || !res.answers && !okRcodes.has(res.rcode)) return climb(name, recursionsLeft, retriesLeft - 1);
1506
+ const caas = [];
1507
+ for (const ans of res.answers || []) if (ans?.type === "CAA" && ans.data) caas.push(ans.data);
1508
+ if (caas.length) return caas;
1509
+ if (isTLD(name)) return [];
1510
+ return climb(parent(name), recursionsLeft - 1, retriesLeft);
1511
+ };
1512
+ const result = await climb(normalizeName(name).replace(/^\*\./, ""), recursions, retries);
1566
1513
  if (!opts.dnsSocket) socket.destroy();
1567
- return caa || [];
1514
+ return result;
1568
1515
  }
1569
1516
  async function caaMatches(name, ca, opts = {}) {
1570
- name = normalizeName(name);
1517
+ const wildcard = isWildcard(name);
1571
1518
  ca = normalizeName(ca);
1572
- const caas = await caa(name, opts);
1573
- if (!caas.length) return true;
1574
- const issueNames = caas.filter((caa) => caa?.tag === "issue").map((name) => normalizeName(name.value.split(";")[0].trim()));
1575
- const issueWildNames = caas.filter((caa) => caa?.tag === "issuewild").map((name) => normalizeName(name.value.split(";")[0].trim()));
1576
- const names = isWildcard(name) ? issueWildNames.length ? issueWildNames : issueNames : issueNames;
1577
- if (names.includes(";")) return false;
1519
+ const records = await caa(name, opts);
1520
+ if (!records.length) return true;
1521
+ if (records.some((r) => r.issuerCritical && !knownTags.has(r.tag))) return false;
1522
+ const issueNames = [];
1523
+ const issueWildNames = [];
1524
+ for (const r of records) if (r.tag === "issue") issueNames.push(normalizeName(r.value.split(";")[0].trim()));
1525
+ else if (r.tag === "issuewild") issueWildNames.push(normalizeName(r.value.split(";")[0].trim()));
1526
+ const names = wildcard && issueWildNames.length ? issueWildNames : issueNames;
1578
1527
  return !names.length || names.includes(ca);
1579
1528
  }
1580
1529
 
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "caa",
3
- "version": "11.0.0",
4
- "description": "rfc6844-conform CAA record lookup and validation",
3
+ "version": "11.0.2",
4
+ "description": "rfc8659-conform CAA record lookup and validation",
5
5
  "author": "silverwind <me@silverwind.io>",
6
- "repository": "silverwind/caa",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/silverwind/caa.git"
9
+ },
7
10
  "license": "BSD-2-Clause",
8
11
  "type": "module",
9
12
  "sideEffects": false,
@@ -20,20 +23,20 @@
20
23
  "dns-socket": "^4.2.2"
21
24
  },
22
25
  "devDependencies": {
23
- "@types/node": "25.6.0",
24
- "@typescript/native-preview": "7.0.0-dev.20260413.1",
25
- "eslint": "9.39.4",
26
- "eslint-config-silverwind": "131.0.4",
26
+ "@types/node": "26.6.2",
27
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
28
+ "eslint": "10.11.0",
29
+ "eslint-config-silverwind": "151.0.0",
27
30
  "jest-extended": "7.0.0",
28
31
  "tlds": "1.261.0",
29
- "tsdown": "0.21.7",
30
- "tsdown-config-silverwind": "3.0.0",
31
- "typescript": "6.0.2",
32
- "typescript-config-silverwind": "17.0.0",
33
- "updates": "17.15.2",
34
- "updates-config-silverwind": "3.0.0",
35
- "versions": "15.0.0",
36
- "vitest": "4.1.4",
37
- "vitest-config-silverwind": "11.3.3"
32
+ "tsdown": "0.23.0",
33
+ "tsdown-config-silverwind": "4.1.1",
34
+ "typescript": "6.0.3",
35
+ "typescript-config-silverwind": "21.0.0",
36
+ "updates": "19.0.0",
37
+ "updates-config-silverwind": "4.0.6",
38
+ "versions": "15.5.0",
39
+ "vitest": "5.0.2",
40
+ "vitest-config-silverwind": "12.0.4"
38
41
  }
39
42
  }