Skip to content

Commit

Permalink
add more tests, and code to fix those newly broken tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pmuellr committed Dec 14, 2020
1 parent 50a80cd commit 9e14968
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 29 deletions.
33 changes: 23 additions & 10 deletions x-pack/plugins/actions/server/lib/mustache_renderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { renderMustacheString, renderMustacheObject } from './mustache_renderer';
import { renderMustacheString, renderMustacheObject, Escape } from './mustache_renderer';

const variables = {
a: 1,
Expand All @@ -14,7 +14,9 @@ const variables = {
e: undefined,
f: {
g: 3,
h: null,
},
i: [42, 43, 44],
lt: '<',
gt: '>',
amp: '&',
Expand All @@ -29,15 +31,26 @@ const variables = {

describe('mustache_renderer', () => {
describe('renderMustacheString()', () => {
it('handles basic templating that does not need escaping', () => {
expect(renderMustacheString('', variables, 'none')).toBe('');
expect(renderMustacheString('{{a}}', variables, 'none')).toBe('1');
expect(renderMustacheString('{{b}}', variables, 'none')).toBe('2');
expect(renderMustacheString('{{c}}', variables, 'none')).toBe('false');
expect(renderMustacheString('{{d}}', variables, 'none')).toBe('');
expect(renderMustacheString('{{e}}', variables, 'none')).toBe('');
expect(renderMustacheString('{{f.g}}', variables, 'none')).toBe('3');
});
for (const escapeVal of ['none', 'slack', 'markdown', 'json']) {
const escape = escapeVal as Escape;

it(`handles basic templating that does not need escaping for ${escape}`, () => {
expect(renderMustacheString('', variables, escape)).toBe('');
expect(renderMustacheString('{{a}}', variables, escape)).toBe('1');
expect(renderMustacheString('{{b}}', variables, escape)).toBe('2');
expect(renderMustacheString('{{c}}', variables, escape)).toBe('false');
expect(renderMustacheString('{{d}}', variables, escape)).toBe('');
expect(renderMustacheString('{{e}}', variables, escape)).toBe('');
if (escape === 'markdown') {
expect(renderMustacheString('{{f}}', variables, escape)).toBe('\\[object Object\\]');
} else {
expect(renderMustacheString('{{f}}', variables, escape)).toBe('[object Object]');
}
expect(renderMustacheString('{{f.g}}', variables, escape)).toBe('3');
expect(renderMustacheString('{{f.h}}', variables, escape)).toBe('');
expect(renderMustacheString('{{i}}', variables, escape)).toBe('42,43,44');
});
}

it('handles escape:none with commonly escaped strings', () => {
expect(renderMustacheString('{{lt}}', variables, 'none')).toBe(variables.lt);
Expand Down
43 changes: 24 additions & 19 deletions x-pack/plugins/actions/server/lib/mustache_renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import Mustache from 'mustache';
import { isString, cloneDeepWith } from 'lodash';

type Escape = 'markdown' | 'slack' | 'json' | 'none';
export type Escape = 'markdown' | 'slack' | 'json' | 'none';
type Variables = Record<string, unknown>;

// return a rendered mustache template given the specified variables and escape
Expand Down Expand Up @@ -40,19 +40,20 @@ export function renderMustacheObject<Params>(params: Params, variables: Variable
return (result as unknown) as Params;
}

function getEscape(escape: Escape): (string: string) => string {
function getEscape(escape: Escape): (value: unknown) => string {
if (escape === 'markdown') return escapeMarkdown;
if (escape === 'slack') return escapeSlack;
if (escape === 'json') return escapeJSON;
return escapeNone;
}

function escapeNone(value: string): string {
return value;
function escapeNone(value: unknown): string {
if (value == null) return '';
return `${value}`;
}

// replace with JSON stringified version, removing leading and trailing double quote
function escapeJSON(value: string): string {
function escapeJSON(value: unknown): string {
if (value == null) return '';

const quoted = JSON.stringify(`${value}`);
Expand All @@ -62,28 +63,32 @@ function escapeJSON(value: string): string {

// see: https://api.slack.com/reference/surfaces/formatting
// but in practice, a bit more needs to be escaped, in drastic ways
function escapeSlack(value: string): string {
function escapeSlack(value: unknown): string {
if (value == null) return '';

const valueString = `${value}`;
// if the value contains * or _, escape the whole thing with back tics
if (value.includes('_') || value.includes('*')) {
if (valueString.includes('_') || valueString.includes('*')) {
// replace unescapable back tics with single quote
value = value.replace(/`/g, `'`);
return '`' + value + '`';
return '`' + valueString.replace(/`/g, `'`) + '`';
}

// otherwise, do "standard" escaping
value = value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
// this isn't really standard escaping, but escaping back tics is problematic
.replace(/`/g, `'`);

return value;
return (
valueString
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
// this isn't really standard escaping, but escaping back tics is problematic
.replace(/`/g, `'`)
);
}

// see: https://www.markdownguide.org/basic-syntax/#characters-you-can-escape
function escapeMarkdown(value: string): string {
return value
function escapeMarkdown(value: unknown): string {
if (value == null) return '';

return `${value}`
.replace(/\\/g, '\\\\')
.replace(/`/g, '\\`')
.replace(/\*/g, '\\*')
Expand Down

0 comments on commit 9e14968

Please sign in to comment.