From cd140b5527fe7721ff5a5a9a5c303225e75bee77 Mon Sep 17 00:00:00 2001 From: Matt Irvine Date: Mon, 30 Apr 2018 17:58:39 -0700 Subject: [PATCH] Encode HTML when entered in edit data cells (#1302) * Encode HTML when entered in edit data cells * Use VS Code's string encoding function --- src/sql/parts/connection/common/utils.ts | 13 ------ src/sql/parts/grid/services/sharedServices.ts | 13 +++--- .../grid/services/sharedServices.test.ts | 43 +++++++++++++++++++ 3 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 src/sqltest/parts/grid/services/sharedServices.test.ts diff --git a/src/sql/parts/connection/common/utils.ts b/src/sql/parts/connection/common/utils.ts index 445eac15e7..4a07c63301 100644 --- a/src/sql/parts/connection/common/utils.ts +++ b/src/sql/parts/connection/common/utils.ts @@ -88,19 +88,6 @@ export function parseNumAsTimeString(value: number): string { return tempVal > 0 ? rs + '.' + mss : rs; } -/** - * Converts <, >, &, ", ', and any characters that are outside \u00A0 to numeric HTML entity values - * like { - * (Adapted from http://stackoverflow.com/a/18750001) - * @param str String to convert - * @return String with characters replaced. - */ -export function htmlEntities(str: string): string { - return typeof (str) === 'string' - ? str.replace(/[\u00A0-\u9999<>\&"']/gim, (i) => { return `&#${i.charCodeAt(0)};`; }) - : undefined; -} - export function generateUri(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): string { let prefix = purpose ? uriPrefixes[purpose] : uriPrefixes.default; let uri = generateUriWithPrefix(connection, prefix); diff --git a/src/sql/parts/grid/services/sharedServices.ts b/src/sql/parts/grid/services/sharedServices.ts index 42d96d20de..c02292730a 100644 --- a/src/sql/parts/grid/services/sharedServices.ts +++ b/src/sql/parts/grid/services/sharedServices.ts @@ -3,14 +3,14 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Utils from 'sql/parts/connection/common/utils'; +import * as Strings from 'vs/base/common/strings'; export class DBCellValue { displayValue: string; isNull: boolean; public static isDBCellValue(object: any): boolean { - return (object !== undefined && object.displayValue !== undefined && object.isNull !== undefined); + return (object !== undefined && object.displayValue !== undefined && object.isNull !== undefined); } } @@ -25,7 +25,7 @@ export function hyperLinkFormatter(row: number, cell: any, value: any, columnDef valueToDisplay = 'NULL'; if (!value.isNull) { cellClasses += ' xmlLink'; - valueToDisplay = Utils.htmlEntities(value.displayValue); + valueToDisplay = Strings.escape(value.displayValue); return `${valueToDisplay}`; } else { cellClasses += ' missing-value'; @@ -44,13 +44,12 @@ export function textFormatter(row: number, cell: any, value: any, columnDef: any if (DBCellValue.isDBCellValue(value)) { valueToDisplay = 'NULL'; if (!value.isNull) { - valueToDisplay = Utils.htmlEntities(value.displayValue.replace(/(\r\n|\n|\r)/g, ' ')); + valueToDisplay = Strings.escape(value.displayValue.replace(/(\r\n|\n|\r)/g, ' ')); } else { cellClasses += ' missing-value'; } - } else if (typeof value === 'string'){ - valueToDisplay = value; - + } else if (typeof value === 'string') { + valueToDisplay = Strings.escape(value); } return `${valueToDisplay}`; diff --git a/src/sqltest/parts/grid/services/sharedServices.test.ts b/src/sqltest/parts/grid/services/sharedServices.test.ts new file mode 100644 index 0000000000..ebcee41727 --- /dev/null +++ b/src/sqltest/parts/grid/services/sharedServices.test.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as sqlops from 'sqlops'; +import * as assert from 'assert'; +import * as SharedServices from 'sql/parts/grid/services/sharedServices'; + +const testText = '
test text
'; + +suite('Grid shared services tests', () => { + test('textFormatter should encode HTML when formatting a DBCellValue object', () => { + // If I format a DBCellValue object that contains HTML + let cellValue = new SharedServices.DBCellValue(); + cellValue.displayValue = testText; + cellValue.isNull = false; + let formattedHtml = SharedServices.textFormatter(undefined, undefined, cellValue, undefined, undefined); + + // Then the result is HTML for a span element containing the cell value's display value as plain text + verifyFormattedHtml(formattedHtml, testText); + }); + + test('textFormatter should encode HTML when formatting a string', () => { + // If I format a string that contains HTML + let formattedHtml = SharedServices.textFormatter(undefined, undefined, testText, undefined, undefined); + + // Then the result is HTML for a span element containing the given text as plain text + verifyFormattedHtml(formattedHtml, testText); + }); +}); + +function verifyFormattedHtml(formattedHtml: string, expectedText: string): void { + // Create an element containing the span returned by the format call + let element = document.createElement('div'); + element.innerHTML = formattedHtml; + let spanElement = element.children[0]; + + // Verify that the span element's text, not its innerHTML, matches the expected text + assert.equal(spanElement.textContent, testText); + assert.notEqual(spanElement.innerHTML, testText); +} \ No newline at end of file