proxywhirl.utils

Utility functions for logging, validation, and encryption.

Classes

CLILock

Context manager for CLI concurrency locking with Typer-aware error handling.

Functions

atomic_write(path, content[, encoding])

Write content atomically using temp file + rename.

atomic_write_json(path, data, **json_kwargs)

Write JSON data atomically.

configure_logging([level, format_type, redact_credentials])

Configure loguru logging with optional JSON formatting and credential redaction.

create_proxy_from_url(url[, source, tags])

Create a Proxy instance from a URL string.

decrypt_credentials(ciphertext, key)

Decrypt credentials using Fernet symmetric encryption.

encrypt_credentials(plaintext[, key])

Encrypt credentials using Fernet symmetric encryption.

generate_encryption_key()

Generate a new Fernet encryption key.

is_valid_proxy_url(url)

Validate proxy URL format.

mask_proxy_url(url)

Mask credentials in a proxy URL for safe display in verbose/debug output.

mask_secret_str(secret)

Mask a SecretStr or string value for safe display.

parse_proxy_url(url)

Parse proxy URL into components.

proxy_to_dict(proxy[, include_stats])

Convert a Proxy instance to a dictionary.

scrub_credentials_from_dict(data)

Recursively scrub credentials from a dictionary for safe output.

validate_proxy_model(proxy)

Validate a Proxy model instance.

validate_target_url_safe(url[, allow_private])

Validate a target URL to prevent SSRF attacks (API-safe version).

Module Contents

class proxywhirl.utils.CLILock(config_dir)[source]

Context manager for CLI concurrency locking with Typer-aware error handling.

Initialize lock file manager.

Parameters:

config_dir (pathlib.Path) – Directory where lock file will be created

proxywhirl.utils.atomic_write(path, content, encoding='utf-8')[source]

Write content atomically using temp file + rename.

This prevents partial writes and corruption if the process is interrupted during the write operation.

Parameters:
  • path (pathlib.Path) – Target file path

  • content (str) – Content to write

  • encoding (str) – File encoding (default: utf-8)

Raises:

OSError – If write or rename fails

Return type:

None

proxywhirl.utils.atomic_write_json(path, data, **json_kwargs)[source]

Write JSON data atomically.

Parameters:
  • path (pathlib.Path) – Target file path

  • data (Any) – Data to serialize as JSON

  • **json_kwargs (Any) – Arguments to pass to json.dumps()

Return type:

None

proxywhirl.utils.configure_logging(level='INFO', format_type='json', redact_credentials=True)[source]

Configure loguru logging with optional JSON formatting and credential redaction.

Parameters:
  • level (str) – Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)

  • format_type (str) – “auto”, “json”, or “text”. “auto” detects TTY on stderr.

  • redact_credentials (bool) – Whether to redact sensitive data

Return type:

None

proxywhirl.utils.create_proxy_from_url(url, source=ProxySource.USER, tags=None)[source]

Create a Proxy instance from a URL string.

Parameters:
  • url (str) – Proxy URL

  • source (proxywhirl.models.ProxySource) – Origin of proxy

  • tags (set[str] | None) – Optional tags

Returns:

Proxy instance

Raises:

ValueError – If URL is invalid

Return type:

proxywhirl.models.Proxy

proxywhirl.utils.decrypt_credentials(ciphertext, key)[source]

Decrypt credentials using Fernet symmetric encryption.

Parameters:
  • ciphertext (str) – Encrypted text (base64-encoded)

  • key (str) – Encryption key (base64-encoded)

Returns:

Decrypted plaintext

Raises:
  • ImportError – If cryptography package not installed

  • InvalidToken – If key is incorrect or ciphertext is invalid

Return type:

str

proxywhirl.utils.encrypt_credentials(plaintext, key=None)[source]

Encrypt credentials using Fernet symmetric encryption.

Parameters:
  • plaintext (str) – Plaintext to encrypt

  • key (str | None) – Optional encryption key (base64-encoded). If None, generates new key.

Returns:

Encrypted text (base64-encoded)

Raises:

ImportError – If cryptography package not installed

Return type:

str

proxywhirl.utils.generate_encryption_key()[source]

Generate a new Fernet encryption key.

Returns:

Base64-encoded encryption key

Raises:

ImportError – If cryptography package not installed

Return type:

str

proxywhirl.utils.is_valid_proxy_url(url)[source]

Validate proxy URL format.

Parameters:

url (str) – URL to validate

Returns:

True if valid proxy URL

Return type:

bool

proxywhirl.utils.mask_proxy_url(url)[source]

Mask credentials in a proxy URL for safe display in verbose/debug output.

Replaces username:password with *:* to prevent credential exposure.

Parameters:

url (str) – Proxy URL that may contain credentials

Returns:

URL with credentials masked

Return type:

str

Example

>>> mask_proxy_url("http://user:pass@proxy.com:8080")
"http://***:***@proxy.com:8080"
proxywhirl.utils.mask_secret_str(secret)[source]

Mask a SecretStr or string value for safe display.

Parameters:

secret (Any) – SecretStr instance or string to mask

Returns:

Masked value (always "***").

Return type:

str

proxywhirl.utils.parse_proxy_url(url)[source]

Parse proxy URL into components.

Parameters:

url (str) – Proxy URL to parse

Returns:

Parsed URL components with protocol, host, port, username, password.

Return type:

dict[str, Any]

Raises:

ValueError – If URL is invalid

proxywhirl.utils.proxy_to_dict(proxy, include_stats=True)[source]

Convert a Proxy instance to a dictionary.

Parameters:
  • proxy (proxywhirl.models.Proxy) – Proxy to convert

  • include_stats (bool) – Include statistics

Returns:

Dictionary representation

Return type:

dict[str, Any]

proxywhirl.utils.scrub_credentials_from_dict(data)[source]

Recursively scrub credentials from a dictionary for safe output.

Masks: - Proxy URLs with credentials - SecretStr values - Dict keys containing sensitive terms (password, secret, token, etc.)

Parameters:

data (dict[str, Any]) – Dictionary that may contain sensitive data

Returns:

New dictionary with credentials masked

Return type:

dict[str, Any]

proxywhirl.utils.validate_proxy_model(proxy)[source]

Validate a Proxy model instance.

Parameters:

proxy (proxywhirl.models.Proxy) – Proxy to validate

Returns:

List of validation error messages (empty if valid)

Return type:

list[str]

proxywhirl.utils.validate_target_url_safe(url, allow_private=False)[source]

Validate a target URL to prevent SSRF attacks (API-safe version).

This function validates URLs to prevent Server-Side Request Forgery (SSRF) attacks by blocking access to: - Localhost and loopback addresses (127.0.0.0/8, ::1) - Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) - Link-local addresses (169.254.0.0/16) - Internal domain names (.local, .internal, .lan, .corp)

Parameters:
  • url (str) – The URL to validate

  • allow_private (bool) – If True, allow private/internal IP addresses (default: False)

Raises:

ValueError – If the URL is invalid or potentially dangerous

Return type:

None

Example

>>> validate_target_url_safe("https://example.com")  # OK
>>> validate_target_url_safe("http://localhost:8080")  # Raises ValueError
>>> validate_target_url_safe("http://192.168.1.1")  # Raises ValueError
>>> validate_target_url_safe("http://192.168.1.1", allow_private=True)  # OK