diff --git a/client/src/components/Navbar.js b/client/src/components/Navbar.js index 283ac71e..38341a87 100644 --- a/client/src/components/Navbar.js +++ b/client/src/components/Navbar.js @@ -186,6 +186,14 @@ const NavBar = ({ role }) => { } } + function handleOpenSettings(e) { + setOpenMenu(e.currentTarget); + } + + function handleCloseSettings() { + setOpenMenu(null); + } + // Navigation Bar for standard user type if (role === "standard_user") return ( diff --git a/client/src/components/administrator/AccountApprovalTable.js b/client/src/components/administrator/AccountApprovalTable.js index 92d97590..17c83ddf 100644 --- a/client/src/components/administrator/AccountApprovalTable.js +++ b/client/src/components/administrator/AccountApprovalTable.js @@ -25,7 +25,7 @@ const AccountApprovalTable = () => { }) .then((data) => setUserData(data)) .catch((err) => { - console.log(err); + console.error("Failed to fetch merchant data."); }); }; @@ -49,7 +49,7 @@ const AccountApprovalTable = () => { fetchMerchants(); }) .catch((err) => { - console.log(err); + console.error("Failed to validate merchant account."); }); }; diff --git a/client/src/components/administrator/AuditLogTable.js b/client/src/components/administrator/AuditLogTable.js index 0fb6e367..5a9d4d7e 100644 --- a/client/src/components/administrator/AuditLogTable.js +++ b/client/src/components/administrator/AuditLogTable.js @@ -84,7 +84,7 @@ const AuditLogTable = () => { setLoading(false); }) .catch((err) => { - console.log(err); + console.error("Failed to fetch log data."); setLoading(false); }); }, [ diff --git a/client/src/components/administrator/UserManagementTable.js b/client/src/components/administrator/UserManagementTable.js index 127045f6..35dab651 100644 --- a/client/src/components/administrator/UserManagementTable.js +++ b/client/src/components/administrator/UserManagementTable.js @@ -79,7 +79,7 @@ const UserManagementTable = () => { setLoading(false); }) .catch((err) => { - console.log(err); + console.error("Failed to fetch user data."); setLoading(false); }); }, [ @@ -109,7 +109,7 @@ const UserManagementTable = () => { }) .then((data) => setRoleData(data)) .catch((err) => { - console.log(err); + console.error("Failed to fetch role data."); }); }, [API_BASE]); @@ -123,7 +123,7 @@ const UserManagementTable = () => { }) .then((data) => setClinicData(data)) .catch(() => { - console.log("Failed to fetch Clinics"); + console.error("Failed to fetch clinic data."); }); }, [API_BASE]); @@ -165,7 +165,7 @@ const UserManagementTable = () => { setNewRole(null); setRowSelectionModel({ type: "include", ids: new Set() }); } catch (err) { - console.log(err); + console.error("Failed to process request."); } }; @@ -199,7 +199,7 @@ const UserManagementTable = () => { setUserToDelete(null); setRowSelectionModel({ type: "include", ids: new Set() }); // Reset the checkbox after deletion. } catch (error) { - console.error("Delete user error:", error); + console.error("Failed to delete user."); setSnackbar({ open: true, message: error.message, severity: "error" }); } }; diff --git a/client/src/components/authentication/CreatePatientForm.js b/client/src/components/authentication/CreatePatientForm.js index d58c925b..2f6f75a1 100644 --- a/client/src/components/authentication/CreatePatientForm.js +++ b/client/src/components/authentication/CreatePatientForm.js @@ -181,7 +181,7 @@ const CreatePatientForm = () => { return response.json(); }) .catch((err) => { - console.log("An error has occurred"); + console.error("An error has occurred."); }); setIsLoading(false); } diff --git a/client/src/components/authentication/PasswordInputField.js b/client/src/components/authentication/PasswordInputField.js index d85347cf..308d7856 100644 --- a/client/src/components/authentication/PasswordInputField.js +++ b/client/src/components/authentication/PasswordInputField.js @@ -95,7 +95,7 @@ const PasswordInputField = ({ if (!isAltered && !showRequired) { return null; } - if (password.length === 0 || password === null || showRequired) { + if (!password || showRequired) { return "*Required"; } diff --git a/client/src/components/authentication/RegistrationForm.js b/client/src/components/authentication/RegistrationForm.js index 9a9742c7..d9a5939e 100644 --- a/client/src/components/authentication/RegistrationForm.js +++ b/client/src/components/authentication/RegistrationForm.js @@ -26,6 +26,7 @@ import Logo from "../../assets/WellAiLogoTR.png"; import EmailInputField from "../authentication/EmailInputField"; import PasswordInputField from "../authentication/PasswordInputField"; import PhoneInputField from "../authentication/PhoneInputField"; +import { stringEqual } from "../../utils/stringEqual"; const FULL_NAME_MAX_LENGTH = 255; const ACCOUNT_TYPES = Object.freeze({ @@ -77,7 +78,7 @@ const RegistrationForm = () => { }) .then((res) => res.json()) .then((data) => setClinicList(data)) - .catch((err) => console.log("An error has occurred")); + .catch((err) => console.error("An error has occurred.")); }, []); function updateGivenName(e) { @@ -134,7 +135,7 @@ const RegistrationForm = () => { const confirmPasswordInput = e.target.value; setConfirmPassword(confirmPasswordInput); setAlertPasswordsDontMatch( - confirmPasswordInput !== passwordState.password || + !stringEqual(confirmPasswordInput, passwordState.password) || confirmPasswordInput === "", ); } @@ -182,7 +183,7 @@ const RegistrationForm = () => { setAlertPasswordRequired(passwordState === null); setAlertPasswordsDontMatch( passwordState === null || - confirmPassword !== passwordState.password || + !stringEqual(confirmPassword, passwordState.password) || confirmPassword === "", ); setAlertGenderRequired(genderState === ""); @@ -194,7 +195,7 @@ const RegistrationForm = () => { setAlertPasswordRequired(passwordState === null); setAlertPasswordsDontMatch( passwordState === null || - confirmPassword !== passwordState.password || + !stringEqual(confirmPassword, passwordState.password) || confirmPassword === "", ); setAlertClinicRequired(clinicState === ""); @@ -214,7 +215,7 @@ const RegistrationForm = () => { (phoneState === null || phoneState.isValid) && passwordState !== null && passwordState.isValid && - passwordState.password === confirmPassword + stringEqual(passwordState.password, confirmPassword) ); } @@ -225,7 +226,7 @@ const RegistrationForm = () => { (phoneState === null || phoneState.isValid) && passwordState !== null && passwordState.isValid && - passwordState.password === confirmPassword && + stringEqual(passwordState.password, confirmPassword) && clinicState !== "" ); } @@ -281,7 +282,7 @@ const RegistrationForm = () => { setShowFailMessage(false); }) .catch((error) => { - console.log(error); + console.error("User reigstration requeset failed."); }); setIsLoading(false); } diff --git a/client/src/components/healthReport/BloodReportUpload.js b/client/src/components/healthReport/BloodReportUpload.js index 97cfb0fb..b65e3335 100644 --- a/client/src/components/healthReport/BloodReportUpload.js +++ b/client/src/components/healthReport/BloodReportUpload.js @@ -28,7 +28,7 @@ const BloodReportUpload = ({ onChange }) => { const file = e.target.files[0]; await pdfToText(file) .then((text) => onChange?.(extractPatientInfoAsDict(text))) - .catch((error) => console.error(error)); + .catch((error) => console.error("Failed to process blood report.")); } function extractPatientInfoAsDict(text) { diff --git a/client/src/components/healthReport/DownloadReportButton.js b/client/src/components/healthReport/DownloadReportButton.js index 0d70646c..5517769c 100644 --- a/client/src/components/healthReport/DownloadReportButton.js +++ b/client/src/components/healthReport/DownloadReportButton.js @@ -64,7 +64,7 @@ const DownloadReportButton = ({ saveAs(blob, fileName); if (onError) onError?.(null); // Clear previous errors on success } catch (error) { - console.error("Failed to download report:", error); + console.error("Failed to download report."); if (onError) onError(error.message); } }; diff --git a/client/src/components/healthReport/GenerateReportForm.js b/client/src/components/healthReport/GenerateReportForm.js index 1616864c..949ef6f8 100644 --- a/client/src/components/healthReport/GenerateReportForm.js +++ b/client/src/components/healthReport/GenerateReportForm.js @@ -123,7 +123,7 @@ const GenerateReportForm = () => { setWorkingStatus(data.workingStatus); setRace(data.race); } catch (err) { - console.log("Failed to fetch patient data."); + console.error("Failed to fetch patient data."); } } fetchPatientData(); @@ -331,7 +331,7 @@ const GenerateReportForm = () => { navigate("/report-history"); // Route the user to the Health prediction page after submission }) .catch((error) => { - console.log("An error has occurred"); + console.error("An error has occurred."); }); } diff --git a/client/src/components/healthReport/MerchantReportForm.js b/client/src/components/healthReport/MerchantReportForm.js index 7aa7e4ad..ecc07d09 100644 --- a/client/src/components/healthReport/MerchantReportForm.js +++ b/client/src/components/healthReport/MerchantReportForm.js @@ -138,7 +138,7 @@ const MerchantReportForm = () => { setPatientName(name.name); } }) - .catch((err) => console.log(err)); + .catch((err) => console.error("Failed to fetch patient names.")); }, [defaultSelectedPatient]); function handleChangeCondition(e) { @@ -173,7 +173,7 @@ const MerchantReportForm = () => { setWorkingStatus(data.workingStatus); setRace(data.race); } catch (err) { - console.log("Failed to fetch patient data."); + console.error("Failed to fetch patient data."); } } fetchPatientData(); @@ -343,7 +343,7 @@ const MerchantReportForm = () => { return response.json(); }) .catch((error) => { - console.log("Something went wrong."); + console.error("Failed to process request."); }); navigate("/merchant-reports"); setIsLoading(false); @@ -406,7 +406,7 @@ const MerchantReportForm = () => { }); // Route the user to the Health prediction page after submission }) .catch((error) => { - console.log("Something went wrong"); + console.error("Failed to process request."); }); } const HiddenInput = styled("input")({ diff --git a/client/src/routes/merchant/MerchantReports.js b/client/src/routes/merchant/MerchantReports.js index fd61a682..d785ab45 100644 --- a/client/src/routes/merchant/MerchantReports.js +++ b/client/src/routes/merchant/MerchantReports.js @@ -84,9 +84,9 @@ const MerchantReports = () => { } }) .catch((err) => { - console.log(err); - }); - }, [defaultSelectedPatientId]); + console.error("Failed to fetch report data."); + }, [defaultSelectedPatientId]); + } // Fetch the merchant reports useEffect(() => { @@ -104,7 +104,7 @@ const MerchantReports = () => { }) .then((res) => res.json()) .then((data) => setReportData(data)) - .catch((err) => console.log(err)); + .catch((err) => console.error("Failed to fetch report health data.")); }, [selectedDate]); // Delete report data @@ -129,7 +129,7 @@ const MerchantReports = () => { setReportDates(patientReports); setSelectedDate(patientReports[0]); } catch (err) { - console.log(err); + console.error("Failed to delete report data."); } // Close Dialog setDeleteDialogOpen(false); diff --git a/client/src/routes/merchant/PatientManagement.js b/client/src/routes/merchant/PatientManagement.js index 5604a512..7018f210 100644 --- a/client/src/routes/merchant/PatientManagement.js +++ b/client/src/routes/merchant/PatientManagement.js @@ -131,7 +131,7 @@ const PatientManagement = () => { setTotalPatients(data.totalPatients || 0); }) .catch((err) => { - console.log("An error has occurred"); + console.error("An error has occurred."); }); }, [ API_BASE, @@ -148,7 +148,7 @@ const PatientManagement = () => { }) .then((response) => response.json()) .catch((err) => { - console.log("An error has occurred"); + console.error("An error has occurred."); }); fetchPatients(); setDeleteDialogOpen(false); diff --git a/client/src/routes/standardUser/HealthAnalytics.js b/client/src/routes/standardUser/HealthAnalytics.js index e104c0bd..a0d7f0db 100644 --- a/client/src/routes/standardUser/HealthAnalytics.js +++ b/client/src/routes/standardUser/HealthAnalytics.js @@ -82,10 +82,9 @@ const HealthAnalytics = () => { throw new Error("Network response was not ok"); } const data = await response.json(); - //console.log("Fetched data:", data); // Log fetched data setHealthData(data); } catch (error) { - console.error("Failed to fetch health analytics data:", error); + console.error("Failed to fetch health analytics data."); } }; diff --git a/client/src/routes/standardUser/ReportHistory.js b/client/src/routes/standardUser/ReportHistory.js index 4347e366..57d32e50 100644 --- a/client/src/routes/standardUser/ReportHistory.js +++ b/client/src/routes/standardUser/ReportHistory.js @@ -54,10 +54,9 @@ const AIHealthPrediction = () => { if (data.length > 0) { setSelectedDate(data[0]); - console.log("The selected date is:", data[0]); } }) - .catch((err) => console.log(err)); + .catch((err) => console.error("Failed to fetch health data dates.")); }, []); useEffect(() => { @@ -75,7 +74,7 @@ const AIHealthPrediction = () => { }) .then((res) => res.json()) .then((data) => setReportData(data)) - .catch((err) => console.log(err)); + .catch((err) => console.error("Failed to fetch report data.")); }, [selectedDate]); // Delete report data @@ -90,9 +89,9 @@ const AIHealthPrediction = () => { }) .then((res) => res.json()) .then((data) => setReportData(data)) - .catch((err) => console.log(err)); + .catch(() => console.error("Failed to delete report data.")); } catch (err) { - console.log(err); + console.error("Failed to delete report data."); } // Reload reports fetchReportDates(); diff --git a/client/src/routes/standardUser/UserSettings.js b/client/src/routes/standardUser/UserSettings.js index f0a44f98..6a26ef67 100644 --- a/client/src/routes/standardUser/UserSettings.js +++ b/client/src/routes/standardUser/UserSettings.js @@ -22,6 +22,7 @@ import { useNavigate } from "react-router-dom"; import PhoneInputField from "../../components/authentication/PhoneInputField"; import ConfirmationDialog from "../../components/dialog/confirmationDialog"; import { UserContext } from "../../utils/UserContext"; +import { stringEqual } from "../../utils/stringEqual"; /** * A page that provides the tools for a user to securely update their @@ -153,7 +154,7 @@ const UserSettings = () => { return response.json(); }) .catch((error) => { - console.log("Failed to change password"); + console.error("Failed to change password."); }); } @@ -498,7 +499,7 @@ const UserSettings = () => { const Password = () => { const mismatch = passwordData.confirmPassword && - passwordData.newPassword !== passwordData.confirmPassword; + !stringEqual(passwordData.newPassword, passwordData.confirmPassword); const disabled = !passwordData.currentPassword || !passwordData.newPassword || mismatch; return ( diff --git a/client/src/utils/stringEqual.js b/client/src/utils/stringEqual.js new file mode 100644 index 00000000..019f51cf --- /dev/null +++ b/client/src/utils/stringEqual.js @@ -0,0 +1,18 @@ +/** + * Compares two strings in constant time using XOR. + * + * @param {string} a - The first string to compare + * @param {string} b - The second string to compare + * + * @returns {boolean} True if the strings are equal, false otherwise + */ +export function stringEqual(a, b) { + if (a.length !== b.length) { + return false; + } + let diff = 0; + for (let i = 0; i < a.length; i++) { + diff |= a.charCodeAt(i) ^ b.charCodeAt(i); + } + return diff === 0; +} diff --git a/load-tests/scenarios/admin.js b/load-tests/scenarios/admin.js index 7088e7d7..1b758e45 100644 --- a/load-tests/scenarios/admin.js +++ b/load-tests/scenarios/admin.js @@ -2,14 +2,16 @@ import http from 'k6/http'; import { check, sleep } from 'k6'; -const BASE_URL = 'http://localhost:8000'; +const BASE_URL = __ENV.BASEURL || 'http://localhost:8000'; +const TEST_EMAIL = __ENV.TEST_EMAIL; +const TEST_PASSWORD = __ENV.TEST_PASSWORD; export default function () { // Test login. let loginResult = http.post(`${BASE_URL}/login`, JSON.stringify({ - email: 'SHP_Admin@example.com', - password: 'password12345678', + email: TEST_EMAIL, + password: TEST_PASSWORD, }), { headers: { 'Content-Type': 'application/json' }, }); diff --git a/load-tests/scenarios/merchant.js b/load-tests/scenarios/merchant.js index 163fb320..fe537e53 100644 --- a/load-tests/scenarios/merchant.js +++ b/load-tests/scenarios/merchant.js @@ -1,15 +1,18 @@ import http from "k6/http"; import { check, sleep } from "k6"; -const BASE_URL = "http://localhost:8000"; + +const BASE_URL = __ENV.BASEURL || 'http://localhost:8000'; +const TEST_EMAIL = __ENV.TEST_EMAIL; +const TEST_PASSWORD = __ENV.TEST_PASSWORD; export default function () { // Test login. let loginResult = http.post( `${BASE_URL}/login`, JSON.stringify({ - email: "service@example.com", - password: "thisismypassword", + email: TEST_EMAIL, + password: TEST_PASSWORD, }), { headers: { "Content-Type": "application/json" }, diff --git a/load-tests/scenarios/user.js b/load-tests/scenarios/user.js index 4e2b0941..6b042f38 100644 --- a/load-tests/scenarios/user.js +++ b/load-tests/scenarios/user.js @@ -1,15 +1,18 @@ import http from "k6/http"; import { check, sleep } from "k6"; -const BASE_URL = "http://localhost:8000"; + +const BASE_URL = __ENV.BASEURL || 'http://localhost:8000'; +const TEST_EMAIL = __ENV.TEST_EMAIL; +const TEST_PASSWORD = __ENV.TEST_PASSWORD; export default function () { // Test login. let loginResult = http.post( `${BASE_URL}/login`, JSON.stringify({ - email: "audrey.young@example.com", - password: "whyaretherebirds", + email: TEST_EMAIL, + password: TEST_PASSWORD, }), { headers: { "Content-Type": "application/json" }, diff --git a/server/routers/authentication.py b/server/routers/authentication.py index 271f9205..c6208535 100644 --- a/server/routers/authentication.py +++ b/server/routers/authentication.py @@ -334,6 +334,7 @@ async def login(request: Request, response: Response, user_cred: LoginCredential cookie_settings = _cookie_security_settings(request) + # bearer:disable python_django_cookies response.set_cookie( key='auth_token', value=token, diff --git a/server/routers/health_prediction.py b/server/routers/health_prediction.py index 6a68803f..28eb6f86 100644 --- a/server/routers/health_prediction.py +++ b/server/routers/health_prediction.py @@ -7,6 +7,7 @@ from sqlalchemy.orm import Session import csv import codecs +import logging from ..utils.database import get_db from ..models.dbmodels import HealthData, Prediction, Recommendation, UserAccount, UserPatientAccess, Patient, LogEventType @@ -15,6 +16,8 @@ from ..utils.audit_log import write_audit_log +logger = logging.getLogger(__name__) + # HealthData class HealthDataInput(CamelModel): age: int @@ -233,8 +236,9 @@ async def predict(data: HealthDataInput, request: Request, db_conn: Session = De db_conn=db_conn, health_data_id=int(_hd_id)) else: recommendations = {"error": "Missing HealthDataID"} - except Exception as e: - recommendations = {"error": str(e)} + except Exception: + logger.error("Health recommendations failed to generate.") + recommendations = None # Persist recommendations when available exercise_rec = None @@ -539,8 +543,9 @@ async def merchant_predict(data: MerchantHealthDataInput, request: Request, db_c db_conn=db_conn, health_data_id=int(_hd_id)) else: recommendations = {"error": "Missing HealthDataID"} - except Exception as e: - recommendations = {"error": str(e)} + except Exception: + logger.error("Health recommendations failed to generate.") + recommendations = None # Persist recommendations when available exercise_rec = None diff --git a/server/tools/generate_dummy_data.py b/server/tools/generate_dummy_data.py index d7c6c11b..4384d30e 100644 --- a/server/tools/generate_dummy_data.py +++ b/server/tools/generate_dummy_data.py @@ -76,6 +76,7 @@ class UserRoleID(Enum): def load_names_csv(filename: str): '''Loads all the names in the CSV file to use in the dummy data''' + # bearer:disable python_lang_path_traversal with open(filename, encoding="utf-8") as csv_file: csv_reader = csvReader(csv_file, delimiter=',') for row in csv_reader: