@relaycorp/dnssec
Advanced tools
Comparing version 1.6.5 to 1.7.0
@@ -15,5 +15,6 @@ export type { IanaRrTypeName, IanaRrTypeIdOrName } from './lib/dns/ianaRrTypes.js'; | ||
export { dnssecLookUp } from './lib/lookup.js'; | ||
export type { ChainVerificationResult, FailureResult, VerifiedRrSet } from './lib/results.js'; | ||
export type { ChainVerificationResult, FailureResult, FailureStatus, VerifiedRrSet, } from './lib/results.js'; | ||
export { SecurityStatus } from './lib/SecurityStatus.js'; | ||
export type { TrustAnchor } from './lib/TrustAnchor.js'; | ||
export type { VerificationOptions } from './lib/VerificationOptions.js'; | ||
export { MockChain } from './lib/testing/MockChain.js'; |
@@ -13,2 +13,3 @@ /* eslint-disable import/no-unused-modules */ | ||
export { SecurityStatus } from './lib/SecurityStatus.js'; | ||
export { MockChain } from './lib/testing/MockChain.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -15,4 +15,7 @@ /* eslint-disable import/exports-last */ | ||
const DNS_CLASS_IDS = { | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
IN: DnsClass.IN, | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
CH: DnsClass.CH, | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
HS: DnsClass.HS, | ||
@@ -19,0 +22,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
/* eslint-disable import/exports-last */ | ||
/* eslint-disable import/exports-last,@typescript-eslint/naming-convention */ | ||
/** | ||
@@ -3,0 +3,0 @@ * DNS RCODEs |
@@ -0,1 +1,2 @@ | ||
/* eslint-disable @typescript-eslint/naming-convention */ | ||
/** | ||
@@ -2,0 +3,0 @@ * IANA Resource Record (RR) Types. |
@@ -10,4 +10,5 @@ import type { SecurityStatus } from './SecurityStatus.js'; | ||
} | ||
export type FailureStatus = SecurityStatus.BOGUS | SecurityStatus.INDETERMINATE | SecurityStatus.INSECURE; | ||
export interface FailureResult extends BaseResult { | ||
readonly status: SecurityStatus.BOGUS | SecurityStatus.INDETERMINATE | SecurityStatus.INSECURE; | ||
readonly status: FailureStatus; | ||
readonly reasonChain: readonly string[]; | ||
@@ -14,0 +15,0 @@ } |
@@ -7,6 +7,6 @@ /** | ||
export declare enum SecurityStatus { | ||
SECURE = 0, | ||
INSECURE = 1, | ||
BOGUS = 2, | ||
INDETERMINATE = 3 | ||
SECURE = "SECURE", | ||
INSECURE = "INSECURE", | ||
BOGUS = "BOGUS", | ||
INDETERMINATE = "INDETERMINATE" | ||
} |
@@ -1,2 +0,1 @@ | ||
/* eslint-disable @typescript-eslint/prefer-enum-initializers */ | ||
/** | ||
@@ -9,7 +8,7 @@ * DNSSEC security status. | ||
(function (SecurityStatus) { | ||
SecurityStatus[SecurityStatus["SECURE"] = 0] = "SECURE"; | ||
SecurityStatus[SecurityStatus["INSECURE"] = 1] = "INSECURE"; | ||
SecurityStatus[SecurityStatus["BOGUS"] = 2] = "BOGUS"; | ||
SecurityStatus[SecurityStatus["INDETERMINATE"] = 3] = "INDETERMINATE"; | ||
SecurityStatus["SECURE"] = "SECURE"; | ||
SecurityStatus["INSECURE"] = "INSECURE"; | ||
SecurityStatus["BOGUS"] = "BOGUS"; | ||
SecurityStatus["INDETERMINATE"] = "INDETERMINATE"; | ||
})(SecurityStatus || (SecurityStatus = {})); | ||
//# sourceMappingURL=SecurityStatus.js.map |
@@ -11,3 +11,3 @@ import { Question } from './dns/Question.js'; | ||
interface MessageByKey { | ||
readonly [key: string]: Message; | ||
readonly [key: string]: Message | undefined; | ||
} | ||
@@ -14,0 +14,0 @@ export declare class UnverifiedChain { |
@@ -8,10 +8,3 @@ import { Question } from './dns/Question.js'; | ||
import { SignedRrSet } from './SignedRrSet.js'; | ||
function getZonesInChain(zoneName, shouldIncludeRoot = true) { | ||
if (zoneName === '') { | ||
return shouldIncludeRoot ? ['.'] : []; | ||
} | ||
const parentZoneName = zoneName.replace(/^[^.]+\./u, ''); | ||
const parentZones = getZonesInChain(parentZoneName, shouldIncludeRoot); | ||
return [...parentZones, zoneName]; | ||
} | ||
import { getZonesInChain } from './utils/dns.js'; | ||
async function retrieveZoneMessages(zoneNames, recordType, classType, resolver) { | ||
@@ -45,6 +38,7 @@ const question = new Question('.', recordType, classType); | ||
}, {}); | ||
if (!(query.key in allMessages)) { | ||
const queryResponse = allMessages[query.key]; | ||
if (!queryResponse) { | ||
throw new Error(`At least one message must answer ${query.key}`); | ||
} | ||
return new UnverifiedChain(query, allMessages[query.key], messageByKey); | ||
return new UnverifiedChain(query, queryResponse, messageByKey); | ||
} | ||
@@ -54,3 +48,3 @@ static async retrieve(question, resolver) { | ||
const message = await resolver(currentQuestion); | ||
return message instanceof Message ? message : Message.deserialise(message); | ||
return message instanceof Buffer ? Message.deserialise(message) : message; | ||
}; | ||
@@ -85,3 +79,4 @@ const zoneNames = getZonesInChain(question.name); | ||
const rootDnskeyKey = `./${DnssecRecordType.DNSKEY}`; | ||
if (!(rootDnskeyKey in this.zoneMessageByKey)) { | ||
const rootDnskeyResponse = this.zoneMessageByKey[rootDnskeyKey]; | ||
if (!rootDnskeyResponse) { | ||
return { | ||
@@ -92,3 +87,3 @@ status: SecurityStatus.INDETERMINATE, | ||
} | ||
const result = Zone.initRoot(this.zoneMessageByKey[rootDnskeyKey], trustAnchors, datePeriod); | ||
const result = Zone.initRoot(rootDnskeyResponse, trustAnchors, datePeriod); | ||
if (result.status !== SecurityStatus.SECURE) { | ||
@@ -103,3 +98,4 @@ return augmentFailureResult(result, 'Got invalid DNSKEY for root zone'); | ||
const dnskeyKey = `${zoneName}/${DnssecRecordType.DNSKEY}`; | ||
if (!(dnskeyKey in this.zoneMessageByKey)) { | ||
const dnskeyResponse = this.zoneMessageByKey[dnskeyKey]; | ||
if (!dnskeyResponse) { | ||
return { | ||
@@ -111,3 +107,4 @@ status: SecurityStatus.INDETERMINATE, | ||
const dsKey = `${zoneName}/${DnssecRecordType.DS}`; | ||
if (!(dsKey in this.zoneMessageByKey)) { | ||
const dsResponse = this.zoneMessageByKey[dsKey]; | ||
if (dsResponse === undefined) { | ||
return { | ||
@@ -119,3 +116,3 @@ status: SecurityStatus.INDETERMINATE, | ||
const parent = zones[zones.length - 1]; | ||
const zoneResult = parent.initChild(zoneName, this.zoneMessageByKey[dnskeyKey], this.zoneMessageByKey[dsKey], datePeriod); | ||
const zoneResult = parent.initChild(zoneName, dnskeyResponse, dsResponse, datePeriod); | ||
if (zoneResult.status !== SecurityStatus.SECURE) { | ||
@@ -122,0 +119,0 @@ return augmentFailureResult(zoneResult, `Failed to verify zone ${zoneName}`); |
/// <reference types="node" /> | ||
export declare function lengthPrefixRdata(rdata: Buffer): Buffer; | ||
export declare function getZonesInChain(zoneName: string, shouldIncludeRoot?: boolean): readonly string[]; |
@@ -7,2 +7,10 @@ export function lengthPrefixRdata(rdata) { | ||
} | ||
export function getZonesInChain(zoneName, shouldIncludeRoot = true) { | ||
if (zoneName === '') { | ||
return shouldIncludeRoot ? ['.'] : []; | ||
} | ||
const parentZoneName = zoneName.replace(/^[^.]+\./u, ''); | ||
const parentZones = getZonesInChain(parentZoneName, shouldIncludeRoot); | ||
return [...parentZones, zoneName]; | ||
} | ||
//# sourceMappingURL=dns.js.map |
{ | ||
"name": "@relaycorp/dnssec", | ||
"version": "1.6.5", | ||
"version": "1.7.0", | ||
"author": { | ||
@@ -41,22 +41,19 @@ "email": "no-reply@relaycorp.tech", | ||
"devDependencies": { | ||
"@jest/globals": "^28.1.3", | ||
"@relaycorp/eslint-config": "^1.1.19", | ||
"@relaycorp/shared-config": "^1.9.2", | ||
"@types/jest": "^27.5.0", | ||
"@typescript-eslint/parser": "^5.44.0", | ||
"@jest/globals": "^29.3.1", | ||
"@relaycorp/eslint-config": "^1.1.52", | ||
"@relaycorp/shared-config": "^1.9.3", | ||
"@types/jest": "^29.2.4", | ||
"@typescript-eslint/parser": "^5.47.0", | ||
"del-cli": "^5.0.0", | ||
"dohdec": "https://gitpkg.now.sh/hildjj/dohdec/pkg/dohdec?acd49694a83825a461bdff55e4a4a63ca7a4bbef", | ||
"eslint": "^8.28.0", | ||
"eslint-plugin-json": "^3.1.0", | ||
"eslint-plugin-putout": "^16.3.0", | ||
"jest": "^28.1.3", | ||
"eslint": "^8.30.0", | ||
"jest": "^29.3.1", | ||
"jest-date-mock": "^1.0.8", | ||
"jest-extended": "^3.1.0", | ||
"jest-extended": "^3.2.0", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^2.7.1", | ||
"putout": "^28.0.1", | ||
"ts-jest": "^28.0.8", | ||
"prettier": "^2.8.1", | ||
"ts-jest": "^29.0.3", | ||
"ts-node": "^10.9.1", | ||
"typedoc": "^0.23.21", | ||
"typescript": "^4.8.4" | ||
"typedoc": "^0.23.23", | ||
"typescript": "^4.9.4" | ||
}, | ||
@@ -63,0 +60,0 @@ "dependencies": { |
# `@relaycorp/dnssec` | ||
[![npm version](https://badge.fury.io/js/@relaycorp%2Fdnssec.svg)](https://badge.fury.io/js/@relaycorp%2Fdnssec) | ||
[![npm version](https://badge.fury.io/js/@relaycorp%2Fdnssec.svg)](https://www.npmjs.com/package/@relaycorp/dnssec) | ||
This is a resolver-agnostic DNSSEC verification library for Node.js. You're free to use any transport you want: UDP, DNS-over-TLS (DoT), DNS-over-HTTP (DoH), etc. | ||
This is a resolver-agnostic DNSSEC verification library for Node.js that allows you to use any transport you want: UDP, DNS-over-TLS (DoT), DNS-over-HTTPS (DoH), etc. | ||
@@ -74,2 +74,9 @@ The latest version can be installed from NPM: | ||
### Successful RRset | ||
When DNSSEC validation succeeds, you get a `VerifiedRrSet` object with the following properties: | ||
- `status`: Set to `SecurityStatus.SECURE`. | ||
- `result`: An RRset containing one or more records (`DnsRecord` instances). Each record exposes its data in both serialised and deserialised forms in the `dataSerialised` and `dataFields` properties, respectively. `dataFields` is an object whose structure is determined by [`dns-packet`](https://www.npmjs.com/package/dns-packet). | ||
### Error handling | ||
@@ -124,4 +131,45 @@ | ||
### API documentation | ||
## Testing | ||
To facilitate the simulation of the various outcomes of DNSSEC validation, we provide the `MockChain` utility so that you can pass a custom resolver and trust anchor to `dnssecLookUp()`. This is particularly useful in unit tests where you aren't able to mock this module (e.g., Jest doesn't support mocking our ESM as of this writing). | ||
The following example shows how to generate a verified RRset: | ||
```javascript | ||
import { | ||
dnssecLookUp, | ||
DnsRecord, | ||
MockChain, | ||
RrSet, | ||
SecurityStatus, | ||
} from '@relaycorp/dnssec'; | ||
const RECORD = new DnsRecord( | ||
`example.com.`, | ||
'TXT', | ||
DnsClass.IN, | ||
42, | ||
'The data', | ||
); | ||
const QUESTION = RECORD.makeQuestion(); | ||
const RRSET = RrSet.init(QUESTION, [RECORD]); | ||
test('Generating a SECURE result', async () => { | ||
const mockChain = await MockChain.generate(RECORD.name); | ||
const { resolver, trustAnchors } = mockChain.generateFixture( | ||
RRSET, | ||
SecurityStatus.SECURE, | ||
); | ||
const result = await dnssecLookUp(QUESTION, resolver, { trustAnchors }); | ||
expect(result).toStrictEqual({ | ||
status: SecurityStatus.SECURE, | ||
result: RRSET, | ||
}); | ||
}); | ||
``` | ||
## API documentation | ||
The API documentation is available on [docs.relaycorp.tech](https://docs.relaycorp.tech/dnssec-js/). | ||
@@ -128,0 +176,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
164046
17
166
2267
203