Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions extension/js/common/core/crypto/pgp/pgp-armor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,35 @@ export class PgpArmor {
return !!new RegExp(`${PgpArmor.ARMOR_HEADER_DICT.encryptedMsg.begin}.*${PgpArmor.ARMOR_HEADER_DICT.encryptedMsg.end}`).exec(msg);
}

public static clipIncomplete(text: string): string | undefined {
const match = text?.match(/(-----BEGIN PGP (MESSAGE|SIGNED MESSAGE|SIGNATURE|PUBLIC KEY BLOCK)-----[^]+)/gm);
return match?.length ? match[0] : undefined;
}

public static clip(text: string): string | undefined {
if (text?.includes(PgpArmor.ARMOR_HEADER_DICT.null.begin) && text.includes(String(PgpArmor.ARMOR_HEADER_DICT.null.end))) {
const match = text.match(
/(-----BEGIN PGP (MESSAGE|SIGNED MESSAGE|SIGNATURE|PUBLIC KEY BLOCK)-----[^]+-----END PGP (MESSAGE|SIGNATURE|PUBLIC KEY BLOCK)-----)/gm
);
return match?.length ? match[0] : undefined;
// Prevent processing extremely large strings that could cause performance issues
const maxLength = 10 * 1024 * 1024; // 10MB limit
if (!text || text.length > maxLength) {
return undefined;
}

if (text.includes(PgpArmor.ARMOR_HEADER_DICT.null.begin) && text.includes(String(PgpArmor.ARMOR_HEADER_DICT.null.end))) {
// Define the PGP armor headers we want to match
const pgpHeaders = [
PgpArmor.ARMOR_HEADER_DICT.encryptedMsg,
PgpArmor.ARMOR_HEADER_DICT.signedMsg,
PgpArmor.ARMOR_HEADER_DICT.signature,
PgpArmor.ARMOR_HEADER_DICT.publicKey,
];

// Build regex patterns from headers, escaping special regex characters
const patterns = pgpHeaders.map(header => {
const escapedBegin = header.begin.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// header.end can be string or RegExp, handle both cases
const escapedEnd = typeof header.end === 'string' ? header.end.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') : header.end.source; // If it's already a RegExp, use its source
return `(${escapedBegin}[\\s\\S]*?${escapedEnd})`;
});

// Create regex with alternation, 'm' flag for multiline (no 'g' needed for single match)
const regex = new RegExp(patterns.join('|'), 'm');
const match = text.match(regex);

return match ? match[0] : undefined;
}
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----

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
T9hnbh8WlHmouxmPg+GyCDI=
=DSs3
-----END PGP PRIVATE KEY BLOCK-----
Binary file not shown.
2 changes: 1 addition & 1 deletion test/source/tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2660,7 +2660,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg==
const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct);
const key = {
title: 'unarmored OpenPGP key',
filePath: 'test/samples/openpgp/multialiaseduserexamplecom-0x357B908F62498DF8.key',
filePath: 'test/samples/openpgp/multialiaseduserexamplecom-0x357B908F62498DF8.asc',
armored: null, // eslint-disable-line no-null/no-null
passphrase: '1basic passphrase to use',
longid: null, // eslint-disable-line no-null/no-null
Expand Down
42 changes: 38 additions & 4 deletions test/source/tests/unit-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2755,10 +2755,44 @@ AAAAAAAAAAAAAAAAzzzzzzzzzzzzzzzzzzzzzzzzzzzz.....`)
t.pass();
});

test(`[unit][PgpArmor.clipIncomplete] correctly handles all the cases`, async t => {
expect(PgpArmor.clipIncomplete('')).to.be.an.undefined;
expect(PgpArmor.clipIncomplete('plain text')).to.be.an.undefined;
expect(PgpArmor.clipIncomplete('prefix -----BEGIN PGP MESSAGE-----\n\nexample')).to.equal('-----BEGIN PGP MESSAGE-----\n\nexample');
test(`[unit][PgpArmor.clip] correctly handles all the cases including edge cases`, async t => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great test improvement 👍

// Test empty and plain text
expect(PgpArmor.clip('')).to.be.an.undefined;
expect(PgpArmor.clip('plain text')).to.be.an.undefined;

// Test valid PGP messages
const validMessage = '-----BEGIN PGP MESSAGE-----\n\ntest content\n-----END PGP MESSAGE-----';
expect(PgpArmor.clip(validMessage)).to.equal(validMessage);

// Test with prefix and suffix
const messageWithNoise = 'prefix text ' + validMessage + ' suffix text';
expect(PgpArmor.clip(messageWithNoise)).to.equal(validMessage);

// Test multiple messages (should return first)
const multipleMessages = validMessage + '\n\n' + validMessage;
expect(PgpArmor.clip(multipleMessages)).to.equal(validMessage);

// Test with different block types
const publicKeyBlock = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nkey data\n-----END PGP PUBLIC KEY BLOCK-----';
expect(PgpArmor.clip(publicKeyBlock)).to.equal(publicKeyBlock);

// Test with signed message
const signedMessage = '-----BEGIN PGP SIGNED MESSAGE-----\n\nmessage\n-----BEGIN PGP SIGNATURE-----\nsig\n-----END PGP SIGNATURE-----';
expect(PgpArmor.clip(signedMessage)).to.equal(signedMessage);

// Test with mismatched begin/end (should not match)
const mismatchedMessage = '-----BEGIN PGP MESSAGE-----\n\ntest\n-----END PGP SIGNATURE-----';
expect(PgpArmor.clip(mismatchedMessage)).to.be.an.undefined;

// Test with very large input (over 10MB limit)
const largeInput = 'a'.repeat(11 * 1024 * 1024);
expect(PgpArmor.clip(largeInput)).to.be.an.undefined;

// Test with pathological input that would have caused stack overflow
const pathologicalBase = '-----BEGIN PGP MESSAGE-----\n' + 'a'.repeat(1000);
const pathologicalInput = pathologicalBase + '\n-----END PGP MESSAGE-----';
expect(PgpArmor.clip(pathologicalInput)).to.equal(pathologicalInput);

t.pass();
});

Expand Down
Loading