powershell-utils 0.2.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 CHANGED
@@ -1,21 +1,31 @@
1
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;
10
15
  };
11
16
 
12
- export type ExecutePowerShellSyncOptions = ExecFileSyncOptions & {
17
+ export type ExecutePowerShellSyncOptions = Omit<ExecFileSyncOptions, 'shell'> & {
13
18
  /**
14
19
  Path to PowerShell executable.
15
20
 
16
21
  @default powerShellPath()
17
22
  */
18
23
  readonly powerShellPath?: string;
24
+
25
+ /**
26
+ Not supported. PowerShell is always executed directly to prevent shell injection.
27
+ */
28
+ readonly shell?: never;
19
29
  };
20
30
 
21
31
  export type ExecutePowerShellResult = {
@@ -105,6 +115,10 @@ export namespace executePowerShell {
105
115
  /**
106
116
  Escape a string argument for use in PowerShell single-quoted strings.
107
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
+
108
122
  @param value - The value to escape.
109
123
  @returns Escaped and quoted string ready for PowerShell.
110
124
 
package/index.js CHANGED
@@ -34,10 +34,19 @@ const argumentsPrefix = [
34
34
 
35
35
  const encodeCommand = command => Buffer.from(command, 'utf16le').toString('base64');
36
36
 
37
- const escapeArgument = value => `'${String(value).replaceAll('\'', '\'\'')}'`;
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)}'`;
38
41
 
39
42
  const createArguments = command => [...argumentsPrefix, encodeCommand(command)];
40
43
 
44
+ const createExecFileOptions = options => ({
45
+ encoding: 'utf8',
46
+ ...options,
47
+ shell: false,
48
+ });
49
+
41
50
  export const executePowerShell = async (command, options = {}) => {
42
51
  const {
43
52
  powerShellPath: psPath,
@@ -47,10 +56,7 @@ export const executePowerShell = async (command, options = {}) => {
47
56
  return execFile(
48
57
  psPath ?? powerShellPath(),
49
58
  createArguments(command),
50
- {
51
- encoding: 'utf8',
52
- ...execFileOptions,
53
- },
59
+ createExecFileOptions(execFileOptions),
54
60
  );
55
61
  };
56
62
 
@@ -68,9 +74,6 @@ export const executePowerShellSync = (command, options = {}) => {
68
74
  return childProcess.execFileSync(
69
75
  psPath ?? powerShellPath(),
70
76
  createArguments(command),
71
- {
72
- encoding: 'utf8',
73
- ...execFileOptions,
74
- },
77
+ createExecFileOptions(execFileOptions),
75
78
  );
76
79
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powershell-utils",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Utilities for executing PowerShell commands",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/powershell-utils",
package/readme.md CHANGED
@@ -76,7 +76,7 @@ The PowerShell command to execute.
76
76
 
77
77
  Type: `object`
78
78
 
79
- The below option and also all options in Node.js [`child_process.execFile()`](https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback) are supported.
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.
80
80
 
81
81
  ##### powerShellPath
82
82
 
@@ -126,6 +126,11 @@ Returns: `string`
126
126
 
127
127
  Escape a string argument for use in PowerShell single-quoted strings.
128
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
+
129
134
  #### value
130
135
 
131
136
  Type: `unknown`
@@ -187,7 +192,7 @@ The PowerShell command to execute.
187
192
 
188
193
  Type: `object`
189
194
 
190
- The below option and also all options in Node.js [`child_process.execFileSync()`](https://nodejs.org/api/child_process.html#child_processexecfilesyncfile-args-options) are supported.
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.
191
196
 
192
197
  ##### powerShellPath
193
198