Summary
src/utils/adminApi.js builds every request with only Content-Type and
X-Requested-With: XMLHttpRequest and never sends the Nextcloud request
token. Nextcloud's CSRF middleware rejects any non-GET, cookie-authenticated
request that lacks it, and none of the target controllers declares
#[NoCSRFRequired].
The result: every write action routed through this helper was unreachable
from the UI. GET is exempt from the CSRF check, so each settings section
rendered perfectly and only its buttons were dead — the failure mode looks
like "the button does nothing", which is why it survived.
Affected actions (all POST/DELETE through apiRequest)
| Surface |
Call |
src/modals/SubmitReviewModal.vue:206 |
POST reviews — submit a review |
src/views/settings/sections/ModerationQueue.vue:241 |
POST moderation/{uuid}/approve and /reject — both moderated types |
src/views/settings/sections/FederationSettings.vue:234 |
POST federation/peers |
src/views/settings/sections/FederationSettings.vue:255 |
DELETE federation/peers |
src/views/settings/sections/FederationSettings.vue:275 |
POST federation/pull |
src/views/settings/sections/EolSyncSettings.vue:306 |
POST eol-sync/config |
src/views/settings/sections/EolSyncSettings.vue:328 |
POST eol-sync/trigger ("Sync now") |
Evidence (measured, not inferred)
A Playwright test driving the real "Write a review" modal failed with the
submit dialog still open. The failure page snapshot contains, verbatim:
- alert: CSRF check failed
- button "Cancel"
- button "Submit review"
Adding requesttoken: getRequestToken() to the helper's headers and rebuilding
turned the same, unchanged test green — the fix is the treatment and the
test is the assay.
Fix
Shipped in the same PR as the new gate-19 e2e coverage
(tests/e2e/spec-coverage/catalog-ratings.spec.ts), because the coverage could
not exist without it: src/utils/adminApi.js now imports getRequestToken
from @nextcloud/auth and sends it on every request.
@nextcloud/auth keeps that value in sync with the data-requesttoken head
meta, so it survives Nextcloud's token rotation. This is the same source
@nextcloud/axios uses — and src/store/modules/facets.js already went
through axios and was never affected, so the repo contained a working example
of the correct pattern the whole time.
Follow-up worth doing separately
There is no regression test that would catch a future helper regressing this
way. The e2e tests added in this PR cover the review-submit path only. A
vitest assertion on apiRequest's constructed headers would cover all seven
call sites at once and is cheap.
Related
Found while closing gate-19 (e2e-coverage) for softwarecatalog. See
tests/e2e/spec-coverage/catalog-ratings.spec.ts.
Summary
src/utils/adminApi.jsbuilds every request with onlyContent-TypeandX-Requested-With: XMLHttpRequestand never sends the Nextcloud requesttoken. Nextcloud's CSRF middleware rejects any non-GET, cookie-authenticated
request that lacks it, and none of the target controllers declares
#[NoCSRFRequired].The result: every write action routed through this helper was unreachable
from the UI. GET is exempt from the CSRF check, so each settings section
rendered perfectly and only its buttons were dead — the failure mode looks
like "the button does nothing", which is why it survived.
Affected actions (all POST/DELETE through
apiRequest)src/modals/SubmitReviewModal.vue:206POST reviews— submit a reviewsrc/views/settings/sections/ModerationQueue.vue:241POST moderation/{uuid}/approveand/reject— both moderated typessrc/views/settings/sections/FederationSettings.vue:234POST federation/peerssrc/views/settings/sections/FederationSettings.vue:255DELETE federation/peerssrc/views/settings/sections/FederationSettings.vue:275POST federation/pullsrc/views/settings/sections/EolSyncSettings.vue:306POST eol-sync/configsrc/views/settings/sections/EolSyncSettings.vue:328POST eol-sync/trigger("Sync now")Evidence (measured, not inferred)
A Playwright test driving the real "Write a review" modal failed with the
submit dialog still open. The failure page snapshot contains, verbatim:
Adding
requesttoken: getRequestToken()to the helper's headers and rebuildingturned the same, unchanged test green — the fix is the treatment and the
test is the assay.
Fix
Shipped in the same PR as the new gate-19 e2e coverage
(
tests/e2e/spec-coverage/catalog-ratings.spec.ts), because the coverage couldnot exist without it:
src/utils/adminApi.jsnow importsgetRequestTokenfrom
@nextcloud/authand sends it on every request.@nextcloud/authkeeps that value in sync with thedata-requesttokenheadmeta, so it survives Nextcloud's token rotation. This is the same source
@nextcloud/axiosuses — andsrc/store/modules/facets.jsalready wentthrough axios and was never affected, so the repo contained a working example
of the correct pattern the whole time.
Follow-up worth doing separately
There is no regression test that would catch a future helper regressing this
way. The e2e tests added in this PR cover the review-submit path only. A
vitest assertion on
apiRequest's constructed headers would cover all sevencall sites at once and is cheap.
Related
Found while closing gate-19 (e2e-coverage) for softwarecatalog. See
tests/e2e/spec-coverage/catalog-ratings.spec.ts.