Skip to content

Commit e11d024

Browse files
authored
[lgtm] Fix LGTM-reported issues. (dotnet#1074)
Remember CodeQL (5a0097b)? CodeQL basically runs [GitHub LGTM][0] on source code, looking for possible security issues. Now that CodeQL is running, we can begin addressing reported issues. Problems found include: * Result of call that may return NULL dereferenced unconditionally * HttpClient created with CheckCertificateRevocationList disabled * Arbitrary file write during archive extraction ("Zip Slip") * Local-user-controlled data in path expression ~~ Result of call that may return NULL dereferenced unconditionally ~~ If **calloc**(3) returns `nullptr`, we shouldn't pass it on to `MultiByteToWideChar()` or `WideCharToMultiByte()` without validation. ~~ HttpClient created with CheckCertificateRevocationList disabled ~~ Apparently the `HttpClient` default constructor is "bad"; we should instead use the [`HttpClient(HttpMessageHandler)` constructor][1], provide our own `HttpClientHandler`, and ensure that [`HttpClientHandler.CheckCertificateRevocationList`][2] is True. ~~ Arbitrary file write during archive extraction ("Zip Slip") ~~ `tools/java-source-utils` (69e1b80) extracts the `.java` files within `.jar`/`.aar`/.etc files to use for type resolution, as I couldn't find an easier way to get `com.github.javaparser` to use Java source code for type resolution purposes unless the Java source code was on-disk. Unfortunately, the `.jar` extraction code was susceptible to "Zip Slip", wherein an entry in the `.jar` may overwrite unexpected files if it has an entry name of e.g. `../../this/is/really/bad.java`. Fix this by verifying that the target filename stays within the target directory structure, and skip the entry when the name is invalid. ~~ Local-user-controlled data in path expression ~~ LGTM is complaining that `tools/java-source-utils` (69e1b80) accepts user-controlled data. These warnings will be *ignored* because the app is *unusable* without "user-controlled data"; consider these `java-source-utils --help` fragments: Java type resolution options: --bootclasspath CLASSPATH ':'-separated list of .jar files to use for type resolution. -a, --aar FILE .aar file to use for type resolution. -j, --jar FILE .jar file to use for type resolution. -s, --source DIR Directory containing .java files for type resolution purposes. DOES NOT parse all files. These are all user-controlled, and they are necessary to allow `java-source-utils` to *work*. Similarly: Output file options: -P, --output-params FILE Write method parameter names to FILE. -D, --output-javadoc FILE Write Javadoc within XML container to FILE. LGTM complains that `--output-javadoc FILE` accepts a user-controlled path which may control directory separator chars, and *this is intentional*; using it would be annoying if that weren't true! These uses can be ignored by appending the comment `// lgtm [java/path-injection-local]`. [0]: https://github.com/marketplace/lgtm [1]: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.-ctor?view=netstandard-2.0#system-net-http-httpclient-ctor(system-net-http-httpmessagehandler) [2]: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.checkcertificaterevocationlist?view=net-7.0
1 parent cf80deb commit e11d024

File tree

4 files changed

+37
-12
lines changed

4 files changed

+37
-12
lines changed

build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/DownloadUri.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ public override bool Execute ()
4141
}
4242

4343
var tasks = new TTask [SourceUris.Length];
44-
using (var client = new HttpClient ()) {
44+
var handler = new HttpClientHandler {
45+
CheckCertificateRevocationList = true,
46+
};
47+
using (var client = new HttpClient (handler)) {
4548
client.Timeout = TimeSpan.FromHours (3);
4649
for (int i = 0; i < SourceUris.Length; ++i) {
4750
tasks [i] = DownloadFile (client, SourceUris [i], DestinationFiles [i].ItemSpec);

src/java-interop/java-interop-util.cc

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ char*
77
utf16_to_utf8 (const wchar_t *widestr)
88
{
99
int required_size = WideCharToMultiByte (CP_UTF8, 0, widestr, -1, NULL, 0, NULL, NULL);
10+
if (required_size <= 0) {
11+
return nullptr;
12+
}
13+
1014
char *mbstr = static_cast<char*> (calloc (required_size, sizeof (char)));
11-
int converted_size = WideCharToMultiByte (CP_UTF8, 0, widestr, -1, mbstr, required_size, NULL, NULL);
15+
if (mbstr == nullptr) {
16+
return nullptr;
17+
}
1218

13-
// Hush a compiler warning about unused variable in RELEASE
14-
(void)converted_size;
19+
int converted_size = WideCharToMultiByte (CP_UTF8, 0, widestr, -1, mbstr, required_size, NULL, NULL);
1520
assert (converted_size == required_size);
21+
if (required_size != converted_size) {
22+
free (mbstr);
23+
return nullptr;
24+
}
1625

1726
return mbstr;
1827
}
@@ -21,12 +30,21 @@ wchar_t*
2130
utf8_to_utf16 (const char *mbstr)
2231
{
2332
int required_chars = MultiByteToWideChar (CP_UTF8, 0, mbstr, -1, NULL, 0);
33+
if (required_chars <= 0) {
34+
return nullptr;
35+
}
36+
2437
wchar_t *widestr = static_cast<wchar_t*> (calloc (required_chars, sizeof (wchar_t)));
25-
int converted_chars = MultiByteToWideChar (CP_UTF8, 0, mbstr, -1, widestr, required_chars);
38+
if (widestr == nullptr) {
39+
return nullptr;
40+
}
2641

27-
// Hush a compiler warning about unused variable in RELEASE
28-
(void)converted_chars;
42+
int converted_chars = MultiByteToWideChar (CP_UTF8, 0, mbstr, -1, widestr, required_chars);
2943
assert (converted_chars == required_chars);
44+
if (required_chars != converted_chars) {
45+
free (widestr);
46+
return nullptr;
47+
}
3048

3149
return widestr;
3250
}

tools/java-source-utils/src/main/java/com/microsoft/android/JavaSourceUtilsOptions.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private final JavaSourceUtilsOptions parse(Iterator<String> args) throws IOExcep
167167
final String bootClassPath = getNextOptionValue(args, arg);
168168
final ArrayList<File> files = new ArrayList<File>();
169169
for (final String cp : bootClassPath.split(File.pathSeparator)) {
170-
final File file = new File(cp);
170+
final File file = new File(cp); // lgtm [java/path-injection-local]
171171
if (!file.exists()) {
172172
System.err.println(App.APP_NAME + ": warning: invalid file path for option `-bootclasspath`: " + cp);
173173
continue;
@@ -253,7 +253,7 @@ private final JavaSourceUtilsOptions parse(Iterator<String> args) throws IOExcep
253253
if (arg.startsWith("@")) {
254254
// response file?
255255
final String responseFileName = arg.substring(1);
256-
final File responseFile = new File(responseFileName);
256+
final File responseFile = new File(responseFileName); // lgtm [java/path-injection-local]
257257
if (responseFile.exists()) {
258258
final Iterator<String> lines =
259259
Files.readAllLines(responseFile.toPath())
@@ -267,7 +267,7 @@ private final JavaSourceUtilsOptions parse(Iterator<String> args) throws IOExcep
267267
break;
268268
}
269269
}
270-
final File file = new File(arg);
270+
final File file = new File(arg); // lgtm [java/path-injection-local]
271271
if (!file.exists()) {
272272
System.err.println(App.APP_NAME + ": warning: invalid file path for option `FILES`: " + arg);
273273
break;
@@ -319,6 +319,10 @@ private static void extractTo(final File zipFilePath, final File toDir, final Co
319319
if (!entry.getName().endsWith(".java"))
320320
continue;
321321
final File target = new File(toDir, entry.getName());
322+
if (!target.toPath().normalize().startsWith(toDir.toPath())) {
323+
System.err.println(App.APP_NAME + ": warning: skipping bad zip entry: " + zipFilePath + "!" + entry.getName());
324+
continue;
325+
}
322326
if (verboseOutput) {
323327
System.out.println ("# creating file: " + target.getAbsolutePath());
324328
}
@@ -343,7 +347,7 @@ static File getNextOptionFile(final Iterator<String> args, final String option)
343347
throw new IllegalArgumentException(
344348
"Expected required value for option `" + option + "`.");
345349
final String fileName = args.next();
346-
final File file = new File(fileName);
350+
final File file = new File(fileName); // lgtm [java/path-injection-local]
347351
if (!file.exists()) {
348352
System.err.println(App.APP_NAME + ": warning: invalid file path for option `" + option + "`: " + fileName);
349353
return null;

tools/java-source-utils/src/main/java/com/microsoft/android/JavadocXmlGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public JavadocXmlGenerator(final String output) throws FileNotFoundException, Pa
3939
if (output == null)
4040
this.output = System.out;
4141
else {
42-
final File file = new File(output);
42+
final File file = new File(output); // lgtm [java/path-injection-local]
4343
final File parent = file.getParentFile();
4444
if (parent != null) {
4545
parent.mkdirs();

0 commit comments

Comments
 (0)