github-advanced-security[bot] commented on code in PR #161:
URL: https://github.com/apache/roller/pull/161#discussion_r4238512315


##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -63,102 +57,134 @@
 
     private static final String ATOM_CONTENT_TYPE = "application/atom+xml";
 
-    /**
-     * Request attribute that carries the handler authenticated by this servlet
-     * to {@link RollerAtomHandlerFactory}, so Propono does not authenticate 
the
-     * request a second time.
-     */
-    static final String HANDLER_ATTRIBUTE = RollerAtomServlet.class.getName() 
+ ".handler";
-
     @Override
-    protected void service(HttpServletRequest req, HttpServletResponse res)
-            throws ServletException, IOException {
+    protected void service(HttpServletRequest request, HttpServletResponse 
response)
+            throws IOException {
 
         if 
(!WebloggerRuntimeConfig.getBooleanProperty("webservices.enableAtomPub")) {
-            LOG.debug("AtomPub service is disabled; rejecting request");
-            sendText(res, HttpServletResponse.SC_NOT_FOUND, "AtomPub service 
is disabled");
+            log.debug("AtomPub service is disabled; rejecting request");
+            sendText(response, HttpServletResponse.SC_NOT_FOUND, "AtomPub 
service is disabled");
             return;
         }
 
-        if (!carriesEntry(req)) {
-            forward(req, res);
+        String method = request.getMethod();
+        if (!"GET".equals(method) && !"POST".equals(method)
+                && !"PUT".equals(method) && !"DELETE".equals(method)) {
+            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
             return;
         }
 
-        // Authenticate before reading the body, as Propono does.
-        AtomHandler handler = createHandler(req, res);
-        if (handler.getAuthenticatedUsername() == null) {
-            res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
-            res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        // Authenticate before reading the body.
+        RollerAtomHandler handler = createHandler(request, response);
+        String userName = handler.getAuthenticatedUsername();
+        if (userName == null) {
+            // The OAuth path may have already written a challenge/error 
response.
+            if (!response.isCommitted()) {
+                response.setHeader("WWW-Authenticate", "Basic 
realm=\"Roller\"");
+                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+            }
             return;
         }
-        req.setAttribute(HANDLER_ATTRIBUTE, handler);
 
-        int maxEntryBytes = maxEntryBytes();
-        // Read one byte past the limit, so an oversized body can be detected.
-        byte[] body = req.getInputStream().readNBytes(maxEntryBytes + 1);
-        if (body.length > maxEntryBytes) {
-            sendText(res, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, 
"Entry is too large");
+        if ("POST".equals(method) && request.getContentType() == null) {
+            sendText(response, HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
+                    "No content-type specified in request");
             return;
         }
-        DefaultHandler contentHandler = new DefaultHandler() {
-            @Override
-            public void error(SAXParseException e) throws SAXException {
-                throw e;
+
+        AtomRequest areq;
+        AtomEntry entry = null;
+        if (carriesEntry(request)) {
+            int maxEntryBytes = maxEntryBytes();
+            // Read one byte past the limit, so an oversized body can be 
detected.
+            byte[] body = request.getInputStream().readNBytes(maxEntryBytes + 
1);
+            if (body.length > maxEntryBytes) {
+                sendText(response, 
HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE,
+                        "Entry is too large");
+                return;
+            }
+            try {
+                entry = new AtomReader().parseEntry(new 
ByteArrayInputStream(body));
+            } catch (AtomException e) {
+                log.debug("Rejecting Atom entry that could not be parsed", e);
+                sendText(response, HttpServletResponse.SC_BAD_REQUEST, 
"Invalid Atom entry");
+                return;
             }
-        };
-        XMLReader reader;
+            areq = new AtomRequest(request, body);
+        } else {
+            // Media bodies are streamed to a temporary file by 
MediaCollection,
+            // where the upload size and quota are checked.
+            areq = AtomRequest.streaming(request);
+        }
+
         try {
-            reader = 
SecureXmlParsers.newSAXParserFactory().newSAXParser().getXMLReader();
-            // Hardening: DOCTYPE declarations should be rejected,
-            // regardless whether the secure reader already does it.
-            DefaultHandler2 doctypeRefuser = new DefaultHandler2() {
-                @Override
-                public void startDTD(String name, String publicId, String 
systemId)
-                        throws SAXException {
-                    throw new SAXException("DOCTYPE is not allowed in an Atom 
entry");
+            switch (method) {
+                case "GET":
+                    doGet(handler, areq, response);
+                    break;
+                case "POST":
+                    doPost(handler, areq, entry, response);
+                    break;
+                case "PUT":
+                    doPut(handler, areq, entry, response);
+                    break;
+                default:
+                    handler.deleteEntry(areq);
+                    response.setStatus(HttpServletResponse.SC_OK);
+            }
+        } catch (AtomException ae) {
+            if (!response.isCommitted()) {
+                if (ae.getStatus() >= 
HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
+                    // Server errors can carry internal details; keep them in 
the log
+                    log.error("Error handling AtomPub request", ae);
+                    response.sendError(ae.getStatus());
+                } else {
+                    // Client errors carry a message written by Roller for the 
client
+                    log.debug("Returning error to client: " + ae.getMessage(), 
ae);
+                    response.sendError(ae.getStatus(), ae.getMessage());

Review Comment:
   ## CodeQL / Information exposure through an error message
   
   [Error information](1) can be exposed to an external user.
   
   [Show more 
details](https://github.com/apache/roller/security/code-scanning/139)



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -188,61 +214,94 @@
         res.getWriter().write(message);
     }
 
-    /** A request whose body has already been read into memory. */
-    static final class BufferedBodyRequest extends HttpServletRequestWrapper {
+    private void doGet(RollerAtomHandler handler, AtomRequest areq, 
HttpServletResponse response)
+            throws AtomException, IOException {
 
-        private final byte[] body;
+        if (handler.isAtomServiceURI(areq)) {
+            AtomServiceDoc service = handler.getAtomService(areq);
+            response.setContentType(AtomConstants.SERVICE_MEDIA_TYPE);
+            new AtomWriter().writeServiceDoc(response.getOutputStream(), 
service);
 
-        BufferedBodyRequest(HttpServletRequest request, byte[] body) {
-            super(request);
-            this.body = body;
-        }
+        } else if (handler.isCollectionURI(areq)) {
+            AtomFeed feed = handler.getCollection(areq);
+            response.setContentType(AtomConstants.FEED_MEDIA_TYPE);
+            new AtomWriter().writeFeed(response.getOutputStream(), feed);
 
-        @Override
-        public ServletInputStream getInputStream() {
-            final ByteArrayInputStream in = new ByteArrayInputStream(body);
-            return new ServletInputStream() {
-                @Override
-                public int read() {
-                    return in.read();
-                }
+        } else if (handler.isEntryURI(areq)) {
+            AtomEntry entry = handler.getEntry(areq);
+            response.setContentType(AtomConstants.ENTRY_MEDIA_TYPE);
+            new AtomWriter().writeEntry(response.getOutputStream(), entry);
 
-                @Override
-                public int read(byte[] b, int off, int len) {
-                    return in.read(b, off, len);
-                }
+        } else if (handler.isMediaEditURI(areq)) {
+            AtomMediaResource resource = handler.getMediaResource(areq);
+            if (resource.getContentType() != null) {
+                response.setContentType(resource.getContentType());
+            }
+            response.setContentLengthLong(resource.getContentLength());
+            if (resource.getLastModified() != null) {
+                response.setDateHeader("Last-Modified", 
resource.getLastModified().getTime());
+            }
+            try (InputStream in = resource.getInputStream()) {
+                in.transferTo(response.getOutputStream());
+            }
 
-                @Override
-                public boolean isFinished() {
-                    return in.available() == 0;
-                }
+        } else {
+            throw new AtomNotFoundException("Cannot find specified resource");
+        }
+    }
 
-                @Override
-                public boolean isReady() {
-                    return true;
-                }
+    private void doPost(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-                @Override
-                public void setReadListener(ReadListener listener) {
-                    throw new UnsupportedOperationException();
-                }
-            };
+        if (!handler.isCollectionURI(areq)) {
+            throw new AtomNotFoundException("Cannot POST to specified URI");
         }
 
-        @Override
-        public BufferedReader getReader() {
-            return new BufferedReader(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8));
+        String contentType = areq.getContentType();
+        AtomEntry created;
+        if (entry != null) {
+            created = handler.postEntry(areq, entry);
+        } else {
+            // Media POST: synthesize an entry carrying the request content 
type
+            // and Slug; the binary data is read from the request body.
+            AtomEntry mediaEntry = new AtomEntry();
+            AtomContent content = new AtomContent();
+            content.setType(contentType);
+            mediaEntry.setContent(content);
+            mediaEntry.setTitle(areq.getHeader("Slug"));
+            created = handler.postMedia(areq, mediaEntry);
         }
+        writeCreated(handler, response, created);
+    }
+
+    private void doPut(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-        @Override
-        public int getContentLength() {
-            return body.length;
+        if (entry != null) {
+            handler.putEntry(areq, entry);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else if (handler.isMediaEditURI(areq)) {
+            handler.putMedia(areq);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else {
+            throw new AtomNotFoundException("Cannot PUT to specified URI");
         }
+    }
 
-        @Override
-        public long getContentLengthLong() {
-            return body.length;
+    private void writeCreated(RollerAtomHandler handler, HttpServletResponse 
response,
+            AtomEntry entry) throws AtomException {
+        String location = safeLocation(entry.getLinkHref("edit"), 
handler.getAtomURL());
+        if (location != null) {
+            response.setHeader("Location", location);

Review Comment:
   ## CodeQL / URL redirection from remote source
   
   Untrusted URL redirection depends on a [user-provided value](1).
   
   [Show more 
details](https://github.com/apache/roller/security/code-scanning/140)



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -188,61 +214,94 @@
         res.getWriter().write(message);
     }
 
-    /** A request whose body has already been read into memory. */
-    static final class BufferedBodyRequest extends HttpServletRequestWrapper {
+    private void doGet(RollerAtomHandler handler, AtomRequest areq, 
HttpServletResponse response)
+            throws AtomException, IOException {
 
-        private final byte[] body;
+        if (handler.isAtomServiceURI(areq)) {
+            AtomServiceDoc service = handler.getAtomService(areq);
+            response.setContentType(AtomConstants.SERVICE_MEDIA_TYPE);
+            new AtomWriter().writeServiceDoc(response.getOutputStream(), 
service);
 
-        BufferedBodyRequest(HttpServletRequest request, byte[] body) {
-            super(request);
-            this.body = body;
-        }
+        } else if (handler.isCollectionURI(areq)) {
+            AtomFeed feed = handler.getCollection(areq);
+            response.setContentType(AtomConstants.FEED_MEDIA_TYPE);
+            new AtomWriter().writeFeed(response.getOutputStream(), feed);
 
-        @Override
-        public ServletInputStream getInputStream() {
-            final ByteArrayInputStream in = new ByteArrayInputStream(body);
-            return new ServletInputStream() {
-                @Override
-                public int read() {
-                    return in.read();
-                }
+        } else if (handler.isEntryURI(areq)) {
+            AtomEntry entry = handler.getEntry(areq);
+            response.setContentType(AtomConstants.ENTRY_MEDIA_TYPE);
+            new AtomWriter().writeEntry(response.getOutputStream(), entry);
 
-                @Override
-                public int read(byte[] b, int off, int len) {
-                    return in.read(b, off, len);
-                }
+        } else if (handler.isMediaEditURI(areq)) {
+            AtomMediaResource resource = handler.getMediaResource(areq);
+            if (resource.getContentType() != null) {
+                response.setContentType(resource.getContentType());
+            }
+            response.setContentLengthLong(resource.getContentLength());
+            if (resource.getLastModified() != null) {
+                response.setDateHeader("Last-Modified", 
resource.getLastModified().getTime());
+            }
+            try (InputStream in = resource.getInputStream()) {
+                in.transferTo(response.getOutputStream());
+            }
 
-                @Override
-                public boolean isFinished() {
-                    return in.available() == 0;
-                }
+        } else {
+            throw new AtomNotFoundException("Cannot find specified resource");
+        }
+    }
 
-                @Override
-                public boolean isReady() {
-                    return true;
-                }
+    private void doPost(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-                @Override
-                public void setReadListener(ReadListener listener) {
-                    throw new UnsupportedOperationException();
-                }
-            };
+        if (!handler.isCollectionURI(areq)) {
+            throw new AtomNotFoundException("Cannot POST to specified URI");
         }
 
-        @Override
-        public BufferedReader getReader() {
-            return new BufferedReader(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8));
+        String contentType = areq.getContentType();
+        AtomEntry created;
+        if (entry != null) {
+            created = handler.postEntry(areq, entry);
+        } else {
+            // Media POST: synthesize an entry carrying the request content 
type
+            // and Slug; the binary data is read from the request body.
+            AtomEntry mediaEntry = new AtomEntry();
+            AtomContent content = new AtomContent();
+            content.setType(contentType);
+            mediaEntry.setContent(content);
+            mediaEntry.setTitle(areq.getHeader("Slug"));
+            created = handler.postMedia(areq, mediaEntry);
         }
+        writeCreated(handler, response, created);
+    }
+
+    private void doPut(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-        @Override
-        public int getContentLength() {
-            return body.length;
+        if (entry != null) {
+            handler.putEntry(areq, entry);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else if (handler.isMediaEditURI(areq)) {
+            handler.putMedia(areq);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else {
+            throw new AtomNotFoundException("Cannot PUT to specified URI");
         }
+    }
 
-        @Override
-        public long getContentLengthLong() {
-            return body.length;
+    private void writeCreated(RollerAtomHandler handler, HttpServletResponse 
response,
+            AtomEntry entry) throws AtomException {
+        String location = safeLocation(entry.getLinkHref("edit"), 
handler.getAtomURL());
+        if (location != null) {
+            response.setHeader("Location", location);
+            response.setHeader("Content-Location", location);

Review Comment:
   ## CodeQL / HTTP response splitting
   
   This header depends on a [user-provided value](1), which may cause a 
response-splitting vulnerability.
   
   [Show more 
details](https://github.com/apache/roller/security/code-scanning/142)



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -188,61 +214,94 @@
         res.getWriter().write(message);
     }
 
-    /** A request whose body has already been read into memory. */
-    static final class BufferedBodyRequest extends HttpServletRequestWrapper {
+    private void doGet(RollerAtomHandler handler, AtomRequest areq, 
HttpServletResponse response)
+            throws AtomException, IOException {
 
-        private final byte[] body;
+        if (handler.isAtomServiceURI(areq)) {
+            AtomServiceDoc service = handler.getAtomService(areq);
+            response.setContentType(AtomConstants.SERVICE_MEDIA_TYPE);
+            new AtomWriter().writeServiceDoc(response.getOutputStream(), 
service);
 
-        BufferedBodyRequest(HttpServletRequest request, byte[] body) {
-            super(request);
-            this.body = body;
-        }
+        } else if (handler.isCollectionURI(areq)) {
+            AtomFeed feed = handler.getCollection(areq);
+            response.setContentType(AtomConstants.FEED_MEDIA_TYPE);
+            new AtomWriter().writeFeed(response.getOutputStream(), feed);
 
-        @Override
-        public ServletInputStream getInputStream() {
-            final ByteArrayInputStream in = new ByteArrayInputStream(body);
-            return new ServletInputStream() {
-                @Override
-                public int read() {
-                    return in.read();
-                }
+        } else if (handler.isEntryURI(areq)) {
+            AtomEntry entry = handler.getEntry(areq);
+            response.setContentType(AtomConstants.ENTRY_MEDIA_TYPE);
+            new AtomWriter().writeEntry(response.getOutputStream(), entry);
 
-                @Override
-                public int read(byte[] b, int off, int len) {
-                    return in.read(b, off, len);
-                }
+        } else if (handler.isMediaEditURI(areq)) {
+            AtomMediaResource resource = handler.getMediaResource(areq);
+            if (resource.getContentType() != null) {
+                response.setContentType(resource.getContentType());
+            }
+            response.setContentLengthLong(resource.getContentLength());
+            if (resource.getLastModified() != null) {
+                response.setDateHeader("Last-Modified", 
resource.getLastModified().getTime());
+            }
+            try (InputStream in = resource.getInputStream()) {
+                in.transferTo(response.getOutputStream());
+            }
 
-                @Override
-                public boolean isFinished() {
-                    return in.available() == 0;
-                }
+        } else {
+            throw new AtomNotFoundException("Cannot find specified resource");
+        }
+    }
 
-                @Override
-                public boolean isReady() {
-                    return true;
-                }
+    private void doPost(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-                @Override
-                public void setReadListener(ReadListener listener) {
-                    throw new UnsupportedOperationException();
-                }
-            };
+        if (!handler.isCollectionURI(areq)) {
+            throw new AtomNotFoundException("Cannot POST to specified URI");
         }
 
-        @Override
-        public BufferedReader getReader() {
-            return new BufferedReader(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8));
+        String contentType = areq.getContentType();
+        AtomEntry created;
+        if (entry != null) {
+            created = handler.postEntry(areq, entry);
+        } else {
+            // Media POST: synthesize an entry carrying the request content 
type
+            // and Slug; the binary data is read from the request body.
+            AtomEntry mediaEntry = new AtomEntry();
+            AtomContent content = new AtomContent();
+            content.setType(contentType);
+            mediaEntry.setContent(content);
+            mediaEntry.setTitle(areq.getHeader("Slug"));
+            created = handler.postMedia(areq, mediaEntry);
         }
+        writeCreated(handler, response, created);
+    }
+
+    private void doPut(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-        @Override
-        public int getContentLength() {
-            return body.length;
+        if (entry != null) {
+            handler.putEntry(areq, entry);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else if (handler.isMediaEditURI(areq)) {
+            handler.putMedia(areq);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else {
+            throw new AtomNotFoundException("Cannot PUT to specified URI");
         }
+    }
 
-        @Override
-        public long getContentLengthLong() {
-            return body.length;
+    private void writeCreated(RollerAtomHandler handler, HttpServletResponse 
response,
+            AtomEntry entry) throws AtomException {
+        String location = safeLocation(entry.getLinkHref("edit"), 
handler.getAtomURL());
+        if (location != null) {
+            response.setHeader("Location", location);

Review Comment:
   ## CodeQL / HTTP response splitting
   
   This header depends on a [user-provided value](1), which may cause a 
response-splitting vulnerability.
   
   [Show more 
details](https://github.com/apache/roller/security/code-scanning/141)



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to