Fix 403 on test-woo: add User-Agent header, classify 403 as WooForbiddenError
WordPress security plugins (Wordfence, WAF) block the default Python-urllib User-Agent with 403. Send a descriptive User-Agent on all Woo requests. Also add WooForbiddenError so 403 is classified distinctly from 401 (auth) per SETTINGS-80, and surface a clear message in test-woo.
This commit is contained in:
+16
-1
@@ -13,6 +13,10 @@ from typing import Any, Callable
|
||||
|
||||
from .crypto import DecryptError, decrypt
|
||||
|
||||
# WordPress security plugins (Wordfence, WAF, CDN) commonly block the default
|
||||
# Python-urllib User-Agent with 403. Send a descriptive one.
|
||||
_USER_AGENT = "SlabUploader/1.0 (+https://www.whidbeywoodstore.com)"
|
||||
|
||||
|
||||
class WooError(Exception):
|
||||
"""Base for all Woo client failures."""
|
||||
@@ -26,6 +30,10 @@ class WooAuthError(WooError):
|
||||
"""The store rejected the credentials (401)."""
|
||||
|
||||
|
||||
class WooForbiddenError(WooError):
|
||||
"""The store blocked the request (403) — usually a security plugin or WAF."""
|
||||
|
||||
|
||||
class WooNotFoundError(WooError):
|
||||
pass
|
||||
|
||||
@@ -54,6 +62,8 @@ def _default_request(url: str, method: str, data: bytes | None, headers: dict[st
|
||||
msg = parsed.get("message") if isinstance(parsed, dict) else None
|
||||
if e.code == 401:
|
||||
raise WooAuthError(msg or "authentication rejected (401)") from e
|
||||
if e.code == 403:
|
||||
raise WooForbiddenError(msg or "request forbidden (403)") from e
|
||||
if e.code == 404:
|
||||
raise WooNotFoundError(msg or "not found (404)") from e
|
||||
raise WooServerError(f"server error {e.code}: {msg}") from e
|
||||
@@ -90,7 +100,11 @@ class WooClient:
|
||||
url = f"{self.base_url}/wp-json/wc/v3{path}"
|
||||
return self._request(
|
||||
url, "GET", None,
|
||||
{"Authorization": f"Basic {self._auth}", "Accept": "application/json"},
|
||||
{
|
||||
"Authorization": f"Basic {self._auth}",
|
||||
"Accept": "application/json",
|
||||
"User-Agent": _USER_AGENT,
|
||||
},
|
||||
)
|
||||
|
||||
def _fetch_all(self, path: str) -> list[dict[str, Any]]:
|
||||
@@ -132,6 +146,7 @@ class WooClient:
|
||||
{
|
||||
"Authorization": f"Basic {self._auth}",
|
||||
"Content-Type": f"multipart/form-data; boundary={boundary}",
|
||||
"User-Agent": _USER_AGENT,
|
||||
},
|
||||
)
|
||||
if code not in (200, 201):
|
||||
|
||||
Reference in New Issue
Block a user