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

rfc2047

Package Overview
Dependencies
Maintainers
3
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rfc2047 - npm Package Compare versions

Comparing version 3.0.1 to 4.0.0

.github/workflows/ci.yml

170

lib/rfc2047.js

@@ -1,8 +0,7 @@

/* jshint regexp:false */
/* global unescape */
var isUtf8RegExp = /^utf-?8$/i;
var isLatin1RegExp = /^(?:iso-8859-1|latin1)$/i;
var iconvLite = require('iconv-lite');
var rfc2047 = (module.exports = {});
const isUtf8RegExp = /^utf-?8$/i;
const isLatin1RegExp = /^(?:iso-8859-1|latin1)$/i;
const iconvLite = require('iconv-lite');
const rfc2047 = (module.exports = {});

@@ -19,3 +18,3 @@ function stringify(obj) {

var iconv;
let iconv;
try {

@@ -30,4 +29,4 @@ iconv = require('' + 'iconv'); // Prevent browserify from detecting iconv and failing

encodedText = encodedText.replace(/_/g, ' ');
var numValidlyEncodedBytes = 0;
var i;
let numValidlyEncodedBytes = 0;
let i;
for (i = 0; i < encodedText.length; i += 1) {

@@ -41,7 +40,9 @@ if (

}
var buffer = Buffer.alloc(encodedText.length - numValidlyEncodedBytes * 2);
var j = 0;
const buffer = Buffer.alloc(
encodedText.length - numValidlyEncodedBytes * 2
);
let j = 0;
for (i = 0; i < encodedText.length; i += 1) {
if (encodedText[i] === '=') {
var hexChars = encodedText.slice(i + 1, i + 3);
const hexChars = encodedText.slice(i + 1, i + 3);
if (/^[0-9a-f]{2}$/i.test(hexChars)) {

@@ -74,3 +75,3 @@ buffer[j] = parseInt(encodedText.substr(i + 1, 2), 16);

} else {
var buffer;
let buffer;
try {

@@ -86,3 +87,3 @@ buffer = decodeBuffer(encodedText, encoding);

if (iconv) {
var converter;
let converter;
try {

@@ -119,46 +120,51 @@ converter = new iconv.Iconv(charset, 'utf-8//TRANSLIT');

var encodedWordRegExp = /=\?([^?]+)\?([QB])\?([^?]*)\?=/gi;
const encodedWordRegExp = /=\?([^?]+)\?([QB])\?([^?]*)\?=/gi;
rfc2047.decode = function (text) {
rfc2047.decode = (text) => {
text = stringify(text).replace(/\?=\s+=\?/g, '?==?'); // Strip whitespace between neighbouring encoded words
var numEncodedWordsToIgnore = 0;
let numEncodedWordsToIgnore = 0;
return text.replace(encodedWordRegExp, function (
encodedWord,
charset,
encoding,
encodedText,
index
) {
if (numEncodedWordsToIgnore > 0) {
numEncodedWordsToIgnore -= 1;
return '';
}
encoding = encoding.toLowerCase();
var decodedTextOrBuffer = decodeEncodedWord(encodedText, encoding, charset);
while (typeof decodedTextOrBuffer !== 'string') {
// The encoded word couldn't be decoded because it contained a partial character in a multibyte charset.
// Keep trying to look ahead and consume an additional encoded word right after this one, and if its
// encoding and charsets match, try to decode the concatenation.
return text.replace(
encodedWordRegExp,
(encodedWord, charset, encoding, encodedText, index) => {
if (numEncodedWordsToIgnore > 0) {
numEncodedWordsToIgnore -= 1;
return '';
}
encoding = encoding.toLowerCase();
let decodedTextOrBuffer = decodeEncodedWord(
encodedText,
encoding,
charset
);
while (typeof decodedTextOrBuffer !== 'string') {
// The encoded word couldn't be decoded because it contained a partial character in a multibyte charset.
// Keep trying to look ahead and consume an additional encoded word right after this one, and if its
// encoding and charsets match, try to decode the concatenation.
// The ongoing replace call is unaffected by this trick, so we don't need to reset .lastIndex afterwards:
encodedWordRegExp.lastIndex = index + encodedWord.length;
var matchNextEncodedWord = encodedWordRegExp.exec(text);
if (
matchNextEncodedWord &&
matchNextEncodedWord.index === index + encodedWord.length &&
matchNextEncodedWord[1] === charset &&
matchNextEncodedWord[2].toLowerCase() === encoding
) {
numEncodedWordsToIgnore += 1;
encodedWord += matchNextEncodedWord[0];
encodedText += matchNextEncodedWord[3];
decodedTextOrBuffer = decodeEncodedWord(encodedText, encoding, charset);
} else {
return encodedWord;
// The ongoing replace call is unaffected by this trick, so we don't need to reset .lastIndex afterwards:
encodedWordRegExp.lastIndex = index + encodedWord.length;
const matchNextEncodedWord = encodedWordRegExp.exec(text);
if (
matchNextEncodedWord &&
matchNextEncodedWord.index === index + encodedWord.length &&
matchNextEncodedWord[1] === charset &&
matchNextEncodedWord[2].toLowerCase() === encoding
) {
numEncodedWordsToIgnore += 1;
encodedWord += matchNextEncodedWord[0];
encodedText += matchNextEncodedWord[3];
decodedTextOrBuffer = decodeEncodedWord(
encodedText,
encoding,
charset
);
} else {
return encodedWord;
}
}
return decodedTextOrBuffer;
}
return decodedTextOrBuffer;
});
);
};

@@ -172,4 +178,4 @@

// Initialize array used as lookup table (int (octet) => string)
var qpTokenByOctet = new Array(256);
var i;
const qpTokenByOctet = new Array(256);
let i;

@@ -180,9 +186,8 @@ for (i = 0; i < 256; i += 1) {

"!#$%&'*+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\\^`abcdefghijklmnopqrstuvwxyz{|}~"
.split(/(?:)/)
.forEach(function (encodedWordSafeAsciiChar) {
qpTokenByOctet[
encodedWordSafeAsciiChar.charCodeAt(0)
] = encodedWordSafeAsciiChar;
});
for (const encodedWordSafeAsciiChar of "!#$%&'*+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\\^`abcdefghijklmnopqrstuvwxyz{|}~".split(
/(?:)/
)) {
qpTokenByOctet[encodedWordSafeAsciiChar.charCodeAt(0)] =
encodedWordSafeAsciiChar;
}

@@ -192,4 +197,4 @@ qpTokenByOctet[32] = '_';

function bufferToQuotedPrintableString(buffer) {
var result = '';
for (var i = 0; i < buffer.length; i += 1) {
let result = '';
for (let i = 0; i < buffer.length; i += 1) {
result += qpTokenByOctet[buffer[i]];

@@ -202,9 +207,9 @@ }

var headerSafeAsciiChars =
const headerSafeAsciiChars =
' !"#$%&\'()*+-,-./0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
var headerUnsafeAsciiChars = '';
let headerUnsafeAsciiChars = '';
for (i = 0; i < 128; i += 1) {
var ch = String.fromCharCode(i);
if (headerSafeAsciiChars.indexOf(ch) === -1) {
const ch = String.fromCharCode(i);
if (!headerSafeAsciiChars.includes(ch)) {
// O(n^2) but only happens at startup

@@ -219,18 +224,18 @@ headerUnsafeAsciiChars += ch;

var unsafeTokenRegExp = new RegExp(
const unsafeTokenRegExp = new RegExp(
`[\u0080-\uffff${quoteCharacterClass(headerUnsafeAsciiChars)}]`
);
var maxNumCharsPerEncodedWord = 8; // Very conservative limit to prevent creating an encoded word of more than 72 ascii chars
const maxNumCharsPerEncodedWord = 8; // Very conservative limit to prevent creating an encoded word of more than 72 ascii chars
rfc2047.encode = function (text) {
rfc2047.encode = (text) => {
text = stringify(text).replace(/\s/g, ' '); // Normalize whitespace
var tokens = text.match(/([^\s]*\s*)/g); // Split at space, but keep trailing space as part of each token
var previousTokenWasEncodedWord = false; // Consecutive encoded words must have a space between them, so this state must be kept
var previousTokenWasWhitespaceFollowingEncodedWord = false;
var result = '';
const tokens = text.match(/([^\s]*\s*)/g); // Split at space, but keep trailing space as part of each token
let previousTokenWasEncodedWord = false; // Consecutive encoded words must have a space between them, so this state must be kept
let previousTokenWasWhitespaceFollowingEncodedWord = false;
let result = '';
if (tokens) {
for (var i = 0; i < tokens.length; i += 1) {
var token = tokens[i];
for (let i = 0; i < tokens.length; i += 1) {
let token = tokens[i];
if (unsafeTokenRegExp.test(token)) {
var matchQuotesAtBeginning = token.match(/^"+/);
const matchQuotesAtBeginning = token.match(/^"+/);
if (matchQuotesAtBeginning) {

@@ -244,3 +249,3 @@ previousTokenWasEncodedWord = false;

var matchWhitespaceOrQuotesAtEnd = token.match(/\\?[\s"]+$/);
const matchWhitespaceOrQuotesAtEnd = token.match(/\\?[\s"]+$/);
if (matchWhitespaceOrQuotesAtEnd) {

@@ -256,4 +261,9 @@ tokens.splice(i + 1, 0, matchWhitespaceOrQuotesAtEnd[0]);

if (token.length > maxNumCharsPerEncodedWord) {
tokens.splice(i + 1, 0, token.substr(maxNumCharsPerEncodedWord));
token = token.substr(0, maxNumCharsPerEncodedWord);
const chars = [...token];
tokens.splice(
i + 1,
0,
chars.slice(maxNumCharsPerEncodedWord).join('')
);
token = chars.slice(0, maxNumCharsPerEncodedWord).join('');
}

@@ -265,5 +275,5 @@

var charset = 'utf-8';
const charset = 'utf-8';
// Around 25% faster than encodeURIComponent(token.replace(/ /g, "_")).replace(/%/g, "="):
var encodedWordBody = bufferToQuotedPrintableString(
const encodedWordBody = bufferToQuotedPrintableString(
Buffer.from(token, 'utf-8')

@@ -270,0 +280,0 @@ );

{
"name": "rfc2047",
"version": "3.0.1",
"version": "4.0.0",
"description": "Encode and decode rfc2047 (MIME encoded words)",

@@ -30,3 +30,3 @@ "main": "lib/rfc2047.js",

"author": "Andreas Lind <andreaslindpetersen@gmail.com>",
"license": "BSD",
"license": "BSD-3-Clause",
"bugs": {

@@ -49,3 +49,3 @@ "url": "https://github.com/One-com/rfc2047/issues"

"nyc": "^15.1.0",
"prettier": "^2.1.2",
"prettier": "~2.4.1",
"proxyquire": "^2.1.3",

@@ -52,0 +52,0 @@ "unexpected": "10.20.0"

@@ -21,1 +21,6 @@ # rfc2047

[![Dependency Status](https://david-dm.org/One-com/rfc2047.png)](https://david-dm.org/One-com/rfc2047)
## License
The rfc2047 module is licensed under a standard 3-clause BSD license -- see the
`LICENSE`-file for details.

@@ -1,9 +0,8 @@

/* global describe, it */
var unexpected = require('unexpected');
var proxyquire = require('proxyquire');
const unexpected = require('unexpected');
const proxyquire = require('proxyquire');
describe('rfc2047', function () {
describe('rfc2047', () => {
for (const iconvAvailable of [false, true]) {
describe(`with iconv ${iconvAvailable ? '' : 'un'}available`, function () {
var rfc2047 = iconvAvailable
describe(`with iconv ${iconvAvailable ? '' : 'un'}available`, () => {
const rfc2047 = iconvAvailable
? require('../lib/rfc2047')

@@ -14,33 +13,32 @@ : proxyquire('../lib/rfc2047', {

var expect = unexpected
const expect = unexpected
.clone()
.addAssertion('to encode to', function (expect, subject, value) {
.addAssertion('to encode to', (expect, subject, value) => {
expect(rfc2047.encode(subject), 'to equal', value);
})
.addAssertion('to decode to', function (expect, subject, value) {
.addAssertion('to decode to', (expect, subject, value) => {
expect(rfc2047.decode(subject), 'to equal', value);
})
.addAssertion('to encode back and forth to', function (
expect,
subject,
value
) {
expect(subject, 'to encode to', value);
expect(value, 'to decode to', subject);
});
.addAssertion(
'to encode back and forth to',
(expect, subject, value) => {
expect(subject, 'to encode to', value);
expect(value, 'to decode to', subject);
}
);
describe('#encode() and #decode()', function () {
it('should handle the empty string', function () {
describe('#encode() and #decode()', () => {
it('should handle the empty string', () => {
expect('', 'to encode back and forth to', '');
});
it('should handle a string only containing a space', function () {
it('should handle a string only containing a space', () => {
expect(' ', 'to encode back and forth to', ' ');
});
it('should not encode an equals sign', function () {
it('should not encode an equals sign', () => {
expect('=', 'to encode back and forth to', '=');
});
it('should handle a string that does not need to be encoded', function () {
it('should handle a string that does not need to be encoded', () => {
expect(

@@ -53,4 +51,13 @@ 'Andreas Lind <andreas@one.com>',

it('should handle a multi-word string where the middle word has to be encoded', function () {
// https://github.com/One-com/rfc2047/issues/11
it('should handle a string with multiple emojis in a row', () => {
expect(
'Seven hills😍🦌',
'to encode back and forth to',
'Seven =?utf-8?Q?hills=F0=9F=98=8D=F0=9F=A6=8C?='
);
});
it('should handle a multi-word string where the middle word has to be encoded', () => {
expect(
'Andreas Lindø <andreas@one.com>',

@@ -62,3 +69,3 @@ 'to encode back and forth to',

it('should use an UTF-8 encoded word when a character is not in iso-8859-1', function () {
it('should use an UTF-8 encoded word when a character is not in iso-8859-1', () => {
expect(

@@ -71,3 +78,3 @@ 'Mr. Smiley face aka ☺ <smiley@face.dk>',

it('should handle two neighbouring words that have to be encoded', function () {
it('should handle two neighbouring words that have to be encoded', () => {
expect(

@@ -85,3 +92,3 @@ '¡Hola, señor!',

it('should not rely on the space between neighbouring encoded words to be preserved', function () {
it('should not rely on the space between neighbouring encoded words to be preserved', () => {
expect(

@@ -94,3 +101,3 @@ '☺ ☺',

it('should handle some dreamed up edge cases', function () {
it('should handle some dreamed up edge cases', () => {
expect(

@@ -103,3 +110,3 @@ 'lördag',

it('should handle a multi-word string where the middle word has to be left unencoded', function () {
it('should handle a multi-word string where the middle word has to be left unencoded', () => {
expect(

@@ -112,3 +119,3 @@ 'Så er fødselen i gang',

it('should place leading quotes correctly', function () {
it('should place leading quotes correctly', () => {
expect(

@@ -121,3 +128,3 @@ '"ÅÄÖ" <sss@example.com>',

it('should place trailing quotes correctly', function () {
it('should place trailing quotes correctly', () => {
expect(

@@ -131,11 +138,11 @@ '"TEST ÅÄÖ" <sss@example.com>',

// Regression test for #2:
it('should handle an emoji test case', function () {
it('should handle an emoji test case', () => {
expect(
'{"tags":"","fullName":"😬"}',
'to encode back and forth to',
'=?utf-8?Q?{=22tags=22=3A?=""=?utf-8?Q?=2C=22fullNa?= =?utf-8?Q?me=22=3A=22=F0=9F=98=AC=22?=}'
'=?utf-8?Q?{=22tags=22=3A?=""=?utf-8?Q?=2C=22fullNa?= =?utf-8?Q?me=22=3A=22=F0=9F=98=AC=22}?='
);
});
it('should handle the replacement character', function () {
it('should handle the replacement character', () => {
expect(

@@ -149,4 +156,4 @@ 'test_�.docx',

describe('#encode()', function () {
it('should handle non-string values correctly', function () {
describe('#encode()', () => {
it('should handle non-string values correctly', () => {
expect(-1, 'to encode to', '-1');

@@ -161,7 +168,7 @@ expect(Infinity, 'to encode to', 'Infinity');

it('should handle a tab character at the beginning of a word', function () {
it('should handle a tab character at the beginning of a word', () => {
expect('\tfoo', 'to encode to', ' foo');
});
it('should handle control chars', function () {
it('should handle control chars', () => {
expect(

@@ -174,11 +181,11 @@ '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f',

it('should handle a tab character at the end of a word', function () {
it('should handle a tab character at the end of a word', () => {
expect('foo\t', 'to encode to', 'foo ');
});
it('should handle a tab character with spaces around it', function () {
it('should handle a tab character with spaces around it', () => {
expect('bar \t foo', 'to encode to', 'bar foo');
});
it('should not split a backslash from the doublequote it is escaping', function () {
it('should not split a backslash from the doublequote it is escaping', () => {
expect('"Öland\\""', 'to encode to', '"=?utf-8?Q?=C3=96land?=\\""');

@@ -188,4 +195,4 @@ });

describe('#decode()', function () {
it('should handle non-string values correctly', function () {
describe('#decode()', () => {
it('should handle non-string values correctly', () => {
expect(-1, 'to decode to', '-1');

@@ -200,15 +207,15 @@ expect(Infinity, 'to decode to', 'Infinity');

it('should decode encoded word with invalid quoted-printable, decodeURIComponent case', function () {
it('should decode encoded word with invalid quoted-printable, decodeURIComponent case', () => {
expect('=?UTF-8?Q?=xxfoo?=', 'to decode to', '=xxfoo');
});
it('should decode encoded word with invalid quoted-printable, unescape case', function () {
it('should decode encoded word with invalid quoted-printable, unescape case', () => {
expect('=?iso-8859-1?Q?=xxfoo?=', 'to decode to', '=xxfoo');
});
it('should decode encoded word with invalid base64', function () {
it('should decode encoded word with invalid base64', () => {
expect('=?iso-8859-1?B?\u0000``?=', 'to decode to', '');
});
it('should decode separated encoded words', function () {
it('should decode separated encoded words', () => {
expect(

@@ -221,3 +228,3 @@ '=?utf-8?Q?One.com=E2=80?= =?utf-8?Q?=99s_=E2=80=9CDon=E2=80=99t_screw_it_up=E2=80=9D_?= =?utf-8?Q?code?=',

it('should handle the test cases listed in RFC 2047', function () {
it('should handle the test cases listed in RFC 2047', () => {
expect(

@@ -258,3 +265,3 @@ '=?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>',

it('should handle subject found in mail with X-Mailer: MailChimp Mailer', function () {
it('should handle subject found in mail with X-Mailer: MailChimp Mailer', () => {
expect(

@@ -272,3 +279,3 @@ '=?utf-8?Q?Spar=2020=20%=20p=C3=A5=20de=20bedste=20businessb=C3=B8ger=20fra=20Gyldendal=21?=',

it('should handle multiple base64 encoded words issued by Thunderbird', function () {
it('should handle multiple base64 encoded words issued by Thunderbird', () => {
expect(

@@ -281,3 +288,3 @@ '=?UTF-8?B?Rm9vw6YsIEZvbyDDpiwgw6bDuMOmw7jDpsO4w6bDuMOmw7jDpsO4LCA=?==?UTF-8?B?4pi6IE1y4pi6IOKYuuKYuuKYuuKYuuKYuuKYuuKYuuKYuuKYuuKYuuKYuuKYuuKYug==?= =?UTF-8?B?4pi64pi64pi64pi64pi64pi64pi6?=',

it('should handle two back-to-back UTF-8 encoded words from the subject in a raygun mail', function () {
it('should handle two back-to-back UTF-8 encoded words from the subject in a raygun mail', () => {
expect(

@@ -290,3 +297,3 @@ '=?utf-8?B?d2VibWFpbCBwcm9kdWN0aW9uIC0gbmV3IGVycm9yIC0gR2XD?==?utf-8?B?p2Vyc2l6IGRlxJ9pxZ9rZW4u?=',

it('should keep encoded words with partial sequences separate if there is text between them', function () {
it('should keep encoded words with partial sequences separate if there is text between them', () => {
expect(

@@ -299,7 +306,7 @@ '=?utf-8?B?d2VibWFpbCBwcm9kdWN0aW9uIC0gbmV3IGVycm9yIC0gR2XD?=foo=?utf-8?B?p2Vyc2l6IGRlxJ9pxZ9rZW4u?=',

it('should decode a UTF-8 smiley (illegally) split up into 2 encoded words', function () {
it('should decode a UTF-8 smiley (illegally) split up into 2 encoded words', () => {
expect('=?utf-8?Q?=E2=98?= =?utf-8?Q?=BA?=', 'to decode to', '☺');
});
it('should decode a UTF-8 smiley (illegally) split up into 3 encoded words', function () {
it('should decode a UTF-8 smiley (illegally) split up into 3 encoded words', () => {
expect(

@@ -312,3 +319,3 @@ '=?utf-8?Q?=E2?= =?utf-8?Q?=98?= =?utf-8?Q?=BA?=',

it('should give up decoding a UTF-8 smiley (illegally) split up into 3 encoded words if there is regular text between the encoded words', function () {
it('should give up decoding a UTF-8 smiley (illegally) split up into 3 encoded words if there is regular text between the encoded words', () => {
expect(

@@ -321,3 +328,3 @@ '=?utf-8?Q?=E2?= =?utf-8?Q?=98?=a=?utf-8?Q?=BA?==?utf-8?Q?=BA?=a',

it('should decode an encoded word following a undecodable sequence of encoded words', function () {
it('should decode an encoded word following a undecodable sequence of encoded words', () => {
expect(

@@ -330,3 +337,3 @@ '=?utf-8?Q?=E2?= =?utf-8?Q?=98?= =?iso-8859-1?Q?=A1?=Hola, se=?iso-8859-1?Q?=F1?=or!',

it('should handle test cases from the MIME tools package', function () {
it('should handle test cases from the MIME tools package', () => {
// From http://search.cpan.org/~dskoll/MIME-tools-5.502/lib/MIME/Words.pm:

@@ -360,3 +367,3 @@ expect(

it('should handle a file name found in a Korean mail', function () {
it('should handle a file name found in a Korean mail', () => {
expect(

@@ -369,3 +376,3 @@ '=?ks_c_5601-1987?B?MTMwMTE3X8HWwvfA5V+1tcDlX7jetLq+8y5wZGY=?=',

it('should handle bogus encoded words (spotted in the wild)', function () {
it('should handle bogus encoded words (spotted in the wild)', () => {
expect(

@@ -379,3 +386,3 @@ '=?utf-8?Q??= <andreas@one.com>',

if (iconvAvailable) {
it('should decode a character set not in iconv-lite', function () {
it('should decode a character set not in iconv-lite', () => {
expect(

@@ -382,0 +389,0 @@ '=?iso-2022-jp?B?GyRCRnxLXDhsJE4lNSVWJTglJyUvJUghXRsoQnRlc3Q=?=',

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