# Risk map — AUTOMATIC1111/stable-diffusion-webui

> Canonical page: https://codevetter.com/unpack/stable-diffusion-webui/risk-map

# Risk map — AUTOMATIC1111/stable-diffusion-webui

Scanned commit 82a973c04367123ae98bd9abdf80d9eda9b910e2 (2024-07-27). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

The dominant risk theme is remote-code-execution surface by design: extensions are arbitrary Python loaded at startup, the Extensions tab can git-clone and run install.py, and the Custom code script exec()s user code — mitigated only by flags that auto-disable extension access when the server is non-local. The API is unauthenticated unless --api-auth is set and can fetch remote URLs (SSRF-checked but opt-out-able). Shell-out is pervasive in launch_utils (shell=True subprocess). Checkpoint loading relies on a monkeypatched torch.load pickle scan that --disable-safe-unpickle bypasses.
- Arbitrary code execution by design: every .py in extensions/*/scripts and extensions/*/preload.py is exec_module'd at startup, and install_extension_from_url runs the extension's install.py; protection is only cmd_opts.disable_extension_access = webui_is_non_local AND not --enable-insecure-extension-access, asserted by check_access(). — [modules/ui_extensions.py#L22-24](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/ui_extensions.py#L22-L24) [modules/ui_extensions.py#L344-396](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/ui_extensions.py#L344-L396) [modules/shared_cmd_options.py#L17-18](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/shared_cmd_options.py#L17-L18) [modules/script_loading.py#L19-35](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/script_loading.py#L19-L35)
- scripts/custom_code.py executes arbitrary Python via exec()/eval() (exec_with_return builds an AST and runs it) gated only by --allow-code flag and a re-check inside run(). — [scripts/custom_code.py#L18-35](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/scripts/custom_code.py#L18-L35) [scripts/custom_code.py#L43-64](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/scripts/custom_code.py#L43-L64) [modules/cmd_args.py#L38](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/cmd_args.py#L38)
- The REST API has no auth by default: --api-auth is optional HTTP Basic; without it every /sdapi/v1/* route (including POST /options which can change output dirs, /train/* jobs, and --api-server-stop's /server-kill|restart|stop) is open to anyone who can reach the port. — [modules/api/api.py#L199-205](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/api/api.py#L199-L205) [modules/api/api.py#L220-251](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/api/api.py#L220-L251) [modules/cmd_args.py#L102-121](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/cmd_args.py#L102-L121)
- SSRF surface: decode_base64_to_image fetches http(s) URLs server-side when opts.api_enable_requests; verify_url() checks resolved IPs are global (blocks local addresses only when api_forbid_local_requests), but DNS-rebinding between gethostbyname_ex and requests.get is not closed. — [modules/api/api.py#L58-99](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/api/api.py#L58-L99)
- shell=True subprocess everywhere in launch_utils.run(): pip installs, git clone/pull/fetch/reset built as f-strings; git_clone interpolates URL/dir into a shell string (quoted but not escaped); TORCH_COMMAND/INDEX_URL env vars directly shape pip commands. — [modules/launch_utils.py#L89-118](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/launch_utils.py#L89-L118) [modules/launch_utils.py#L139-198](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/launch_utils.py#L139-L198) [modules/launch_utils.py#L319-346](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/launch_utils.py#L319-L346)
- Checkpoint deserialization: torch.load is globally replaced by safe.load which scans pickles (check_pt) for malicious code before unsafe_torch_load; --disable-safe-unpickle turns this off entirely; the scan is a blocklist-style heuristic, and safetensors path bypasses it. — [modules/safe.py#L79-195](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/safe.py#L79-L195) [modules/sd_models.py#L318-323](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/sd_models.py#L318-L323) [modules/cmd_args.py#L100](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/cmd_args.py#L100)
- Gradio's permissive CORS middleware is manually stripped post-launch (app.user_middleware filter) specifically because a malicious page could otherwise drive the local API to install extensions; any code re-adding middleware could silently reopen this hole. — [webui.py#L100-106](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/webui.py#L100-L106)
- File-system writes outside repo root: --data-dir/--ckpt-dir/--embeddings-dir and outdir_* options let users/API redirect outputs and model loads to arbitrary paths; images.save_image and Options.save have no sandboxing beyond --gradio-allowed-path for serving and --hide-ui-dir-config for hiding dir settings. — [modules/cmd_args.py#L22-37](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/cmd_args.py#L22-L37) [modules/shared_options.py#L79-87](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/shared_options.py#L79-L87) [modules/images.py#L624](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/images.py#L624) [modules/options.py#L122-124](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/options.py#L122-L124)
- Single global queue_lock + mutable shared.sd_model/shared.opts: process_images mutates global opts via override_settings then restores in finally — an exception or crash between set and restore, or concurrent API calls interleaving opts changes, can leave settings or the loaded checkpoint in an inconsistent state. (inferred) — [modules/processing.py#L819-860](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/processing.py#L819-L860) [modules/call_queue.py#L8-18](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/call_queue.py#L8-L18)
- Options.__setattr__ uses assert for freeze-settings enforcement ('assert not cmd_opts.freeze_settings'); running Python with -O would strip these checks, silently disabling the freeze protections. — [modules/options.py#L98-120](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/options.py#L98-L120)
- Startup update path fetches remote state and prints notices (version_check hits api.github.com; --update-all-extensions runs git pull --autostash across every repo under extensions_dir including subrepos) — network-dependent and can leave extension trees in autostash-conflict states. — [modules/launch_utils.py#L201-225](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/launch_utils.py#L201-L225) [modules/launch_utils.py#L440-442](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/launch_utils.py#L440-L442)
- sys.path is temporarily prepended with each script's basedir during load_scripts() — an extension script can shadow stdlib/modules imports for subsequently loaded scripts (restored in finally per file, but cross-file ordering via metadata.ini Requires/Before/After is attacker-orderable). (inferred) — [modules/scripts.py#L495-524](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/scripts.py#L495-L524) [modules/scripts.py#L393-468](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/82a973c04367123ae98bd9abdf80d9eda9b910e2/modules/scripts.py#L393-L468)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
