Skip to content

fix: require auth and tenant check on /thumbnails endpoint (CWE-287)#14800

Closed
sebastiondev wants to merge 2 commits into
infiniflow:mainfrom
sebastiondev:fix/cwe287-document-app-thumbnails-66ee
Closed

fix: require auth and tenant check on /thumbnails endpoint (CWE-287)#14800
sebastiondev wants to merge 2 commits into
infiniflow:mainfrom
sebastiondev:fix/cwe287-document-app-thumbnails-66ee

Conversation

@sebastiondev
Copy link
Copy Markdown
Contributor

Summary

The GET /api/v1/thumbnails endpoint introduced in #14344 (api/apps/restful_apis/document_api.py) is missing authentication. Every other route in the same file — and the equivalent route in the legacy document_app.py — is protected by @login_required and performs a tenant-scoped access check. This one isn't.

The result is that anyone who can reach the API can submit arbitrary doc_ids and receive a {doc_id: thumbnail_path} mapping, which leaks kb_id values, thumbnail filenames, and confirms the existence of document IDs across tenants. Because RAGFlow binds to 0.0.0.0 by default, this is reachable on internet-exposed deployments.

  • CWE-287 — Improper Authentication
  • File / function: api/apps/restful_apis/document_api.pylist_thumbnails()
  • Severity: Medium (unauthenticated information disclosure across tenants)
  • Introduced in: Refactor: migrate document thumbnails API #14344

Fix

Two minimal changes, mirroring the patterns already used by neighboring routes in the same file:

  1. Add @login_required to the route.
  2. For each requested doc_id, call DocumentService.accessible(doc_id, current_user.id) and return AUTHENTICATION_ERROR if the current user does not have access to it.
@manager.route("/thumbnails", methods=["GET"])  # noqa: F821
@login_required
def list_thumbnails():
    ...
    for doc_id in doc_ids:
        if not DocumentService.accessible(doc_id, current_user.id):
            return get_json_result(data=False, message="No authorization.", code=RetCode.AUTHENTICATION_ERROR)

This matches exactly how DocumentService.accessible is used throughout the rest of the codebase for per-document authorization.

Diff is 6 added / 1 changed line in a single file.

What was tested

  • Verified via grep that list_thumbnails was the only route in restful_apis/document_api.py without @login_required; the neighboring routes (/list, /run, /change_status, /rm, etc.) all use the same login_required + DocumentService.accessible(...) pattern this PR adopts.
  • Confirmed DocumentService.accessible(doc_id, current_user.id) is the standard tenant-scoped check used by other endpoints in the file.
  • Confirmed current_user is already exported from api.apps and used elsewhere in this module.
  • Confirmed the legacy api/apps/document_app.py thumbnails endpoint (pre-migration) also required login, so this restores the prior security posture rather than changing intended behavior.

Security analysis

Pre-fix, an unauthenticated attacker on the network can:

  1. GET /api/v1/thumbnails?doc_ids=<id> with any document IDs (or enumerate via brute force / leaks from other endpoints).
  2. Receive a JSON map of {doc_id: "<kb_id>/<filename>"} for any document ID that exists, regardless of which tenant owns it.

That gives the attacker:

  • Confirmation of valid document IDs (oracle for further attacks).
  • Knowledge base IDs (kb_id) belonging to other tenants.
  • Thumbnail filenames stored in the object store.

Post-fix, the request requires a valid session and each doc_id must belong to a tenant the caller has access to.

Adversarial review

Before submitting, we tried to disprove this. Specifically: (a) is there an upstream auth gate (e.g. nginx, a Quart before_request) that protects this route despite the missing decorator? There isn't — the other routes in this file rely solely on @login_required, and removing it from any of them would expose them too. (b) Are doc_id values unguessable enough to make this benign? They are UUID-like, but the endpoint still functions as an existence/enumeration oracle and leaks kb_id plus filenames once an ID is known (e.g. from logs, referrer leaks, or another endpoint), so it's a real issue independent of brute-force feasibility. (c) Does DocumentService.get_thumbnails itself enforce access? It does not — it's a plain DB lookup by ID.

Notes

A previous version of this PR (#13822) was opened against an earlier commit and later closed for inactivity. This is a fresh submission rebased onto current main with the same minimal fix. Happy to adjust naming, error code, or split per reviewer preference.

Submitted by Sebastion — autonomous open-source security research from Foundation Machines. Free for public repos via the Sebastion AI GitHub App.

@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. 🐞 bug Something isn't working, pull request that fix bug. labels May 11, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1bb20e23-dc29-4505-b9cd-e6ac721418de

📥 Commits

Reviewing files that changed from the base of the PR and between bfe9763 and 8e1b559.

📒 Files selected for processing (1)
  • api/apps/restful_apis/document_api.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/apps/restful_apis/document_api.py

📝 Walkthrough

Walkthrough

The /thumbnails endpoint in document_api.py now requires login and checks each requested doc_id via DocumentService.accessible(doc_id, current_user.id), returning an authorization error if any requested document is not accessible.

Changes

Thumbnail Endpoint Authorization

Layer / File(s) Summary
Authentication Dependencies
api/apps/restful_apis/document_api.py
current_user is imported to access the authenticated user context in authorization checks.
Endpoint Requires Login
api/apps/restful_apis/document_api.py
@login_required decorator added to list_thumbnails.
Per-document Authorization
api/apps/restful_apis/document_api.py
list_thumbnails calls DocumentService.accessible(doc_id, current_user.id) for each doc_id and returns an authorization error when access is denied.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DocumentAPI
  participant CurrentUser
  participant DocumentService
  Client->>DocumentAPI: GET /thumbnails?doc_ids=...
  DocumentAPI->>CurrentUser: read current_user context (current_user.id)
  DocumentAPI->>DocumentService: accessible(doc_id, current_user.id)
  DocumentService-->>DocumentAPI: accessible? (true/false)
  alt accessible for all
    DocumentAPI-->>Client: 200 OK (thumbnails)
  else any inaccessible
    DocumentAPI-->>Client: 403 Forbidden (authorization error)
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Possibly related issues

Possibly related PRs

  • infiniflow/ragflow#14625: Also adds tenant-scoped DocumentService.accessible() checks and login protection in document_api.py.

Suggested labels

🐖api

Suggested reviewers

  • wangq8
  • xugangqiang
  • yingfeng

Poem

🐰 I hopped where thumbnails once stood bare,
Found doors unlatched in open air.
I fetched current_user, checked each doc id,
Guarded the doors — no stray slip inside.
Now thumbnails greet only those who've tried.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main security fix: adding authentication and tenant checks to the /thumbnails endpoint, and references the CWE-287 vulnerability being addressed.
Description check ✅ Passed The description is comprehensive and covers problem statement, fix details, testing verification, and security analysis. However, it does not use the required template structure with 'What problem does this PR solve?' and 'Type of change' sections.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/apps/restful_apis/document_api.py`:
- Around line 1209-1211: Add a warning log before the authorization-denial
return in the thumbnail access loop: when DocumentService.accessible(doc_id,
current_user.id) is false, log a warning that includes the doc_id and
current_user.id (and optional context like request path or action "thumbnail
access") and then return get_json_result(...,
code=RetCode.AUTHENTICATION_ERROR). Use the module logger already used in this
file (or create one via logging.getLogger(__name__) if absent) and place the log
immediately before the existing return in the denial branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b5240d7b-08f3-4289-af84-112459f47f2e

📥 Commits

Reviewing files that changed from the base of the PR and between 2f2d156 and bfe9763.

📒 Files selected for processing (1)
  • api/apps/restful_apis/document_api.py

Comment thread api/apps/restful_apis/document_api.py
@yingfeng yingfeng added the ci Continue Integration label May 11, 2026
@yingfeng yingfeng marked this pull request as draft May 11, 2026 13:58
@yingfeng yingfeng marked this pull request as ready for review May 11, 2026 13:58
@codecov
Copy link
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.16%. Comparing base (965717c) to head (8e1b559).
⚠️ Report is 239 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #14800      +/-   ##
==========================================
- Coverage   95.30%   94.16%   -1.14%     
==========================================
  Files          10       10              
  Lines         703      703              
  Branches      112      112              
==========================================
- Hits          670      662       -8     
- Misses         16       25       +9     
+ Partials       17       16       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels May 11, 2026
@sebastiondev
Copy link
Copy Markdown
Contributor Author

Thanks for the catch — agreed, the denial path should be observable.

Added a logging.warning immediately before the AUTHENTICATION_ERROR return at the thumbnail access check, including user_id, doc_id, and request.path so denied attempts are auditable. Uses the existing module-level logging import (the file already uses logging.warning(...) / logging.error(...) elsewhere, so I kept the style consistent rather than introducing a new logger = logging.getLogger(__name__)).

Commit: 8e1b559

for doc_id in doc_ids:
    if not DocumentService.accessible(doc_id, current_user.id):
        logging.warning(
            "thumbnail access denied: user_id=%s doc_id=%s path=%s",
            current_user.id, doc_id, request.path,
        )
        return get_json_result(data=False, message="No authorization.", code=RetCode.AUTHENTICATION_ERROR)

@KevinHuSh
Copy link
Copy Markdown
Collaborator

Appreciations!
Duplicated PR #14765.

@KevinHuSh KevinHuSh closed this May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 bug Something isn't working, pull request that fix bug. ci Continue Integration size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants