Skip to content

Commit 1832dc2

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(quickbooks): sanitize bill payment faults
1 parent 688cdab commit 1832dc2

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

apps/sim/tools/quickbooks/create_bill_payment.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { readResponseJsonWithLimit } from '@/lib/core/utils/stream-limits'
22
import { ErrorExtractorId, extractErrorMessage } from '@/tools/error-extractors'
33
import { QUICKBOOKS_MAX_RESPONSE_BYTES } from '@/tools/quickbooks/client'
4+
import { sanitizeQuickBooksFaultData } from '@/tools/quickbooks/fault'
45
import { buildQuickBooksCreateBillPaymentBody } from '@/tools/quickbooks/purchasing_utils'
56
import type {
67
QuickBooksAccount,
@@ -39,7 +40,7 @@ async function getQuickBooksDirectExecutionError(
3940
const errorInfo = {
4041
status: response.status,
4142
statusText: response.statusText,
42-
data,
43+
data: sanitizeQuickBooksFaultData(data),
4344
headers: response.headers,
4445
}
4546
return Object.assign(

apps/sim/tools/quickbooks/purchasing.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,54 @@ describe('QuickBooks BillPayment account compatibility', () => {
593593
)
594594
})
595595

596+
it('allowlists QuickBooks fault data from direct execution errors', async () => {
597+
vi.stubGlobal(
598+
'fetch',
599+
vi.fn().mockResolvedValue(
600+
Response.json(
601+
{
602+
Fault: {
603+
Error: [
604+
{
605+
code: '3200',
606+
Message: 'Authentication failed',
607+
Detail: 'Reconnect the credential',
608+
secret: 'must-not-leak',
609+
},
610+
],
611+
privateMetadata: 'must-not-leak',
612+
},
613+
undocumentedRootField: 'must-not-leak',
614+
},
615+
{ status: 401 }
616+
)
617+
)
618+
)
619+
620+
let caught: unknown
621+
try {
622+
await quickbooksCreateBillPaymentTool.directExecution!(params)
623+
} catch (error) {
624+
caught = error
625+
}
626+
627+
expect(caught).toBeInstanceOf(Error)
628+
expect((caught as Error & { data?: unknown }).data).toEqual({
629+
Fault: {
630+
Error: [
631+
{
632+
code: '3200',
633+
Message: 'Authentication failed',
634+
Detail: 'Reconnect the credential',
635+
},
636+
],
637+
},
638+
})
639+
expect(JSON.stringify((caught as Error & { data?: unknown }).data)).not.toContain(
640+
'must-not-leak'
641+
)
642+
})
643+
596644
it('propagates cancellation and does not create a payment', async () => {
597645
const controller = new AbortController()
598646
const fetchMock = vi.fn().mockImplementationOnce(() => {

0 commit comments

Comments
 (0)