Conversation
Map /roller-services/app/* to a small RollerAtomServlet that extends Propono's AtomServlet: - Answer 404 while webservices.enableAtomPub is off, matching the XML-RPC endpoint. Previously only the service document checked the setting. - Read Atom entry bodies (POST of Atom content, PUT to an entry URI) with Roller's standard XML parser configuration, cap them at 10 MB, and hand Propono a buffered copy. Unparseable entries get 400; oversized ones 413. RollerAtomHandler's entry-URI test moves into a static helper so the servlet and handler use the same rule.
snoopdave
left a comment
There was a problem hiding this comment.
PR-Review: 1 inline comment posted.
|
🐞Claude Issue: PR-Review: General Issues The following issues were found but cannot be attached to a specific line in the diff:
|
|
🤖Claude:
|
RollerAtomServlet now creates the AtomPub handler first and answers 401 when no user is authenticated, before buffering or parsing the entry. The handler is passed to Propono through a request attribute, which RollerAtomHandlerFactory returns, so authentication runs once per request. Add a 6.1.7 CHANGES.md section for the AtomPub behaviour changes.
…al entity in user-controlled data' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Fold the disabled-service read check into the write check, and drop the media PUT, unauthenticated PUT and entry-path cases, which repeat paths the remaining tests already cover.
|
With those fixes, I think this PR is ready for merge. |
|
Manually tested on the 6.1.7 integration build (Tomcat 9, JDK 11, MySQL 8) with curl and basic auth. Pass.
Testing found some old |
| byte[] body = readBody(req.getInputStream()); | ||
| if (body == null) { | ||
| sendText(res, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, "Entry is too large"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
You already have commons-io as a transitive dependency. You could use it to replace readBody:
| byte[] body = readBody(req.getInputStream()); | |
| if (body == null) { | |
| sendText(res, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, "Entry is too large"); | |
| return; | |
| } | |
| // Read one byte past the limit, so an oversized body can be detected. | |
| byte[] body = IOUtils.toByteArray( | |
| BoundedInputStream.builder() | |
| .setInputStream(req.getInputStream()) | |
| .setMaxCount(MAX_ENTRY_BYTES + 1L) | |
| .get()); | |
| if (body.length > MAX_ENTRY_BYTES) { | |
| sendText(res, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, "Entry is too large"); | |
| return; | |
| } |
In Java 9+ you also have InputStream.readNBytes()
| try { | ||
| // Propono reads the entry as UTF-8 text, so check the same text. | ||
| SafeSAXBuilder saxBuilder = new SafeSAXBuilder(); | ||
| saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); | ||
| saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); | ||
| saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); | ||
| saxBuilder.build(new InputStreamReader( | ||
| new ByteArrayInputStream(body), StandardCharsets.UTF_8)); | ||
| } catch (JDOMException e) { | ||
| LOG.debug("Rejecting Atom entry that could not be parsed", e); | ||
| sendText(res, HttpServletResponse.SC_BAD_REQUEST, "Invalid Atom entry"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
JDOM builds a tree that is later discarded. You might use a SAXParser instead with an empty content handler. The following code uses Commons Secure XML 1.1.0 (which will be released today) to validate the stream:
try {
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");
}
};
SAXParser parser = SecureSAXParserFactory.newNSSAXParser();
parser.setProperty("http://xml.org/sax/properties/lexical-handler", doctypeRefuser);
parser.parse(new InputSource(new InputStreamReader(
new ByteArrayInputStream(body), StandardCharsets.UTF_8)), doctypeRefuser);
} catch (SAXException e) {
LOG.debug("Rejecting Atom entry that could not be parsed", e);
sendText(res, HttpServletResponse.SC_BAD_REQUEST, "Invalid Atom entry");
return;
}| /** Largest Atom entry body accepted, in bytes. Media uploads are not affected. */ | ||
| static final int MAX_ENTRY_BYTES = 10 * 1024 * 1024; |
There was a problem hiding this comment.
Isn't this limit a little high form one Atom entry? The entry still need to fit in the database. Even counting for the overhead of XML, 128 KiB or 1 MiB should be enough for all users.
This value might also be configurable (webservices.atomPubMaxEntrySize?).
Summary
Maps
/roller-services/app/*to a newRollerAtomServlet, a small subclass of Propono'sAtomServlet, so Roller controls how AtomPub requests are handled before Propono sees them.webservices.enableAtomPubon every request. Until now only the service document checked the setting. With AtomPub off, the endpoint now answers 404, the same way the XML-RPC endpoint does.SafeSAXBuildersettings Roller uses elsewhere, and hands Propono a buffered copy. An entry that does not parse gets 400; an oversized one gets 413. Media uploads and reads are unchanged.RollerAtomHandler's entry-URI rule moves into a staticisEntryPathhelper, so the servlet and the handler use the same test.Testing
RollerAtomServletTest(10 tests): disabled service, pass-through of reads and media, buffered forwarding of a normal entry, refused entries on POST and PUT, size limit, and the shared entry-path rule.mvn -pl app teston JDK 11: 335 tests, 0 failures, 1 skipped.Targets
roller-6.1.xfor 6.1.7. It will be cherry-picked tomasterafter the 6.1.7 release.