forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexec.ts
More file actions
88 lines (83 loc) · 3.43 KB
/
exec.ts
File metadata and controls
88 lines (83 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as fsapi from 'fs';
import * as path from 'path';
import { getEnvironmentVariable, getOSType, OSType } from './platform';
/**
* Determine the env var to use for the executable search path.
*/
export function getSearchPathEnvVarNames(ostype = getOSType()): ('Path' | 'PATH')[] {
if (ostype === OSType.Windows) {
// On Windows both are supported now.
return ['Path', 'PATH'];
}
return ['PATH'];
}
/**
* Get the OS executable lookup "path" from the appropriate env var.
*/
export function getSearchPathEntries(): string[] {
const envVars = getSearchPathEnvVarNames();
for (const envVar of envVars) {
const value = getEnvironmentVariable(envVar);
if (value !== undefined) {
return parseSearchPathEntries(value);
}
}
// No env var was set.
return [];
}
function parseSearchPathEntries(envVarValue: string): string[] {
return envVarValue
.split(path.delimiter)
.map((entry: string) => entry.trim())
.filter((entry) => entry.length > 0);
}
/**
* Determine if the given file is executable by the current user.
*
* If the file does not exist or has any other problem when accessed
* then `false` is returned. The caller is responsible to determine
* whether or not the file exists.
*
* If it could not be determined if the file is executable (e.g. on
* Windows) then `undefined` is returned. This allows the caller
* to decide what to do.
*/
export async function isValidAndExecutable(filename: string): Promise<boolean | undefined> {
// There are three options when it comes to checking if a file
// is executable: `fs.stat()`, `fs.access()`, and
// `child_process.exec()`. `stat()` requires non-trivial logic
// to deal with user/group/everyone permissions. `exec()` requires
// that we make an attempt to actually execute the file, which is
// beyond the scope of this function (due to potential security
// risks). That leaves `access()`, which is what we use.
try {
// We do not need to check if the file exists. `fs.access()`
// takes care of that for us.
await fsapi.promises.access(filename, fsapi.constants.X_OK);
} catch (err) {
return false;
}
if (getOSType() === OSType.Windows) {
// On Windows a file is determined to be executable through
// its ACLs. However, the FS-related functionality provided
// by node does not check them (currently). This includes both
// `fs.stat()` and `fs.access()` (which we used above). One
// option is to use the "acl" NPM package (or something similar)
// to make the relevant checks. However, we want to avoid
// adding a dependency needlessly. Another option is to fall
// back to checking the filename's suffix (e.g. ".exe"). The
// problem there is that such a check is a bit *too* naive.
// Finally, we could still go the `exec()` route. We'd
// rather not given the concern identified above. Instead,
// it is good enough to return `undefined` and let the
// caller decide what to do about it. That is better
// than returning `true` when we aren't sure.
//
// Note that we still call `fs.access()` on Windows first,
// in case node makes it smarter later.
return undefined;
}
return true;
}