-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathindex.js
More file actions
173 lines (161 loc) · 5.18 KB
/
index.js
File metadata and controls
173 lines (161 loc) · 5.18 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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
* A simple shell escape library. Use it to escape user-controlled inputs to
* shell commands to prevent shell injection.
*
* @overview Entrypoint for the library.
* @module shescape
* @version 2.1.10
* @license MPL-2.0
*/
import os from "node:os";
import process from "node:process";
import { parseOptions } from "./internal/options.js";
import { getHelpersByPlatform } from "./internal/platforms.js";
import { checkedToString, ensureArray } from "./internal/reflection.js";
/**
* A class to escape user-controlled inputs to shell commands to prevent shell
* injection.
*
* @example
* import { spawn } from "node:child_process";
* const shescape = new Shescape({ shell: false });
* spawn(
* "echo",
* ["Hello", shescape.escape(userInput)],
* null // `options.shell` MUST be falsy
* );
* @example
* import { spawn } from "node:child_process";
* const shescape = new Shescape({ shell: false });
* spawn(
* "echo",
* shescape.escapeAll(["Hello", userInput]),
* null // `options.shell` MUST be falsy
* );
* @example
* import { spawn } from "node:child_process";
* const spawnOptions = { shell: true }; // `options.shell` SHOULD be truthy
* const shescape = new Shescape({ shell: spawnOptions.shell });
* spawn(
* "echo",
* ["Hello", shescape.quote(userInput)],
* spawnOptions
* );
* @example
* import { spawn } from "node:child_process";
* const spawnOptions = { shell: true }; // `options.shell` SHOULD be truthy
* const shescape = new Shescape({ shell: spawnOptions.shell });
* spawn(
* "echo",
* shescape.quoteAll(["Hello", userInput]),
* spawnOptions
* );
*/
export class Shescape {
#escape;
#quote;
/**
* Create a new {@link Shescape} instance.
*
* @param {object} [options] The escape options.
* @param {boolean} [options.flagProtection=true] Is flag protection enabled.
* @param {boolean | string} [options.shell=true] The shell to escape for.
* @throws {Error} The shell is not supported or could not be found.
* @since 2.0.0
*/
constructor(options = {}) {
const platform = getHelpersByPlatform({
env: process.env,
platform: os.platform(),
});
options = parseOptions(
{ env: process.env, options, version: process.version },
platform,
);
const { flagProtection, shellName } = options;
const shell = platform.getShellHelpers(shellName);
{
const escape = shell.getEscapeFunction();
if (flagProtection) {
const flagProtect = shell.getFlagProtectionFunction();
this.#escape = (arg) => flagProtect(escape(arg));
} else {
this.#escape = escape;
}
}
{
const [escape, quote] = shell.getQuoteFunction();
if (flagProtection) {
const flagProtect = shell.getFlagProtectionFunction();
this.#quote = (arg) => quote(flagProtect(escape(arg)));
} else {
this.#quote = (arg) => quote(escape(arg));
}
}
}
/**
* Take a single value, the argument, and escape any dangerous characters.
*
* Non-string inputs will be converted to strings using a `toString()` method.
*
* @param {string} arg The argument to escape.
* @returns {string} The escaped argument.
* @throws {TypeError} The argument is not stringable.
* @since 2.0.0
*/
escape(arg) {
const argAsString = checkedToString(arg);
return this.#escape(argAsString);
}
/**
* Take an array of values, the arguments, and escape any dangerous characters
* in every argument.
*
* Non-array inputs are rejected. Non-string entries will be converted to
* strings using a `toString()` method.
*
* @param {string[]} args The arguments to escape.
* @returns {string[]} The escaped arguments.
* @throws {TypeError} The arguments are not an array.
* @throws {TypeError} One of the arguments is not stringable.
* @since 2.0.0
*/
escapeAll(args) {
ensureArray(args);
return args.map((arg) => this.escape(arg));
}
/**
* Take a single value, the argument, put shell-specific quotes around it and
* escape any dangerous characters.
*
* Non-string inputs will be converted to strings using a `toString()` method.
*
* @param {string} arg The argument to quote and escape.
* @returns {string} The quoted and escaped argument.
* @throws {TypeError} The argument is not stringable.
* @throws {Error} Quoting is not supported with `shell: false`.
* @since 2.0.0
*/
quote(arg) {
const argAsString = checkedToString(arg);
return this.#quote(argAsString);
}
/**
* Take an array of values, the arguments, put shell-specific quotes around
* every argument and escape any dangerous characters in every argument.
*
* Non-array inputs are rejected. Non-string entries will be converted to
* strings using a `toString()` method.
*
* @param {string[]} args The arguments to quote and escape.
* @returns {string[]} The quoted and escaped arguments.
* @throws {TypeError} The arguments are not an array.
* @throws {TypeError} One of the arguments is not stringable.
* @throws {Error} Quoting is not supported with `shell: false`.
* @since 2.0.0
*/
quoteAll(args) {
ensureArray(args);
return args.map((arg) => this.quote(arg));
}
}