Skip to content

Commit 2e76ae1

Browse files
committed
fix: return escaped HTML from login failure
Before, we were sending raw HTML from the Express server to the client. This opens an opportunity for cross-site scripting. By escaping first, we remove that opportunity.
1 parent 4a08292 commit 2e76ae1

File tree

4 files changed

+18
-21
lines changed

4 files changed

+18
-21
lines changed

src/node/routes/login.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { RateLimiter as Limiter } from "limiter"
44
import * as path from "path"
55
import { rootPath } from "../constants"
66
import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http"
7-
import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString } from "../util"
7+
import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString, escapeHtml } from "../util"
88

99
export enum Cookie {
1010
Key = "key",
@@ -37,9 +37,6 @@ const getRoot = async (req: Request, error?: Error): Promise<string> => {
3737
passwordMsg = "Password was set from $HASHED_PASSWORD."
3838
}
3939

40-
if (error) {
41-
console.log("there is an error", error.message)
42-
}
4340
return replaceTemplates(
4441
req,
4542
content
@@ -115,6 +112,8 @@ router.post("/", async (req, res) => {
115112

116113
throw new Error("Incorrect password")
117114
} catch (error) {
118-
res.send(await getRoot(req, error))
115+
const html = await getRoot(req, error)
116+
const escapedHtml = escapeHtml(html)
117+
res.send(escapedHtml)
119118
}
120119
})

src/node/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ export const isFile = async (path: string): Promise<boolean> => {
515515
*
516516
* Source: https://stackoverflow.com/a/6234804/3015595
517517
**/
518-
export function escapeHTML(unsafe: string): string {
518+
export function escapeHtml(unsafe: string): string {
519519
return unsafe
520520
.replace(/&/g, "&amp;")
521521
.replace(/</g, "&lt;")

test/unit/node/util.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,9 @@ describe("onLine", () => {
435435
})
436436
})
437437

438-
describe("escapeHTML", () => {
438+
describe("escapeHtml", () => {
439439
it("should escape HTML", () => {
440-
expect(util.escapeHTML(`<div class="error">"Hello & world"</div>`)).toBe(
440+
expect(util.escapeHtml(`<div class="error">"Hello & world"</div>`)).toBe(
441441
"&lt;div class=&quot;error&quot;&gt;&quot;Hello &amp; world&quot;&lt;/div&gt;",
442442
)
443443
})

test/unit/routes/login.test.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe("login", () => {
3737
expect(limiter.removeToken()).toBe(false)
3838
})
3939
})
40-
describe.only("/", () => {
40+
describe("/login", () => {
4141
let _codeServer: httpserver.HttpServer | undefined
4242
function codeServer(): httpserver.HttpServer {
4343
if (!_codeServer) {
@@ -60,20 +60,18 @@ describe("login", () => {
6060
process.env.PASSWORD = previousEnvPassword
6161
})
6262

63-
// TODO@jsjoeio fix this name of test
64-
it("should return an error", async () => {
65-
const resp = await codeServer().fetch("/", { method: "POST" })
66-
// TODO@jsjoeio um not sure why we are getting a 404
67-
expect(resp.status).toBe(404)
63+
it("should return escaped HTML with 'Missing password' message", async () => {
64+
const resp = await codeServer().fetch("/login", { method: "POST" })
6865

69-
try {
70-
const content = JSON.parse(await resp.text())
66+
expect(resp.status).toBe(200)
7167

72-
expect(content.error).toMatch("ENOENT")
73-
} catch (error) {
74-
console.log("heree")
75-
console.error(error)
76-
}
68+
const htmlContent = await resp.text()
69+
70+
expect(htmlContent).not.toContain(">")
71+
expect(htmlContent).not.toContain("<")
72+
expect(htmlContent).not.toContain('"')
73+
expect(htmlContent).not.toContain("'")
74+
expect(htmlContent).toContain("&lt;div class=&quot;error&quot;&gt;Missing password&lt;/div&gt;")
7775
})
7876
})
7977
})

0 commit comments

Comments
 (0)