Skip to content

Security: phase 2 SQL Injection remediation#2400

Merged
LiamStanziani merged 17 commits intomaintenancefrom
security/phase-2-sqli-remediation
Apr 15, 2026
Merged

Security: phase 2 SQL Injection remediation#2400
LiamStanziani merged 17 commits intomaintenancefrom
security/phase-2-sqli-remediation

Conversation

@LiamStanziani
Copy link
Copy Markdown
Collaborator

@LiamStanziani LiamStanziani commented Apr 2, 2026

Summary

Phase 2 of SQL injection remediation across 148 files. Converts string-concatenated SQL to parameterized queries using PreparedStatement with ? bind parameters, and adds allowlist validation for dynamic SQL identifiers (table names, column names, sort orders) that cannot be parameterized.

Problem

Numerous DAO classes, form record classes, report generators, billing modules, and JSPs constructed SQL queries via string concatenation of user-supplied or request-derived values. This exposed the application to SQL injection attacks — flagged by Snyk as HIGH and MEDIUM severity findings across multiple code paths including:

  • Form record classes (~50 files) — demographic_no concatenated directly into queries
  • Report-by-template system — user-supplied report parameters substituted directly into SQL templates
  • Billing modules (BC MSP, ON) — provider numbers and invoice IDs concatenated into queries
  • Lookup actions — table IDs and codes from HTTP parameters used in dynamic queries
  • EForm/DatabaseAP system — template field substitution without parameterization
  • JSP files — inline SQL with request parameters concatenated directly

Solution

Core utilities added/extended:

  • SqlUtils — validation methods (validateNumericId, validateTableName, validateColumnName, validateSortColumn, inClausePlaceholders) for cases where
    parameterization isn't possible (dynamic table/column names, IN clauses)
  • PreparedSQL — value object pairing a parameterized SQL string with its bind parameters, used by the report template system
  • FrmRecordHelp — parameterized overloads of getResultSet/saveFrm delegating to DBHandler.GetPreSQL/GetPreSQLUpdatable
  • DBHandler.GetPreSQL / GetPreSQLUpdatable — centralized parameterized query execution

Parameterization approach by area:

  • Form records (~50 Frm*Record.java files): Switched from DBHandler.GetSQL("...'" + demoNo + "'...") to FrmRecordHelp.getResultSet(sql, demoNo) with
    ? placeholders
  • DAO layer: Converted native SQL queries to use named parameters (:paramName) or positional ? placeholders with PreparedStatement
  • Report templates (ReportObjectGeneric): New getParameterizedSQL() replaces {param} markers with ? placeholders and collects bind values, supporting
    single values, multi-value IN clauses, and empty checkbox cases
  • JSPs: Replaced inline concatenated SQL with PreparedStatement usage
  • Lookup actions: Added strict regex validation (^[A-Za-z0-9]{1,10}$) for table IDs; downstream queries already parameterized via Hibernate
  • Billing: Converted to DBHandler.GetPreSQL or inline PreparedStatement with proper parameter binding
  • Dynamic identifiers: Where table/column names must be concatenated (cannot use ?), validated via SqlUtils.validateTableName() / allowlist
    Set.of(...) before inclusion

Remaining Snyk findings: All 20 remaining SQL injection alerts are confirmed false positives — Snyk's taint analysis does not recognize intermediate
validation (regex allowlists, numeric validation) or parameterization (PreparedSQL, parameterizeFields) between the source and sink.

Areas tested manually

  1. EForms with DatabaseAP

Path: Patient chart → eForms → Add eForm → select "Test DatabaseAP"
Verifies: EForm.parameterizeFields, EFormUtil.getValues, PreparedSQL, efmformapconfig_lookup.jsp
Expected: Patient name, DOB, age, address, provider auto-populate

  1. Patient Search

Path: Main screen → Search bar → search by last name, try clicking column headers to sort
Verifies: DemographicDaoImpl.findByField, PatientSearch.jsp
Expected: Results appear, sorting works

  1. Forms (Frm*Record family)

Path: Patient chart → Forms → open any form (BC INR, Growth 0-36, Annual, Mental Health, BCAR, Rourke, Lab Req, etc.)
Verifies: FrmRecordHelp, FrmData.executeWithValidatedTable, all ~50 Frm*Record.java files
Expected: Form loads with patient data, can save without errors

  1. Report by Template

Path: Report → Report by Template → pick a template → fill parameters → Generate
Verifies: ReportObjectGeneric.getParameterizedSQL, SQLReporter, DBHandler.GetPreSQL
Expected: Report generates results, no SQL errors

  1. Clinical Reports

Path: Report → Clinical Reports → select denominator/numerator → Run
Verifies: SQLNumerator.executeParameterizedSQL, SQLDenominator.parameterizeAll
Expected: Report runs, patient list or counts display

  1. Log Report

Path: Administration → Log Report → select a provider → set date range → Submit. Also try "All Providers"
Verifies: logReport.jsp all three query branches
Expected: Log entries display

  1. Ontario Billing Reports

Path: Billing → ON → Billing Report
Verifies: billingONNewReport.jsp, reportbilledvisit1/2/3.jsp, reportonbilledphcp.jsp
Expected: Reports generate with correct data

  1. Messenger / Messages

Path: Msg → view messages → try sorting by different columns
Verifies: MsgViewMessageByPosition2Action
Expected: Messages display, sorting works

  1. Antenatal Planner

Path: Patient chart → Forms → AR form → Scroll to the bottom of the page → click "AR Planner" link
Verifies: antenatalplanner.jsp, antenatalplannerprint.jsp
Expected: Planner loads or gracefully handles missing data

  1. Security Admin Search

Path: Administration → User Management → Search/Edit/Delete Security Records → Search for a providers number or username
Verifies: securitysearchresults.jsp, SecurityDaoImpl
Expected: Search results display

  1. Encounter Display Records

Path: Patient chart → E-Chart → click through left navbar panels (Annual, Mental Health, MMSE, Palliative Care, Peri-Menopausal, Rourke, Type 2
Diabetes, AR)
Verifies: All Ect*Record.java files, EctPatientData
Expected: Each panel loads patient data without errors

  1. Disease Registry

Path: Patient chart → E-Chart → Disease Registry → add or view a diagnosis
Verifies: DxDaoImpl, DxresearchDAOImpl
Expected: Diagnosis search and list works

  1. Inbox

Path: Main screen → Inbox → view lab results, documents
Verifies: InboxResultsRepositoryImpl
Expected: Inbox items load and display

  1. ON Billing Review

Path: Billing → ON → review billing records
Verifies: JdbcBillingReviewImpl, BillingONCHeader1DaoImpl
Expected: Billing review displays records

  1. Patient Letters

Path: Report → Ontario Prevention Report → Query for demographic results → Scroll to the bottom of the page → Generate Patient Letters
Verifies: GeneratePatientLetters2Action
Expected: Letter generation works

  1. Form Navigation

Path: Patient chart → Forms → open a form via shortcut name, or navigate between forms
Verifies: FormForward2Action, FrmForm2Action, FrmSetupForm2Action
Expected: Forms open to the correct form type for the patient

  1. Pregnancy Forms

Path: Patient chart → Forms → ONAR or pregnancy-related form
Verifies: PregnancyFormsDao
Expected: Form loads pregnancy data

  1. EForm Report Tool

Path: Administration → Reports → EForm Report Tool → Add New eForm
Verifies: EFormReportToolDaoImpl
Expected: Report queries work


Summary by cubic

Phase 2 SQL injection hardening across billing, eForms, DAOs, and form records. Replaced concatenated SQL with parameterized queries, validated dynamic identifiers, added safe ORDER BY/IN builders, and removed unused deprecated SQL helpers. DatabaseAP templates now use PreparedSQL; most paths use DBHandler.GetPreSQL/GetPreSQLUpdatable and parameterized FrmRecordHelp.

  • Bug Fixes
    • Billing: MSP reconcile/report queries now parameterized (ParameterizedCriteria); validated rano; fixed provider/payee and service code lookups; updated WCB view/reprocess actions; removed unsafe invoice report methods in JdbcBillingReviewImpl.
    • eForms: APExecute/EForm build PreparedSQL from templates (stripped template quotes); EFormUtil adds ORDER BY whitelist with tiebreakers; FetchUpdatedData2Action validates demographic, provider, uuid.
    • DAOs/Utilities: Removed SecurityDao.findAllOrderBy(String) in favor of findAllOrderByUserName; deleted BillingONCHeader1Dao.findBillingData; added whitelists/validation and parameterized queries across DemographicDaoImpl, DxresearchDAOImpl, DxDaoImpl, ProviderDaoImpl, BillingBCDaoImpl, Billing3rdPartyAddressDaoImpl, InboxResultsRepositoryImpl, EFormReportToolDaoImpl, LookupDaoImpl, PregnancyFormsDao; parameterized importCasemgmt.
    • Forms/Encounter: Migrated dozens of Frm*/Ect* records to DBHandler.GetPreSQL; FrmRecordHelp.getFormRecord(sql, ...) binds params and maps types safely; added DBHandler.GetPreSQLUpdatable for updatable cursors.

Written for commit db43ea5. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Enhanced input validation for numeric IDs, table names, and column identifiers across database queries to prevent invalid data processing.
    • Improved error handling for malformed request parameters in reporting and billing features.
  • Chores

    • Deprecated unsafe legacy database query methods; developers should migrate to updated parameterized query methods.
    • Updated internal database query handling to use safer execution patterns throughout the application.
    • Hardened query construction with whitelisting of valid sort columns and table names.

@LiamStanziani LiamStanziani requested a review from Copilot April 2, 2026 20:31
@LiamStanziani LiamStanziani self-assigned this Apr 2, 2026
Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @LiamStanziani, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

This PR implements comprehensive SQL injection prevention by systematically converting string-concatenated SQL queries to parameterized prepared statements across the entire codebase. It introduces new utility classes for SQL validation, updates over 150 files including DAOs, business logic, form handlers, utilities, and JSP pages with parameterized query execution, and adds validation for user-controlled identifiers and SQL parameters.

Changes

Cohort / File(s) Summary
Core Database Handler & Utilities
src/main/java/ca/openosp/openo/db/DBHandler.java, src/main/java/ca/openosp/openo/util/PreparedSQL.java, src/main/java/ca/openosp/openo/util/SqlUtils.java
Added new GetPreSQLUpdatable method and created PreparedSQL class to encapsulate parameterized SQL with bind parameters. Introduced SqlUtils with 8+ new validation/execution methods for numeric IDs, table/column names, sort columns, IN clause placeholders, and parameterized query execution.
DAO Layer - Import/Migration
database/mysql/importCasemgmt.java, src/main/java/ca/openosp/openo/PMmodule/dao/ProviderDaoImpl.java, src/main/java/ca/openosp/openo/commn/dao/BillingONCHeader1Dao.java, src/main/java/ca/openosp/openo/commn/dao/BillingONCHeader1DaoImpl.java, src/main/java/ca/openosp/openo/commn/dao/SecurityDao.java, src/main/java/ca/openosp/openo/commn/dao/SecurityDaoImpl.java
Deprecated legacy raw-SQL methods (e.g., findBillingData, findAllOrderBy with dynamic column names), replaced with dedicated parameterized methods. Converted dynamic queries to use ? placeholders and parameter binding via Hibernate/JPA setParameter.
DAO Layer - Billing & Lookup
src/main/java/ca/openosp/openo/billings/ca/bc/MSP/CDMReminderHlp.java, src/main/java/ca/openosp/openo/billings/ca/bc/MSP/CreateBillingReport2Action.java, src/main/java/ca/openosp/openo/billings/ca/bc/MSP/MSPReconcile.java, src/main/java/ca/openosp/openo/billings/ca/bc/pageUtil/ViewWCB2Action.java, src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java, src/main/java/ca/openosp/openo/commn/dao/Billing3rdPartyAddressDaoImpl.java, src/main/java/ca/openosp/openo/commn/dao/BillingBCDaoImpl.java, src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java
Parameterized all billing queries: IN clauses with placeholder arrays, WHERE clause predicates with ? placeholders, pagination via setFirstResult/setMaxResults instead of string LIMIT. Added whitelist validation for dynamic table/column names in lookup and billing 3rd-party address queries.
DAO Layer - Demographics & Research
src/main/java/ca/openosp/openo/commn/dao/DemographicDaoImpl.java, src/main/java/ca/openosp/openo/commn/dao/DxDaoImpl.java, src/main/java/ca/openosp/openo/commn/dao/DxresearchDAOImpl.java, src/main/java/ca/openosp/openo/commn/dao/SiteDaoImpl.java
Updated HQL/JPQL to use positional parameters (?1, ?2) instead of string concatenation. Added NATIVE_ORDER_FIELDS map for safe ordering. Replaced regex validation with enum-based codingSystem whitelist. Converted multi-condition searches to parameterized disjunctions.
DAO Layer - EForm & Results
src/main/java/ca/openosp/openo/commn/dao/EFormReportToolDaoImpl.java, src/main/java/ca/openosp/openo/commn/dao/InboxResultsRepositoryImpl.java, src/main/java/ca/openosp/openo/commn/dao/PregnancyFormsDao.java
Added table/column name validation via SqlUtils before dynamic SQL construction. Parameterized all WHERE clause predicates (?1 style). Implemented safe ORDER BY whitelisting with deterministic tie-breaker columns (segment_id DESC, lab_type ASC). Added explicit ResultSet closing in parameterized methods.
Billing Module - Ontario & Reconciliation
src/main/java/ca/openosp/openo/billings/ca/on/data/JdbcBillingReviewImpl.java, src/main/java/ca/openosp/openo/billings/ca/on/data/JdbcBillingReviewImpl.java
Removed 2 legacy overloads that built raw-SQL temp conditions via string concatenation and manually mapped String[] rows. Replaced with exclusive use of parameterized JPA-based overloads using BillingONCHeader1 entity and optional result sorting.
EForm/AP Module
src/main/java/ca/openosp/openo/eform/APExecute.java, src/main/java/ca/openosp/openo/eform/EFormUtil.java, src/main/java/ca/openosp/openo/eform/actions/FetchUpdatedData2Action.java, src/main/java/ca/openosp/openo/eform/data/EForm.java
Added parameterization of AP template variables (${demographic}, ${providers}, ${uuid}) to ? placeholders. Introduced parameterizeTemplate/parameterizeFields methods. Added sort-column whitelisting and new overloads of getValues/getJsonValues accepting Object... params. Validated template inputs via regex/SqlUtils.
Encounter Form Records
src/main/java/ca/openosp/openo/encounter/data/Ect*.java (11 files: ARRecord, AlphaRecord, AnnualRecord, MMSERecord, MentalHealthRecord, PalliativeCareRecord, PatientData, PeriMenopausalRecord, RourkeRecord, Type2DiabetesRecord)
Systematically converted all demographic/record-ID lookups from string concatenation to DBHandler.GetPreSQL(sql, demographicNo, ...) / GetPreSQLUpdatable(sql, ...) with parameterized WHERE clauses. Replaced LAST_INSERT_ID() retrieval from GetSQL to GetPreSQL.
Form Records - General
src/main/java/ca/openosp/openo/form/Frm*.java (40+ form record classes: 2MinWalk, ARBloodWorkTest, ARRecord, Adf, AnnualV2, BCAR*, BCAnnual, BCHPRecord, BCINRRecord, BCNewBorn*, BCAnnual, CaregiverRecord, CESD, ConsultantRecord, CostQuestionnaire, Counseling, CounsellorAssessment, DischargeSummary, Falls, GripStrength, Growth0_36, GrowthChart, HomeFalls, ImmunAllergy, IntakeHx, IntakeInfo, InternetAccess, Invoice, LabReq*, LateLifeFDI*, MMSERecord, MentalHealthForm*, MentalHealthRecord, OvulationRecord, PalliativeCare, PeriMenopausal, Policy, PositionHazard, ReceptionAssessment, RhImmuneGlobulin, Rourke*, SF36*, SatisfactionScale, SelfAdministered, SelfAssessment, SelfEfficacy, SelfManagement, TreatmentPref, Type2Diabete, chf)
Bulk conversion: replaced all demographic_no/existingID/ID string concatenation with parameterized WHERE demographic_no = ? AND ID = ? queries. Updated FrmRecordHelp calls to pass parameters as additional arguments (e.g., saveFormRecord(props, sql, demographic_no) instead of saveFormRecord(props, sql)).
Form Records - Helpers & Support
src/main/java/ca/openosp/openo/form/FrmRecordHelp.java, src/main/java/ca/openosp/openo/form/data/FrmData.java, src/main/java/ca/openosp/openo/form/pageUtil/FormForward2Action.java, src/main/java/ca/openosp/openo/form/pageUtil/FrmForm2Action.java, src/main/java/ca/openosp/openo/form/pageUtil/FrmSetupForm2Action.java, src/main/java/ca/openosp/openo/form/pageUtil/FrmXmlUpload2Action.java
Deprecated unparameterized FrmRecordHelp overloads and added parameterized variants accepting Object... params. Introduced executeWithValidatedTable helper for dynamic table names. Added table/column name validation via SqlUtils.validateTableName. Added validation comments and assertions for demographic/form-name inputs.
Hospital Report Manager
src/main/java/ca/openosp/openo/hospitalReportManager/dao/HRMDocumentDao.java, src/main/java/ca/openosp/openo/hospitalReportManager/v2018/HRM2Action.java
Replaced manual ORDER BY validation with allowlist (ORDER_ASC_FRAGMENTS, ORDER_DESC_FRAGMENTS). Refactored query building to use buildQueryHql with fixed HQL + safe order fragments. Validated orderingColumnDirection against whitelist ("ASC", "DESC").
Lab & Report Modules
src/main/java/ca/openosp/openo/lab/ca/bc/PathNet/PathNetInfo.java, src/main/java/ca/openosp/openo/managers/SecurityManager.java, src/main/java/ca/openosp/openo/messenger/docxfer/send/MsgGenerate.java, src/main/java/ca/openosp/openo/messenger/pageUtil/MsgViewMessageByPosition2Action.java, src/main/java/ca/openosp/openo/report/ClinicalReports/SQLDenominator.java, src/main/java/ca/openosp/openo/report/ClinicalReports/SQLNumerator.java, src/main/java/ca/openosp/openo/report/data/Rpt*.java
Parameterized all lab/report queries: provider_no=?, demographic_no=?, report_id=?. Added safe ORDER BY resolution for message queries. Updated DBHandler calls from GetSQL to GetPreSQL. Replaced findAllOrderBy("user_name") with dedicated findAllOrderByUserName() method.
Report Generation - Templates
src/main/java/ca/openosp/openo/report/reportByTemplate/ReportObject.java, src/main/java/ca/openosp/openo/report/reportByTemplate/ReportObjectGeneric.java, src/main/java/ca/openosp/openo/report/reportByTemplate/SQLReporter.java
Added new parameterized API: getParameterizedSQL(Map) / getParameterizedSQL(int, Map) replacing {param} markers with ? placeholders and collecting bind values. Introduced missing-parameter detection (isMissingParams()). Updated report execution to use DBHandler.GetPreSQL(sql, prepared.getParamsArray()).
Utility Classes
src/main/java/ca/openosp/openo/util/JDBCUtil.java, src/main/java/ca/openosp/openo/www/lookup/LookupCodeEdit2Action.java, src/main/java/ca/openosp/openo/www/lookup/LookupCodeList2Action.java, src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java
Added VALID_NAME_PATTERN whitelist and prepareWithValidatedTable helper for dynamic table construction. Introduced table ID validation patterns (^[A-Za-z0-9]{1,10}$). Updated all lookup queries to parameterize user inputs and validate identifiers before use.
JSP Pages - Billing & Reports
src/main/webapp/admin/logReport.jsp, src/main/webapp/admin/securitysearchresults.jsp, src/main/webapp/billing/CA/BC/onSearch3rdBillAddr.jsp, src/main/webapp/billing/CA/ON/billingONNewReport.jsp, src/main/webapp/decision/antenatal/antenatalplanner.jsp, src/main/webapp/decision/antenatal/antenatalplannerprint.jsp, src/main/webapp/eform/efmformapconfig_lookup.jsp, src/main/webapp/oscarMDS/PatientSearch.jsp, src/main/webapp/report/reportResult.jsp, src/main/webapp/report/reportbilledvisit*.jsp, src/main/webapp/report/reportonbilledphcp*.jsp
Systematically parameterized all JSP server-side SQL: replaced string concatenation of sdate, edate, provider_no, content, orderby with ? placeholders and parameter arrays passed to dbObj.queryResults(sql, params). Added whitelist validation for ORDER BY columns. Added numeric validation for report IDs and form IDs before query execution.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

This is a large-scale, high-complexity refactoring affecting 150+ files with heterogeneous changes (DAOs, business logic, JSP, utilities, new APIs). Multiple patterns applied (parameterized queries, validation logic, API deprecations, new helper methods) across diverse modules require careful verification of parameter binding correctness, deprecated method migration, and potential behavioral changes in dynamic query construction.

Possibly related PRs

Suggested labels

security, sql-injection-prevention, database, large-refactor, breaking-change

Suggested reviewers

  • D3V41

Poem

🐰 Hops through SQL with prepared care,
No more strings that float in air—
Parameters bind where quotes once roamed,
Injection attacks now find no home! 🛡️✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security/phase-2-sqli-remediation

@LiamStanziani LiamStanziani changed the title [WIP] Security/phase 2 sqli remediation [WIP] Security: Phase 2 sqli remediation Apr 2, 2026
@LiamStanziani
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@LiamStanziani LiamStanziani changed the title [WIP] Security: Phase 2 sqli remediation Security: Phase 2 sqli remediation Apr 2, 2026
@LiamStanziani LiamStanziani changed the title Security: Phase 2 sqli remediation Security: phase 2 sqli remediation Apr 2, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements extensive SQL injection mitigations by replacing raw SQL concatenation with parameterized queries and introducing a PreparedSQL utility. It also adds regex-based whitelisting for dynamic identifiers like table and column names. Feedback highlights several critical resource management issues where database objects are not closed, as well as thread-safety concerns and logic bugs in PostgreSQL-specific code and report template processing.

Comment thread src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java Outdated
Comment thread src/main/java/ca/openosp/openo/billings/MSP/dbExtract.java Outdated
Comment thread src/main/java/ca/openosp/openo/db/DBHandler.java
Comment thread src/main/java/ca/openosp/openo/form/FrmRecordHelp.java Outdated
Comment thread src/main/java/ca/openosp/openo/util/JDBCUtil.java Outdated
Comment thread database/mysql/importCasemgmt.java
Comment thread database/mysql/importCasemgmt.java
Comment thread src/main/java/ca/openosp/openo/report/reportByTemplate/ReportObjectGeneric.java Outdated
@LiamStanziani LiamStanziani changed the title Security: phase 2 sqli remediation Security: phase 2 SQL Injection remediation Apr 2, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the “phase 2” SQL-injection remediation work by replacing string-concatenated SQL with parameterized queries and by adding allowlist validation where dynamic SQL fragments (e.g., ORDER BY) are unavoidable.

Changes:

  • Converted multiple JSP SQL statements from string concatenation to prepared/parameterized execution.
  • Added allowlist validation for dynamic ordering/column selection inputs (e.g., orderby, lookup table IDs).
  • Introduced/extended reusable parameterized-SQL helpers (PreparedSQL) and added stricter validation for dynamic table usage in JDBCUtil.

Reviewed changes

Copilot reviewed 149 out of 149 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/main/webapp/report/reportResult.jsp Validates id as numeric before use to reduce injection risk.
src/main/webapp/report/reportbilledvisit1.jsp Converts date/provider query fragments to parameterized queries.
src/main/webapp/oscarMDS/PatientSearch.jsp Adds orderby allowlist to prevent injection via ORDER BY.
src/main/webapp/eform/efmformapconfig_lookup.jsp Switches eForm AP SQL substitution to parameterized execution via parameterizeFields.
src/main/webapp/decision/antenatal/antenatalplannerprint.jsp Uses prepared query with bound ID parameter.
src/main/webapp/decision/antenatal/antenatalplanner.jsp Uses prepared query with bound ID parameter.
src/main/webapp/billing/CA/BC/onSearch3rdBillAddr.jsp Adds allowlists + parameter binding for search and ordering.
src/main/webapp/admin/securitysearchresults.jsp Replaces dynamic order-by DAO call with a fixed safe DAO method.
src/main/webapp/admin/logReport.jsp Parameterizes provider/site filtering and content filter in log queries.
src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java Adds tableId validation for defense-in-depth on lookup routing.
src/main/java/ca/openosp/openo/www/lookup/LookupCodeList2Action.java Adds tableId validation for lookup list retrieval.
src/main/java/ca/openosp/openo/www/lookup/LookupCodeEdit2Action.java Adds validation for lookup tableId/code identifiers.
src/main/java/ca/openosp/openo/util/PreparedSQL.java Introduces a value object for SQL text + ordered bind params.
src/main/java/ca/openosp/openo/util/JDBCUtil.java Validates dynamic table names and uses prepared statements for zip-import DB operations.
Comments suppressed due to low confidence (1)

src/main/webapp/oscarMDS/PatientSearch.jsp:231

  • limit1/limit2 are taken directly from the request and used to build a limit ... offset ... SQL fragment without validation, but that fragment is currently unused (the + " " + limit part is commented out). To avoid future accidental reintroduction of an injection vector and reduce dead code, either remove these limit variables entirely or validate them as integers and apply paging via a safe API (e.g., setFirstResult/setMaxResults or parameterized LIMIT/OFFSET where supported).

Comment thread src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java Outdated
Comment thread src/main/java/ca/openosp/openo/util/JDBCUtil.java Outdated
@LiamStanziani LiamStanziani requested a review from Copilot April 6, 2026 14:24
@LiamStanziani
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 148 out of 148 changed files in this pull request and generated 6 comments.

Comment thread src/main/webapp/decision/antenatal/antenatalplannerprint.jsp
Comment thread src/main/webapp/decision/antenatal/antenatalplanner.jsp
Comment thread src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java
Comment thread src/main/webapp/eform/efmformapconfig_lookup.jsp Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (18)
src/main/webapp/admin/securitysearchresults.jsp (1)

150-159: ⚠️ Potential issue | 🟡 Minor

Potential NullPointerException if query parameters are missing.

If keyword or search_mode request parameters are null, this code will throw NPE at lines 151 and 154. Consider adding null checks.

🛡️ Proposed null-safety fix
             //if action is good, then give me the result
             String searchMode = request.getParameter("search_mode");
-            String keyword = request.getParameter("keyword").trim() + "%";
+            String keywordParam = request.getParameter("keyword");
+            String keyword = (keywordParam != null ? keywordParam.trim() : "") + "%";

             // if search mode is provider_no
-            if (searchMode.equals("search_providerno"))
+            if ("search_providerno".equals(searchMode))
                 securityList = securityDao.findByLikeProviderNo(keyword);

             // if search mode is user_name
-            if (searchMode.equals("search_username"))
+            if ("search_username".equals(searchMode))
                 securityList = securityDao.findByLikeUserName(keyword);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/admin/securitysearchresults.jsp` around lines 150 - 159, The
code reads request parameters into searchMode and keyword and calls
keyword.trim() and searchMode.equals(...) which can throw NullPointerException;
update the handling in this block so you null-check both
request.getParameter("search_mode") and request.getParameter("keyword") before
using them, provide sensible defaults (e.g., empty string) or early-return when
missing, and only call keyword.trim() + "%" when keyword is non-null/non-empty;
then branch on searchMode safely (use "searchMode != null &&
searchMode.equals(...)" or switch on a normalized value) before invoking
securityDao.findByLikeProviderNo or findByLikeUserName so those DAO methods are
only called with a valid keyword.
database/mysql/importCasemgmt.java (1)

71-72: ⚠️ Potential issue | 🔴 Critical

Critical: Remove database password from log output.

Logging credentials to stdout is a severe security vulnerability. This exposes sensitive authentication data in logs, terminals, and potentially log aggregation systems.

Proposed fix
-                        String passwd = prop.getProperty("db_password");
-                        System.out.println("DB PASSWD " + passwd);
+                        String passwd = prop.getProperty("db_password");
+                        // Password intentionally not logged for security
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@database/mysql/importCasemgmt.java` around lines 71 - 72, The code is
printing the database password to stdout via System.out.println("DB PASSWD " +
passwd); — remove that line and any other logging of the sensitive variable
passwd (obtained from prop.getProperty("db_password")). Replace it with a
non-sensitive log (e.g., an informational message that the DB password was
loaded or omit logging entirely) or use a secure logger without including
secrets; ensure functions/classes referencing passwd (the variable in
importCasemgmt.java) do not expose it in logs or exceptions.
src/main/java/ca/openosp/openo/commn/dao/SiteDaoImpl.java (1)

242-249: ⚠️ Potential issue | 🔴 Critical

Critical bug: Named parameter placeholders with positional binding will fail at runtime.

The query uses named parameters (:groupno, :sitename) but the setParameter calls use positional indices (1, 2). This mismatch will throw an IllegalArgumentException at runtime.

Since this PR is addressing parameterization across DAOs, this should be fixed for consistency.

🐛 Proposed fix: Use indexed positional parameters
     `@Override`
     public Long site_searchmygroupcount(String myGroupNo, String siteName) {
-        Query query = entityManager.createNativeQuery("select count(provider_no) from mygroup where mygroup_no=:groupno  and provider_no in (select ps.provider_no from providersite ps inner join site s on ps.site_id = s.site_id where s.name = :sitename)");
-        query.setParameter(1, myGroupNo);
-        query.setParameter(2, siteName);
+        Query query = entityManager.createNativeQuery("select count(provider_no) from mygroup where mygroup_no = ?1 and provider_no in (select ps.provider_no from providersite ps inner join site s on ps.site_id = s.site_id where s.name = ?2)");
+        query.setParameter(1, myGroupNo);
+        query.setParameter(2, siteName);

         Long result = ((BigInteger) query.getSingleResult()).longValue();
         return result;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/commn/dao/SiteDaoImpl.java` around lines 242 -
249, The method site_searchmygroupcount uses a native query with named
parameters (:groupno, :sitename) but binds them positionally; update the
parameter binding in site_searchmygroupcount to use the named parameter API
(call query.setParameter("groupno", myGroupNo) and
query.setParameter("sitename", siteName)) so the names used in the SQL match the
names used when setting parameters on the Query returned by
entityManager.createNativeQuery.
src/main/java/ca/openosp/openo/commn/dao/DxresearchDAOImpl.java (1)

399-419: ⚠️ Potential issue | 🟡 Minor

Enum validation pattern is correct, but sanitize the log output.

The approach of validating codingSystem against an enum before using it in dynamic table/column names is the correct pattern for this use case (parameterized queries cannot be used for identifiers). The actual data values are properly parameterized.

However, line 406 logs the invalid codingSystem value directly without sanitization. While codingSystem is unlikely to contain PHI, defensive coding requires sanitizing all user input before logging.

🛡️ Proposed fix to sanitize log output
-            logger.error("Invalid coding system provided: " + codingSystem);
+            logger.error("Invalid coding system provided: " + org.owasp.encoder.Encode.forJava(codingSystem));

Based on coding guidelines: "Sanitize user input for logging using Encode.forJava() to prevent log injection"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/commn/dao/DxresearchDAOImpl.java` around lines
399 - 419, In findResearchAndCodingSystemByDemographicAndCondingSystem validate
codingSystem as you already do with
AbstractCodeSystemDaoImpl.codingSystem.valueOf(...), but when logging the
invalid value use a sanitized form (e.g., Encode.forJava(codingSystem)) instead
of logging raw input; update the logger.error call to include the
encoded/sanitized codingSystem (or a minimal safe message) so you avoid possible
log injection while keeping the same validation flow.
src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java (1)

66-76: ⚠️ Potential issue | 🟠 Major

This action still executes lookup reads without an authorization gate.

Both list() and search() now validate tableId, but neither path performs SecurityInfoManager.hasPrivilege() before loading lookup data. Please enforce the privilege check at the start of execute() before dispatching.

As per coding guidelines, "ALL actions MUST include SecurityInfoManager.hasPrivilege() checks before performing operations" and "Security check must be the FIRST operation in action methods - before any business logic".

Also applies to: 91-93

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java` around
lines 66 - 76, Add a SecurityInfoManager.hasPrivilege() check as the very first
operation in the execute() method (before any request parameter reads,
validateTableId calls, or dispatch to search()/list()), and return or throw
appropriately when the privilege is missing; ensure the same first-operation
security gating covers both flows dispatched to list() and search() (and
likewise ensure any duplicate entry points that load lookup data such as the
code referenced around the search/list calls also perform the same check). Use
the SecurityInfoManager.hasPrivilege(...) call and place it at the top of
execute() so no business logic (including request.getParameter or
validateTableId in list()/search()) runs without the privilege check.
src/main/java/ca/openosp/openo/messenger/docxfer/send/MsgGenerate.java (1)

235-247: ⚠️ Potential issue | 🔴 Critical

SQL injection vulnerability: Config attributes must not be directly concatenated into SQL queries.

The sqlWhere and sqlOrder attributes from the XML configuration are appended directly to the SQL string (lines 241-246), violating the parameterized query requirement. Even if DocXferConfig.xml is protected on the server, config files can be compromised and should never be trusted as safe for SQL concatenation. These attributes must be validated and either:

  1. Excluded from the query entirely, or
  2. Properly parameterized (added as bind parameters), or
  3. Validated against a whitelist of allowed column/clause patterns

The coding guidelines require: "Use parameterized queries ONLY - never use string concatenation in SQL queries."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/messenger/docxfer/send/MsgGenerate.java`
around lines 235 - 247, The code in MsgGenerate that appends
cfgTable.getAttribute("sqlWhere") and cfgTable.getAttribute("sqlOrder") directly
to the sql String creates an SQL injection risk; instead, change the logic that
builds the sql variable so that sqlWhere is not concatenated raw — either remove
it or parse and validate it against a strict whitelist of allowed column
names/operators and translate each allowed filter into bindable predicates with
PreparedStatement parameters (use setX on the PreparedStatement created after
building the parameterized WHERE clause), and for sqlOrder, only allow a
validated whitelist of column names and an optional direction (ASC/DESC) and
then append the validated ORDER BY fragment; update code references around
cfgTable, getAttribute("sqlWhere"), getAttribute("sqlOrder"), and the
PreparedStatement creation in MsgGenerate to use the parameterized/validated
approach rather than raw concatenation.
src/main/java/ca/openosp/openo/www/lookup/LookupCodeEdit2Action.java (1)

50-55: ⚠️ Potential issue | 🟠 Major

Missing SecurityInfoManager.hasPrivilege() check before business logic.

Per coding guidelines, all actions must include SecurityInfoManager.hasPrivilege() checks before performing operations. The execute() method proceeds directly to save() or loadCode() without verifying user privileges.

🛡️ Suggested fix
+    private SecurityInfoManager securityInfoManager = SpringUtils.getBean(SecurityInfoManager.class);
+
     public String execute() throws Exception {
+        if (!securityInfoManager.hasPrivilege(LoggedInInfo.getLoggedInInfoFromSession(request), "_admin", "r", null)) {
+            throw new SecurityException("Missing required privilege for lookup management");
+        }
         if ("save".equals(request.getParameter("method"))) {
             return save();
         }
         return loadCode();
     }

As per coding guidelines: "ALL actions MUST include SecurityInfoManager.hasPrivilege() checks before performing operations" and "Security check must be the FIRST operation in action methods - before any business logic".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/www/lookup/LookupCodeEdit2Action.java` around
lines 50 - 55, In LookupCodeEdit2Action.update the execute() method to call
SecurityInfoManager.hasPrivilege(...) as the very first operation (before any
business logic) and short-circuit if the check fails; specifically, add the
privilege check at the top of execute() and only proceed to call save() or
loadCode() when hasPrivilege() returns true, returning the appropriate
no-privilege result (or handling per app convention) when it returns false.
src/main/java/ca/openosp/openo/commn/dao/PregnancyFormsDao.java (1)

13-27: ⚠️ Potential issue | 🟡 Minor

SQL injection fix is correct; resource leak in exception path.

The parameterized query correctly prevents SQL injection. However, if rs.next() or rs.getInt("id") throws an exception that isn't SQLException, or if a SQLException occurs after rs is obtained, the ResultSet won't be closed. The catch block at line 23-25 returns without closing rs.

🛡️ Suggested fix using try-with-resources
     public static Integer getLatestFormIdByPregnancy(Integer episodeId) {
         String sql = "SELECT id from formONAREnhancedRecord WHERE episodeId = ? ORDER BY formEdited DESC";
-        try {
-            ResultSet rs = DBHandler.GetPreSQL(sql, episodeId);
+        try (ResultSet rs = DBHandler.GetPreSQL(sql, episodeId)) {
             if (rs.next()) {
                 Integer id = rs.getInt("id");
-                rs.close();
                 return id;
             }
-            rs.close();
         } catch (SQLException e) {
             MiscUtils.getLogger().error("Error", e);
             return 0;
         }
         return 0;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/commn/dao/PregnancyFormsDao.java` around lines
13 - 27, In getLatestFormIdByPregnancy, the ResultSet obtained from
DBHandler.GetPreSQL(sql, episodeId) may not be closed if an exception occurs;
change the method to acquire the ResultSet in a try-with-resources (e.g., try
(ResultSet rs = DBHandler.GetPreSQL(...)) { ... }) so rs is always closed, move
rs.next()/rs.getInt("id") inside that try block, and keep the existing
SQLException catch that returns 0; reference: getLatestFormIdByPregnancy and
DBHandler.GetPreSQL.
src/main/webapp/report/reportonbilledphcp.jsp (2)

137-141: ⚠️ Potential issue | 🟠 Major

Missing OWASP encoding for user-controlled output creates XSS vulnerabilities.

Multiple user-controlled values are output without encoding:

  • Lines 137, 140: startDate, endDate in form inputs
  • Line 676: providerName in table header
  • Line 686: startDate, endDate in display text
  • Lines 772-774: vServiceCode.get(i), vServiceDesc.get(i) from database

For a security-focused PR, these should use OWASP Encoder.

🛡️ Proposed fix for XSS protection

Add the import at the top of the file:

<%@ page import="org.owasp.encoder.Encode" %>

Then encode outputs:

-<input type="text" name="startDate" id="startDate" value="<%=startDate!=null?startDate:""%>" size="10"
+<input type="text" name="startDate" id="startDate" value="<%=startDate!=null?Encode.forHtmlAttribute(startDate):""%>" size="10"

-<input type="text" name="endDate" id="endDate" value="<%=endDate!=null?endDate:""%>" size="10"
+<input type="text" name="endDate" id="endDate" value="<%=endDate!=null?Encode.forHtmlAttribute(endDate):""%>" size="10"

-<th align="left"><font face="Helvetica" color="white"><%=providerName%>
+<th align="left"><font face="Helvetica" color="white"><%=Encode.forHtml(providerName)%>

-<td>Period: ( <%= startDate %> ~ <%= endDate %> )</td>
+<td>Period: ( <%= Encode.forHtml(startDate) %> ~ <%= Encode.forHtml(endDate) %> )</td>

-<td><%=vServiceCode.get(i)%></td>
-<td><%=vServiceDesc.get(i)%></td>
+<td><%=Encode.forHtml((String)vServiceCode.get(i))%></td>
+<td><%=Encode.forHtml((String)vServiceDesc.get(i))%></td>

As per coding guidelines: "Use Encode.forHtml(), Encode.forHtmlAttribute()... from OWASP Encoder for ALL user inputs in web output"

Also applies to: 676-686, 772-774

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/report/reportonbilledphcp.jsp` around lines 137 - 141, The
JSP outputs several user-controlled values without encoding (startDate, endDate
input values and display text, providerName table header, and
vServiceCode.get(i)/vServiceDesc.get(i) list entries), creating XSS risk; add
the OWASP Encoder import (org.owasp.encoder.Encode) at the top of the file and
replace raw outputs: use Encode.forHtmlAttribute(...) for values rendered inside
attributes (e.g., the value attributes of startDate and endDate inputs) and
Encode.forHtml(...) for values rendered as HTML content (e.g., providerName
header, the displayed startDate/endDate text, and the vServiceCode/vServiceDesc
outputs) so all user-controlled data is safely encoded before rendering.

125-126: ⚠️ Potential issue | 🟡 Minor

Missing CSRF token in POST form.

The form uses method="POST" but lacks CSRF protection.

🛡️ Proposed fix
 <form name="myform" action="reportonbilledphcp.jsp" method="POST"
       onsubmit="return onSub();">
+    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
     <table width="100%" border="0" bgcolor="ivory" cellspacing="1"

As per coding guidelines: "Include CSRF token in all HTML forms"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/report/reportonbilledphcp.jsp` around lines 125 - 126, The
POST form named "myform" (onsubmit handler onSub()) lacks a CSRF token; add a
hidden input containing the server-generated CSRF value (e.g. from request
attribute/session like csrfToken or ${csrfToken}) inside the form and ensure the
server-side handler that processes reportonbilledphcp.jsp validates that token;
update any client-side code in onSub() if it inspects form fields to not strip
the hidden input and ensure token generation/placement uses the same symbol your
server exposes (e.g. request.getAttribute("csrfToken") or
session.getAttribute("CSRF_TOKEN")).
src/main/webapp/eform/efmformapconfig_lookup.jsp (1)

39-53: ⚠️ Potential issue | 🟠 Major

Import OWASP Encoder and use it at all HTML attribute sinks.

This file has multiple XSS encoding violations:

  1. Line 16: request.getParameter("oscarAPCacheLookupType") rendered directly in attribute value without encoding
  2. Line 51: Uses StringEscapeUtils.escapeHtml4() instead of OWASP Encoder (non-compliant with repo guidelines)
  3. Lines 53, 57, 61: key variable from request.getParameterValues() rendered directly in the name attribute without encoding

Add <%@ page import="org.owasp.encoder.Encode" %> and replace:

  • Line 16 attribute value with Encode.forHtmlAttribute(request.getParameter("oscarAPCacheLookupType"))
  • Line 51 encoding with Encode.forHtml(values.get(i))
  • Lines 53, 57, 61: both name and value attributes with Encode.forHtmlAttribute(key) and Encode.forHtmlAttribute(output)

Per coding guidelines: "Use OWASP Encoder library from org.owasp.encoder package - never use custom encoding" and "**/*.jsp: Use Encode.forHtmlAttribute() ... for ALL user inputs in web output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/eform/efmformapconfig_lookup.jsp` around lines 39 - 53,
Import org.owasp.encoder.Encode in the JSP and replace all current HTML sinks to
use OWASP Encoder: change any direct rendering of request parameters (e.g.
request.getParameter("oscarAPCacheLookupType")) to Encode.forHtmlAttribute(...),
replace the use of
org.apache.commons.text.StringEscapeUtils.escapeHtml4(values.get(i)) with
Encode.forHtml(values.get(i)), and ensure the attributes that output the key and
output variables in the input tag (the name and value attributes that use key
and output) use Encode.forHtmlAttribute(key) and
Encode.forHtmlAttribute(output); keep the existing logic that computes names,
values, sql, swp and uses DatabaseAP.parserReplace/EFormUtil.getValues but pass
encoded strings to the rendered HTML.
src/main/webapp/report/reportbilledvisit2.jsp (3)

225-228: ⚠️ Potential issue | 🟡 Minor

XSS vulnerability: Nurse names output without encoding.

vNurse.get(i) values from database are output without HTML encoding.

🛡️ Proposed fix
-        <TH width="6%"><%="pat_" + vNurse.get(i)%>
+        <TH width="6%"><%="pat_" + Encode.forHtml((String)vNurse.get(i))%>
         </TH>
-        <TH width="6%"><%="vis_" + vNurse.get(i)%>
+        <TH width="6%"><%="vis_" + Encode.forHtml((String)vNurse.get(i))%>
         </TH>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/report/reportbilledvisit2.jsp` around lines 225 - 228, The
nurse names are written directly via vNurse.get(i) in reportbilledvisit2.jsp
(<TH> elements) causing an XSS risk; replace the raw scriptlet output with an
HTML-encoded value (e.g., use JSTL/EL <c:out value="..."/> or fn:escapeXml, or
call a server-side encoder such as StringEscapeUtils.escapeHtml4) when rendering
vNurse.get(i) and any other user-sourced values so special characters are
escaped before writing to the TH elements.

206-206: ⚠️ Potential issue | 🟠 Major

XSS vulnerability: sdate and edate output without encoding.

Request parameters are output directly in HTML without OWASP encoding. While SQL injection is fixed, this introduces XSS risk.

As per coding guidelines: "Use Encode.forHtml()... for ALL user inputs in web output."

🛡️ Proposed fix

Add OWASP Encoder import at the top:

<%@ page import="org.owasp.encoder.Encode" %>

Then encode the output:

-        <td>Period: (<%=sdate%> ~ <%=edate%>)</td>
+        <td>Period: (<%=Encode.forHtml(sdate)%> ~ <%=Encode.forHtml(edate)%>)</td>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/report/reportbilledvisit2.jsp` at line 206, Outputting sdate
and edate directly in reportbilledvisit2.jsp causes XSS; import
org.owasp.encoder.Encode at the top of the JSP and replace direct scriptlet
outputs for sdate and edate with encoded calls (use Encode.forHtml(sdate) and
Encode.forHtml(edate)) where the template currently renders "<%=sdate%>" and
"<%=edate%>" so all user-supplied date values are HTML-encoded before being
written to the page.

235-241: ⚠️ Potential issue | 🟡 Minor

XSS vulnerability: Service codes and properties output without encoding.

All vServiceCode, vServiceDesc, and props.getProperty() values should be HTML-encoded before output.

🛡️ Proposed fix
-        <td align="center"><%=vServiceCode.get(i)%>
+        <td align="center"><%=Encode.forHtml((String)vServiceCode.get(i))%>
         </td>
-        <td><%=vServiceDesc.get(i)%>
+        <td><%=Encode.forHtml((String)vServiceDesc.get(i))%>
         </td>
-        <td align="center"><%=props.getProperty(vServiceCode.get(i) + "pat" + vServiceDesc.get(i))%>
+        <td align="center"><%=Encode.forHtml(props.getProperty(vServiceCode.get(i) + "pat" + vServiceDesc.get(i)))%>
         </td>

Apply similar encoding to all remaining props.getProperty() outputs in this table.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/report/reportbilledvisit2.jsp` around lines 235 - 241,
Outputting vServiceCode, vServiceDesc and props.getProperty(...) directly
creates XSS risk; HTML-encode these values before rendering. Replace the inline
scriptlet expressions (vServiceCode.get(i), vServiceDesc.get(i), and
props.getProperty(...)) with their HTML-escaped equivalents (e.g., use
StringEscapeUtils.escapeHtml4(...) from Apache Commons Text or JSTL/EL <c:out>
or fn:escapeXml) and apply the same encoding to all other props.getProperty()
outputs in this table so every user-controlled value is escaped.
src/main/java/ca/openosp/openo/form/FrmBCHPRecord.java (1)

80-86: ⚠️ Potential issue | 🟠 Major

Close the new demographic ResultSet in the edit branch.

Line 81 opens a second ResultSet, but the existingID > 0 path returns without closing it. That will leak JDBC resources on repeated form edits.

Minimal fix
-            ResultSet rs = DBHandler.GetPreSQL(sql, demographicNo);
-            if (rs.next()) {
-                props.setProperty("pg1_patientName_cur", rs.getString("last_name") + ", " + rs.getString("first_name"));
-                props.setProperty("pg1_phn_cur", rs.getString("hin"));
-                props.setProperty("pg1_phone_cur", rs.getString("phone") + "  " + rs.getString("phone2"));
-            }
+            ResultSet rs = DBHandler.GetPreSQL(sql, demographicNo);
+            try {
+                if (rs.next()) {
+                    props.setProperty("pg1_patientName_cur", rs.getString("last_name") + ", " + rs.getString("first_name"));
+                    props.setProperty("pg1_phn_cur", rs.getString("hin"));
+                    props.setProperty("pg1_phone_cur", rs.getString("phone") + "  " + rs.getString("phone2"));
+                }
+            } finally {
+                rs.close();
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/form/FrmBCHPRecord.java` around lines 80 - 86,
The demographic ResultSet opened via DBHandler.GetPreSQL in FrmBCHPRecord (the
variable rs used with props.setProperty("pg1_patientName_cur", ...),
"pg1_phn_cur", "pg1_phone_cur") is not closed on the edit path that returns
early; update the code to close rs before any return by either using a
try-with-resources or ensuring rs.close() is called in a finally block (or
immediately before the existingID > 0 return) so the ResultSet is always closed
and JDBC resources are not leaked.
src/main/java/ca/openosp/openo/form/FrmDischargeSummaryRecord.java (1)

231-243: ⚠️ Potential issue | 🟠 Major

Use the selected clientName alias when populating the property.

The query at Line 231 aliases the name as clientName, but Line 243 still reads pName. On the new-record path this will either leave clientName empty or fail with a column lookup error.

Suggested fix
-                props.setProperty("clientName", Misc.getString(rs, "pName"));
+                props.setProperty("clientName", Misc.getString(rs, "clientName"));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/form/FrmDischargeSummaryRecord.java` around
lines 231 - 243, The code in FrmDischargeSummaryRecord uses an SQL alias
"clientName" in the SELECT but then reads the wrong column "pName" from the
ResultSet; update the ResultSet lookup where props.setProperty("clientName",
...) is set (in the block that uses ResultSet rs from DBHandler.GetPreSQL) to
use Misc.getString(rs, "clientName") (or otherwise match the SELECT alias),
ensuring the property key "clientName" is populated from the aliased column.
src/main/java/ca/openosp/openo/form/FrmRourke2006Record.java (1)

67-87: ⚠️ Potential issue | 🔴 Critical

Close the second demographic ResultSet.

The ResultSet opened on Line 68 is never closed before the method returns. That leaks JDBC resources every time an existing Rourke record is loaded.

🛠️ Proposed fix
             if (rs.next()) {
                 String rourkeVal = props.getProperty("c_pName", "");
                 String demoVal = Misc.getString(rs, "pName");
@@
                 if (!rourkeVal.equals(demoVal)) {
                     props.setProperty("c_birthDate", demoVal);
                     updated = "true";
                 }
             }
+            rs.close();
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/form/FrmRourke2006Record.java` around lines 67
- 87, The ResultSet 'rs' returned by DBHandler.GetPreSQL in FrmRourke2006Record
is not closed, leaking JDBC resources; update the code around the
DBHandler.GetPreSQL(...) call and the block that reads from 'rs' to ensure 'rs'
is closed in a finally block or try-with-resources (use try (ResultSet rs =
DBHandler.GetPreSQL(sql, demographicNo)) { ... }) so that 'rs' is always closed
after use, and remove any early returns that would skip closing.
src/main/java/ca/openosp/openo/billings/ca/bc/MSP/MSPReconcile.java (1)

1204-1208: ⚠️ Potential issue | 🟠 Major

Close the nested ResultSet inside the rejected-bill loop.

Line 1204 opens a new ResultSet for every rejected bill and never closes it. On larger remittance runs that leaks cursors/statements and can exhaust the connection before the outer report finishes.

♻️ Suggested fix
-                    ResultSet rsDemo = DBHandler.GetPreSQL("select phone,phone2 from demographic where demographic_no = ?", b.demoNo);
-                    if (rsDemo.next()) {
-                        b.demoPhone = rsDemo.getString("phone");
-                        b.demoPhone2 = rsDemo.getString("phone2");
-                    }
+                    try (ResultSet rsDemo = DBHandler.GetPreSQL(
+                            "select phone,phone2 from demographic where demographic_no = ?",
+                            b.demoNo)) {
+                        if (rsDemo.next()) {
+                            b.demoPhone = rsDemo.getString("phone");
+                            b.demoPhone2 = rsDemo.getString("phone2");
+                        }
+                    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/billings/ca/bc/MSP/MSPReconcile.java` around
lines 1204 - 1208, The nested ResultSet rsDemo obtained via DBHandler.GetPreSQL
inside the rejected-bill loop is never closed; wrap the call to
DBHandler.GetPreSQL and the subsequent rsDemo.next()/getString(...) usage in a
try-with-resources (or ensure rsDemo.close() in a finally) so the ResultSet is
always closed after use; locate the code that declares ResultSet rsDemo and the
assignments to b.demoPhone and b.demoPhone2 and ensure the resource is closed
immediately after those reads to prevent cursor/statement leaks.
🟡 Minor comments (9)
src/main/java/ca/openosp/openo/commn/dao/Billing3rdPartyAddressDaoImpl.java-69-72 (1)

69-72: ⚠️ Potential issue | 🟡 Minor

Normalize keyword in the non-search_name branch.

Line 72 turns null into "null%", so an empty search on any allowlisted column silently returns no rows instead of following the empty-prefix behavior already used in the search_name path.

💡 Suggested change
         } else {
             // Validate search_mode as a column name
             String safeSearchColumn = validColumns.contains(search_mode) ? search_mode : "company_name";
             where = safeSearchColumn.concat(" like :searchMode");
-            params.put("searchMode", keyword + "%");
+            params.put("searchMode", (keyword == null ? "" : keyword) + "%");
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/commn/dao/Billing3rdPartyAddressDaoImpl.java`
around lines 69 - 72, The non-search_name branch in
Billing3rdPartyAddressDaoImpl currently does params.put("searchMode", keyword +
"%") which turns null into "null%"; normalize keyword the same way as in the
search_name path by replacing null with empty string (or defaulting to "")
before building the like pattern, then set params.put("searchMode",
normalizedKeyword + "%"); keep the existing safeSearchColumn, where assignment,
and use normalizedKeyword wherever keyword is used for the search pattern.
src/main/webapp/decision/antenatal/antenatalplanner.jsp-101-101 (1)

101-101: ⚠️ Potential issue | 🟡 Minor

Handle invalid formId parse failures gracefully.

Line 101 can throw NumberFormatException for malformed input and produce a hard page failure. Consider validating first and returning a controlled error path.

Suggested fix
-            rsdemo = DBHandler.GetPreSQL("select * from formONAR where ID = ?", Integer.parseInt(form_no));
+            Integer formId;
+            try {
+                formId = Integer.valueOf(form_no);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException("Invalid formId");
+            }
+            rsdemo = DBHandler.GetPreSQL("select * from formONAR where ID = ?", formId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/decision/antenatal/antenatalplanner.jsp` at line 101, The
code calls Integer.parseInt(form_no) directly when building the query with
DBHandler.GetPreSQL, which can throw NumberFormatException for malformed or null
input; wrap the parse in a validation and safe-parse block (check form_no for
null/empty, use a try/catch around Integer.parseInt or use a helper
parseIntSafe) and on failure take a controlled error path (e.g., set an error
message/redirect or skip the DB call) before calling DBHandler.GetPreSQL; update
the code paths that reference form_no/formId so they only call
DBHandler.GetPreSQL when the parsed integer is valid.
src/main/webapp/decision/antenatal/antenatalplannerprint.jsp-70-70 (1)

70-70: ⚠️ Potential issue | 🟡 Minor

Add parse guard for formId before binding.

Line 70 can throw NumberFormatException on invalid input. Use a guarded parse and fail cleanly.

Suggested fix
-        rsdemo = DBHandler.GetPreSQL("select * from formAR where ID = ?", Integer.parseInt(form_no));
+        Integer formId;
+        try {
+            formId = Integer.valueOf(form_no);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("Invalid formId");
+        }
+        rsdemo = DBHandler.GetPreSQL("select * from formAR where ID = ?", formId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/webapp/decision/antenatal/antenatalplannerprint.jsp` at line 70, The
call to Integer.parseInt(form_no) when creating rsdemo via DBHandler.GetPreSQL
can throw NumberFormatException for null/invalid form_no; before binding,
validate and safely parse form_no (e.g., check null/empty, use a try/catch
around Integer.parseInt or Integer.valueOf) and handle failure by logging the
invalid input and returning/forwarding an error response instead of calling
DBHandler.GetPreSQL; update the code path that sets rsdemo to only call
DBHandler.GetPreSQL with a guaranteed valid integer ID.
src/main/java/ca/openosp/openo/billings/ca/bc/pageUtil/ViewWCB2Action.java-111-113 (1)

111-113: ⚠️ Potential issue | 🟡 Minor

Guard empty provider query results before indexing.

Line 113 assumes at least one row exists. If no provider matches, this throws and fails the action.

Suggested fix
-                if (lstResults != null) {
+                if (lstResults != null && !lstResults.isEmpty()) {
                     String[] providerData = (String[]) lstResults.get(0);
                     this.setW_pracno(providerData[0]);
                     this.setW_payeeno(providerData[1]);
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/billings/ca/bc/pageUtil/ViewWCB2Action.java`
around lines 111 - 113, The code in ViewWCB2Action where
SqlUtils.getQueryResultsList is called must guard against empty results before
casting/indexing: in the method containing the lines with providerNo, check that
lstResults is not null and not empty (e.g., lstResults.size() > 0) before doing
String[] providerData = (String[]) lstResults.get(0); and handle the empty case
(return an error/early return, set an appropriate error message, or use a
default) so the action does not throw when no provider rows exist.
src/main/java/ca/openosp/openo/report/pageUtil/GeneratePatientLetters2Action.java-91-102 (1)

91-102: ⚠️ Potential issue | 🟡 Minor

Handle missing demos before iterating.

Line 135 still assumes demos is non-null. A request without demos will throw a runtime error.

Suggested fix
-        if (demos != null) {
-            for (String demo : demos) {
-                if (demo != null) {
-                    SqlUtils.validateNumericId(demo, "demographic_no");
-                }
-            }
-        }
+        if (demos == null || demos.length == 0) {
+            throw new IllegalArgumentException("Missing required parameter: demos");
+        }
+        for (String demo : demos) {
+            if (demo != null) {
+                SqlUtils.validateNumericId(demo, "demographic_no");
+            }
+        }

Also applies to: 135-135

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ca/openosp/openo/report/pageUtil/GeneratePatientLetters2Action.java`
around lines 91 - 102, The code is iterating over demos without guarding against
null in GeneratePatientLetters2Action; add a null check before any loop that
accesses the demos array (the for (String demo : demos) block that calls
SqlUtils.validateNumericId) so you only iterate when demos != null (or treat an
absent demos as an empty array) and apply the same null-check pattern wherever
demos is referenced to avoid runtime NPEs; also keep the existing id validation
via SqlUtils.validateNumericId(id, "reportLetter") unchanged.
src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java-159-161 (1)

159-161: ⚠️ Potential issue | 🟡 Minor

ResultSet rs is never closed — potential resource leak.

The ResultSet returned by DBHandler.GetPreSQL() at line 159 is iterated but never explicitly closed. While the connection may eventually be cleaned up, failing to close the ResultSet can exhaust database cursors under load.

Consider closing rs after the while loop completes (around line 299), or wrapping it in a try-with-resources if the API supports it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java` around lines
159 - 161, The ResultSet returned by DBHandler.GetPreSQL(query, providerNo)
(variable rs in ExtractBean) is never closed, risking cursor/resource leaks;
modify the code to ensure rs is closed after use—either wrap the call and
result-processing loop in a try-with-resources (try (ResultSet rs =
DBHandler.GetPreSQL(...)) { ... }) or explicitly call rs.close() in a finally
block after the while (rs.next()) loop in the same method, ensuring any
SQLException is handled/logged and not swallowed.
src/main/java/ca/openosp/openo/encounter/data/EctPatientData.java-53-54 (1)

53-54: ⚠️ Potential issue | 🟡 Minor

Wrap Integer.parseInt() in exception handling for unvalidated numeric conversion.

Both getProviderNo() (lines 53–54) and getPatient() (lines 80–81) convert the String demographicNo parameter directly to an integer without catching NumberFormatException. If a non-numeric value reaches these methods, an uncaught exception will propagate instead of returning an empty result like the existing SQLException handling does. Validate input upstream or catch NumberFormatException locally and return an empty result.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/encounter/data/EctPatientData.java` around
lines 53 - 54, Both getProviderNo and getPatient call
Integer.parseInt(demographicNo) without handling NumberFormatException; wrap the
parseInt call in a try/catch to catch NumberFormatException for the
demographicNo parameter and return the same empty/neutral result (and/or null)
that the existing SQLException handling returns, optionally logging the invalid
input; ensure you modify the parse in getProviderNo and getPatient so invalid
non-numeric demographicNo does not propagate an exception but follows the
existing empty-result flow.
src/main/java/ca/openosp/openo/hospitalReportManager/dao/HRMDocumentDao.java-53-59 (1)

53-59: ⚠️ Potential issue | 🟡 Minor

Reject unknown sort directions instead of defaulting to ASC.

Line 57 currently treats every non-DESC value as ascending, so invalid input still changes query behavior instead of being ignored as the method contract says.

Suggested fix
 private static String getSafeOrderByFragment(String orderColumn, String orderDirection) {
-    if (StringUtils.isEmpty(orderColumn) || StringUtils.isEmpty(orderDirection)) {
+    if (StringUtils.isBlank(orderColumn) || StringUtils.isBlank(orderDirection)) {
         return "";
     }
-    Map<String, String> fragments = "DESC".equalsIgnoreCase(orderDirection) ? ORDER_DESC_FRAGMENTS : ORDER_ASC_FRAGMENTS;
+    Map<String, String> fragments;
+    if ("ASC".equalsIgnoreCase(orderDirection)) {
+        fragments = ORDER_ASC_FRAGMENTS;
+    } else if ("DESC".equalsIgnoreCase(orderDirection)) {
+        fragments = ORDER_DESC_FRAGMENTS;
+    } else {
+        return "";
+    }
     String fragment = fragments.get(orderColumn);
     return fragment != null ? fragment : "";
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/ca/openosp/openo/hospitalReportManager/dao/HRMDocumentDao.java`
around lines 53 - 59, getSafeOrderByFragment currently treats any non-"DESC"
orderDirection as ASC; change it to only accept "ASC" or "DESC" and return ""
for any other values. In practice, update the logic in getSafeOrderByFragment so
you first normalize orderDirection and explicitly check equalsIgnoreCase("DESC")
or equalsIgnoreCase("ASC") to select ORDER_DESC_FRAGMENTS or
ORDER_ASC_FRAGMENTS, and if neither matches return "" immediately; then look up
the orderColumn in the chosen fragments and return fragment or "" as before.
src/main/java/ca/openosp/openo/messenger/pageUtil/MsgViewMessageByPosition2Action.java-225-226 (1)

225-226: ⚠️ Potential issue | 🟡 Minor

linked sorting is currently a no-op.

Line 226 orders by m.messageid is null, but m.messageid cannot be null in this inner join (m.messageid = mapp.messageID). Requests with orderBy=linked will therefore just fall back to m.messageid desc. Map this key to the real linked-status expression, or drop it from the whitelist.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ca/openosp/openo/messenger/pageUtil/MsgViewMessageByPosition2Action.java`
around lines 225 - 226, The "linked" orderBy is a no-op because it uses
"m.messageid is null" but m.messageid is never null due to the inner join;
update the mapping in MsgViewMessageByPosition2Action where key == "linked" to
use the actual linked-status expression (e.g., reference the mapping table
column such as "mapp.messageID IS NOT NULL" for linked messages or
"mapp.messageID IS NULL" for unlinked), or remove "linked" from the whitelist
entirely; if you need to detect absence of a mapping change the join to a LEFT
JOIN and then use "mapp.messageID IS NULL/IS NOT NULL" as appropriate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f638b26-c619-4338-934a-c3a6afab1123

📥 Commits

Reviewing files that changed from the base of the PR and between 2897f4d and 64728f7.

📒 Files selected for processing (148)
  • database/mysql/importCasemgmt.java
  • src/main/java/ca/openosp/openo/PMmodule/dao/ProviderDaoImpl.java
  • src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java
  • src/main/java/ca/openosp/openo/billings/ca/bc/MSP/CDMReminderHlp.java
  • src/main/java/ca/openosp/openo/billings/ca/bc/MSP/CreateBillingReport2Action.java
  • src/main/java/ca/openosp/openo/billings/ca/bc/MSP/MSPReconcile.java
  • src/main/java/ca/openosp/openo/billings/ca/bc/pageUtil/ViewWCB2Action.java
  • src/main/java/ca/openosp/openo/billings/ca/on/data/JdbcBillingReviewImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/Billing3rdPartyAddressDaoImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/BillingBCDaoImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/BillingONCHeader1Dao.java
  • src/main/java/ca/openosp/openo/commn/dao/BillingONCHeader1DaoImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/DemographicDaoImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/DxDaoImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/DxresearchDAOImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/EFormReportToolDaoImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/InboxResultsRepositoryImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/PregnancyFormsDao.java
  • src/main/java/ca/openosp/openo/commn/dao/SecurityDao.java
  • src/main/java/ca/openosp/openo/commn/dao/SecurityDaoImpl.java
  • src/main/java/ca/openosp/openo/commn/dao/SiteDaoImpl.java
  • src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java
  • src/main/java/ca/openosp/openo/db/DBHandler.java
  • src/main/java/ca/openosp/openo/eform/APExecute.java
  • src/main/java/ca/openosp/openo/eform/EFormUtil.java
  • src/main/java/ca/openosp/openo/eform/actions/FetchUpdatedData2Action.java
  • src/main/java/ca/openosp/openo/eform/data/EForm.java
  • src/main/java/ca/openosp/openo/encounter/data/EctARRecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctAlphaRecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctAnnualRecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctMMSERecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctMentalHealthRecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctPalliativeCareRecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctPatientData.java
  • src/main/java/ca/openosp/openo/encounter/data/EctPeriMenopausalRecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctRourkeRecord.java
  • src/main/java/ca/openosp/openo/encounter/data/EctType2DiabetesRecord.java
  • src/main/java/ca/openosp/openo/form/Frm2MinWalkRecord.java
  • src/main/java/ca/openosp/openo/form/FrmARBloodWorkTest.java
  • src/main/java/ca/openosp/openo/form/FrmAREnhancedBloodWorkTest.java
  • src/main/java/ca/openosp/openo/form/FrmARRecord.java
  • src/main/java/ca/openosp/openo/form/FrmAdfRecord.java
  • src/main/java/ca/openosp/openo/form/FrmAdfV2Record.java
  • src/main/java/ca/openosp/openo/form/FrmAnnualRecord.java
  • src/main/java/ca/openosp/openo/form/FrmAnnualV2Record.java
  • src/main/java/ca/openosp/openo/form/FrmBCAR2007Record.java
  • src/main/java/ca/openosp/openo/form/FrmBCAR2012Record.java
  • src/main/java/ca/openosp/openo/form/FrmBCARRecord.java
  • src/main/java/ca/openosp/openo/form/FrmBCBirthSumMo2008Record.java
  • src/main/java/ca/openosp/openo/form/FrmBCBrithSumMoRecord.java
  • src/main/java/ca/openosp/openo/form/FrmBCClientChartChecklistRecord.java
  • src/main/java/ca/openosp/openo/form/FrmBCHPRecord.java
  • src/main/java/ca/openosp/openo/form/FrmBCINRRecord.java
  • src/main/java/ca/openosp/openo/form/FrmBCNewBorn2008Record.java
  • src/main/java/ca/openosp/openo/form/FrmBCNewBornRecord.java
  • src/main/java/ca/openosp/openo/form/FrmCESDRecord.java
  • src/main/java/ca/openosp/openo/form/FrmCaregiverRecord.java
  • src/main/java/ca/openosp/openo/form/FrmConsultantRecord.java
  • src/main/java/ca/openosp/openo/form/FrmCostQuestionnaireRecord.java
  • src/main/java/ca/openosp/openo/form/FrmCounselingRecord.java
  • src/main/java/ca/openosp/openo/form/FrmCounsellorAssessmentRecord.java
  • src/main/java/ca/openosp/openo/form/FrmDischargeSummaryRecord.java
  • src/main/java/ca/openosp/openo/form/FrmFallsRecord.java
  • src/main/java/ca/openosp/openo/form/FrmGripStrengthRecord.java
  • src/main/java/ca/openosp/openo/form/FrmGrowth0_36Record.java
  • src/main/java/ca/openosp/openo/form/FrmGrowthChartRecord.java
  • src/main/java/ca/openosp/openo/form/FrmHomeFallsRecord.java
  • src/main/java/ca/openosp/openo/form/FrmImmunAllergyRecord.java
  • src/main/java/ca/openosp/openo/form/FrmIntakeHxRecord.java
  • src/main/java/ca/openosp/openo/form/FrmIntakeInfoRecord.java
  • src/main/java/ca/openosp/openo/form/FrmInternetAccessRecord.java
  • src/main/java/ca/openosp/openo/form/FrmInvoiceRecord.java
  • src/main/java/ca/openosp/openo/form/FrmLabReq07Record.java
  • src/main/java/ca/openosp/openo/form/FrmLabReq10Record.java
  • src/main/java/ca/openosp/openo/form/FrmLabReqRecord.java
  • src/main/java/ca/openosp/openo/form/FrmLateLifeFDIDisabilityRecord.java
  • src/main/java/ca/openosp/openo/form/FrmLateLifeFDIFunctionRecord.java
  • src/main/java/ca/openosp/openo/form/FrmMMSERecord.java
  • src/main/java/ca/openosp/openo/form/FrmMentalHealthForm14Record.java
  • src/main/java/ca/openosp/openo/form/FrmMentalHealthForm1Record.java
  • src/main/java/ca/openosp/openo/form/FrmMentalHealthForm42Record.java
  • src/main/java/ca/openosp/openo/form/FrmMentalHealthRecord.java
  • src/main/java/ca/openosp/openo/form/FrmONAREnhancedRecord.java
  • src/main/java/ca/openosp/openo/form/FrmONARRecord.java
  • src/main/java/ca/openosp/openo/form/FrmOvulationRecord.java
  • src/main/java/ca/openosp/openo/form/FrmPalliativeCareRecord.java
  • src/main/java/ca/openosp/openo/form/FrmPeriMenopausalRecord.java
  • src/main/java/ca/openosp/openo/form/FrmPolicyRecord.java
  • src/main/java/ca/openosp/openo/form/FrmPositionHazardRecord.java
  • src/main/java/ca/openosp/openo/form/FrmReceptionAssessmentRecord.java
  • src/main/java/ca/openosp/openo/form/FrmRecordHelp.java
  • src/main/java/ca/openosp/openo/form/FrmRhImmuneGlobulinRecord.java
  • src/main/java/ca/openosp/openo/form/FrmRourke2006Record.java
  • src/main/java/ca/openosp/openo/form/FrmRourke2009Record.java
  • src/main/java/ca/openosp/openo/form/FrmRourkeRecord.java
  • src/main/java/ca/openosp/openo/form/FrmSF36CaregiverRecord.java
  • src/main/java/ca/openosp/openo/form/FrmSF36Record.java
  • src/main/java/ca/openosp/openo/form/FrmSatisfactionScaleRecord.java
  • src/main/java/ca/openosp/openo/form/FrmSelfAdministeredRecord.java
  • src/main/java/ca/openosp/openo/form/FrmSelfAssessmentRecord.java
  • src/main/java/ca/openosp/openo/form/FrmSelfEfficacyRecord.java
  • src/main/java/ca/openosp/openo/form/FrmSelfManagementRecord.java
  • src/main/java/ca/openosp/openo/form/FrmTreatmentPrefRecord.java
  • src/main/java/ca/openosp/openo/form/FrmType2DiabeteRecord.java
  • src/main/java/ca/openosp/openo/form/FrmchfRecord.java
  • src/main/java/ca/openosp/openo/form/data/FrmData.java
  • src/main/java/ca/openosp/openo/form/pageUtil/FormForward2Action.java
  • src/main/java/ca/openosp/openo/form/pageUtil/FrmForm2Action.java
  • src/main/java/ca/openosp/openo/form/pageUtil/FrmSetupForm2Action.java
  • src/main/java/ca/openosp/openo/form/pageUtil/FrmXmlUpload2Action.java
  • src/main/java/ca/openosp/openo/hospitalReportManager/dao/HRMDocumentDao.java
  • src/main/java/ca/openosp/openo/hospitalReportManager/v2018/HRM2Action.java
  • src/main/java/ca/openosp/openo/lab/ca/bc/PathNet/PathNetInfo.java
  • src/main/java/ca/openosp/openo/managers/SecurityManager.java
  • src/main/java/ca/openosp/openo/messenger/docxfer/send/MsgGenerate.java
  • src/main/java/ca/openosp/openo/messenger/pageUtil/MsgViewMessageByPosition2Action.java
  • src/main/java/ca/openosp/openo/report/ClinicalReports/SQLDenominator.java
  • src/main/java/ca/openosp/openo/report/ClinicalReports/SQLNumerator.java
  • src/main/java/ca/openosp/openo/report/data/RptReportConfigData.java
  • src/main/java/ca/openosp/openo/report/data/RptReportCreator.java
  • src/main/java/ca/openosp/openo/report/data/RptReportFilter.java
  • src/main/java/ca/openosp/openo/report/data/RptReportItem.java
  • src/main/java/ca/openosp/openo/report/pageUtil/GeneratePatientLetters2Action.java
  • src/main/java/ca/openosp/openo/report/pageUtil/RptDownloadCSVServlet.java
  • src/main/java/ca/openosp/openo/report/pageUtil/RptFormQuery.java
  • src/main/java/ca/openosp/openo/report/reportByTemplate/ReportObject.java
  • src/main/java/ca/openosp/openo/report/reportByTemplate/ReportObjectGeneric.java
  • src/main/java/ca/openosp/openo/report/reportByTemplate/SQLReporter.java
  • src/main/java/ca/openosp/openo/util/JDBCUtil.java
  • src/main/java/ca/openosp/openo/util/PreparedSQL.java
  • src/main/java/ca/openosp/openo/util/SqlUtils.java
  • src/main/java/ca/openosp/openo/www/lookup/LookupCodeEdit2Action.java
  • src/main/java/ca/openosp/openo/www/lookup/LookupCodeList2Action.java
  • src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java
  • src/main/webapp/admin/logReport.jsp
  • src/main/webapp/admin/securitysearchresults.jsp
  • src/main/webapp/billing/CA/BC/onSearch3rdBillAddr.jsp
  • src/main/webapp/billing/CA/ON/billingONNewReport.jsp
  • src/main/webapp/decision/antenatal/antenatalplanner.jsp
  • src/main/webapp/decision/antenatal/antenatalplannerprint.jsp
  • src/main/webapp/eform/efmformapconfig_lookup.jsp
  • src/main/webapp/oscarMDS/PatientSearch.jsp
  • src/main/webapp/report/reportResult.jsp
  • src/main/webapp/report/reportbilledvisit1.jsp
  • src/main/webapp/report/reportbilledvisit2.jsp
  • src/main/webapp/report/reportbilledvisit3.jsp
  • src/main/webapp/report/reportonbilledphcp.jsp
  • src/main/webapp/report/reportonbilledphcpv2.jsp
💤 Files with no reviewable changes (1)
  • src/main/java/ca/openosp/openo/commn/dao/BillingONCHeader1Dao.java

Comment thread src/main/java/ca/openosp/openo/form/FrmBCINRRecord.java
Comment thread src/main/java/ca/openosp/openo/form/FrmMentalHealthForm1Record.java
Comment thread src/main/java/ca/openosp/openo/report/ClinicalReports/SQLDenominator.java Outdated
Comment thread src/main/webapp/admin/logReport.jsp
Comment thread src/main/webapp/billing/CA/ON/billingONNewReport.jsp
Comment thread src/main/webapp/report/reportbilledvisit3.jsp
@LiamStanziani LiamStanziani marked this pull request as ready for review April 6, 2026 16:02
Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @LiamStanziani, your pull request is larger than the review limit of 150000 diff characters

@LiamStanziani
Copy link
Copy Markdown
Collaborator Author

I want to do more testing on this branch after any new AI reviews come in, setting to ready for review to see any final comments, not specifically fully ready yet

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Security: Phase 2 SQL Injection Remediation — Parameterized Queries & Input Validation Across 148+ Files

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
  **Phase 2 SQL Injection Remediation** — Comprehensive hardening across 148+ files to eliminate SQL
  injection vulnerabilities by converting string-concatenated queries to parameterized statements and
  adding input validation for dynamic SQL identifiers.
  **Core Changes:**
• **Parameterized Query Conversion**: Replaced string concatenation with PreparedStatement using
  ? placeholders across form records (~50 files), DAOs, report generators, billing modules, and JSPs
• **New Utility Infrastructure**:
  - SqlUtils — validation methods (validateNumericId, validateTableName, validateColumnName,
  validateSortColumn, inClausePlaceholders)
  - PreparedSQL — value object pairing parameterized SQL with bind parameters
  - DBHandler.GetPreSQL() / GetPreSQLUpdatable() — centralized parameterized query execution
  - FrmRecordHelp — parameterized overloads for form record operations
• **Dynamic Identifier Whitelisting**: Added allowlist validation for table names, column names, and
  ORDER BY clauses where parameterization is impossible
• **Report Template System**: New getParameterizedSQL() methods replace {param} markers with ?
  placeholders, supporting single values, multi-value IN clauses, and empty checkbox cases
• **Billing & EForm Hardening**: Converted MSP/ON billing queries, EForm DatabaseAP templates, and
  lookup actions to use parameterized queries with strict input validation
• **Deprecated Unsafe APIs**: Marked legacy methods (SecurityDao.findAllOrderBy,
  FrmRecordHelp.getFormRecord(String)) as @Deprecated with security warnings
• **JPA Parameter Fixes**: Corrected positional parameter indexing (?1, ?2, etc.) in Hibernate
  queries
  **Validation Patterns Applied:**
• Numeric ID validation: ^[0-9]+$
• Table/column names: ^[A-Za-z0-9_]{1,10}$ with allowlist sets
• Sort columns: Hardcoded safe ORDER BY fragments via whitelisted maps
• Lookup table IDs: Strict regex ^[A-Za-z0-9]{1,10}$ before downstream parameterized queries
  **Files Modified**: Form records (FrmAnnualRecord, FrmARRecord, FrmCaregiverRecord, etc.), DAOs
  (DemographicDaoImpl, LookupDaoImpl, EFormReportToolDaoImpl, etc.), billing modules (MSPReconcile,
  JdbcBillingReviewImpl, BillingONCHeader1DaoImpl), report system (ReportObjectGeneric, SQLReporter),
  eForm system (EForm, APExecute, FetchUpdatedData2Action), and utility classes (SqlUtils, JDBCUtil,
  EFormUtil)
  **Security Impact**: Eliminates HIGH and MEDIUM severity SQL injection findings across multiple code
  paths; all 20 remaining Snyk alerts are confirmed false positives due to unrecognized intermediate
  validation and parameterization.
Diagram
flowchart LR
  A["String-Concatenated SQL<br/>User Input + Query"] -->|"Convert to<br/>Parameterized"| B["PreparedStatement<br/>with ? Placeholders"]
  A -->|"Dynamic Identifiers<br/>Cannot Parameterize"| C["Whitelist Validation<br/>Table/Column Names"]
  B --> D["DBHandler.GetPreSQL<br/>FrmRecordHelp<br/>SqlUtils"]
  C --> D
  D --> E["Safe Query Execution<br/>Bind Parameters Separated<br/>from SQL Logic"]
  F["Report Templates<br/>EForm DatabaseAP<br/>Billing Queries"] -->|"New Utilities"| G["PreparedSQL<br/>getParameterizedSQL<br/>parameterizeFields"]
  G --> E
  H["Deprecated Unsafe APIs<br/>findAllOrderBy<br/>getFormRecord String"] -->|"Migration Path"| D
Loading

Grey Divider

File Changes

1. src/main/java/ca/openosp/openo/billings/ca/bc/MSP/MSPReconcile.java 🐞 Bug fix +116/-70

Parameterize MSP billing queries and add input validation

• Replaced string-concatenated SQL queries with parameterized DBHandler.GetPreSQL() calls using
 ? placeholders
• Refactored createCriteriaString() to createParameterizedCriteria() returning a
 ParameterizedCriteria object with SQL and bind parameters
• Added whitelist validation for dateFieldOption parameter to prevent SQL injection
• Converted multiple query construction methods to use StringBuilder with parameterized values
 instead of string concatenation

src/main/java/ca/openosp/openo/billings/ca/bc/MSP/MSPReconcile.java


2. database/mysql/importCasemgmt.java 🐞 Bug fix +65/-47

Parameterize import utility SQL and fix parameter indices

• Converted dynamic SQL string concatenation to parameterized PreparedStatement with ?
 placeholders
• Fixed parameter index alignment in multiple PreparedStatement.setString() and setInt() calls
• Added proper resource cleanup with close() calls for PreparedStatement objects
• Replaced inline programId concatenation with parameterized binding at correct indices

database/mysql/importCasemgmt.java


3. src/main/java/ca/openosp/openo/eform/EFormUtil.java ✨ Enhancement +144/-21

Add parameterized query helpers and sort column validation

• Added VALID_SORT_COLUMNS whitelist and validateSortColumn() method to prevent ORDER BY
 injection
• Created executeWithSort() helper for parameterized queries with whitelisted sort columns
• Added parameterized overloads of getValues() and getJsonValues() accepting Object... params
• Refactored listEForms() and listPatientEForms() to use parameterized queries with validated
 sort columns
• Deprecated unsafe non-parameterized method overloads with @Deprecated annotations

src/main/java/ca/openosp/openo/eform/EFormUtil.java


View more (145)
4. src/main/java/ca/openosp/openo/commn/dao/DemographicDaoImpl.java 🐞 Bug fix +97/-41

Parameterize demographic queries and add field whitelists

• Converted native SQL query to use JPA named parameters (?1, ?2, ?3) instead of string
 concatenation
• Added NATIVE_ORDER_FIELDS map to whitelist allowed ORDER BY columns with safe field mappings
• Refactored getOrderField() to use allowlist lookup instead of string concatenation
• Added field name validation in findByField() with whitelist of valid Demographic fields
• Extracted flu report SQL into static constants FLU_REPORT_SQL and FLU_REPORT_SQL_WITH_PROVIDER
 with parameterized :providerNo
• Fixed parameter handling in searchPatientCount() and searchPatients() to support collection
 parameters via setParameterList()

src/main/java/ca/openosp/openo/commn/dao/DemographicDaoImpl.java


5. src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java 🐞 Bug fix +52/-19

Validate lookup table identifiers and parameterize queries

• Added SAFE_SQL_EXPRESSION regex pattern and validateFieldSql() method for DB-configured field
 expressions
• Applied SqlUtils.validateTableName() to table names before use in queries
• Converted GetCodeFieldValues() to use parameterized query with ? placeholder for code
 parameter
• Added validation of all FieldDefValue objects loaded from database to prevent second-order SQL
 injection
• Parameterized getCountOfActiveClient() queries using DBPreparedHandler.queryResults() with
 bind parameters

src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java


6. src/main/java/ca/openosp/openo/util/SqlUtils.java ✨ Enhancement +164/-0

Add SQL validation utilities and parameterized query helpers

• Added validation methods: validateNumericId(), isNumericId(), validateOptionalNumericId(),
 validateTableName(), validateColumnName(), validateSortColumn()
• Added inClausePlaceholders() utility to generate comma-separated ? placeholders for IN clauses
• Added parameterized overloads getQueryResultsList() and getRow() accepting Object... params
• Deprecated unsafe constructInClauseString() and constructInClauseForStatements() methods with
 @Deprecated annotations

src/main/java/ca/openosp/openo/util/SqlUtils.java


7. src/main/java/ca/openosp/openo/hospitalReportManager/dao/HRMDocumentDao.java 🐞 Bug fix +75/-77

Whitelist HRM document query ORDER BY columns

• Added ORDER_ASC_FRAGMENTS and ORDER_DESC_FRAGMENTS maps to whitelist allowed ORDER BY columns
• Created getSafeOrderByFragment() method to return pre-built safe ORDER BY clauses
• Extracted common HQL building logic into buildQueryHql() method to prevent HQL injection
• Refactored query() and queryForCount() to use whitelisted ORDER BY fragments instead of string
 concatenation

src/main/java/ca/openosp/openo/hospitalReportManager/dao/HRMDocumentDao.java


8. src/main/java/ca/openosp/openo/form/FrmRecordHelp.java ✨ Enhancement +126/-21

Add parameterized form record query methods

• Added parameterized overloads for getFormRecord(), saveFormRecord(), updateFormRecord(),
 getPrintRecord(), getPrintRecords(), and getDemographicIds() accepting Object... params
• Deprecated unsafe non-parameterized method overloads with @Deprecated annotations and security
 warnings
• Extracted common result set processing into extractFormProperties() helper method
• Updated all new parameterized methods to use DBHandler.GetPreSQL() and GetPreSQLUpdatable()

src/main/java/ca/openosp/openo/form/FrmRecordHelp.java


9. src/main/java/ca/openosp/openo/commn/dao/EFormReportToolDaoImpl.java 🐞 Bug fix +40/-39

Validate and parameterize EForm report tool queries

• Added SqlUtils.validateTableName() and validateColumnName() calls for all dynamic table/column
 names
• Converted markLatest() to use parameterized query with ?1 placeholder for demographicNo
• Refactored populateReportTableItem() to use parameterized INSERT with ?1-?N placeholders
 instead of string concatenation
• Added validation of field names from EFormValue objects before use in SQL

src/main/java/ca/openosp/openo/commn/dao/EFormReportToolDaoImpl.java


10. src/main/java/ca/openosp/openo/commn/dao/DxresearchDAOImpl.java 🐞 Bug fix +49/-60

Parameterize Dxresearch HQL queries

• Converted patientRegistedDistincted() to use parameterized HQL with ?N placeholders instead of
 string concatenation
• Refactored patientRegistedAll() to build parameterized HQL with dynamic parameter binding
• Updated patientRegistedStatus() to use parameterized HQL with ?N placeholders for all search
 criteria
• Replaced string concatenation loops with StringBuilder and parameter list collection

src/main/java/ca/openosp/openo/commn/dao/DxresearchDAOImpl.java


11. src/main/java/ca/openosp/openo/commn/dao/InboxResultsRepositoryImpl.java 🐞 Bug fix +21/-2

Whitelist inbox results sort columns

• Added VALID_SORT_COLUMNS and VALID_SORT_ORDERS whitelists for ORDER BY validation
• Created buildSafeOrderBy() method to construct safe ORDER BY clauses from whitelisted values
• Refactored generateSelectQuery() to use whitelisted ORDER BY instead of direct string
 concatenation
• Added deterministic tiebreaker columns (segment_id DESC, lab_type ASC) to safe ORDER BY clause

src/main/java/ca/openosp/openo/commn/dao/InboxResultsRepositoryImpl.java


12. src/main/java/ca/openosp/openo/form/data/FrmData.java Security enhancement +51/-17

Parameterize form data queries with validated table names

• Added executeWithValidatedTable() helper method to safely execute queries with validated table
 names and parameterized values
• Converted string-concatenated SQL queries to use DBHandler.GetPreSQL() with ? placeholders for
 all user-supplied parameters
• Replaced dynamic table name concatenation with SqlUtils.validateTableName() validation followed
 by template-based substitution
• Added imports for Connection, PreparedStatement, DbConnectionFilter, and SqlUtils

src/main/java/ca/openosp/openo/form/data/FrmData.java


13. src/main/java/ca/openosp/openo/form/FrmBCINRRecord.java Security enhancement +24/-27

Parameterize BCINR form record database queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately instead of embedding in SQL
 strings
• Replaced deprecated DBHelp.searchDBRecord() calls with DBHandler.GetPreSQL()
• Added rs.close() calls to properly close result sets

src/main/java/ca/openosp/openo/form/FrmBCINRRecord.java


14. src/main/java/ca/openosp/openo/form/FrmDischargeSummaryRecord.java Security enhancement +22/-26

Parameterize discharge summary form queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic and ID parameters separately
• Replaced single-quoted string parameters with ? placeholders throughout

src/main/java/ca/openosp/openo/form/FrmDischargeSummaryRecord.java


15. src/main/java/ca/openosp/openo/form/FrmRourke2006Record.java Security enhancement +11/-22

Parameterize Rourke 2006 form record queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters as separate arguments
• Consolidated multi-line SQL string concatenations into single-line parameterized queries

src/main/java/ca/openosp/openo/form/FrmRourke2006Record.java


16. src/main/java/ca/openosp/openo/eform/data/EForm.java Security enhancement +63/-3

Parameterize eForm DatabaseAP template substitution

• Added parameterizeFields() method to replace ${name} template placeholders with ? markers
 and collect bind parameters
• Added parameterizeToken() helper to handle individual token replacement with parameter
 collection
• Added input validation for demographicNo, appointment_no, and providerNo before SQL template
 substitution
• Updated putValuesFromAP() to use PreparedSQL for parameterized query execution

src/main/java/ca/openosp/openo/eform/data/EForm.java


17. src/main/java/ca/openosp/openo/encounter/data/EctRourkeRecord.java Security enhancement +12/-12

Parameterize encounter Rourke record queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated DBHandler.GetPreSQLUpdatable() call for updatable result sets
• Replaced inline parameter concatenation with ? placeholders

src/main/java/ca/openosp/openo/encounter/data/EctRourkeRecord.java


18. src/main/java/ca/openosp/openo/form/FrmRourkeRecord.java Security enhancement +9/-21

Parameterize Rourke form record queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately
• Consolidated multi-line SQL concatenations into single parameterized queries

src/main/java/ca/openosp/openo/form/FrmRourkeRecord.java


19. src/main/java/ca/openosp/openo/PMmodule/dao/ProviderDaoImpl.java Security enhancement +11/-22

Parameterize provider DAO Hibernate queries

• Converted Hibernate HQL queries from string concatenation to positional parameters (?0, ?1)
• Converted native SQL queries to use positional parameters (?1) with query.setParameter()
• Removed inline string concatenation of provider numbers and facility IDs

src/main/java/ca/openosp/openo/PMmodule/dao/ProviderDaoImpl.java


20. src/main/java/ca/openosp/openo/billings/ca/on/data/JdbcBillingReviewImpl.java Security enhancement +4/-84

Remove vulnerable billing query overloads

• Removed vulnerable getBill() overloads that used raw SQL concatenation with
 findBillingData(conditions)
• Added comment explaining migration to parameterized findByMagic/findByMagic2 JPA path
• Preserved safe getBill(String[], ...) overloads that use parameterized queries

src/main/java/ca/openosp/openo/billings/ca/on/data/JdbcBillingReviewImpl.java


21. src/main/java/ca/openosp/openo/eform/APExecute.java Security enhancement +41/-13

Parameterize APExecute DatabaseAP template queries

• Added parameterizeToken() helper method to replace ${name} placeholders with ? markers
• Added parameterizeTemplate() convenience method for single-variable parameterization
• Updated execute() methods to use PreparedSQL for parameterized query execution
• Updated EFormUtil.getJsonValues() and EFormUtil.getValues() calls to pass parameter arrays

src/main/java/ca/openosp/openo/eform/APExecute.java


22. src/main/java/ca/openosp/openo/form/FrmLabReq07Record.java Security enhancement +18/-24

Parameterize lab request form queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately
• Replaced date formatting in SQL strings with java.sql.Timestamp parameters
• Removed unused SimpleDateFormat import

src/main/java/ca/openosp/openo/form/FrmLabReq07Record.java


23. src/main/java/ca/openosp/openo/encounter/data/EctARRecord.java Security enhancement +11/-11

Parameterize encounter AR record queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated DBHandler.GetPreSQLUpdatable() call for updatable result sets
• Replaced inline parameter concatenation with ? placeholders

src/main/java/ca/openosp/openo/encounter/data/EctARRecord.java


24. src/main/java/ca/openosp/openo/util/JDBCUtil.java Security enhancement +54/-23

Parameterize JDBC utility with table name validation

• Added VALID_NAME_PATTERN regex to validate table/form names (alphanumeric and underscores only)
• Added prepareWithValidatedTable() helper to build PreparedStatement with validated table names
• Converted toDataBase() method to use parameterized queries with validated table names and
 timestamps
• Added input validation for form names, demographic numbers, and timestamps extracted from zip
 entries

src/main/java/ca/openosp/openo/util/JDBCUtil.java


25. src/main/java/ca/openosp/openo/report/reportByTemplate/ReportObjectGeneric.java Security enhancement +83/-0

Parameterize report template SQL generation

• Added getParameterizedSQL() methods to replace {param} markers with ? placeholders
• Added parameterizeTemplate() core logic to handle single values, multi-value IN-clauses, and
 empty checkbox cases
• Added parameterizeToken() helper to replace individual tokens and collect bind parameters
• Supports NULL placeholders for unchecked checkboxes and comma-separated IN-clause values

src/main/java/ca/openosp/openo/report/reportByTemplate/ReportObjectGeneric.java


26. src/main/java/ca/openosp/openo/eform/actions/FetchUpdatedData2Action.java Security enhancement +33/-6

Validate and parameterize eForm fetch data action

• Added input validation for demographic, provider, and uuid parameters using regex patterns
 and SqlUtils
• Added parameterizeToken() helper method to replace ${name} placeholders with ? markers
• Updated DatabaseAP template substitution to use parameterized queries instead of string
 concatenation
• Updated EFormUtil method calls to pass parameter arrays for prepared statement binding

src/main/java/ca/openosp/openo/eform/actions/FetchUpdatedData2Action.java


27. src/main/java/ca/openosp/openo/form/FrmMentalHealthForm1Record.java Security enhancement +14/-18

Parameterize mental health form 1 queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately
• Replaced single-quoted string parameters with ? placeholders

src/main/java/ca/openosp/openo/form/FrmMentalHealthForm1Record.java


28. src/main/java/ca/openosp/openo/encounter/data/EctType2DiabetesRecord.java Security enhancement +8/-8

Parameterize encounter Type 2 Diabetes record queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated DBHandler.GetPreSQLUpdatable() call for updatable result sets
• Replaced inline parameter concatenation with ? placeholders

src/main/java/ca/openosp/openo/encounter/data/EctType2DiabetesRecord.java


29. src/main/java/ca/openosp/openo/report/reportByTemplate/SQLReporter.java Security enhancement +32/-10

Parameterize SQL reporter query execution

• Added template ID validation using SqlUtils.isNumericId()
• Updated generateReport() to use getParameterizedSQL() and pass parameter arrays to
 DBHandler.GetPreSQL()
• Updated generateSequencedReport() to use parameterized queries and added null/missing parameter
 checks
• Added setSequencedAttribute() helper method for consistent attribute naming

src/main/java/ca/openosp/openo/report/reportByTemplate/SQLReporter.java


30. src/main/java/ca/openosp/openo/form/FrmMentalHealthForm14Record.java Security enhancement +14/-18

Parameterize mental health form 14 queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately
• Replaced single-quoted string parameters with ? placeholders

src/main/java/ca/openosp/openo/form/FrmMentalHealthForm14Record.java


31. src/main/java/ca/openosp/openo/form/FrmMentalHealthForm42Record.java Security enhancement +14/-18

Parameterize mental health form 42 queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately
• Replaced single-quoted string parameters with ? placeholders

src/main/java/ca/openosp/openo/form/FrmMentalHealthForm42Record.java


32. src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java Security enhancement +12/-5

Parameterize MSP billing extract queries

• Converted string-concatenated SQL queries to parameterized queries using DBHandler.GetPreSQL()
• Replaced inline dbExt.executeQuery() calls with DBHandler.GetPreSQL() for parameterized
 execution
• Added try-with-resources for PreparedStatement to ensure proper resource cleanup
• Removed date range concatenation from query string

src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java


33. src/main/java/ca/openosp/openo/form/FrmLabReqRecord.java Security enhancement +14/-17

Parameterize lab request form queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately
• Replaced inline parameter concatenation with ? placeholders

src/main/java/ca/openosp/openo/form/FrmLabReqRecord.java


34. src/main/java/ca/openosp/openo/encounter/data/EctAlphaRecord.java Security enhancement +9/-9

Parameterize encounter Alpha record queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated DBHandler.GetPreSQLUpdatable() call for updatable result sets
• Replaced inline parameter concatenation with ? placeholders

src/main/java/ca/openosp/openo/encounter/data/EctAlphaRecord.java


35. src/main/java/ca/openosp/openo/form/FrmConsultantRecord.java Security enhancement +12/-12

Parameterize consultant form record queries

• Converted all string-concatenated SQL queries to parameterized queries using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters separately
• Replaced single-quoted string parameters with ? placeholders

src/main/java/ca/openosp/openo/form/FrmConsultantRecord.java


36. src/main/java/ca/openosp/openo/commn/dao/Billing3rdPartyAddressDaoImpl.java Security enhancement +22/-10

Validate billing 3rd party address query columns

• Added whitelists for valid column names (validColumns) and ORDER BY columns (validOrderBy)
• Validated orderByParam against whitelist before use in query construction
• Validated searchModeParam against whitelist before use as column name
• Converted limit/offset parameters to integers with try-catch parsing and used JPA
 setFirstResult()/setMaxResults()

src/main/java/ca/openosp/openo/commn/dao/Billing3rdPartyAddressDaoImpl.java


37. src/main/java/ca/openosp/openo/billings/ca/bc/MSP/CreateBillingReport2Action.java Security enhancement +3/-0

Validate MSP billing report parameters

• Added SqlUtils.validateNumericId() calls to validate rano parameter before use in MSP queries
• Added import for SqlUtils

src/main/java/ca/openosp/openo/billings/ca/bc/MSP/CreateBillingReport2Action.java


38. src/main/java/ca/openosp/openo/billings/ca/bc/pageUtil/ViewWCB2Action.java Security enhancement +1/-1

Parameterize WCB view query

• Converted string-concatenated SQL query to parameterized query using
 SqlUtils.getQueryResultsList() with ? placeholder
• Replaced inline provider number concatenation with parameter binding

src/main/java/ca/openosp/openo/billings/ca/bc/pageUtil/ViewWCB2Action.java


39. src/main/java/ca/openosp/openo/messenger/pageUtil/MsgViewMessageByPosition2Action.java 🐞 Bug fix +45/-6

Parameterize ORDER BY and validate message position parameter

• Added SqlUtils.isNumericId() validation for messagePosition parameter to prevent SQL injection
• Replaced dynamic ORDER BY concatenation with resolveMessageQueryWithSafeOrderBy() method using
 whitelisted column names
• Fixed type casting for query result from Integer to `((Number)
 query.getSingleResult()).intValue()`
• Introduced static helper method with hardcoded safe SQL and allowlist-based ORDER BY resolution

src/main/java/ca/openosp/openo/messenger/pageUtil/MsgViewMessageByPosition2Action.java


40. src/main/java/ca/openosp/openo/form/Frm2MinWalkRecord.java 🐞 Bug fix +10/-19

Parameterize form record queries with demographic and ID parameters

• Converted string-concatenated SQL queries to parameterized queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters instead
 of concatenating into SQL
• Replaced DBHandler.GetSQL() calls with DBHandler.GetPreSQL() for demographic_no and ID
 filtering

src/main/java/ca/openosp/openo/form/Frm2MinWalkRecord.java


41. src/main/java/ca/openosp/openo/report/data/RptReportConfigData.java 🐞 Bug fix +11/-14

Parameterize report configuration data access queries

• Replaced string concatenation in SQL queries with parameterized DBHandler.GetPreSQL() calls
• Updated all report configuration queries to use ? placeholders with parameter binding
• Changed from DBHelp.searchDBRecord() to DBHandler.GetPreSQL() for consistent parameterization

src/main/java/ca/openosp/openo/report/data/RptReportConfigData.java


42. src/main/java/ca/openosp/openo/encounter/data/EctMentalHealthRecord.java 🐞 Bug fix +11/-11

Parameterize mental health form record queries

• Converted demographic and provider queries to use DBHandler.GetPreSQL() with parameter binding
• Updated form record retrieval to use parameterized queries instead of string concatenation
• Replaced DBHandler.GetSQL(sql, true) with DBHandler.GetPreSQLUpdatable() for updatable result
 sets

src/main/java/ca/openosp/openo/encounter/data/EctMentalHealthRecord.java


43. src/main/java/ca/openosp/openo/form/FrmBCClientChartChecklistRecord.java 🐞 Bug fix +10/-16

Parameterize BC client chart checklist form queries

• Replaced DBHelp.searchDBRecord() with DBHandler.GetPreSQL() for parameterized queries
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as separate parameters
• Converted all string-concatenated SQL to use ? placeholders with parameter binding

src/main/java/ca/openosp/openo/form/FrmBCClientChartChecklistRecord.java


44. src/main/java/ca/openosp/openo/form/FrmBCHPRecord.java 🐞 Bug fix +10/-13

Parameterize BC health profile form record queries

• Removed DBHelp import and replaced with DBHandler for parameterized queries
• Updated demographic and form record queries to use DBHandler.GetPreSQL() with parameters
• Modified FrmRecordHelp calls to pass demographic_no and existingID as method parameters

src/main/java/ca/openosp/openo/form/FrmBCHPRecord.java


45. src/main/java/ca/openosp/openo/form/FrmAdfRecord.java 🐞 Bug fix +8/-17

Parameterize ADF form record queries

• Converted demographic and form record queries to parameterized format using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method signatures to accept demographic_no and existingID parameters
• Replaced string concatenation with ? placeholders throughout the file

src/main/java/ca/openosp/openo/form/FrmAdfRecord.java


46. src/main/java/ca/openosp/openo/form/FrmGrowthChartRecord.java 🐞 Bug fix +10/-10

Parameterize growth chart form record queries

• Parameterized demographic and form record queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass parameters instead of concatenating SQL
• Replaced all string-concatenated WHERE clauses with ? placeholders

src/main/java/ca/openosp/openo/form/FrmGrowthChartRecord.java


47. src/main/java/ca/openosp/openo/form/FrmGrowth0_36Record.java 🐞 Bug fix +10/-13

Parameterize growth 0-36 form record queries

• Converted demographic and form record queries to use DBHandler.GetPreSQL() with parameter
 binding
• Updated FrmRecordHelp method calls to accept demographic_no and existingID as parameters
• Replaced string concatenation with parameterized ? placeholders

src/main/java/ca/openosp/openo/form/FrmGrowth0_36Record.java


48. src/main/java/ca/openosp/openo/form/FrmMentalHealthRecord.java 🐞 Bug fix +12/-12

Parameterize mental health form record queries

• Parameterized demographic and provider queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmMentalHealthRecord.java


49. src/main/java/ca/openosp/openo/form/FrmLateLifeFDIDisabilityRecord.java 🐞 Bug fix +10/-19

Parameterize late life FDI disability form queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders throughout

src/main/java/ca/openosp/openo/form/FrmLateLifeFDIDisabilityRecord.java


50. src/main/java/ca/openosp/openo/form/FrmLateLifeFDIFunctionRecord.java 🐞 Bug fix +10/-19

Parameterize late life FDI function form queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmLateLifeFDIFunctionRecord.java


51. src/main/java/ca/openosp/openo/commn/dao/BillingONCHeader1DaoImpl.java 🐞 Bug fix +7/-24

Remove unsafe billing data query and parameterize provider invoice count

• Refactored getNumberOfDemographicsWithInvoicesForProvider() to use conditional SQL string
 selection instead of runtime concatenation
• Removed unsafe findBillingData(String conditions) method that concatenated raw SQL conditions
• Updated JPA query to use positional parameters (?1, ?2, ?3) for provider_no, startDate, and
 endDate

src/main/java/ca/openosp/openo/commn/dao/BillingONCHeader1DaoImpl.java


52. src/main/java/ca/openosp/openo/form/FrmGripStrengthRecord.java 🐞 Bug fix +12/-21

Parameterize grip strength form record queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmGripStrengthRecord.java


53. src/main/java/ca/openosp/openo/form/FrmSF36CaregiverRecord.java 🐞 Bug fix +10/-22

Parameterize SF36 caregiver form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmSF36CaregiverRecord.java


54. src/main/java/ca/openosp/openo/form/FrmSelfEfficacyRecord.java 🐞 Bug fix +10/-22

Parameterize self-efficacy form record queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders throughout

src/main/java/ca/openosp/openo/form/FrmSelfEfficacyRecord.java


55. src/main/java/ca/openosp/openo/form/FrmCostQuestionnaireRecord.java 🐞 Bug fix +10/-19

Parameterize cost questionnaire form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmCostQuestionnaireRecord.java


56. src/main/java/ca/openosp/openo/form/FrmIntakeInfoRecord.java 🐞 Bug fix +10/-19

Parameterize intake info form record queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders

src/main/java/ca/openosp/openo/form/FrmIntakeInfoRecord.java


57. src/main/java/ca/openosp/openo/form/FrmSelfAdministeredRecord.java 🐞 Bug fix +10/-22

Parameterize self-administered form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmSelfAdministeredRecord.java


58. src/main/java/ca/openosp/openo/form/FrmSatisfactionScaleRecord.java 🐞 Bug fix +10/-22

Parameterize satisfaction scale form record queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders throughout

src/main/java/ca/openosp/openo/form/FrmSatisfactionScaleRecord.java


59. src/main/java/ca/openosp/openo/form/FrmHomeFallsRecord.java 🐞 Bug fix +10/-19

Parameterize home falls form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmHomeFallsRecord.java


60. src/main/java/ca/openosp/openo/form/FrmInternetAccessRecord.java 🐞 Bug fix +10/-19

Parameterize internet access form record queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders

src/main/java/ca/openosp/openo/form/FrmInternetAccessRecord.java


61. src/main/java/ca/openosp/openo/form/FrmSF36Record.java 🐞 Bug fix +10/-22

Parameterize SF36 form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmSF36Record.java


62. src/main/java/ca/openosp/openo/form/FrmSelfManagementRecord.java 🐞 Bug fix +10/-22

Parameterize self-management form record queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders throughout

src/main/java/ca/openosp/openo/form/FrmSelfManagementRecord.java


63. src/main/java/ca/openosp/openo/form/FrmTreatmentPrefRecord.java 🐞 Bug fix +10/-22

Parameterize treatment preference form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmTreatmentPrefRecord.java


64. src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java 🐞 Bug fix +26/-1

Validate lookup table identifiers with regex allowlist

• Added VALID_TABLE_ID_PATTERN regex to validate tableId format (1-10 alphanumeric characters)
• Introduced validateTableId() method to enforce strict validation before lookup operations
• Replaced string indexOf check with Set.of() contains check for access control validation
• Added inline comments documenting parameterized HQL usage and validation defense-in-depth

src/main/java/ca/openosp/openo/www/lookup/LookupList2Action.java


65. src/main/java/ca/openosp/openo/form/FrmARRecord.java 🐞 Bug fix +8/-11

Parameterize AR form record queries

• Parameterized demographic and form record queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmARRecord.java


66. src/main/java/ca/openosp/openo/form/FrmAdfV2Record.java 🐞 Bug fix +8/-17

Parameterize ADF V2 form record queries

• Converted demographic and form record queries to parameterized format using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders throughout

src/main/java/ca/openosp/openo/form/FrmAdfV2Record.java


67. src/main/java/ca/openosp/openo/form/FrmAnnualV2Record.java 🐞 Bug fix +8/-8

Parameterize annual V2 form record queries

• Parameterized demographic and form record queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmAnnualV2Record.java


68. src/main/java/ca/openosp/openo/form/FrmAnnualRecord.java 🐞 Bug fix +8/-8

Parameterize annual form record queries

• Converted demographic and form record queries to parameterized format using
 DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders

src/main/java/ca/openosp/openo/form/FrmAnnualRecord.java


69. src/main/java/ca/openosp/openo/form/FrmFallsRecord.java 🐞 Bug fix +10/-19

Parameterize falls form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmFallsRecord.java


70. src/main/java/ca/openosp/openo/commn/dao/PregnancyFormsDao.java 🐞 Bug fix +13/-7

Parameterize pregnancy form data access queries

• Replaced DBHelp.searchDBRecord() with DBHandler.GetPreSQL() for parameterized queries
• Updated all pregnancy form queries to use ? placeholders with parameter binding
• Added explicit rs.close() calls to ensure proper resource cleanup

src/main/java/ca/openosp/openo/commn/dao/PregnancyFormsDao.java


71. src/main/java/ca/openosp/openo/form/FrmONARRecord.java 🐞 Bug fix +9/-13

Parameterize ONAR form record queries

• Replaced DBHelp.searchDBRecord() with DBHandler.GetPreSQL() for parameterized queries
• Removed unnecessary DBHelp instantiation
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters

src/main/java/ca/openosp/openo/form/FrmONARRecord.java


72. src/main/java/ca/openosp/openo/form/FrmCESDRecord.java 🐞 Bug fix +10/-19

Parameterize CESD form record queries

• Converted demographic and study queries to parameterized format using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced string concatenation with ? placeholders throughout

src/main/java/ca/openosp/openo/form/FrmCESDRecord.java


73. src/main/java/ca/openosp/openo/form/FrmCaregiverRecord.java 🐞 Bug fix +10/-19

Parameterize caregiver form record queries

• Parameterized demographic and study queries using DBHandler.GetPreSQL()
• Updated FrmRecordHelp method calls to pass demographic_no and existingID as parameters
• Replaced all string-concatenated SQL with ? placeholders

src/main/java/ca/openosp/openo/form/FrmCaregiverRecord.java


74. src/main/java/ca/openosp/openo/report/pageUtil/RptDownloadCSVServlet.java 🐞 Bug fix +17/-3

Validate report ID and query parameters with regex allowlist

• Added SqlUtils.isNumericId() validation for reportId parameter to prevent SQL injection
• Added regex validation (^[a-zA-Z0-9_ \-/:.,%]*$) for report query parameters in demoReport()
• Added inline comments documenting validation and controlled admin-only execution path

src/main/java/ca/openosp/openo/report/pageUtil/RptDownloadCSVServlet.java


75. src/main/java/ca/openosp/openo/commn/dao/DxDaoImpl.java 🐞 Bug fix +20/-6

Validate coding system with enum whitelist instead of regex

• Added isValidCodingSystem() method to validate codingSystem against enum whitelist instead of
 regex
• Updated three methods to use enum-based validation for stronger type safety
• Replaced regex pattern matching with enum valueOf() validation

<a href='https://github.com/openo-beta/Open-O/pull/2400/files#diff-bd546e2ea3337567ab44c719...

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 6, 2026

Code Review by Qodo

🐞 Bugs (1)   📘 Rule violations (1)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ☼ Reliability (1)
📘\ ⛨ Security (1)

Grey Divider


Action required

1. orderbyParam concatenated into SQL 📘
Description
PatientSearch.jsp builds an ORDER BY clause by concatenating request.getParameter("orderby")
into a SQL fragment. This violates the requirement to use parameterized SQL only (no user-influenced
string concatenation), even when an allowlist is present.
Code

src/main/webapp/oscarMDS/PatientSearch.jsp[R215-225]

+                if (request.getParameter("orderby") != null) {
+                    // Whitelist valid column names to prevent SQL injection
+                    String orderbyParam = request.getParameter("orderby");
+                    java.util.Set<String> validOrderBy = new java.util.HashSet<>(java.util.Arrays.asList(
+                        "last_name", "first_name", "demographic_no", "chart_no", "sex",
+                        "year_of_birth", "month_of_birth", "date_of_birth", "roster_status",
+                        "patient_status", "provider_no", "hin", "address", "phone"
+                    ));
+                    if (validOrderBy.contains(orderbyParam)) {
+                        orderby = "order by " + orderbyParam;
+                    }
Evidence
PR Compliance ID 2 forbids constructing SQL via concatenation of variables/user input. The added
code sets orderby = "order by " + orderbyParam, where orderbyParam comes from
request.getParameter("orderby").

CLAUDE.md
src/main/webapp/oscarMDS/PatientSearch.jsp[215-225]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`orderbyParam` from the request is concatenated into the SQL `ORDER BY` clause.
## Issue Context
Even with allowlisting, PR Compliance ID 2 requires avoiding SQL string concatenation with variables/user input.
## Fix Focus Areas
- src/main/webapp/oscarMDS/PatientSearch.jsp[215-225]
## Implementation notes
- Replace `orderby = "order by " + orderbyParam;` with a `switch`/mapping that assigns **fully-formed constant** clauses (e.g., `" order by last_name"`) per allowed option, without appending the request value.
- If feasible, consider non-dynamic sorting (separate queries per sort option) to eliminate dynamic SQL assembly.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. saveAsXML path not validated📘
Description
FrmRecordHelp.saveFormRecord(Properties, String, Object...) constructs fileName via string
concatenation and writes it with JDBCUtil.saveAsXML without using PathValidationUtils. If any
component is user-influenced (e.g., demographic_no from props), this can enable path traversal
or unsafe filesystem writes.
Code

src/main/java/ca/openosp/openo/form/FrmRecordHelp.java[R259-273]

+        if (saveAsXml.equalsIgnoreCase("true")) {
+            String demographicNo = props.getProperty("demographic_no");
+            int index = sql.indexOf("form");
+            int spaceIndex = sql.indexOf(" ", index);
+            String formClass = sql.substring(index, spaceIndex);
+            Date d = new Date();
+            String now = UtilDateUtilities.DateToString(d, "yyyyMMddHHmmss");
+            String place = OscarProperties.getInstance().getProperty("form_record_path", "/root");
+            if (!place.endsWith(System.getProperty("file.separator")))
+                place = place + System.getProperty("file.separator");
+            String fileName = place + formClass + "_" + demographicNo + "_" + now + ".xml";
+            try {
+                Document doc = JDBCUtil.toDocument(rs);
+                JDBCUtil.saveAsXML(doc, fileName);
+            } catch (Exception e) {
Evidence
PR Compliance ID 5 requires PathValidationUtils for file path operations involving user input. The
new code builds fileName = place + formClass + "_" + demographicNo + ... and passes it to
JDBCUtil.saveAsXML with no PathValidationUtils validation.

CLAUDE.md
src/main/java/ca/openosp/openo/form/FrmRecordHelp.java[259-273]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A filesystem path is assembled by concatenating strings and is used for writing XML without `PathValidationUtils` validation.
## Issue Context
`demographicNo` is sourced from `props`, and `formClass` is derived from `sql`; either can become unsafe if influenced by external inputs. Compliance requires central path validation utilities.
## Fix Focus Areas
- src/main/java/ca/openosp/openo/form/FrmRecordHelp.java[259-273]
## Implementation notes
- Build the destination using `Paths.get(baseDir, filename).normalize()`.
- Validate the resulting path using `PathValidationUtils` (e.g., `validatePath` / `validateExistingPath` / temp-directory checks as appropriate) before writing.
- Sanitize filename components (e.g., restrict `formClass`/`demographicNo` to a safe pattern) to prevent injecting separators or `..`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Lookup field regex breaks config🐞
Description
LookupDaoImpl.validateFieldSql() rejects SQL expressions containing quoted literals (e.g., the
documented concat(first_name, ' ', last_name)), so lookup screens can fail at runtime with
IllegalArgumentException when such DB-configured FieldDefValue expressions are used. The new
validation is executed during SQL construction (not at admin-save time), turning existing configs
into hard runtime failures.
Code

src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java[R40-58]

+    /**
+     * More permissive pattern for DB-configured field expressions that may contain
+     * SQL functions like {@code concat(first_name, ' ', last_name)}.
+     * For simple table/column names, use {@link SqlUtils#validateTableName(String)} or
+     * {@link SqlUtils#validateColumnName(String)} instead.
+     */
+    private static final java.util.regex.Pattern SAFE_SQL_EXPRESSION = java.util.regex.Pattern.compile("^[a-zA-Z0-9_()., ]+$");
+
+    /**
+     * Validates that a field expression from DB config is safe for use in queries.
+     * @param fieldSql the field name or expression from FieldDefValue
+     * @return the validated field expression
+     * @throws IllegalArgumentException if the expression contains unsafe characters
+     */
+    private static String validateFieldSql(String fieldSql) {
+        if (fieldSql == null || !SAFE_SQL_EXPRESSION.matcher(fieldSql).matches()) {
+            throw new IllegalArgumentException("Invalid field name from config");
+        }
+        return fieldSql;
Evidence
The newly introduced SAFE_SQL_EXPRESSION explicitly excludes single quotes, but the adjacent comment
states DB-configured expressions may include quoted literals; validateFieldSql throws if the regex
doesn’t match, and the method is called when building the SELECT list for lookups, so incompatible
configs will throw during request handling.

src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java[40-59]
src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java[102-122]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`LookupDaoImpl` introduced `SAFE_SQL_EXPRESSION`/`validateFieldSql` to validate DB-configured field expressions, but the allowlist regex (`^[a-zA-Z0-9_()., ]+$`) rejects single quotes. This contradicts the documented supported expression example `concat(first_name, ' ', last_name)` and will throw `IllegalArgumentException` at runtime when such configs exist.
### Issue Context
This validation is applied while constructing lookup SQL (not during config entry), so existing deployments with such expressions will break lookups immediately.
### Fix Focus Areas
- src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java[40-59]
- src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java[114-122]
### What to change
- Update `SAFE_SQL_EXPRESSION` to permit the characters required by the documented supported expressions (at minimum include single quotes). 
- Consider whether additional operators/functions used in real configs (e.g., `+`, `-`, `||`) should be allowed, or replace the regex approach with an explicit allowlist of known-safe functions and identifier tokens.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Statements leaked by ResultSet API 🐞
Description
DBHandler.GetPreSQL() and the newly added GetPreSQLUpdatable() create a PreparedStatement and return
its ResultSet without any mechanism to close the statement when callers close the ResultSet, keeping
statement/cursor resources open until the thread-local Connection is released. This can exhaust DB
resources during requests that run multiple queries (including form save/update paths that use the
new updatable ResultSet method).
Code

src/main/java/ca/openosp/openo/db/DBHandler.java[R98-110]

return ps.executeQuery();
}
+	/**
+	 * Parameterized query with updatable ResultSet support.
+	 * Used by form record classes that insert/update rows via ResultSet cursor operations.
+	 */
+	public static ResultSet GetPreSQLUpdatable(String sql, Object... params) throws SQLException {
+		PreparedStatement ps = DbConnectionFilter.getThreadLocalDbConnection()
+			.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
+		bindParams(ps, params);
+		return ps.executeQuery();
+	}
Evidence
DBHandler returns ResultSets from freshly created PreparedStatements without closing the statement
or enabling close-on-completion. Callers added/updated in this PR (e.g., FrmRecordHelp and SqlUtils)
close only the ResultSet, and DbConnectionFilter only closes the thread-local Connection at the end
of the request, so statements remain open for the duration of the request.

src/main/java/ca/openosp/openo/db/DBHandler.java[93-110]
src/main/java/ca/openosp/openo/form/FrmRecordHelp.java[252-308]
src/main/java/ca/openosp/openo/util/SqlUtils.java[222-242]
src/main/java/ca/openosp/openo/utility/DbConnectionFilter.java[65-84]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`DBHandler.GetPreSQL` and `DBHandler.GetPreSQLUpdatable` allocate a `PreparedStatement` and return a `ResultSet` without closing the statement. Many callers (including ones introduced/expanded in this PR) close only the `ResultSet`, leaving statement/cursor resources open until the thread-local `Connection` is closed at end-of-request.
### Issue Context
This is particularly risky for `GetPreSQLUpdatable` (updatable cursors are heavier) and for helper methods like `SqlUtils.getQueryResultsList` that may be called repeatedly within one request.
### Fix Focus Areas
- src/main/java/ca/openosp/openo/db/DBHandler.java[93-110]
### What to change
- After creating the `PreparedStatement`, call `ps.closeOnCompletion()` so the statement closes automatically when the returned `ResultSet` is closed.
- Alternatively (more invasive), change the API to not return raw `ResultSet` (e.g., accept a callback/mapper and use try-with-resources internally), but `closeOnCompletion()` is the minimal, compatible fix.
- Apply the same pattern to both `GetPreSQL` and `GetPreSQLUpdatable` (and consider doing likewise for the deprecated `GetSQL` Statement path if still heavily used).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. SqlUtils.getQueryResultsList JavaDoc incomplete📘
Description
New public methods getQueryResultsList(String, Object...) and getRow(String, Object...) have
JavaDoc that omits required tags (e.g., @param, @return, and any thrown exceptions). This
violates the comprehensive JavaDoc requirement for public methods.
Code

src/main/java/ca/openosp/openo/util/SqlUtils.java[R219-258]

+    /**
+     * Returns a List of String[] which contain the results of the specified parameterized query.
+     */
+    public static List<String[]> getQueryResultsList(String qry, Object... params) {
+        ArrayList<String[]> records = null;
+        ResultSet rs = null;
+        try {
+            records = new ArrayList<String[]>();
+            rs = DBHandler.GetPreSQL(qry, params);
+            int cols = rs.getMetaData().getColumnCount();
+            while (rs.next()) {
+                String[] record = new String[cols];
+                for (int i = 0; i < cols; i++) {
+                    record[i] = Misc.getString(rs, i + 1);
+                }
+                records.add(record);
+            }
+        } catch (SQLException e) {
+            records = null;
+            MiscUtils.getLogger().error("Error", e);
+        }
+        if (rs != null) {
+            try { rs.close(); } catch (SQLException ex1) { MiscUtils.getLogger().error("Error", ex1); }
+        }
+        if (records != null) {
+            records = records.isEmpty() ? null : records;
+        }
+        return records;
+    }
+
+    /**
+     * Returns a single row from a parameterized query result.
+     */
+    public static String[] getRow(String qry, Object... params) {
+        String ret[] = null;
+        List<String[]> list = getQueryResultsList(qry, params);
+        if (list != null) {
+            ret = list.get(0);
+        }
+        return ret;
Evidence
PR Compliance ID 8 requires comprehensive JavaDoc and specific tag requirements for public methods.
The added JavaDoc blocks for these new public overloads are brief and do not include required
@param/@return documentation.

CLAUDE.md
src/main/java/ca/openosp/openo/util/SqlUtils.java[219-258]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New public `SqlUtils` methods have incomplete JavaDoc missing required tags.
## Issue Context
Compliance requires complete JavaDoc for public methods, including `@param` (with type in description) and `@return` with exact return type.
## Fix Focus Areas
- src/main/java/ca/openosp/openo/util/SqlUtils.java[219-258]
## Implementation notes
- Add `@param` tags for `qry` and `params` (include type in description).
- Add `@return` tags specifying `List<String[]>` / `String[]`.
- Document any relevant error/exception behavior (e.g., returns `null`, logs SQL exceptions).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread src/main/webapp/oscarMDS/PatientSearch.jsp
Comment thread src/main/java/ca/openosp/openo/form/FrmRecordHelp.java
Comment thread src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java
Comment thread src/main/java/ca/openosp/openo/db/DBHandler.java
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 148 files

Confidence score: 2/5

  • High risk: Set.of() in src/main/java/ca/openosp/openo/eform/EFormUtil.java will throw at runtime due to duplicate elements, which can crash form handling.
  • Security/behavior concerns: src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java regex can allow unsafe SQL sequences, and src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java drops the date-range filter causing unexpected data expansion.
  • Overall severity is elevated due to a definite runtime exception plus user-facing query/regression risks; proceed only after fixes.
  • Pay close attention to src/main/java/ca/openosp/openo/eform/EFormUtil.java, src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java, src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java - crash risk, SQL safety, and missing filter behavior.

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/main/java/ca/openosp/openo/encounter/data/EctPatientData.java">

<violation number="1" location="src/main/java/ca/openosp/openo/encounter/data/EctPatientData.java:54">
P2: `Integer.parseInt(demographicNo)` is unhandled here; non-numeric input will throw `NumberFormatException` and bypass the existing `SQLException` handling.</violation>
</file>

<file name="src/main/java/ca/openosp/openo/form/FrmGrowth0_36Record.java">

<violation number="1" location="src/main/java/ca/openosp/openo/form/FrmGrowth0_36Record.java:27">
P2: Close this `ResultSet` in the `else` branch (prefer try-with-resources) to avoid leaking JDBC resources.</violation>
</file>

<file name="src/main/java/ca/openosp/openo/form/FrmGripStrengthRecord.java">

<violation number="1" location="src/main/java/ca/openosp/openo/form/FrmGripStrengthRecord.java:61">
P2: The `studyID` query is duplicated consecutively, causing an unnecessary extra DB call with no behavioral benefit.</violation>
</file>

<file name="src/main/java/ca/openosp/openo/commn/dao/DemographicDaoImpl.java">

<violation number="1" location="src/main/java/ca/openosp/openo/commn/dao/DemographicDaoImpl.java:2506">
P2: Guard against null `fieldName` before using `String.concat`; this new code will throw a NullPointerException if `fieldName` is null.</violation>
</file>

<file name="src/main/java/ca/openosp/openo/eform/EFormUtil.java">

<violation number="1" location="src/main/java/ca/openosp/openo/eform/EFormUtil.java:112">
P0: `Set.of()` throws `IllegalArgumentException` when given duplicate elements. Since `NAME` equals `"form_name"`, `SUBJECT` equals `"subject"`, etc., there are 5 duplicate pairs here. This will crash with `ExceptionInInitializerError` when the class is loaded, making all EForm functionality unusable.

Remove the duplicate string literals and keep only the constants plus the two extras (`"form_date"`, `"form_time"`) that don't have corresponding constants.</violation>
</file>

<file name="src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java">

<violation number="1" location="src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java:157">
P2: `dateRange` is still set via `setDateRange(...)`, but it is no longer applied to the billing query. This drops the date-range filter entirely and will return all open/waiting records for the provider instead of the requested range.</violation>
</file>

<file name="src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java">

<violation number="1" location="src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java:46">
P2: The `SAFE_SQL_EXPRESSION` regex allows spaces and parentheses together, which permits arbitrary SQL keyword sequences (e.g., `field) UNION SELECT secret FROM users WHERE (1`) to pass validation. For a pattern whose stated purpose is preventing second-order SQL injection from DB config, this is overly permissive. Consider either (a) disallowing spaces (using a strict identifier pattern with only `_`, `.`, `()`, `,`) or (b) adding an explicit keyword denylist. 

Also, the Javadoc example `concat(first_name, ' ', last_name)` would fail this regex because `'` is not in the allowed character class — the comment should be corrected to match the actual behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/main/java/ca/openosp/openo/eform/EFormUtil.java
Comment thread src/main/java/ca/openosp/openo/encounter/data/EctPatientData.java
Comment thread src/main/java/ca/openosp/openo/form/FrmGrowth0_36Record.java
Comment thread src/main/java/ca/openosp/openo/form/FrmGripStrengthRecord.java
Comment thread src/main/java/ca/openosp/openo/commn/dao/DemographicDaoImpl.java Outdated
Comment thread src/main/java/ca/openosp/openo/billings/MSP/ExtractBean.java
Comment thread src/main/java/ca/openosp/openo/daos/LookupDaoImpl.java Outdated
@LiamStanziani
Copy link
Copy Markdown
Collaborator Author

LiamStanziani commented Apr 7, 2026

This should be good for review now.

I have added a section for manually tested areas.

I expect that overall testing with these changes would be good when testing out the general application, as many of these changes should be caught with that approach anyway.

I expect this to be the same approach used for the Path Traversal and XSS fixes, since there will be so many changes that targeted testing of every change will most likely take too long, and should be delegated to when overall testing is done (so repeatedly testing areas of the project could cover many different changes at once for issues).

@LiamStanziani LiamStanziani requested a review from D3V41 April 7, 2026 15:05
Comment thread src/main/java/ca/openosp/openo/form/FrmRecordHelp.java Outdated
Comment thread src/main/java/ca/openosp/openo/form/FrmRecordHelp.java Outdated
Comment thread src/main/java/ca/openosp/openo/form/FrmRecordHelp.java Outdated
Comment thread src/main/java/ca/openosp/openo/util/SqlUtils.java Outdated
Comment thread src/main/java/ca/openosp/openo/util/SqlUtils.java Outdated
Comment thread src/main/java/ca/openosp/openo/util/SqlUtils.java Outdated
Comment thread src/main/java/ca/openosp/openo/eform/data/EForm.java Outdated
Comment thread src/main/java/ca/openosp/openo/eform/EFormUtil.java Outdated
Copy link
Copy Markdown
Collaborator

@D3V41 D3V41 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks amazing, @LiamStanziani. Just one question: would it make sense to remove these deprecated methods and GetSQL() from the DBHandler? What do you think?

@LiamStanziani
Copy link
Copy Markdown
Collaborator Author

LiamStanziani commented Apr 10, 2026

Thank you! @D3V41, yeah I think that would be a good idea, I'll review soon, I assume they are reworked already so that these methods probably have no callers currently and will most likely be removed in a commit after i check over these.

…s to work with sql parm version of the old deprecated methods, avoided removing DBHandler.GetSQL for now as the remaining calls are quite complex, and should probably be done in its own PR
@LiamStanziani
Copy link
Copy Markdown
Collaborator Author

LiamStanziani commented Apr 10, 2026

As an update @D3V41

I have removed the flagged methods you have mentioned in the review, and some other unused methods I found along the way, as well as updating the connections left connected to the old method to fit the PreSQL() method version.

The only thing skipped was removing GetSQL() from DBHandler, the reason for this was that the remaining calls seemed to be quite complex dynamic report building, so I was thinking about fixing it in its own branch, let me know if you disagree with that.

here is the commit if you would like to review the new changes: db43ea5

@D3V41
Copy link
Copy Markdown
Collaborator

D3V41 commented Apr 15, 2026

@LiamStanziani Reviewed it. We can handle the GetSQL() method another day. Overall, the PR looks good. Thank you.

@LiamStanziani LiamStanziani merged commit de42189 into maintenance Apr 15, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants