mirror of
https://github.com/coder/code-server.git
synced 2026-11-02 22:09:11 +01:00
Compare commits
5 Commits
a1acc25dc3
...
v4.141.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
279b5702aa | ||
|
|
27baccf843 | ||
|
|
8aab1b0d72 | ||
|
|
777c493a9b | ||
|
|
f07405cba3 |
@@ -1 +1 @@
|
||||
24.21.0
|
||||
24.22.1
|
||||
|
||||
@@ -22,6 +22,13 @@ Code v99.99.999
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Security
|
||||
|
||||
- Update bundled Node.js to 24.22.1 to fix a remote code execution bug in the
|
||||
Brotli decoder (CVE-2026-111497).
|
||||
- Reject compressed request bodies (`Content-Encoding: br`, `gzip`, or
|
||||
`deflate`) instead of decompressing them.
|
||||
|
||||
## [4.141.0](https://github.com/coder/code-server/releases/tag/v4.141.0) - 2026-10-08
|
||||
|
||||
Code v1.141.0
|
||||
|
||||
33
patches/node-version.diff
Normal file
33
patches/node-version.diff
Normal file
@@ -0,0 +1,33 @@
|
||||
Bump the bundled Node.js version
|
||||
|
||||
Pull in Node.js security fixes (Brotli decoder RCE) ahead of upstream. Drop
|
||||
this patch once upstream targets this version or newer.
|
||||
|
||||
Index: code-server/lib/vscode/remote/.npmrc
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/remote/.npmrc
|
||||
+++ code-server/lib/vscode/remote/.npmrc
|
||||
@@ -1,5 +1,5 @@
|
||||
disturl="https://nodejs.org/dist"
|
||||
-target="24.21.0"
|
||||
+target="24.22.1"
|
||||
ms_build_id="474716"
|
||||
runtime="node"
|
||||
build_from_source="true"
|
||||
Index: code-server/lib/vscode/build/checksums/nodejs.txt
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/build/checksums/nodejs.txt
|
||||
+++ code-server/lib/vscode/build/checksums/nodejs.txt
|
||||
@@ -1,6 +1,6 @@
|
||||
-bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057 node-v24.21.0-darwin-arm64.tar.gz
|
||||
-1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097 node-v24.21.0-darwin-x64.tar.gz
|
||||
-724282c3b43aec998aa9527380465b45d229e021b58035f5f4f63095eabfe5d5 node-v24.21.0-linux-arm64.tar.gz
|
||||
-6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff node-v24.21.0-linux-x64.tar.gz
|
||||
-dff59da18b6ffe1bf1ca99e1d2af4906080c481740619f5b5098c0fca28bd9b7 win-arm64/node.exe
|
||||
-ba4e6d110e8c1592a1ecd390f6b05f3da124b13871a5be62b341a07a853c6c32 win-x64/node.exe
|
||||
+89c682593d76cd44233fa53b1563264e7de9cbb189cad169135c5b4fadfbc0a7 node-v24.22.1-darwin-arm64.tar.gz
|
||||
+d132557e7ee2b85af08b0c8c34338e65c6430f2238d01f727728ef42dc83b035 node-v24.22.1-darwin-x64.tar.gz
|
||||
+33c37be234ba8bee041961047b19160f4d7d0f164e8e246f78bdd10feff148b3 node-v24.22.1-linux-arm64.tar.gz
|
||||
+eb6728e77c576a35162c122c45ccadf224d89ff00abc8dae46cc2897c6002d7c node-v24.22.1-linux-x64.tar.gz
|
||||
+8bea91dc437fe01e7d92ef0efc6c9eaf654369aaba9b2d3e9841552ea7b95a00 win-arm64/node.exe
|
||||
+d702daa815eabbff763c7d3808e235cf031cf2ca70211b56847bc72f51e240e5 win-x64/node.exe
|
||||
@@ -27,3 +27,4 @@ app-name.diff
|
||||
remote-storage.diff
|
||||
remote-secret-storage.diff
|
||||
csp-hashes.diff
|
||||
node-version.diff
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as crypto from "crypto"
|
||||
import { promises as fs } from "fs"
|
||||
import { load } from "js-yaml"
|
||||
import * as path from "path"
|
||||
import { generateCertificate, generatePassword, paths, splitOnFirstEquals } from "./util"
|
||||
import { expandTilde, generateCertificate, generatePassword, paths, splitOnFirstEquals } from "./util"
|
||||
import { EditorSessionManagerClient } from "./vscodeSocket"
|
||||
|
||||
export enum Feature {
|
||||
@@ -466,7 +466,7 @@ export const parse = (
|
||||
}
|
||||
|
||||
if (option.path) {
|
||||
value = path.resolve(value)
|
||||
value = path.resolve(expandTilde(value))
|
||||
}
|
||||
|
||||
switch (option.type) {
|
||||
|
||||
@@ -132,8 +132,10 @@ export const register = async (
|
||||
})
|
||||
})
|
||||
|
||||
app.router.use(express.json())
|
||||
app.router.use(express.urlencoded({ extended: true }))
|
||||
// Reject compressed request bodies so unauthenticated requests cannot reach
|
||||
// the zlib/Brotli decoders. Browsers never compress request bodies.
|
||||
app.router.use(express.json({ inflate: false }))
|
||||
app.router.use(express.urlencoded({ extended: true, inflate: false }))
|
||||
|
||||
app.router.use(
|
||||
"/_static",
|
||||
|
||||
@@ -511,3 +511,18 @@ export function splitOnFirstEquals(str: string): [string, string | undefined] {
|
||||
const split = str.split(/=(.+)?/, 2)
|
||||
return [split[0], split[1]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a leading `~` to the home directory. The shell does this for
|
||||
* command-line arguments but values from the config file are not expanded.
|
||||
* `~user` forms are left alone.
|
||||
*/
|
||||
export function expandTilde(p: string): string {
|
||||
if (p === "~") {
|
||||
return os.homedir()
|
||||
}
|
||||
if (p.startsWith("~/") || p.startsWith(`~${path.sep}`)) {
|
||||
return path.join(os.homedir(), p.slice(2))
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export async function makeEditorSessionManagerServer(
|
||||
): Promise<http.Server> {
|
||||
const router = express()
|
||||
|
||||
router.use(express.json())
|
||||
router.use(express.json({ inflate: false }))
|
||||
|
||||
router.get<{}, GetSessionResponse | string | unknown, undefined, { filePath?: string }>(
|
||||
"/session",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Level, logger } from "@coder/logger"
|
||||
import { promises as fs } from "fs"
|
||||
import * as os from "os"
|
||||
import * as path from "path"
|
||||
import {
|
||||
UserProvidedArgs,
|
||||
@@ -575,6 +576,17 @@ describe("parser", () => {
|
||||
"install-extension": ["extension.number1", "extension.number2"],
|
||||
})
|
||||
})
|
||||
it("should expand ~ in path options from the config file", () => {
|
||||
const configContents = `
|
||||
user-data-dir: ~/.config/code-server-data
|
||||
extensions-dir: ~/extensions
|
||||
`
|
||||
expect(parseConfigFile(configContents, "/fake-config-path")).toEqual({
|
||||
config: "/fake-config-path",
|
||||
"user-data-dir": path.join(os.homedir(), ".config/code-server-data"),
|
||||
"extensions-dir": path.join(os.homedir(), "extensions"),
|
||||
})
|
||||
})
|
||||
it("should ignore optional strings set to false", async () => {
|
||||
expect(parse(["--cert=false"])).toEqual({})
|
||||
})
|
||||
|
||||
@@ -75,6 +75,17 @@ describe("login", () => {
|
||||
expect(htmlContent).toContain("Missing password")
|
||||
})
|
||||
|
||||
it("should reject compressed request bodies", async () => {
|
||||
for (const encoding of ["br", "gzip", "deflate"]) {
|
||||
const resp = await codeServer().fetch("/login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded", "Content-Encoding": encoding },
|
||||
body: "password=test",
|
||||
})
|
||||
expect(resp.status).toBe(415)
|
||||
}
|
||||
})
|
||||
|
||||
it("should return HTML with 'Incorrect password' message", async () => {
|
||||
const params = new URLSearchParams()
|
||||
params.append("password", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as cp from "child_process"
|
||||
import { promises as fs } from "fs"
|
||||
import * as os from "os"
|
||||
import * as path from "path"
|
||||
import { generateUuid } from "../../../src/common/util"
|
||||
import { tmpdir } from "../../../src/node/constants"
|
||||
@@ -623,3 +624,18 @@ describe("splitOnFirstEquals", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("expandTilde", () => {
|
||||
it("should expand a bare ~", () => {
|
||||
expect(util.expandTilde("~")).toBe(os.homedir())
|
||||
})
|
||||
it("should expand a leading ~/", () => {
|
||||
expect(util.expandTilde("~/.config/code-server")).toBe(path.join(os.homedir(), ".config/code-server"))
|
||||
})
|
||||
it("should leave other paths alone", () => {
|
||||
expect(util.expandTilde("/tmp/~/data")).toBe("/tmp/~/data")
|
||||
expect(util.expandTilde("relative/~")).toBe("relative/~")
|
||||
expect(util.expandTilde("~user/data")).toBe("~user/data")
|
||||
expect(util.expandTilde("")).toBe("")
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user