mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-29 10:12:34 +00:00
## Changelog
- Added conditional Active Record encryption to every external
credential we store (SMTP/IMAP passwords, Twilio tokens,
Slack/OpenAI hook tokens, Facebook/Instagram tokens, LINE/Telegram keys,
Twitter secrets) so new writes are encrypted
whenever Chatwoot.encryption_configured? is true; legacy installs still
receive plaintext until their secrets are
updated.
- Tuned encryption settings in config/application.rb to allow legacy
reads (support_unencrypted_data) and to extend
deterministic queries so lookups continue to match plaintext rows during
the rollout; added TODOs to retire the
fallback once encryption becomes mandatory.
- Introduced an MFA-pipeline test suite
(spec/models/external_credentials_encryption_spec.rb) plus shared
examples to
verify each attribute encrypts at rest and that plaintext records
re-encrypt on update, with a dedicated Telegram case.
The existing MFA GitHub workflow now runs these tests using the
preconfigured encryption keys.
fixes:
https://linear.app/chatwoot/issue/CW-5453/encrypt-sensitive-credentials-stored-in-plain-text-in-database
## Testing Instructions
1. Instance without encryption keys
- Unset ACTIVE_RECORD_ENCRYPTION_* vars (or run in an environment where
they’re absent).
- Create at least one credentialed channel (e.g., Email SMTP).
- Confirm workflows still function (send/receive mail or a similar
sanity check).
- In the DB you should still see plaintext values—this confirms the
guard prevents encryption when keys are missing.
2. Instance with encryption keys
- Configure the three encryption env vars and restart.
- Pick a couple of representative integrations (e.g., Email SMTP +
Twilio SMS).
- Legacy channel check:
- Use existing records created before enabling keys. Trigger their
workflow (send an email / SMS, or hit the
webhook) to ensure they still authenticate.
- Inspect the raw column—value remains plaintext until changed.
- Update legacy channel:
- Edit one legacy channel’s credential (e.g., change SMTP password).
- Verify the operation still works and the stored value is now encrypted
(raw column differs, accessor returns
original).
- New channel creation:
- Create a new channel of the same type; confirm functionality and that
the stored credential is encrypted from
the start.
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
101 lines
3.2 KiB
YAML
101 lines
3.2 KiB
YAML
name: Run MFA Tests
|
|
permissions:
|
|
contents: read
|
|
|
|
on:
|
|
pull_request:
|
|
|
|
# If two pushes happen within a short time in the same PR, cancel the run of the oldest push
|
|
concurrency:
|
|
group: pr-${{ github.workflow }}-${{ github.head_ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-22.04
|
|
# Only run if MFA test keys are available
|
|
if: github.event_name == 'workflow_dispatch' || (github.repository == 'chatwoot/chatwoot' && github.actor != 'dependabot[bot]')
|
|
|
|
services:
|
|
postgres:
|
|
image: pgvector/pgvector:pg15
|
|
env:
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: ''
|
|
POSTGRES_DB: postgres
|
|
POSTGRES_HOST_AUTH_METHOD: trust
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--mount type=tmpfs,destination=/var/lib/postgresql/data
|
|
--health-cmd pg_isready
|
|
--health-interval 10s
|
|
--health-timeout 5s
|
|
--health-retries 5
|
|
redis:
|
|
image: redis
|
|
ports:
|
|
- 6379:6379
|
|
options: --entrypoint redis-server
|
|
|
|
env:
|
|
RAILS_ENV: test
|
|
POSTGRES_HOST: localhost
|
|
# Active Record encryption keys required for MFA - test keys only, not for production use
|
|
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: 'test_key_a6cde8f7b9c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7'
|
|
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: 'test_key_b7def9a8c0d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d8'
|
|
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: 'test_salt_c8efa0b9d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d9'
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: ruby/setup-ruby@v1
|
|
with:
|
|
bundler-cache: true
|
|
|
|
- name: Create database
|
|
run: bundle exec rake db:create
|
|
|
|
- name: Install pgvector extension
|
|
run: |
|
|
PGPASSWORD="" psql -h localhost -U postgres -d chatwoot_test -c "CREATE EXTENSION IF NOT EXISTS vector;"
|
|
|
|
- name: Seed database
|
|
run: bundle exec rake db:schema:load
|
|
|
|
- name: Run MFA-related backend tests
|
|
run: |
|
|
bundle exec rspec \
|
|
spec/services/mfa/token_service_spec.rb \
|
|
spec/services/mfa/authentication_service_spec.rb \
|
|
spec/requests/api/v1/profile/mfa_controller_spec.rb \
|
|
spec/controllers/devise_overrides/sessions_controller_spec.rb \
|
|
spec/models/application_record_external_credentials_encryption_spec.rb \
|
|
--profile=10 \
|
|
--format documentation
|
|
env:
|
|
NODE_OPTIONS: --openssl-legacy-provider
|
|
|
|
- name: Run MFA-related tests in user_spec
|
|
run: |
|
|
# Run specific MFA-related tests from user_spec
|
|
bundle exec rspec spec/models/user_spec.rb \
|
|
-e "two factor" \
|
|
-e "2FA" \
|
|
-e "MFA" \
|
|
-e "otp" \
|
|
-e "backup code" \
|
|
--profile=10 \
|
|
--format documentation
|
|
env:
|
|
NODE_OPTIONS: --openssl-legacy-provider
|
|
|
|
- name: Upload test logs
|
|
uses: actions/upload-artifact@v4
|
|
if: failure()
|
|
with:
|
|
name: mfa-test-logs
|
|
path: |
|
|
log/test.log
|
|
tmp/screenshots/
|