fix: formatting + crypto comparisons

This commit is contained in:
Nicholas Tindle
2025-06-13 11:49:32 -05:00
parent bf26b8f14a
commit 0955cfb869
22 changed files with 189 additions and 63 deletions

1
.gitignore vendored
View File

@@ -176,3 +176,4 @@ autogpt_platform/backend/settings.py
*.ign.*
.test-contents
.claude/settings.local.json

View File

@@ -31,4 +31,5 @@ class APIKeyManager:
"""Verify if a provided API key matches the stored hash."""
if not provided_key.startswith(self.PREFIX):
return False
return hashlib.sha256(provided_key.encode()).hexdigest() == stored_hash
provided_hash = hashlib.sha256(provided_key.encode()).hexdigest()
return secrets.compare_digest(provided_hash, stored_hash)

View File

@@ -1,5 +1,6 @@
import inspect
import logging
import secrets
from typing import Any, Callable, Optional
from fastapi import HTTPException, Request, Security
@@ -93,7 +94,11 @@ class APIKeyValidator:
self.error_message = error_message
async def default_validator(self, api_key: str) -> bool:
return api_key == self.expected_token
if not self.expected_token:
raise ValueError(
"Expected Token Required to be set when uisng API Key Validator default validation"
)
return secrets.compare_digest(api_key, self.expected_token)
async def __call__(
self, request: Request, api_key: str = Security(APIKeyHeader)

View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
@@ -177,7 +177,7 @@ files = [
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
]
markers = {main = "python_version < \"3.11\"", dev = "python_full_version < \"3.11.3\""}
markers = {main = "python_version == \"3.10\"", dev = "python_full_version < \"3.11.3\""}
[[package]]
name = "attrs"
@@ -323,6 +323,21 @@ files = [
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
name = "click"
version = "8.2.1"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
{file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
@@ -375,7 +390,7 @@ description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["main"]
markers = "python_version < \"3.11\""
markers = "python_version == \"3.10\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
@@ -399,6 +414,27 @@ files = [
[package.extras]
tests = ["coverage", "coveralls", "dill", "mock", "nose"]
[[package]]
name = "fastapi"
version = "0.115.12"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"},
{file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"},
]
[package.dependencies]
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
starlette = ">=0.40.0,<0.47.0"
typing-extensions = ">=4.8.0"
[package.extras]
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "frozenlist"
version = "1.4.1"
@@ -895,6 +931,47 @@ files = [
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "launchdarkly-eventsource"
version = "1.2.4"
description = "LaunchDarkly SSE Client"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "launchdarkly_eventsource-1.2.4-py3-none-any.whl", hash = "sha256:048ef8c4440d0d8219778661ee4d4b5e12aa6ed2c29a3004417ede44c2386e8c"},
{file = "launchdarkly_eventsource-1.2.4.tar.gz", hash = "sha256:b8b9342681f55e1d35c56243431cbbaca4eb9812d6785f8de204af322104e066"},
]
[package.dependencies]
urllib3 = ">=1.26.0,<3"
[[package]]
name = "launchdarkly-server-sdk"
version = "9.11.1"
description = "LaunchDarkly SDK for Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "launchdarkly_server_sdk-9.11.1-py3-none-any.whl", hash = "sha256:128569cebf666dd115cc0ba03c48ff75f6acc9788301a7e2c3a54d06107e445a"},
{file = "launchdarkly_server_sdk-9.11.1.tar.gz", hash = "sha256:150e29656cb8c506d1967f3c59e62b69310d345ec27217640a6146dd1db5d250"},
]
[package.dependencies]
certifi = ">=2018.4.16"
expiringdict = ">=1.1.4"
launchdarkly-eventsource = ">=1.2.4,<2.0.0"
pyRFC3339 = ">=1.0"
semver = ">=2.10.2"
urllib3 = ">=1.26.0,<3"
[package.extras]
consul = ["python-consul (>=1.0.1)"]
dynamodb = ["boto3 (>=1.9.71)"]
redis = ["redis (>=2.10.5)"]
test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"]
[[package]]
name = "multidict"
version = "6.1.0"
@@ -1412,6 +1489,18 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "pyrfc3339"
version = "2.0.1"
description = "Generate and parse RFC 3339 timestamps"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "pyRFC3339-2.0.1-py3-none-any.whl", hash = "sha256:30b70a366acac3df7386b558c21af871522560ed7f3f73cf344b8c2cbb8b0c9d"},
{file = "pyrfc3339-2.0.1.tar.gz", hash = "sha256:e47843379ea35c1296c3b6c67a948a1a490ae0584edfcbdea0eaffb5dd29960b"},
]
[[package]]
name = "pytest"
version = "8.3.3"
@@ -1604,6 +1693,18 @@ files = [
{file = "ruff-0.11.10.tar.gz", hash = "sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6"},
]
[[package]]
name = "semver"
version = "3.0.4"
description = "Python helper for Semantic Versioning (https://semver.org)"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"},
{file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"},
]
[[package]]
name = "six"
version = "1.16.0"
@@ -1628,6 +1729,24 @@ files = [
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
]
[[package]]
name = "starlette"
version = "0.46.2"
description = "The little ASGI library that shines."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"},
{file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"},
]
[package.dependencies]
anyio = ">=3.6.2,<5"
[package.extras]
full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"]
[[package]]
name = "storage3"
version = "0.11.0"
@@ -1704,7 +1823,7 @@ description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version < \"3.11\""
markers = "python_version == \"3.10\""
files = [
{file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"},
{file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"},
@@ -1755,6 +1874,26 @@ h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
version = "0.34.3"
description = "The lightning-fast ASGI server."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885"},
{file = "uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a"},
]
[package.dependencies]
click = ">=7.0"
h11 = ">=0.8"
typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
[package.extras]
standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"]
[[package]]
name = "websockets"
version = "12.0"
@@ -2037,4 +2176,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10,<4.0"
content-hash = "78ebf65cdef769cfbe92fe204f01e32d219cca9ee5a6ca9e657aa0630be63802"
content-hash = "d92143928a88ca3a56ac200c335910eafac938940022fed8bd0d17c95040b54f"

View File

@@ -17,6 +17,9 @@ pyjwt = "^2.10.1"
pytest-asyncio = "^0.26.0"
pytest-mock = "^3.14.0"
supabase = "^2.15.1"
launchdarkly-server-sdk = "^9.11.1"
fastapi = "^0.115.12"
uvicorn = "^0.34.3"
[tool.poetry.group.dev.dependencies]
redis = "^5.2.1"

View File

@@ -79,9 +79,7 @@ class ExaContentsBlock(Block):
}
try:
response = Requests().post(
url, headers=headers, json=payload
)
response = Requests().post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
yield "results", data.get("results", [])

View File

@@ -136,9 +136,7 @@ class ExaSearchBlock(Block):
payload[api_field] = value
try:
response = Requests().post(
url, headers=headers, json=payload
)
response = Requests().post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
# Extract just the results array from the response

View File

@@ -120,9 +120,7 @@ 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().post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
yield "results", data.get("results", [])

View File

@@ -67,9 +67,7 @@ class HubSpotCompanyBlock(Block):
}
]
}
response = Requests().post(
search_url, headers=headers, json=search_data
)
response = Requests().post(search_url, headers=headers, json=search_data)
result = response.json()
yield "company", result.get("results", [{}])[0]
yield "status", "retrieved"

View File

@@ -68,9 +68,7 @@ class HubSpotContactBlock(Block):
}
]
}
response = Requests().post(
search_url, headers=headers, json=search_data
)
response = Requests().post(search_url, headers=headers, json=search_data)
result = response.json()
yield "contact", result.get("results", [{}])[0]
yield "status", "retrieved"

View File

@@ -66,9 +66,7 @@ class HubSpotEngagementBlock(Block):
}
}
response = Requests().post(
email_url, headers=headers, json=email_data
)
response = Requests().post(email_url, headers=headers, json=email_data)
result = response.json()
yield "result", result
yield "status", "email_sent"
@@ -82,9 +80,7 @@ class HubSpotEngagementBlock(Block):
params = {"limit": 100, "after": from_date.isoformat()}
response = Requests().get(
engagement_url, headers=headers, params=params
)
response = Requests().get(engagement_url, headers=headers, params=params)
engagements = response.json()
# Process engagement metrics

View File

@@ -267,9 +267,7 @@ class IdeogramModelBlock(Block):
}
try:
response = Requests().post(
url, json=data, headers=headers
)
response = Requests().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)}")
@@ -282,9 +280,7 @@ class IdeogramModelBlock(Block):
try:
# Step 1: Download the image from the provided URL
image_response = Requests().get(
image_url
)
image_response = Requests().get(image_url)
# Step 2: Send the downloaded image to the upscale API
files = {

View File

@@ -55,9 +55,7 @@ class JinaChunkingBlock(Block):
"max_chunk_length": str(input_data.max_chunk_length),
}
response = Requests().post(
url, headers=headers, json=data
)
response = Requests().post(url, headers=headers, json=data)
result = response.json()
all_chunks.extend(result.get("chunks", []))

View File

@@ -38,8 +38,6 @@ 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().post(url, headers=headers, json=data)
embeddings = [e["embedding"] for e in response.json()["data"]]
yield "embeddings", embeddings

View File

@@ -59,9 +59,7 @@ class NvidiaDeepfakeDetectBlock(Block):
}
try:
response = Requests().post(
url, headers=headers, json=payload
)
response = Requests().post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()

View File

@@ -398,7 +398,7 @@ class IntegrationCredentialsStore:
(
state
for state in oauth_states
if state.token == token
if secrets.compare_digest(state.token, token)
and state.provider == provider
and state.expires_at > now.timestamp()
),

View File

@@ -52,9 +52,7 @@ class NotionOAuthHandler(BaseOAuthHandler):
"Authorization": f"Basic {auth_str}",
"Accept": "application/json",
}
response = Requests().post(
self.token_url, json=request_body, headers=headers
)
response = Requests().post(self.token_url, json=request_body, headers=headers)
token_data = response.json()
# Email is only available for non-bot users
email = (

View File

@@ -77,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().post(
self.TOKEN_URL, headers=headers, data=data, auth=auth
)
response.raise_for_status()
tokens = response.json()
@@ -101,7 +103,9 @@ class TwitterOAuthHandler(BaseOAuthHandler):
params = {"user.fields": "username"}
response = Requests().get(f"{self.USERNAME_URL}?{urllib.parse.urlencode(params)}", headers=headers)
response = Requests().get(
f"{self.USERNAME_URL}?{urllib.parse.urlencode(params)}", headers=headers
)
response.raise_for_status()
return response.json()["data"]["username"]
@@ -156,7 +160,9 @@ class TwitterOAuthHandler(BaseOAuthHandler):
auth = (self.client_id, self.client_secret)
response = Requests().post(self.REVOKE_URL, headers=header, data=data, auth=auth)
response = Requests().post(
self.REVOKE_URL, headers=header, data=data, auth=auth
)
try:
response.raise_for_status()

View File

@@ -73,9 +73,7 @@ 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().post(ping_url, headers=headers)
if response.status_code != 204:
error_msg = extract_github_error_msg(response)
@@ -155,9 +153,7 @@ class GithubWebhooksManager(BaseWebhooksManager):
f"Unsupported webhook type '{webhook.webhook_type}'"
)
response = Requests().delete(
delete_url, headers=headers
)
response = Requests().delete(delete_url, headers=headers)
if response.status_code not in [204, 404]:
# 204 means successful deletion, 404 means the webhook was already deleted

View File

@@ -29,7 +29,7 @@ export async function GET(request: Request) {
// if "next" is in param, use it as the redirect URL
const nextParam = searchParams.get("next") ?? "/";
// Validate redirect URL to prevent open redirect attacks
const next = validateRedirectUrl(nextParam);
let next = validateRedirectUrl(nextParam);
if (code) {
const supabase = await getServerSupabase();

View File

@@ -27,6 +27,7 @@ import {
cn,
getValue,
hasNonNullNonObjectValue,
isObject,
parseKeys,
setNestedProperty,
} from "@/lib/utils";
@@ -436,12 +437,7 @@ export const CustomNode = React.memo(
try {
const parsedValue = JSON.parse(value);
// Validate that the parsed value is safe before using it
if (
(typeof parsedValue === "object" &&
parsedValue !== null &&
!Array.isArray(parsedValue)) ||
Array.isArray(parsedValue)
) {
if (isObject(parsedValue) || Array.isArray(parsedValue)) {
handleInputChange(activeKey, parsedValue);
} else {
// For primitive values, use the original string

View File

@@ -396,3 +396,8 @@ export function getValue(key: string, value: any) {
export function isEmptyOrWhitespace(str: string | undefined | null): boolean {
return !str || str.trim().length === 0;
}
/** Chech if a value is an object or not */
export function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}