diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.test.ts index ad99eb8af9ee6..e183d994eff25 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.test.ts @@ -428,6 +428,100 @@ input: logs }); }); + const rerouteConfigVars = { + reroute_config: { + type: 'yaml', + value: ` +- if: + and: + - not.has_fields: _conf.dataset + - regexp.message: "devid=\\"?FG" + then: + - add_fields: + target: '' + fields: + _conf.dataset: "fortinet_fortigate.log" +- if: + and: + - not.has_fields: _conf.dataset + - regexp.message: " CheckPoint [0-9]+ - " + then: + - add_fields: + target: '' + fields: + _conf.dataset: "checkpoint.firewall" + _conf.tz_offset: "UTC" + `, + }, + }; + + it('should handle deeply nested yaml values without producing compact mappings', () => { + const streamTemplate = ` +input: udp +host: "0.0.0.0:9515" +reroute_config: {{reroute_config}} + `; + + const output = compileTemplate(rerouteConfigVars, getMockedMetaVariable(), streamTemplate); + expect(output).toEqual({ + input: 'udp', + host: '0.0.0.0:9515', + reroute_config: [ + { + if: { + and: [{ 'not.has_fields': '_conf.dataset' }, { 'regexp.message': 'devid="?FG' }], + }, + then: [ + { + add_fields: { + target: '', + fields: { '_conf.dataset': 'fortinet_fortigate.log' }, + }, + }, + ], + }, + { + if: { + and: [ + { 'not.has_fields': '_conf.dataset' }, + { 'regexp.message': ' CheckPoint [0-9]+ - ' }, + ], + }, + then: [ + { + add_fields: { + target: '', + fields: { + '_conf.dataset': 'checkpoint.firewall', + '_conf.tz_offset': 'UTC', + }, + }, + }, + ], + }, + ], + }); + }); + + it('should handle root-level yaml variables with values containing double quotes (syslog_router pattern)', () => { + const streamTemplate = ` +input: udp +host: "0.0.0.0:9515" +processors: +- add_locale: ~ +{{#if reroute_config}} +{{reroute_config}} +{{/if}} + `; + + const output = compileTemplate(rerouteConfigVars, getMockedMetaVariable(), streamTemplate); + expect(output.processors).toBeDefined(); + expect(output.processors).toHaveLength(3); + expect(output.processors[0]).toEqual({ add_locale: null }); + expect(output.processors[1].if.and[1]['regexp.message']).toBe('devid="?FG'); + expect(output.processors[2].if.and[1]['regexp.message']).toBe(' CheckPoint [0-9]+ - '); + }); + it('should support $$$$ yaml values at root level', () => { const streamTemplate = ` input: logs diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.ts index fde5b00883cd0..7e025feb4806e 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/agent/agent.ts @@ -113,20 +113,17 @@ export function compileTemplate( throw new PackageInvalidArchiveError(`Error while compiling agent template: ${err.message}`); } - compiledTemplate = replaceRootLevelYamlVariables(yamlValues, compiledTemplate); - - // Normalize multi-line double-quoted YAML scalars. The yaml package (unlike - // js-yaml) rejects literal newlines inside double-quoted strings. Values that - // were JSON-stringified and then had parameters resolved may contain actual - // newlines (doubled by handleMultilineStringFormatter). Apply YAML double-quoted - // folding semantics: N consecutive newlines become N-1 \n escape sequences, - // matching the output that js-yaml.load produced from the multi-line format. + // Must run before replaceRootLevelYamlVariables: stringify output may contain + // unquoted `"` chars (e.g. regexp patterns) that cause this regex to match + // across lines and corrupt the YAML structure. compiledTemplate = compiledTemplate.replace(/"(?:[^"\\]|\\.)*"/gs, (match) => match.includes('\n') ? match.replace(/\n+/g, (newlines) => '\\n'.repeat(newlines.length - 1)) : match ); + compiledTemplate = replaceRootLevelYamlVariables(yamlValues, compiledTemplate); + try { const yamlFromCompiledTemplate = parse(compiledTemplate); @@ -342,7 +339,7 @@ function replaceRootLevelYamlVariables(yamlVariables: { [k: string]: any }, yaml let patchedTemplate = yamlTemplate; Object.entries(yamlVariables).forEach(([key, val]) => { patchedTemplate = patchedTemplate.replace(new RegExp(`^"${key}"`, 'gm'), () => - val ? stringify(val) : '' + val ? stringify(val, { collectionStyle: 'block', lineWidth: 0 }) : '' ); });