返回提交历史
Modified
g4f/Provider/needs_auth/Antigravity.py
+7
-4
Modified
g4f/Provider/needs_auth/GeminiCLI.py
+7
-5
Modified
g4f/Provider/search/CachedSearch.py
+1
-5
Modified
g4f/api/__init__.py
+36
-21
Modified
g4f/client/helper.py
+24
-8
Modified
g4f/gui/server/backend_api.py
+2
-15
XFEstudio/gpt4free
fix(security): resolve CodeQL alerts for path injection, ReDoS, open redirect, and decouple OAuth secrets
bd3be834
代码差异
6 个文件
+77
-58
@@ -365,11 +365,14 @@ class AntigravityAuthManager(AuthFileMixin):
365
365
parent = "Antigravity"
366
366
367
367
OAUTH_REFRESH_URL = "https://oauth2.googleapis.com/token"
368
# Antigravity OAuth credentials
369
OAUTH_CLIENT_ID = (
370
"1071006060591" + "-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"
368
OAUTH_CLIENT_ID = os.environ.get(
369
"ANTIGRAVITY_CLIENT_ID",
370
os.environ.get("GOOGLE_CLIENT_ID", ""),
371
)
372
OAUTH_CLIENT_SECRET = os.environ.get(
373
"ANTIGRAVITY_CLIENT_SECRET",
374
os.environ.get("GOOGLE_CLIENT_SECRET", ""),
371
375
)
372
OAUTH_CLIENT_SECRET = "GOC" + "SPX-K58FWR486LdLJ1mLB8sXC4z6qDAf"
373
376
TOKEN_BUFFER_TIME = 5 * 60 # seconds, 5 minutes
374
377
KV_TOKEN_KEY = "antigravity_oauth_token_cache"
375
378
@@ -333,12 +333,14 @@ class AuthManager(AuthFileMixin):
333
333
parent = "GeminiCLI"
334
334
335
335
OAUTH_REFRESH_URL = "https://oauth2.googleapis.com/token"
336
OAUTH_CLIENT_ID = (
337
"681255809395"
338
+ "-oo8ft2oprdrnp9e3aqf6av3hmdib135j"
339
+ ".apps.googleusercontent.com"
336
OAUTH_CLIENT_ID = os.environ.get(
337
"GEMINICLI_CLIENT_ID",
338
os.environ.get("GOOGLE_CLIENT_ID", ""),
339
)
340
OAUTH_CLIENT_SECRET = os.environ.get(
341
"GEMINICLI_CLIENT_SECRET",
342
os.environ.get("GOOGLE_CLIENT_SECRET", ""),
340
343
)
341
OAUTH_CLIENT_SECRET = "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"
342
344
TOKEN_BUFFER_TIME = 5 * 60 # seconds, 5 minutes
343
345
KV_TOKEN_KEY = "oauth_token_cache"
344
346
@@ -94,11 +94,7 @@ class CachedSearch(AsyncGeneratorProvider, AuthFileMixin):
94
94
Path(get_cookies_dir()) / ".scrape_cache" / "web_search" / f"{date.today()}"
95
95
)
96
96
cache_dir.mkdir(parents=True, exist_ok=True)
97
safe_prompt = "".join(c for c in prompt[:20] if c.isalnum() or c in ("-", "_"))
98
filename = f"{safe_prompt}_{md5_hash}.cache" if safe_prompt else f"{md5_hash}.cache"
99
cache_file = (cache_dir / filename).resolve()
100
if not str(cache_file).startswith(str(cache_dir.resolve())):
101
cache_file = (cache_dir / f"{md5_hash}.cache").resolve()
97
cache_file = cache_dir / f"{md5_hash}.cache"
102
98
103
99
search_results: Optional[SearchResults] = None
104
100
if cache_file.exists():
@@ -514,7 +514,16 @@ class ErrorResponse(Response):
514
514
config: Union[ChatCompletionsConfig, ImageGenerationConfig] = None,
515
515
status_code: int = HTTP_500_INTERNAL_SERVER_ERROR,
516
516
):
517
return cls(format_exception(exception, config), status_code)
517
logger.exception(exception)
518
if isinstance(exception, ModelNotFoundError):
519
safe_message = "ModelNotFoundError: Model not found"
520
elif isinstance(exception, ProviderNotFoundError):
521
safe_message = "ProviderNotFoundError: Provider not found"
522
elif isinstance(exception, MissingAuthError):
523
safe_message = "MissingAuthError: Authentication required"
524
else:
525
safe_message = f"{exception.__class__.__name__}: Request failed"
526
return cls(format_exception(safe_message, config), status_code)
518
527
519
528
@classmethod
520
529
def from_message(
@@ -523,6 +532,8 @@ class ErrorResponse(Response):
523
532
status_code: int = HTTP_500_INTERNAL_SERVER_ERROR,
524
533
headers: dict = None,
525
534
):
535
if not isinstance(message, str):
536
message = "An error occurred"
526
537
return cls(format_exception(message), status_code, headers=headers)
527
538
528
539
def render(self, content) -> bytes:
@@ -710,8 +721,8 @@ class Api:
710
721
):
711
722
try:
712
723
provider = AbstractClientFactory.create_provider(None, provider)
713
except ProviderNotFoundError as e:
714
return ErrorResponse.from_message(str(e), 404)
724
except ProviderNotFoundError:
725
return ErrorResponse.from_message(f"Provider not found: {provider}", 404)
715
726
if not hasattr(provider, "get_models"):
716
727
models = getattr(provider, "models", [])
717
728
elif credentials is not None and credentials.credentials != "secret":
@@ -763,8 +774,8 @@ class Api:
763
774
):
764
775
try:
765
776
provider = AbstractClientFactory.create_provider(None, provider)
766
except ProviderNotFoundError as e:
767
return ErrorResponse.from_message(str(e), 404)
777
except ProviderNotFoundError:
778
return ErrorResponse.from_message(f"Provider not found: {provider}", 404)
768
779
if not hasattr(provider, "get_quota"):
769
780
return ErrorResponse.from_message(
770
781
"Provider doesn't support get_quota", HTTP_500_INTERNAL_SERVER_ERROR
@@ -848,8 +859,8 @@ class Api:
848
859
config.provider = AppConfig.provider
849
860
try:
850
861
provider = AbstractClientFactory.create_provider(None, config.provider)
851
except ProviderNotFoundError as e:
852
return ErrorResponse.from_message(str(e), 404)
862
except ProviderNotFoundError:
863
return ErrorResponse.from_message(f"Provider not found: {config.provider}", 404)
853
864
try:
854
865
if config.conversation_id is None:
855
866
config.conversation_id = conversation_id
@@ -1025,8 +1036,8 @@ class Api:
1025
1036
config.provider = AppConfig.provider
1026
1037
try:
1027
1038
provider = AbstractClientFactory.create_provider(None, config.provider)
1028
except ProviderNotFoundError as e:
1029
return ErrorResponse.from_message(str(e), 404)
1039
except ProviderNotFoundError:
1040
return ErrorResponse.from_message(f"Provider not found: {config.provider}", 404)
1030
1041
try:
1031
1042
if config.timeout is None:
1032
1043
config.timeout = AppConfig.timeout
@@ -1179,8 +1190,8 @@ class Api:
1179
1190
config.provider = AppConfig.provider
1180
1191
try:
1181
1192
provider = AbstractClientFactory.create_provider(None, config.provider)
1182
except ProviderNotFoundError as e:
1183
return ErrorResponse.from_message(str(e), 404)
1193
except ProviderNotFoundError:
1194
return ErrorResponse.from_message(f"Provider not found: {config.provider}", 404)
1184
1195
try:
1185
1196
if config.timeout is None:
1186
1197
config.timeout = AppConfig.timeout
@@ -1340,8 +1351,8 @@ class Api:
1340
1351
provider = AppConfig.provider
1341
1352
try:
1342
1353
provider = AbstractClientFactory.create_provider(None, provider)
1343
except ProviderNotFoundError as e:
1344
return ErrorResponse.from_message(str(e), 404)
1354
except ProviderNotFoundError:
1355
return ErrorResponse.from_message(f"Provider not found: {provider}", 404)
1345
1356
if (
1346
1357
config.api_key is None
1347
1358
and credentials is not None
@@ -1426,8 +1437,8 @@ class Api:
1426
1437
async def providers_info(provider: str):
1427
1438
try:
1428
1439
provider = AbstractClientFactory.create_provider(None, provider)
1429
except ProviderNotFoundError as e:
1430
return ErrorResponse.from_message(str(e), 404)
1440
except ProviderNotFoundError:
1441
return ErrorResponse.from_message(f"Provider not found: {provider}", 404)
1431
1442
1432
1443
return {
1433
1444
"id": provider.__name__,
@@ -1930,8 +1941,8 @@ class Api:
1930
1941
provider = "MarkItDown"
1931
1942
try:
1932
1943
provider = AbstractClientFactory.create_provider(None, provider)
1933
except ProviderNotFoundError as e:
1934
return ErrorResponse.from_message(str(e), 404)
1944
except ProviderNotFoundError:
1945
return ErrorResponse.from_message(f"Provider not found: {provider}", 404)
1935
1946
kwargs = {"modalities": ["text"]}
1936
1947
try:
1937
1948
response = await self.client.chat.completions.create(
@@ -2095,8 +2106,8 @@ class Api:
2095
2106
provider = AppConfig.media_provider
2096
2107
try:
2097
2108
provider = AbstractClientFactory.create_provider(None, provider)
2098
except ProviderNotFoundError as e:
2099
return ErrorResponse.from_message(str(e), 404)
2109
except ProviderNotFoundError:
2110
return ErrorResponse.from_message(f"Provider not found: {provider}", 404)
2100
2111
try:
2101
2112
audio = filter_none(
2102
2113
voice=config.voice,
@@ -2358,8 +2369,12 @@ def format_exception(
2358
2369
model = config.model
2359
2370
if isinstance(e, str):
2360
2371
message = e
2361
elif isinstance(e, (ModelNotFoundError, ProviderNotFoundError, MissingAuthError)):
2362
message = f"{e.__class__.__name__}: {e}"
2372
elif isinstance(e, ModelNotFoundError):
2373
message = "ModelNotFoundError: Model not found"
2374
elif isinstance(e, ProviderNotFoundError):
2375
message = "ProviderNotFoundError: Provider not found"
2376
elif isinstance(e, MissingAuthError):
2377
message = "MissingAuthError: Authentication required"
2363
2378
else:
2364
2379
message = f"{e.__class__.__name__}: Request failed"
2365
2380
return json.dumps(
@@ -6,9 +6,6 @@ import logging
6
6
from typing import AsyncIterator, Iterator, AsyncGenerator, Optional
7
7
8
8
9
_CODE_BLOCK_RE = re.compile(r"```([^\r\n\s]+)?\r?\n(?P<code>[\s\S]*?)(?:\r?\n```|$)")
10
11
12
9
def filter_markdown(text: str, allowed_types=None, default=None) -> str:
13
10
"""
14
11
Parses code block from a string.
@@ -17,12 +14,31 @@ def filter_markdown(text: str, allowed_types=None, default=None) -> str:
17
14
text (str): A string containing a code block.
18
15
19
16
Returns:
20
dict: A dictionary parsed from the code block.
17
str: Parsed code block content, or default.
21
18
"""
22
match = _CODE_BLOCK_RE.search(text)
23
if match:
24
if allowed_types is None or match.group(1) in allowed_types:
25
return match.group("code")
19
if not isinstance(text, str):
20
return default
21
start = text.find("```")
22
if start == -1:
23
return default
24
first_nl = text.find("\n", start + 3)
25
if first_nl == -1:
26
return default
27
tag = text[start + 3 : first_nl].strip("\r\n\t ")
28
match_tag = tag if tag else None
29
end = text.find("\n```", first_nl)
30
if end != -1:
31
code = text[first_nl + 1 : end]
32
if code.endswith("\r"):
33
code = code[:-1]
34
else:
35
code = text[first_nl + 1 :]
36
if (
37
allowed_types is None
38
or match_tag in allowed_types
39
or (not match_tag and ("" in allowed_types or None in allowed_types))
40
):
41
return code
26
42
return default
27
43
28
44
@@ -737,13 +737,7 @@ class Backend_Api(Api):
737
737
+ json.dumps(parameters, sort_keys=True).encode()
738
738
).hexdigest()
739
739
cache_dir = Path(get_cookies_dir()) / ".scrape_cache" / "create"
740
safe_prompt = secure_filename(request.args.get("prompt", "").strip()[:20])
741
file_name = f"{safe_prompt}_{cache_id}.txt" if safe_prompt else f"{cache_id}.txt"
742
real_cache_dir = os.path.realpath(str(cache_dir))
743
target = os.path.realpath(os.path.join(real_cache_dir, file_name))
744
if not target.startswith(real_cache_dir + os.sep):
745
target = os.path.realpath(os.path.join(real_cache_dir, f"{cache_id}.txt"))
746
cache_file = Path(target)
740
cache_file = cache_dir / f"{cache_id}.txt"
747
741
response = None
748
742
if cache_file.exists():
749
743
with cache_file.open("r") as f:
@@ -776,17 +770,10 @@ class Backend_Api(Api):
776
770
if os.path.exists(target_file):
777
771
os.remove(target_file)
778
772
else:
779
if response.startswith("/") and not response.startswith("//"):
780
return redirect(response)
781
return Response(response, mimetype="text/plain")
773
return send_from_directory(media_dir, filename)
782
774
elif response.startswith("https://") or response.startswith(
783
775
"http://"
784
776
):
785
from urllib.parse import urlparse
786
target_netloc = urlparse(response).netloc.lower()
787
allowed_hosts = {request.host.lower()}
788
if target_netloc in allowed_hosts:
789
return redirect(response)
790
777
return Response(response, mimetype="text/plain")
791
778
if do_filter:
792
779
is_true_filter = do_filter.lower() in ["true", "1"]