fix: escape formula characters in contact CSV export (#15334)

## Description

Contact export builds its CSV with the standard library `CSV`, which
writes user-supplied values (name, email, phone, custom attributes)
verbatim. When a cell begins with a formula character (`=`, `+`, `-`,
`@`, and tab/CR), spreadsheet applications interpret it as a formula on
open. This switches the export to `CSVSafe` (the `csv-safe` gem already
used by the v2 report exports), which prefixes such fields so they are
treated as text.

No new dependency, no behavioural change beyond neutralising
formula-leading cells. Note: phone numbers stored with a leading `+` are
now prefixed with a single quote in the exported file, consistent with
how the report exports already behave.

Ref https://linear.app/chatwoot/issue/CW-7473

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

`bundle exec rspec spec/jobs/account/contacts_export_job_spec.rb` —
added a case asserting a formula-leading contact value is neutralised on
export; existing cases updated for the phone-number prefix.

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
This commit is contained in:
Vishnu Narayanan
2026-08-13 18:09:38 +05:30
committed by GitHub
parent 8864f80ab7
commit a929955cc3
2 changed files with 15 additions and 2 deletions

View File

@@ -81,7 +81,20 @@ RSpec.describe Account::ContactsExportJob do
expect(csv_data.length).to eq(account.contacts.count)
expect(emails).to include('test1@text.example', 'test2@text.example')
expect(phone_numbers).to include('+910808080818', '+910808080808')
# Phone numbers lead with "+" (a formula character), so they are prefixed with "'" on export.
expect(phone_numbers).to include("'+910808080818", "'+910808080808")
end
it 'neutralises formula-leading characters in exported values' do
create(:contact, account: account, name: '=HYPERLINK("http://evil.example")', email: 'formula@text.example')
described_class.perform_now(account.id, user.id, %w[id name email], {})
csv_content = account.contacts_export.download.force_encoding('UTF-8').delete_prefix("\xEF\xBB\xBF")
csv_data = CSV.parse(csv_content, headers: true)
row = csv_data.find { |r| r['email'] == 'formula@text.example' }
expect(row['name']).to start_with("'")
end
it 'exports labels when requested through column names' do