Skip to content

Separate submitted and response salts in UI filters - #167

Open
snoopdave wants to merge 2 commits into
masterfrom
salt-filter-request-handling
Open

Separate submitted and response salts in UI filters#167
snoopdave wants to merge 2 commits into
masterfrom
salt-filter-request-handling

Conversation

@snoopdave

@snoopdave snoopdave commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Roller's UI request handling uses two salt filters: one processes the token submitted with a request, and the other generates the token for the next response. This change gives each filter a single responsibility and corrects their execution order.

What changed

  • Validate and consume the salt value submitted with the request.
  • Run request validation before response-salt generation and create a fresh response salt only after successful processing.
  • Route multipart *.rol validation through an interceptor that runs immediately after the upload interceptor.
  • Remove the salt.ignored.urls setting and its isIgnoredURL() helper, whose shipped values did not match its behavior.

Tests

  • Cover missing, valid, and repeated submitted salts.
  • Cover filter ordering and response-token generation.
  • Cover multipart token handling on the supported Tomcat deployment and repository Jetty test setup.

Validate the salt submitted with the request rather than a request attribute, and run validation before the response salt is generated. Move multipart validation into a Struts interceptor after the upload interceptor, since filters cannot read multipart fields. Drop the unused salt.ignored.urls bypass.

Claude-Session: https://claude.ai/code/session_01A1fhY1E2PCFU6UAPXu2WtV

@mraible mraible left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Please address the two workflow regressions noted in the review before merge.


// Remove salt from cache after successful validation
saltCache.remove(salt);
if (!SaltValidator.consumeSubmittedSalt(httpReq)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments.jsp:429 reads #comments_salt once and sends it on every commentdata AJAX POST. With the salt removed on first use, editing a second comment without reloading fails here with a 500, and the $.ajax call has no error handler, so the save just silently doesn't happen. CommentDataServlet could return a fresh salt in its JSON (the response passes through LoadSaltFilter, so request.getAttribute("salt") is available) and the JS update #comments_salt from it.


SaltCache saltCache = SaltCache.getInstance();
synchronized (saltCache) {
if (!Objects.equals(saltCache.get(salt), userId)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this is the only path, cache.salt.size (5000) and cache.salt.timeout (3600s) are user-visible limits: LoadSaltFilter mints a salt on every /roller-ui request and on every tiles FORWARD, so entries get evicted quickly on a multi-user site, and a form left open for over an hour is rejected with the entry text lost. Not a blocker, but please call it out in the description; raising the defaults would soften it.


@Test
public void testSubmittedSaltIsValidatedBeforeResponseSaltIsLoaded() throws Exception {
String webXml = Files.readString(Path.of("src/main/webapp/WEB-INF/web.xml"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cwd-relative, so this passes only when run from app/; the other two tests in this class use getResourceAsStream, and surefire sets project.build.directory for this module.

Comment thread app/src/main/resources/struts.xml Outdated
<!-- <interceptor-ref name="scopedModelDriven"/> -->
<!-- <interceptor-ref name="modelDriven"/> -->
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="ValidateSaltInterceptor"/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment in ValidateSaltFilter says multipart params only exist after fileUpload, but Struts wraps the request in MultiPartRequestWrapper in StrutsPrepareAndExecuteFilter before any interceptor runs; fileUpload only copies file items into action params. So this ref could sit right after exception (validate before anything else does work), and SaltConfigurationTest:51 shouldn't pin it as adjacent to fileUpload.

log.debug("Valid salt value not found on POST to URL : "
+ httpReq.getServletPath());
}
throw new ServletException("Security Violation");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this block is duplicated in the interceptor; a SaltValidator.requireSubmittedSalt(request) that throws would keep both rejection paths in sync.

}

@Test
public void testValidationRunsBeforeResponseSaltGeneration() throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This builds the chain in validate-then-load order and then asserts that order, so it can't fail if web.xml is swapped back; SaltConfigurationTest already covers the ordering.

String properties = readResource(
"/org/apache/roller/weblogger/config/roller.properties");

assertFalse(properties.contains("salt.ignored.urls"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserting the literal salt.ignored.urls is absent from roller.properties means a migration note like # salt.ignored.urls is no longer supported breaks the build. The behaviour is already covered by the filter no longer reading the property.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants