fix(vector): address PR review round 12 — bool guard + strict doc_id validation

- _group_int_doc_ids: use type(value) is not int instead of isinstance,
  since bool is an int subclass and would otherwise stringify to
  "True"/"False" and corrupt legacy payloads on backfill.
- Replace doc_id.isdigit() guards in 5 boundary sites
  (api/visualization, auth/viz_routes, search/context note/news_item/
  deck_card branches) with a shared is_valid_nextcloud_doc_id helper
  that rejects "0", leading zeros, and Unicode digit classes
  (superscripts, Arabic-Indic, Devanagari) which pass isdigit() but
  cannot be valid MySQL AUTO_INCREMENT IDs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-09 20:28:47 +02:00
co-authored by Claude Opus 4.7
parent f3ce46da0f
commit f9ad7dc52e
8 changed files with 133 additions and 24 deletions
+4 -2
View File
@@ -34,6 +34,7 @@ from nextcloud_mcp_server.search.context import (
get_chunk_bbox_and_page_from_qdrant,
get_chunk_with_context,
)
from nextcloud_mcp_server.utils.validation import is_valid_nextcloud_doc_id
from nextcloud_mcp_server.vector.oauth_sync import (
NotProvisionedError,
get_user_client_basic_auth,
@@ -502,8 +503,9 @@ async def get_chunk_context(request: Request) -> JSONResponse:
# otherwise pass through to get_chunk_with_context and bottom out as a
# 404 from deep inside, not a clear 400. Nextcloud IDs are unsigned
# ints from MySQL auto_increment; doc_id stays a str downstream
# (Qdrant payload index is keyword-typed).
if not doc_id.isdigit():
# (Qdrant payload index is keyword-typed). is_valid_nextcloud_doc_id
# rejects "0", leading zeros, and Unicode digits that pass isdigit().
if not is_valid_nextcloud_doc_id(doc_id):
return JSONResponse(
{
"success": False,