XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

fix(security): resolve CodeQL alerts for path injection, ReDoS, open redirect, and decouple OAuth secrets

bd3be834
Anand Mall <anand@example.com>
提交于

代码差异

6 个文件 +77 -58
Modified g4f/Provider/needs_auth/Antigravity.py +7 -4
@@ -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
Modified g4f/Provider/needs_auth/GeminiCLI.py +7 -5
@@ -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
Modified g4f/Provider/search/CachedSearch.py +1 -5
@@ -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():
Modified g4f/api/__init__.py +36 -21
@@ -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(
Modified g4f/client/helper.py +24 -8
@@ -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
Modified g4f/gui/server/backend_api.py +2 -15
@@ -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"]