diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d9ff0b..9bd398a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.6.0] - 2023-11-18 + +### Changed + +- Ensure that all of the content from snippets files are escaped before being output in the view. + ## [1.5.1] - 2023-11-18 ### Fixed diff --git a/package.json b/package.json index ffb870a..add0955 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "description": "View and edit all your snippets in one purty place. Yee-haw!", "icon": "img/logo.webp", - "version": "1.5.1", + "version": "1.6.0", "engines": { "vscode": "^1.4.0", "node": ">=12.0.0" diff --git a/src/helper/format.js b/src/helper/format.js index e217fdf..58d318a 100644 --- a/src/helper/format.js +++ b/src/helper/format.js @@ -32,17 +32,17 @@ function escapeHtml(text) { } /** - * Escapes all the HTML content in the items of an array and concatenates it into a string - * that can be consumed by a web page. Each item is appended with a HTML line break (BR tag). + * Escapes all the HTML content in the items of an array/string that can be consumed by a web page. + * Each item in the array is appended with a HTML line break (BR tag) and returned as a single string. * @param {string|Array} array Array of values */ -function escapeBody(body) { +function escape(content) { let str = ""; - if (typeof body === "string") { - str = escapeHtml(body); - } else if (Array.isArray(body)) { - body.forEach((element) => { + if (typeof content === "string") { + str = escapeHtml(content); + } else if (Array.isArray(content)) { + content.forEach((element) => { str += escapeHtml(element); str += "
"; }); @@ -82,7 +82,7 @@ function slugify(text) { module.exports = { capitalize, escapeHtml, - escapeBody, + escape, convertToArray, slugify, }; diff --git a/src/model/snippet.js b/src/model/snippet.js index 6984e83..2f0fde5 100644 --- a/src/model/snippet.js +++ b/src/model/snippet.js @@ -1,5 +1,4 @@ const format = require("../helper/format"); -const util = require("../helper/util"); /** * The body can be a string or an array. This formats it to be an array internally always. @@ -64,7 +63,6 @@ function Snippet( let formattedPrefix = formatPrefix(prefix); let formattedBody = formatBody(body); let formattedScope = formatScope(scope); - let endOfLineDelimiter = util.getEndOfLineDelimiter(); return { name, diff --git a/src/view/snippets-table.js b/src/view/snippets-table.js index 2b122ef..d13bdf9 100644 --- a/src/view/snippets-table.js +++ b/src/view/snippets-table.js @@ -58,14 +58,14 @@ const createTableBody = (snippetsFile) => { body += ``; body += `${createPrefixList(snippet.prefix)}`; - body += `${snippet.name}`; - body += `${snippet.description}`; + body += `${format.escape(snippet.name)}`; + body += `${format.escape(snippet.description)}`; body += ""; - body += format.escapeBody(snippet.body); + body += format.escape(snippet.body); body += ""; if (snippetsFile.isScoped() === true) { - body += `${snippet.scope}`; + body += `${format.escape(snippet.scope)}`; } body += `${editButton}${deleteButton}`; @@ -83,7 +83,7 @@ function createPrefixList(prefix) { list = ``; diff --git a/src/view/table-of-contents.js b/src/view/table-of-contents.js index ad8861f..1b8b652 100644 --- a/src/view/table-of-contents.js +++ b/src/view/table-of-contents.js @@ -55,7 +55,7 @@ const createSnippetsFileEntry = (snippetsFile) => { }; const createExtensionEntry = (extensions) => { - let html = ""; + let html = `
  • Extension Snippets`; let entries = ""; extensions.forEach((extension) => { @@ -70,9 +70,11 @@ const createExtensionEntry = (extensions) => { }); if (entries !== "") { - html += `
  • Extension Snippets
  • `; + html += ``; } + html += ``; + return html; }; diff --git a/test/suite/helper/format.test.js b/test/suite/helper/format.test.js index 7527474..4e41bf9 100644 --- a/test/suite/helper/format.test.js +++ b/test/suite/helper/format.test.js @@ -80,18 +80,15 @@ describe("format.js", () => { }); }); - describe("escapeBody()", () => { + describe("escape()", () => { it("should replace html elements with a HTML-safe equivalent for a string", () => { - let body = format.escapeBody(`This is the tag.`); + let body = format.escape(`This is the tag.`); assert.strictEqual(body, "This is the <html> tag."); }); it("should replace html elements with a HTML-safe equivalent for an array", () => { - let text1 = format.escapeBody([ - `This is the tag.`, - `\t let x = 1;`, - ]); + let text1 = format.escape([`This is the tag.`, `\t let x = 1;`]); assert.strictEqual( text1, diff --git a/test/suite/view/snippets-table.test.js b/test/suite/view/snippets-table.test.js index 4f0bd8d..cde5902 100644 --- a/test/suite/view/snippets-table.test.js +++ b/test/suite/view/snippets-table.test.js @@ -68,5 +68,25 @@ describe("snippets-table.js", () => { assert.strictEqual(table.startsWith(expected), true); }); + + it("should create a table when a snippet contains reserved HTML characters", () => { + let snippet = { + name: "test", + prefix: ["", "test"], + body: ["This is "], + description: "Testing
    ", + scope: "", + }; + + let snippetsFile = new SnippetsFile("/somepath/a.code-snippets", [ + snippet, + ]); + + let table = createSnippetsTable(snippetsFile); + + let expected = `
    • <html>
    • test
    testTesting <div>This is <i>
    <bogus>`; + + assert.strictEqual(table.includes(expected), true); + }); }); }); diff --git a/test/suite/view/table-of-contents.test.js b/test/suite/view/table-of-contents.test.js index d62e668..6edbc79 100644 --- a/test/suite/view/table-of-contents.test.js +++ b/test/suite/view/table-of-contents.test.js @@ -7,10 +7,14 @@ const Extension = require("../../../src/model/extension"); describe("table-of-contents.js", () => { describe("createTableOfContents()", () => { - it("should create an empty table if an empty outline is provided", () => { + it("should create a table with category entries", () => { let toc = createTableOfContents(); // OR createTableOfContents([], [], [], []); - assert.strictEqual(toc, ""); + let expected = `

    Table of Contents

    `; + + assert.strictEqual(toc, expected); }); it("should create a table with an entry for a collection of snippets files", () => { @@ -24,14 +28,16 @@ describe("table-of-contents.js", () => { [] ); - let expectedOutput = `

    Table of Contents

      `; - expectedOutput += `
    • Project Snippets
        `; - expectedOutput += `
      • a
      • `; - expectedOutput += `
      • b
      • `; - expectedOutput += `
    • `; - expectedOutput += `
    `; + let expected = `

    Table of Contents

    `; - assert.strictEqual(toc, expectedOutput); + assert.strictEqual(toc, expected); }); it("should create a table with an entry for an extension and its snippets files", () => { @@ -46,15 +52,18 @@ describe("table-of-contents.js", () => { let toc = createTableOfContents([], [], [], [extension]); - let expectedOutput = `

    Table of Contents

      `; - expectedOutput += `
    • Extension Snippets
        `; - expectedOutput += `
      • Snippets Ranger
          `; - expectedOutput += `
        • a
        • `; - expectedOutput += `
        • b
        • `; - expectedOutput += `
      `; - expectedOutput += `
    `; + let expected = `

    Table of Contents

    `; - assert.strictEqual(toc, expectedOutput); + assert.strictEqual(toc, expected); }); }); }); diff --git a/todo.md b/todo.md index e9dd752..af22b6e 100644 --- a/todo.md +++ b/todo.md @@ -1,8 +1,5 @@ # To Do -1. Can I make the extension more resilient? What about funky data? -1. This snippet extension - https://open-vsx.org/extension/hollowtree/vue-snippets - appears to cause an error. Why? - ## Changes to consider 1. Support for multi-root workspaces. For multi-root workspaces, a sub workspace can have its own snippets in its *.vscode* folder. You would need look in the `.code-workspace` to discover the sub workspaces and inspect them.