diff --git a/CHANGELOG.md b/CHANGELOG.md index 28873bc0b..aa0870cd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - add torch amd arch detection - fix prompt weighted lists and internal wildcards - improve `path_to_repo` handling for custom paths + - eliminate `api` auth security bypass ## Update for 2026-04-01 diff --git a/modules/api/api.py b/modules/api/api.py index f460fea51..41ec90876 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -107,15 +107,15 @@ class Api: # options api from modules.api import options - options.register_api(self.app) + options.register_api(self) # caption api from modules.api import caption - caption.register_api(self.app) + caption.register_api(self) # lora api from modules.api import loras - loras.register_api(self.app) + loras.register_api(self) # gallery api from modules.api import gallery @@ -123,19 +123,19 @@ class Api: # nudenet api from modules.api import nudenet - nudenet.register_api(self.app) + nudenet.register_api(self) # xyz-grid api from modules.api import xyz_grid - xyz_grid.register_api(self.app) + xyz_grid.register_api(self) # civitai api from modules.civitai import api_civitai - api_civitai.register_api(self.app) + api_civitai.register_api(self) # rembg api from modules.rembg import rembg_api - rembg_api.register_api(self.app) + rembg_api.register_api(self) # hide trailing-slash duplicates from OpenAPI schema from fastapi.routing import APIRoute diff --git a/modules/api/caption.py b/modules/api/caption.py index 893838775..ab12c1920 100644 --- a/modules/api/caption.py +++ b/modules/api/caption.py @@ -662,12 +662,12 @@ def get_tagger_models(): # Route Registration # ============================================================================= -def register_api(app): - app.add_api_route("/sdapi/v1/openclip", get_caption, methods=["GET"], response_model=list[str], tags=["Caption"]) - app.add_api_route("/sdapi/v1/caption", post_caption_dispatch, methods=["POST"], response_model=ResCaptionDispatch, tags=["Caption"]) - app.add_api_route("/sdapi/v1/openclip", post_caption, methods=["POST"], response_model=ResCaption, tags=["Caption"]) - app.add_api_route("/sdapi/v1/vqa", post_vqa, methods=["POST"], response_model=ResVQA, tags=["Caption"]) - app.add_api_route("/sdapi/v1/vqa/models", get_vqa_models, methods=["GET"], response_model=list[ItemVLMModel], tags=["Caption"]) - app.add_api_route("/sdapi/v1/vqa/prompts", get_vqa_prompts, methods=["GET"], response_model=ResVLMPrompts, tags=["Caption"]) - app.add_api_route("/sdapi/v1/tagger", post_tagger, methods=["POST"], response_model=ResTagger, tags=["Caption"]) - app.add_api_route("/sdapi/v1/tagger/models", get_tagger_models, methods=["GET"], response_model=list[ItemTaggerModel], tags=["Caption"]) +def register_api(api): + api.add_api_route("/sdapi/v1/openclip", get_caption, methods=["GET"], response_model=list[str], tags=["Caption"]) + api.add_api_route("/sdapi/v1/caption", post_caption_dispatch, methods=["POST"], response_model=ResCaptionDispatch, tags=["Caption"]) + api.add_api_route("/sdapi/v1/openclip", post_caption, methods=["POST"], response_model=ResCaption, tags=["Caption"]) + api.add_api_route("/sdapi/v1/vqa", post_vqa, methods=["POST"], response_model=ResVQA, tags=["Caption"]) + api.add_api_route("/sdapi/v1/vqa/models", get_vqa_models, methods=["GET"], response_model=list[ItemVLMModel], tags=["Caption"]) + api.add_api_route("/sdapi/v1/vqa/prompts", get_vqa_prompts, methods=["GET"], response_model=ResVLMPrompts, tags=["Caption"]) + api.add_api_route("/sdapi/v1/tagger", post_tagger, methods=["POST"], response_model=ResTagger, tags=["Caption"]) + api.add_api_route("/sdapi/v1/tagger/models", get_tagger_models, methods=["GET"], response_model=list[ItemTaggerModel], tags=["Caption"]) diff --git a/modules/api/loras.py b/modules/api/loras.py index 0d636fa64..f3f155b33 100644 --- a/modules/api/loras.py +++ b/modules/api/loras.py @@ -34,7 +34,7 @@ def _invalidate_extra_networks(): page.create_items('txt2img') -def register_api(app): - app.add_api_route("/sdapi/v1/lora", get_lora, methods=["GET"], response_model=dict, tags=["Enumerators"]) - app.add_api_route("/sdapi/v1/loras", get_loras, methods=["GET"], response_model=list[dict], tags=["Enumerators"]) - app.add_api_route("/sdapi/v1/refresh-loras", post_refresh_loras, methods=["POST"], tags=["Functional"]) +def register_api(api): + api.add_api_route("/sdapi/v1/lora", get_lora, methods=["GET"], response_model=dict, tags=["Enumerators"]) + api.add_api_route("/sdapi/v1/loras", get_loras, methods=["GET"], response_model=list[dict], tags=["Enumerators"]) + api.add_api_route("/sdapi/v1/refresh-loras", post_refresh_loras, methods=["POST"], tags=["Functional"]) diff --git a/modules/api/nudenet.py b/modules/api/nudenet.py index 3f0ef5e65..b35a35da2 100644 --- a/modules/api/nudenet.py +++ b/modules/api/nudenet.py @@ -61,8 +61,8 @@ def banned_words( return found -def register_api(app): - app.add_api_route("/sdapi/v1/nudenet", nudenet_censor, methods=["POST"], response_model=dict, tags=["Processing"]) - app.add_api_route("/sdapi/v1/prompt-lang", prompt_check, methods=["POST"], response_model=dict, tags=["Processing"]) - app.add_api_route("/sdapi/v1/image-guard", image_guard, methods=["POST"], response_model=dict, tags=["Processing"]) - app.add_api_route("/sdapi/v1/prompt-banned", banned_words, methods=["POST"], response_model=list, tags=["Processing"]) +def register_api(api_instance): + api_instance.add_api_route("/sdapi/v1/nudenet", nudenet_censor, methods=["POST"], response_model=dict, tags=["Processing"]) + api_instance.add_api_route("/sdapi/v1/prompt-lang", prompt_check, methods=["POST"], response_model=dict, tags=["Processing"]) + api_instance.add_api_route("/sdapi/v1/image-guard", image_guard, methods=["POST"], response_model=dict, tags=["Processing"]) + api_instance.add_api_route("/sdapi/v1/prompt-banned", banned_words, methods=["POST"], response_model=list, tags=["Processing"]) diff --git a/modules/api/options.py b/modules/api/options.py index 1ce2f22de..1c871757d 100644 --- a/modules/api/options.py +++ b/modules/api/options.py @@ -88,7 +88,7 @@ def get_options_info(): return {"options": options_info, "sections": list(sections_seen.values())} -def register_api(app): - app.add_api_route("/sdapi/v1/options", get_options, methods=["GET"], response_model=dict, tags=["Server"]) - app.add_api_route("/sdapi/v1/options", set_options, methods=["POST"], tags=["Server"]) - app.add_api_route("/sdapi/v1/options-info", get_options_info, methods=["GET"], tags=["Server"]) +def register_api(api): + api.add_api_route("/sdapi/v1/options", get_options, methods=["GET"], response_model=dict, tags=["Server"]) + api.add_api_route("/sdapi/v1/options", set_options, methods=["POST"], tags=["Server"]) + api.add_api_route("/sdapi/v1/options-info", get_options_info, methods=["GET"], tags=["Server"]) diff --git a/modules/api/xyz_grid.py b/modules/api/xyz_grid.py index 35eb01763..02504e228 100644 --- a/modules/api/xyz_grid.py +++ b/modules/api/xyz_grid.py @@ -1,5 +1,4 @@ - def xyz_grid_enum(option: str = "") -> list[dict]: """List XYZ grid axis options. Optionally filter by label prefix/suffix and expand choices.""" from scripts.xyz import xyz_grid_classes # pylint: disable=no-name-in-module @@ -21,5 +20,5 @@ def xyz_grid_enum(option: str = "") -> list[dict]: return options -def register_api(app): - app.add_api_route("/sdapi/v1/xyz-grid", xyz_grid_enum, methods=["GET"], response_model=list[dict], tags=["Scripts"]) +def register_api(api): + api.add_api_route("/sdapi/v1/xyz-grid", xyz_grid_enum, methods=["GET"], response_model=list[dict], tags=["Scripts"]) diff --git a/modules/civitai/api_civitai.py b/modules/civitai/api_civitai.py index 37ba28bf0..7612797d5 100644 --- a/modules/civitai/api_civitai.py +++ b/modules/civitai/api_civitai.py @@ -572,35 +572,35 @@ def legacy_post_civitai(page: str | None = None): # Registration # --------------------------------------------------------------------------- -def register_api(app): +def register_api(api): # New REST API - app.add_api_route("/sdapi/v2/civitai/search", get_search, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/model/{model_id}", get_model, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/version/{version_id}", get_version, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/version/by-hash/{hash_str}", get_version_by_hash, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/options", get_options, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/tags", get_tags, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/creators", get_creators, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/images", get_images, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/me", get_me, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/download", post_download, methods=["POST"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/download/{download_id}/cancel", post_download_cancel, methods=["POST"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/download/status", get_download_status, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/settings", get_settings, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/settings", post_settings, methods=["POST"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/resolve-path", get_resolve_path, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/metadata/scan", post_metadata_scan, methods=["POST"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/metadata/update", post_metadata_update, methods=["POST"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/bookmarks", get_bookmarks, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/bookmarks", post_bookmark, methods=["POST"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/bookmarks/{name}", delete_bookmark, methods=["DELETE"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/banned", get_banned, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/banned", post_banned, methods=["POST"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/banned/{name}", delete_banned, methods=["DELETE"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/history", get_history, methods=["GET"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/history", delete_history, methods=["DELETE"], tags=["CivitAI"]) - app.add_api_route("/sdapi/v2/civitai/check-local", post_check_local, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/search", get_search, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/model/{model_id}", get_model, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/version/{version_id}", get_version, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/version/by-hash/{hash_str}", get_version_by_hash, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/options", get_options, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/tags", get_tags, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/creators", get_creators, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/images", get_images, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/me", get_me, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/download", post_download, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/download/{download_id}/cancel", post_download_cancel, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/download/status", get_download_status, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/settings", get_settings, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/settings", post_settings, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/resolve-path", get_resolve_path, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/metadata/scan", post_metadata_scan, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/metadata/update", post_metadata_update, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/bookmarks", get_bookmarks, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/bookmarks", post_bookmark, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/bookmarks/{name}", delete_bookmark, methods=["DELETE"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/banned", get_banned, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/banned", post_banned, methods=["POST"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/banned/{name}", delete_banned, methods=["DELETE"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/history", get_history, methods=["GET"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/history", delete_history, methods=["DELETE"], tags=["CivitAI"]) + api.add_api_route("/sdapi/v2/civitai/check-local", post_check_local, methods=["POST"], tags=["CivitAI"]) # Legacy endpoints (backward compatibility) - app.add_api_route("/sdapi/v1/civitai", legacy_get_civitai, methods=["GET"], response_model=list, tags=["Models"]) - app.add_api_route("/sdapi/v1/civitai", legacy_post_civitai, methods=["POST"], response_model=list, tags=["Models"]) + api.add_api_route("/sdapi/v1/civitai", legacy_get_civitai, methods=["GET"], response_model=list, tags=["Models"]) + api.add_api_route("/sdapi/v1/civitai", legacy_post_civitai, methods=["POST"], response_model=list, tags=["Models"]) diff --git a/modules/rembg/rembg_api.py b/modules/rembg/rembg_api.py index 44a6c6b70..25bb72223 100644 --- a/modules/rembg/rembg_api.py +++ b/modules/rembg/rembg_api.py @@ -27,9 +27,9 @@ async def post_rembg( for pkg in ["dctorch==0.1.2", "pymatting", "pooch", "rembg"]: install(pkg, no_deps=True, ignore=False) import rembg - image = rembg.remove( + image = rembg.remove( # pylint: disable=c-extension-no-member input_image, - session=rembg.new_session(model), + session=rembg.new_session(model), # pylint: disable=c-extension-no-member only_mask=return_mask, alpha_matting=alpha_matting, alpha_matting_foreground_threshold=alpha_matting_foreground_threshold, @@ -39,5 +39,5 @@ async def post_rembg( return {"image": api.encode_pil_to_base64(image).decode("utf-8")} -def register_api(app): - app.add_api_route("/sdapi/v1/rembg", post_rembg, methods=["POST"], tags=["REMBG"]) +def register_api(api): + api.add_api_route("/sdapi/v1/rembg", post_rembg, methods=["POST"], tags=["REMBG"])