New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

mailauth

Package Overview
Dependencies
Maintainers
1
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mailauth - npm Package Compare versions

Comparing version 2.3.3 to 2.3.4

304

lib/dkim/body/relaxed.js

@@ -0,7 +1,12 @@

/* eslint no-control-regex: 0 */
'use strict';
// Calculates relaxed body hash for a message body stream
const crypto = require('crypto');
const CHAR_CR = 0x0d;
const CHAR_LF = 0x0a;
const CHAR_SPACE = 0x20;
const CHAR_TAB = 0x09;
/**

@@ -19,6 +24,7 @@ * Class for calculating body hash of an email message body stream

constructor(algorithm, maxBodyLength) {
algorithm = (algorithm || 'sha256').split('-').pop();
algorithm = (algorithm || 'sha256').split('-').pop().toLowerCase();
this.bodyHash = crypto.createHash(algorithm);
this.remainder = '';
this.remainder = false;
this.byteLength = 0;

@@ -28,6 +34,14 @@

this.maxBodyLength = maxBodyLength;
this.maxSizeReached = false;
this.emptyLinesQueue = [];
}
_updateBodyHash(chunk) {
// the following is needed for l= option
if (this.maxSizeReached) {
return;
}
// the following is needed for the l= option
if (

@@ -39,2 +53,3 @@ typeof this.maxBodyLength === 'number' &&

) {
this.maxSizeReached = true;
if (this.bodyHashedBytes >= this.maxBodyLength) {

@@ -44,4 +59,5 @@ // nothing to do here, skip entire chunk

}
// only use allowed size of bytes
chunk = chunk.slice(0, this.maxBodyLength - this.bodyHashedBytes);
chunk = chunk.subarray(0, this.maxBodyLength - this.bodyHashedBytes);
}

@@ -51,82 +67,164 @@

this.bodyHash.update(chunk);
//process.stdout.write(chunk);
}
update(chunk) {
this.byteLength += chunk.length;
_drainPendingEmptyLines() {
if (this.emptyLinesQueue.length) {
for (let emptyLine of this.emptyLinesQueue) {
this._updateBodyHash(emptyLine);
}
this.emptyLinesQueue = [];
}
}
let bodyStr;
_pushBodyHash(chunk) {
if (!chunk || !chunk.length) {
return;
}
// find next remainder
let nextRemainder = '';
// remove line endings
let foundNonLn = false;
// This crux finds and removes the spaces from the last line and the newline characters after the last non-empty line
// If we get another chunk that does not match this description then we can restore the previously processed data
let state = 'file';
// buffer line endings and empty lines
for (let i = chunk.length - 1; i >= 0; i--) {
let c = chunk[i];
if (state === 'file' && (c === 0x0a || c === 0x0d)) {
// do nothing, found \n or \r at the end of chunk, stil end of file
} else if (state === 'file' && (c === 0x09 || c === 0x20)) {
// switch to line ending mode, this is the last non-empty line
state = 'line';
} else if (state === 'line' && (c === 0x09 || c === 0x20)) {
// do nothing, found ' ' or \t at the end of line, keep processing the last non-empty line
} else if (state === 'file' || state === 'line') {
// non line/file ending character found, switch to body mode
state = 'body';
if (i === chunk.length - 1) {
// final char is not part of line end or file end, so do nothing
break;
if (chunk[i] !== CHAR_LF && chunk[i] !== CHAR_CR) {
this._drainPendingEmptyLines();
if (i < chunk.length - 1) {
this.emptyLinesQueue.push(chunk.subarray(i + 1));
chunk = chunk.subarray(0, i + 1);
}
foundNonLn = true;
break;
}
}
if (i === 0) {
// reached to the beginning of the chunk, check if it is still about the ending
// and if the remainder also matches
if (
(state === 'file' && (!this.remainder || /[\r\n]$/.test(this.remainder))) ||
(state === 'line' && (!this.remainder || /[ \t]$/.test(this.remainder)))
) {
// keep everything
this.remainder += chunk.toString('binary');
return;
} else if (state === 'line' || state === 'file') {
// process existing remainder as normal line but store the current chunk
nextRemainder = chunk.toString('binary');
chunk = false;
break;
if (!foundNonLn) {
this.emptyLinesQueue.push(chunk);
return;
}
this._updateBodyHash(chunk);
}
fixLineBuffer(line) {
let resultLine = [];
let nonWspFound = false;
let prevWsp = false;
for (let i = line.length - 1; i >= 0; i--) {
if (line[i] === CHAR_LF) {
resultLine.unshift(line[i]);
if (i === 0 || line[i - 1] !== CHAR_CR) {
// add missing carriage return
resultLine.unshift(CHAR_CR);
}
continue;
}
if (state !== 'body') {
if (line[i] === CHAR_CR) {
resultLine.unshift(line[i]);
continue;
}
// reached first non ending byte
nextRemainder = chunk.slice(i + 1).toString('binary');
chunk = chunk.slice(0, i + 1);
break;
if (line[i] === CHAR_SPACE || line[i] === CHAR_TAB) {
if (nonWspFound) {
prevWsp = true;
}
continue;
}
if (prevWsp) {
resultLine.unshift(CHAR_SPACE);
prevWsp = false;
}
nonWspFound = true;
resultLine.unshift(line[i]);
}
let needsFixing = !!this.remainder;
if (chunk && !needsFixing) {
// check if we even need to change anything
for (let i = 0, len = chunk.length; i < len; i++) {
if (i && chunk[i] === 0x0a && chunk[i - 1] !== 0x0d) {
// missing \r before \n
needsFixing = true;
break;
} else if (i && chunk[i] === 0x0d && chunk[i - 1] === 0x20) {
// trailing WSP found
needsFixing = true;
break;
} else if (i && chunk[i] === 0x20 && chunk[i - 1] === 0x20) {
// multiple spaces found, needs to be replaced with just one
needsFixing = true;
break;
} else if (chunk[i] === 0x09) {
// TAB found, needs to be replaced with a space
needsFixing = true;
break;
if (prevWsp && nonWspFound) {
resultLine.unshift(CHAR_SPACE);
}
return Buffer.from(resultLine);
}
update(chunk, final) {
this.byteLength += (chunk && chunk.length) || 0;
if (this.maxSizeReached) {
return;
}
// Canonicalize content by applying a and b in order:
// a.1. Ignore all whitespace at the end of lines.
// a.2. Reduce all sequences of WSP within a line to a single SP character.
// b.1. Ignore all empty lines at the end of the message body.
// b.2. If the body is non-empty but does not end with a CRLF, a CRLF is added.
let lineEndPos = -1;
let lineNeedsFixing = false;
let cursorPos = 0;
if (this.remainder && this.remainder.length) {
if (chunk) {
// concatting chunks might be bad for performance :S
chunk = Buffer.concat([this.remainder, chunk]);
} else {
chunk = this.remainder;
}
this.remainder = false;
}
if (chunk && chunk.length) {
for (let pos = 0; pos < chunk.length; pos++) {
switch (chunk[pos]) {
case CHAR_LF:
if (
!lineNeedsFixing &&
// previous character is not <CR>
((pos >= 1 && chunk[pos - 1] !== CHAR_CR) ||
// LF is the first byte on the line
pos === 0 ||
// there's a space before line break
(pos >= 2 && chunk[pos - 1] === CHAR_CR && chunk[pos - 2] === CHAR_SPACE))
) {
lineNeedsFixing = true;
}
// line break
if (lineNeedsFixing) {
// emit pending bytes up to the last line break before current line
if (lineEndPos >= 0 && lineEndPos >= cursorPos) {
let chunkPart = chunk.subarray(cursorPos, lineEndPos + 1);
this._pushBodyHash(chunkPart);
}
let line = chunk.subarray(lineEndPos + 1, pos + 1);
this._pushBodyHash(this.fixLineBuffer(line));
lineNeedsFixing = false;
// move cursor to the start of next line
cursorPos = pos + 1;
}
lineEndPos = pos;
break;
case CHAR_SPACE:
if (!lineNeedsFixing && pos && chunk[pos - 1] === CHAR_SPACE) {
lineNeedsFixing = true;
}
break;
case CHAR_TAB:
// non-space WSP always needs replacing
lineNeedsFixing = true;
break;
default:
}

@@ -136,23 +234,35 @@ }

if (needsFixing) {
bodyStr = this.remainder + (chunk ? chunk.toString('binary') : '');
this.remainder = nextRemainder;
bodyStr = bodyStr
.replace(/\r?\n/g, '\n') // use js line endings
.replace(/[ \t]*$/gm, '') // remove line endings, rtrim
.replace(/[ \t]+/gm, ' ') // single spaces
.replace(/\n/g, '\r\n'); // restore rfc822 line endings
chunk = Buffer.from(bodyStr, 'binary');
} else if (nextRemainder) {
this.remainder = nextRemainder;
if (chunk && cursorPos < chunk.length && cursorPos !== lineEndPos) {
// emit data from chunk
let chunkPart = chunk.subarray(cursorPos, lineEndPos + 1);
if (chunkPart.length) {
this._pushBodyHash(lineNeedsFixing ? this.fixLineBuffer(chunkPart) : chunkPart);
lineNeedsFixing = false;
}
cursorPos = lineEndPos + 1;
}
this._updateBodyHash(chunk);
if (chunk && !final && cursorPos < chunk.length) {
this.remainder = chunk.subarray(cursorPos);
}
if (final) {
let chunkPart = (cursorPos && chunk && chunk.subarray(cursorPos)) || chunk;
if (chunkPart && chunkPart.length) {
this._pushBodyHash(lineNeedsFixing ? this.fixLineBuffer(chunkPart) : chunkPart);
lineNeedsFixing = false;
}
if (this.bodyHashedBytes) {
// terminating line break for non-empty messages
this._updateBodyHash(Buffer.from([CHAR_CR, CHAR_LF]));
}
}
}
digest(encoding) {
if (/[\r\n]$/.test(this.remainder) && this.bodyHashedBytes > 0) {
// add terminating line end
this._updateBodyHash(Buffer.from('\r\n'));
}
this.update(null, true);

@@ -165,1 +275,25 @@ // finalize

module.exports = { RelaxedHash };
/*
let fs = require('fs');
const getBody = message => {
message = message.toString('binary');
let match = message.match(/\r?\n\r?\n/);
if (match) {
message = message.substr(match.index + match[0].length);
}
return Buffer.from(message, 'binary');
};
let s = fs.readFileSync(process.argv[2]);
let k = new RelaxedHash('rsa-sha256', -1);
for (let byte of getBody(s)) {
k.update(Buffer.from([byte]));
}
console.error(k.digest('base64'));
console.error(k.byteLength, k.bodyHashedBytes);
*/

@@ -25,2 +25,4 @@ 'use strict';

this.maxBodyLength = maxBodyLength;
this.lastNewline = false;
}

@@ -46,2 +48,4 @@

this.bodyHash.update(chunk);
//process.stdout.write(chunk);
}

@@ -86,6 +90,7 @@

this._updateBodyHash(chunk);
this.lastNewline = chunk[chunk.length - 1] === 0x0a;
}
digest(encoding) {
if (this.remainder.length || !this.bodyHashedBytes) {
if (!this.lastNewline || !this.bodyHashedBytes) {
// emit empty line buffer to keep the stream flowing

@@ -92,0 +97,0 @@ this._updateBodyHash(Buffer.from('\r\n'));

@@ -6,7 +6,7 @@ name license type link installed version author

joi BSD-3-Clause git://github.com/sideway/joi.git 17.6.0
libmime MIT git://github.com/andris9/libmime.git 5.0.0 Andris Reinman andris@kreata.ee
libmime MIT git://github.com/andris9/libmime.git 5.1.0 Andris Reinman andris@kreata.ee
node-forge (BSD-3-Clause OR GPL-2.0) git+https://github.com/digitalbazaar/forge.git 1.3.1 Digital Bazaar, Inc. support@digitalbazaar.com http://digitalbazaar.com/
nodemailer MIT git+https://github.com/nodemailer/nodemailer.git 6.7.3 Andris Reinman
nodemailer MIT git+https://github.com/nodemailer/nodemailer.git 6.7.5 Andris Reinman
psl MIT git+ssh://git@github.com/lupomontero/psl.git 1.8.0 Lupo Montero lupomontero@gmail.com https://lupomontero.com/
punycode MIT git+https://github.com/bestiejs/punycode.js.git 2.1.1 Mathias Bynens https://mathiasbynens.be/
yargs MIT git+https://github.com/yargs/yargs.git 17.4.1
yargs MIT git+https://github.com/yargs/yargs.git 17.5.0
{
"name": "mailauth",
"version": "2.3.3",
"version": "2.3.4",
"description": "Email authentication library for Node.js",

@@ -36,3 +36,3 @@ "main": "lib/mailauth.js",

"chai": "4.3.6",
"eslint": "8.15.0",
"eslint": "8.17.0",
"eslint-config-nodemailer": "1.2.0",

@@ -46,3 +46,3 @@ "eslint-config-prettier": "8.5.0",

"mocha": "10.0.0",
"pkg": "5.6.0"
"pkg": "5.7.0"
},

@@ -58,3 +58,3 @@ "dependencies": {

"punycode": "2.1.1",
"yargs": "17.5.0"
"yargs": "17.5.1"
},

@@ -61,0 +61,0 @@ "engines": {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc