diff --git a/server/src/main/java/com/defold/extender/Extender.java b/server/src/main/java/com/defold/extender/Extender.java index 497a7b40..d53b346d 100644 --- a/server/src/main/java/com/defold/extender/Extender.java +++ b/server/src/main/java/com/defold/extender/Extender.java @@ -175,7 +175,7 @@ private class ProGuardContext { this.manifestValidator = new ExtensionManifestValidator(new WhitelistConfig(), this.platformConfig.allowedFlags, allowedSymbols); // Make sure the user hasn't input anything invalid in the manifest - this.manifestValidator.validate(this.appManifestPath, appManifestContext); + this.manifestValidator.validate(this.appManifestPath, this.uploadDirectory, appManifestContext); // Collect extension directories (used by both buildEngine and buildClassesDex) this.manifests = allFiles.stream().filter(f -> f.getName().equals("ext.manifest")).collect(Collectors.toList()); @@ -445,8 +445,18 @@ else if(f.getName().endsWith(".aar")) { private File compileFile(int index, File extDir, File src, Map manifestContext, List commands) throws IOException, InterruptedException, ExtenderException { List includes = new ArrayList<>(); - includes.add( ExtenderUtil.getRelativePath(jobDirectory, new File(extDir, "include") ) ); + File extIncludeDir = new File(extDir, "include"); + includes.add( ExtenderUtil.getRelativePath(jobDirectory, extIncludeDir ) ); includes.add( ExtenderUtil.getRelativePath(jobDirectory, uploadDirectory) ); + + // Add the other extensions include folders + for (File otherExtDir : this.getExtensionFolders()) { + File otherIncludeDir = new File(otherExtDir, "include"); + if (extIncludeDir.equals(otherIncludeDir)) + continue; + includes.add( ExtenderUtil.getRelativePath(jobDirectory, otherIncludeDir ) ); + } + File o = new File(buildDirectory, String.format("%s_%d.o", src.getName(), index)); List frameworks = getFrameworks(extDir); @@ -987,7 +997,7 @@ private Map buildJava(File rJar) throws ExtenderExceptio manifestContext = getManifestContext(platform, config, manifestConfig); } - this.manifestValidator.validate(manifestConfig.name, manifestContext); + this.manifestValidator.validate(manifestConfig.name, manifest.getParentFile(), manifestContext); manifestConfigs.put(manifestConfig.name, manifestContext); @@ -1287,7 +1297,7 @@ private void loadManifests() throws IOException, ExtenderException { } String relativePath = ExtenderUtil.getRelativePath(this.uploadDirectory, manifest); - this.manifestValidator.validate(relativePath, manifestContext); + this.manifestValidator.validate(relativePath, manifest.getParentFile(), manifestContext); // Apply any global settings to the context manifestContext.put("extension_name", manifestConfig.name); diff --git a/server/src/main/java/com/defold/extender/ExtenderUtil.java b/server/src/main/java/com/defold/extender/ExtenderUtil.java index ddfa4cfb..135f4079 100644 --- a/server/src/main/java/com/defold/extender/ExtenderUtil.java +++ b/server/src/main/java/com/defold/extender/ExtenderUtil.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.nio.file.Path; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.DirectoryFileFilter; @@ -379,4 +380,9 @@ public static File getAndroidResourceFolder(File dir) { return null; } + public static boolean isChild(File parent, File child) { + Path parentPath = parent.toPath().normalize().toAbsolutePath(); + Path childPath = child.toPath().normalize().toAbsolutePath(); + return childPath.startsWith(parentPath); + } } diff --git a/server/src/main/java/com/defold/extender/ExtensionManifestValidator.java b/server/src/main/java/com/defold/extender/ExtensionManifestValidator.java index 56384c8c..21a314d3 100644 --- a/server/src/main/java/com/defold/extender/ExtensionManifestValidator.java +++ b/server/src/main/java/com/defold/extender/ExtensionManifestValidator.java @@ -1,13 +1,18 @@ package com.defold.extender; +import java.io.File; +import java.util.Arrays; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class ExtensionManifestValidator { + private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionManifestValidator.class); private final List allowedLibs = new ArrayList<>(); private final List allowedFlags = new ArrayList<>(); @@ -27,7 +32,28 @@ private static boolean isListOfStrings(List list) { return list != null && list.stream().allMatch(o -> o instanceof String); } - void validate(String extensionName, Map context) throws ExtenderException { + private void validateIncludePaths(String extensionName, File extensionFolder, List includes) throws ExtenderException { + for (String include : includes) { + String[] tokens = include.split("/"); + for (int i = 0; i < tokens.length; ++i) + { + String[] subtokens = Arrays.copyOfRange(tokens, 0, i); + String s = String.join("/", subtokens); + File f = new File(extensionFolder, s); + + if (!ExtenderUtil.isChild(extensionFolder, f)) + { + throw new ExtenderException(String.format("Error in '%s': The include '%s' path must be relative subdirectory to the extension folder '%s'", extensionName, include, extensionFolder)); + } + + if (!f.exists()) { + LOGGER.warn("The include path '%s' does not exist:", f); + } + } + } + } + + void validate(String extensionName, File extensionFolder, Map context) throws ExtenderException { Set keys = context.keySet(); for (String k : keys) { Object v = context.get(k); @@ -64,6 +90,14 @@ void validate(String extensionName, Map context) throws Extender type = "symbol"; break; + case "includes": + if (!(v instanceof List)) { + throw new ExtenderException(String.format("Error in '%s': The 'includes' must be a list of strings. Got %s: %s (type %s)", extensionName, k, v.toString(), v.getClass().getCanonicalName())); + } + + validateIncludePaths(extensionName, extensionFolder, (List) v); + continue; + case "excludeLibs": case "excludeJars": case "excludeJsLibs": diff --git a/server/src/test/java/com/defold/extender/ExtenderUtilTest.java b/server/src/test/java/com/defold/extender/ExtenderUtilTest.java index 4fb7cd65..cd568db7 100644 --- a/server/src/test/java/com/defold/extender/ExtenderUtilTest.java +++ b/server/src/test/java/com/defold/extender/ExtenderUtilTest.java @@ -54,7 +54,6 @@ private File getRelative(File dir, File path) { @Test public void testAndroidAssetFolders() throws IOException, InterruptedException, ExtenderException { - System.out.printf("MAWE: testAndroidAssetFolders\n"); File d; d = new File(uploadDir, "extension1/res/android/res/com.foo.name/res/values"); d.mkdirs(); assertTrue(d.exists()); d = new File(uploadDir, "extension2/res/android/res/com.foo.name/values"); d.mkdirs(); assertTrue(d.exists()); @@ -79,4 +78,12 @@ public void testAndroidAssetFolders() throws IOException, InterruptedException, d = ExtenderUtil.getAndroidResourceFolder(new File(uploadDir, "extension3/res/android/res/values")); assertNull(d); } + + @Test + public void testChild() throws IOException, InterruptedException, ExtenderException { + File parent = new File("upload/extension1"); + assertEquals( ExtenderUtil.isChild(parent, new File("upload/extension1")), true ); + assertEquals( ExtenderUtil.isChild(parent, new File("upload/extension1/file")), true ); + assertEquals( ExtenderUtil.isChild(parent, new File("upload")), false); + } } diff --git a/server/src/test/java/com/defold/extender/ExtensionManifestValidatorTest.java b/server/src/test/java/com/defold/extender/ExtensionManifestValidatorTest.java index ebb6f1bd..b81c88d5 100644 --- a/server/src/test/java/com/defold/extender/ExtensionManifestValidatorTest.java +++ b/server/src/test/java/com/defold/extender/ExtensionManifestValidatorTest.java @@ -59,6 +59,7 @@ public void testWhitelistCheckContext() throws ExtenderException, IOException { Map context = new HashMap<>(); // LIBS + File extensionFolder = new File("ext-folder"); stringValues.clear(); context.clear(); @@ -66,7 +67,7 @@ public void testWhitelistCheckContext() throws ExtenderException, IOException { stringValues.add("QuickTime"); context.put("libs", stringValues); - validator.validate("test_extension", context); + validator.validate("test_extension", extensionFolder, context); stringValues.clear(); @@ -74,7 +75,7 @@ public void testWhitelistCheckContext() throws ExtenderException, IOException { stringValues.add("c++"); context.put("libs", stringValues); - validator.validate("test_extension", context); + validator.validate("test_extension", extensionFolder, context); stringValues.clear(); @@ -86,7 +87,7 @@ public void testWhitelistCheckContext() throws ExtenderException, IOException { boolean thrown; try { thrown = false; - validator.validate("test_extension", context); + validator.validate("test_extension", extensionFolder, context); } catch (ExtenderException e) { thrown = true; } @@ -101,7 +102,7 @@ public void testWhitelistCheckContext() throws ExtenderException, IOException { stringValues.add("-Weverything"); context.put("flags", stringValues); - validator.validate("test_extension", context); + validator.validate("test_extension", extensionFolder, context); stringValues.clear(); @@ -111,7 +112,7 @@ public void testWhitelistCheckContext() throws ExtenderException, IOException { stringValues.add("-std=c++14"); context.put("flags", stringValues); - validator.validate("test_extension", context); + validator.validate("test_extension", extensionFolder, context); stringValues.clear(); @@ -122,7 +123,7 @@ public void testWhitelistCheckContext() throws ExtenderException, IOException { try { thrown = false; - validator.validate("test_extension", context); + validator.validate("test_extension", extensionFolder, context); } catch (ExtenderException e) { if (e.toString().contains("rm -rf")) { thrown = true; @@ -146,9 +147,10 @@ public void testAllowedLibs() throws ExtenderException { l.add("z"); context.put("libs", l); + File extensionFolder = new File("ext-folder"); boolean thrown = false; try { - validator.validate("testExtension", context); + validator.validate("testExtension", extensionFolder, context); } catch (ExtenderException e) { thrown = true; System.out.println(e.toString()); @@ -159,7 +161,7 @@ public void testAllowedLibs() throws ExtenderException { l.add("../libfoobar.a"); context.put("libs", l); try { - validator.validate("testExtension", context); + validator.validate("testExtension", extensionFolder, context); } catch (ExtenderException e) { System.out.println(e.toString()); if (e.toString().contains("Invalid")) { // expected error