From 11dc90faa23d2abe7921a9e70b8d7289e5b614bf Mon Sep 17 00:00:00 2001 From: John Redwood Date: Thu, 26 Jun 2025 17:44:41 +1000 Subject: [PATCH 1/7] fix: full contrib for report and length of audit Signed-off-by: John Redwood --- .changeset/three-mammals-move.md | 5 +++ app-config.yaml | 2 ++ plugins/scaffolder-backend/report.api.md | 3 ++ .../src/scaffolder/tasks/TaskWorker.ts | 32 ++++++++++++++++++- .../scaffolder-backend/src/service/router.ts | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 .changeset/three-mammals-move.md diff --git a/.changeset/three-mammals-move.md b/.changeset/three-mammals-move.md new file mode 100644 index 0000000000..646e60f84c --- /dev/null +++ b/.changeset/three-mammals-move.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Implement max length for scaffolder auditor audit logging with default of 256 diff --git a/app-config.yaml b/app-config.yaml index 910a3e4f99..d0189595e4 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -200,6 +200,8 @@ catalog: - allow: [Template] scaffolder: + auditor: + maxLength: 256 # Use to customize default commit author info used when new components are created defaultAuthor: name: Scaffolder diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index 7ece0f84ea..debc9ac60a 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -273,6 +273,7 @@ export type CreateWorkerOptions = { workingDirectory: string; logger: LoggerService; auditor?: AuditorService; + config?: Config; additionalTemplateFilters?: Record; concurrentTasksLimit?: number; additionalTemplateGlobals?: Record; @@ -601,6 +602,8 @@ export class TaskWorker { start(): void; // (undocumented) stop(): Promise; + // (undocumented) + protected truncateParameters(parameters: JsonObject): JsonObject; } // @public @deprecated diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index b0c3166ff6..221121b176 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -29,6 +29,8 @@ import { TemplateActionRegistry } from '../actions'; import { NunjucksWorkflowRunner } from './NunjucksWorkflowRunner'; import { WorkflowRunner } from './types'; import { setTimeout } from 'timers/promises'; +import { JsonObject } from '@backstage/types'; +import { Config } from '@backstage/config'; /** * TaskWorkerOptions @@ -44,6 +46,7 @@ export type TaskWorkerOptions = { permissions?: PermissionEvaluator; logger?: LoggerService; auditor?: AuditorService; + config?: Config; gracefulShutdown?: boolean; }; @@ -59,6 +62,7 @@ export type CreateWorkerOptions = { workingDirectory: string; logger: LoggerService; auditor?: AuditorService; + config?: Config; additionalTemplateFilters?: Record; /** * The number of tasks that can be executed at the same time by the worker @@ -87,12 +91,14 @@ export class TaskWorker { private taskQueue: PQueue; private logger: LoggerService | undefined; private auditor: AuditorService | undefined; + private config: Config | undefined; private stopWorkers: boolean; private constructor(private readonly options: TaskWorkerOptions) { this.stopWorkers = false; this.logger = options.logger; this.auditor = options.auditor; + this.config = options.config; this.taskQueue = new PQueue({ concurrency: options.concurrentTasksLimit, }); @@ -103,6 +109,7 @@ export class TaskWorker { taskBroker, logger, auditor, + config, actionRegistry, integrations, workingDirectory, @@ -130,6 +137,7 @@ export class TaskWorker { concurrentTasksLimit, permissions, auditor, + config, gracefulShutdown, }); } @@ -182,6 +190,28 @@ export class TaskWorker { }); } + protected truncateParameters(parameters: JsonObject) { + const auditMaxLength = + this.config?.getOptionalNumber('scaffolder.auditor.maxLength') ?? 256; + const truncatedParameters: JsonObject = {}; + + for (const key in parameters) { + if (Object.prototype.hasOwnProperty.call(parameters, key)) { + const rawValue = parameters[key]; + const value = rawValue?.toString(); + if (value && value.length > auditMaxLength) { + truncatedParameters[key] = value + .slice(0, auditMaxLength) + .concat('...'); + } else { + truncatedParameters[key] = rawValue; + } + } + } + + return truncatedParameters; + } + async runOneTask(task: TaskContext) { const auditorEvent = await this.auditor?.createEvent({ eventId: 'task', @@ -189,7 +219,7 @@ export class TaskWorker { meta: { actionType: 'execution', taskId: task.taskId, - taskParameters: task.spec.parameters, + taskParameters: this.truncateParameters(task.spec.parameters), templateRef: task.spec.templateInfo?.entityRef, }, }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 0ea6293f36..64e7dcb4b4 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -271,6 +271,7 @@ export async function createRouter( integrations, logger, auditor, + config, workingDirectory, concurrentTasksLimit, permissions, From b29c4a8a0744ef2b9c5de5130601410ee73662a5 Mon Sep 17 00:00:00 2001 From: John Redwood Date: Wed, 9 Jul 2025 18:19:24 +1000 Subject: [PATCH 2/7] fix: added ability to disable the audit logger with value set to -1 Signed-off-by: John Redwood --- .../scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index 221121b176..40a56e4368 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -193,6 +193,14 @@ export class TaskWorker { protected truncateParameters(parameters: JsonObject) { const auditMaxLength = this.config?.getOptionalNumber('scaffolder.auditor.maxLength') ?? 256; + + if (auditMaxLength === -1) { + this.logger?.debug( + `scaffolder.auditor.maxLength manually disabled via configuration, no task parameter length limit set.`, + ); + return parameters; + } + const truncatedParameters: JsonObject = {}; for (const key in parameters) { From b90047cfec1a7b205a361862cfe44077ac4982c0 Mon Sep 17 00:00:00 2001 From: John Redwood Date: Wed, 16 Jul 2025 21:05:03 +1000 Subject: [PATCH 3/7] chore: resolve raised changes Signed-off-by: John Redwood --- app-config.yaml | 2 +- plugins/scaffolder-backend/config.d.ts | 15 +++++ .../src/scaffolder/tasks/TaskWorker.test.ts | 57 ++++++++++++++++++- .../src/scaffolder/tasks/TaskWorker.ts | 44 ++++++++------ 4 files changed, 99 insertions(+), 19 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index d0189595e4..59a790d406 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -201,7 +201,7 @@ catalog: scaffolder: auditor: - maxLength: 256 + taskParameterMaxLength: 256 # Use to customize default commit author info used when new components are created defaultAuthor: name: Scaffolder diff --git a/plugins/scaffolder-backend/config.d.ts b/plugins/scaffolder-backend/config.d.ts index ae91c905b3..b4fb532901 100644 --- a/plugins/scaffolder-backend/config.d.ts +++ b/plugins/scaffolder-backend/config.d.ts @@ -93,5 +93,20 @@ export interface Config { * Default value is 24 hours. */ taskTimeout?: HumanDuration | string; + + /** + * Sets the maximum length for task parameters recorded by the auditor. + * + * If set to -1, the limit is disabled and parameters are not truncated. + * Defaults to 256 character length. + * + * @example + * scaffolder: + * auditor: + * taskParameterMaxLength: 512 + */ + auditor?: { + taskParameterMaxLength?: number; + }; }; } diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts index 6fedc4df40..c66b5dbae5 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts @@ -16,7 +16,7 @@ import os from 'os'; import { DatabaseManager } from '@backstage/backend-defaults/database'; -import { ConfigReader } from '@backstage/config'; +import { Config, ConfigReader } from '@backstage/config'; import { DatabaseTaskStore } from './DatabaseTaskStore'; import { StorageTaskBroker } from './StorageTaskBroker'; import { TaskWorker, TaskWorkerOptions } from './TaskWorker'; @@ -343,3 +343,58 @@ describe('TaskWorker internals', () => { expect(inflightTasks.length).toBe(2); }); }); + +describe('TaskWorker.truncateParameters', () => { + let worker: TaskWorker; + + beforeEach(async () => { + jest.resetAllMocks(); + + const logger = { debug: jest.fn() } as any; + + const config = { + getOptionalNumber: jest.fn().mockReturnValue(5), + } as unknown as Config; + + worker = await TaskWorker.create({ + logger, + workingDirectory: '/tmp', + integrations: {} as ScmIntegrations, + taskBroker: {} as TaskBroker, + actionRegistry: {} as TemplateActionRegistry, + config, + }); + }); + + it('successfully does nothing', async () => { + const testParams = {}; + + // @ts-expect-error (truncateParameters is private, but for test we can access) + const result = worker.truncateParameters(testParams); + + expect(result).toEqual({}); + }); + + it('truncates long strings in nested objects and arrays', async () => { + const params = { + test: 'short', + test2: 'thisisaverylongstring', + nested: { + test3: 'anotherlongstringhere', + test4: ['ok', 'toolongstring'], + }, + }; + + // @ts-expect-error (truncateParameters is private, but for test we can access) + const result = worker.truncateParameters(params); + + expect(result).toEqual({ + test: 'short', + test2: 'thisi...', + nested: { + test3: 'anoth...', + test4: ['ok', 'toolo...'], + }, + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index 40a56e4368..119cfaf180 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -190,34 +190,44 @@ export class TaskWorker { }); } - protected truncateParameters(parameters: JsonObject) { - const auditMaxLength = - this.config?.getOptionalNumber('scaffolder.auditor.maxLength') ?? 256; + private truncateParameters(parameters: JsonObject) { + const taskParameterMaxLength = + this.config?.getOptionalNumber( + 'scaffolder.auditor.taskParameterMaxLength', + ) ?? 256; - if (auditMaxLength === -1) { + if (taskParameterMaxLength === -1) { this.logger?.debug( - `scaffolder.auditor.maxLength manually disabled via configuration, no task parameter length limit set.`, + `scaffolder.auditor.taskParameterMaxLength manually disabled via configuration, no task parameter length limit set.`, ); return parameters; } - const truncatedParameters: JsonObject = {}; - - for (const key in parameters) { - if (Object.prototype.hasOwnProperty.call(parameters, key)) { - const rawValue = parameters[key]; - const value = rawValue?.toString(); - if (value && value.length > auditMaxLength) { - truncatedParameters[key] = value - .slice(0, auditMaxLength) + function truncate(value: unknown): unknown { + if (typeof value === 'string') { + if (value.length > taskParameterMaxLength) { + return value + .slice(0, taskParameterMaxLength) .concat('...'); - } else { - truncatedParameters[key] = rawValue; } + return value; } + if (Array.isArray(value)) { + return value.map(truncate); + } + if (value && typeof value === 'object') { + const result: Record = {}; + for (const k in value as object) { + if (Object.hasOwn(value, k)) { + result[k] = truncate((value as any)[k]); + } + } + return result; + } + return value; } - return truncatedParameters; + return truncate(parameters) as JsonObject; } async runOneTask(task: TaskContext) { From c91f267b88b06189c65ed801e195dfd27aa3eeb5 Mon Sep 17 00:00:00 2001 From: John Redwood Date: Thu, 17 Jul 2025 09:12:04 +1000 Subject: [PATCH 4/7] chore: run api-reports Signed-off-by: John Redwood --- plugins/scaffolder-backend/report.api.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index debc9ac60a..d833fd1cd9 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -602,8 +602,6 @@ export class TaskWorker { start(): void; // (undocumented) stop(): Promise; - // (undocumented) - protected truncateParameters(parameters: JsonObject): JsonObject; } // @public @deprecated From 3af9eacf0b40069c4563926cb7bfa3e32112f8ed Mon Sep 17 00:00:00 2001 From: John Redwood Date: Thu, 17 Jul 2025 09:49:22 +1000 Subject: [PATCH 5/7] fix: merge issues Signed-off-by: John Redwood --- plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index 6035a54fdf..9cdab0d58a 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -236,10 +236,9 @@ export class TaskWorker { severityLevel: 'medium', meta: { actionType: 'execution', + createdBy: task.createdBy, taskId: task.taskId, taskParameters: this.truncateParameters(task.spec.parameters), - createdBy: task.createdBy, - taskParameters: task.spec.parameters, templateRef: task.spec.templateInfo?.entityRef, }, }); From 3aeaf2555fb79fd64c207c790d277d2d7bea1788 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 12 Aug 2025 17:04:19 +0200 Subject: [PATCH 6/7] scaffolder-backend: refactor parameter truncation Signed-off-by: Patrik Oldsberg --- .../src/scaffolder/tasks/TaskWorker.test.ts | 111 +++++++++++++----- .../src/scaffolder/tasks/TaskWorker.ts | 107 ++++++++++------- 2 files changed, 143 insertions(+), 75 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts index c66b5dbae5..4b28eb8729 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts @@ -16,10 +16,14 @@ import os from 'os'; import { DatabaseManager } from '@backstage/backend-defaults/database'; -import { Config, ConfigReader } from '@backstage/config'; +import { ConfigReader } from '@backstage/config'; import { DatabaseTaskStore } from './DatabaseTaskStore'; import { StorageTaskBroker } from './StorageTaskBroker'; -import { TaskWorker, TaskWorkerOptions } from './TaskWorker'; +import { + createParameterTruncator, + TaskWorker, + TaskWorkerOptions, +} from './TaskWorker'; import { ScmIntegrations } from '@backstage/integration'; import { TemplateActionRegistry } from '../actions'; import { NunjucksWorkflowRunner } from './NunjucksWorkflowRunner'; @@ -140,6 +144,66 @@ describe('TaskWorker', () => { const event = events.find(e => e.type === 'completion'); expect(event?.body.output).toEqual({ testOutput: 'testmockoutput' }); }); + + it('should log an audit event with task parameters when running a task', async () => { + (workflowRunner.execute as jest.Mock).mockResolvedValue({ + output: {}, + }); + + const auditor = mockServices.auditor.mock(); + const auditEvent = { + success: jest.fn(), + fail: jest.fn(), + }; + auditor.createEvent.mockResolvedValue(auditEvent); + + const broker = new StorageTaskBroker(storage, logger); + const taskWorker = await TaskWorker.create({ + logger, + workingDirectory, + integrations, + taskBroker: broker, + actionRegistry, + auditor, + config: mockServices.rootConfig({ + data: { + scaffolder: { + auditor: { + taskParameterMaxLength: 5, + }, + }, + }, + }), + }); + + await taskWorker.runOneTask({ + spec: { + apiVersion: 'scaffolder.backstage.io/v1beta3', + parameters: { + test: 'thisisaverylongstring', + }, + steps: [], + output: {}, + }, + complete: jest.fn(), + createdBy: 'test-creator', + taskId: 'test-id', + } as unknown as TaskContext); + + expect(auditor.createEvent).toHaveBeenCalledWith({ + eventId: 'task', + severityLevel: 'medium', + meta: { + actionType: 'execution', + createdBy: 'test-creator', + taskId: 'test-id', + taskParameters: { + test: 'thisi...', + }, + }, + }); + expect(auditEvent.success).toHaveBeenCalled(); + }); }); describe('Concurrent TaskWorker', () => { @@ -344,33 +408,11 @@ describe('TaskWorker internals', () => { }); }); -describe('TaskWorker.truncateParameters', () => { - let worker: TaskWorker; - - beforeEach(async () => { - jest.resetAllMocks(); - - const logger = { debug: jest.fn() } as any; - - const config = { - getOptionalNumber: jest.fn().mockReturnValue(5), - } as unknown as Config; - - worker = await TaskWorker.create({ - logger, - workingDirectory: '/tmp', - integrations: {} as ScmIntegrations, - taskBroker: {} as TaskBroker, - actionRegistry: {} as TemplateActionRegistry, - config, - }); - }); - +describe('createParameterTruncator', () => { it('successfully does nothing', async () => { const testParams = {}; - // @ts-expect-error (truncateParameters is private, but for test we can access) - const result = worker.truncateParameters(testParams); + const result = createParameterTruncator()(testParams); expect(result).toEqual({}); }); @@ -381,19 +423,28 @@ describe('TaskWorker.truncateParameters', () => { test2: 'thisisaverylongstring', nested: { test3: 'anotherlongstringhere', - test4: ['ok', 'toolongstring'], + test4: ['ok', 'toolongstring', { prop: 'thisisaverylongstring' }], }, }; - // @ts-expect-error (truncateParameters is private, but for test we can access) - const result = worker.truncateParameters(params); + const result = createParameterTruncator( + mockServices.rootConfig({ + data: { + scaffolder: { + auditor: { + taskParameterMaxLength: 5, + }, + }, + }, + }), + )(params); expect(result).toEqual({ test: 'short', test2: 'thisi...', nested: { test3: 'anoth...', - test4: ['ok', 'toolo...'], + test4: ['ok', 'toolo...', { prop: 'thisi...' }], }, }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index 9cdab0d58a..9abe5f3ba6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -15,7 +15,7 @@ */ import { AuditorService, LoggerService } from '@backstage/backend-plugin-api'; -import { assertError, stringifyError } from '@backstage/errors'; +import { assertError, InputError, stringifyError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { @@ -32,6 +32,8 @@ import { setTimeout } from 'timers/promises'; import { JsonObject } from '@backstage/types'; import { Config } from '@backstage/config'; +const DEFAULT_TASK_PARAMETER_MAX_LENGTH = 256; + /** * TaskWorkerOptions * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. @@ -91,17 +93,21 @@ export class TaskWorker { private taskQueue: PQueue; private logger: LoggerService | undefined; private auditor: AuditorService | undefined; - private config: Config | undefined; + private parameterAuditTransform: ParameterAuditTransform; private stopWorkers: boolean; - private constructor(private readonly options: TaskWorkerOptions) { + private constructor( + private readonly options: TaskWorkerOptions & { + parameterAuditTransform: ParameterAuditTransform; + }, + ) { this.stopWorkers = false; this.logger = options.logger; this.auditor = options.auditor; - this.config = options.config; this.taskQueue = new PQueue({ concurrency: options.concurrentTasksLimit, }); + this.parameterAuditTransform = options.parameterAuditTransform; } static async create(options: CreateWorkerOptions): Promise { @@ -139,6 +145,7 @@ export class TaskWorker { auditor, config, gracefulShutdown, + parameterAuditTransform: createParameterTruncator(config), }); } @@ -190,46 +197,6 @@ export class TaskWorker { }); } - private truncateParameters(parameters: JsonObject) { - const taskParameterMaxLength = - this.config?.getOptionalNumber( - 'scaffolder.auditor.taskParameterMaxLength', - ) ?? 256; - - if (taskParameterMaxLength === -1) { - this.logger?.debug( - `scaffolder.auditor.taskParameterMaxLength manually disabled via configuration, no task parameter length limit set.`, - ); - return parameters; - } - - function truncate(value: unknown): unknown { - if (typeof value === 'string') { - if (value.length > taskParameterMaxLength) { - return value - .slice(0, taskParameterMaxLength) - .concat('...'); - } - return value; - } - if (Array.isArray(value)) { - return value.map(truncate); - } - if (value && typeof value === 'object') { - const result: Record = {}; - for (const k in value as object) { - if (Object.hasOwn(value, k)) { - result[k] = truncate((value as any)[k]); - } - } - return result; - } - return value; - } - - return truncate(parameters) as JsonObject; - } - async runOneTask(task: TaskContext) { const auditorEvent = await this.auditor?.createEvent({ eventId: 'task', @@ -238,7 +205,7 @@ export class TaskWorker { actionType: 'execution', createdBy: task.createdBy, taskId: task.taskId, - taskParameters: this.truncateParameters(task.spec.parameters), + taskParameters: this.parameterAuditTransform(task.spec.parameters), templateRef: task.spec.templateInfo?.entityRef, }, }); @@ -267,3 +234,53 @@ export class TaskWorker { } } } + +type ParameterAuditTransform = (parameters: JsonObject) => JsonObject; + +/** + * Truncates task parameters for audit logging using the configured max length. + * @internal + */ +export function createParameterTruncator( + config?: Config, +): ParameterAuditTransform { + const maxLength = + config?.getOptionalNumber('scaffolder.auditor.taskParameterMaxLength') ?? + DEFAULT_TASK_PARAMETER_MAX_LENGTH; + + if (!Number.isSafeInteger(maxLength) || maxLength < -1) { + throw new InputError( + `Invalid configuration for 'scaffolder.auditor.taskParameterMaxLength', got ${maxLength}. Must be a positive integer or -1 to disable truncation.`, + ); + } + + if (maxLength === -1) { + return (parameters: JsonObject) => parameters; + } + + return (parameters: JsonObject) => { + function truncate(value: unknown): unknown { + if (typeof value === 'string') { + if (value.length > maxLength) { + return value.slice(0, maxLength).concat('...'); + } + return value; + } + if (Array.isArray(value)) { + return value.map(truncate); + } + if (value && typeof value === 'object') { + const result: Record = {}; + for (const k in value as object) { + if (Object.hasOwn(value, k)) { + result[k] = truncate((value as any)[k]); + } + } + return result; + } + return value; + } + + return truncate(parameters) as JsonObject; + }; +} From 7b123a215343c07518128af288471fe2aed3cadf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 12 Aug 2025 17:10:09 +0200 Subject: [PATCH 7/7] scaffolder-backend: add parameter truncation with more configuration values Signed-off-by: Patrik Oldsberg --- .../src/scaffolder/tasks/TaskWorker.test.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts index 4b28eb8729..7fe0618af2 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts @@ -448,4 +448,70 @@ describe('createParameterTruncator', () => { }, }); }); + + it('should not truncate if max length is -1', async () => { + const params = { + test: 'short', + test2: 'thisisaverylongstring', + nested: { + test3: 'anotherlongstringhere', + test4: ['ok', 'toolongstring', { prop: 'thisisaverylongstring' }], + }, + }; + + const result = createParameterTruncator( + mockServices.rootConfig({ + data: { + scaffolder: { + auditor: { + taskParameterMaxLength: -1, + }, + }, + }, + }), + )(params); + + expect(result).toEqual({ + test: 'short', + test2: 'thisisaverylongstring', + nested: { + test3: 'anotherlongstringhere', + test4: ['ok', 'toolongstring', { prop: 'thisisaverylongstring' }], + }, + }); + }); + + it('should throw on invalid max length', async () => { + expect(() => + createParameterTruncator( + mockServices.rootConfig({ + data: { + scaffolder: { + auditor: { + taskParameterMaxLength: -2, + }, + }, + }, + }), + ), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid configuration for 'scaffolder.auditor.taskParameterMaxLength', got -2. Must be a positive integer or -1 to disable truncation."`, + ); + + expect(() => + createParameterTruncator( + mockServices.rootConfig({ + data: { + scaffolder: { + auditor: { + taskParameterMaxLength: 1.5, + }, + }, + }, + }), + ), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid configuration for 'scaffolder.auditor.taskParameterMaxLength', got 1.5. Must be a positive integer or -1 to disable truncation."`, + ); + }); });