Skip to content

Commit

Permalink
🔒 Added escaping to member export CSV fields
Browse files Browse the repository at this point in the history
fix https://linear.app/tryghost/issue/ENG-805/
refs https://owasp.org/www-community/attacks/CSV_Injection

- it's possible for certain fields in a member CSV export to be executed
  by software that opens the CSVs
- we can protect against this for the user by escaping any forumulae in
  the CSV fields
- papaparse provides this option natively, so it's just a case of
  providing the field to the unparse method
- credits to Harvey Spec (phulelouch) for reporting
  • Loading branch information
daniellockyer committed Apr 3, 2024
1 parent a732164 commit de668e7
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 0 deletions.
1 change: 1 addition & 0 deletions ghost/members-csv/lib/unparse.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const unparse = (rows, columns = DEFAULT_COLUMNS.slice()) => {
});

return papaparse.unparse(mappedRows, {
escapeFormulae: true,
columns
});
};
Expand Down
28 changes: 28 additions & 0 deletions ghost/members-csv/test/unparse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,32 @@ third-member-email@email.com,"banana, avocado"`;
const expected = `email,tiers\r\nmember-email@email.com,Bronze Level`;
assert.equal(result, expected);
});

it('escapes fields starting with CSV injection characters', async function () {
const json = [{
email: 'email@example.com',
name: '=1+2',
note: 'Early supporter'
}];

const result = unparse(json);
assert.ok(result);

const expected = `id,email,name,note,subscribed_to_emails,complimentary_plan,stripe_customer_id,created_at,deleted_at,labels,tiers\r\n,email@example.com,"'=1+2",Early supporter,,,,,,,`;
assert.equal(result, expected);
});

it('escapes fields with CSV injection characters and quotes', async function () {
const json = [{
email: 'email@example.com',
name: `=1+2'" `,
note: 'Early supporter'
}];

const result = unparse(json);
assert.ok(result);

const expected = `id,email,name,note,subscribed_to_emails,complimentary_plan,stripe_customer_id,created_at,deleted_at,labels,tiers\r\n,email@example.com,"'=1+2'"" ",Early supporter,,,,,,,`;
assert.equal(result, expected);
});
});

0 comments on commit de668e7

Please sign in to comment.