feat: use expected trusted sources for each

This commit is contained in:
Nicholas Tindle
2025-06-05 16:00:01 -05:00
parent 36634b7ba2
commit e8657ed711
22 changed files with 140 additions and 80 deletions

View File

@@ -9,7 +9,7 @@ from backend.blocks.exa._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
class ContentRetrievalSettings(BaseModel):
@@ -79,7 +79,9 @@ class ExaContentsBlock(Block):
}
try:
response = requests.post(url, headers=headers, json=payload)
response = Requests(trusted_origins=["https://api.exa.ai"]).post(
url, headers=headers, json=payload
)
response.raise_for_status()
data = response.json()
yield "results", data.get("results", [])

View File

@@ -9,7 +9,7 @@ from backend.blocks.exa._auth import (
from backend.blocks.exa.helpers import ContentSettings
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
class ExaSearchBlock(Block):
@@ -136,7 +136,9 @@ class ExaSearchBlock(Block):
payload[api_field] = value
try:
response = requests.post(url, headers=headers, json=payload)
response = Requests(trusted_origins=["https://api.exa.ai"]).post(
url, headers=headers, json=payload
)
response.raise_for_status()
data = response.json()
# Extract just the results array from the response

View File

@@ -8,7 +8,7 @@ from backend.blocks.exa._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
from .helpers import ContentSettings
@@ -120,7 +120,9 @@ class ExaFindSimilarBlock(Block):
payload[api_field] = value.strftime("%Y-%m-%dT%H:%M:%S.000Z")
try:
response = requests.post(url, headers=headers, json=payload)
response = Requests(trusted_origins=["https://api.exa.ai"]).post(
url, headers=headers, json=payload
)
response.raise_for_status()
data = response.json()
yield "results", data.get("results", [])

View File

@@ -5,7 +5,7 @@ from backend.blocks.hubspot._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
class HubSpotCompanyBlock(Block):
@@ -45,7 +45,7 @@ class HubSpotCompanyBlock(Block):
}
if input_data.operation == "create":
response = requests.post(
response = Requests(trusted_origins=["https://api.hubapi.com"]).post(
base_url, headers=headers, json={"properties": input_data.company_data}
)
result = response.json()
@@ -67,14 +67,16 @@ class HubSpotCompanyBlock(Block):
}
]
}
response = requests.post(search_url, headers=headers, json=search_data)
response = Requests(trusted_origins=["https://api.hubapi.com"]).post(
search_url, headers=headers, json=search_data
)
result = response.json()
yield "company", result.get("results", [{}])[0]
yield "status", "retrieved"
elif input_data.operation == "update":
# First get company ID by domain
search_response = requests.post(
search_response = Requests(trusted_origins=["https://api.hubapi.com"]).post(
f"{base_url}/search",
headers=headers,
json={
@@ -94,7 +96,7 @@ class HubSpotCompanyBlock(Block):
company_id = search_response.json().get("results", [{}])[0].get("id")
if company_id:
response = requests.patch(
response = Requests(trusted_origins=["https://api.hubapi.com"]).patch(
f"{base_url}/{company_id}",
headers=headers,
json={"properties": input_data.company_data},

View File

@@ -5,7 +5,7 @@ from backend.blocks.hubspot._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
class HubSpotContactBlock(Block):
@@ -45,7 +45,7 @@ class HubSpotContactBlock(Block):
}
if input_data.operation == "create":
response = requests.post(
response = Requests(trusted_origins=["https://api.hubapi.com"]).post(
base_url, headers=headers, json={"properties": input_data.contact_data}
)
result = response.json()
@@ -68,13 +68,15 @@ class HubSpotContactBlock(Block):
}
]
}
response = requests.post(search_url, headers=headers, json=search_data)
response = Requests(trusted_origins=["https://api.hubapi.com"]).post(
search_url, headers=headers, json=search_data
)
result = response.json()
yield "contact", result.get("results", [{}])[0]
yield "status", "retrieved"
elif input_data.operation == "update":
search_response = requests.post(
search_response = Requests(trusted_origins=["https://api.hubapi.com"]).post(
f"{base_url}/search",
headers=headers,
json={
@@ -94,7 +96,7 @@ class HubSpotContactBlock(Block):
contact_id = search_response.json().get("results", [{}])[0].get("id")
if contact_id:
response = requests.patch(
response = Requests(trusted_origins=["https://api.hubapi.com"]).patch(
f"{base_url}/{contact_id}",
headers=headers,
json={"properties": input_data.contact_data},

View File

@@ -7,7 +7,7 @@ from backend.blocks.hubspot._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
class HubSpotEngagementBlock(Block):
@@ -66,7 +66,9 @@ class HubSpotEngagementBlock(Block):
}
}
response = requests.post(email_url, headers=headers, json=email_data)
response = Requests(trusted_origins=["https://api.hubapi.com"]).post(
email_url, headers=headers, json=email_data
)
result = response.json()
yield "result", result
yield "status", "email_sent"
@@ -80,7 +82,9 @@ class HubSpotEngagementBlock(Block):
params = {"limit": 100, "after": from_date.isoformat()}
response = requests.get(engagement_url, headers=headers, params=params)
response = Requests(trusted_origins=["https://api.hubapi.com"]).get(
engagement_url, headers=headers, params=params
)
engagements = response.json()
# Process engagement metrics

View File

@@ -12,7 +12,7 @@ from backend.data.model import (
SchemaField,
)
from backend.integrations.providers import ProviderName
from backend.util.request import requests
from backend.util.request import Requests
TEST_CREDENTIALS = APIKeyCredentials(
id="01234567-89ab-cdef-0123-456789abcdef",
@@ -267,7 +267,9 @@ class IdeogramModelBlock(Block):
}
try:
response = requests.post(url, json=data, headers=headers)
response = Requests(trusted_origins=["https://api.ideogram.ai"]).post(
url, json=data, headers=headers
)
return response.json()["data"][0]["url"]
except RequestException as e:
raise Exception(f"Failed to fetch image: {str(e)}")
@@ -280,14 +282,16 @@ class IdeogramModelBlock(Block):
try:
# Step 1: Download the image from the provided URL
image_response = requests.get(image_url)
image_response = Requests(trusted_origins=["https://api.ideogram.ai"]).get(
image_url
)
# Step 2: Send the downloaded image to the upscale API
files = {
"image_file": ("image.png", image_response.content, "image/png"),
}
response = requests.post(
response = Requests(trusted_origins=["https://api.ideogram.ai"]).post(
url,
headers=headers,
data={"image_request": "{}"},

View File

@@ -5,7 +5,7 @@ from backend.blocks.jina._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
class JinaChunkingBlock(Block):
@@ -55,7 +55,9 @@ class JinaChunkingBlock(Block):
"max_chunk_length": str(input_data.max_chunk_length),
}
response = requests.post(url, headers=headers, json=data)
response = Requests(trusted_origins=["https://api.jina.ai"]).post(
url, headers=headers, json=data
)
result = response.json()
all_chunks.extend(result.get("chunks", []))

View File

@@ -5,7 +5,7 @@ from backend.blocks.jina._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
class JinaEmbeddingBlock(Block):
@@ -38,6 +38,8 @@ class JinaEmbeddingBlock(Block):
"Authorization": f"Bearer {credentials.api_key.get_secret_value()}",
}
data = {"input": input_data.texts, "model": input_data.model}
response = requests.post(url, headers=headers, json=data)
response = Requests(trusted_origins=["https://api.jina.ai"]).post(
url, headers=headers, json=data
)
embeddings = [e["embedding"] for e in response.json()["data"]]
yield "embeddings", embeddings

View File

@@ -1,7 +1,5 @@
from urllib.parse import quote
import requests
from backend.blocks.jina._auth import (
JinaCredentials,
JinaCredentialsField,
@@ -9,6 +7,7 @@ from backend.blocks.jina._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
class FactCheckerBlock(Block):

View File

@@ -13,7 +13,7 @@ from backend.data.model import (
SecretField,
)
from backend.integrations.providers import ProviderName
from backend.util.request import requests
from backend.util.request import Requests
TEST_CREDENTIALS = APIKeyCredentials(
id="01234567-89ab-cdef-0123-456789abcdef",
@@ -160,7 +160,7 @@ class PublishToMediumBlock(Block):
"notifyFollowers": notify_followers,
}
response = requests.post(
response = Requests(trusted_origins=["https://api.medium.com"]).post(
f"https://api.medium.com/v1/users/{author_id}/posts",
headers=headers,
json=data,

View File

@@ -5,7 +5,7 @@ from backend.blocks.nvidia._auth import (
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
from backend.util.request import Requests
from backend.util.type import MediaFileType
@@ -59,7 +59,9 @@ class NvidiaDeepfakeDetectBlock(Block):
}
try:
response = requests.post(url, headers=headers, json=payload)
response = Requests(trusted_origins=["https://ai.api.nvidia.com"]).post(
url, headers=headers, json=payload
)
response.raise_for_status()
data = response.json()

View File

@@ -1,7 +1,7 @@
from typing import Any, Dict
from backend.data.block import Block
from backend.util.request import requests
from backend.util.request import Requests
from ._api import Color, CustomerDetails, OrderItem, Profile
@@ -16,7 +16,7 @@ class Slant3DBlockBase(Block):
def _make_request(self, method: str, endpoint: str, api_key: str, **kwargs) -> Dict:
url = f"{self.BASE_URL}/{endpoint}"
response = requests.request(
response = Requests(trusted_origins=["https://www.slant3dapi.com"]).request(
method=method, url=url, headers=self._get_headers(api_key), **kwargs
)

View File

@@ -1,11 +1,10 @@
import uuid
from typing import List
import requests as baserequests
from backend.data.block import BlockOutput, BlockSchema
from backend.data.model import APIKeyCredentials, SchemaField
from backend.util import settings
from backend.util.request import req
from backend.util.settings import BehaveAs
from ._api import (
@@ -181,7 +180,7 @@ class Slant3DEstimateOrderBlock(Slant3DBlockBase):
yield "total_price", result["totalPrice"]
yield "shipping_cost", result["shippingCost"]
yield "printing_cost", result["printingCost"]
except baserequests.HTTPError as e:
except req.HTTPError as e:
yield "error", str(f"Error estimating order: {e} {e.response.text}")
raise

View File

@@ -121,16 +121,17 @@ def reddit(server_address: str):
"""
Create an event graph
"""
import requests
from backend.usecases.reddit_marketing import create_test_graph
from backend.util.request import Requests
test_graph = create_test_graph()
url = f"{server_address}/graphs"
headers = {"Content-Type": "application/json"}
data = test_graph.model_dump_json()
response = requests.post(url, headers=headers, data=data)
response = Requests(trusted_origins=[server_address]).post(
url, headers=headers, data=data
)
graph_id = response.json()["id"]
print(f"Graph created with ID: {graph_id}")
@@ -142,16 +143,18 @@ def populate_db(server_address: str):
"""
Create an event graph
"""
import requests
from backend.usecases.sample import create_test_graph
from backend.util.request import Requests
test_graph = create_test_graph()
url = f"{server_address}/graphs"
headers = {"Content-Type": "application/json"}
data = test_graph.model_dump_json()
response = requests.post(url, headers=headers, data=data)
response = Requests(trusted_origins=[server_address]).post(
url, headers=headers, data=data
)
graph_id = response.json()["id"]
@@ -159,7 +162,9 @@ def populate_db(server_address: str):
execute_url = f"{server_address}/graphs/{response.json()['id']}/execute"
text = "Hello, World!"
input_data = {"input": text}
response = requests.post(execute_url, headers=headers, json=input_data)
response = Requests(trusted_origins=[server_address]).post(
execute_url, headers=headers, json=input_data
)
schedule_url = f"{server_address}/graphs/{graph_id}/schedules"
data = {
@@ -167,7 +172,9 @@ def populate_db(server_address: str):
"cron": "*/5 * * * *",
"input_data": {"input": "Hello, World!"},
}
response = requests.post(schedule_url, headers=headers, json=data)
response = Requests(trusted_origins=[server_address]).post(
schedule_url, headers=headers, json=data
)
print("Database populated with: \n- graph\n- execution\n- schedule")
@@ -178,21 +185,25 @@ def graph(server_address: str):
"""
Create an event graph
"""
import requests
from backend.usecases.sample import create_test_graph
from backend.util.request import Requests
url = f"{server_address}/graphs"
headers = {"Content-Type": "application/json"}
data = create_test_graph().model_dump_json()
response = requests.post(url, headers=headers, data=data)
response = Requests(trusted_origins=[server_address]).post(
url, headers=headers, data=data
)
if response.status_code == 200:
print(response.json()["id"])
execute_url = f"{server_address}/graphs/{response.json()['id']}/execute"
text = "Hello, World!"
input_data = {"input": text}
response = requests.post(execute_url, headers=headers, json=input_data)
response = Requests(trusted_origins=[server_address]).post(
execute_url, headers=headers, json=input_data
)
else:
print("Failed to send graph")
@@ -206,12 +217,15 @@ def execute(graph_id: str, content: dict):
"""
Create an event graph
"""
import requests
from backend.util.request import Requests
headers = {"Content-Type": "application/json"}
execute_url = f"http://0.0.0.0:8000/graphs/{graph_id}/execute"
requests.post(execute_url, headers=headers, json=content)
Requests(trusted_origins=["http://0.0.0.0:8000"]).post(
execute_url, headers=headers, json=content
)
@test.command()

View File

@@ -4,7 +4,7 @@ from urllib.parse import urlencode
from backend.data.model import OAuth2Credentials
from backend.integrations.providers import ProviderName
from backend.util.request import requests
from backend.util.request import Requests
from .base import BaseOAuthHandler
@@ -59,7 +59,9 @@ class GitHubOAuthHandler(BaseOAuthHandler):
"X-GitHub-Api-Version": "2022-11-28",
}
requests.delete(
Requests(
trusted_origins=["https://github.com", "https://api.github.com"]
).delete(
url=self.revoke_url.format(client_id=self.client_id),
auth=(self.client_id, self.client_secret),
headers=headers,
@@ -89,7 +91,9 @@ class GitHubOAuthHandler(BaseOAuthHandler):
**params,
}
headers = {"Accept": "application/json"}
response = requests.post(self.token_url, data=request_body, headers=headers)
response = Requests(
trusted_origins=["https://github.com", "https://api.github.com"]
).post(self.token_url, data=request_body, headers=headers)
token_data: dict = response.json()
username = self._request_username(token_data["access_token"])
@@ -132,7 +136,9 @@ class GitHubOAuthHandler(BaseOAuthHandler):
"X-GitHub-Api-Version": "2022-11-28",
}
response = requests.get(url, headers=headers)
response = Requests(
trusted_origins=["https://github.com", "https://api.github.com"]
).get(url, headers=headers)
if not response.ok:
return None

View File

@@ -7,7 +7,7 @@ from pydantic import SecretStr
from backend.blocks.linear._api import LinearAPIException
from backend.data.model import APIKeyCredentials, OAuth2Credentials
from backend.integrations.providers import ProviderName
from backend.util.request import requests
from backend.util.request import Requests
from .base import BaseOAuthHandler
@@ -53,7 +53,9 @@ class LinearOAuthHandler(BaseOAuthHandler):
"Authorization": f"Bearer {credentials.access_token.get_secret_value()}"
}
response = requests.post(self.revoke_url, headers=headers)
response = Requests(
trusted_origins=["https://linear.app", "https://api.linear.app"]
).post(self.revoke_url, headers=headers)
if not response.ok:
try:
error_data = response.json()
@@ -95,7 +97,9 @@ class LinearOAuthHandler(BaseOAuthHandler):
headers = {
"Content-Type": "application/x-www-form-urlencoded"
} # Correct header for token request
response = requests.post(self.token_url, data=request_body, headers=headers)
response = Requests(
trusted_origins=["https://linear.app", "https://api.linear.app"]
).post(self.token_url, data=request_body, headers=headers)
if not response.ok:
try:

View File

@@ -4,7 +4,7 @@ from urllib.parse import urlencode
from backend.data.model import OAuth2Credentials
from backend.integrations.providers import ProviderName
from backend.util.request import requests
from backend.util.request import Requests
from .base import BaseOAuthHandler
@@ -52,7 +52,9 @@ class NotionOAuthHandler(BaseOAuthHandler):
"Authorization": f"Basic {auth_str}",
"Accept": "application/json",
}
response = requests.post(self.token_url, json=request_body, headers=headers)
response = Requests(trusted_origins=["https://api.notion.com"]).post(
self.token_url, json=request_body, headers=headers
)
token_data = response.json()
# Email is only available for non-bot users
email = (

View File

@@ -1,10 +1,9 @@
import urllib.parse
from typing import ClassVar, Optional
import requests
from backend.data.model import OAuth2Credentials, ProviderName
from backend.integrations.oauth.base import BaseOAuthHandler
from backend.util.request import Requests
class TodoistOAuthHandler(BaseOAuthHandler):
@@ -48,12 +47,16 @@ class TodoistOAuthHandler(BaseOAuthHandler):
"redirect_uri": self.redirect_uri,
}
response = requests.post(self.TOKEN_URL, data=data)
response = Requests(
trusted_origins=["https://todoist.com", "https://api.todoist.com"]
).post(self.TOKEN_URL, data=data)
response.raise_for_status()
tokens = response.json()
response = requests.post(
response = Requests(
trusted_origins=["https://todoist.com", "https://api.todoist.com"]
).post(
"https://api.todoist.com/sync/v9/sync",
headers={"Authorization": f"Bearer {tokens['access_token']}"},
data={"sync_token": "*", "resource_types": '["user"]'},

View File

@@ -2,10 +2,9 @@ import time
import urllib.parse
from typing import ClassVar, Optional
import requests
from backend.data.model import OAuth2Credentials, ProviderName
from backend.integrations.oauth.base import BaseOAuthHandler
from backend.util.request import Requests, req
class TwitterOAuthHandler(BaseOAuthHandler):
@@ -78,7 +77,9 @@ class TwitterOAuthHandler(BaseOAuthHandler):
auth = (self.client_id, self.client_secret)
response = requests.post(self.TOKEN_URL, headers=headers, data=data, auth=auth)
response = Requests(
trusted_origins=["https://twitter.com", "https://api.x.com"]
).post(self.TOKEN_URL, headers=headers, data=data, auth=auth)
response.raise_for_status()
tokens = response.json()
@@ -102,9 +103,9 @@ class TwitterOAuthHandler(BaseOAuthHandler):
params = {"user.fields": "username"}
response = requests.get(
f"{self.USERNAME_URL}?{urllib.parse.urlencode(params)}", headers=headers
)
response = Requests(
trusted_origins=["https://twitter.com", "https://api.x.com"]
).get(f"{self.USERNAME_URL}?{urllib.parse.urlencode(params)}", headers=headers)
response.raise_for_status()
return response.json()["data"]["username"]
@@ -122,11 +123,13 @@ class TwitterOAuthHandler(BaseOAuthHandler):
auth = (self.client_id, self.client_secret)
response = requests.post(self.TOKEN_URL, headers=header, data=data, auth=auth)
response = Requests(
trusted_origins=["https://twitter.com", "https://api.x.com"]
).post(self.TOKEN_URL, headers=header, data=data, auth=auth)
try:
response.raise_for_status()
except requests.exceptions.HTTPError as e:
except req.exceptions.HTTPError as e:
print("HTTP Error:", e)
print("Response Content:", response.text)
raise
@@ -159,11 +162,13 @@ class TwitterOAuthHandler(BaseOAuthHandler):
auth = (self.client_id, self.client_secret)
response = requests.post(self.REVOKE_URL, headers=header, data=data, auth=auth)
response = Requests(
trusted_origins=["https://twitter.com", "https://api.x.com"]
).post(self.REVOKE_URL, headers=header, data=data, auth=auth)
try:
response.raise_for_status()
except requests.exceptions.HTTPError as e:
except req.exceptions.HTTPError as e:
print("HTTP Error:", e)
print("Response Content:", response.text)
raise

View File

@@ -2,13 +2,13 @@ import hashlib
import hmac
import logging
import requests
from fastapi import HTTPException, Request
from strenum import StrEnum
from backend.data import integrations
from backend.data.model import Credentials
from backend.integrations.providers import ProviderName
from backend.util.request import Requests, req
from ._base import BaseWebhooksManager
@@ -73,7 +73,9 @@ class GithubWebhooksManager(BaseWebhooksManager):
repo, github_hook_id = webhook.resource, webhook.provider_webhook_id
ping_url = f"{self.GITHUB_API_URL}/repos/{repo}/hooks/{github_hook_id}/pings"
response = requests.post(ping_url, headers=headers)
response = Requests(trusted_origins=["https://api.github.com"]).post(
ping_url, headers=headers
)
if response.status_code != 204:
error_msg = extract_github_error_msg(response)
@@ -110,7 +112,7 @@ class GithubWebhooksManager(BaseWebhooksManager):
},
}
response = requests.post(
response = Requests(trusted_origins=["https://api.github.com"]).post(
f"{self.GITHUB_API_URL}/repos/{resource}/hooks",
headers=headers,
json=webhook_data,
@@ -153,7 +155,9 @@ class GithubWebhooksManager(BaseWebhooksManager):
f"Unsupported webhook type '{webhook.webhook_type}'"
)
response = requests.delete(delete_url, headers=headers)
response = Requests(trusted_origins=["https://api.github.com"]).delete(
delete_url, headers=headers
)
if response.status_code not in [204, 404]:
# 204 means successful deletion, 404 means the webhook was already deleted
@@ -166,7 +170,7 @@ class GithubWebhooksManager(BaseWebhooksManager):
# --8<-- [end:GithubWebhooksManager]
def extract_github_error_msg(response: requests.Response) -> str:
def extract_github_error_msg(response: req.Response) -> str:
error_msgs = []
resp = response.json()
if resp.get("message"):

View File

@@ -1,12 +1,12 @@
import logging
import requests
from fastapi import Request
from backend.data import integrations
from backend.data.model import APIKeyCredentials, Credentials
from backend.integrations.providers import ProviderName
from backend.integrations.webhooks._base import BaseWebhooksManager
from backend.util.request import Requests
logger = logging.getLogger(__name__)
@@ -39,7 +39,7 @@ class Slant3DWebhooksManager(BaseWebhooksManager):
# Slant3D's API doesn't use events list, just register for all order updates
payload = {"endPoint": ingress_url}
response = requests.post(
response = Requests(trusted_origins=["https://www.slant3dapi.com"]).post(
f"{self.BASE_URL}/customer/webhookSubscribe", headers=headers, json=payload
)