Repository navigation
Require POST for action methods that change data - #227
Conversation
Links that change data (ping settings, invitation replies) now carry the salt.
snoopdave
left a comment
There was a problem hiding this comment.
🤖 GLM: PR-Review — 1 inline comment posted.
| * Action methods that only display data. Links and redirects may reach | ||
| * these without a salt; any other method needs a POST or a salt. | ||
| */ | ||
| static final Set<String> DISPLAY_METHODS = Set.of( |
There was a problem hiding this comment.
Important: The fresh-install click-through breaks with this change: CreateDatabase.jsp:36 and UpgradeDatabase.jsp:54 render installer.tryBootstrapping as a plain GET link (<a href="{0}"> around <s:url action="install!bootstrap"/>), bootstrap is not in DISPLAY_METHODS, and the link carries no salt — so completing a fresh installation by clicking that link now throws. Same class of miss as the menu/pings links this PR fixes. Either add salt params to those two s:urls (the pages render a salt) or route the installer flow through POST.
There was a problem hiding this comment.
🤖Claude: fixed by adding the salt to the install!bootstrap URL in CreateDatabase.jsp and UpgradeDatabase.jsp. Checked on a fresh Jetty install: token, create tables, then the bootstrap link completes setup and lands on the front page (6e13c0c).
|
🤖 GLM: PR-Review — General Issues The following issues were found but cannot be attached to a specific line in the diff:
Strengths (not action items): the allowlist approach with tests for all three paths (GET without salt rejected, GET with salt accepted, display methods exempt) is a clean class-level fix rather than a per-action patch, and |
|
🤖Claude: fixed by adding a CHANGES.md entry under 6.1.7 behaviour changes (6e13c0c). |
mraible
left a comment
There was a problem hiding this comment.
Salting the GET links is a good fix. One data-changing path still gets through, though, and the refreshed pages run into a consumed salt. Details are inline.
| * these without a salt; any other method needs a POST or a salt. | ||
| */ | ||
| static final Set<String> DISPLAY_METHODS = Set.of( | ||
| "execute", "input", "view", "search", "query", "edit", "cancel", |
There was a problem hiding this comment.
PingTargetEdit.execute() validates, saves and flushes a ping target, and commonPingTargetAdd/commonPingTargetEdit allow only execute. That means a GET like commonPingTargetAdd.rol?bean.name=x&bean.pingUrl=... still creates or repoints a ping target without a salt. Spring CSRF is disabled and ValidateSaltFilter only checks POST, so nothing else catches it. Could the save move to a save method, or could those two actions require POST?
There was a problem hiding this comment.
🤖Claude: fixed by having PingTargetEdit take the request method (like GlobalConfig) and save only on a POST. Any other method returns the input result without saving. The modal already saves by AJAX POST, so the page works as before. New PingTargetEditTest checks that a GET does not save or flush and that a POST does; the GET case fails without the change. Also checked on Jetty: a GET to commonPingTargetAdd.rol with bean parameters adds nothing, and the modal POST still adds the target. A sweep of the other exempt methods found no others that save. (Local change, in the next push.)
| <td class="rollertable" align="center" > | ||
| <s:if test="pingStatus[#pingTarget.id]"> | ||
| <s:url var="disableUrl" action="pings!disable" > | ||
| <s:param name="salt" value="%{salt}" /> |
There was a problem hiding this comment.
pings!enable, disable and pingNow render the list at the salted URL, and the salt is single-use, so refreshing the page (or using a second link from the same page in another tab) shows "Security Violation". Redirecting to pings after the change would keep the used salt out of the address bar. The same applies to the PingTargets and MainMenu links.
There was a problem hiding this comment.
🤖Claude: fixed by returning a new done result from pings!enable/disable/pingNow, commonPingTargets!enable/disable and menu!accept/decline. The result redirects to the plain page (pings.rol?weblog=…, commonPingTargets.rol, menu.rol), and those three actions add the Struts store interceptor in AUTOMATIC mode, so the ping result and the invite messages survive the redirect and show once. Checked on Jetty: each link returns a 302 without the salt in Location, the message appears after the redirect, and a refresh shows the page with no error. While checking this I found that the invite links double-escaped & (s:url and then s:property), so inviteId never arrived; they now use s:a href="%{…}" like the pings pages. Opening a second link from the same, already-used page still needs a reload, because every link on a page shares one salt. (Local change, in the next push.)
| - **Startup fails if the XML-RPC parser cannot be configured.** Roller used to | ||
| log an error and continue. It now stops at startup, so check the log if a | ||
| custom XML parser is on the classpath. | ||
| - **Links and URLs that change data need the form token.** A request other |
There was a problem hiding this comment.
This says every non-POST request that changes data needs the salt, but because execute is exempt, the ping target add/edit GET above still doesn't need one. Either close that path or narrow this entry to the links the PR covers.
There was a problem hiding this comment.
🤖Claude: fixed by closing the ping target path (see the PingTargetEdit reply), so the entry holds as written. I also added that saving a common ping target needs a POST, and that the links return to the page without the salt in the address. (Local change, in the next push.)
…ix invite links Claude-Session: https://claude.ai/code/session_01C5X1Z1eoSH7o25gZskPGzb
mraible
left a comment
There was a problem hiding this comment.
Saving a common ping target now needs a POST, the modal still saves over AJAX POST, and the link actions redirect so a refresh doesn't resend a used salt. LGTM.
|
GET should be safe, so I'd rather not accept a salt on GET. Can we make these actions POST-only instead?
This keeps the salt out of URLs, logs and |
A request other than a POST to an action method outside the display list now gets 405 Method Not Allowed. The ping, common ping target and invitation links are one-button POST forms, and the installer's bootstrap link submits a POST form. Every POST still needs the salt, which ValidateSaltFilter checks.
|
🤖Claude: fixed, with one change from the plan. Struts 2.5.29 (our 6.1.x version) has no
Checked on a fresh Jetty install: GET to |
…hods # Conflicts: # CHANGES.md
…hods # Conflicts: # CHANGES.md
Action methods that change data now need a POST:
execute,input,view,search,query,edit,canceland a few redirect targets) gets405 Method Not Allowed. Every POST still needs the salt.Tests:
ValidateSaltInterceptorTestandPingTargetEditTest. Full build and tests pass on JDK 11. On a fresh Jetty install, a GET to each of those URLs got 405, and the forms bootstrapped Roller, enabled pings and accepted an invitation; ping now was not tried.