fix(cron): switch manual trigger to POST to defeat CSRF

GHSA-8qhj-4f8c-j8qg: GET /api/v1/cron/:id/manual changed shared state
on the agent stream, and the JWT cookie is SameSite=Lax so a victim's
browser would send the cookie on a top-level cross-site GET. An
attacker could trick a logged-in user into firing any of their own
cron commands.

Switch the route to POST: SameSite=Lax cookies are not sent on cross-
site POST, closing the CSRF window without introducing a new token.

Tests:
- TestCronManualTriggerRejectsCrossSiteGET locks in that GET no longer
  resolves.
- TestCronManualTriggerAcceptsSameSitePOST locks in the legitimate POST
  still works.

Frontend (admin-frontend) updated in a follow-up commit.

Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
This commit is contained in:
naiba
2026-05-26 03:59:50 +00:00
co-authored by cloudcode
parent e05700c2f0
commit 9c0fa84c95
3 changed files with 107 additions and 2 deletions
+1 -1
View File
@@ -166,7 +166,7 @@ func updateCron(c *gin.Context) (any, error) {
// @param id path uint true "Task ID"
// @Produce json
// @Success 200 {object} model.CommonResponse[any]
// @Router /cron/{id}/manual [get]
// @Router /cron/{id}/manual [post]
func manualTriggerCron(c *gin.Context) (any, error) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 64)