Skip to content

Fix broken API routes and add CSRF bypass for ExApp REST calls#132

Draft
Copilot wants to merge 20 commits intomainfrom
copilot/test-new-tools-in-pr-127
Draft

Fix broken API routes and add CSRF bypass for ExApp REST calls#132
Copilot wants to merge 20 commits intomainfrom
copilot/test-new-tools-in-pr-127

Conversation

Copy link

Copilot AI commented Mar 17, 2026

Audited all new tool modules from PR #127 against upstream Nextcloud app source code. Found 5 tools with incorrect API routes or payloads that would fail at runtime, and identified a CSRF token issue affecting all non-DAV REST API calls from ExApp context.

deck.py

  • list_boards() returned response.text instead of response.json()

mail.py

  • get_mail_account_list() had copy-pasted docstring from send_email
  • list_mail_folders, search_emails, get_email_messages, move_email_to_folder, delete_email all used nonexistent OCS routes — the Mail app exposes these as REST endpoints at /index.php/apps/mail/api/, not OCS
# Before (broken — these OCS routes don't exist)
await nc.ocs('GET', f'/ocs/v2.php/apps/mail/account/{account_id}/mailboxes')

# After (correct REST API)
response = await nc._session._create_adapter(True).request(
    'GET', f"{nc.app_cfg.endpoint}/index.php/apps/mail/api/mailboxes",
    params={'accountId': account_id})

polls.py

  • All routes missing required /api/v1.0/ prefix (e.g. /ocs/v2.php/apps/polls/polls/ocs/v2.php/apps/polls/api/v1.0/polls)
  • create_poll passed description param the API doesn't accept
  • add_poll_option used wrong payload key (pollOptionTextoption array)
  • vote_on_poll passed answer in JSON body; API expects it in URL path (/option/{id}/vote/{answer})

forms.py

  • Used nonexistent API version v2.4; updated to v3
  • create_form passed title/description to newForm which only accepts fromId — now uses two-step create + PATCH
  • add_question_to_form used wrong routes and option creation used per-item POST instead of batch optionTexts
  • update_form_settings used wrong route and payload format (needs keyValuePairs wrapper)

files.py

  • get_folder_tree() passed depth via json= (request body) on a GET request; changed to params=

CSRF bypass for ExApp REST calls

Nextcloud's SecurityMiddleware enforces CSRF checks on controller methods without @NoCSRFRequired. ExApps don't have CSRF tokens, but Request::passesCSRFCheck() bypasses CSRF when the OCS-APIREQUEST header is present. The nc_py_api library sets this header on the main OCS adapter, but NOT on fresh adapters created via _create_adapter(True) used for REST calls.

Added "OCS-APIREQUEST": "true" to all non-DAV REST API calls in: bookmarks, cookbook, deck, mail, music, news, passwords, photos.

Routes that don't need this fix:

  • OCS routes (forms, polls, notes, circles, shares): the nc.ocs() adapter already sets OCS-APIREQUEST: true
  • DAV routes (files, contacts, calendar): use /remote.php/dav/ with Sabre auth (AppAPIAuthBackend), not the CSRF middleware

Verified correct (no changes needed beyond CSRF header)

bookmarks, circles, cookbook, music, news, notes, passwords, photos, shares


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

marcelklehr and others added 17 commits March 11, 2026 12:09
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Copilot AI and others added 2 commits March 17, 2026 22:07
- deck.py: Fix list_boards() returning response.text instead of response.json()
- mail.py: Fix wrong docstring on get_mail_account_list(), rewrite
  list_mail_folders, search_emails, get_email_messages, move_email_to_folder,
  delete_email to use correct Nextcloud Mail REST API routes instead of
  nonexistent OCS routes
- polls.py: Fix all routes to include correct /api/v1.0/ prefix, fix
  create_poll payload (API doesn't accept description), fix add_poll_option
  payload structure, fix vote_on_poll to use correct URL-based answer format
- forms.py: Update from nonexistent v2.4 API to correct v3 API, fix
  create_form to use two-step create+update pattern, fix add_question_to_form
  payload, fix option creation to use batch optionTexts parameter
- files.py: Fix get_folder_tree to use params= instead of json= for GET request

Co-authored-by: marcelklehr <986878+marcelklehr@users.noreply.github.com>
Copilot AI changed the title [WIP] Test new tools and fix issues from PR #127 Fix broken API routes in deck, mail, polls, forms, and files tools Mar 17, 2026
Copilot AI requested a review from marcelklehr March 17, 2026 22:24
@marcelklehr
Copy link
Member

@copilot Make sure that the APIs we use do not requre the sending of CSRF tokens, as we don't have those in an ex app context

Nextcloud's SecurityMiddleware enforces CSRF checks on controller methods
without @NoCSRFRequired. ExApps don't have CSRF tokens, but Nextcloud's
Request::passesCSRFCheck() bypasses CSRF if the OCS-APIREQUEST header is
present (any non-empty value).

The nc_py_api library sets this header on the main OCS adapter, but NOT
on fresh adapters created via _create_adapter(True) used for REST calls.

Added "OCS-APIREQUEST": "true" header to all non-DAV REST API calls in:
bookmarks, cookbook, deck, mail, music, news, passwords, photos.

DAV calls (files.py, contacts.py, calendar.py) use /remote.php/dav/ which
goes through Sabre auth (AppAPIAuthBackend), not the CSRF middleware.

OCS calls (forms.py, polls.py, notes.py, circles.py, shares.py) already
have CSRF bypass via the OCS adapter's built-in OCS-APIREQUEST header.

Co-authored-by: marcelklehr <986878+marcelklehr@users.noreply.github.com>
Copy link
Author

Copilot AI commented Mar 18, 2026

@copilot Make sure that the APIs we use do not requre the sending of CSRF tokens, as we don't have those in an ex app context

Audited all API routes for CSRF requirements. The issue: nc._session._create_adapter(True) creates a fresh adapter without the OCS-APIREQUEST header, so Nextcloud's SecurityMiddleware would throw CrossSiteRequestForgeryException on controllers lacking @NoCSRFRequired.

The fix in faac2a1: added "OCS-APIREQUEST": "true" to all non-DAV REST calls (bookmarks, cookbook, deck, mail, music, news, passwords, photos). Nextcloud's Request::passesCSRFCheck() returns true when this header is present, bypassing CSRF.

Routes that don't need this fix:

  • OCS routes (forms, polls, notes, circles, shares): the nc.ocs() adapter already sets OCS-APIREQUEST: true
  • DAV routes (files, contacts, calendar): use /remote.php/dav/ with Sabre auth (AppAPIAuthBackend), not the CSRF middleware

Copilot AI changed the title Fix broken API routes in deck, mail, polls, forms, and files tools Fix broken API routes and add CSRF bypass for ExApp REST calls Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants