fix(Databricks AI Agent Node): Scrub MLflow trace request and response previews (#41247)

This commit is contained in:
Yen Su 2026-10-21 08:50:00 +00:00 committed by GitHub
parent 953e15459f
commit bed2ab9210
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 17 additions and 2 deletions

View File

@ -355,4 +355,16 @@ describe('previews', () => {
expect(info.response_preview).toContain('25 days');
});
it('removes credential material from the plain-text previews, even across the cap', () => {
const token = ['dapi', '0123456789abcdef0123456789abcdef'].join('');
// The token straddles the 1,000-character cut, so a slice before the scrub
// would leave a partial token in the preview.
const withToken = { ...trace(), requestPreview: `${'x'.repeat(989)} ${token}` };
const info = buildTraceInfo(withToken, '42');
expect(info.request_preview).toContain('[REDACTED]');
expect(info.request_preview).not.toContain(token);
});
});

View File

@ -1,5 +1,6 @@
import { isRecord } from '@n8n/utils/is-record';
import { sanitizeErrorDetail } from '@n8n/utils/redaction/sanitize-error-detail';
import { scrubSecretsInText } from '@n8n/utils/scrub-secrets';
import { OperationalError, UnexpectedError, UserError, type IDataObject } from 'n8n-workflow';
import type { CollectedTrace, MlflowSpan } from './types';
@ -140,14 +141,16 @@ export async function ensureExperiment(
/**
* The previews are the two columns of the trace list, so they carry the question
* and the answer as plain text. The serialized span attribute is the fallback
* for a run that ended before the agent produced either.
* for a run that ended before the agent produced either. The plain text is raw
* chain input, so it is scrubbed here, before the cut, so a token that straddles
* the limit cannot survive as a partial match.
*/
function preview(
text: string | undefined,
span: MlflowSpan | undefined,
attribute: string,
): string {
return (text ?? span?.attributes[attribute] ?? '').slice(0, MAX_PREVIEW_CHARS);
return scrubSecretsInText(text ?? span?.attributes[attribute] ?? '').slice(0, MAX_PREVIEW_CHARS);
}
/**