mailauth 2.2.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/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2020-2021 Andris Reinman
1
+ Copyright (c) 2020-2022 Postal Systems OÜ
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![](https://github.com/andris9/mailauth/raw/master/assets/mailauth.png)
1
+ ![](https://github.com/postalsys/mailauth/raw/master/assets/mailauth.png)
2
2
 
3
3
  [Command line utility](cli.md) and a Node.js library for email authentication.
4
4
 
@@ -442,6 +442,6 @@ const { authenticate } = require('mailauth');
442
442
 
443
443
  ## License
444
444
 
445
- © 2020 Andris Reinman
445
+ © 2020-2022 Postal Systems OÜ
446
446
 
447
447
  Licensed under MIT license
package/bin/mailauth.js CHANGED
@@ -10,6 +10,8 @@ const commandReport = require('../lib/commands/report');
10
10
  const commandSign = require('../lib/commands/sign');
11
11
  const commandSeal = require('../lib/commands/seal');
12
12
  const commandSpf = require('../lib/commands/spf');
13
+ const fs = require('fs');
14
+ const pathlib = require('path');
13
15
 
14
16
  const argv = yargs(hideBin(process.argv))
15
17
  .command(
@@ -285,6 +287,41 @@ const argv = yargs(hideBin(process.argv))
285
287
  });
286
288
  }
287
289
  )
290
+ .command(
291
+ ['license'],
292
+ 'Show license information',
293
+ () => false,
294
+ () => {
295
+ fs.readFile(pathlib.join(__dirname, '..', 'LICENSE.txt'), (err, license) => {
296
+ if (err) {
297
+ console.error('Failed to load license information');
298
+ console.error(err);
299
+ return process.exit(1);
300
+ }
301
+
302
+ console.error('Mailauth License');
303
+ console.error('================');
304
+
305
+ console.error(license.toString().trim());
306
+
307
+ console.error('');
308
+
309
+ fs.readFile(pathlib.join(__dirname, '..', 'licenses.txt'), (err, data) => {
310
+ if (err) {
311
+ console.error('Failed to load license information');
312
+ console.error(err);
313
+ return process.exit(1);
314
+ }
315
+
316
+ console.error('Included Modules');
317
+ console.error('================');
318
+
319
+ console.error(data.toString().trim());
320
+ process.exit();
321
+ });
322
+ });
323
+ }
324
+ )
288
325
  .option('verbose', {
289
326
  alias: 'v',
290
327
  type: 'boolean',
package/cli.md CHANGED
@@ -2,29 +2,26 @@
2
2
 
3
3
  ## TOC
4
4
 
5
- - [Requirements](#requirements)
6
5
  - [Installation](#installation)
7
6
  - [Help](#help)
8
7
  - [Available commands](#available-commands)
9
8
  - [report](#report) – to validate SPF, DKIM, DMARC, ARC
10
9
  - [sign](#sign) - to sign an email with DKIM
11
10
  - [seal](#seal) - to seal an email with ARC
12
- - [spt](#spf) - to validate SPF for an IP address and email address
11
+ - [spf](#spf) - to validate SPF for an IP address and an email address
12
+ - [license](#license) - display licenses for `mailauth` and included modules
13
13
  - [DNS cache file](#dns-cache-file)
14
14
 
15
- ## Requirements
16
-
17
- - **Node.js v14+** – using an older version of Node is not supported
18
-
19
15
  ## Installation
20
16
 
21
- Install `mailauth` globally to get the command line interface
17
+ Download `mailauth` for your platform:
22
18
 
23
- ```
24
- npm install -g mailauth
25
- ```
19
+ - [MacOS](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.pkg)
20
+ - [Linux](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.tar.gz)
21
+ - [Windows](https://github.com/postalsys/mailauth/releases/latest/download/mailauth.exe)
22
+ - Or install from the NPM registry: `npm install -g mailauth`
26
23
 
27
- > Depending on you system you might need to use _sudo_ for this step.
24
+ > **NB!** Downloadable files are quite large because these are packaged Node.js applications
28
25
 
29
26
  ## Help
30
27
 
@@ -36,12 +33,6 @@ $ mailauth seal --help
36
33
  $ mailauth spf --help
37
34
  ```
38
35
 
39
- In some systems you can also use manpages (manpage availability depends on how node and npm were installed to the system).
40
-
41
- ```
42
- $ man mailauth
43
- ```
44
-
45
36
  ## Available commands
46
37
 
47
38
  ### report
@@ -211,6 +202,14 @@ DNS query for A mail.wildduck.email: ["217.146.76.20"]
211
202
  ...
212
203
  ```
213
204
 
205
+ ### license
206
+
207
+ Display licenses for `mailauth` and included modules.
208
+
209
+ ```
210
+ $ mailauth licenses
211
+ ```
212
+
214
213
  ## DNS cache file
215
214
 
216
215
  In general you would use the `--dns-cache` option only when testing. This way you can provide different kind of DNS responses without actually setting up a DNS server and unlike when using real DNS you do not have to wait for the changes in the DNS server to propagate – whatever is in the provided cache file, is used for the DNS query responses.
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.security.cs.allow-jit</key>
6
+ <true/>
7
+ <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
8
+ <true/>
9
+ <key>com.apple.security.cs.allow-dyld-environment-variables</key>
10
+ <true/>
11
+ <key>com.apple.security.cs.disable-library-validation</key>
12
+ <true/>
13
+ <key>com.apple.security.network.client</key>
14
+ <true/>
15
+ <key>com.apple.security.network.server</key>
16
+ <true/>
17
+ </dict>
18
+ </plist>
@@ -172,7 +172,12 @@ class DkimVerifier extends MessageParser {
172
172
  instance: ['ARC', 'AS'].includes(signatureHeader.type) ? signatureHeader.parsed?.i?.value : false
173
173
  });
174
174
 
175
- let publicKey, rr;
175
+ let signingHeaders = {
176
+ keys: signingHeaderLines.keys,
177
+ headers: signingHeaderLines.headers.map(l => l.line.toString())
178
+ };
179
+
180
+ let publicKey, rr, modulusLength;
176
181
  let status = {
177
182
  result: 'neutral',
178
183
  comment: false,
@@ -208,6 +213,7 @@ class DkimVerifier extends MessageParser {
208
213
 
209
214
  publicKey = res?.publicKey;
210
215
  rr = res?.rr;
216
+ modulusLength = res?.modulusLength;
211
217
 
212
218
  try {
213
219
  status.result = crypto.verify(
@@ -283,6 +289,7 @@ class DkimVerifier extends MessageParser {
283
289
  format: signatureHeader.parsed?.c?.value,
284
290
  bodyHash,
285
291
  bodyHashExpecting: signatureHeader.parsed?.bh?.value,
292
+ signingHeaders,
286
293
  status
287
294
  };
288
295
 
@@ -298,6 +305,10 @@ class DkimVerifier extends MessageParser {
298
305
  result.publicKey = publicKey.toString();
299
306
  }
300
307
 
308
+ if (modulusLength) {
309
+ result.modulusLength = modulusLength;
310
+ }
311
+
301
312
  if (rr) {
302
313
  result.rr = rr;
303
314
  }
package/lib/spf/index.js CHANGED
@@ -61,29 +61,6 @@ let limitedResolver = (resolver, maxResolveCount) => {
61
61
 
62
62
  try {
63
63
  let result = await resolver(domain, type);
64
-
65
- switch (type) {
66
- case 'TXT':
67
- for (let row of result) {
68
- if (
69
- /[^\x20-\x7E]/.test(
70
- []
71
- .concat(row || [])
72
- .map(line => line.trim())
73
- .join('')
74
- )
75
- ) {
76
- let err = new Error('Invalid characters in DNS response');
77
- err.spfResult = {
78
- error: 'permerror',
79
- text: 'DNS response includes invalid characters'
80
- };
81
- throw err;
82
- }
83
- }
84
- break;
85
- }
86
-
87
64
  return result;
88
65
  } catch (err) {
89
66
  switch (err.code) {
package/lib/spf/macro.js CHANGED
@@ -89,7 +89,7 @@ const macro = (input, values) => {
89
89
  break;
90
90
 
91
91
  case 'h':
92
- curval = helo;
92
+ curval = helo || `[${ipaddr.parse(ip).toString()}]`;
93
93
  break;
94
94
 
95
95
  case 'c':
@@ -90,6 +90,15 @@ const spfVerify = async (domain, opts) => {
90
90
  }
91
91
  spfRr = row;
92
92
  spfRecord = parts.slice(1);
93
+
94
+ if (spfRr && /[^\x20-\x7E]/.test(spfRr)) {
95
+ let err = new Error('Invalid characters in DNS response');
96
+ err.spfResult = {
97
+ error: 'permerror',
98
+ text: 'DNS response includes invalid characters'
99
+ };
100
+ throw err;
101
+ }
93
102
  }
94
103
  }
95
104
 
package/lib/tools.js CHANGED
@@ -6,7 +6,6 @@ const punycode = require('punycode/');
6
6
  const libmime = require('libmime');
7
7
  const dns = require('dns').promises;
8
8
  const crypto = require('crypto');
9
- const pki = require('node-forge').pki;
10
9
  const https = require('https');
11
10
  const packageData = require('../package');
12
11
  const parseDkimHeaders = require('./parse-dkim-headers');
@@ -15,6 +14,9 @@ const { Certificate } = require('@fidm/x509');
15
14
  const zlib = require('zlib');
16
15
  const util = require('util');
17
16
  const gunzip = util.promisify(zlib.gunzip);
17
+ const pki = require('node-forge').pki;
18
+ const Joi = require('joi');
19
+ const base64Schema = Joi.string().base64({ paddingRequired: false });
18
20
 
19
21
  const defaultDKIMFieldNames =
20
22
  'From:Sender:Reply-To:Subject:Date:Message-ID:To:' +
@@ -247,14 +249,23 @@ const getPublicKey = async (type, name, minBitLength, resolver) => {
247
249
  // prefix value for parsing as there is no default value
248
250
  let entry = parseDkimHeaders(`DNS: TXT;${rr}`);
249
251
 
250
- let publicKey = entry?.parsed?.p?.value;
251
- if (!publicKey) {
252
+ const publicKeyValue = entry?.parsed?.p?.value;
253
+ if (!publicKeyValue) {
252
254
  let err = new Error('Missing key value');
253
255
  err.code = 'EINVALIDVAL';
254
256
  err.rr = rr;
255
257
  throw err;
256
258
  }
257
259
 
260
+ let validation = base64Schema.validate(publicKeyValue);
261
+ if (validation.error) {
262
+ let err = new Error('Invalid base64 format for public key');
263
+ err.code = 'EINVALIDVAL';
264
+ err.rr = rr;
265
+ err.details = validation.error;
266
+ throw err;
267
+ }
268
+
258
269
  if (type === 'DKIM' && entry?.parsed?.v && (entry?.parsed?.v?.value || '').toString().toLowerCase().trim() !== 'dkim1') {
259
270
  let err = new Error('Unknown key version');
260
271
  err.code = 'EINVALIDVER';
@@ -262,28 +273,42 @@ const getPublicKey = async (type, name, minBitLength, resolver) => {
262
273
  throw err;
263
274
  }
264
275
 
265
- publicKey = Buffer.from(`-----BEGIN PUBLIC KEY-----\n${publicKey}\n-----END PUBLIC KEY-----`);
266
- let keyType = crypto.createPublicKey({ key: publicKey, format: 'pem' }).asymmetricKeyType;
276
+ const publicKeyPem = Buffer.from(`-----BEGIN PUBLIC KEY-----\n${publicKeyValue.replace(/.{64}/g, '$&\r\n')}\n-----END PUBLIC KEY-----`);
277
+ const publicKeyObj = crypto.createPublicKey({
278
+ key: publicKeyPem,
279
+ format: 'pem'
280
+ });
281
+
282
+ let keyType = publicKeyObj.asymmetricKeyType;
267
283
 
268
284
  if (!['rsa', 'ed25519'].includes(keyType) || (entry?.parsed?.k && entry?.parsed?.k?.value?.toLowerCase() !== keyType)) {
269
- let err = new Error('Unknown key type');
285
+ let err = new Error('Unknown key type (${keyType})');
270
286
  err.code = 'EINVALIDTYPE';
271
287
  err.rr = rr;
272
288
  throw err;
273
289
  }
274
290
 
275
- if (keyType === 'rsa') {
276
- // check key length
277
- const pubKeyData = pki.publicKeyFromPem(publicKey.toString());
278
- if (pubKeyData.n.bitLength() < 1024) {
279
- let err = new Error('Key too short');
280
- err.code = 'ESHORTKEY';
281
- err.rr = rr;
282
- throw err;
283
- }
291
+ let modulusLength;
292
+ if (publicKeyObj.asymmetricKeyDetails) {
293
+ modulusLength = publicKeyObj.asymmetricKeyDetails.modulusLength;
294
+ } else {
295
+ // fall back to node-forge
296
+ const pubKeyData = pki.publicKeyFromPem(publicKeyPem.toString());
297
+ modulusLength = pubKeyData.n.bitLength();
298
+ }
299
+
300
+ if (keyType === 'rsa' && modulusLength < 1024) {
301
+ let err = new Error('RSA key too short');
302
+ err.code = 'ESHORTKEY';
303
+ err.rr = rr;
304
+ throw err;
284
305
  }
285
306
 
286
- return { publicKey, rr };
307
+ return {
308
+ publicKey: publicKeyPem,
309
+ rr,
310
+ modulusLength
311
+ };
287
312
  }
288
313
 
289
314
  let err = new Error('Missing key value');
@@ -0,0 +1,3 @@
1
+ {
2
+ "fields": ["name", "licenseType", "link", "installedVersion", "author"]
3
+ }
package/licenses.txt ADDED
@@ -0,0 +1,11 @@
1
+ name license type link installed version author
2
+ ---- ------------ ---- ----------------- ------
3
+ @fidm/x509 MIT git+ssh://git@github.com/fidm/x509.git 1.2.1 n/a
4
+ ipaddr.js MIT git://github.com/whitequark/ipaddr.js.git 2.0.1 whitequark
5
+ joi BSD-3-Clause git://github.com/sideway/joi.git 17.5.0 n/a
6
+ libmime MIT git://github.com/andris9/libmime.git 5.0.0 Andris Reinman
7
+ node-forge (BSD-3-Clause OR GPL-2.0) git+https://github.com/digitalbazaar/forge.git 1.2.1 Digital Bazaar, Inc.
8
+ nodemailer MIT git+https://github.com/nodemailer/nodemailer.git 6.7.2 Andris Reinman
9
+ psl MIT git+ssh://git@github.com/lupomontero/psl.git 1.8.0 Lupo Montero
10
+ punycode MIT git+https://github.com/bestiejs/punycode.js.git 2.1.1 Mathias Bynens
11
+ yargs MIT git+https://github.com/yargs/yargs.git 17.3.1 n/a
package/man/mailauth.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "MAILAUTH" "1" "August 2021" "v2.2.0" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "January 2022" "v2.3.0" "Mailauth Help"
2
2
  .SH "NAME"
3
3
  \fBmailauth\fR
4
4
  .QP
@@ -33,9 +33,13 @@ Authenticates an email and seals it with an ARC digital signature
33
33
  \fBspf\fR
34
34
  .br
35
35
  Authenticates SPF for an IP address and email address
36
+ .P
37
+ \fBlicense\fR
38
+ .br
39
+ Display licenses for mailauth and included modules
36
40
  .SH Website
37
41
  .P
38
- \fIhttps://github\.com/andris9/mailauth\fR
42
+ \fIhttps://github\.com/postalsys/mailauth\fR
39
43
  .SH EXAMPLES
40
44
  .P
41
45
  \fBnpm install mailauth \-g\fP
@@ -128,10 +132,10 @@ For cached DNS requests use the following JSON structure where main keys are dom
128
132
  Longer TXT strings can be split into multiple strings\. Unlike in real DNS there is no length limit, so you can put the entire public key into a single string\.
129
133
  .SH BUGS
130
134
  .P
131
- Please report any bugs to https://github\.com/andris9/mailauth/issues\.
135
+ Please report any bugs to https://github\.com/postalsys/mailauth/issues\.
132
136
  .SH LICENSE
133
137
  .P
134
- Copyright (c) 2020, Andris Reinman (MIT)\.
138
+ Copyright (c) 2020\-2022, Postal Systems (MIT)\.
135
139
  .SH SEE ALSO
136
140
  .P
137
141
  node\.js(1)
package/man/man.md CHANGED
@@ -28,9 +28,12 @@ Authenticates an email and seals it with an ARC digital signature
28
28
  **spf**\
29
29
  Authenticates SPF for an IP address and email address
30
30
 
31
+ **license**\
32
+ Display licenses for mailauth and included modules
33
+
31
34
  ## Website
32
35
 
33
- [](https://github.com/andris9/mailauth)
36
+ [](https://github.com/postalsys/mailauth)
34
37
 
35
38
  ## EXAMPLES
36
39
 
@@ -123,11 +126,11 @@ Longer TXT strings can be split into multiple strings. Unlike in real DNS there
123
126
 
124
127
  ## BUGS
125
128
 
126
- Please report any bugs to https://github.com/andris9/mailauth/issues.
129
+ Please report any bugs to https://github.com/postalsys/mailauth/issues.
127
130
 
128
131
  ## LICENSE
129
132
 
130
- Copyright (c) 2020, Andris Reinman (MIT).
133
+ Copyright (c) 2020-2022, Postal Systems (MIT).
131
134
 
132
135
  ## SEE ALSO
133
136
 
package/package.json CHANGED
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "name": "mailauth",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Email authentication library for Node.js",
5
5
  "main": "lib/mailauth.js",
6
6
  "scripts": {
7
7
  "test": "eslint \"lib/**/*.js\" \"test/**/*.js\" && mocha --recursive \"./test/**/*.js\" --reporter spec",
8
8
  "prepublish": "npm run man || true",
9
- "man": "cd man && marked-man --version `node -e \"console.log('v'+require('../package.json').version)\"` --manual 'Mailauth Help' --section 1 man.md > mailauth.1"
9
+ "man": "cd man && marked-man --version `node -e \"console.log('v'+require('../package.json').version)\"` --manual 'Mailauth Help' --section 1 man.md > mailauth.1",
10
+ "build-dist": "npm run man && npm run licenses && pkg package.json",
11
+ "licenses": "license-report --only=prod --output=table --config license-report-config.json > licenses.txt"
10
12
  },
11
13
  "repository": {
12
14
  "type": "git",
13
- "url": "git+https://github.com/andris9/mailauth.git"
15
+ "url": "git+https://github.com/postalsys/mailauth.git"
14
16
  },
15
17
  "keywords": [
16
18
  "rfc822",
@@ -22,33 +24,35 @@
22
24
  "bimi",
23
25
  "mta-sts"
24
26
  ],
25
- "author": "Andris Reinman",
27
+ "author": "Postal Systems OÜ",
26
28
  "license": "MIT",
27
29
  "bugs": {
28
- "url": "https://github.com/andris9/mailauth/issues"
30
+ "url": "https://github.com/postalsys/mailauth/issues"
29
31
  },
30
- "homepage": "https://github.com/andris9/mailauth",
32
+ "homepage": "https://github.com/postalsys/mailauth",
31
33
  "devDependencies": {
32
34
  "chai": "4.3.4",
33
- "eslint": "7.32.0",
35
+ "eslint": "8.7.0",
34
36
  "eslint-config-nodemailer": "1.2.0",
35
37
  "eslint-config-prettier": "8.3.0",
36
38
  "js-yaml": "4.1.0",
39
+ "license-report": "4.5.0",
37
40
  "marked": "0.7.0",
38
41
  "marked-man": "0.7.0",
39
42
  "mbox-reader": "1.1.5",
40
- "mocha": "9.1.0"
43
+ "mocha": "9.1.4",
44
+ "pkg": "5.5.2"
41
45
  },
42
46
  "dependencies": {
43
47
  "@fidm/x509": "1.2.1",
44
48
  "ipaddr.js": "2.0.1",
45
- "joi": "17.4.2",
49
+ "joi": "17.5.0",
46
50
  "libmime": "5.0.0",
47
- "node-forge": "0.10.0",
48
- "nodemailer": "6.6.3",
51
+ "node-forge": "1.2.1",
52
+ "nodemailer": "6.7.2",
49
53
  "psl": "1.8.0",
50
54
  "punycode": "2.1.1",
51
- "yargs": "17.1.1"
55
+ "yargs": "17.3.1"
52
56
  },
53
57
  "engines": {
54
58
  "node": ">=14.0.0"
@@ -58,5 +62,19 @@
58
62
  },
59
63
  "directories": {
60
64
  "man": "man"
65
+ },
66
+ "pkg": {
67
+ "scripts": [
68
+ "workers/**/*.js"
69
+ ],
70
+ "assets": [
71
+ "man/**/*",
72
+ "licenses.txt",
73
+ "LICENSE.txt"
74
+ ],
75
+ "_targets": [
76
+ "node16-macos-x64"
77
+ ],
78
+ "outputPath": "dist"
61
79
  }
62
80
  }