Skip to content

fix(api): require auth on model-manager and app-info endpoints (#9365)#9367

Merged
lstein merged 9 commits into
invoke-ai:mainfrom
lstein:fix/9365-unauth-model-manager-endpoints
Jul 21, 2026
Merged

fix(api): require auth on model-manager and app-info endpoints (#9365)#9367
lstein merged 9 commits into
invoke-ai:mainfrom
lstein:fix/9365-unauth-model-manager-endpoints

Conversation

@lstein

@lstein lstein commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #9365. A number of model-management and app-info routes carried no auth dependency at all, so in multi-user mode they were reachable by a fully unauthenticated network attacker — contradicting the documented model that model management is administrator-only.

The reported route, GET /api/v2/models/scan_folder, takes an attacker-controlled filesystem path and recursively enumerates it, returning absolute paths of model-like files. Its distinct 200/400/500 responses also formed an existence/readability oracle for arbitrary paths. Thanks to @geo-chen for the report.

An AST sweep of all 19 routers found 11 unauthenticated model-manager routes and 12 in app_info.py.

⚠️ Also found: unauthenticated credential disclosure

While auditing I found something with higher impact than the reported issue. GET /api/v1/app/runtime_config served the raw InvokeAIAppConfig to anyone — including remote_api_tokens (bearer tokens used for authenticated model downloads) and all four external_*_api_key values, in plaintext. The frontend only redacts them client-side in the About modal, which confirms they were being shipped to the browser.

Deployments running multi-user mode on an untrusted network should rotate any configured provider API keys and remote API tokens. Note this was not only reachable unauthenticated: because the About modal's client-side redaction never covered the external_*_api_key fields, any logged-in non-admin could also read them.

Changes

  • scan_folder requires AdminUserOrDefault, and every failure mode now returns one generic 400 with details going to the server log, removing the filesystem oracle.
  • Admin-only (AdminUserOrDefault): missing, hugging_face, starter_models, stats, hf_login, external_providers/config, logging (GET/POST), and the invocation_cache routes. All are either operator-only with no frontend caller, or rendered exclusively behind the admin-gated install panel.
  • Authenticated-user (CurrentUserOrDefault): model record reads, plus app_deps, patchmatch_status, external_providers/status.
  • runtime_config is administrator-only, and additionally masks API keys and download tokens server-side. Nothing outside the admin UI needed it: the three non-admin call sites read config.multiuser only to derive "admin or single-user", which already comes from /auth/status's multiuser_enabled. That predicate is now a shared useIsAdmin hook, which useIsModelManagerEnabled delegates to (semantics preserved exactly).
  • AboutModal's client-side redaction was incomplete — it masked remote_api_tokens but never the external_*_api_key fields, so any logged-in non-admin could copy provider API keys out of the debug blob. The config section is now admin-only and the server masks both.

Single-user mode is unaffected — *OrDefault resolves to a system admin when multiuser is off.

Two deviations from the issue's suggested remediation

  1. No scan_path allowlist. The issue recommends confining scan_path to models_path. I skipped it: install_model already accepts arbitrary local paths from admins, so an allowlist would be inconsistent and would break the legitimate "scan my external model drive" workflow. Auth is the real boundary; the oracle is closed separately via uniform errors.
  2. list_model_records is CurrentUserOrDefault, not admin. The issue suggests AdminUserOrDefault, but that would break generation for every non-admin, since the model dropdowns depend on this route.

Not fixed here

get_image_full, get_image_thumbnail, get_model_image and get_workflow_thumbnail remain unauthenticated. They are consumed via <img src> and so cannot carry a Bearer header — closing them requires a signed-URL or cookie scheme, which is a separate design change. GET /api/v1/app/version is intentionally left public.

Testing

  • New tests/app/routers/test_model_manager_authorization.py: 20 routes parametrized for 401-when-unauthenticated, 11 for 403-when-non-admin, plus oracle-uniformity and secret-redaction tests.
  • Five pre-existing tests patched only their router's ApiDependencies and not auth_dependencies, so they broke once the routes gained an auth dep. Fixed, and both routers are now registered in conftest.py's _PATCHED_API_DEPENDENCIES_MODULES.
  • Full router suite: 497 passed.
  • openapi.json regenerated: the diff is exactly the 21 gated routes gaining a security: [{HTTPBearer: []}] requirement. No route loses one and the rest of the document is byte-identical — which also serves as an independent record of which routes previously had no security requirement at all.
  • schema.ts is unchanged, since openapi-typescript does not encode security requirements in the generated types, so there is no frontend type churn.
  • Traced the UI to confirm non-admins won't hit 403s: every newly admin-gated query renders only inside InstallModels, which ModelPane.tsx shows only when useIsModelManagerEnabled() is true.

🤖 Generated with Claude Code

A number of model-management and app-info routes carried no auth dependency
at all. In multi-user mode this left them reachable by a fully unauthenticated
network attacker, contradicting the documented model that model management is
administrator-only.

The highest-impact route was `GET /api/v2/models/scan_folder`, which takes an
attacker-controlled filesystem path and recursively enumerates it, returning
absolute paths of model-like files. Its distinct 200/400/500 responses also
formed an existence/readability oracle for arbitrary paths.

While auditing, `GET /api/v1/app/runtime_config` turned out to be worse: it
served the raw `InvokeAIAppConfig` — including `remote_api_tokens` and the
`external_*_api_key` values — in plaintext, to anyone.

Changes:

- `scan_folder` now requires `AdminUserOrDefault`, and every failure mode
  returns one generic 400 (details go to the server log) so the response
  cannot be used as a filesystem oracle. Arbitrary paths remain scannable by
  admins, consistent with `install_model`, which already accepts local paths.
- Admin-only (`AdminUserOrDefault`): `missing`, `hugging_face`,
  `starter_models`, `stats`, `hf_login`, `external_providers/config`,
  `logging` (GET/POST) and the `invocation_cache` routes. All are either
  operator-only or rendered exclusively behind the admin-gated install panel.
- Authenticated-user (`CurrentUserOrDefault`): model record reads, which
  ordinary users need for generation, plus `runtime_config`, `app_deps`,
  `patchmatch_status` and `external_providers/status`.
- `runtime_config` now masks API keys and download tokens server-side rather
  than shipping them to the browser; the UI only ever needed the
  is-configured signal.

Single-user mode is unaffected — `*OrDefault` resolves to a system admin when
`multiuser` is off.

Not changed: the binary-serving routes (image full/thumbnail, model cover
image, workflow thumbnail) are consumed via `<img src>` and cannot carry a
Bearer header, so closing those needs a signed-URL or cookie scheme.

Closes invoke-ai#9365

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added api python PRs that change python files python-tests PRs that change python tests labels Jul 19, 2026
@lstein lstein added the 6.14.0 label Jul 19, 2026
@lstein lstein moved this to 6.14.x Theme: USER EXPERIENCE in Invoke - Community Roadmap Jul 19, 2026
The added auth dependencies make FastAPI emit a `security: [{HTTPBearer: []}]`
requirement on each affected route, so the checked-in schema no longer matched
the generated one.

Regenerated with the openapi-checks command. The diff is exactly the 21 routes
gated in the previous commit gaining a security requirement; no route loses
one, and the rest of the document is byte-identical.

`schema.ts` is unchanged — openapi-typescript does not encode security
requirements in the generated types.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the frontend PRs that change frontend files label Jul 19, 2026
Serving the runtime config to every authenticated user relied on a denylist:
`_redact_config_secrets` masks the credential fields we know about, so any
future secret added to `InvokeAIAppConfig` would leak by default until someone
remembered to update it. Admin-only is an allowlist posture without that
failure mode.

Nothing outside the admin UI actually needed it. The three non-admin call
sites read `config.multiuser` purely to derive "admin or single-user", which is
already available from the unauthenticated `/auth/status` route as
`multiuser_enabled` — the same source `useIsModelManagerEnabled` uses. The
remaining fields (`max_queue_history`, `image_subfolder_strategy`) only feed
admin-gated edit controls.

- Extract that predicate into `useIsAdmin`, and have `useIsModelManagerEnabled`
  delegate to it. Semantics are preserved exactly, so this is a pure refactor.
- Use it in the three settings components instead of `runtime_config`, and skip
  the query for non-admins so they generate no failed requests.
- AboutModal's debug blob now omits the config section for non-admins. Its
  client-side redaction is dropped: it only ever masked `remote_api_tokens` and
  never the `external_*_api_key` fields, so a logged-in non-admin could read
  provider API keys out of it. The server now masks both.

Server-side masking is kept as defense in depth — an admin's browser has no use
for the raw values either.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JPPhoto JPPhoto self-assigned this Jul 20, 2026

@JPPhoto JPPhoto left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  • invokeai/app/api/routers/app_info.py:198: PATCH /runtime_config still returns the unredacted runtime config. An admin changing max_queue_history or image_subfolder_strategy receives every external API key and remote_api_tokens value in the response, which the frontend then inserts into its RTK Query cache. This bypasses the PR's server-side redaction and its stated guarantee that even an admin browser never receives raw credentials. Return _redact_config_secrets(config) here as the GET route does. To expose this issue, add a test that configures secrets, patches a writable setting, and asserts the secrets are absent from the response.

  • invokeai/app/api/routers/model_manager.py:203: Making /models/missing admin-only breaks model selection for ordinary users. invokeai/frontend/web/src/services/api/hooks/modelsByType.ts:48 and :76 call this endpoint for every model hook and use its result to exclude records whose files are missing. A non-admin now receives 403, missingModelsData remains undefined, and the hooks consequently include every missing model in generation dropdowns; selecting one fails when execution tries to load its absent files. Keep this read available through CurrentUserOrDefault, or provide ordinary users another authenticated way to filter unavailable models. To expose this issue, add a test where a non-admin has a missing model record and verify it does not appear among selectable models.

  • invokeai/app/api/routers/app_info.py:394-424: The invocation-cache routes are now admin-only, but the universally available Queue tab remains unchanged. invokeai/frontend/web/src/features/queue/components/InvocationCacheStatus.tsx:13 therefore receives 403 and renders zeroed statistics, while invokeai/frontend/web/src/features/queue/components/ToggleInvocationCacheButton.tsx:28-35 interprets the missing status as "disabled" and presents an active Enable button to connected non-admins. Clicking it always fails with the generic mutation error toast. Gate the cache panel and mutations with useIsAdmin, or leave the status endpoint available to authenticated users while hiding admin-only controls. To expose this issue, add a frontend logic test for a connected non-admin with an authorization failure and assert that no cache mutation is offered.

… gating

Three issues from JPPhoto's review of invoke-ai#9367:

- `PATCH /runtime_config` echoed the updated config back unredacted, so an admin
  changing an unrelated setting still pulled every external API key and
  `remote_api_tokens` value into the browser's RTK Query cache, defeating the
  masking added to the GET route. It now returns `_redact_config_secrets(config)`
  as GET does.

- `GET /models/missing` is `CurrentUserOrDefault` again, not admin-only. The
  frontend's model hooks (`modelsByType.ts`) subtract this set from the model
  list so unusable records stay out of the generation dropdowns. Under an
  admin-only gate a non-admin got 403, the subtraction became a silent no-op,
  and missing models were offered for selection, failing only at execution.

- The invocation-cache panel lives on the universally available Queue tab, but
  its routes are admin-only. A non-admin saw zeroed statistics and an active
  Enable button that always 403s. The panel is now gated with `useIsAdmin`, and
  the enable/disable/clear hooks additionally refuse to offer the mutation to a
  non-admin, so the guard does not depend on the render site alone.

`useIsAdmin`'s predicate is extracted as a pure `getIsAdmin` so it can be
unit-tested alongside the new cache-control predicates.

Tests:
- `test_runtime_config_patch_response_has_no_secrets` patches a writable setting
  with secrets configured and asserts they are absent from the response.
- `test_non_admin_can_filter_out_missing_models` gives a non-admin one present
  and one missing model record and checks the missing one is not selectable.
- `invocationCacheControls.test.ts` asserts a connected non-admin is offered no
  cache mutation, and that admin semantics are unchanged.

openapi.json is unchanged: `/models/missing` keeps the same HTTPBearer security
requirement either way.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lstein

lstein commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks — all three confirmed and fixed in f8dad23.

1. PATCH /runtime_config returned the unredacted config. Correct, and it fully defeated the GET-side masking: the frontend writes the PATCH response straight into the RTK Query cache, so any settings change re-populated the browser with the raw keys. It now returns _redact_config_secrets(config). test_runtime_config_patch_response_has_no_secrets configures secrets, patches max_queue_history, and asserts neither the API key nor the token appears in the response body — and that the in-memory config keeps the real values, since the masking must be response-only.

2. /models/missing admin-only broke model selection. Confirmed — modelsByType.ts:48 and :76 subtract this set from the model list in buildModelsHook, which backs every model dropdown. Under 403 the subtraction became a silent no-op, so missing models were selectable and only failed at execution. Reverted to CurrentUserOrDefault; the route only reveals which records the user can already see are unusable. test_non_admin_can_filter_out_missing_models gives a non-admin one present and one absent record and asserts all - missing is just the present one.

3. Invocation cache panel on the Queue tab. Confirmed, and the ToggleInvocationCacheButton failure mode is exactly as you describe: undefined status reads as "disabled" and presents an active Enable button. I took the first of your two options — the panel is gated with useIsAdmin in QueueTabContent — since the stats are only meaningful next to the controls. I also pushed the guard into useEnableInvocationCache / useDisableInvocationCache / useClearInvocationCache so it does not rest on the render site alone. The disabled-state logic moved to pure predicates in invocationCacheControls.ts; invocationCacheControls.test.ts asserts a connected non-admin is offered no mutation (with or without status data), and that the admin semantics are unchanged. useIsAdmin's predicate is now an exported pure getIsAdmin so the test drives the same code the hook does.

openapi.json is unchanged — /models/missing carries the same HTTPBearer requirement under either dependency.

Verified: tests/app/routers 499 passed; frontend pnpm test:no-watch 1352 passed, lint:eslint/lint:prettier/lint:tsc/lint:dpdm clean.

… gating

Backend:
- scan_for_models: guard scan_path before pathlib.Path() — the query param
  defaults to None and Path(None) raised a TypeError (500) when omitted.
- scan_for_models: mid-scan failures return a detail-free 500 again instead of
  the generic 400. By that point is_dir() has confirmed existence, so a distinct
  status leaks nothing, and it restores the caller-error vs server-fault
  distinction for admins and retrying clients. Details still go to the log only.
- _redact_config_secrets docstring no longer overclaims: coverage is by naming
  convention, and a differently-named future credential must extend the function.
- openapi.json/schema.ts regenerated — the list_missing_models docstring was
  edited after the last regeneration, which would have failed typegen CI.

Tests:
- New default-deny meta-test walks app.routes and asserts every route carries an
  auth dependency or sits on an explicit 12-entry public allowlist, with a
  staleness check in the other direction. A hand-maintained route list cannot
  catch the next unauthenticated route — the exact failure mode behind invoke-ai#9365.
- The oracle test no longer scans '/' (a real, unbounded ModelSearch walk of the
  host filesystem); it uses tmp_path fixtures, and the mid-scan-failure path is
  exercised deterministically by stubbing ModelSearch.
- New test for the missing-scan_path 400.

Frontend:
- SettingsModal hides the Max Queue History field for non-admins instead of
  rendering it permanently blank-and-disabled (the backing runtime_config query
  is skipped for them), matching SettingsImageSubfolderStrategySelect.
- onModelInstallError only fetches HF token status for admins: the event is
  broadcast to every client, but /hf_login is admin-only, so every non-admin
  session fired a doomed request and logged a spurious 403.
- Consolidated five inline copies of the admin predicate (UseCacheCheckbox,
  useStarterModelsToast, NoContentForViewer, ModelPicker, UpscaleWarning) onto
  useIsAdmin, and getIsCustomNodesEnabled now delegates to getIsAdmin. The
  toast/viewer/picker copies treated a still-loading setup status as admin=true,
  disagreeing with the canonical predicate.
- getIsAdmin unit tests moved to a colocated useIsAdmin.test.ts per the
  frontend CLAUDE.md colocation rule; the cache-control tests no longer import it.

Test results: tests/app/routers 500 passed; frontend vitest 1354 passed;
eslint/prettier/tsc clean; production bundle rebuilt via vite build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lstein

lstein commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed b39f282 — fixes from a self-review pass over the branch.

Correctness

  • scan_for_models crashed with a 500 TypeError when scan_path was omitted: the query param defaults to None and pathlib.Path(None) was constructed before the not scan_path guard. The guard now runs first, and omitting the param is a clean 400 (new test).
  • Mid-scan failures (e.g. a PermissionError on a subdirectory of a valid path) return a detail-free 500 again rather than the same generic 400 as a bad path. Rationale: by that point is_dir() has already confirmed existence to the caller, so a distinct status adds no oracle surface — and collapsing server faults into 400 had removed the admin's only way to distinguish "fix my path" from "fix server permissions", while misleading clients that treat 4xx as non-retryable. Details still go only to the server log (asserted by a new test that stubs ModelSearch to throw).
  • openapi.json/schema.ts were stale: the list_missing_models docstring was edited after the last regeneration, which would have failed typegen CI. Regenerated — the diff is exactly the drifted description.

Hardening the fix itself

  • New default-deny meta-test: it walks app.routes and asserts every route either carries an auth dependency in its dependant tree or sits on an explicit 12-entry public allowlist (auth bootstrap, version, docs, and the <img src> binary routes), with a reverse check that flags stale allowlist entries. The hand-maintained PROTECTED_ROUTES list can only catch auth being removed from known routes; it cannot catch the next route added with no auth at all — which is exactly how [Security] InvokeAI: unauthenticated arbitrary-directory enumeration via /models/scan_folder in multi-user mode #9365 happened. The allowlist also turns the get_model_image exception into a recorded, enforced decision instead of PR-description lore.
  • The oracle test no longer scans / — that was a real, unmocked ModelSearch walk of the host filesystem whose runtime depended on scandir ordering (and could be very long on permissive runners). It now uses tmp_path fixtures.

Frontend

  • SettingsModal now hides Max Queue History for non-admins instead of rendering it permanently blank-and-disabled (its backing runtime_config query is skipped for them) — consistent with how the subfolder-strategy select handles the same situation.
  • model_install_error is broadcast to every client, but the handler called the now admin-only getHFTokenStatus unconditionally, so each non-admin session fired a doomed request and logged a spurious 403 per event. The HF-token branch is now gated on admin state; the toast concerns the server-wide HF token that only an admin can fix anyway.
  • Follow-through on this PR making useIsAdmin canonical: five pre-existing inline copies of the same predicate (UseCacheCheckbox, useStarterModelsToast, NoContentForViewer, ModelPicker, UpscaleWarning) now use the hook, and getIsCustomNodesEnabled delegates to getIsAdmin. Three of those copies treated a still-loading setup status as admin=true — the opposite of the canonical rule — so e.g. the starter-models toast could show the admin variant to a non-admin during the query window.
  • getIsAdmin's unit tests moved to a colocated useIsAdmin.test.ts.

Verified: tests/app/routers 500 passed; frontend vitest 1354 passed; eslint/prettier/tsc clean; production bundle rebuilt and smoke-tested locally.

…y its formatting

Local ruff passed but CI pins ruff@0.11.2, which flags zip() without strict=
(B905) and formats two signatures differently. Replaced the zip with a plain
loop over the paths and ran the pinned version's formatter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lstein
lstein requested a review from JPPhoto July 21, 2026 00:38

@JPPhoto JPPhoto left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  • tests/app/routers/test_model_manager_authorization.py:154: The new default-deny check exempts POST /api/v1/images/ solely because create_image_upload_entry currently returns 501. Unlike the browser image routes, this state-mutating endpoint has no reason to be public; if invokeai/app/api/routers/images.py:183 is later implemented without adding authentication, the allowlist will silently approve the new unauthenticated upload behavior and defeat the test's purpose. Add CurrentUserOrDefault now and remove the exemption. Test: In multiuser mode, assert that an unauthenticated POST /api/v1/images/ returns 401 while an authenticated request reaches the stub and returns 501.

  • tests/app/routers/test_model_manager_authorization.py:43: The authorization matrix omits two routes made admin-only by this PR: POST /api/v1/app/logging and GET /api/v1/app/invocation_cache/status. The default-deny test only detects the complete absence of authentication, so changing either dependency from AdminUserOrDefault to CurrentUserOrDefault would still pass every test and expose a server-wide control or operator statistic to non-admins. Test: Add both endpoints to ADMIN_ONLY_ROUTES, add POST /api/v1/app/logging to PROTECTED_ROUTES, and assert unauthenticated requests receive 401 and authenticated non-admin requests receive 403.

…e admin matrix

- create_image_upload_entry (POST /api/v1/images/) now requires
  CurrentUserOrDefault. It was allowlisted as public only because it is a
  501 stub, but a later implementation would have shipped unauthenticated
  without the route-audit test noticing. The allowlist entry is removed;
  unauthenticated requests get 401 and an authenticated request still
  reaches the stub (501, covered by a new test).
- POST /api/v1/app/logging and GET /api/v1/app/invocation_cache/status
  added to the authorization matrices (both PROTECTED_ROUTES and
  ADMIN_ONLY_ROUTES for the former, ADMIN_ONLY_ROUTES for the latter), so
  demoting either dependency from AdminUserOrDefault would now fail a test.
- openapi.json regenerated (HTTPBearer marker on the upload-entry op);
  schema.ts unchanged by typegen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lstein

lstein commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @JPPhoto — both points confirmed and fixed in 6e01b4b.

POST /api/v1/images/ (upload-entry stub): You're right that the 501-stub justification was exactly the kind of exemption the route-audit test exists to prevent. create_image_upload_entry now takes CurrentUserOrDefault, the PUBLIC_ROUTES entry is gone, and the route is in PROTECTED_ROUTES (unauthenticated → 401). A new test authenticates and asserts the request still reaches the stub and returns 501, so a future implementation inherits the auth dependency or fails the audit. openapi.json picked up the HTTPBearer security marker (5-line diff); typegen left schema.ts unchanged.

Authorization matrix gaps: POST /api/v1/app/logging is now in both PROTECTED_ROUTES (401) and ADMIN_ONLY_ROUTES (403 for authenticated non-admins), and GET /api/v1/app/invocation_cache/status is in ADMIN_ONLY_ROUTES. Demoting either from AdminUserOrDefault to CurrentUserOrDefault now fails a test rather than sliding past the default-deny check, which — as you note — only detects the total absence of auth.

All 46 authorization tests and the full tests/app/routers/ suite pass locally (one unrelated flake in test_custom_nodes.py fired during the sweep — that's the global-shutil.rmtree-patch/GC issue fixed by #9369 — and passes in isolation).

@JPPhoto

JPPhoto commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

I found and repaired this:

  • invokeai/frontend/web/src/features/system/components/AboutModal/AboutModal.tsx:37: The new admin-only runtime_config query is only skipped for non-admins; its response is not removed from the shared RTK Query cache. invokeai/frontend/web/src/services/api/endpoints/auth.ts:77 clears only gallery tags on logout, so an admin who loads the app and then logs out leaves filesystem paths, network configuration, and other admin-only settings in state.api.queries for a subsequent non-admin login in the same SPA until cache eviction. Hiding the query from hooks does not provide logout/login cache isolation. Test: populate getRuntimeConfig as an admin, perform the existing logout and non-admin login sequence without reloading the page, and assert that appInfoApi.endpoints.getRuntimeConfig.select() returns no cached data.

@JPPhoto JPPhoto left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@lstein Approved with the latest changes!

@lstein
lstein merged commit d315b89 into invoke-ai:main Jul 21, 2026
17 checks passed
@lstein
lstein deleted the fix/9365-unauth-model-manager-endpoints branch July 21, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.14.0 api frontend PRs that change frontend files python PRs that change python files python-tests PRs that change python tests

Projects

Status: 6.14.x Theme: USER EXPERIENCE

Development

Successfully merging this pull request may close these issues.

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

3 participants