fix(security): vault integration secrets instead of plaintext config (R1)
PayPal client_secret (and any secret-named integration field) was stored plaintext in integrations.config, unlike Stripe/door which use the AES-256-GCM vault. A DB read or backup leaked it in cleartext. - lib/secrets/vault.ts: canonical generic storeSecret/readSecret + isSecretConfigField (the proven Stripe vault logic, generalized). - saveIntegration: secret-named fields go to the vault, only a *_ref is kept in config; blank submit preserves the ref (write-only like Stripe); legacy plaintext auto-migrates into the vault on next save. - paypal/sync reads client_secret from the vault, transitional fallback to legacy plaintext until the next save. - Docs updated (API_REFERENCE, ARCHITECTURE 10.3). 502 tests.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { isSecretConfigField } from '@/lib/secrets/vault'
|
||||
|
||||
describe('isSecretConfigField', () => {
|
||||
it('treats credential-named fields as secrets to vault', () => {
|
||||
for (const k of ['client_secret', 'api_key', 'secret_key', 'webhook_secret', 'signing_secret']) {
|
||||
expect(isSecretConfigField(k)).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('leaves non-secret config fields in plaintext config', () => {
|
||||
for (const k of ['client_id', 'sandbox', 'mode', 'publishable_key', 'username']) {
|
||||
expect(isSecretConfigField(k)).toBe(false)
|
||||
}
|
||||
})
|
||||
|
||||
it('never vaults derived flag/ref keys (no recursion on re-save)', () => {
|
||||
expect(isSecretConfigField('client_secret_set')).toBe(false)
|
||||
expect(isSecretConfigField('client_secret_ref')).toBe(false)
|
||||
expect(isSecretConfigField('api_key_ref')).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user