Skip to content

Commit cf06b17

Browse files
committed
fix(agiloft): redact credentials on every error path the two routes relay
Review finding. Create redacted both its log line and its response; natural language search redacted only the refusal branch, leaving the non-refusal path logging the raw error object and returning an unredacted message. Both routes now relay the same redacted string to the log and to the caller on every branch. The outermost handler could not redact at all, because the parsed body it would need is scoped to the try it is catching. Both routes now capture the credentials as they are parsed, so that handler can redact too. It is not reachable with upstream text today - it catches auth and contract validation, neither of which talks to Agiloft - but "unreachable today" is the kind of reasoning that stops being true without anyone noticing.
1 parent aa2b24f commit cf06b17

2 files changed

Lines changed: 34 additions & 10 deletions

File tree

apps/sim/app/api/tools/agiloft/create_record/route.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ const AGILOFT_EXCEPTION = /EW[A-Za-z]*Exception/
3535
export const POST = withRouteHandler(async (request: NextRequest) => {
3636
const requestId = generateRequestId()
3737

38+
/**
39+
* Captured for the outer catch, which sits outside the scope the parsed body
40+
* lives in. Anything relayed from there can still carry upstream text, and
41+
* these operations put the credentials on the request itself.
42+
*/
43+
let credentials: { login: string; password: string } | undefined
44+
3845
try {
3946
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
4047

@@ -66,6 +73,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
6673
)
6774
if (!parsed.success) return parsed.response
6875
const params = parsed.data.body
76+
credentials = params
6977

7078
let fieldValues: Record<string, unknown>
7179
try {
@@ -109,7 +117,11 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
109117
} catch (error) {
110118
logger.warn(`[${requestId}] Rejected Agiloft instance URL`, { error })
111119
return NextResponse.json(
112-
{ success: false, output: { id: null, fields: {} }, error: toError(error).message },
120+
{
121+
success: false,
122+
output: { id: null, fields: {} },
123+
error: redactAgiloftSecrets(toError(error).message, params),
124+
},
113125
{ status: 400 }
114126
)
115127
}
@@ -239,8 +251,11 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
239251

240252
return NextResponse.json(result)
241253
} catch (error) {
242-
logger.error(`[${requestId}] Error creating Agiloft record:`, error)
254+
const described = credentials
255+
? redactAgiloftSecrets(toError(error).message, credentials)
256+
: toError(error).message
257+
logger.error(`[${requestId}] Error creating Agiloft record`, { error: described })
243258

244-
return NextResponse.json({ success: false, error: toError(error).message }, { status: 500 })
259+
return NextResponse.json({ success: false, error: described }, { status: 500 })
245260
}
246261
})

apps/sim/app/api/tools/agiloft/nlp_search/route.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ const logger = createLogger('AgiloftNlpSearchAPI')
2222
export const POST = withRouteHandler(async (request: NextRequest) => {
2323
const requestId = generateRequestId()
2424

25+
/**
26+
* Captured for the outer catch, which sits outside the scope the parsed body
27+
* lives in. Anything relayed from there can still carry upstream text, and
28+
* these operations put the credentials on the request itself.
29+
*/
30+
let credentials: { login: string; password: string } | undefined
31+
2532
try {
2633
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
2734

@@ -53,6 +60,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
5360
)
5461
if (!parsed.success) return parsed.response
5562
const params = parsed.data.body
63+
credentials = params
5664

5765
let result: AgiloftNlpSearchResponse
5866
try {
@@ -117,17 +125,18 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
117125
})
118126
}
119127

120-
logger.error(`[${requestId}] Error running Agiloft NLP search:`, error)
121-
return NextResponse.json(
122-
{ success: false, error: redactAgiloftSecrets(toError(error).message, params) },
123-
{ status: 500 }
124-
)
128+
const described = redactAgiloftSecrets(toError(error).message, params)
129+
logger.error(`[${requestId}] Error running Agiloft NLP search`, { error: described })
130+
return NextResponse.json({ success: false, error: described }, { status: 500 })
125131
}
126132

127133
return NextResponse.json(result)
128134
} catch (error) {
129-
logger.error(`[${requestId}] Error running Agiloft NLP search:`, error)
135+
const described = credentials
136+
? redactAgiloftSecrets(toError(error).message, credentials)
137+
: toError(error).message
138+
logger.error(`[${requestId}] Error running Agiloft NLP search`, { error: described })
130139

131-
return NextResponse.json({ success: false, error: toError(error).message }, { status: 500 })
140+
return NextResponse.json({ success: false, error: described }, { status: 500 })
132141
}
133142
})

0 commit comments

Comments
 (0)