wkd-checker 2.1.0 → 2.3.0
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/dist/index.d.ts +16 -0
- package/dist/index.js +90 -8
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface KeyCheckResult {
|
|
|
5
5
|
policy_location: string;
|
|
6
6
|
policyAvailable: boolean;
|
|
7
7
|
policyCorsValid: boolean;
|
|
8
|
+
policy: PolicyFlags | null;
|
|
8
9
|
key_location: string | null;
|
|
9
10
|
key_available: boolean;
|
|
10
11
|
keyCorsValid: boolean;
|
|
@@ -14,6 +15,7 @@ export interface KeyCheckResult {
|
|
|
14
15
|
emailInKey: boolean;
|
|
15
16
|
expired: boolean;
|
|
16
17
|
revoked: boolean;
|
|
18
|
+
policyCompliant: boolean;
|
|
17
19
|
valid: boolean;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
@@ -24,6 +26,20 @@ export declare enum KeyType {
|
|
|
24
26
|
BinaryKey = "BinaryKey",
|
|
25
27
|
ArmoredKey = "ArmoredKey"
|
|
26
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Result of the policy file check.
|
|
31
|
+
*/
|
|
32
|
+
export interface PolicyFlags {
|
|
33
|
+
mailboxOnly: boolean;
|
|
34
|
+
daneOnly: boolean;
|
|
35
|
+
authSubmit: boolean;
|
|
36
|
+
protocolVersion: number | null;
|
|
37
|
+
submissionAddress: string | null;
|
|
38
|
+
others: Array<{
|
|
39
|
+
key: string;
|
|
40
|
+
value: string | null;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
27
43
|
export declare class WKDError extends Error {
|
|
28
44
|
constructor(message: string);
|
|
29
45
|
}
|
package/dist/index.js
CHANGED
|
@@ -80,6 +80,64 @@ const detectKeyType = (keyData) => __awaiter(void 0, void 0, void 0, function* (
|
|
|
80
80
|
return { keyType: KeyType.Invalid, key: null };
|
|
81
81
|
}
|
|
82
82
|
});
|
|
83
|
+
/**
|
|
84
|
+
* Parses the Policy Flags file.
|
|
85
|
+
* Section 4.5. Policy Flags
|
|
86
|
+
* @param {string} text - Content of the policy file.
|
|
87
|
+
* @returns {PolicyFlags} Parsed policy flags.
|
|
88
|
+
*/
|
|
89
|
+
const parsePolicy = (text) => {
|
|
90
|
+
const flags = {
|
|
91
|
+
mailboxOnly: false,
|
|
92
|
+
daneOnly: false,
|
|
93
|
+
authSubmit: false,
|
|
94
|
+
protocolVersion: null,
|
|
95
|
+
submissionAddress: null,
|
|
96
|
+
others: []
|
|
97
|
+
};
|
|
98
|
+
if (!text)
|
|
99
|
+
return flags;
|
|
100
|
+
const lines = text.split(/\r?\n/);
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
const trimmed = line.trim();
|
|
103
|
+
// "Empty lines and lines starting with a '#' character are considered comment lines."
|
|
104
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
105
|
+
continue;
|
|
106
|
+
const colonIndex = trimmed.indexOf(':');
|
|
107
|
+
let key = '';
|
|
108
|
+
let value = null;
|
|
109
|
+
if (colonIndex !== -1) {
|
|
110
|
+
key = trimmed.substring(0, colonIndex).trim().toLowerCase();
|
|
111
|
+
value = trimmed.substring(colonIndex + 1).trim();
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
key = trimmed.trim().toLowerCase();
|
|
115
|
+
}
|
|
116
|
+
switch (key) {
|
|
117
|
+
case 'mailbox-only':
|
|
118
|
+
flags.mailboxOnly = true;
|
|
119
|
+
break;
|
|
120
|
+
case 'dane-only':
|
|
121
|
+
flags.daneOnly = true;
|
|
122
|
+
break;
|
|
123
|
+
case 'auth-submit':
|
|
124
|
+
flags.authSubmit = true;
|
|
125
|
+
break;
|
|
126
|
+
case 'protocol-version':
|
|
127
|
+
if (value)
|
|
128
|
+
flags.protocolVersion = parseInt(value, 10);
|
|
129
|
+
break;
|
|
130
|
+
case 'submission-address':
|
|
131
|
+
if (value)
|
|
132
|
+
flags.submissionAddress = value;
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
// "For experimental use of new features or for provider specific settings, keywords MUST be prefixed with a domain name and an underscore."
|
|
136
|
+
flags.others.push({ key, value });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return flags;
|
|
140
|
+
};
|
|
83
141
|
/**
|
|
84
142
|
* Checks for a key at a specific WKD URL.
|
|
85
143
|
* @param {string} url - The WKD URL for the key.
|
|
@@ -89,15 +147,26 @@ const detectKeyType = (keyData) => __awaiter(void 0, void 0, void 0, function* (
|
|
|
89
147
|
* @returns {Promise<KeyCheckResult>} The result of the key check.
|
|
90
148
|
*/
|
|
91
149
|
const checkKeyUrl = (url, policy, options, email) => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
|
-
var _a, _b;
|
|
150
|
+
var _a, _b, _c;
|
|
93
151
|
const [policyData, keyData] = yield Promise.all([
|
|
94
152
|
fetchWithCorsCheck(policy, options),
|
|
95
153
|
fetchWithCorsCheck(url, options)
|
|
96
154
|
]);
|
|
155
|
+
let policyFlags = null;
|
|
156
|
+
if (policyData.response) {
|
|
157
|
+
try {
|
|
158
|
+
const policyText = yield policyData.response.text();
|
|
159
|
+
policyFlags = parsePolicy(policyText);
|
|
160
|
+
}
|
|
161
|
+
catch (_d) {
|
|
162
|
+
policyFlags = parsePolicy("");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
97
165
|
const result = {
|
|
98
166
|
policy_location: ((_a = policyData.response) === null || _a === void 0 ? void 0 : _a.url) || policy,
|
|
99
167
|
policyAvailable: !!policyData.response,
|
|
100
168
|
policyCorsValid: policyData.corsValid,
|
|
169
|
+
policy: policyFlags,
|
|
101
170
|
key_location: ((_b = keyData.response) === null || _b === void 0 ? void 0 : _b.url) || url,
|
|
102
171
|
key_available: !!keyData.response,
|
|
103
172
|
keyCorsValid: keyData.corsValid,
|
|
@@ -107,6 +176,7 @@ const checkKeyUrl = (url, policy, options, email) => __awaiter(void 0, void 0, v
|
|
|
107
176
|
emailInKey: false,
|
|
108
177
|
expired: false,
|
|
109
178
|
revoked: false,
|
|
179
|
+
policyCompliant: true,
|
|
110
180
|
valid: false,
|
|
111
181
|
};
|
|
112
182
|
if (keyData.response) {
|
|
@@ -118,15 +188,26 @@ const checkKeyUrl = (url, policy, options, email) => __awaiter(void 0, void 0, v
|
|
|
118
188
|
const identities = yield key.getUserIDs();
|
|
119
189
|
// Spec Section 5: "The mail provider MUST make sure to publish a key in a way that only the mail address belonging to the requested user is part of the User ID packets included in the returned key."
|
|
120
190
|
// "Other User ID packets and their associated binding signatures MUST be removed before publication."
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// robustly extract email from "Name <email>" format
|
|
191
|
+
result.emailInKey = identities.length > 0;
|
|
192
|
+
for (const id of identities) {
|
|
124
193
|
const match = id.match(/<(.+)>/);
|
|
125
194
|
const emailInId = match ? match[1] : id;
|
|
126
|
-
|
|
195
|
+
if (emailInId.trim() !== email) {
|
|
196
|
+
result.emailInKey = false;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
127
199
|
}
|
|
128
|
-
|
|
129
|
-
|
|
200
|
+
// Check policy compliance
|
|
201
|
+
if (((_c = result.policy) === null || _c === void 0 ? void 0 : _c.mailboxOnly) && identities.length > 0) {
|
|
202
|
+
for (const id of identities) {
|
|
203
|
+
// If mailbox-only, the ID MUST
|
|
204
|
+
const match = id.match(/<(.+)>/);
|
|
205
|
+
const emailInId = match ? match[1] : id;
|
|
206
|
+
const expected = [`<${emailInId.trim()}>`, emailInId.trim()];
|
|
207
|
+
if (!expected.includes(id.trim())) {
|
|
208
|
+
result.policyCompliant = false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
130
211
|
}
|
|
131
212
|
result.fingerprint = key.getFingerprint().toUpperCase();
|
|
132
213
|
const expirationTime = yield key.getExpirationTime();
|
|
@@ -139,7 +220,8 @@ const checkKeyUrl = (url, policy, options, email) => __awaiter(void 0, void 0, v
|
|
|
139
220
|
result.valid = result.policyAvailable &&
|
|
140
221
|
result.key_available &&
|
|
141
222
|
result.keyTypeValid &&
|
|
142
|
-
result.emailInKey
|
|
223
|
+
result.emailInKey &&
|
|
224
|
+
result.policyCompliant;
|
|
143
225
|
return result;
|
|
144
226
|
});
|
|
145
227
|
/**
|