Skip to content

Commit a399ee8

Browse files
committed
Ignore non-writeable HOME (microsoft/vscode-remote-release#10707)
1 parent 1769aed commit a399ee8

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

src/spec-common/injectHeadless.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export async function getContainerProperties(options: {
244244
params.output.write(toWarningText(`User ${containerUser} not found with 'getent passwd'.`));
245245
}
246246
const shell = await getUserShell(containerEnv, passwdUser);
247-
const homeFolder = await getHomeFolder(containerEnv, passwdUser);
247+
const homeFolder = await getHomeFolder(shellServer, containerEnv, passwdUser);
248248
const userDataFolder = getUserDataFolder(homeFolder, params);
249249
let rootShellServerP: Promise<ShellServer> | undefined;
250250
if (rootShellServer) {
@@ -278,8 +278,19 @@ export async function getUser(shellServer: ShellServer) {
278278
return (await shellServer.exec('id -un')).stdout.trim();
279279
}
280280

281-
export async function getHomeFolder(containerEnv: NodeJS.ProcessEnv, passwdUser: PasswdUser | undefined) {
282-
return containerEnv.HOME || (passwdUser && passwdUser.home) || '/root';
281+
export async function getHomeFolder(shellServer: ShellServer, containerEnv: NodeJS.ProcessEnv, passwdUser: PasswdUser | undefined) {
282+
if (containerEnv.HOME) {
283+
if (containerEnv.HOME === passwdUser?.home || passwdUser?.uid === '0') {
284+
return containerEnv.HOME;
285+
}
286+
try {
287+
await shellServer.exec(`[ ! -e '${containerEnv.HOME}' ] || [ -w '${containerEnv.HOME}' ]`);
288+
return containerEnv.HOME;
289+
} catch {
290+
// Exists but not writable.
291+
}
292+
}
293+
return passwdUser?.home || '/root';
283294
}
284295

285296
async function getUserShell(containerEnv: NodeJS.ProcessEnv, passwdUser: PasswdUser | undefined) {

src/test/getHomeFolder.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as assert from 'assert';
7+
import { shellExec, output } from './testUtils';
8+
import { dockerExecFunction } from '../spec-shutdown/dockerUtils';
9+
import { plainExec } from '../spec-common/commonUtils';
10+
import { launch } from '../spec-common/shellServer';
11+
import { getHomeFolder, getUserFromPasswdDB } from '../spec-common/injectHeadless';
12+
13+
describe('getHomeFolder', function () {
14+
this.timeout('20s');
15+
16+
it(`should ignore non-writeable HOME`, async () => {
17+
const res = await shellExec(`docker run -d mcr.microsoft.com/devcontainers/base:latest sleep inf`);
18+
const containerId = res.stdout.trim();
19+
20+
const vscodeShellServer = await launchShellServer(containerId, 'vscode');
21+
const vscodeUser = await getUserFromPasswdDB(vscodeShellServer, 'vscode');
22+
assert.ok(vscodeUser);
23+
24+
assert.strictEqual(await getHomeFolder(vscodeShellServer, {}, vscodeUser), '/home/vscode');
25+
assert.strictEqual(await getHomeFolder(vscodeShellServer, { HOME: '/root' }, vscodeUser), '/home/vscode');
26+
assert.strictEqual(await getHomeFolder(vscodeShellServer, { HOME: '/home/vscode' }, vscodeUser), '/home/vscode');
27+
assert.strictEqual(await getHomeFolder(vscodeShellServer, { HOME: '/home/vscode/foo' }, vscodeUser), '/home/vscode/foo');
28+
29+
const rootServer = await launchShellServer(containerId, 'root');
30+
const rootUser = await getUserFromPasswdDB(rootServer, 'root');
31+
assert.ok(rootUser);
32+
33+
assert.strictEqual(await getHomeFolder(rootServer, {}, rootUser), '/root');
34+
assert.strictEqual(await getHomeFolder(rootServer, { HOME: '/home/vscode' }, rootUser), '/home/vscode');
35+
});
36+
37+
async function launchShellServer(containerId: string, username: string) {
38+
const exec = dockerExecFunction({
39+
exec: plainExec(undefined),
40+
cmd: 'docker',
41+
env: {},
42+
output,
43+
}, containerId, username);
44+
return launch(exec, output);
45+
}
46+
});

0 commit comments

Comments
 (0)