r3gm commited on
Commit
2f6da1c
·
verified ·
1 Parent(s): c858018

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +63 -13
utils.py CHANGED
@@ -1,5 +1,8 @@
1
  import os
2
  import re
 
 
 
3
  import gradio as gr
4
  from constants import (
5
  DIFFUSERS_FORMAT_LORAS,
@@ -20,7 +23,6 @@ from diffusers.pipelines.pipeline_loading_utils import variant_compatible_siblin
20
  from stablepy.diffusers_vanilla.utils import checkpoint_model_type
21
  from pathlib import PosixPath
22
  from unidecode import unidecode
23
- import urllib.parse
24
  import copy
25
  import requests
26
  from requests.adapters import HTTPAdapter
@@ -39,8 +41,42 @@ MODEL_ARCH = {
39
  }
40
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def read_safetensors_header_from_url(url: str):
43
  """Read safetensors header from a remote Hugging Face file."""
 
44
  meta = get_hf_file_metadata(url)
45
 
46
  # Step 1: first 8 bytes → header length
@@ -248,7 +284,7 @@ def civ_redirect_down(url, dir_, civitai_api_key, romanize, alternative_name):
248
  curl_command = (
249
  f'curl -L -sI --connect-timeout 5 --max-time 5 '
250
  f'-H "Content-Type: application/json" '
251
- f'-H "Authorization: Bearer {civitai_api_key}" "{url}"'
252
  )
253
 
254
  headers = os.popen(curl_command).read()
@@ -277,7 +313,7 @@ def civ_redirect_down(url, dir_, civitai_api_key, romanize, alternative_name):
277
 
278
  wget_command = (
279
  f'wget -c -nv '
280
- f'-O "{os.path.join(dir_, filename_base)}" "{redirect_url}"'
281
  )
282
  r_code = os.system(wget_command) # noqa
283
 
@@ -303,7 +339,7 @@ def civ_api_down(url, dir_, civitai_api_key, civ_filename):
303
  if not civ_filename:
304
  wget_command = (
305
  f'wget -c -nv '
306
- f'-P "{dir_}" "{url_dl}"'
307
  )
308
  os.system(wget_command)
309
 
@@ -313,12 +349,13 @@ def civ_api_down(url, dir_, civitai_api_key, civ_filename):
313
  if not os.path.exists(output_path):
314
  wget_command = (
315
  f'wget -c -nv '
316
- f'-O "{output_path}" "{url_dl}"'
317
  )
318
  os.system(wget_command)
319
 
320
  return output_path
321
 
 
322
  def drive_down(url, dir_):
323
  import gdown
324
 
@@ -361,13 +398,13 @@ def hf_down(url, dir_, hf_token, romanize):
361
  if hf_token:
362
  os.system(
363
  f'wget -c -nv '
364
- f'--header="Authorization: Bearer {hf_token}" '
365
- f'-O "{os.path.join(dir_, filename)}" "{url}"'
366
  )
367
  else:
368
  os.system(
369
  f'wget -c -nv '
370
- f'-O "{os.path.join(dir_, filename)}" "{url}"'
371
  )
372
 
373
  return output_path
@@ -375,13 +412,26 @@ def hf_down(url, dir_, hf_token, romanize):
375
 
376
  def download_things(directory, url, hf_token="", civitai_api_key="", romanize=False):
377
  url = url.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  downloaded_file_path = None
379
 
380
- if "drive.google.com" in url:
381
  downloaded_file_path = drive_down(url, directory)
382
- elif "huggingface.co" in url:
383
  downloaded_file_path = hf_down(url, directory, hf_token, romanize)
384
- elif "civitai." in url:
385
  url = url.replace("civitai.red", "civitai.com")
386
  if not civitai_api_key:
387
  msg = "You need an API key to download Civitai models."
@@ -407,7 +457,7 @@ def download_things(directory, url, hf_token="", civitai_api_key="", romanize=Fa
407
  else:
408
  os.system(
409
  f'wget -c -nv '
410
- f'-P "{directory}" "{url}"'
411
  )
412
 
413
  return downloaded_file_path
@@ -726,4 +776,4 @@ def html_template_message(msg):
726
 
727
  def escape_html(text):
728
  """Escapes HTML special characters in the input text."""
729
- return text.replace("<", "&lt;").replace(">", "&gt;").replace("\n", "<br>")
 
1
  import os
2
  import re
3
+ import ipaddress
4
+ import urllib.parse
5
+ from shlex import quote as shqt
6
  import gradio as gr
7
  from constants import (
8
  DIFFUSERS_FORMAT_LORAS,
 
23
  from stablepy.diffusers_vanilla.utils import checkpoint_model_type
24
  from pathlib import PosixPath
25
  from unidecode import unidecode
 
26
  import copy
27
  import requests
28
  from requests.adapters import HTTPAdapter
 
41
  }
42
 
43
 
44
+ def validate_url(url: str) -> str:
45
+ """Validate URL protocol and block SSRF (localhost, private & cloud metadata IPs)."""
46
+ url = url.strip()
47
+ if not url:
48
+ raise ValueError("URL cannot be empty.")
49
+
50
+ parsed = urllib.parse.urlparse(url)
51
+ if parsed.scheme not in ("http", "https"):
52
+ raise ValueError(f"Invalid protocol '{parsed.scheme}'. Only HTTP/HTTPS are allowed.")
53
+
54
+ hostname = (parsed.hostname or "").lower()
55
+ if not hostname:
56
+ raise ValueError("Invalid URL: missing hostname.")
57
+
58
+ # SSRF Protection: Block loopback and local hosts
59
+ if hostname in ("localhost", "0.0.0.0", "127.0.0.1", "::1"):
60
+ raise ValueError("Access to local/loopback address is blocked.")
61
+
62
+ # SSRF Protection: Block private and link-local / cloud metadata IPs (e.g. 169.254.169.254)
63
+ try:
64
+ ip = ipaddress.ip_address(hostname)
65
+ if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
66
+ raise ValueError(f"Access to internal IP ({hostname}) is blocked.")
67
+ except ValueError:
68
+ pass # Standard domain name
69
+
70
+ # Normalize Civitai domain alias
71
+ if hostname == "civitai.red" or hostname.endswith(".civitai.red"):
72
+ url = url.replace("civitai.red", "civitai.com")
73
+
74
+ return url
75
+
76
+
77
  def read_safetensors_header_from_url(url: str):
78
  """Read safetensors header from a remote Hugging Face file."""
79
+ url = validate_url(url)
80
  meta = get_hf_file_metadata(url)
81
 
82
  # Step 1: first 8 bytes → header length
 
284
  curl_command = (
285
  f'curl -L -sI --connect-timeout 5 --max-time 5 '
286
  f'-H "Content-Type: application/json" '
287
+ f'-H {shqt(f"Authorization: Bearer {civitai_api_key}")} {shqt(url)}'
288
  )
289
 
290
  headers = os.popen(curl_command).read()
 
313
 
314
  wget_command = (
315
  f'wget -c -nv '
316
+ f'-O {shqt(os.path.join(dir_, filename_base))} {shqt(redirect_url)}'
317
  )
318
  r_code = os.system(wget_command) # noqa
319
 
 
339
  if not civ_filename:
340
  wget_command = (
341
  f'wget -c -nv '
342
+ f'-P {shqt(dir_)} {shqt(url_dl)}'
343
  )
344
  os.system(wget_command)
345
 
 
349
  if not os.path.exists(output_path):
350
  wget_command = (
351
  f'wget -c -nv '
352
+ f'-O {shqt(output_path)} {shqt(url_dl)}'
353
  )
354
  os.system(wget_command)
355
 
356
  return output_path
357
 
358
+
359
  def drive_down(url, dir_):
360
  import gdown
361
 
 
398
  if hf_token:
399
  os.system(
400
  f'wget -c -nv '
401
+ f'--header={shqt(f"Authorization: Bearer {hf_token}")} '
402
+ f'-O {shqt(output_path)} {shqt(url)}'
403
  )
404
  else:
405
  os.system(
406
  f'wget -c -nv '
407
+ f'-O {shqt(output_path)} {shqt(url)}'
408
  )
409
 
410
  return output_path
 
412
 
413
  def download_things(directory, url, hf_token="", civitai_api_key="", romanize=False):
414
  url = url.strip()
415
+ if not url:
416
+ return None
417
+
418
+ # SSRF & protocol validation
419
+ try:
420
+ url = validate_url(url)
421
+ except Exception as e:
422
+ msg = f"Download blocked: {e}"
423
+ print(f"\033[91m{msg}\033[0m")
424
+ gr.Warning(msg)
425
+ return None
426
+
427
+ hostname = (urllib.parse.urlparse(url).hostname or "").lower()
428
  downloaded_file_path = None
429
 
430
+ if hostname == "drive.google.com" or hostname.endswith(".drive.google.com"):
431
  downloaded_file_path = drive_down(url, directory)
432
+ elif hostname in ("huggingface.co", "hf.co") or hostname.endswith((".huggingface.co", ".hf.co")):
433
  downloaded_file_path = hf_down(url, directory, hf_token, romanize)
434
+ elif hostname == "civitai.com" or hostname.endswith(".civitai.com"):
435
  url = url.replace("civitai.red", "civitai.com")
436
  if not civitai_api_key:
437
  msg = "You need an API key to download Civitai models."
 
457
  else:
458
  os.system(
459
  f'wget -c -nv '
460
+ f'-P {shqt(directory)} {shqt(url)}'
461
  )
462
 
463
  return downloaded_file_path
 
776
 
777
  def escape_html(text):
778
  """Escapes HTML special characters in the input text."""
779
+ return text.replace("<", "&lt;").replace(">", "&gt;").replace("\n", "<br>")