sonatype-lift[bot] commented on code in PR #1966:
URL: https://github.com/apache/zookeeper/pull/1966#discussion_r1049123156


##########
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/JettyAdminServer.java:
##########
@@ -257,17 +263,84 @@ protected void doGet(
             for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                 kwargs.put(entry.getKey(), entry.getValue()[0]);
             }
+            final String authInfo = 
request.getHeader(HttpHeader.AUTHORIZATION.asString());
 
             // Run the command
-            CommandResponse cmdResponse = Commands.runCommand(cmd, zkServer, 
kwargs);
+            final CommandResponse cmdResponse = Commands.runCommand(cmd, 
zkServer, kwargs, null, authInfo, request);
+            response.setStatus(cmdResponse.getStatusCode());
 
-            // Format and print the output of the command
-            CommandOutputter outputter = new JsonOutputter();
-            response.setStatus(HttpServletResponse.SC_OK);
+            final Map<String, String> headers = cmdResponse.getHeaders();
+            for (final Map.Entry<String, String> header : headers.entrySet()) {
+                response.addHeader(header.getKey(), header.getValue());
+            }
+            final String clientIP = 
IPAuthenticationProvider.getClientIPAddress(request);
+            if (cmdResponse.getInputStream() == null) {
+                // Format and print the output of the command
+                CommandOutputter outputter = new JsonOutputter(clientIP);
+                response.setContentType(outputter.getContentType());
+                outputter.output(cmdResponse, response.getWriter());
+            } else {
+                // Stream out the output of the command
+                CommandOutputter outputter = new StreamOutputter(clientIP);
+                response.setContentType(outputter.getContentType());
+                outputter.output(cmdResponse, response.getOutputStream());
+            }
+        }
+
+        /**
+         * Serves HTTP POST requests. It reads request payload as raw data.
+         * It's up to each command to process the payload accordingly.
+         * For example, RestoreCommand uses the payload InputStream directly
+         * to read snapshot data.
+         */
+        @Override
+        protected void doPost(final HttpServletRequest request,
+                              final HttpServletResponse response) throws 
ServletException, IOException {
+            final String cmdName = extractCommandNameFromURL(request, 
response);
+            if (cmdName != null) {
+                final String authInfo = 
request.getHeader(HttpHeader.AUTHORIZATION.asString());
+                final CommandResponse cmdResponse = 
Commands.runCommand(cmdName, zkServer, null, request.getInputStream(), 
authInfo, request);
+                final String clientIP = 
IPAuthenticationProvider.getClientIPAddress(request);
+                sendJSONResponse(response, cmdResponse, clientIP);
+            }
+        }
+
+        /**
+         * Extracts the command name from URL if it exists otherwise null
+         */
+        private String extractCommandNameFromURL(final HttpServletRequest 
request,
+                                                 final HttpServletResponse 
response) throws IOException {
+            String cmd = request.getPathInfo();
+            if (cmd == null || cmd.equals("/")) {
+                printCommandLinks(response);
+                return null;
+            }
+            // Strip leading "/"
+            return cmd.substring(1);
+        }
+
+        /**
+         * Prints the list of URLs to each registered command as response.
+         */
+        private void printCommandLinks(final HttpServletResponse response) 
throws IOException {
+            for (final String link : commandLinks()) {
+                response.getWriter().println(link);

Review Comment:
   *[XSS_SERVLET](https://find-sec-bugs.github.io/bugs.htm#XSS_SERVLET):*  This 
use of java/io/PrintWriter.println(Ljava/lang/String;)V could be vulnerable to 
XSS in the Servlet
   
   ---
   
   <details><summary><b>â„šī¸ Learn about @sonatype-lift commands</b></summary>
   
   You can reply with the following commands. For example, reply with 
***@sonatype-lift ignoreall*** to leave out all findings.
   | **Command** | **Usage** |
   | ------------- | ------------- |
   | `@sonatype-lift ignore` | Leave out the above finding from this PR |
   | `@sonatype-lift ignoreall` | Leave out all the existing findings from this 
PR |
   | `@sonatype-lift exclude <file\|issue\|path\|tool>` | Exclude specified 
`file\|issue\|path\|tool` from Lift findings by updating your config.toml file |
   
   **Note:** When talking to LiftBot, you need to **refresh** the page to see 
its response.
   <sub>[Click here](https://github.com/apps/sonatype-lift/installations/new) 
to add LiftBot to another repo.</sub></details>
   
   
   
   ---
   
   Was this a good recommendation?
   [ [🙁 Not 
relevant](https://www.sonatype.com/lift-comment-rating?comment=361419525&lift_comment_rating=1)
 ] - [ [😕 Won't 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419525&lift_comment_rating=2)
 ] - [ [😑 Not critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419525&lift_comment_rating=3)
 ] - [ [🙂 Critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419525&lift_comment_rating=4)
 ] - [ [😊 Critical, fixing 
now](https://www.sonatype.com/lift-comment-rating?comment=361419525&lift_comment_rating=5)
 ]



##########
zookeeper-server/src/main/java/org/apache/zookeeper/server/ZKDatabase.java:
##########
@@ -620,6 +623,43 @@ public void deserializeSnapshot(InputArchive ia) throws 
IOException {
         initialized = true;
     }
 
+    /**
+     * Deserialize a snapshot that contains FileHeader from an input archive. 
It is used by
+     * the admin restore command.
+     *
+     * @param ia the input archive to deserialize from
+     * @param is the CheckInputStream to check integrity
+     *
+     * @throws IOException
+     */
+    public void deserializeSnapshot(final InputArchive ia, final 
CheckedInputStream is) throws IOException {
+        clear();

Review Comment:
   <picture><img alt="8% of developers fix this issue" 
src="https://lift.sonatype.com/api/commentimage/fixrate/8/display.svg";></picture>
   
   *THREAD_SAFETY_VIOLATION:*  Unprotected write. Non-private method 
`ZKDatabase.deserializeSnapshot(...)` indirectly writes to field 
`this.dataTree` outside of synchronization.
    Reporting because another access to the same memory occurs on a background 
thread, although this access may not.
   
   ---
   
   <details><summary><b>â„šī¸ Learn about @sonatype-lift commands</b></summary>
   
   You can reply with the following commands. For example, reply with 
***@sonatype-lift ignoreall*** to leave out all findings.
   | **Command** | **Usage** |
   | ------------- | ------------- |
   | `@sonatype-lift ignore` | Leave out the above finding from this PR |
   | `@sonatype-lift ignoreall` | Leave out all the existing findings from this 
PR |
   | `@sonatype-lift exclude <file\|issue\|path\|tool>` | Exclude specified 
`file\|issue\|path\|tool` from Lift findings by updating your config.toml file |
   
   **Note:** When talking to LiftBot, you need to **refresh** the page to see 
its response.
   <sub>[Click here](https://github.com/apps/sonatype-lift/installations/new) 
to add LiftBot to another repo.</sub></details>
   
   
   
   ---
   
   Was this a good recommendation?
   [ [🙁 Not 
relevant](https://www.sonatype.com/lift-comment-rating?comment=361419637&lift_comment_rating=1)
 ] - [ [😕 Won't 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419637&lift_comment_rating=2)
 ] - [ [😑 Not critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419637&lift_comment_rating=3)
 ] - [ [🙂 Critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419637&lift_comment_rating=4)
 ] - [ [😊 Critical, fixing 
now](https://www.sonatype.com/lift-comment-rating?comment=361419637&lift_comment_rating=5)
 ]



##########
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/JettyAdminServer.java:
##########
@@ -257,17 +263,84 @@ protected void doGet(
             for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                 kwargs.put(entry.getKey(), entry.getValue()[0]);
             }
+            final String authInfo = 
request.getHeader(HttpHeader.AUTHORIZATION.asString());
 
             // Run the command
-            CommandResponse cmdResponse = Commands.runCommand(cmd, zkServer, 
kwargs);
+            final CommandResponse cmdResponse = Commands.runCommand(cmd, 
zkServer, kwargs, null, authInfo, request);
+            response.setStatus(cmdResponse.getStatusCode());
 
-            // Format and print the output of the command
-            CommandOutputter outputter = new JsonOutputter();
-            response.setStatus(HttpServletResponse.SC_OK);
+            final Map<String, String> headers = cmdResponse.getHeaders();
+            for (final Map.Entry<String, String> header : headers.entrySet()) {
+                response.addHeader(header.getKey(), header.getValue());
+            }
+            final String clientIP = 
IPAuthenticationProvider.getClientIPAddress(request);
+            if (cmdResponse.getInputStream() == null) {
+                // Format and print the output of the command
+                CommandOutputter outputter = new JsonOutputter(clientIP);
+                response.setContentType(outputter.getContentType());
+                outputter.output(cmdResponse, response.getWriter());
+            } else {
+                // Stream out the output of the command
+                CommandOutputter outputter = new StreamOutputter(clientIP);
+                response.setContentType(outputter.getContentType());
+                outputter.output(cmdResponse, response.getOutputStream());
+            }
+        }
+
+        /**
+         * Serves HTTP POST requests. It reads request payload as raw data.
+         * It's up to each command to process the payload accordingly.
+         * For example, RestoreCommand uses the payload InputStream directly
+         * to read snapshot data.
+         */
+        @Override
+        protected void doPost(final HttpServletRequest request,
+                              final HttpServletResponse response) throws 
ServletException, IOException {
+            final String cmdName = extractCommandNameFromURL(request, 
response);
+            if (cmdName != null) {
+                final String authInfo = 
request.getHeader(HttpHeader.AUTHORIZATION.asString());
+                final CommandResponse cmdResponse = 
Commands.runCommand(cmdName, zkServer, null, request.getInputStream(), 
authInfo, request);

Review Comment:
   *SHELL_INJECTION:*  UserControlledString(HttpServletRequest.getHeader(...)) 
at line 301 ~> ClassLoading(ClassLoader.loadClass(...)) in procedure 
ProviderRegistry.addOrUpdateProvider(...) at line 69.
   
   ---
   
   <details><summary><b>â„šī¸ Learn about @sonatype-lift commands</b></summary>
   
   You can reply with the following commands. For example, reply with 
***@sonatype-lift ignoreall*** to leave out all findings.
   | **Command** | **Usage** |
   | ------------- | ------------- |
   | `@sonatype-lift ignore` | Leave out the above finding from this PR |
   | `@sonatype-lift ignoreall` | Leave out all the existing findings from this 
PR |
   | `@sonatype-lift exclude <file\|issue\|path\|tool>` | Exclude specified 
`file\|issue\|path\|tool` from Lift findings by updating your config.toml file |
   
   **Note:** When talking to LiftBot, you need to **refresh** the page to see 
its response.
   <sub>[Click here](https://github.com/apps/sonatype-lift/installations/new) 
to add LiftBot to another repo.</sub></details>
   
   
   
   ---
   
   Was this a good recommendation?
   [ [🙁 Not 
relevant](https://www.sonatype.com/lift-comment-rating?comment=361420208&lift_comment_rating=1)
 ] - [ [😕 Won't 
fix](https://www.sonatype.com/lift-comment-rating?comment=361420208&lift_comment_rating=2)
 ] - [ [😑 Not critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361420208&lift_comment_rating=3)
 ] - [ [🙂 Critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361420208&lift_comment_rating=4)
 ] - [ [😊 Critical, fixing 
now](https://www.sonatype.com/lift-comment-rating?comment=361420208&lift_comment_rating=5)
 ]



##########
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/JettyAdminServer.java:
##########
@@ -257,17 +263,84 @@ protected void doGet(
             for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                 kwargs.put(entry.getKey(), entry.getValue()[0]);
             }
+            final String authInfo = 
request.getHeader(HttpHeader.AUTHORIZATION.asString());
 
             // Run the command
-            CommandResponse cmdResponse = Commands.runCommand(cmd, zkServer, 
kwargs);
+            final CommandResponse cmdResponse = Commands.runCommand(cmd, 
zkServer, kwargs, null, authInfo, request);

Review Comment:
   *SHELL_INJECTION:*  UserControlledString(HttpServletRequest.getHeader(...)) 
at line 266 ~> ClassLoading(ClassLoader.loadClass(...)) in procedure 
ProviderRegistry.addOrUpdateProvider(...) at line 69.
   
   ---
   
   <details><summary><b>â„šī¸ Learn about @sonatype-lift commands</b></summary>
   
   You can reply with the following commands. For example, reply with 
***@sonatype-lift ignoreall*** to leave out all findings.
   | **Command** | **Usage** |
   | ------------- | ------------- |
   | `@sonatype-lift ignore` | Leave out the above finding from this PR |
   | `@sonatype-lift ignoreall` | Leave out all the existing findings from this 
PR |
   | `@sonatype-lift exclude <file\|issue\|path\|tool>` | Exclude specified 
`file\|issue\|path\|tool` from Lift findings by updating your config.toml file |
   
   **Note:** When talking to LiftBot, you need to **refresh** the page to see 
its response.
   <sub>[Click here](https://github.com/apps/sonatype-lift/installations/new) 
to add LiftBot to another repo.</sub></details>
   
   
   
   ---
   
   Was this a good recommendation?
   [ [🙁 Not 
relevant](https://www.sonatype.com/lift-comment-rating?comment=361420264&lift_comment_rating=1)
 ] - [ [😕 Won't 
fix](https://www.sonatype.com/lift-comment-rating?comment=361420264&lift_comment_rating=2)
 ] - [ [😑 Not critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361420264&lift_comment_rating=3)
 ] - [ [🙂 Critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361420264&lift_comment_rating=4)
 ] - [ [😊 Critical, fixing 
now](https://www.sonatype.com/lift-comment-rating?comment=361420264&lift_comment_rating=5)
 ]



##########
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java:
##########
@@ -92,24 +112,97 @@ public static void registerCommand(Command command) {
      *
      * @param cmdName
      * @param zkServer
-     * @param kwargs String-valued keyword arguments to the command
+     * @param kwargs String-valued keyword arguments to the command from HTTP 
GET request
      *        (may be null if command requires no additional arguments)
+     * @param inputStream inputStream from HTTP POST request
+     *        (null if it's HTTP GET request)
+     * @param authInfo auth info for auth check
+     *        (null if command requires no auth check)
+     * @param request HTTP request
      * @return Map representing response to command containing at minimum:
      *    - "command" key containing the command's primary name
      *    - "error" key containing a String error message or null if no error
      */
     public static CommandResponse runCommand(
-        String cmdName,
-        ZooKeeperServer zkServer,
-        Map<String, String> kwargs) {
+            String cmdName,
+            ZooKeeperServer zkServer,
+            Map<String, String> kwargs,
+            InputStream inputStream,
+            String authInfo,
+            HttpServletRequest request) {
         Command command = getCommand(cmdName);
         if (command == null) {
-            return new CommandResponse(cmdName, "Unknown command: " + cmdName);
+            // set the status code to 200 to keep the current behavior of 
existing commands
+            LOG.warn("Unknown command: {}", cmdName);
+            return new CommandResponse(cmdName, "Unknown command: " + cmdName, 
HttpServletResponse.SC_OK);
         }
         if (command.isServerRequired() && (zkServer == null || 
!zkServer.isRunning())) {
-            return new CommandResponse(cmdName, "This ZooKeeper instance is 
not currently serving requests");
+            // set the status code to 200 to keep the current behavior of 
existing commands
+            LOG.warn("This ZooKeeper instance is not currently serving 
requests for command: {}", cmdName);
+            return new CommandResponse(cmdName, "This ZooKeeper instance is 
not currently serving requests", HttpServletResponse.SC_OK);
         }
-        return command.run(zkServer, kwargs);
+
+        final AuthRequest authRequest = command.getAuthRequest();
+        if (authRequest != null) {
+            if (authInfo == null) {
+                LOG.warn("Auth info is missing for command: {}", cmdName);
+                return new CommandResponse(cmdName, "Auth info is missing for 
the command", HttpServletResponse.SC_UNAUTHORIZED);
+            }
+            try {
+                final List<Id> ids = handleAuthentication(request, authInfo);
+                handleAuthorization(zkServer, ids, 
authRequest.getPermission(), authRequest.getPath());
+            } catch (final KeeperException.AuthFailedException e) {
+                return new CommandResponse(cmdName, "Not authenticated", 
HttpServletResponse.SC_UNAUTHORIZED);
+            } catch (final KeeperException.NoAuthException e) {
+                return new CommandResponse(cmdName, "Not authorized", 
HttpServletResponse.SC_FORBIDDEN);
+            } catch (final Exception e) {
+                LOG.warn("Error occurred during auth for command: {}", 
cmdName, e);
+                return new CommandResponse(cmdName, "Error occurred during 
auth", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+        }
+        return command.run(zkServer, kwargs, inputStream);
+    }
+
+    private static List<Id> handleAuthentication(final HttpServletRequest 
request, final String authInfo) throws KeeperException.AuthFailedException {
+        final String[] authData = authInfo.split(AUTH_INFO_SEPARATOR);
+        // for IP and x509, auth info only contains the schema and Auth Id 
will be extracted from HTTP request
+        if (authData.length != 1 && authData.length != 2) {
+            LOG.warn("Invalid auth info: {}", authInfo);
+            throw new KeeperException.AuthFailedException();
+        }
+
+        final String schema = authData[0];
+        final ServerAuthenticationProvider authProvider = 
ProviderRegistry.getServerProvider(schema);
+        if (authProvider != null) {
+            try {
+                final byte[] auth = authData.length == 2 ? 
authData[1].getBytes(StandardCharsets.UTF_8) : null;
+                final List<Id> ids = 
authProvider.handleAuthentication(request, auth);
+                if (ids.isEmpty()) {
+                    LOG.warn("Auth Id list is empty: {}", authInfo);

Review Comment:
   <picture><img alt="20% of developers fix this issue" 
src="https://lift.sonatype.com/api/commentimage/fixrate/20/display.svg";></picture>
   
   đŸ’Ŧ 5 similar findings have been found in this PR
   
   ---
   
   
*[CRLF_INJECTION_LOGS](https://find-sec-bugs.github.io/bugs.htm#CRLF_INJECTION_LOGS):*
  This use of org/slf4j/Logger.warn(Ljava/lang/String;Ljava/lang/Object;)V 
might be used to include CRLF characters into log messages
   
   ---
   
   <details><summary><b>🔎 Expand here to view all instances of this 
finding</b></summary><br/>
     
     
   <div align=\"center\">
   
   
   | **File Path** | **Line Number** |
   | ------------- | ------------- |
   | 
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java 
| 
[170](https://github.com/apache/zookeeper/blob/322458ec920c23cb7bfe778250e1574565dce464/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java#L170)
 |
   | 
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java 
| 
[190](https://github.com/apache/zookeeper/blob/322458ec920c23cb7bfe778250e1574565dce464/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java#L190)
 |
   | 
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java 
| 
[148](https://github.com/apache/zookeeper/blob/322458ec920c23cb7bfe778250e1574565dce464/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java#L148)
 |
   | 
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java 
| 
[141](https://github.com/apache/zookeeper/blob/322458ec920c23cb7bfe778250e1574565dce464/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java#L141)
 |
   | 
zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java 
| 
[136](https://github.com/apache/zookeeper/blob/322458ec920c23cb7bfe778250e1574565dce464/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java#L136)
 |
   <p><a 
href="https://lift.sonatype.com/results/github.com/apache/zookeeper/01GM9N64NEZK9Q7F2RCV474B5V?t=FindSecBugs|CRLF_INJECTION_LOGS"
 target="_blank">Visit the Lift Web Console</a> to find more details in your 
report.</p></div></details>
   
   
   
   ---
   
   <details><summary><b>â„šī¸ Learn about @sonatype-lift commands</b></summary>
   
   You can reply with the following commands. For example, reply with 
***@sonatype-lift ignoreall*** to leave out all findings.
   | **Command** | **Usage** |
   | ------------- | ------------- |
   | `@sonatype-lift ignore` | Leave out the above finding from this PR |
   | `@sonatype-lift ignoreall` | Leave out all the existing findings from this 
PR |
   | `@sonatype-lift exclude <file\|issue\|path\|tool>` | Exclude specified 
`file\|issue\|path\|tool` from Lift findings by updating your config.toml file |
   
   **Note:** When talking to LiftBot, you need to **refresh** the page to see 
its response.
   <sub>[Click here](https://github.com/apps/sonatype-lift/installations/new) 
to add LiftBot to another repo.</sub></details>
   
   
   
   ---
   
   Was this a good recommendation?
   [ [🙁 Not 
relevant](https://www.sonatype.com/lift-comment-rating?comment=361419498&lift_comment_rating=1)
 ] - [ [😕 Won't 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419498&lift_comment_rating=2)
 ] - [ [😑 Not critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419498&lift_comment_rating=3)
 ] - [ [🙂 Critical, will 
fix](https://www.sonatype.com/lift-comment-rating?comment=361419498&lift_comment_rating=4)
 ] - [ [😊 Critical, fixing 
now](https://www.sonatype.com/lift-comment-rating?comment=361419498&lift_comment_rating=5)
 ]



-- 
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: notifications-unsubscr...@zookeeper.apache.org

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

Reply via email to