Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 47 additions & 8 deletions routes/NewsRoutes.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,41 @@
from flask import *
from controllers.NewsController import NewsController
routes = Blueprint("routes", __name__)
# news_controller = NewsController("mistralai") # default model name

MAX_KEYWORD_LENGTH = 200


def _validate_keyword(raw_list):
"""
Validates the ?keywords= query parameter.

Returns (keyword, error_response):
- On success: (stripped_keyword, None)
- On failure: (None, (json_response, status_code))

Checks performed:
1. Parameter must be present.
2. Value must be non-empty after stripping whitespace.
3. Value must not exceed MAX_KEYWORD_LENGTH characters.
"""
if not raw_list:
return None, (jsonify({"error": "Missing required query parameter: keywords"}), 400)

keyword = raw_list[0].strip()

if not keyword:
return None, (jsonify({"error": "keywords parameter cannot be empty"}), 400)

if len(keyword) > MAX_KEYWORD_LENGTH:
return None, (
jsonify({
"error": f"keywords parameter exceeds maximum length of {MAX_KEYWORD_LENGTH} characters"
}),
400,
)

return keyword, None


"""
home page route
Expand Down Expand Up @@ -37,11 +71,13 @@ def getNews_route(llm_name):
"""
@routes.route("/<llm_name>/news_keywords", methods=["GET"])
def getNewsWithKeywords_route(llm_name):
# get list of keywords as argument from User's request
keyword, err = _validate_keyword(request.args.getlist("keywords"))
if err:
return err

g.news_controller = NewsController(llm_name)
user_keywords = request.args.getlist("keywords")
data = g.news_controller.getNewsWithKeywords(user_keywords[0])
return render_template("news_key.html", data=data, keyword=user_keywords[0])
data = g.news_controller.getNewsWithKeywords(keyword)
return render_template("news_key.html", data=data, keyword=keyword)


"""
Expand All @@ -60,11 +96,14 @@ def getNews_raw_route():
"""
@routes.route("/raw/news_keywords", methods=["GET"])
def getNewsWithKeywords_raw_route():
keyword, err = _validate_keyword(request.args.getlist("keywords"))
if err:
return err

# Instantiate without a model to bypass LLM initialization entirely
g.news_controller = NewsController(None)
user_keywords = request.args.getlist("keywords")
data = g.news_controller.getNewsWithKeywords(user_keywords[0])
return render_template("news_key.html", data=data, keyword=user_keywords[0], llm_name="raw")
data = g.news_controller.getNewsWithKeywords(keyword)
return render_template("news_key.html", data=data, keyword=keyword, llm_name="raw")


"""
Expand Down
25 changes: 24 additions & 1 deletion tests/unitTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,30 @@ def test_news(self):
def test_news_keywords(self):
response = self.app.get('/news_keywords?keywords=firewall')
self.assertEqual(response.status_code, 200)


# ── Input sanitisation tests (issue #140) ─────────────────────────────────

def test_keywords_missing_param(self):
"""No ?keywords= param should return 400, not crash with IndexError."""
response = self.app.get('/mistralai/news_keywords')
self.assertEqual(response.status_code, 400)

def test_keywords_empty_string(self):
"""Blank keyword after stripping should return 400."""
response = self.app.get('/mistralai/news_keywords?keywords= ')
self.assertEqual(response.status_code, 400)

def test_keywords_too_long(self):
"""Keyword exceeding 200 characters should return 400."""
long_keyword = 'a' * 201
response = self.app.get(f'/mistralai/news_keywords?keywords={long_keyword}')
self.assertEqual(response.status_code, 400)

def test_raw_keywords_missing_param(self):
"""Raw endpoint also returns 400 when ?keywords= is missing."""
response = self.app.get('/raw/news_keywords')
self.assertEqual(response.status_code, 400)

def test_invalid_route(self):
response = self.app.get('/xxx')
self.assertEqual(response.status_code, 404)
Expand Down