Skip to content

Commit

Permalink
Replace further occurrences of cmd.exe with ComSpec
Browse files Browse the repository at this point in the history
This follows #136, fixing the same problem in other places before anyone
raises another CVE. Unfortunately, with a security manager in play, one
cannot always look up ComSpec, and a soft-fail to the hard-coded
location becomes worthwhile (full path, not cmd.exe).
  • Loading branch information
jeff5 committed May 2, 2022
1 parent 15e6613 commit bd6dc94
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 17 deletions.
21 changes: 21 additions & 0 deletions src/org/python/core/PrePy.java
Expand Up @@ -490,4 +490,25 @@ public static String getCommandResult(String... command) {
}
return result;
}

/**
* A specialised wrapper on {@link #getCommandResult(String...)} for the Windows platform, where
* we have to consult the environment variable {@code ComSpec} to locate the command processor.
*
* @param command as strings (as for <code>ProcessBuilder</code>)
* @return the first line with content, or ""
*/
public static String getCommandResultWindows(String... command) {
String[] c = new String[command.length + 2];
c[1] = "/c";
System.arraycopy(command, 0, c, 2, command.length);
String comSpec = null;
try {
comSpec = System.getenv("ComSpec");
} catch (SecurityException e) {}
// If no ComSpec for us ... use the default full path (not just cmd.exe).
c[0] = comSpec != null ? comSpec : "C:\\WINDOWS\\System32\\cmd.exe";
return getCommandResult(c);
}

}
17 changes: 2 additions & 15 deletions src/org/python/core/PySystemState.java
Expand Up @@ -1003,7 +1003,7 @@ private static String getConsoleEncoding(Properties props) {

} else if (os != null && os.startsWith("Windows")) {
// Go via the Windows code page built-in command "chcp".
String output = Py.getCommandResult("cmd", "/c", "chcp");
String output = Py.getCommandResultWindows("chcp");
/*
* The output will be like "Active code page: 850" or maybe "Aktive Codepage: 1252." or
* "활성 코드 페이지: 949". Assume the first number with 2 or more digits is the code page.
Expand Down Expand Up @@ -1785,26 +1785,13 @@ public static String getSystemVersionString() {
// "Microsoft Windows [版本 10.0.17134.472]"
// We match the dots and digits within square brackets.
Pattern p = Pattern.compile("\\[.* ([\\d.]+)\\]");
Matcher m = p.matcher(Py.getCommandResult(System.getenv("ComSpec"), "/c", "ver"));
Matcher m = p.matcher(Py.getCommandResultWindows("ver"));
return m.find() ? m.group(1) : "";
} else {
return Py.getCommandResult("uname", "-v");
}
}

/**
* Run a command as a sub-process and return as the result the first line of output that
* consists of more than white space. It returns "" on any kind of error.
*
* @param command as strings (as for <code>ProcessBuilder</code>)
* @return the first line with content, or ""
* @deprecated Use {@link Py#getCommandResult(String...)} instead
*/
@Deprecated
private static String getCommandResult(String... command) {
return PrePy.getCommandResult(command);
}

/* Traverseproc implementation */
@Override
public int traverse(Visitproc visit, Object arg) {
Expand Down
30 changes: 28 additions & 2 deletions src/org/python/modules/posix/OS.java
Expand Up @@ -2,6 +2,8 @@
package org.python.modules.posix;

import java.util.Locale;

import org.python.core.Options;
import org.python.core.PySystemState;
import org.python.core.RegistryKey;

Expand All @@ -10,9 +12,12 @@
* that platform.
*/
enum OS {
NT("Windows", new String[] {"cmd.exe", "/c"}, new String[] {"command.com", "/c"}),

// Need for "command.com" questionable (https://en.wikipedia.org/wiki/COMMAND.COM)
NT("Windows", new String[] {getenv("ComSpec", "C:\\WINDOWS\\System32\\cmd.exe"), "/c"},
new String[] {"command.com", "/c"}), //
// http://bugs.jython.org/issue1842
IBMi("OS/400", new String[] {"/QOpenSys/usr/bin/sh", "-c"}),
IBMi("OS/400", new String[] {"/QOpenSys/usr/bin/sh", "-c"}), //
POSIX(new String[] {"/bin/sh", "-c"});

/** An array of potential shell commands this platform may use. */
Expand Down Expand Up @@ -58,4 +63,25 @@ static OS getOS() {
}
return OS.POSIX;
}

/**
* Get the value of an environment variable, or return the given default if the variable is
* undefined or the security environment prevents access. An empty string value from the
* environment is treated as undefined.
* <p>
* This accesses the read-only Java copy of the system environment directly, not
* {@code os.environ} so that it is safe to use before the {@code os} module is available.
*
* @param name to access in the environment.
* @param defaultValue to return if {@code name} is not defined or "" or access is forbidden.
* @return the corresponding value or {@code defaultValue}.
*/
private static String getenv(String name, String defaultValue) {
try {
String value = System.getenv(name);
return (value != null && value.length() > 0) ? value : defaultValue;
} catch (SecurityException e) {
return defaultValue;
}
}
}

0 comments on commit bd6dc94

Please sign in to comment.