|
8 | 8 | import java.util.HashMap; |
9 | 9 | import java.util.List; |
10 | 10 | import java.util.Map; |
11 | | -import java.util.regex.Pattern; |
| 11 | +import java.util.stream.Collectors; |
12 | 12 |
|
13 | 13 | import com.fasterxml.jackson.annotation.JsonSetter; |
14 | 14 | import com.fasterxml.jackson.databind.ObjectMapper; |
|
44 | 44 | public abstract class AbstractSoda extends Task { |
45 | 45 | private static final String DEFAULT_IMAGE = "sodadata/soda-core"; |
46 | 46 | protected static final ObjectMapper MAPPER = JacksonMapper.ofYaml(); |
47 | | - private static final Pattern SAFE_REQUIREMENT_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\-.\\[\\]>=<,!~ ]+$"); |
48 | 47 |
|
49 | 48 | @Schema( |
50 | 49 | title = "Runner to use", |
@@ -187,24 +186,30 @@ private String virtualEnvCommand(RunContext runContext, Path workingDirectory, L |
187 | 186 | renderer.add("python -m venv --system-site-packages " + workingDirectory + " > /dev/null"); |
188 | 187 |
|
189 | 188 | if (requirements != null) { |
190 | | - for (String requirement : requirements) { |
191 | | - if (!SAFE_REQUIREMENT_PATTERN.matcher(requirement).matches()) { |
192 | | - throw new IllegalArgumentException( |
193 | | - "Invalid requirement '" + requirement + "': requirements must only contain " + |
194 | | - "alphanumeric characters and the following symbols: _ - . [ ] > = < , ! ~ and space. " + |
195 | | - "Shell metacharacters are not allowed." |
196 | | - ); |
197 | | - } |
198 | | - } |
| 189 | + String installArgs = requirements.stream() |
| 190 | + .map(AbstractSoda::shellQuote) |
| 191 | + .collect(Collectors.joining(" ")); |
199 | 192 |
|
200 | 193 | renderer.addAll( |
201 | 194 | Arrays.asList( |
202 | 195 | "./bin/pip install pip --upgrade > /dev/null", |
203 | | - "./bin/pip install " + String.join(" ", requirements) + " > /dev/null" |
| 196 | + "./bin/pip install " + installArgs + " > /dev/null" |
204 | 197 | ) |
205 | 198 | ); |
206 | 199 | } |
207 | 200 |
|
208 | 201 | return String.join("\n", renderer); |
209 | 202 | } |
| 203 | + |
| 204 | + /** |
| 205 | + * Wraps a value in single quotes for safe interpolation into a {@code /bin/sh -c} command line. |
| 206 | + * Inside single quotes the shell treats every character literally, so all metacharacters |
| 207 | + * ({@code ; & | > < * $ ` ( )} …) are neutralized while any valid {@code requirements.txt} syntax |
| 208 | + * (version specifiers, extras, VCS URLs, environment markers) is preserved verbatim. The only |
| 209 | + * character that cannot appear inside single quotes — the single quote itself — is escaped with |
| 210 | + * the standard {@code '\''} sequence (close quote, escaped quote, reopen quote). |
| 211 | + */ |
| 212 | + private static String shellQuote(String value) { |
| 213 | + return "'" + value.replace("'", "'\\''") + "'"; |
| 214 | + } |
210 | 215 | } |
0 commit comments