diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a9115cf5..2eb85a27 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -35,7 +35,7 @@ a proposal for your work first, to be sure that we can use it.
## Submission Guidelines
### Submitting an Issue
-Before you submit an issue, search the archive, maybe your question was already answered.
+Before you submit an issue, search the archive, maybe your question has already been answered.
If your issue appears to be a bug, and hasn't been reported, open a new issue.
Help us to maximize the effort we can spend fixing issues and adding new
diff --git a/End_to_end_Solutions/AOAISearchDemo/app/backend/app.py b/End_to_end_Solutions/AOAISearchDemo/app/backend/app.py
index 35c33a30..f107e13f 100644
--- a/End_to_end_Solutions/AOAISearchDemo/app/backend/app.py
+++ b/End_to_end_Solutions/AOAISearchDemo/app/backend/app.py
@@ -340,7 +340,7 @@ def chat():
return jsonify(response.to_item()), 400
except Exception as e:
logger.exception(f"Exception in /chat: {e}", extra=properties)
- response = ChatResponse(answer=Answer(), error=str(e), show_retry=True)
+ response = ChatResponse(answer=Answer(), error="An internal error has occurred.", show_retry=True)
return jsonify(response.to_item()), 500
@@ -352,7 +352,7 @@ def get_all_user_profiles():
return jsonify(user_profiles_dict)
except Exception as e:
logger.exception(f"Exception in /user-profiles: {e}")
- return jsonify({"error": str(e)}), 500
+ return jsonify({"error": "An internal error has occurred."}), 500
@app.route("/chat-sessions//", methods=["DELETE"])
@@ -369,7 +369,7 @@ def clear_chat_session(user_id: str, conversation_id: str):
logger.exception(
f"Exception in /chat-sessions//: {e}"
)
- return jsonify({"error": str(e)}), 500
+ return jsonify({"error": "An internal error has occurred."}), 500
@app.route("/search-settings", methods=["GET"])
@@ -391,7 +391,7 @@ def get_search_settings():
return jsonify(search_settings.to_item())
except Exception as e:
logger.exception(f"Exception in /search-settings: {e}")
- return jsonify({"error": str(e)}), 500
+ return jsonify({"error": "An internal error has occurred."}), 500
if __name__ == "__main__":
diff --git a/End_to_end_Solutions/AOAISearchDemo/app/backend/data_client/data_client.py b/End_to_end_Solutions/AOAISearchDemo/app/backend/data_client/data_client.py
index 16beca3c..db83ea46 100644
--- a/End_to_end_Solutions/AOAISearchDemo/app/backend/data_client/data_client.py
+++ b/End_to_end_Solutions/AOAISearchDemo/app/backend/data_client/data_client.py
@@ -12,6 +12,26 @@
from typing import List, Optional
from common.logging.log_helper import CustomLogger
+from urllib.parse import urlparse
+from urllib.parse import urlparse, urljoin
+
+def _validate_base_uri(self, base_uri: str):
+ parsed_uri = urlparse(base_uri)
+ if parsed_uri.scheme not in ["http", "https"]:
+ raise ValueError("Invalid URI scheme")
+ if not parsed_uri.netloc:
+ raise ValueError("Invalid URI netloc")
+
+def _validate_base_uri(self, base_uri: str):
+ parsed_uri = urlparse(base_uri)
+ if parsed_uri.scheme not in ["http", "https"]:
+ raise ValueError("Invalid URI scheme")
+ if not parsed_uri.netloc:
+ raise ValueError("Invalid URI netloc")
+
+def _validate_path(self, path: str):
+ if not path.startswith("/"):
+ raise ValueError("Invalid path")
class DataClient:
class HttpMethod(Enum):
@@ -21,8 +41,22 @@ class HttpMethod(Enum):
DELETE="DELETE"
def __init__(self, base_uri: str, logger: CustomLogger):
+ self._validate_base_uri(base_uri)
self.base_uri = base_uri
+ self.base_uri = self._validate_base_uri(base_uri)
self.logger = logger
+
+ def _validate_base_uri(self, base_uri: str) -> str:
+ # Ensure the base_uri is a trusted URL
+ if not base_uri.startswith("https://trusted-domain.com"):
+ raise ValueError("Invalid base URI")
+ return base_uri
+
+ def _sanitize_path(self, path: str) -> str:
+ # Sanitize the path to prevent malicious input
+ if ".." in path or path.startswith("/"):
+ raise ValueError("Invalid path")
+ return path
def check_chat_session(self, user_id: str, conversation_id: str) -> bool:
path = f"/check-chat-session/{user_id}/{conversation_id}"
@@ -113,6 +147,8 @@ def get_user_resources(self, user_id: str) -> List[ResourceProfile]:
@retry(reraise=True, stop = stop_after_attempt(3), wait = wait_exponential(multiplier = 1, max = 60))
def _make_request(self, path: str, method: HttpMethod, payload: Optional[dict] = None) -> str:
+ path = self._sanitize_path(path)
+ self._validate_path(path)
headers = self.logger.get_converation_and_dialog_ids()
properties = self.logger.get_updated_properties(headers)
diff --git a/End_to_end_Solutions/AOAISearchDemo/app/data/app.py b/End_to_end_Solutions/AOAISearchDemo/app/data/app.py
index d5377e9a..14c193eb 100644
--- a/End_to_end_Solutions/AOAISearchDemo/app/data/app.py
+++ b/End_to_end_Solutions/AOAISearchDemo/app/data/app.py
@@ -11,8 +11,9 @@
from data.managers.entities.api.manager import EntitiesManager
from datetime import datetime
from flask import Flask, Response, request
+import html
from typing import List, Set
-
+import logging
# initialize config
DefaultConfig.initialize()
@@ -61,13 +62,13 @@ def create_chat_session(user_id: str, conversation_id: str):
return Response(response=json.dumps(session.to_item()), status=201)
except (TypeError, NullValueError, MissingPropertyError) as e:
logger.exception(f"create-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=400)
+ return Response(response="A bad request error occurred.", status=400)
except CosmosConflictError as e:
logger.exception(f"create-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=409)
+ return Response(response="Conflict occurred while creating chat session.", status=409)
except Exception as e:
logger.exception(f"create-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=500)
+ return Response(response="An internal server error occurred.", status=500)
@app.route('/chat-sessions//', methods=['GET'])
def get_chat_session(user_id: str, conversation_id: str):
@@ -84,14 +85,14 @@ def get_chat_session(user_id: str, conversation_id: str):
properties = logger.get_updated_properties(addl_dim)
if session is None:
- logger.info(f"get-chat-session: session with conversation_id {conversation_id} not found", extra=properties)
- return Response(response=f"Chat session with conversation_id {conversation_id} not found.", status=404)
+ logger.info(f"get-chat-session: session with conversation_id {html.escape(conversation_id)} not found", extra=properties)
+ return Response(response=f"Chat session with conversation_id {html.escape(conversation_id)} not found.", status=404)
else:
logger.info("get-chat-session: session found", extra=properties)
return Response(response=json.dumps(session.to_item()), status=200)
except Exception as e:
logger.exception(f"get-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=500)
+ return Response(response="An internal server error occurred.", status=500)
@app.route('/check-chat-session//', methods=['GET'])
def check_chat_session(user_id: str, conversation_id: str):
@@ -113,7 +114,7 @@ def check_chat_session(user_id: str, conversation_id: str):
return Response(response="true", status=200)
except Exception as e:
logger.exception(f"check-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=500)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/chat-sessions//', methods=['PUT'])
def update_chat_session(user_id: str, conversation_id: str):
@@ -147,13 +148,13 @@ def update_chat_session(user_id: str, conversation_id: str):
return Response(response=json.dumps(session.to_item()), status=200)
except (TypeError, NullValueError, MissingPropertyError, ValueError) as e:
logger.exception(f"update-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=400)
+ return Response(response="An error occurred while processing your request.", status=400)
except SessionNotFoundError as e:
logger.exception(f"update-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=404)
+ return Response(response="Chat session not found.", status=404)
except Exception as e:
logger.exception(f"update-chat-session: error: {e} ", extra=properties)
- return Response(response=str(e), status=500)
+ return Response(response="An internal server error occurred.", status=500)
@app.route('/chat-sessions//', methods=['DELETE'])
def clear_chat_session(user_id: str, conversation_id: str):
@@ -161,9 +162,11 @@ def clear_chat_session(user_id: str, conversation_id: str):
chat_manager.clear_chat_session(user_id, conversation_id)
return Response(status=200)
except SessionNotFoundError as e:
- return Response(response=str(e), status=404)
+ logger.exception(f"clear-chat-session: error: {e} ")
+ return Response(response="Chat session not found.", status=404)
except Exception as e:
- return Response(response=str(e), status=500)
+ logger.exception(f"clear-chat-session: error: {e} ")
+ return Response(response="An internal server error occurred.", status=500)
@app.route('/user-profiles/', methods=['POST'])
def create_user_profile(user_id: str):
@@ -184,11 +187,12 @@ def create_user_profile(user_id: str):
user_profile = entities_manager.create_user_profile(user_id, user_name, description, sample_questions)
return Response(response=json.dumps(user_profile.to_item()), status=201)
except (TypeError, NullValueError, MissingPropertyError) as e:
- return Response(response=str(e), status=400)
+ return Response(response="Invalid request data.", status=400)
except CosmosConflictError as e:
- return Response(response=str(e), status=409)
+ return Response(response="Conflict occurred while creating user profile.", status=409)
except Exception as e:
- return Response(response=str(e), status=500)
+ logger.exception(f"create-user-profile: error: {e}")
+ return Response(response="An internal server error occurred.", status=500)
@app.route('/user-profiles/', methods=['GET'])
def get_user_profile(user_id: str):
@@ -196,11 +200,13 @@ def get_user_profile(user_id: str):
try:
user_profile = entities_manager.get_user_profile(user_id)
if user_profile is None:
- return Response(response=f"User profile with user_id {user_id} not found.", status=404)
+ escaped_user_id = html.escape(user_id)
+ return Response(response=f"User profile with user_id {escaped_user_id} not found.", status=404)
else:
return Response(response=json.dumps(user_profile.to_item()), status=200)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error("An error occurred while fetching user profile: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/user-profiles', methods=['GET'])
def get_all_user_profiles():
@@ -209,7 +215,8 @@ def get_all_user_profiles():
json_user_profiles = [user_profile.to_item() for user_profile in user_profiles]
return Response(response=json.dumps(json_user_profiles), status=200)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error("An error occurred while fetching all user profiles: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/user-groups/', methods=['POST'])
def create_user_group(group_id: str):
@@ -229,33 +236,39 @@ def create_user_group(group_id: str):
user_group = entities_manager.create_user_group(group_id, group_name, users)
return Response(response=json.dumps(user_group.to_item()), status=201)
except (TypeError, NullValueError, MissingPropertyError) as e:
- return Response(response=str(e), status=400)
+ logging.error("A validation error occurred: %s", e, exc_info=True)
+ return Response(response="A validation error has occurred.", status=400)
except CosmosConflictError as e:
- return Response(response=str(e), status=409)
+ logging.error("A conflict error occurred while creating user group: %s", e, exc_info=True)
+ return Response(response="A conflict error has occurred.", status=409)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error("An error occurred while creating user group: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/user-groups/', methods=['GET'])
def get_user_group(group_id: str):
try:
user_group = entities_manager.get_user_group(group_id)
if user_group is None:
- return Response(response=f"User group with group_id {group_id} not found.", status=404)
+ escaped_group_id = html.escape(group_id)
+ return Response(response=f"User group with group_id {escaped_group_id} not found.", status=404)
else:
return Response(response=json.dumps(user_group.to_item()), status=200)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error("An error occurred while fetching user group: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/user-groups/user/', methods=['GET'])
def get_user_member_groups(user_id: str):
try:
user_groups = entities_manager.get_user_member_groups(user_id)
if user_groups is None:
- return Response(response=f"User with user_id {user_id} not found.", status=404)
+ return Response(response=f"User with user_id {html.escape(user_id)} not found.", status=404)
else:
return Response(response=json.dumps([user_group.to_item_no_users() for user_group in user_groups]), status=200)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error("An error occurred while fetching user member groups: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/user-groups/', methods=['PUT'])
def update_user_group(group_id: str):
@@ -273,11 +286,14 @@ def update_user_group(group_id: str):
user_group = entities_manager.add_users_to_user_group(group_id, new_users)
return Response(response=json.dumps(user_group.to_item()), status=200)
except (TypeError, NullValueError, MissingPropertyError, ValueError) as e:
- return Response(response=str(e), status=400)
+ logging.error("An error occurred while updating user group: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=400)
except SessionNotFoundError as e:
- return Response(response=str(e), status=404)
+ logging.error("Session not found: %s", e, exc_info=True)
+ return Response(response="Session not found.", status=404)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error("An error occurred while updating user group: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/resources/', methods=['POST'])
def create_resource(resource_id: str):
@@ -292,29 +308,33 @@ def create_resource(resource_id: str):
resource = entities_manager.create_resource(resource_id, resource_type)
return Response(response=json.dumps(resource.to_item()), status=201)
except (TypeError, NullValueError, MissingPropertyError) as e:
- return Response(response=str(e), status=400)
+ logging.error("An error occurred while creating resource: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=400)
except CosmosConflictError as e:
- return Response(response=str(e), status=409)
+ logging.error("A conflict occurred while creating resource: %s", e, exc_info=True)
+ return Response(response="A conflict occurred while creating the resource.", status=409)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error("An error occurred while creating resource: %s", e, exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/resources/', methods=['GET'])
def get_resource(resource_id: str):
try:
resource = entities_manager.get_resource(resource_id)
if resource is None:
- return Response(response=f"Resource with resource_id {resource_id} not found.", status=404)
+ return Response(response=f"Resource with resource_id {html.escape(resource_id)} not found.", status=404)
else:
return Response(response=json.dumps(resource.to_item()), status=200)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error(f"Error in get_resource: {e}", exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/resources/user/', methods=['GET'])
def get_user_resources(user_id: str):
try:
user_profile = entities_manager.get_user_profile(user_id)
if user_profile is None:
- return Response(response=f"User with user_id {user_id} not found.", status=404)
+ return Response(response=f"User with user_id {html.escape(user_id)} not found.", status=404)
user_groups = entities_manager.get_user_member_groups(user_id)
resources = permissions_manager.get_user_resources(user_profile, user_groups)
@@ -327,7 +347,8 @@ def get_user_resources(user_id: str):
return Response(response="Could not find resource profile for resource ID {resource.resource_id}.", status=500)
return Response(response=json.dumps([resource_profile.to_item() for resource_profile in resource_profiles]), status=200)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error(f"Error in get_user_resources: {e}", exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/access-rules/', methods=['POST'])
def create_access_rule(rule_id: str):
@@ -350,22 +371,26 @@ def create_access_rule(rule_id: str):
access_rule = permissions_manager.create_access_rule(rule_id, resources, members)
return Response(response=json.dumps(access_rule.to_item()), status=201)
except (TypeError, NullValueError, MissingPropertyError) as e:
- return Response(response=str(e), status=400)
+ logging.error(f"Validation error in create_access_rule: {e}", exc_info=True)
+ return Response(response="Invalid input provided.", status=400)
except CosmosConflictError as e:
- return Response(response=str(e), status=409)
+ logging.error(f"Cosmos conflict error in create_access_rule: {e}", exc_info=True)
+ return Response(response="A conflict occurred while processing your request.", status=409)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error(f"Error in create_access_rule: {e}", exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
@app.route('/access-rules/', methods=['GET'])
def get_access_rule(rule_id: str):
try:
access_rule = permissions_manager.get_access_rule(rule_id)
if access_rule is None:
- return Response(response=f"Access rule with rule_id {rule_id} not found.", status=404)
+ return Response(response=f"Access rule with rule_id {html.escape(rule_id)} not found.", status=404)
else:
return Response(response=json.dumps(access_rule.to_item()), status=200)
except Exception as e:
- return Response(response=str(e), status=500)
+ logging.error(f"Error in get_access_rule: {e}", exc_info=True)
+ return Response(response="An internal error has occurred.", status=500)
def get_log_properties(request, user_id: str) -> dict:
conversation_id = request.headers.get('Conversation-Id')
diff --git a/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js b/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
index 5f84d461..18c6b32a 100644
--- a/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
+++ b/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
@@ -1073,7 +1073,7 @@
return;
}
- var target = $(selector)[0];
+ var target = $.find(selector)[0];
if (!target || !$(target).hasClass(ClassName$2.CAROUSEL)) {
return;
diff --git a/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js b/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js
index cdf7fb73..d88d9cbd 100644
--- a/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js
+++ b/End_to_end_Solutions/AOAIVirtualAssistant/src/botwebapp/Bot.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js
@@ -146,6 +146,11 @@
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : '';
}
+ // Sanitize the selector to prevent XSS
+ var tempDiv = document.createElement('div');
+ tempDiv.appendChild(document.createTextNode(selector));
+ selector = tempDiv.innerHTML;
+
try {
return document.querySelector(selector) ? selector : null;
} catch (err) {
diff --git a/End_to_end_Solutions/InsightsGenerator/insights_generator/core/OAI_client.py b/End_to_end_Solutions/InsightsGenerator/insights_generator/core/OAI_client.py
index 0ac8d020..f427761b 100644
--- a/End_to_end_Solutions/InsightsGenerator/insights_generator/core/OAI_client.py
+++ b/End_to_end_Solutions/InsightsGenerator/insights_generator/core/OAI_client.py
@@ -3,10 +3,18 @@
import os
import pdb
import tiktoken
+import urllib.parse
+
+def is_valid_url(url):
+ parsed_url = urllib.parse.urlparse(url)
+ return parsed_url.scheme in ["http", "https"] and parsed_url.netloc != ""
def make_prompt_request(prompt, max_tokens = 2048, timeout = 4):
- #url = "https://api.openai.com/v1/embeddings"
+ # Whitelist of allowed URLs
+ allowed_urls = ["https://api.openai.com/v1/embeddings", "https://another-trusted-url.com"]
url = os.getenv("AOAI_ENDPOINT")
+ if not is_valid_url(url) or url not in allowed_urls:
+ raise ValueError("The provided URL is not allowed.")
key = os.getenv("AOAI_KEY")
payload_dict = {"prompt": prompt,