2026-07-28 16:33:34 +02:00
|
|
|
"""SSRF-guarded, multi-method HTTP client exposed to plugins (SPEC §10.6).
|
|
|
|
|
|
|
|
|
|
``sdk.http_get`` covers simple GETs; heavy plugins that speak CalDAV/CardDAV need
|
|
|
|
|
other verbs (PROPFIND/REPORT/PUT/DELETE) with request bodies and basic auth. This
|
|
|
|
|
facade routes all of them through the same SSRF policy (HTTPS-only, public host,
|
|
|
|
|
no off-domain redirects, size cap) and returns status + headers + body.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from app.accounts.ssrf import GuardedResponse, guarded_request
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PluginHttp:
|
|
|
|
|
async def request(
|
|
|
|
|
self,
|
|
|
|
|
method: str,
|
|
|
|
|
url: str,
|
|
|
|
|
*,
|
|
|
|
|
headers: dict[str, str] | None = None,
|
|
|
|
|
content: bytes | str | None = None,
|
|
|
|
|
auth: tuple[str, str] | None = None,
|
|
|
|
|
) -> GuardedResponse:
|
2026-10-14 22:08:06 +02:00
|
|
|
return await guarded_request(method, url, headers=headers, content=content, auth=auth)
|
2026-07-28 16:33:34 +02:00
|
|
|
|
|
|
|
|
async def get(self, url: str, **kw: object) -> GuardedResponse:
|
|
|
|
|
return await self.request("GET", url, **kw) # type: ignore[arg-type]
|
|
|
|
|
|
|
|
|
|
async def put(self, url: str, **kw: object) -> GuardedResponse:
|
|
|
|
|
return await self.request("PUT", url, **kw) # type: ignore[arg-type]
|
|
|
|
|
|
|
|
|
|
async def delete(self, url: str, **kw: object) -> GuardedResponse:
|
|
|
|
|
return await self.request("DELETE", url, **kw) # type: ignore[arg-type]
|
|
|
|
|
|
|
|
|
|
async def propfind(self, url: str, **kw: object) -> GuardedResponse:
|
|
|
|
|
return await self.request("PROPFIND", url, **kw) # type: ignore[arg-type]
|
|
|
|
|
|
|
|
|
|
async def report(self, url: str, **kw: object) -> GuardedResponse:
|
|
|
|
|
return await self.request("REPORT", url, **kw) # type: ignore[arg-type]
|