* feat: implement URL validation to prevent SSRF

* feat: add zip extraction security

* ruff fixes

* fix: standardize error messages across API responses

* fix: improve error handling and standardize error messages across multiple routes

* fix: enhance JavaScript string safety in ConnectorCallbackStatus

* fix: improve OAuth error handling and message formatting in MCPOAuthCallback
This commit is contained in:
Alex
2025-12-25 00:57:25 +00:00
committed by GitHub
parent 197e94302b
commit b0eee7be24
7 changed files with 33 additions and 24 deletions

View File

@@ -487,11 +487,15 @@ class ConnectorCallbackStatus(Resource):
session_token = request.args.get('session_token', '')
user_email = html.escape(request.args.get('user_email', ''))
# Use json.dumps for safe JavaScript string embedding
js_status = json.dumps(status)
js_session_token = json.dumps(session_token)
js_user_email = json.dumps(user_email)
js_provider_type = json.dumps(provider_raw)
def safe_js_string(value: str) -> str:
"""Safely encode a string for embedding in inline JavaScript."""
js_encoded = json.dumps(value)
return js_encoded.replace('</', '<\\/').replace('<!--', '<\\!--')
js_status = safe_js_string(status)
js_session_token = safe_js_string(session_token)
js_user_email = safe_js_string(user_email)
js_provider_type = safe_js_string(provider_raw)
html_content = f"""
<!DOCTYPE html>

View File

@@ -307,9 +307,10 @@ class CreateAgent(Resource):
400,
)
except Exception as e:
current_app.logger.error(f"Invalid JSON schema: {e}")
return make_response(
jsonify(
{"success": False, "message": f"Invalid JSON schema: {str(e)}"}
{"success": False, "message": "Invalid JSON schema format"}
),
400,
)

View File

@@ -255,8 +255,8 @@ class ShareAgent(Resource):
{"$unset": {"shared_metadata": ""}},
)
except Exception as err:
current_app.logger.error(f"Error sharing/unsharing agent: {err}")
return make_response(jsonify({"success": False, "error": str(err)}), 400)
current_app.logger.error(f"Error sharing/unsharing agent: {err}", exc_info=True)
return make_response(jsonify({"success": False, "error": "Failed to update agent sharing status"}), 400)
shared_token = shared_token if shared else None
return make_response(
jsonify({"success": True, "shared_token": shared_token}), 200

View File

@@ -99,11 +99,8 @@ class StoreAttachment(Resource):
})
if not tasks:
error_msg = "No valid files to upload"
if errors:
error_msg += f". Errors: {errors}"
return make_response(
jsonify({"status": "error", "message": error_msg, "errors": errors}),
jsonify({"status": "error", "message": "No valid files to upload"}),
400,
)
@@ -135,7 +132,7 @@ class StoreAttachment(Resource):
)
except Exception as err:
current_app.logger.error(f"Error storing attachment: {err}", exc_info=True)
return make_response(jsonify({"success": False, "error": str(err)}), 400)
return make_response(jsonify({"success": False, "error": "Failed to store attachment"}), 400)
@attachments_ns.route("/images/<path:image_path>")

View File

@@ -579,8 +579,9 @@ class TaskStatus(Resource):
):
task_meta = str(task_meta) # Convert to a string representation
except ConnectionError as err:
current_app.logger.error(f"Connection error getting task status: {err}")
return make_response(
jsonify({"success": False, "message": str(err)}), 503
jsonify({"success": False, "message": "Service unavailable"}), 503
)
except Exception as err:
current_app.logger.error(f"Error getting task status: {err}", exc_info=True)

View File

@@ -1,7 +1,7 @@
"""Tool management MCP server integration."""
import json
from email.quoprimime import unquote
from urllib.parse import unquote, urlencode
from bson.objectid import ObjectId
from flask import current_app, jsonify, make_response, redirect, request
@@ -64,6 +64,11 @@ class TestMCPServerConfig(Resource):
mcp_tool = MCPTool(config=test_config, user_id=user)
result = mcp_tool.test_connection()
# Sanitize the response to avoid exposing internal error details
if not result.get("success") and "message" in result:
current_app.logger.error(f"MCP connection test failed: {result.get('message')}")
result["message"] = "Connection test failed"
return make_response(jsonify(result), 200)
except Exception as e:
current_app.logger.error(f"Error testing MCP server: {e}", exc_info=True)
@@ -263,9 +268,12 @@ class MCPOAuthCallback(Resource):
error = request.args.get("error")
if error:
return redirect(
f"/api/connectors/callback-status?status=error&message=OAuth+error:+{error}.+Please+try+again+and+make+sure+to+grant+all+requested+permissions,+including+offline+access.&provider=mcp_tool"
)
params = {
"status": "error",
"message": f"OAuth error: {error}. Please try again and make sure to grant all requested permissions, including offline access.",
"provider": "mcp_tool"
}
return redirect(f"/api/connectors/callback-status?{urlencode(params)}")
if not code or not state:
return redirect(
"/api/connectors/callback-status?status=error&message=Authorization+code+or+state+not+provided.+Please+complete+the+authorization+process+and+make+sure+to+grant+offline+access.&provider=mcp_tool"

View File

@@ -462,10 +462,8 @@ class ParseSpec(Resource):
200,
)
except ValueError as e:
error_msg = str(e)
current_app.logger.error(f"Spec validation error: {error_msg}")
return make_response(jsonify({"success": False, "error": error_msg}), 400)
current_app.logger.error(f"Spec validation error: {e}")
return make_response(jsonify({"success": False, "error": "Invalid specification format"}), 400)
except Exception as err:
error_msg = str(err)
current_app.logger.error(f"Error parsing spec: {error_msg}", exc_info=True)
return make_response(jsonify({"success": False, "error": error_msg}), 500)
current_app.logger.error(f"Error parsing spec: {err}", exc_info=True)
return make_response(jsonify({"success": False, "error": "Failed to parse specification"}), 500)