Skip to content

Commit

Permalink
v1.6.0 - Ensure that all of the content from snippets files
Browse files Browse the repository at this point in the history
 are escaped before being output in the view
  • Loading branch information
robole committed Nov 18, 2023
1 parent 1bb9a2f commit 0ff3902
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 44 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 8 additions & 8 deletions src/helper/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 += "<br>";
});
Expand Down Expand Up @@ -82,7 +82,7 @@ function slugify(text) {
module.exports = {
capitalize,
escapeHtml,
escapeBody,
escape,
convertToArray,
slugify,
};
2 changes: 0 additions & 2 deletions src/model/snippet.js
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -64,7 +63,6 @@ function Snippet(
let formattedPrefix = formatPrefix(prefix);
let formattedBody = formatBody(body);
let formattedScope = formatScope(scope);
let endOfLineDelimiter = util.getEndOfLineDelimiter();

return {
name,
Expand Down
10 changes: 5 additions & 5 deletions src/view/snippets-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ const createTableBody = (snippetsFile) => {

body += `<tr data-name="${snippet.name}">`;
body += `<td>${createPrefixList(snippet.prefix)}</td>`;
body += `<td>${snippet.name}</td>`;
body += `<td>${snippet.description}</td>`;
body += `<td>${format.escape(snippet.name)}</td>`;
body += `<td>${format.escape(snippet.description)}</td>`;
body += "<td><code>";
body += format.escapeBody(snippet.body);
body += format.escape(snippet.body);
body += "</code></td>";

if (snippetsFile.isScoped() === true) {
body += `<td>${snippet.scope}</td>`;
body += `<td>${format.escape(snippet.scope)}</td>`;
}

body += `<td>${editButton}${deleteButton}</td></tr>`;
Expand All @@ -83,7 +83,7 @@ function createPrefixList(prefix) {
list = `<ul class="prefix-list simple-list">`;

prefix.forEach((item) => {
list += `<li><code>${item}</code></li>`;
list += `<li><code>${format.escape(item)}</code></li>`;
});

list += `</ul>`;
Expand Down
6 changes: 4 additions & 2 deletions src/view/table-of-contents.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const createSnippetsFileEntry = (snippetsFile) => {
};

const createExtensionEntry = (extensions) => {
let html = "";
let html = `<li><a href="#extension">Extension Snippets</a>`;
let entries = "";

extensions.forEach((extension) => {
Expand All @@ -70,9 +70,11 @@ const createExtensionEntry = (extensions) => {
});

if (entries !== "") {
html += `<li><a href="#extension">Extension Snippets</a><ul>${entries}</ul></li>`;
html += `<ul>${entries}</ul>`;
}

html += `</li>`;

return html;
};

Expand Down
9 changes: 3 additions & 6 deletions test/suite/helper/format.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <html> tag.`);
let body = format.escape(`This is the <html> tag.`);

assert.strictEqual(body, "This is the &lt;html&gt; tag.");
});

it("should replace html elements with a HTML-safe equivalent for an array", () => {
let text1 = format.escapeBody([
`This is the <html> tag.`,
`\t let x = 1;`,
]);
let text1 = format.escape([`This is the <html> tag.`, `\t let x = 1;`]);

assert.strictEqual(
text1,
Expand Down
20 changes: 20 additions & 0 deletions test/suite/view/snippets-table.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: ["<html>", "test"],
body: ["This is <i>"],
description: "Testing <div>",
scope: "<bogus>",
};

let snippetsFile = new SnippetsFile("/somepath/a.code-snippets", [
snippet,
]);

let table = createSnippetsTable(snippetsFile);

let expected = `<tr data-name="test"><td><ul class="prefix-list simple-list"><li><code>&lt;html&gt;</code></li><li><code>test</code></li></ul></td><td>test</td><td>Testing &lt;div&gt;</td><td><code>This is &lt;i&gt;<br></code></td><td>&lt;bogus&gt;</td>`;

assert.strictEqual(table.includes(expected), true);
});
});
});
43 changes: 26 additions & 17 deletions test/suite/view/table-of-contents.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `<div id="toc"><h2>Table of Contents</h2><ul><li><a href="#project">Project Snippets</a></li>`;
expected += `<li><a href="#user">User Snippets</a></li><li><a href="#extension">Extension Snippets</a></li>`;
expected += `<li><a href="#app">App Snippets</a></li></ul></div>`;

assert.strictEqual(toc, expected);
});

it("should create a table with an entry for a collection of snippets files", () => {
Expand All @@ -24,14 +28,16 @@ describe("table-of-contents.js", () => {
[]
);

let expectedOutput = `<div id="toc"><h2>Table of Contents</h2><ul>`;
expectedOutput += `<li><a href="#project">Project Snippets</a><ul>`;
expectedOutput += `<li><a href="#somepathacode-snippets">a</a></li>`;
expectedOutput += `<li><a href="#somepathbcode-snippets">b</a></li>`;
expectedOutput += `</ul></li>`;
expectedOutput += `</ul></div>`;
let expected = `<div id="toc"><h2>Table of Contents</h2><ul>`;
expected += `<li><a href="#project">Project Snippets</a><ul>`;
expected += `<li><a href="#somepathacode-snippets">a</a></li>`;
expected += `<li><a href="#somepathbcode-snippets">b</a></li></ul></li>`;
expected += `<li><a href="#user">User Snippets</a></li>`;
expected += `<li><a href="#extension">Extension Snippets</a></li>`;
expected += `<li><a href="#app">App Snippets</a></li>`;
expected += `</ul></div>`;

assert.strictEqual(toc, expectedOutput);
assert.strictEqual(toc, expected);
});

it("should create a table with an entry for an extension and its snippets files", () => {
Expand All @@ -46,15 +52,18 @@ describe("table-of-contents.js", () => {

let toc = createTableOfContents([], [], [], [extension]);

let expectedOutput = `<div id="toc"><h2>Table of Contents</h2><ul>`;
expectedOutput += `<li><a href="#extension">Extension Snippets</a><ul>`;
expectedOutput += `<li><a href="#robole-snippets-ranger">Snippets Ranger</a><ul>`;
expectedOutput += `<li><a href="#somepathacode-snippets">a</a></li>`;
expectedOutput += `<li><a href="#somepathbcode-snippets">b</a></li>`;
expectedOutput += `</ul></li></ul>`;
expectedOutput += `</li></ul></div>`;
let expected = `<div id="toc"><h2>Table of Contents</h2><ul>`;
expected += `<li><a href="#project">Project Snippets</a></li>`;
expected += `<li><a href="#user">User Snippets</a></li>`;
expected += `<li><a href="#extension">Extension Snippets</a><ul>`;
expected += `<li><a href="#robole-snippets-ranger">Snippets Ranger</a><ul>`;
expected += `<li><a href="#somepathacode-snippets">a</a></li>`;
expected += `<li><a href="#somepathbcode-snippets">b</a></li>`;
expected += `</ul></li></ul></li>`;
expected += `<li><a href="#app">App Snippets</a></li>`;
expected += `</ul></div>`;

assert.strictEqual(toc, expectedOutput);
assert.strictEqual(toc, expected);
});
});
});
3 changes: 0 additions & 3 deletions todo.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down

0 comments on commit 0ff3902

Please sign in to comment.