powershell-utils 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +65 -12
- package/index.js +40 -19
- package/package.json +1 -1
- package/readme.md +69 -13
package/index.d.ts
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
import type {ExecFileOptions} from 'node:child_process';
|
|
1
|
+
import type {ExecFileOptions, ExecFileSyncOptions} from 'node:child_process';
|
|
2
2
|
|
|
3
|
-
export type ExecutePowerShellOptions = ExecFileOptions & {
|
|
3
|
+
export type ExecutePowerShellOptions = Omit<ExecFileOptions, 'shell'> & {
|
|
4
4
|
/**
|
|
5
5
|
Path to PowerShell executable.
|
|
6
6
|
|
|
7
7
|
@default powerShellPath()
|
|
8
8
|
*/
|
|
9
9
|
readonly powerShellPath?: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
Not supported. PowerShell is always executed directly to prevent shell injection.
|
|
13
|
+
*/
|
|
14
|
+
readonly shell?: never;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ExecutePowerShellSyncOptions = Omit<ExecFileSyncOptions, 'shell'> & {
|
|
18
|
+
/**
|
|
19
|
+
Path to PowerShell executable.
|
|
20
|
+
|
|
21
|
+
@default powerShellPath()
|
|
22
|
+
*/
|
|
23
|
+
readonly powerShellPath?: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
Not supported. PowerShell is always executed directly to prevent shell injection.
|
|
27
|
+
*/
|
|
28
|
+
readonly shell?: never;
|
|
10
29
|
};
|
|
11
30
|
|
|
12
31
|
export type ExecutePowerShellResult = {
|
|
@@ -70,17 +89,9 @@ export function executePowerShell(
|
|
|
70
89
|
|
|
71
90
|
export namespace executePowerShell {
|
|
72
91
|
/**
|
|
73
|
-
Standard PowerShell arguments that prefix the encoded command
|
|
92
|
+
Standard PowerShell arguments that prefix the encoded command: `['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand']`
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@example
|
|
78
|
-
```
|
|
79
|
-
import {executePowerShell} from 'powershell-utils';
|
|
80
|
-
|
|
81
|
-
const arguments_ = [...executePowerShell.argumentsPrefix, encodedCommand];
|
|
82
|
-
childProcess.spawn(powerShellPath(), arguments_);
|
|
83
|
-
```
|
|
94
|
+
Exposed for debugging or for advanced use cases where you need to customize the arguments. For most cases, use `createArguments()` instead.
|
|
84
95
|
*/
|
|
85
96
|
export const argumentsPrefix: readonly string[];
|
|
86
97
|
|
|
@@ -104,6 +115,10 @@ export namespace executePowerShell {
|
|
|
104
115
|
/**
|
|
105
116
|
Escape a string argument for use in PowerShell single-quoted strings.
|
|
106
117
|
|
|
118
|
+
This makes a value safe as a PowerShell string literal, not as an argument to a native program. PowerShell removes the outer quotes before passing a value onwards, and the receiving process then re-splits it under its own command-line rules, where spaces and double quotes are delimiters. For example, `Start-Process -ArgumentList` receives `'a b'` as two arguments rather than one.
|
|
119
|
+
|
|
120
|
+
Escaping also does not make an untrusted value safe to use as the program that `Start-Process` runs. Validate such values against an allowlist.
|
|
121
|
+
|
|
107
122
|
@param value - The value to escape.
|
|
108
123
|
@returns Escaped and quoted string ready for PowerShell.
|
|
109
124
|
|
|
@@ -119,4 +134,42 @@ export namespace executePowerShell {
|
|
|
119
134
|
```
|
|
120
135
|
*/
|
|
121
136
|
export function escapeArgument(value: unknown): string;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
Create the full arguments array for PowerShell execution.
|
|
140
|
+
|
|
141
|
+
Combines `argumentsPrefix` with the encoded command. Useful when using `spawn()`, `execFile()`, or other process execution methods.
|
|
142
|
+
|
|
143
|
+
@param command - The PowerShell command.
|
|
144
|
+
@returns Array of arguments ready to pass to a process spawner.
|
|
145
|
+
|
|
146
|
+
@example
|
|
147
|
+
```
|
|
148
|
+
import {spawn} from 'node:child_process';
|
|
149
|
+
import {powerShellPath, executePowerShell} from 'powershell-utils';
|
|
150
|
+
|
|
151
|
+
const args = executePowerShell.createArguments('Get-Process');
|
|
152
|
+
spawn(powerShellPath(), args);
|
|
153
|
+
```
|
|
154
|
+
*/
|
|
155
|
+
export function createArguments(command: string): string[];
|
|
122
156
|
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
Execute a PowerShell command synchronously.
|
|
160
|
+
|
|
161
|
+
@param command - The PowerShell command to execute.
|
|
162
|
+
@returns The stdout output as a string.
|
|
163
|
+
|
|
164
|
+
@example
|
|
165
|
+
```
|
|
166
|
+
import {executePowerShellSync} from 'powershell-utils';
|
|
167
|
+
|
|
168
|
+
const stdout = executePowerShellSync('Get-Process');
|
|
169
|
+
console.log(stdout);
|
|
170
|
+
```
|
|
171
|
+
*/
|
|
172
|
+
export function executePowerShellSync(
|
|
173
|
+
command: string,
|
|
174
|
+
options?: ExecutePowerShellSyncOptions
|
|
175
|
+
): string;
|
package/index.js
CHANGED
|
@@ -24,35 +24,56 @@ export const canAccessPowerShell = async () => {
|
|
|
24
24
|
return canAccessCache;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
const argumentsPrefix = [
|
|
28
|
+
'-NoProfile',
|
|
29
|
+
'-NonInteractive',
|
|
30
|
+
'-ExecutionPolicy',
|
|
31
|
+
'Bypass',
|
|
32
|
+
'-EncodedCommand',
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const encodeCommand = command => Buffer.from(command, 'utf16le').toString('base64');
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
PowerShell's tokenizer treats the typographic single quotes U+2018, U+2019, U+201A, and U+201B as string delimiters, exactly like the ASCII one, and it does not require the closing quote to match the opening quote. Escaping only the ASCII quote would let any of the others terminate the string and inject a statement. Doubling works for all of them.
|
|
39
|
+
*/
|
|
40
|
+
const escapeArgument = value => `'${String(value).replaceAll(/['‘’‚‛]/g, match => match + match)}'`;
|
|
41
|
+
|
|
42
|
+
const createArguments = command => [...argumentsPrefix, encodeCommand(command)];
|
|
43
|
+
|
|
44
|
+
const createExecFileOptions = options => ({
|
|
45
|
+
encoding: 'utf8',
|
|
46
|
+
...options,
|
|
47
|
+
shell: false,
|
|
48
|
+
});
|
|
49
|
+
|
|
27
50
|
export const executePowerShell = async (command, options = {}) => {
|
|
28
51
|
const {
|
|
29
52
|
powerShellPath: psPath,
|
|
30
53
|
...execFileOptions
|
|
31
54
|
} = options;
|
|
32
55
|
|
|
33
|
-
const encodedCommand = executePowerShell.encodeCommand(command);
|
|
34
|
-
|
|
35
56
|
return execFile(
|
|
36
57
|
psPath ?? powerShellPath(),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
encodedCommand,
|
|
40
|
-
],
|
|
41
|
-
{
|
|
42
|
-
encoding: 'utf8',
|
|
43
|
-
...execFileOptions,
|
|
44
|
-
},
|
|
58
|
+
createArguments(command),
|
|
59
|
+
createExecFileOptions(execFileOptions),
|
|
45
60
|
);
|
|
46
61
|
};
|
|
47
62
|
|
|
48
|
-
executePowerShell.argumentsPrefix =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'Bypass',
|
|
53
|
-
'-EncodedCommand',
|
|
54
|
-
];
|
|
63
|
+
executePowerShell.argumentsPrefix = argumentsPrefix;
|
|
64
|
+
executePowerShell.encodeCommand = encodeCommand;
|
|
65
|
+
executePowerShell.escapeArgument = escapeArgument;
|
|
66
|
+
executePowerShell.createArguments = createArguments;
|
|
55
67
|
|
|
56
|
-
|
|
68
|
+
export const executePowerShellSync = (command, options = {}) => {
|
|
69
|
+
const {
|
|
70
|
+
powerShellPath: psPath,
|
|
71
|
+
...execFileOptions
|
|
72
|
+
} = options;
|
|
57
73
|
|
|
58
|
-
|
|
74
|
+
return childProcess.execFileSync(
|
|
75
|
+
psPath ?? powerShellPath(),
|
|
76
|
+
createArguments(command),
|
|
77
|
+
createExecFileOptions(execFileOptions),
|
|
78
|
+
);
|
|
79
|
+
};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -13,11 +13,9 @@ npm install powershell-utils
|
|
|
13
13
|
```js
|
|
14
14
|
import {executePowerShell, powerShellPath} from 'powershell-utils';
|
|
15
15
|
|
|
16
|
-
// Execute a PowerShell command
|
|
17
16
|
const {stdout} = await executePowerShell('Get-Process');
|
|
18
17
|
console.log(stdout);
|
|
19
18
|
|
|
20
|
-
// Get PowerShell path
|
|
21
19
|
console.log(powerShellPath());
|
|
22
20
|
//=> 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
|
|
23
21
|
```
|
|
@@ -64,7 +62,6 @@ Execute a PowerShell command.
|
|
|
64
62
|
```js
|
|
65
63
|
import {executePowerShell} from 'powershell-utils';
|
|
66
64
|
|
|
67
|
-
// Execute a PowerShell command
|
|
68
65
|
const {stdout} = await executePowerShell('Get-Process');
|
|
69
66
|
console.log(stdout);
|
|
70
67
|
```
|
|
@@ -79,7 +76,7 @@ The PowerShell command to execute.
|
|
|
79
76
|
|
|
80
77
|
Type: `object`
|
|
81
78
|
|
|
82
|
-
The below option and
|
|
79
|
+
The below option and all options except `shell` in Node.js [`child_process.execFile()`](https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback) are supported. PowerShell is always executed directly to prevent shell injection.
|
|
83
80
|
|
|
84
81
|
##### powerShellPath
|
|
85
82
|
|
|
@@ -99,16 +96,9 @@ Character encoding for stdout and stderr.
|
|
|
99
96
|
|
|
100
97
|
Type: `string[]`
|
|
101
98
|
|
|
102
|
-
Standard PowerShell arguments that prefix the encoded command
|
|
99
|
+
Standard PowerShell arguments that prefix the encoded command: `['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand']`
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```js
|
|
107
|
-
import {executePowerShell} from 'powershell-utils';
|
|
108
|
-
|
|
109
|
-
const arguments_ = [...executePowerShell.argumentsPrefix, encodedCommand];
|
|
110
|
-
childProcess.spawn(powerShellPath(), arguments_);
|
|
111
|
-
```
|
|
101
|
+
Exposed for debugging or for advanced use cases where you need to customize the arguments. For most cases, use `createArguments()` instead.
|
|
112
102
|
|
|
113
103
|
### executePowerShell.encodeCommand(command)
|
|
114
104
|
|
|
@@ -136,6 +126,11 @@ Returns: `string`
|
|
|
136
126
|
|
|
137
127
|
Escape a string argument for use in PowerShell single-quoted strings.
|
|
138
128
|
|
|
129
|
+
> [!IMPORTANT]
|
|
130
|
+
> This makes a value safe as a PowerShell string literal, not as an argument to a native program. PowerShell removes the outer quotes before passing a value onwards, and the receiving process then re-splits it under its own command-line rules, where spaces and double quotes are delimiters. For example, `Start-Process -ArgumentList` receives `'a b'` as two arguments rather than one.
|
|
131
|
+
>
|
|
132
|
+
> Escaping also does not make an untrusted value safe to use as the program that `Start-Process` runs. Validate such values against an allowlist.
|
|
133
|
+
|
|
139
134
|
#### value
|
|
140
135
|
|
|
141
136
|
Type: `unknown`
|
|
@@ -151,3 +146,64 @@ const escaped = executePowerShell.escapeArgument("it's a test");
|
|
|
151
146
|
// Use in command building
|
|
152
147
|
const command = `Start-Process ${executePowerShell.escapeArgument(appName)}`;
|
|
153
148
|
```
|
|
149
|
+
|
|
150
|
+
### executePowerShell.createArguments(command)
|
|
151
|
+
|
|
152
|
+
Returns: `string[]`
|
|
153
|
+
|
|
154
|
+
Create the full arguments array for PowerShell execution.
|
|
155
|
+
|
|
156
|
+
Combines `argumentsPrefix` with the encoded command. Useful when using `spawn()`, `execFile()`, or other process execution methods.
|
|
157
|
+
|
|
158
|
+
#### command
|
|
159
|
+
|
|
160
|
+
Type: `string`
|
|
161
|
+
|
|
162
|
+
The PowerShell command.
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
import {spawn} from 'node:child_process';
|
|
166
|
+
import {powerShellPath, executePowerShell} from 'powershell-utils';
|
|
167
|
+
|
|
168
|
+
const args = executePowerShell.createArguments('Get-Process');
|
|
169
|
+
spawn(powerShellPath(), args);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### executePowerShellSync(command, options?)
|
|
173
|
+
|
|
174
|
+
Returns: `string`
|
|
175
|
+
|
|
176
|
+
Execute a PowerShell command synchronously.
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
import {executePowerShellSync} from 'powershell-utils';
|
|
180
|
+
|
|
181
|
+
const stdout = executePowerShellSync('Get-Process');
|
|
182
|
+
console.log(stdout);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### command
|
|
186
|
+
|
|
187
|
+
Type: `string`
|
|
188
|
+
|
|
189
|
+
The PowerShell command to execute.
|
|
190
|
+
|
|
191
|
+
#### options
|
|
192
|
+
|
|
193
|
+
Type: `object`
|
|
194
|
+
|
|
195
|
+
The below option and all options except `shell` in Node.js [`child_process.execFileSync()`](https://nodejs.org/api/child_process.html#child_processexecfilesyncfile-args-options) are supported. PowerShell is always executed directly to prevent shell injection.
|
|
196
|
+
|
|
197
|
+
##### powerShellPath
|
|
198
|
+
|
|
199
|
+
Type: `string`\
|
|
200
|
+
Default: `powerShellPath()`
|
|
201
|
+
|
|
202
|
+
Path to PowerShell executable.
|
|
203
|
+
|
|
204
|
+
##### encoding
|
|
205
|
+
|
|
206
|
+
Type: `string`\
|
|
207
|
+
Default: `'utf8'`
|
|
208
|
+
|
|
209
|
+
Character encoding for the output.
|