Optimize util.removeTrailingSpaces (#848)
Backtracking regexes have pathological worst-case performance when a long line contains a large amount of whitespace not followed by a newline, since the regex engine will attempt to match the regex at each whitespace character, read ahead to the non-whitespace non- newline, declare no match, and try again at the next whitespace. E.g. try running util.removeTrailingSpaces(new Array(1e6).join(' ') + 'a').length which would hang V8.
This commit is contained in:
parent
f018f60b9c
commit
d91b064e14
|
@ -243,7 +243,7 @@ function dearmor(input) {
|
||||||
throw new Error('Misformed armored text');
|
throw new Error('Misformed armored text');
|
||||||
}
|
}
|
||||||
// remove trailing whitespace at end of lines
|
// remove trailing whitespace at end of lines
|
||||||
line = line.replace(/[\t\r\n ]+$/, '');
|
line = util.removeTrailingSpaces(line.replace(/[\r\n]/g, ''));
|
||||||
if (!type) {
|
if (!type) {
|
||||||
if (reSplit.test(line)) {
|
if (reSplit.test(line)) {
|
||||||
type = getType(line);
|
type = getType(line);
|
||||||
|
@ -294,7 +294,7 @@ function dearmor(input) {
|
||||||
let remainder = await reader.readToEnd();
|
let remainder = await reader.readToEnd();
|
||||||
if (!remainder.length) remainder = '';
|
if (!remainder.length) remainder = '';
|
||||||
remainder = line + remainder;
|
remainder = line + remainder;
|
||||||
remainder = remainder.replace(/[\t\r ]+$/mg, '');
|
remainder = util.removeTrailingSpaces(remainder.replace(/\r/g, ''));
|
||||||
const parts = remainder.split(reSplit);
|
const parts = remainder.split(reSplit);
|
||||||
if (parts.length === 1) {
|
if (parts.length === 1) {
|
||||||
throw new Error('Misformed armored text');
|
throw new Error('Misformed armored text');
|
||||||
|
|
|
@ -702,7 +702,11 @@ export default {
|
||||||
* Remove trailing spaces and tabs from each line
|
* Remove trailing spaces and tabs from each line
|
||||||
*/
|
*/
|
||||||
removeTrailingSpaces: function(text) {
|
removeTrailingSpaces: function(text) {
|
||||||
return text.replace(/[ \t]+$/mg, "");
|
return text.split('\n').map(line => {
|
||||||
|
let i = line.length - 1;
|
||||||
|
for (; i >= 0 && (line[i] === ' ' || line[i] === '\t'); i--);
|
||||||
|
return line.substr(0, i + 1);
|
||||||
|
}).join('\n');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1807,7 +1807,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
verifyOpt.message = await openpgp.cleartext.readArmored(signed.data);
|
verifyOpt.message = await openpgp.cleartext.readArmored(signed.data);
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
const signingKey = await privateKey.keys[0].getSigningKey();
|
const signingKey = await privateKey.keys[0].getSigningKey();
|
||||||
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
||||||
|
@ -1833,7 +1833,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
let signingKey;
|
let signingKey;
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
signingKey = await privateKey.keys[0].getSigningKey();
|
signingKey = await privateKey.keys[0].getSigningKey();
|
||||||
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
||||||
|
@ -1860,7 +1860,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
verifyOpt.signature = await openpgp.signature.readArmored(signed.signature);
|
verifyOpt.signature = await openpgp.signature.readArmored(signed.signature);
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
const signingKey = await privateKey.keys[0].getSigningKey();
|
const signingKey = await privateKey.keys[0].getSigningKey();
|
||||||
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
||||||
|
@ -1881,7 +1881,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
verifyOpt.message = await openpgp.cleartext.readArmored(signed.data);
|
verifyOpt.message = await openpgp.cleartext.readArmored(signed.data);
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.null;
|
expect(verified.signatures[0].valid).to.be.null;
|
||||||
const signingKey = await privateKey.keys[0].getSigningKey();
|
const signingKey = await privateKey.keys[0].getSigningKey();
|
||||||
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
||||||
|
@ -1904,7 +1904,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
verifyOpt.signature = await openpgp.signature.readArmored(signed.signature);
|
verifyOpt.signature = await openpgp.signature.readArmored(signed.signature);
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.null;
|
expect(verified.signatures[0].valid).to.be.null;
|
||||||
const signingKey = await privateKey.keys[0].getSigningKey();
|
const signingKey = await privateKey.keys[0].getSigningKey();
|
||||||
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
||||||
|
@ -1926,7 +1926,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
verifyOpt.message = signed.message;
|
verifyOpt.message = signed.message;
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
const signingKey = await privateKey.keys[0].getSigningKey();
|
const signingKey = await privateKey.keys[0].getSigningKey();
|
||||||
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
expect(verified.signatures[0].keyid.toHex()).to.equal(signingKey.getKeyId().toHex());
|
||||||
|
@ -1951,7 +1951,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
verifyOpt.signature = signed.signature;
|
verifyOpt.signature = signed.signature;
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(+verified.signatures[0].signature.packets[0].created).to.be.lte(+openpgp.util.normalizeDate());
|
expect(+verified.signatures[0].signature.packets[0].created).to.be.lte(+openpgp.util.normalizeDate());
|
||||||
expect(+verified.signatures[0].signature.packets[0].created).to.be.gte(+start);
|
expect(+verified.signatures[0].signature.packets[0].created).to.be.gte(+start);
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
|
@ -1980,7 +1980,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
verifyOpt.signature = signed.signature;
|
verifyOpt.signature = signed.signature;
|
||||||
return openpgp.verify(verifyOpt).then(async function (verified) {
|
return openpgp.verify(verifyOpt).then(async function (verified) {
|
||||||
expect(+verified.signatures[0].signature.packets[0].created).to.equal(+past);
|
expect(+verified.signatures[0].signature.packets[0].created).to.equal(+past);
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
expect(await signOpt.privateKeys[0].getSigningKey(verified.signatures[0].keyid, past))
|
expect(await signOpt.privateKeys[0].getSigningKey(verified.signatures[0].keyid, past))
|
||||||
.to.be.not.null;
|
.to.be.not.null;
|
||||||
|
@ -1990,7 +1990,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
|
||||||
return openpgp.verify(verifyOpt);
|
return openpgp.verify(verifyOpt);
|
||||||
}).then(async function (verified) {
|
}).then(async function (verified) {
|
||||||
expect(+verified.signatures[0].signature.packets[0].created).to.equal(+past);
|
expect(+verified.signatures[0].signature.packets[0].created).to.equal(+past);
|
||||||
expect(verified.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
expect(await signOpt.privateKeys[0].getSigningKey(verified.signatures[0].keyid, null))
|
expect(await signOpt.privateKeys[0].getSigningKey(verified.signatures[0].keyid, null))
|
||||||
.to.be.not.null;
|
.to.be.not.null;
|
||||||
|
|
|
@ -525,7 +525,7 @@ zmuVOdNuWQqxT9Sqa84=
|
||||||
|
|
||||||
return openpgp.verify({ publicKeys:[pubKey], message:csMsg }).then(function(cleartextSig) {
|
return openpgp.verify({ publicKeys:[pubKey], message:csMsg }).then(function(cleartextSig) {
|
||||||
expect(cleartextSig).to.exist;
|
expect(cleartextSig).to.exist;
|
||||||
expect(cleartextSig.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(cleartextSig.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(cleartextSig.signatures).to.have.length(1);
|
expect(cleartextSig.signatures).to.have.length(1);
|
||||||
expect(cleartextSig.signatures[0].valid).to.be.true;
|
expect(cleartextSig.signatures[0].valid).to.be.true;
|
||||||
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
@ -648,7 +648,7 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
|
||||||
|
|
||||||
}).then(function(cleartextSig) {
|
}).then(function(cleartextSig) {
|
||||||
expect(cleartextSig).to.exist;
|
expect(cleartextSig).to.exist;
|
||||||
expect(cleartextSig.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext.replace(/\r/g,'')));
|
expect(cleartextSig.data).to.equal(plaintext.replace(/[ \t\r]+$/mg, ''));
|
||||||
expect(cleartextSig.signatures).to.have.length(1);
|
expect(cleartextSig.signatures).to.have.length(1);
|
||||||
expect(cleartextSig.signatures[0].valid).to.be.true;
|
expect(cleartextSig.signatures[0].valid).to.be.true;
|
||||||
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
@ -688,7 +688,7 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
|
||||||
|
|
||||||
}).then(function(cleartextSig) {
|
}).then(function(cleartextSig) {
|
||||||
expect(cleartextSig).to.exist;
|
expect(cleartextSig).to.exist;
|
||||||
expect(cleartextSig.data).to.equal(openpgp.util.removeTrailingSpaces(plaintext));
|
expect(cleartextSig.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
|
||||||
expect(cleartextSig.signatures).to.have.length(1);
|
expect(cleartextSig.signatures).to.have.length(1);
|
||||||
expect(cleartextSig.signatures[0].valid).to.be.true;
|
expect(cleartextSig.signatures[0].valid).to.be.true;
|
||||||
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
|
|
@ -180,7 +180,7 @@ describe('X25519 Cryptography', function () {
|
||||||
const result = await openpgp.verify({ publicKeys: [pub], message: msg});
|
const result = await openpgp.verify({ publicKeys: [pub], message: msg});
|
||||||
|
|
||||||
expect(result).to.exist;
|
expect(result).to.exist;
|
||||||
expect(result.data).to.equal(openpgp.util.removeTrailingSpaces(randomData));
|
expect(result.data).to.equal(randomData.replace(/[ \t]+$/mg, ''));
|
||||||
expect(result.signatures).to.have.length(1);
|
expect(result.signatures).to.have.length(1);
|
||||||
expect(result.signatures[0].valid).to.be.true;
|
expect(result.signatures[0].valid).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user