Skip to content

[WIP] Fix unauthenticated directory enumeration via scan_folder#9366

Closed
lstein with Copilot wants to merge 1 commit into
mainfrom
copilot/security-invokeai-fix-unauthenticated-access
Closed

[WIP] Fix unauthenticated directory enumeration via scan_folder#9366
lstein with Copilot wants to merge 1 commit into
mainfrom
copilot/security-invokeai-fix-unauthenticated-access

Conversation

Copilot AI commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.


This section details on the original issue you should resolve

<issue_title>[Security] InvokeAI: unauthenticated arbitrary-directory enumeration via /models/scan_folder in multi-user mode</issue_title>
<issue_description>### Is there an existing issue for this problem?

  • I have searched the existing issues

Install method

Invoke's Launcher

Operating system

Linux

GPU vendor

Nvidia (CUDA)

GPU model

No response

GPU VRAM

No response

Version number

commit 0df229f

Browser

No response

System Information

No response

What happened

reported via email on 13 June 2026 - no response, trying here:

Summary

The model-manager scan_folder endpoint takes an attacker-controlled filesystem path and recursively enumerates it, returning absolute paths of model-like files, with no authentication dependency. Sibling mutation routes declare AdminUserOrDefault, but this read route declares none, and the only global middleware merely refreshes tokens. So even in the shipped, documented Multi-User Mode, an unauthenticated network attacker can enumerate arbitrary server directories and use the distinct status codes as a filesystem existence/readability oracle, contradicting the documented model that model management is administrator-only. Confirmed against the verbatim handler body and the ModelSearch.

Details

Sink: invokeai/app/api/routers/model_manager.py:343 async def scan_for_models(scan_path: str = Query(...)), route GET /api/v2/models/scan_folder, mounted at /api (invokeai/app/api_app.py:177).

Source: the scan_path query parameter (any filesystem path).

Flow: scan_for_models -> ModelSearch().search(path) (invokeai/backend/model_manager/search.py:91) -> _walk_directory (:100) recursively os.scandirs the attacker path to depth 20 and returns absolute paths of every model-like file (*.ckpt/.bin/.pth/.safetensors/.pt/.gguf, or any dir containing config.json/model_index.json/etc.). The response returns those paths to the caller.

The handler signature has no auth dependency, unlike install_model/delete_model/convert_model which declare AdminUserOrDefault. The only global middleware (SlidingWindowTokenMiddleware, api_app.py:84) refreshes tokens and does not enforce auth. So with multiuser=True, the endpoint is reachable by a fully unauthenticated attacker, violating the documented Multi-User model ("Non-administrator users do not have the ability to install or configure models"). The same no-auth gap affects list_model_records, get_model_record, get_hugging_face_models, get_stats, etc.; scan_folder is the highest impact because the path is attacker-chosen. The 400 (nonexistent) vs 500 (exists-but-unreadable) vs 200 (readable) responses form an existence oracle for arbitrary paths.

The boundary crossed is an unauthenticated network attacker reaching a model-management endpoint that the documented multi-user security model reserves for administrators.

PoC

GET /api/v2/models/scan_folder?scan_path=/home/victim/private   (no Authorization header)  -> 200, model paths
GET /api/v2/models/scan_folder?scan_path=/etc/ssl               -> 500 (exists, unreadable)
GET /api/v2/models/scan_folder?scan_path=/nonexistent_xyz       -> 400 (does not exist)

Validated by importing the genuine ModelSearch and executing the verbatim scan_for_models body extracted from the repo via AST (decorators stripped, body unchanged), mounted in FastAPI exactly as api_app.py does (per-route deps, no global auth), with multiuser=True:

GET /api/v2/models/scan_folder?scan_path=/home/.../victim_secret_models   (NO Authorization header)
-> HTTP 200
-> [{'path':'.../victim_secret_models/private_lora','is_installed':False},
    {'path':'.../victim_secret_models/top_secret.ckpt','is_installed':False}]
oracle: /nonexistent_xyz->400, /etc/ssl->500(exists,unreadable), $HOME->500, victim->200

An AST audit confirmed scan_for_models/list_model_records/get_hugging_face_models carry no auth dependency while install_model/delete_model/convert_model carry AdminUserOrDefault.

Impact

On a multi-user InvokeAI deployment, an unauthenticated network attacker enumerates arbitrary server directories (model inventory and absolute paths) and probes arbitrary path existence/readability, bypassing the auth system that the multi-user mode exists to enforce. In default single-user mode there is no auth by design and the host is meant to be local-only, so the security claim, and the bug, are specific to multi-user mode.

Remediation

Add current_admin: AdminUserOrDefault (matching install_model) to scan_for_models and to the other unauthenticated read endpoints (list_model_records, get_model_record, get_model_records_by_attrs/hash, get_hugging_face_models, get_stats, get_model_image, get_starter_models, get_hf_login_status). For scan_folder specifically, constrain scan_path to a configured allowlist root (under models_path/configured search dirs) instead of accepting any absolute path, and return a generic 4xx for both nonexistent and unreadable paths to remove the existence oracle.

What you expected to happen

How to reproduce the problem

No response

Additional context

No response

Discord username

No response</issue_description>

Comments on the Issue (you are @copilot in this section)

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.

[Security] InvokeAI: unauthenticated arbitrary-directory enumeration via /models/scan_folder in multi-user mode

2 participants