Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d20519f

Browse files
author
Katie Horne
committedJul 9, 2021
Merge conflicts
2 parents 4d6bbab + 2a5f5e4 commit d20519f

File tree

20 files changed

+514
-156
lines changed

20 files changed

+514
-156
lines changed
 

‎.github/dependabot.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ updates:
1919
time: "11:00"
2020
ignore:
2121
- dependency-name: "@types/node"
22-
versions: ["15.x", "14.x", "13.x"]
22+
update-types: ["version-update:semver-major"]
2323
- dependency-name: "xdg-basedir"
2424
# 5.0.0 has breaking changes as they switch to named exports
2525
# and convert the module to ESM

‎.github/workflows/ci.yaml

+8-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ on:
1616

1717
jobs:
1818
prebuild:
19-
name: Pre-build checks
19+
name: Pre-Build checks
2020
runs-on: ubuntu-latest
21+
timeout-minutes: 5
2122
env:
2223
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
2324
steps:
@@ -65,6 +66,7 @@ jobs:
6566
name: Run audit-ci
6667
needs: prebuild
6768
runs-on: ubuntu-latest
69+
timeout-minutes: 5
6870
steps:
6971
- name: Checkout repo
7072
uses: actions/checkout@v2
@@ -95,6 +97,7 @@ jobs:
9597
name: Build
9698
needs: prebuild
9799
runs-on: ubuntu-latest
100+
timeout-minutes: 30
98101
steps:
99102
- uses: actions/checkout@v2
100103
with:
@@ -170,6 +173,7 @@ jobs:
170173
name: x86-64 Linux build
171174
needs: build
172175
runs-on: ubuntu-latest
176+
timeout-minutes: 15
173177
container: "centos:7"
174178

175179
steps:
@@ -240,6 +244,7 @@ jobs:
240244
name: Linux ARM64 cross-compile build
241245
needs: build
242246
runs-on: ubuntu-16.04
247+
timeout-minutes: 15
243248
env:
244249
AR: aarch64-linux-gnu-ar
245250
CC: aarch64-linux-gnu-gcc
@@ -293,6 +298,7 @@ jobs:
293298
name: x86-64 macOS build
294299
needs: build
295300
runs-on: macos-latest
301+
timeout-minutes: 15
296302
steps:
297303
- uses: actions/checkout@v2
298304

@@ -333,6 +339,7 @@ jobs:
333339
name: End-to-end tests
334340
needs: package-linux-amd64
335341
runs-on: ubuntu-latest
342+
timeout-minutes: 15
336343
env:
337344
# Since we build code-server we might as well run tests from the release
338345
# since VS Code will load faster due to the bundling.

‎docs/guide.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
- [Using a self-signed certificate](#using-a-self-signed-certificate)
1111
- [External authentication](#external-authentication)
1212
- [HTTPS](#https)
13-
- [Securely accessing development web services](#securely-accessing-development-web-services)
13+
- [Self Signed Certificate](#self-signed-certificate)
1414
- [Using a subdomain](#using-a-subdomain)
1515
- [Using a subpath](#using-a-subpath)
1616
- [Stripping `/proxy/<port>` from the request path](#stripping-proxyport-from-the-request-path)
@@ -202,6 +202,10 @@ At this point, you should be able to access code-server via
202202
At this point, you should be able to access code-server via
203203
`https://mydomain.com`.
204204

205+
> If you set `proxy_set_header Host $host;` in your reverse proxy config, it
206+
> will change the address displayed in the green section of code-server in the
207+
> bottom left to show the correct address.
208+
205209
### Using a self-signed certificate
206210

207211
> Self signed certificates do not work with iPad; see [./ipad.md](./ipad.md) for
@@ -276,7 +280,7 @@ redirect all HTTP requests to HTTPS.
276280
> You can use [Let's Encrypt](https://letsencrypt.org/) to get a TLS certificate
277281
for free.
278282

279-
## Securely accessing development web services
283+
### Self Signed Certificate
280284

281285
If you're working on a web service and want to access it locally, code-server
282286
can proxy to any port using either a subdomain or a subpath, allowing you to

‎docs/install.md

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
33
# Install
44

5+
<<<<<<< HEAD
56
- [Install](#install)
67
- [install.sh](#installsh)
78
- [Detection reference](#detection-reference)
@@ -16,6 +17,22 @@
1617
- [Raspberry Pi](#raspberry-pi)
1718
- [Termux](#termux)
1819
- [Cloud providers](#cloud-providers)
20+
=======
21+
- [Install](#install)
22+
- [install.sh](#installsh)
23+
- [Detection reference](#detection-reference)
24+
- [yarn, npm](#yarn-npm)
25+
- [Standalone releases](#standalone-releases)
26+
- [Debian, Ubuntu](#debian-ubuntu)
27+
- [Fedora, CentOS, RHEL, SUSE](#fedora-centos-rhel-suse)
28+
- [Arch Linux](#arch-linux)
29+
- [macOS](#macos)
30+
- [Docker](#docker)
31+
- [Helm](#helm)
32+
- [Raspberry Pi](#raspberry-pi)
33+
- [Termux](#termux)
34+
- [Cloud providers](#cloud-providers)
35+
>>>>>>> main
1936
2037
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2138

@@ -247,3 +264,4 @@ information.
247264
We maintain [one-click apps and install scripts for cloud
248265
providers](https://github.com/cdr/deploy-code-server) such as DigitalOcean,
249266
Railway, Heroku, and Azure.
267+
[https://github.com/cdr/deploy-code-server](https://github.com/cdr/deploy-code-server)

‎docs/npm.md

+21
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- [Alpine](#alpine)
1010
- [macOS](#macos)
1111
- [FreeBSD](#freebsd)
12+
- [Issues with Node.js after version upgrades](#issues-with-nodejs-after-version-upgrades)
1213

1314
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
1415

@@ -63,3 +64,23 @@ xcode-select --install
6364
pkg install -y git python npm-node14 yarn-node14 pkgconf
6465
pkg install -y libinotify
6566
```
67+
68+
## Issues with Node.js after version upgrades
69+
70+
Occasionally, you may run into issues with Node.js.
71+
72+
If you install code-server using `yarn` or `npm`, and you upgrade your Node.js
73+
version, you may need to reinstall code-server to recompile native modules.
74+
Sometimes, you can get around this by navigating into code-server's `lib/vscode`
75+
directory and running `npm rebuild` to recompile the modules.
76+
77+
A step-by-step example of how you might do this is:
78+
79+
1. Install code-server: `brew install code-server`
80+
2. Navigate into the directory: `cd
81+
/usr/local/Cellar/code-server/<version>/libexec/lib/vscode/`
82+
3. Recompile the native modules: `npm rebuild`
83+
4. Restart code-server
84+
85+
If you need further assistance, post on our [GitHub Discussions
86+
page](https://github.com/cdr/code-server/discussions).

‎install.sh

+15-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set -eu
33

44
# code-server's automatic install script.
5-
# See https://github.com/cdr/code-server/blob/main/docs/install.md
5+
# See https://coder.com/docs/code-server/v3.10.2/install
66

77
usage() {
88
arg0="$0"
@@ -58,7 +58,7 @@ Usage:
5858
- If Homebrew is not installed it will install the latest standalone release
5959
into ~/.local
6060
61-
- For FreeBSD, it will install the npm package with yarn or npm.
61+
- For FreeBSD or Alpine, it will install the npm package with yarn or npm.
6262
6363
- If ran on an architecture with no releases, it will install the
6464
npm package with yarn or npm.
@@ -67,7 +67,7 @@ Usage:
6767
6868
It will cache all downloaded assets into ~/.cache/code-server
6969
70-
More installation docs are at https://github.com/cdr/code-server/blob/main/docs/install.md
70+
More installation docs are at https://coder.com/docs/code-server/v3.10.2/install
7171
EOF
7272
}
7373

@@ -238,6 +238,17 @@ main() {
238238
return
239239
fi
240240

241+
if [ "$OS" = "linux" ] && [ "$(distro)" = "alpine" ]; then
242+
if [ "$METHOD" = standalone ]; then
243+
echoerr "No precompiled releases available for alpine."
244+
echoerr 'Please rerun without the "--method standalone" flag to install from npm.'
245+
exit 1
246+
fi
247+
echoh "No precompiled releases available for alpine."
248+
install_npm
249+
return
250+
fi
251+
241252
CACHE_DIR="$(echo_cache_dir)"
242253

243254
if [ "$METHOD" = standalone ]; then
@@ -419,7 +430,7 @@ install_npm() {
419430
echoh
420431
echoerr "Please install npm or yarn to install code-server!"
421432
echoerr "You will need at least node v12 and a few C dependencies."
422-
echoerr "See the docs https://github.com/cdr/code-server/blob/v3.10.2/docs/install.md#yarn-npm"
433+
echoerr "See the docs https://coder.com/docs/code-server/v3.10.2/install#yarn-npm"
423434
exit 1
424435
}
425436

‎lib/vscode/src/vs/workbench/contrib/files/browser/explorerViewlet.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,15 @@ viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
303303
order: 1
304304
});
305305

306+
// NOTE@coder:
307+
// We use OpenFolderAction.ID instead of commandId
308+
// because for some reason, the command openFileFolder
309+
// does not work as expected and causes the "Open Folder"
310+
// command to not work
311+
// See: https://github.com/cdr/code-server/issues/3457
306312
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
307313
content: localize({ key: 'noFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
308-
"You have not yet opened a folder.\n[Open Folder](command:{0})", commandId),
314+
"You have not yet opened a folder.\n[Open Folder](command:{0})", OpenFolderAction.ID),
309315
when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext)),
310316
group: ViewContentGroups.Open,
311317
order: 1

‎package.json

+1-6
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
"@types/tar-fs": "^2.0.0",
5353
"@types/tar-stream": "^2.1.0",
5454
"@types/ws": "^7.2.6",
55-
"@types/wtfnode": "^0.7.0",
5655
"@typescript-eslint/eslint-plugin": "^4.7.0",
5756
"@typescript-eslint/parser": "^4.7.0",
5857
"audit-ci": "^4.0.0",
@@ -64,15 +63,13 @@
6463
"eslint-import-resolver-alias": "^1.1.2",
6564
"eslint-plugin-import": "^2.18.2",
6665
"eslint-plugin-prettier": "^3.1.0",
67-
"leaked-handles": "^5.2.0",
6866
"prettier": "^2.2.1",
6967
"prettier-plugin-sh": "^0.7.1",
7068
"shellcheck": "^1.0.0",
7169
"stylelint": "^13.0.0",
7270
"stylelint-config-recommended": "^5.0.0",
7371
"ts-node": "^10.0.0",
74-
"typescript": "^4.1.3",
75-
"wtfnode": "^0.9.0"
72+
"typescript": "^4.1.3"
7673
},
7774
"resolutions": {
7875
"normalize-package-data": "^3.0.0",
@@ -95,7 +92,6 @@
9592
"httpolyglot": "^0.1.2",
9693
"js-yaml": "^4.0.0",
9794
"limiter": "^1.1.5",
98-
"node-fetch": "^2.6.1",
9995
"pem": "^1.14.2",
10096
"proxy-agent": "^4.0.0",
10197
"proxy-from-env": "^1.1.0",
@@ -106,7 +102,6 @@
106102
"semver": "^7.1.3",
107103
"split2": "^3.2.2",
108104
"tar-fs": "^2.0.0",
109-
"tar-stream": "^2.2.0",
110105
"ws": "^7.2.0",
111106
"xdg-basedir": "^4.0.0",
112107
"yarn": "^1.22.4"

‎src/browser/pages/vscode.ts

+58-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,64 @@ try {
9595
console.error(error)
9696
}
9797

98+
export function setBodyBackgroundToThemeBackgroundColor(document: Document, localStorage: Storage) {
99+
const errorMsgPrefix = "[vscode]"
100+
101+
if (!document) {
102+
throw new Error(`${errorMsgPrefix} Could not set body background to theme background color. Document is undefined.`)
103+
}
104+
105+
if (!localStorage) {
106+
throw new Error(
107+
`${errorMsgPrefix} Could not set body background to theme background color. localStorage is undefined.`,
108+
)
109+
}
110+
111+
const colorThemeData = localStorage.getItem("colorThemeData")
112+
113+
if (!colorThemeData) {
114+
throw new Error(
115+
`${errorMsgPrefix} Could not set body background to theme background color. Could not find colorThemeData in localStorage.`,
116+
)
117+
}
118+
119+
let _colorThemeData
120+
try {
121+
// We wrap this JSON.parse logic in a try/catch
122+
// because it can throw if the JSON is invalid.
123+
// and instead of throwing a random error
124+
// we can throw our own error, which will be more helpful
125+
// to the end user.
126+
_colorThemeData = JSON.parse(colorThemeData)
127+
} catch {
128+
throw new Error(
129+
`${errorMsgPrefix} Could not set body background to theme background color. Could not parse colorThemeData from localStorage.`,
130+
)
131+
}
132+
133+
const hasColorMapProperty = Object.prototype.hasOwnProperty.call(_colorThemeData, "colorMap")
134+
if (!hasColorMapProperty) {
135+
throw new Error(
136+
`${errorMsgPrefix} Could not set body background to theme background color. colorThemeData is missing colorMap.`,
137+
)
138+
}
139+
140+
const editorBgColor = _colorThemeData.colorMap["editor.background"]
141+
142+
if (!editorBgColor) {
143+
throw new Error(
144+
`${errorMsgPrefix} Could not set body background to theme background color. colorThemeData.colorMap["editor.background"] is undefined.`,
145+
)
146+
}
147+
148+
document.body.style.background = editorBgColor
149+
150+
return null
151+
}
152+
98153
try {
99-
document.body.style.background = JSON.parse(localStorage.getItem("colorThemeData")!).colorMap["editor.background"]
154+
setBodyBackgroundToThemeBackgroundColor(document, localStorage)
100155
} catch (error) {
101-
// Oh well.
156+
console.error("Something went wrong setting the body background to the theme background color.")
157+
console.error(error)
102158
}

‎src/node/http.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { normalize, Options } from "../common/util"
77
import { AuthType, DefaultedArgs } from "./cli"
88
import { commit, rootPath } from "./constants"
99
import { Heart } from "./heart"
10-
import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString } from "./util"
10+
import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString, escapeHtml } from "./util"
1111

1212
declare global {
1313
// eslint-disable-next-line @typescript-eslint/no-namespace
@@ -35,7 +35,7 @@ export const replaceTemplates = <T extends object>(
3535
...extraOpts,
3636
}
3737
return content
38-
.replace(/{{TO}}/g, (typeof req.query.to === "string" && req.query.to) || "/")
38+
.replace(/{{TO}}/g, (typeof req.query.to === "string" && escapeHtml(req.query.to)) || "/")
3939
.replace(/{{BASE}}/g, options.base)
4040
.replace(/{{CS_STATIC_BASE}}/g, options.csStaticBase)
4141
.replace(/"{{OPTIONS}}"/, `'${JSON.stringify(options)}'`)
@@ -100,7 +100,8 @@ export const relativeRoot = (req: express.Request): string => {
100100
}
101101

102102
/**
103-
* Redirect relatively to `/${to}`. Query variables will be preserved.
103+
* Redirect relatively to `/${to}`. Query variables on the current URI will be preserved.
104+
* `to` should be a simple path without any query parameters
104105
* `override` will merge with the existing query (use `undefined` to unset).
105106
*/
106107
export const redirect = (

‎src/node/routes/login.ts

+5-3
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",
@@ -36,11 +36,12 @@ const getRoot = async (req: Request, error?: Error): Promise<string> => {
3636
} else if (req.args.usingEnvHashedPassword) {
3737
passwordMsg = "Password was set from $HASHED_PASSWORD."
3838
}
39+
3940
return replaceTemplates(
4041
req,
4142
content
4243
.replace(/{{PASSWORD_MSG}}/g, passwordMsg)
43-
.replace(/{{ERROR}}/, error ? `<div class="error">${error.message}</div>` : ""),
44+
.replace(/{{ERROR}}/, error ? `<div class="error">${escapeHtml(error.message)}</div>` : ""),
4445
)
4546
}
4647

@@ -111,6 +112,7 @@ router.post("/", async (req, res) => {
111112

112113
throw new Error("Incorrect password")
113114
} catch (error) {
114-
res.send(await getRoot(req, error))
115+
const renderedHtml = await getRoot(req, error)
116+
res.send(renderedHtml)
115117
}
116118
})

‎src/node/util.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,13 @@ export const hash = async (password: string): Promise<string> => {
166166
* Used to verify if the password matches the hash
167167
*/
168168
export const isHashMatch = async (password: string, hash: string) => {
169-
if (password === "" || hash === "") {
169+
if (password === "" || hash === "" || !hash.startsWith("$")) {
170170
return false
171171
}
172172
try {
173173
return await argon2.verify(hash, password)
174174
} catch (error) {
175-
logger.error(error)
176-
return false
175+
throw new Error(error)
177176
}
178177
}
179178

@@ -509,3 +508,17 @@ export const isFile = async (path: string): Promise<boolean> => {
509508
return false
510509
}
511510
}
511+
512+
/**
513+
* Escapes any HTML string special characters, like &, <, >, ", and '.
514+
*
515+
* Source: https://stackoverflow.com/a/6234804/3015595
516+
**/
517+
export function escapeHtml(unsafe: string): string {
518+
return unsafe
519+
.replace(/&/g, "&amp;")
520+
.replace(/</g, "&lt;")
521+
.replace(/>/g, "&gt;")
522+
.replace(/"/g, "&quot;")
523+
.replace(/'/g, "&apos;")
524+
}

‎test/browser/pages/login.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,49 @@ describe("login", () => {
4242
expect(el?.value).toBe("/hello-world")
4343
})
4444
})
45+
describe("there is not an element with id 'base'", () => {
46+
let spy: jest.SpyInstance
47+
48+
beforeAll(() => {
49+
// This is important because we're manually requiring the file
50+
// If you don't call this before all tests
51+
// the module registry from other tests may cause side effects.
52+
jest.resetModuleRegistry()
53+
})
54+
55+
beforeEach(() => {
56+
const dom = new JSDOM()
57+
global.document = dom.window.document
58+
spy = jest.spyOn(document, "getElementById")
59+
60+
const location: LocationLike = {
61+
pathname: "/healthz",
62+
origin: "http://localhost:8080",
63+
}
64+
65+
global.location = location as Location
66+
})
67+
68+
afterEach(() => {
69+
spy.mockClear()
70+
jest.resetModules()
71+
// Reset the global.document
72+
global.document = undefined as any as Document
73+
global.location = undefined as any as Location
74+
})
75+
76+
afterAll(() => {
77+
jest.restoreAllMocks()
78+
})
79+
80+
it("should do nothing", () => {
81+
spy.mockImplementation(() => null)
82+
// Load file
83+
require("../../../src/browser/pages/login")
84+
85+
// It's called once by getOptions in the top of the file
86+
// and then another to get the base element
87+
expect(spy).toHaveBeenCalledTimes(2)
88+
})
89+
})
4590
})

‎test/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"node-fetch": "^2.6.1",
1414
"playwright": "^1.12.1",
1515
"supertest": "^6.1.1",
16-
"ts-jest": "^26.4.4"
16+
"ts-jest": "^26.4.4",
17+
"@types/wtfnode": "^0.7.0",
18+
"wtfnode": "^0.9.0"
1719
}
1820
}

‎test/unit/browser/vscode.test.ts

+118-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
* @jest-environment jsdom
33
*/
44
import { JSDOM } from "jsdom"
5-
import { getNlsConfiguration, nlsConfigElementId } from "../../../src/browser/pages/vscode"
5+
import {
6+
getNlsConfiguration,
7+
nlsConfigElementId,
8+
setBodyBackgroundToThemeBackgroundColor,
9+
} from "../../../src/browser/pages/vscode"
610

711
describe("vscode", () => {
812
describe("getNlsConfiguration", () => {
@@ -58,4 +62,117 @@ describe("vscode", () => {
5862
document.body.removeChild(mockElement)
5963
})
6064
})
65+
describe("setBodyBackgroundToThemeBackgroundColor", () => {
66+
beforeEach(() => {
67+
// We need to set the url in the JSDOM constructor
68+
// to prevent this error "SecurityError: localStorage is not available for opaque origins"
69+
// See: https://github.com/jsdom/jsdom/issues/2304#issuecomment-622314949
70+
const { window } = new JSDOM("", { url: "http://localhost" })
71+
global.document = window.document
72+
global.localStorage = window.localStorage
73+
})
74+
it("should return null", () => {
75+
const test = {
76+
colorMap: {
77+
[`editor.background`]: "#ff3270",
78+
},
79+
}
80+
localStorage.setItem("colorThemeData", JSON.stringify(test))
81+
82+
expect(setBodyBackgroundToThemeBackgroundColor(document, localStorage)).toBeNull()
83+
84+
localStorage.removeItem("colorThemeData")
85+
})
86+
it("should throw an error if Document is undefined", () => {
87+
const errorMsgPrefix = "[vscode]"
88+
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. Document is undefined.`
89+
90+
expect(() => {
91+
// @ts-expect-error We need to test when document is undefined
92+
setBodyBackgroundToThemeBackgroundColor(undefined, localStorage)
93+
}).toThrowError(errorMessage)
94+
})
95+
it("should throw an error if localStorage is undefined", () => {
96+
const errorMsgPrefix = "[vscode]"
97+
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. localStorage is undefined.`
98+
99+
expect(() => {
100+
// @ts-expect-error We need to test when localStorage is undefined
101+
setBodyBackgroundToThemeBackgroundColor(document, undefined)
102+
}).toThrowError(errorMessage)
103+
})
104+
it("should throw an error if it can't find colorThemeData in localStorage", () => {
105+
const errorMsgPrefix = "[vscode]"
106+
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. Could not find colorThemeData in localStorage.`
107+
108+
expect(() => {
109+
setBodyBackgroundToThemeBackgroundColor(document, localStorage)
110+
}).toThrowError(errorMessage)
111+
})
112+
it("should throw an error if there is an error parsing colorThemeData from localStorage", () => {
113+
const errorMsgPrefix = "[vscode]"
114+
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. Could not parse colorThemeData from localStorage.`
115+
116+
localStorage.setItem(
117+
"colorThemeData",
118+
'{"id":"vs-dark max-SS-Cyberpunk-themes-cyberpunk-umbra-color-theme-json","label":"Activate UMBRA protocol","settingsId":"Activate "errorForeground":"#ff3270","foreground":"#ffffff","sideBarTitle.foreground":"#bbbbbb"},"watch\\":::false}',
119+
)
120+
121+
expect(() => {
122+
setBodyBackgroundToThemeBackgroundColor(document, localStorage)
123+
}).toThrowError(errorMessage)
124+
125+
localStorage.removeItem("colorThemeData")
126+
})
127+
it("should throw an error if there is no colorMap property", () => {
128+
const errorMsgPrefix = "[vscode]"
129+
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. colorThemeData is missing colorMap.`
130+
131+
const test = {
132+
id: "hey-joe",
133+
}
134+
localStorage.setItem("colorThemeData", JSON.stringify(test))
135+
136+
expect(() => {
137+
setBodyBackgroundToThemeBackgroundColor(document, localStorage)
138+
}).toThrowError(errorMessage)
139+
140+
localStorage.removeItem("colorThemeData")
141+
})
142+
it("should throw an error if there is no editor.background color", () => {
143+
const errorMsgPrefix = "[vscode]"
144+
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. colorThemeData.colorMap["editor.background"] is undefined.`
145+
146+
const test = {
147+
id: "hey-joe",
148+
colorMap: {
149+
editor: "#fff",
150+
},
151+
}
152+
localStorage.setItem("colorThemeData", JSON.stringify(test))
153+
154+
expect(() => {
155+
setBodyBackgroundToThemeBackgroundColor(document, localStorage)
156+
}).toThrowError(errorMessage)
157+
158+
localStorage.removeItem("colorThemeData")
159+
})
160+
it("should set the body background to the editor background color", () => {
161+
const test = {
162+
colorMap: {
163+
[`editor.background`]: "#ff3270",
164+
},
165+
}
166+
localStorage.setItem("colorThemeData", JSON.stringify(test))
167+
168+
setBodyBackgroundToThemeBackgroundColor(document, localStorage)
169+
170+
// When the body.style.backgroundColor is set using hex
171+
// it is converted to rgb
172+
// which is why we use that in the assertion
173+
expect(document.body.style.backgroundColor).toBe("rgb(255, 50, 112)")
174+
175+
localStorage.removeItem("colorThemeData")
176+
})
177+
})
61178
})

‎test/unit/emitter.test.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe("emitter", () => {
4141

4242
// Register the onHelloWorld listener
4343
// and the onGoodbyeWorld
44-
emitter.event(onHelloWorld)
44+
const _onHelloWorld = emitter.event(onHelloWorld)
4545
emitter.event(onGoodbyeWorld)
4646

4747
await emitter.emit({ event: HELLO_WORLD, callback: mockCallback })
@@ -56,6 +56,12 @@ describe("emitter", () => {
5656
expect(mockSecondCallback).toHaveBeenCalled()
5757
expect(mockSecondCallback).toHaveBeenCalledTimes(1)
5858

59+
// Dispose of individual listener
60+
_onHelloWorld.dispose()
61+
62+
// Try disposing twice
63+
_onHelloWorld.dispose()
64+
5965
// Dispose of all the listeners
6066
emitter.dispose()
6167
})

‎test/unit/node/util.test.ts

+19
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,17 @@ describe("isHashMatch", () => {
189189
const actual = await util.isHashMatch(password, _hash)
190190
expect(actual).toBe(false)
191191
})
192+
it("should return false and not throw an error if the hash doesn't start with a $", async () => {
193+
const password = "hellowpasssword"
194+
const _hash = "n2i$v=19$m=4096,t=3,p=1$EAoczTxVki21JDfIZpTUxg$rkXgyrW4RDGoDYrxBFD4H2DlSMEhP4h+Api1hXnGnFY"
195+
expect(async () => await util.isHashMatch(password, _hash)).not.toThrow()
196+
expect(await util.isHashMatch(password, _hash)).toBe(false)
197+
})
198+
it("should reject the promise and throw if error", async () => {
199+
const password = "hellowpasssword"
200+
const _hash = "$ar2i"
201+
expect(async () => await util.isHashMatch(password, _hash)).rejects.toThrow()
202+
})
192203
})
193204

194205
describe("hashLegacy", () => {
@@ -434,3 +445,11 @@ describe("onLine", () => {
434445
expect(await received).toEqual(expected)
435446
})
436447
})
448+
449+
describe("escapeHtml", () => {
450+
it("should escape HTML", () => {
451+
expect(util.escapeHtml(`<div class="error">"'ello & world"</div>`)).toBe(
452+
"&lt;div class=&quot;error&quot;&gt;&quot;&apos;ello &amp; world&quot;&lt;/div&gt;",
453+
)
454+
})
455+
})

‎test/unit/routes/login.test.ts

+39
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { RateLimiter } from "../../../src/node/routes/login"
2+
import * as httpserver from "../../utils/httpserver"
3+
import * as integration from "../../utils/integration"
24

35
describe("login", () => {
46
describe("RateLimiter", () => {
@@ -34,4 +36,41 @@ describe("login", () => {
3436
expect(limiter.removeToken()).toBe(false)
3537
})
3638
})
39+
describe("/login", () => {
40+
let _codeServer: httpserver.HttpServer | undefined
41+
function codeServer(): httpserver.HttpServer {
42+
if (!_codeServer) {
43+
throw new Error("tried to use code-server before setting it up")
44+
}
45+
return _codeServer
46+
}
47+
48+
// Store whatever might be in here so we can restore it afterward.
49+
// TODO: We should probably pass this as an argument somehow instead of
50+
// manipulating the environment.
51+
const previousEnvPassword = process.env.PASSWORD
52+
53+
beforeEach(async () => {
54+
process.env.PASSWORD = "test"
55+
_codeServer = await integration.setup(["--auth=password"], "")
56+
})
57+
58+
afterEach(async () => {
59+
process.env.PASSWORD = previousEnvPassword
60+
if (_codeServer) {
61+
await _codeServer.close()
62+
_codeServer = undefined
63+
}
64+
})
65+
66+
it("should return HTML with 'Missing password' message", async () => {
67+
const resp = await codeServer().fetch("/login", { method: "POST" })
68+
69+
expect(resp.status).toBe(200)
70+
71+
const htmlContent = await resp.text()
72+
73+
expect(htmlContent).toContain("Missing password")
74+
})
75+
})
3776
})

‎test/yarn.lock

+19-1
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,11 @@
11631163
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"
11641164
integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==
11651165

1166+
"@types/wtfnode@^0.7.0":
1167+
version "0.7.0"
1168+
resolved "https://registry.yarnpkg.com/@types/wtfnode/-/wtfnode-0.7.0.tgz#e5d9c675bccc2f786830ffa40ffe8865f92057d9"
1169+
integrity sha512-kdBHgE9+M1Os7UqWZtiLhKye5reFl8cPBYyCsP2fatwZRz7F7GdIxIHZ20Kkc0hYBfbXE+lzPOTUU1I0qgjtHA==
1170+
11661171
"@types/yargs-parser@*":
11671172
version "20.2.0"
11681173
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
@@ -1651,6 +1656,11 @@ code-point-at@^1.0.0:
16511656
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
16521657
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
16531658

1659+
coffeescript@^2.5.1:
1660+
version "2.5.1"
1661+
resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-2.5.1.tgz#b2442a1f2c806139669534a54adc35010559d16a"
1662+
integrity sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ==
1663+
16541664
collect-v8-coverage@^1.0.0:
16551665
version "1.0.1"
16561666
resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
@@ -4145,7 +4155,7 @@ source-map-support@^0.4.18:
41454155
dependencies:
41464156
source-map "^0.5.6"
41474157

4148-
source-map-support@^0.5.6:
4158+
source-map-support@^0.5.19, source-map-support@^0.5.6:
41494159
version "0.5.19"
41504160
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
41514161
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
@@ -4733,6 +4743,14 @@ ws@^7.2.3, ws@^7.4.6:
47334743
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
47344744
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
47354745

4746+
wtfnode@^0.9.0:
4747+
version "0.9.0"
4748+
resolved "https://registry.yarnpkg.com/wtfnode/-/wtfnode-0.9.0.tgz#f0f880e11309b7120b14fecda39f363f78b66a70"
4749+
integrity sha512-IKHfNAFZwfm0uCt/zuFADN3mHyoB+ZrmwFpRGOxKPIXV0tifqpIaTH3NvImA7yy7GimsAayZGTaNvOmavKzE+A==
4750+
dependencies:
4751+
coffeescript "^2.5.1"
4752+
source-map-support "^0.5.19"
4753+
47364754
xml-name-validator@^3.0.0:
47374755
version "3.0.0"
47384756
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"

‎yarn.lock

+104-126
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.