You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@solr.apache.org by GitBox <gi...@apache.org> on 2021/08/27 21:42:01 UTC

[GitHub] [solr] sonatype-lift[bot] commented on a change in pull request #74: SOLR-15080: Add 'zeppelin' integration to bin/solr

sonatype-lift[bot] commented on a change in pull request #74:
URL: https://github.com/apache/solr/pull/74#discussion_r697733633



##########
File path: solr/core/src/java/org/apache/solr/util/SolrCLI.java
##########
@@ -3689,6 +3697,329 @@ public AssertionFailureException(String message) {
     }
   }
 
+  public static class ZeppelinTool extends ToolBase {
+    private static final String ZEPP_VERSION = "0.10.0";
+    private static final String ZEPP_INSTALL_TYPE = "netinst";
+    private static final String ZEPP_UNPACK_DIR_NAME = "zeppelin-" + ZEPP_VERSION + "-bin-" + ZEPP_INSTALL_TYPE;
+    private static final String MIRROR_BASE = "http://apache.cs.utah.edu/zeppelin/zeppelin-" + ZEPP_VERSION + "/";
+    private static final String ZEPP_ARCHIVE = ZEPP_UNPACK_DIR_NAME + ".tgz";
+    private static final String ZEPP_ARCHIVE_URL = MIRROR_BASE + ZEPP_ARCHIVE;
+    private static final int PROCESS_WAIT_TIME_SECONDS = 90;
+
+    private static final String SOLR_INSTALL_DIR = System.getProperty("solr.install.dir");
+    private static final String ZEPP_INTERPRETER_TEMPLATE_ABS_PATH = Paths.get(SOLR_INSTALL_DIR, "server", "resources", "zeppelin-solr-interpreter.json.template").toAbsolutePath().toString();
+    private static final String ZEPP_INTERPRETER_ABS_PATH = Paths.get(SOLR_INSTALL_DIR, "server", "resources", "zeppelin-solr-interpreter.json").toAbsolutePath().toString();
+    private static final String ZEPP_BASE_ABS_PATH = Paths.get(SOLR_INSTALL_DIR, "zeppelin").toAbsolutePath().toString();
+    private static final String ZEPP_UNPACK_LOG_ABS_PATH = Paths.get(ZEPP_BASE_ABS_PATH, "untar-logs.txt").toAbsolutePath().toString();
+    private static final String ZEPP_ARCHIVE_ABS_PATH = Paths.get(ZEPP_BASE_ABS_PATH, ZEPP_ARCHIVE).toAbsolutePath().toString();
+    private static final String ZEPP_UNPACKED_ABS_PATH = Paths.get(ZEPP_BASE_ABS_PATH, ZEPP_UNPACK_DIR_NAME).toAbsolutePath().toString();
+    private static final String ZEPP_DAEMON_EXE_NAME = "zeppelin-daemon.sh"; // Only valid on *nix
+    private static final String ZEPP_INTERP_EXE_NAME = "install-interpreter.sh"; // Only valid on *nix
+    private static final String ZEPP_DAEMON_ABS_PATH = Paths.get(ZEPP_UNPACKED_ABS_PATH, "bin", ZEPP_DAEMON_EXE_NAME).toAbsolutePath().toString();
+    private static final String ZEPP_INTERP_ABS_PATH = Paths.get(ZEPP_UNPACKED_ABS_PATH, "bin", ZEPP_INTERP_EXE_NAME).toAbsolutePath().toString();
+    private static final String ZEPP_SOLR_INTERPRETER_ID = "solr";
+
+    @Override
+    public String getName() { return "zeppelin"; }
+
+    @Override
+    public Option[] getOptions() {
+      return new Option[] {
+              Option.builder("z")
+                      .desc("Only valid for action=update-interpreter.  The Zeppelin URL to update.  Defaults to http://localhost:8080")
+                      .longOpt("zeppelinUrl")
+                      .hasArg()
+                      .argName("url")
+                      .build(),
+              Option.builder("s")
+                      .desc("The URL of a valid Solr instance. (e.g. http://localhost:7574/solr)  Used to configure the" +
+                              " Zeppelin interpreter.  Defaults to values read from solr.in.sh")
+                      .longOpt("solrUrl")
+                      .hasArg()
+                      .argName("url")
+                      .required()
+                      .build()
+      };
+    }
+
+    /*
+     * Since ZeppelinTool uses positional arguments not represented in getOptions, the help text generated from those
+     * values alone doesn't really cover the tool appropriately.  This method returns custom help text, similar to that
+     * hardcoded in the solr/solr.cmd scripts themselves.
+     */
+    @Override
+    public void printHelpText() {
+      stdout.println("Usage: solr zeppelin bootstrap [--zeppelinUrl <url>] [--solrUrl <url>]");
+      stdout.println("       solr zeppelin clean");
+      stdout.println("       solr zeppelin start");
+      stdout.println("       solr zeppelin stop");
+      stdout.println("       solr zeppelin update_interpreter [--zeppelinUrl <url>] [--solrUrl <url>]");
+      stdout.println();
+      stdout.println("  Sets up and manages an Apache Zeppelin installation configured for use with a local Solr.");
+      stdout.println("    - 'bootstrap' downloads and starts Zeppelin in a 'zeppelin' folder within the Solr install");
+      stdout.println("    - 'clean' stops Zeppelin (if necessary) and removes the install created by 'bootstrap'.");
+      stdout.println("    - 'start' starts Zeppelin if not already running.");
+      stdout.println("    - 'stop' stops Zeppelin if not already stopped.");
+      stdout.println("    - 'update_interpreter' reinstalls the Zeppelin interpreter used to talk to Solr.  Useful for");
+      stdout.println("       pointing Zeppelin to a different Solr cluster or changing other settings.");
+      stdout.println();
+      stdout.println("  This tool relies on management scripts that ship with Zeppelin - several of which are not");
+      stdout.println("  available on Windows.  As a result this tool isn't supported in Windows environments at this time");
+      stdout.println();
+
+      // Append option information to help text
+      final HelpFormatter helpFormatter = new HelpFormatter();
+      try (final PrintWriter writer = new PrintWriter(stdout, true, StandardCharsets.UTF_8)) {
+        helpFormatter.printOptions(writer, 100, getToolOptions(this), helpFormatter.getLeftPadding(),
+                helpFormatter.getDescPadding());
+      }
+    }
+
+    enum Action {
+      bootstrap, clean, start, stop, update_interpreter
+    }
+
+    private boolean isValidAction(String userAction) {
+      try {
+        return Action.valueOf(userAction) != null;
+      } catch (IllegalArgumentException e) { // Thrown when the user-specified action is unrepresented in enum.
+        return false;
+      }
+    }
+
+    @Override
+    protected void runImpl(CommandLine cli) throws Exception {
+      final String zeppelinUrl = cli.getOptionValue("zeppelinUrl", "http://localhost:8080");
+      final String solrUrl = cli.getOptionValue("solrUrl");
+      final String[] positionalArgs = cli.getArgs();
+
+      // Validate provided action
+      final String validActions = Arrays.stream(Action.values()).map(x -> x.toString()).collect(Collectors.joining(",", "[", "]"));
+      if (positionalArgs.length != 1) {
+        echo("Zeppelin action not provided.  Valid values are " + validActions);
+        exit(1);
+      } else if ("help".equals(positionalArgs[0])) {
+        printHelpText();
+        exit(0);
+      } else if (! isValidAction(positionalArgs[0])) {
+        echo("Invalid zeppelin action [" + positionalArgs[0] + "] provided.  Valid values are " + validActions);
+        exit(1);
+      }
+
+      final Action action = Action.valueOf(positionalArgs[0].toLowerCase(Locale.ROOT));
+      switch (action) {
+        case bootstrap:
+          bootstrapZeppelinInstallation(cli, zeppelinUrl, solrUrl);
+          break;
+        case clean:
+          cleanZeppelinInstallation(cli);
+          break;
+        case start:
+          startZeppelinInstall();
+          break;
+        case stop:
+          stopZeppelin(cli);
+          break;
+        case update_interpreter:
+          updateSolrInterpreter(cli, zeppelinUrl, solrUrl);
+          break;
+        default:
+          echo("Invalid action value [" + action + "]; unable to proceed");
+          exit(1);
+      }
+    }
+
+    private void bootstrapZeppelinInstallation(CommandLine cli, String zeppelinUrl, String solrUrl) throws Exception {
+      final File zeppelinBaseDirFile = new File(ZEPP_BASE_ABS_PATH);
+      if (! zeppelinBaseDirFile.exists()) {
+        final boolean result = zeppelinBaseDirFile.mkdir();
+        if (! result) {
+          echo("Unable to create base directory [" + ZEPP_BASE_ABS_PATH + "] for Zeppelin install; exiting.");
+          exit(1);
+        }
+      }
+      echoIfVerbose("Zeppelin base dir created successfully at " + ZEPP_BASE_ABS_PATH, cli);
+
+      final File zeppelinArchiveFile = new File(ZEPP_ARCHIVE_ABS_PATH);
+      if (! zeppelinArchiveFile.exists()) {
+        echo("Downloading zeppelin; this may take a few minutes");
+        FileUtils.copyURLToFile(new URL(ZEPP_ARCHIVE_URL), zeppelinArchiveFile);
+      }
+
+      final File unpackedZeppelinArchiveFile = new File(ZEPP_UNPACKED_ABS_PATH);
+      if (! unpackedZeppelinArchiveFile.exists()) {
+        final File unpackLogs = new File(ZEPP_UNPACK_LOG_ABS_PATH);
+        Process ps = new ProcessBuilder().command("tar", "-xvf", ZEPP_ARCHIVE_ABS_PATH)
+            .redirectError(unpackLogs)
+            .redirectOutput(unpackLogs)
+            .directory(zeppelinBaseDirFile)
+            .start();
+        final boolean completed = ps.waitFor(PROCESS_WAIT_TIME_SECONDS, TimeUnit.SECONDS);
+        if (! completed) {
+          echo("Zeppelin could not be unpacked within " + PROCESS_WAIT_TIME_SECONDS + "s; unable to install Zeppelin");
+          exit(1);
+        }
+
+        if (0 != ps.exitValue()) {
+          echo("Attempt to unpack Zeppelin archive failed with code " + ps.exitValue() + "; exiting");
+          exit(1);
+        } else {
+          echoIfVerbose("Zeppelin successfully downloaded and unpacked to " + zeppelinBaseDirFile.getAbsolutePath(), cli);
+        }
+
+        if (unpackLogs.exists()) {
+          unpackLogs.delete();
+        }
+      }
+
+      echoIfVerbose("Finished initializing Zeppelin sandbox, attempting to start zeppelin...", cli);
+      startZeppelinInstall();
+      echoIfVerbose("Finished starting zeppelin, attempting to update zeppelin-solr plugin", cli);
+      runCommand(ZEPP_INTERP_ABS_PATH, "--name", "solr", "--artifact", "com.lucidworks.zeppelin:zeppelin-solr:0.1.6");
+      echoIfVerbose("Finished installing zeppelin-solr plugin, attempting to restart zepplin", cli);
+      runCommand(ZEPP_DAEMON_ABS_PATH, "restart");
+      echoIfVerbose("Finished restarting zeppelin, updating 'solr' interpreter to point to Solr URL: " + solrUrl, cli);
+      updateSolrInterpreter(cli, zeppelinUrl, solrUrl);
+    }
+
+    private void cleanZeppelinInstallation(CommandLine cli) throws Exception {
+     if (new File(ZEPP_DAEMON_ABS_PATH).exists() && isZeppelinRunning()) {
+        echoIfVerbose("Stopping zeppelin prior to 'clean'", cli);
+        stopZeppelin(cli);
+      }
+
+      FileUtils.deleteDirectory(new File(ZEPP_BASE_ABS_PATH));
+    }
+
+    private void startZeppelinInstall() throws Exception {
+        runCommand(ZEPP_DAEMON_ABS_PATH, "start");
+    }
+
+    private void stopZeppelin(CommandLine cli) throws Exception {
+      if (! new File(ZEPP_DAEMON_ABS_PATH).exists()) {
+        echoIfVerbose("No Zeppelin sandbox exists to stop; done.", cli);
+        exit(0);
+      }
+
+      if (! isZeppelinRunning()) {
+        echoIfVerbose("Zeppelin was already stopped", cli);
+        exit(0);
+      }
+
+      echoIfVerbose("Stopping Zeppelin using executable: " + ZEPP_DAEMON_ABS_PATH, cli);
+      runCommand(ZEPP_DAEMON_ABS_PATH, "stop");
+    }
+
+    private void ensureZeppelinAvailableAtUrl(CommandLine cli, String zeppelinUrl) throws Exception {
+      final HttpGet request = new HttpGet(new URI(zeppelinUrl + "/api/interpreter"));
+      final int maxAttempts = 30;
+      boolean success = false;
+      String lastErrorMessage = null;
+
+
+      try (final CloseableHttpClient httpClient = HttpClientUtil.createClient(new ModifiableSolrParams())) {
+        for (int i = 0; i < maxAttempts; i++) {
+          try {
+            final HttpResponse response = httpClient.execute(request);
+            if (response.getStatusLine().getStatusCode() <= 299) {
+              success = true;
+              break;
+            } else {
+              final ByteArrayOutputStream entityStream = new ByteArrayOutputStream();
+              response.getEntity().writeTo(entityStream);
+              lastErrorMessage = new String(entityStream.toByteArray(), StandardCharsets.UTF_8);
+            }
+          } catch (ConnectException e) {
+            lastErrorMessage = e.getMessage();
+          }
+          echoIfVerbose("Failed to reach zeppelin at url [" + zeppelinUrl + "]; error was: " + lastErrorMessage + ", retrying.", cli);
+          Thread.sleep(1000);
+        }
+
+        if (! success) {
+          echo("Failed to find Zeppelin at URL [" + zeppelinUrl + "].  Last error was: " + lastErrorMessage);
+          exit(-1);
+        }
+      }
+    }
+
+    private void updateSolrInterpreter(CommandLine cli, String zeppelinUrl, String solrUrl) throws Exception {
+      final File interpreterEntityFile = createInterpreterFromTemplate(solrUrl);
+      echoIfVerbose("Updating interpreter to have solrUrl: " + solrUrl, cli);
+      ensureZeppelinAvailableAtUrl(cli, zeppelinUrl);
+      // Zeppelin-solr creates the 'solr' interpreter setting upon install, so it always exists for a 'PUT' here.
+      try (final CloseableHttpClient httpClient = HttpClientUtil.createClient(new ModifiableSolrParams())) {
+        final HttpPut request = new HttpPut();
+        final URI requestURI = new URI(zeppelinUrl + "/api/interpreter/setting/" + ZEPP_SOLR_INTERPRETER_ID);
+        request.setURI(requestURI);
+        request.setEntity(new FileEntity(interpreterEntityFile, ContentType.APPLICATION_JSON));
+        final HttpResponse response = httpClient.execute(request);
+        if (response.getStatusLine().getStatusCode() > 299) {
+          echo("Received [" + response.getStatusLine().getStatusCode() + "] status when creating Solr interpreter; aborting.");
+          response.getEntity().writeTo(System.err);
+          exit(1);
+        }
+      } catch (Exception e) {
+        echo("Error encountered creating/updating Solr interpreter " + e.getMessage());
+        e.printStackTrace(System.err);
+        exit(1);
+      }
+
+      interpreterEntityFile.delete();
+      echoIfVerbose("Successfully created Solr interpreter", cli);
+    }
+
+    private File createInterpreterFromTemplate(String solrUrl) throws IOException {
+      final File interpreterTemplateFile = new File(ZEPP_INTERPRETER_TEMPLATE_ABS_PATH);
+      final String interpreterTemplate = FileUtils.readFileToString(interpreterTemplateFile, StandardCharsets.UTF_8);
+      final String interpreter = interpreterTemplate.replaceAll("@@SOLR_URL@@", solrUrl);
+
+      final File interpreterFile = new File(ZEPP_INTERPRETER_ABS_PATH);
+      FileUtils.writeStringToFile(interpreterFile, interpreter, StandardCharsets.UTF_8, false);
+      return interpreterFile;
+    }
+
+    private boolean isZeppelinRunning() throws Exception {
+      final int cmdStatus = runCommandForStatus(ZEPP_DAEMON_ABS_PATH, "restart");
+      return cmdStatus == 0;
+    }
+
+    private int runCommandForStatus(String executable, String... args) throws Exception {
+      final List<String> allArgs = new ArrayList<>();
+      allArgs.add(executable);
+      for (String arg : args) allArgs.add(arg);
+
+      final Process ps = new ProcessBuilder().command(allArgs).start();

Review comment:
       *COMMAND_INJECTION:*  This usage of java/lang/ProcessBuilder.command(Ljava/util/List;)Ljava/lang/ProcessBuilder; can be vulnerable to Command Injection [(details)](https://find-sec-bugs.github.io/bugs.htm#COMMAND_INJECTION)
   (at-me [in a reply](https://help.sonatype.com/lift) with `help` or `ignore`)

##########
File path: solr/core/src/java/org/apache/solr/util/SolrCLI.java
##########
@@ -3689,6 +3697,329 @@ public AssertionFailureException(String message) {
     }
   }
 
+  public static class ZeppelinTool extends ToolBase {
+    private static final String ZEPP_VERSION = "0.10.0";
+    private static final String ZEPP_INSTALL_TYPE = "netinst";
+    private static final String ZEPP_UNPACK_DIR_NAME = "zeppelin-" + ZEPP_VERSION + "-bin-" + ZEPP_INSTALL_TYPE;
+    private static final String MIRROR_BASE = "http://apache.cs.utah.edu/zeppelin/zeppelin-" + ZEPP_VERSION + "/";
+    private static final String ZEPP_ARCHIVE = ZEPP_UNPACK_DIR_NAME + ".tgz";
+    private static final String ZEPP_ARCHIVE_URL = MIRROR_BASE + ZEPP_ARCHIVE;
+    private static final int PROCESS_WAIT_TIME_SECONDS = 90;
+
+    private static final String SOLR_INSTALL_DIR = System.getProperty("solr.install.dir");
+    private static final String ZEPP_INTERPRETER_TEMPLATE_ABS_PATH = Paths.get(SOLR_INSTALL_DIR, "server", "resources", "zeppelin-solr-interpreter.json.template").toAbsolutePath().toString();
+    private static final String ZEPP_INTERPRETER_ABS_PATH = Paths.get(SOLR_INSTALL_DIR, "server", "resources", "zeppelin-solr-interpreter.json").toAbsolutePath().toString();
+    private static final String ZEPP_BASE_ABS_PATH = Paths.get(SOLR_INSTALL_DIR, "zeppelin").toAbsolutePath().toString();
+    private static final String ZEPP_UNPACK_LOG_ABS_PATH = Paths.get(ZEPP_BASE_ABS_PATH, "untar-logs.txt").toAbsolutePath().toString();
+    private static final String ZEPP_ARCHIVE_ABS_PATH = Paths.get(ZEPP_BASE_ABS_PATH, ZEPP_ARCHIVE).toAbsolutePath().toString();
+    private static final String ZEPP_UNPACKED_ABS_PATH = Paths.get(ZEPP_BASE_ABS_PATH, ZEPP_UNPACK_DIR_NAME).toAbsolutePath().toString();
+    private static final String ZEPP_DAEMON_EXE_NAME = "zeppelin-daemon.sh"; // Only valid on *nix
+    private static final String ZEPP_INTERP_EXE_NAME = "install-interpreter.sh"; // Only valid on *nix
+    private static final String ZEPP_DAEMON_ABS_PATH = Paths.get(ZEPP_UNPACKED_ABS_PATH, "bin", ZEPP_DAEMON_EXE_NAME).toAbsolutePath().toString();
+    private static final String ZEPP_INTERP_ABS_PATH = Paths.get(ZEPP_UNPACKED_ABS_PATH, "bin", ZEPP_INTERP_EXE_NAME).toAbsolutePath().toString();
+    private static final String ZEPP_SOLR_INTERPRETER_ID = "solr";
+
+    @Override
+    public String getName() { return "zeppelin"; }
+
+    @Override
+    public Option[] getOptions() {
+      return new Option[] {
+              Option.builder("z")
+                      .desc("Only valid for action=update-interpreter.  The Zeppelin URL to update.  Defaults to http://localhost:8080")
+                      .longOpt("zeppelinUrl")
+                      .hasArg()
+                      .argName("url")
+                      .build(),
+              Option.builder("s")
+                      .desc("The URL of a valid Solr instance. (e.g. http://localhost:7574/solr)  Used to configure the" +
+                              " Zeppelin interpreter.  Defaults to values read from solr.in.sh")
+                      .longOpt("solrUrl")
+                      .hasArg()
+                      .argName("url")
+                      .required()
+                      .build()
+      };
+    }
+
+    /*
+     * Since ZeppelinTool uses positional arguments not represented in getOptions, the help text generated from those
+     * values alone doesn't really cover the tool appropriately.  This method returns custom help text, similar to that
+     * hardcoded in the solr/solr.cmd scripts themselves.
+     */
+    @Override
+    public void printHelpText() {
+      stdout.println("Usage: solr zeppelin bootstrap [--zeppelinUrl <url>] [--solrUrl <url>]");
+      stdout.println("       solr zeppelin clean");
+      stdout.println("       solr zeppelin start");
+      stdout.println("       solr zeppelin stop");
+      stdout.println("       solr zeppelin update_interpreter [--zeppelinUrl <url>] [--solrUrl <url>]");
+      stdout.println();
+      stdout.println("  Sets up and manages an Apache Zeppelin installation configured for use with a local Solr.");
+      stdout.println("    - 'bootstrap' downloads and starts Zeppelin in a 'zeppelin' folder within the Solr install");
+      stdout.println("    - 'clean' stops Zeppelin (if necessary) and removes the install created by 'bootstrap'.");
+      stdout.println("    - 'start' starts Zeppelin if not already running.");
+      stdout.println("    - 'stop' stops Zeppelin if not already stopped.");
+      stdout.println("    - 'update_interpreter' reinstalls the Zeppelin interpreter used to talk to Solr.  Useful for");
+      stdout.println("       pointing Zeppelin to a different Solr cluster or changing other settings.");
+      stdout.println();
+      stdout.println("  This tool relies on management scripts that ship with Zeppelin - several of which are not");
+      stdout.println("  available on Windows.  As a result this tool isn't supported in Windows environments at this time");
+      stdout.println();
+
+      // Append option information to help text
+      final HelpFormatter helpFormatter = new HelpFormatter();
+      try (final PrintWriter writer = new PrintWriter(stdout, true, StandardCharsets.UTF_8)) {
+        helpFormatter.printOptions(writer, 100, getToolOptions(this), helpFormatter.getLeftPadding(),
+                helpFormatter.getDescPadding());
+      }
+    }
+
+    enum Action {
+      bootstrap, clean, start, stop, update_interpreter
+    }
+
+    private boolean isValidAction(String userAction) {
+      try {
+        return Action.valueOf(userAction) != null;
+      } catch (IllegalArgumentException e) { // Thrown when the user-specified action is unrepresented in enum.
+        return false;
+      }
+    }
+
+    @Override
+    protected void runImpl(CommandLine cli) throws Exception {
+      final String zeppelinUrl = cli.getOptionValue("zeppelinUrl", "http://localhost:8080");
+      final String solrUrl = cli.getOptionValue("solrUrl");
+      final String[] positionalArgs = cli.getArgs();
+
+      // Validate provided action
+      final String validActions = Arrays.stream(Action.values()).map(x -> x.toString()).collect(Collectors.joining(",", "[", "]"));
+      if (positionalArgs.length != 1) {
+        echo("Zeppelin action not provided.  Valid values are " + validActions);
+        exit(1);
+      } else if ("help".equals(positionalArgs[0])) {
+        printHelpText();
+        exit(0);
+      } else if (! isValidAction(positionalArgs[0])) {
+        echo("Invalid zeppelin action [" + positionalArgs[0] + "] provided.  Valid values are " + validActions);
+        exit(1);
+      }
+
+      final Action action = Action.valueOf(positionalArgs[0].toLowerCase(Locale.ROOT));
+      switch (action) {
+        case bootstrap:
+          bootstrapZeppelinInstallation(cli, zeppelinUrl, solrUrl);
+          break;
+        case clean:
+          cleanZeppelinInstallation(cli);
+          break;
+        case start:
+          startZeppelinInstall();
+          break;
+        case stop:
+          stopZeppelin(cli);
+          break;
+        case update_interpreter:
+          updateSolrInterpreter(cli, zeppelinUrl, solrUrl);
+          break;
+        default:
+          echo("Invalid action value [" + action + "]; unable to proceed");
+          exit(1);
+      }
+    }
+
+    private void bootstrapZeppelinInstallation(CommandLine cli, String zeppelinUrl, String solrUrl) throws Exception {
+      final File zeppelinBaseDirFile = new File(ZEPP_BASE_ABS_PATH);
+      if (! zeppelinBaseDirFile.exists()) {
+        final boolean result = zeppelinBaseDirFile.mkdir();
+        if (! result) {
+          echo("Unable to create base directory [" + ZEPP_BASE_ABS_PATH + "] for Zeppelin install; exiting.");
+          exit(1);
+        }
+      }
+      echoIfVerbose("Zeppelin base dir created successfully at " + ZEPP_BASE_ABS_PATH, cli);
+
+      final File zeppelinArchiveFile = new File(ZEPP_ARCHIVE_ABS_PATH);
+      if (! zeppelinArchiveFile.exists()) {
+        echo("Downloading zeppelin; this may take a few minutes");
+        FileUtils.copyURLToFile(new URL(ZEPP_ARCHIVE_URL), zeppelinArchiveFile);
+      }
+
+      final File unpackedZeppelinArchiveFile = new File(ZEPP_UNPACKED_ABS_PATH);
+      if (! unpackedZeppelinArchiveFile.exists()) {
+        final File unpackLogs = new File(ZEPP_UNPACK_LOG_ABS_PATH);
+        Process ps = new ProcessBuilder().command("tar", "-xvf", ZEPP_ARCHIVE_ABS_PATH)

Review comment:
       *COMMAND_INJECTION:*  This usage of java/lang/ProcessBuilder.command([Ljava/lang/String;)Ljava/lang/ProcessBuilder; can be vulnerable to Command Injection [(details)](https://find-sec-bugs.github.io/bugs.htm#COMMAND_INJECTION)
   (at-me [in a reply](https://help.sonatype.com/lift) with `help` or `ignore`)




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org