block urls pointing to internal addresses (#1012)
This commit is contained in:
@@ -34,6 +34,7 @@ class Settings(BaseSettings):
|
|||||||
LOG_LEVEL: str = "INFO"
|
LOG_LEVEL: str = "INFO"
|
||||||
PORT: int = 8000
|
PORT: int = 8000
|
||||||
ALLOWED_ORIGINS: list[str] = ["*"]
|
ALLOWED_ORIGINS: list[str] = ["*"]
|
||||||
|
BLOCKED_HOSTS: list[str] = ["localhost"]
|
||||||
|
|
||||||
# Secret key for JWT. Please generate your own secret key in production
|
# Secret key for JWT. Please generate your own secret key in production
|
||||||
SECRET_KEY: str = "PLACEHOLDER"
|
SECRET_KEY: str = "PLACEHOLDER"
|
||||||
|
|||||||
@@ -502,3 +502,11 @@ class InvalidUrl(SkyvernHTTPException):
|
|||||||
super().__init__(
|
super().__init__(
|
||||||
f"Invalid URL: {url}. Skyvern supports HTTP and HTTPS urls.", status_code=status.HTTP_400_BAD_REQUEST
|
f"Invalid URL: {url}. Skyvern supports HTTP and HTTPS urls.", status_code=status.HTTP_400_BAD_REQUEST
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BlockedHost(SkyvernHTTPException):
|
||||||
|
def __init__(self, host: str) -> None:
|
||||||
|
super().__init__(
|
||||||
|
f"The host in your url is blocked: {host}",
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import ipaddress
|
||||||
|
|
||||||
from pydantic import HttpUrl, ValidationError, parse_obj_as
|
from pydantic import HttpUrl, ValidationError, parse_obj_as
|
||||||
|
|
||||||
|
from skyvern.config import settings
|
||||||
from skyvern.exceptions import InvalidUrl
|
from skyvern.exceptions import InvalidUrl
|
||||||
|
|
||||||
|
|
||||||
@@ -11,3 +14,18 @@ def validate_url(url: str) -> str:
|
|||||||
except ValidationError:
|
except ValidationError:
|
||||||
# Handle the validation error
|
# Handle the validation error
|
||||||
raise InvalidUrl(url=url)
|
raise InvalidUrl(url=url)
|
||||||
|
|
||||||
|
|
||||||
|
def is_blocked_host(host: str) -> bool:
|
||||||
|
try:
|
||||||
|
ip = ipaddress.ip_address(host)
|
||||||
|
# Check if the IP is private, link-local, loopback, or reserved
|
||||||
|
return ip.is_private or ip.is_link_local or ip.is_loopback or ip.is_reserved
|
||||||
|
except ValueError:
|
||||||
|
# If the host is not a valid IP address (e.g., it's a domain name like localhost), handle it here
|
||||||
|
for blocked_host in settings.BLOCKED_HOSTS:
|
||||||
|
if blocked_host == host:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ from datetime import datetime
|
|||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, HttpUrl
|
from pydantic import BaseModel, Field, HttpUrl, field_validator
|
||||||
|
|
||||||
from skyvern.exceptions import InvalidTaskStatusTransition, TaskAlreadyCanceled
|
from skyvern.exceptions import BlockedHost, InvalidTaskStatusTransition, TaskAlreadyCanceled
|
||||||
|
from skyvern.forge.sdk.core.validators import is_blocked_host
|
||||||
|
|
||||||
|
|
||||||
class ProxyLocation(StrEnum):
|
class ProxyLocation(StrEnum):
|
||||||
@@ -89,6 +90,17 @@ class TaskRequest(TaskBase):
|
|||||||
)
|
)
|
||||||
totp_verification_url: HttpUrl | None = None
|
totp_verification_url: HttpUrl | None = None
|
||||||
|
|
||||||
|
@field_validator("url", "webhook_callback_url", "totp_verification_url")
|
||||||
|
@classmethod
|
||||||
|
def validate_urls(cls, v: HttpUrl | None) -> HttpUrl | None:
|
||||||
|
if not v or not v.host:
|
||||||
|
return None
|
||||||
|
host = v.host
|
||||||
|
blocked = is_blocked_host(host)
|
||||||
|
if blocked:
|
||||||
|
raise BlockedHost(host=host)
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
class TaskStatus(StrEnum):
|
class TaskStatus(StrEnum):
|
||||||
created = "created"
|
created = "created"
|
||||||
|
|||||||
Reference in New Issue
Block a user