Skip to content

Security: Fix XSS and JSON injection in SearchDemographicAutoComplete2Action#296

Open
FinnBotOrca wants to merge 6 commits intocarlos-emr:developfrom
FinnBotOrca:fix/xss-autocomplete
Open

Security: Fix XSS and JSON injection in SearchDemographicAutoComplete2Action#296
FinnBotOrca wants to merge 6 commits intocarlos-emr:developfrom
FinnBotOrca:fix/xss-autocomplete

Conversation

@FinnBotOrca
Copy link

@FinnBotOrca FinnBotOrca commented Feb 7, 2026

User description

This PR fixes XSS vulnerabilities in the SearchDemographicAutoComplete2Action endpoint by using Encode.forHtml() for escaping patient names, status, and provider names. It also refactors formatJSON() to use ObjectMapper to prevent JSON injection. Fixes #250


Description


Changes walkthrough 📝

Relevant files
Bug fix
SearchDemographicAutoComplete2Action.java
Improve XSS and JSON injection prevention in
SearchDemographicAutoComplete2Action

src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java

  • Added Encode.forHtml() to escape patient names, statuses, and provider
    names to prevent XSS.
  • Refactored formatJSON() to use ObjectMapper for JSON serialization,
    enhancing security against JSON injection.
  • +19/-23 

    💡 Penify usage:
    Comment /help on the PR to get a list of all available Penify tools and their descriptions

    Summary by CodeRabbit

    • Bug Fixes
      • HTML-encode demographic fields and return structured JSON for safer, consistent client rendering.
    • Security
      • Enforced access control for demographic reads; unauthorized requests now receive a clear no-privilege response.
    • Refactor
      • Switched autocomplete item rendering from HTML-string assembly to DOM-based construction for safer, more robust UI updates.

    @FinnBotOrca FinnBotOrca requested a review from yingbull as a code owner February 7, 2026 14:02
    @sourcery-ai
    Copy link
    Contributor

    sourcery-ai bot commented Feb 7, 2026

    Reviewer's guide (collapsed on small PRs)

    Reviewer's Guide

    Refactors SearchDemographicAutoComplete2Action to properly HTML-encode user-visible fields and to generate JSON responses via ObjectMapper instead of manual string concatenation, eliminating XSS and JSON injection risks.

    Sequence diagram for SearchDemographicAutoComplete2Action secure response generation

    sequenceDiagram
        actor Client
        participant SearchDemographicAutoComplete2Action
        participant Demographic
        participant Provider
        participant Encode
        participant ObjectMapper
    
        Client->>SearchDemographicAutoComplete2Action: HTTP request (search term)
        SearchDemographicAutoComplete2Action->>Demographic: query matching demographics
        Demographic-->>SearchDemographicAutoComplete2Action: List<Demographic>
    
        loop for each Demographic
            SearchDemographicAutoComplete2Action->>Demographic: getFormattedName, getFormattedDob, getPatientStatus, getProviderNo
            Demographic-->>SearchDemographicAutoComplete2Action: name, dob, status, providerNo
    
            SearchDemographicAutoComplete2Action->>Provider: getProvider(providerNo)
            Provider-->>SearchDemographicAutoComplete2Action: Provider
            SearchDemographicAutoComplete2Action->>Provider: getSurname, getFirstName
            Provider-->>SearchDemographicAutoComplete2Action: surname, firstName
    
            SearchDemographicAutoComplete2Action->>Encode: forHtml(name, status, providerName)
            Encode-->>SearchDemographicAutoComplete2Action: htmlEncoded values
    
            SearchDemographicAutoComplete2Action->>SearchDemographicAutoComplete2Action: build HashMap record
        end
    
        SearchDemographicAutoComplete2Action->>ObjectMapper: writeValueAsString(results)
        ObjectMapper-->>SearchDemographicAutoComplete2Action: JSON array string
        SearchDemographicAutoComplete2Action-->>Client: HTTP response (JSON)
    
    Loading

    Class diagram for SearchDemographicAutoComplete2Action security refactor

    classDiagram
        class SearchDemographicAutoComplete2Action {
            - ObjectMapper objectMapper
            + String execute() throws Exception
            - String formatJSON(List<HashMap<String, String>> info) throws Exception
        }
    
        class Demographic {
            + String getFormattedDob()
            + String getFormattedName()
            + String getPatientStatus()
            + int getDemographicNo()
            + String getProviderNo()
        }
    
        class Provider {
            + String getSurname()
            + String getFirstName()
        }
    
        class ObjectMapper {
            + String writeValueAsString(Object value) throws Exception
        }
    
        class Encode {
            <<static>>
            + String forHtml(String input)
        }
    
        SearchDemographicAutoComplete2Action --> Demographic : reads
        SearchDemographicAutoComplete2Action --> Provider : reads
        SearchDemographicAutoComplete2Action --> ObjectMapper : uses
        SearchDemographicAutoComplete2Action --> Encode : uses
    
    Loading

    Flow diagram for secure JSON construction in formatJSON

    flowchart TD
        A[Input info List<HashMap>] --> B[Initialize empty results List<HashMap>]
        B --> C{For each record in info}
        C -->|yes| D[Create new HashMap h]
        D --> E[Set h label = formattedName + fomattedDob + status]
        E --> F[Set h value = demographicNo]
        F --> G[Set h providerNo, provider, nextAppt, formattedName]
        G --> H[Add h to results]
        H --> C
        C -->|no| I["Call objectMapper.writeValueAsString(results)"]
        I --> J[Return JSON string]
    
    Loading

    File-Level Changes

    Change Details Files
    Escape all user-visible text fields in autocomplete results using OWASP Encode.forHtml to mitigate XSS.
    • Replace StringEscapeUtils-based escaping of formattedName with Encode.forHtml
    • HTML-encode patient status before including it in the response payload
    • HTML-encode provider surname/first name combinations for primary and custom providers before adding them to the response map
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java
    Refactor JSON response construction to use Jackson ObjectMapper instead of manual string concatenation to prevent JSON injection and ensure valid JSON.
    • Change formatJSON signature to declare throws Exception and internally build a list of maps representing result items
    • For each input record, construct a new HashMap with label, value, providerNo, provider, nextAppt, and formattedName fields based on the existing map
    • Serialize the collected result list using objectMapper.writeValueAsString instead of manually appending JSON strings
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java

    Assessment against linked issues

    Issue Objective Addressed Explanation
    #250 Use proper HTML escaping (OWASP Encode.forHtml) for all user-controllable fields in SearchDemographicAutoComplete2Action, including formattedName, status, providerName, and cust*Name fields.
    #250 Refactor formatJSON() in SearchDemographicAutoComplete2Action to use a JSON library (Jackson ObjectMapper) instead of manual string concatenation, preventing JSON injection and ensuring correct JSON encoding.

    Possibly linked issues


    Tips and commands

    Interacting with Sourcery

    • Trigger a new review: Comment @sourcery-ai review on the pull request.
    • Continue discussions: Reply directly to Sourcery's review comments.
    • Generate a GitHub issue from a review comment: Ask Sourcery to create an
      issue from a review comment by replying to it. You can also reply to a
      review comment with @sourcery-ai issue to create an issue from it.
    • Generate a pull request title: Write @sourcery-ai anywhere in the pull
      request title to generate a title at any time. You can also comment
      @sourcery-ai title on the pull request to (re-)generate the title at any time.
    • Generate a pull request summary: Write @sourcery-ai summary anywhere in
      the pull request body to generate a PR summary at any time exactly where you
      want it. You can also comment @sourcery-ai summary on the pull request to
      (re-)generate the summary at any time.
    • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
      request to (re-)generate the reviewer's guide at any time.
    • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
      pull request to resolve all Sourcery comments. Useful if you've already
      addressed all the comments and don't want to see them anymore.
    • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
      request to dismiss all existing Sourcery reviews. Especially useful if you
      want to start fresh with a new review - don't forget to comment
      @sourcery-ai review to trigger a new review!

    Customizing Your Experience

    Access your dashboard to:

    • Enable or disable review features such as the Sourcery-generated pull request
      summary, the reviewer's guide, and others.
    • Change the review language.
    • Add, remove or edit custom review instructions.
    • Adjust other review settings.

    Getting Help

    @gemini-code-assist
    Copy link
    Contributor

    Summary of Changes

    Hello @FinnBotOrca, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

    This pull request significantly enhances the security posture of the SearchDemographicAutoComplete2Action endpoint. It addresses potential Cross-Site Scripting (XSS) vulnerabilities by implementing robust HTML encoding for sensitive patient and provider data. Additionally, it improves the integrity of JSON output by transitioning from manual string concatenation to a secure ObjectMapper-based serialization approach, effectively preventing JSON injection attacks.

    Highlights

    • XSS Vulnerability Fixes: Implemented Encode.forHtml() from the OWASP Encoder library to escape patient names, status, and provider names, mitigating Cross-Site Scripting (XSS) vulnerabilities in the SearchDemographicAutoComplete2Action endpoint.
    • JSON Injection Prevention: Refactored the formatJSON() method to utilize com.fasterxml.jackson.databind.ObjectMapper for JSON serialization, replacing manual string concatenation. This change prevents JSON injection vulnerabilities and ensures robust JSON output.
    • Dependency Update: Added the org.owasp.encoder.Encode import to support the new HTML encoding practices.

    🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

    Changelog
    • src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java
      • Added import for org.owasp.encoder.Encode.
      • Replaced StringEscapeUtils.escapeJava and direct string concatenation with Encode.forHtml() for formattedName, status, providerName, cust1Name, cust2Name, and cust4Name fields.
      • Completely refactored the formatJSON method to use objectMapper.writeValueAsString() for secure JSON serialization, replacing the previous StringBuilder approach.
    Activity
    • No specific activity (comments, reviews, progress) has been recorded for this pull request yet.
    Using Gemini Code Assist

    The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

    Invoking Gemini

    You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

    Feature Command Description
    Code Review /gemini review Performs a code review for the current pull request in its current state.
    Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
    Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
    Help /gemini help Displays a list of available commands.

    Customization

    To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

    Limitations & Feedback

    Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

    You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

    Footnotes

    1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

    @penify-dev
    Copy link
    Contributor

    penify-dev bot commented Feb 7, 2026

    PR Review 🔍

    ⏱️ Estimated effort to review [1-5]

    3, because the changes involve security improvements and refactoring, which require careful review to ensure no new issues are introduced.

    🧪 Relevant tests

    No

    ⚡ Possible issues

    No

    🔒 Security concerns

    No

    @qodo-code-review
    Copy link

    Review Summary by Qodo

    Fix XSS and JSON injection vulnerabilities in SearchDemographicAutoComplete2Action

    🐞 Bug fix

    Grey Divider

    Walkthroughs

    Description
    • Replace unsafe string escaping with OWASP Encode.forHtml() for XSS prevention
    • Refactor formatJSON() to use ObjectMapper instead of manual string concatenation
    • Apply HTML encoding to patient names, status, and provider names
    • Eliminate JSON injection vulnerability through proper serialization
    
    Diagram
    flowchart LR
      A["Unsafe String Escaping"] -->|Replace with| B["OWASP Encode.forHtml()"]
      C["Manual JSON String Building"] -->|Refactor to| D["ObjectMapper Serialization"]
      B --> E["XSS Prevention"]
      D --> F["JSON Injection Prevention"]
    
    Loading

    Grey Divider

    File Changes

    1. src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java 🐞 Bug fix +19/-23

    Secure HTML encoding and JSON serialization implementation

    • Added OWASP Encoder import for secure HTML encoding
    • Replaced StringEscapeUtils.escapeJava() with Encode.forHtml() for formatted names
    • Applied Encode.forHtml() to patient status and all provider name fields
    • Refactored formatJSON() method to use ObjectMapper.writeValueAsString() instead of manual
     StringBuilder concatenation
    • Changed formatJSON() signature to throw Exception for proper error handling
    

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java


    Grey Divider

    Qodo Logo

    @penify-dev
    Copy link
    Contributor

    penify-dev bot commented Feb 7, 2026

    PR Code Suggestions ✨

    CategorySuggestion                                                                                                                                    Score
    Security
    Ensure consistent use of encoding for all user-generated content
    Suggestion Impact:The commit adjusted handling of user-generated fields by introducing separate HTML-encoded variants (e.g., formattedNameHtml/statusHtml/providerNameHtml) using Encode.forHtml and also HTML-encoding the composed "label" value, rather than encoding the original formattedName field directly.

    code diff:

    @@ -124,9 +130,11 @@
             for (Demographic demo : list) {
                 HashMap<String, String> h = new HashMap<String, String>();
                 h.put("fomattedDob", demo.getFormattedDob());
    -            h.put("formattedName", Encode.forHtml(demo.getFormattedName()));
    +            h.put("formattedName", demo.getFormattedName());
    +            h.put("formattedNameHtml", Encode.forHtml(demo.getFormattedName()));
                 h.put("demographicNo", String.valueOf(demo.getDemographicNo()));
    -            h.put("status", Encode.forHtml(demo.getPatientStatus()));
    +            h.put("status", demo.getPatientStatus());
    +            h.put("statusHtml", Encode.forHtml(demo.getPatientStatus()));
     
     
                 Provider p = rx.getProvider(demo.getProviderNo());
    @@ -134,7 +142,8 @@
                     h.put("providerNo", demo.getProviderNo());
                 }
                 if (p.getSurname() != null && p.getFirstName() != null) {
    -                h.put("providerName", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    +                h.put("providerName", p.getSurname() + ", " + p.getFirstName());
    +                h.put("providerNameHtml", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                 }
     
                 if (OscarProperties.getInstance().isPropertyActive("workflow_enhance")) {
    @@ -149,17 +158,20 @@
                         if (cust1 != null) {
                             h.put("cust1", cust1);
                             p = rx.getProvider(cust1);
    -                        h.put("cust1Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    +                        h.put("cust1Name", p.getSurname() + ", " + p.getFirstName());
    +                        h.put("cust1NameHtml", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                         }
                         if (cust2 != null) {
                             h.put("cust2", cust2);
                             p = rx.getProvider(cust2);
    -                        h.put("cust2Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    +                        h.put("cust2Name", p.getSurname() + ", " + p.getFirstName());
    +                        h.put("cust2NameHtml", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                         }
                         if (cust4 != null) {
                             h.put("cust4", cust4);
                             p = rx.getProvider(cust4);
    -                        h.put("cust4Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    +                        h.put("cust4Name", p.getSurname() + ", " + p.getFirstName());
    +                        h.put("cust4NameHtml", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                         }
                     }
                 }
    @@ -183,10 +195,10 @@
         }
     
         private String formatJSON(List<HashMap<String, String>> info) throws Exception {
    -        List<HashMap<String, String>> results = new ArrayList<>();
    +        List<HashMap<String, String>> results = new ArrayList<>(info.size());
             for (HashMap<String, String> record : info) {
                 HashMap<String, String> h = new HashMap<>();
    -            h.put("label", record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")");
    +            h.put("label", Encode.forHtml(record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")"));
                 h.put("value", record.get("demographicNo"));

    Ensure that the Encode.forHtml method is used consistently for all user-generated content
    to prevent XSS vulnerabilities.

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java [127]

    +h.put("formattedName", Encode.forHtml(demo.getFormattedName()));
     
    -
    Suggestion importance[1-10]: 10

    Why: This suggestion addresses a critical security concern by ensuring that user-generated content is properly encoded to prevent XSS vulnerabilities.

    10
    Apply encoding to all concatenated strings that include user input to prevent XSS
    Suggestion Impact:The commit added HTML-encoded variants for concatenated name fields (cust1NameHtml, cust2NameHtml, cust4NameHtml, providerNameHtml) using Encode.forHtml, and also encoded the concatenated "label" output. However, it kept the original keys (e.g., cust1Name) unencoded and introduced separate *Html fields instead of encoding the existing fields directly.

    code diff:

    @@ -149,17 +158,20 @@
                         if (cust1 != null) {
                             h.put("cust1", cust1);
                             p = rx.getProvider(cust1);
    -                        h.put("cust1Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    +                        h.put("cust1Name", p.getSurname() + ", " + p.getFirstName());
    +                        h.put("cust1NameHtml", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                         }
                         if (cust2 != null) {
                             h.put("cust2", cust2);
                             p = rx.getProvider(cust2);
    -                        h.put("cust2Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    +                        h.put("cust2Name", p.getSurname() + ", " + p.getFirstName());
    +                        h.put("cust2NameHtml", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                         }
                         if (cust4 != null) {
                             h.put("cust4", cust4);
                             p = rx.getProvider(cust4);
    -                        h.put("cust4Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    +                        h.put("cust4Name", p.getSurname() + ", " + p.getFirstName());
    +                        h.put("cust4NameHtml", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                         }
                     }
                 }
    @@ -183,10 +195,10 @@
         }
     
         private String formatJSON(List<HashMap<String, String>> info) throws Exception {
    -        List<HashMap<String, String>> results = new ArrayList<>();
    +        List<HashMap<String, String>> results = new ArrayList<>(info.size());
             for (HashMap<String, String> record : info) {
                 HashMap<String, String> h = new HashMap<>();
    -            h.put("label", record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")");
    +            h.put("label", Encode.forHtml(record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")"));
                 h.put("value", record.get("demographicNo"));

    Ensure that the Encode.forHtml method is applied to all concatenated strings that include
    user input to prevent XSS.

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java [152]

    +h.put("cust1Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
     
    -
    Suggestion importance[1-10]: 10

    Why: This suggestion is crucial for security as it ensures that all concatenated strings containing user input are properly encoded to prevent XSS vulnerabilities.

    10
    Possible bug
    Validate the provider number before fetching the provider to avoid null pointer exceptions

    Consider validating the providerNo before using it to fetch the provider to avoid
    potential null pointer exceptions.

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java [132]

    -Provider p = rx.getProvider(demo.getProviderNo());
    +Provider p = demo.getProviderNo() != null ? rx.getProvider(demo.getProviderNo()) : null;
     
    Suggestion importance[1-10]: 7

    Why: Validating the provider number can prevent potential null pointer exceptions, which is a good practice for robustness, but it is not a major bug.

    7
    Performance
    Optimize the initialization of the results list for better performance
    Suggestion Impact:Updated results list initialization from new ArrayList<>() to new ArrayList<>(info.size()) in formatJSON.

    code diff:

         private String formatJSON(List<HashMap<String, String>> info) throws Exception {
    -        List<HashMap<String, String>> results = new ArrayList<>();
    +        List<HashMap<String, String>> results = new ArrayList<>(info.size());
             for (HashMap<String, String> record : info) {

    Use a more efficient data structure for results if the size is known beforehand to improve
    performance.

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java [186]

    -List<HashMap<String, String>> results = new ArrayList<>();
    +List<HashMap<String, String>> results = new ArrayList<>(info.size());
     
    Suggestion importance[1-10]: 6

    Why: While optimizing the initialization of the results list can improve performance, it is a minor enhancement compared to the security issues addressed in other suggestions.

    6

    @coderabbitai
    Copy link
    Contributor

    coderabbitai bot commented Feb 7, 2026

    📝 Walkthrough

    Walkthrough

    Adds a runtime privilege check for demographic READ, derives providerNo from LoggedInInfo, applies OWASP HTML encoding to user-visible fields, replaces manual JSON/string assembly with Jackson ObjectMapper (formatJSON now throws JsonProcessingException), and switches several JSP autocomplete renderings to DOM-based jQuery construction.

    Changes

    Cohort / File(s) Summary
    Search Action (security & encoding)
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java
    Added runtime retrieval of LoggedInInfo and SecurityInfoManager privilege check for demographic READ (throws SecurityException / returns noPrivilege on denial); derive providerNo from LoggedInInfo; fetch DemographicCustDao; apply Encode.forHtml to user-visible fields (status, providerName, formattedName, cust*Name).
    JSON serialization / method signature
    src/main/java/.../SearchDemographicAutoComplete2Action.java
    Replaced manual JSON/string building with constructing List/Map and serializing via Jackson ObjectMapper; formatJSON(...) now declares throws JsonProcessingException and returns proper application/json response.
    Collections & minor refactors
    src/main/java/.../SearchDemographicAutoComplete2Action.java
    Replaced raw collections with diamond/empty-generic instantiations, removed duplicated DAO retrieval inside loop, added imports (Objects, JsonProcessingException, Encode).
    Autocomplete JSPs (DOM-based rendering)
    src/main/webapp/appointment/addappointment.jsp, src/main/webapp/appointment/editappointment.jsp, src/main/webapp/oscarMDS/CreateLab.jsp
    Replaced string-concatenated HTML for autocomplete items with jQuery DOM-method construction ($("<div>"), $("<b>"), $("<span>")) to avoid inserting raw HTML strings client-side.

    Sequence Diagram(s)

    sequenceDiagram
        participant Client
        participant Action as SearchDemographicAutoComplete2Action
        participant SecMgr as SecurityInfoManager
        participant DB as Demographic / DemographicCust DAO
        participant JSON as ObjectMapper
    
        Client->>Action: autocomplete request (query)
        Action->>SecMgr: check "demographic" READ privilege
        SecMgr-->>Action: allow / deny
        alt deny
            Action-->>Client: "noPrivilege"
        else allow
            Action->>DB: fetch demographics + provider info
            DB-->>Action: demographic list
            Action->>Action: read LoggedInInfo (providerNo)
            Action->>Action: apply Encode.forHtml to names/status/provider fields
            Action->>JSON: build list-of-maps and serialize to JSON
            JSON-->>Action: JSON payload
            Action-->>Client: JSON response (application/json)
        end
    
    Loading

    Estimated code review effort

    🎯 3 (Moderate) | ⏱️ ~20 minutes

    Poem

    🐰 I nibble tags and tuck the angle-brackets tight,

    Encode.forHtml keeps mischief out of sight,
    ObjectMapper stacks maps in tidy rows,
    jQuery builds elements so unsafe markup goes,
    A little rabbit patch — secure, neat, and light.

    🚥 Pre-merge checks | ✅ 4 | ❌ 1
    ❌ Failed checks (1 warning)
    Check name Status Explanation Resolution
    Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
    ✅ Passed checks (4 passed)
    Check name Status Explanation
    Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
    Title check ✅ Passed The title accurately and concisely describes the main security fixes in the PR: XSS mitigation and JSON injection prevention in SearchDemographicAutoComplete2Action.
    Linked Issues check ✅ Passed The PR addresses all core objectives from issue #250: replaced StringEscapeUtils.escapeJava() with Encode.forHtml() for formattedName/status/provider names [#250], refactored formatJSON() to use Jackson ObjectMapper for safe JSON serialization [#250], and added runtime security checks with privilege verification.
    Out of Scope Changes check ✅ Passed Changes are well-scoped to the security objectives. JSP file refactors (addappointment.jsp, editappointment.jsp, CreateLab.jsp) defensively use DOM methods instead of HTML strings to prevent XSS, aligning with the PR's security goals.

    ✏️ Tip: You can configure your own custom pre-merge checks in the settings.

    ✨ Finishing touches
    • 📝 Generate docstrings
    🧪 Generate unit tests (beta)
    • Create PR with unit tests
    • Post copyable unit tests in a comment

    No actionable comments were generated in the recent review. 🎉

    🧹 Recent nitpick comments
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java (2)

    160-183: Repetitive null-check-and-encode blocks for cust providers — consider extracting a helper.

    Lines 160–166, 168–174, and 176–182 are nearly identical: check null, look up provider, put raw name + HTML-encoded name. A small private helper (e.g., putProviderFields(map, prefix, providerNo, rx)) would reduce duplication and make future maintenance easier.

    ♻️ Example helper extraction
    private void putProviderFields(HashMap<String, String> h, String prefix, String providerNo, RxProviderData rx) {
        h.put(prefix, providerNo);
        Provider p = rx.getProvider(providerNo);
        if (p.getSurname() != null && p.getFirstName() != null) {
            String fullName = p.getSurname() + ", " + p.getFirstName();
            h.put(prefix + "Name", fullName);
            h.put(prefix + "NameHtml", Encode.forHtml(fullName));
        }
    }

    Then each block becomes:

    if (cust1 != null) { putProviderFields(h, "cust1", cust1, rx); }
    if (cust2 != null) { putProviderFields(h, "cust2", cust2, rx); }
    if (cust4 != null) { putProviderFields(h, "cust4", cust4, rx); }

    61-63: Empty JavaDoc on a public class.

    The class-level JavaDoc is blank. Per coding guidelines, public classes should have comprehensive JavaDoc including @since. This is a pre-existing gap, but since the file is being substantially modified, it's a good time to add a brief description.

    📝 Suggested JavaDoc
    -/**
    - *
    - */
    +/**
    + * Struts2 action providing demographic autocomplete search results as JSON.
    + * Supports both standard and jQuery-style JSON response formats.
    + *
    + * `@since` 2001 (McMaster University); security hardening 2026
    + */

    Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

    ❤️ Share

    Comment @coderabbitai help to get the list of available commands and usage tips.

    Copy link
    Contributor

    @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.

    Hey - I've found 2 issues, and left some high level feedback:

    • Applying Encode.forHtml() at this service/action layer will HTML-escape data for all consumers of this JSON endpoint; consider moving the HTML encoding to the view/template layer (or the specific HTML-rendering context) to avoid double-encoding or breaking non-HTML clients.
    • The formatJSON method now declares throws Exception, which changes its contract; consider narrowing this to the specific checked exception(s) that can be thrown by objectMapper.writeValueAsString and confirming callers handle or propagate them appropriately.
    Prompt for AI Agents
    Please address the comments from this code review:
    
    ## Overall Comments
    - Applying `Encode.forHtml()` at this service/action layer will HTML-escape data for all consumers of this JSON endpoint; consider moving the HTML encoding to the view/template layer (or the specific HTML-rendering context) to avoid double-encoding or breaking non-HTML clients.
    - The `formatJSON` method now declares `throws Exception`, which changes its contract; consider narrowing this to the specific checked exception(s) that can be thrown by `objectMapper.writeValueAsString` and confirming callers handle or propagate them appropriately.
    
    ## Individual Comments
    
    ### Comment 1
    <location> `src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java:185` </location>
    <code_context>
    -            if (idx < size - 1) {
    -                json.append(",");
    -            }
    +    private String formatJSON(List<HashMap<String, String>> info) throws Exception {
    +        List<HashMap<String, String>> results = new ArrayList<>();
    +        for (HashMap<String, String> record : info) {
    </code_context>
    
    <issue_to_address>
    **suggestion:** The new `throws Exception` on `formatJSON` broadens the method’s contract more than necessary.
    
    Given that the only new failure source is `objectMapper.writeValueAsString`, consider either catching `JsonProcessingException` inside `formatJSON` (and wrapping/handling it appropriately) or declaring that specific exception instead of `Exception`. A broad checked exception makes callers handle more than they need to and obscures the concrete failure modes.
    
    ```suggestion
        private String formatJSON(List<HashMap<String, String>> info) throws com.fasterxml.jackson.core.JsonProcessingException {
    ```
    </issue_to_address>
    
    ### Comment 2
    <location> `src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java:187-194` </location>
    <code_context>
    -            }
    +    private String formatJSON(List<HashMap<String, String>> info) throws Exception {
    +        List<HashMap<String, String>> results = new ArrayList<>();
    +        for (HashMap<String, String> record : info) {
    +            HashMap<String, String> h = new HashMap<>();
    +            h.put("label", record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")");
    +            h.put("value", record.get("demographicNo"));
    +            h.put("providerNo", record.get("providerNo"));
    +            h.put("provider", record.get("providerName"));
    +            h.put("nextAppt", record.get("nextAppointment"));
    +            h.put("formattedName", record.get("formattedName"));
    +            results.add(h);
             }
    </code_context>
    
    <issue_to_address>
    **issue (bug_risk):** Behavior change when some fields are missing: JSON `null` vs the string "null".
    
    Previously, Java string concatenation would turn missing fields into the literal string "null". Now, `record.get(...)` returning `null` will become JSON `null` when serialized. If any downstream code or tests rely on always getting string values (including the literal "null"), this is a behavioral change and may be breaking. Consider normalizing missing values (e.g., to empty strings) before putting them into `h` if you need to preserve the old behavior.
    </issue_to_address>

    Sourcery is free for open source - if you like our reviews please consider sharing them ✨
    Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

    Copy link
    Contributor

    @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 effectively addresses critical security vulnerabilities, namely Cross-Site Scripting (XSS) and JSON injection, by correctly employing Encode.forHtml() for output encoding and ObjectMapper for safe JSON serialization. The changes are well-aligned with security best practices. I have identified one high-severity functional bug where provider names can be rendered as "null, null" if a provider is not found. Additionally, I've suggested a medium-severity refactoring to enhance maintainability by using a POJO for JSON serialization, which will make the code more robust and readable.

    Comment on lines 149 to 163
    if (cust1 != null) {
    h.put("cust1", cust1);
    p = rx.getProvider(cust1);
    h.put("cust1Name", p.getSurname() + ", " + p.getFirstName());
    h.put("cust1Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    }
    if (cust2 != null) {
    h.put("cust2", cust2);
    p = rx.getProvider(cust2);
    h.put("cust2Name", p.getSurname() + ", " + p.getFirstName());
    h.put("cust2Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    }
    if (cust4 != null) {
    h.put("cust4", cust4);
    p = rx.getProvider(cust4);
    h.put("cust4Name", p.getSurname() + ", " + p.getFirstName());
    h.put("cust4Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    }
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    high

    While adding HTML encoding is a good security improvement, there's a potential functional bug in this block. If rx.getProvider() is called with a provider number that doesn't exist, it returns a Provider object where getSurname() and getFirstName() return null. The current code would then concatenate these nulls, resulting in the string "null, null" being displayed in the UI. To prevent this, you should add null checks for the provider's first and last names, similar to the check performed for the primary provider on line 136.

                        if (cust1 != null) {
                            h.put("cust1", cust1);
                            p = rx.getProvider(cust1);
                            if (p.getSurname() != null && p.getFirstName() != null) {
                                h.put("cust1Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                            }
                        }
                        if (cust2 != null) {
                            h.put("cust2", cust2);
                            p = rx.getProvider(cust2);
                            if (p.getSurname() != null && p.getFirstName() != null) {
                                h.put("cust2Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                            }
                        }
                        if (cust4 != null) {
                            h.put("cust4", cust4);
                            p = rx.getProvider(cust4);
                            if (p.getSurname() != null && p.getFirstName() != null) {
                                h.put("cust4Name", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
                            }
                        }

    Comment on lines 185 to 198
    private String formatJSON(List<HashMap<String, String>> info) throws Exception {
    List<HashMap<String, String>> results = new ArrayList<>();
    for (HashMap<String, String> record : info) {
    HashMap<String, String> h = new HashMap<>();
    h.put("label", record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")");
    h.put("value", record.get("demographicNo"));
    h.put("providerNo", record.get("providerNo"));
    h.put("provider", record.get("providerName"));
    h.put("nextAppt", record.get("nextAppointment"));
    h.put("formattedName", record.get("formattedName"));
    results.add(h);
    }
    json.append("]");

    return json.toString();
    return objectMapper.writeValueAsString(results);
    }
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    medium

    Replacing manual JSON string building with ObjectMapper is an excellent security enhancement. To further improve code quality, consider refactoring this method to use a dedicated Plain Old Java Object (POJO) instead of a HashMap for the data being serialized. This approach enhances type safety, readability, and maintainability by creating an explicit data contract.

    You could define a static inner class for the autocomplete items and then use it in this method. This would make the code more robust and self-documenting.

        private String formatJSON(List<HashMap<String, String>> info) throws Exception {
            List<AutoCompleteItem> results = new ArrayList<>();
            for (HashMap<String, String> record : info) {
                AutoCompleteItem item = new AutoCompleteItem();
                item.label = record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")";
                item.value = record.get("demographicNo");
                item.providerNo = record.get("providerNo");
                item.provider = record.get("providerName");
                item.nextAppt = record.get("nextAppointment");
                item.formattedName = record.get("formattedName");
                results.add(item);
            }
            return objectMapper.writeValueAsString(results);
        }
    
        private static class AutoCompleteItem {
            public String label;
            public String value;
            public String providerNo;
            public String provider;
            public String nextAppt;
            public String formattedName;
        }

    Copy link
    Contributor

    @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: 1

    Caution

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

    ⚠️ Outside diff range comments (1)
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java (1)

    72-73: ⚠️ Potential issue | 🟠 Major

    Add mandatory SecurityInfoManager.hasPrivilege() check to execute() method.

    This action searches and returns patient demographic data without verifying user privileges. Every 2Action must perform security checks first, before any business logic. The check is currently missing.

    Add at the start of execute():

    if (!securityInfoManager.hasPrivilege(LoggedInInfo.getLoggedInInfoFromSession(request), "_demographic", "r", null)) {
        throw new SecurityException("Access denied: insufficient privileges for demographic search");
    }

    Ensure SecurityInfoManager is available via SpringUtils.getBean(SecurityInfoManager.class) as a class field.

    🤖 Fix all issues with AI agents
    In
    `@src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java`:
    - Line 44: Remove the unused import of StringEscapeUtils: delete the line
    importing org.apache.commons.text.StringEscapeUtils from
    SearchDemographicAutoComplete2Action and ensure there are no remaining
    references to StringEscapeUtils in that class (all encoding should use
    Encode.forHtml()). This cleans up the unused import and prevents unnecessary
    dependency usage.
    
    🧹 Nitpick comments (2)
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java (2)

    185-198: formatJSON refactored to use Jackson — good fix for JSON injection. A few things to tighten up.

    Using ObjectMapper.writeValueAsString() instead of manual string concatenation is the correct approach to prevent JSON injection.

    Issues in the label construction on line 189:

    1. record.get("status"), record.get("providerNo"), record.get("providerName"), and record.get("nextAppointment") can all be null (e.g., providerName is only set when both surname and firstName are non-null; nextAppointment is only set when workflow_enhance is active). String concatenation with null produces literal "null" in the output — e.g., "Smith, John null (null)".

    2. The throws Exception is overly broad — ObjectMapper.writeValueAsString() throws JsonProcessingException, which is more precise.

    ♻️ Suggested improvements
    -    private String formatJSON(List<HashMap<String, String>> info) throws Exception {
    +    private String formatJSON(List<HashMap<String, String>> info) throws com.fasterxml.jackson.core.JsonProcessingException {
             List<HashMap<String, String>> results = new ArrayList<>();
             for (HashMap<String, String> record : info) {
                 HashMap<String, String> h = new HashMap<>();
    -            h.put("label", record.get("formattedName") + " " + record.get("fomattedDob") + " (" + record.get("status") + ")");
    +            String name = StringUtils.defaultString(record.get("formattedName"));
    +            String dob = StringUtils.defaultString(record.get("fomattedDob"));
    +            String status = StringUtils.defaultString(record.get("status"));
    +            h.put("label", name + " " + dob + " (" + status + ")");
                 h.put("value", record.get("demographicNo"));
                 h.put("providerNo", record.get("providerNo"));
                 h.put("provider", record.get("providerName"));

    66-67: Servlet API access via field initializers may fail at class-load time.

    ServletActionContext.getRequest() and getResponse() are initialized as instance fields. These rely on the Struts2 thread-local ActionContext being set, which is only guaranteed during action invocation — not at construction time. If the container eagerly instantiates or pools this class, these will be null or throw. The conventional pattern is to call ServletActionContext.getRequest() at the start of execute().

    This is a pre-existing issue and not introduced by this PR, so flagging for awareness only.

    @qodo-code-review
    Copy link

    qodo-code-review bot commented Feb 7, 2026

    Code Review by Qodo

    🐞 Bugs (2) 📘 Rule violations (1) 📎 Requirement gaps (0)

    Grey Divider


    Action required

    ✅ 1. execute() lacks privilege check 📘 Rule violation ⛨ Security
    Description
    SearchDemographicAutoComplete2Action.execute() performs demographic search and returns
      patient/provider data without any SecurityInfoManager.hasPrivilege() authorization gate.
    • This allows the action to execute protected business logic without an explicit privilege check,
      which can enable unauthorized access if upstream protections are misconfigured or bypassed.
    • Add an early privilege check (before any DAO/search logic) and return an authorization error
      outcome when the privilege is missing.
    
    Code

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[R124-129]

       for (Demographic demo : list) {
           HashMap<String, String> h = new HashMap<String, String>();
           h.put("fomattedDob", demo.getFormattedDob());
    -            h.put("formattedName", StringEscapeUtils.escapeJava(demo.getFormattedName().replaceAll("\"", "\\\"")));
    +            h.put("formattedName", Encode.forHtml(demo.getFormattedName()));
           h.put("demographicNo", String.valueOf(demo.getDemographicNo()));
    -            h.put("status", demo.getPatientStatus());
    +            h.put("status", Encode.forHtml(demo.getPatientStatus()));
    Evidence
    Compliance ID 16 requires all Struts2 action entry points (including *2Action) to enforce
    authorization via SecurityInfoManager.hasPrivilege() before executing protected logic. In
    execute(), the code retrieves request parameters and calls demographicDao.searchDemographicBy*
    and then builds the response, but contains no SecurityInfoManager.hasPrivilege() check anywhere in
    the method.
    

    CLAUDE.md
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[72-120]

    Agent prompt
    The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
    
    ## Issue description
    `SearchDemographicAutoComplete2Action.execute()` performs demographic searches and returns patient/provider information without enforcing an explicit authorization check using `SecurityInfoManager.hasPrivilege()`.
    ## Issue Context
    Compliance requires that every Struts2 `*2Action` entry point performs a privilege check *before* any protected business logic. This action currently proceeds directly into DAO search and response generation.
    ## Fix Focus Areas
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[72-120]
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[171-181]
    

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


    2. Unused import fails build 🐞 Bug ⛯ Reliability
    Description
    StringEscapeUtils is still imported but no longer referenced after the change away from
      escapeJava(...).
    • Checkstyle is configured to fail the build on UnusedImports, so this will block CI/merge.
    
    Code

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[44]

    import org.apache.commons.text.StringEscapeUtils;
    Evidence
    The class still imports StringEscapeUtils but no longer uses it. The repo’s Checkstyle
    configuration explicitly enables UnusedImports, and Maven is configured to fail on Checkstyle
    errors, making this a build breaker.
    

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[41-46]
    utils/checkstyle.xml[93-97]
    pom.xml[1589-1645]

    Agent prompt
    The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
    
    ## Issue description
    `SearchDemographicAutoComplete2Action.java` still imports `org.apache.commons.text.StringEscapeUtils` but no longer uses it, causing Checkstyle `UnusedImports` to fail the build.
    ### Issue Context
    The repository runs Checkstyle in `process-sources` with `failsOnError=true`, and `UnusedImports` is enabled.
    ### Fix Focus Areas
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[41-46]
    - utils/checkstyle.xml[93-97]
    - pom.xml[1589-1645]
    

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


    ✅ 3. HTML encoding breaks autocomplete values 🐞 Bug ✓ Correctness
    Description
    • The action now applies Encode.forHtml(...) to formattedName, status, and provider names
      before returning them in JSON.
    • Multiple UIs set these JSON fields directly into form inputs via .val(...); with the server now
      returning HTML entities (e.g., ', &), the input value becomes the encoded text, causing visible
      regressions and potential mismatches where raw values are expected.
    • If the goal is XSS prevention, the safer fix is contextual escaping at the HTML rendering sink
      (e.g., render with .text()/text nodes) or providing separate *Html fields, rather than mutating
      canonical data fields used as form values.
    
    Code

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[R127-137]

    +            h.put("formattedName", Encode.forHtml(demo.getFormattedName()));
           h.put("demographicNo", String.valueOf(demo.getDemographicNo()));
    -            h.put("status", demo.getPatientStatus());
    +            h.put("status", Encode.forHtml(demo.getPatientStatus()));
           Provider p = rx.getProvider(demo.getProviderNo());
           if (demo.getProviderNo() != null) {
               h.put("providerNo", demo.getProviderNo());
           }
           if (p.getSurname() != null && p.getFirstName() != null) {
    -                h.put("providerName", p.getSurname() + ", " + p.getFirstName());
    +                h.put("providerName", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));
    Evidence
    The backend now transforms the name/status/provider fields via HTML encoding before serialization.
    Frontend code consumes these fields as raw values for inputs (.val(ui.item.formattedName),
    .val(ui.item.label), .val(ui.item.provider)), so any encoding becomes part of the literal input
    value. Separately, some pages also build suggestion markup via string concatenation, which is the
    actual XSS sink that should be fixed/escaped at render time.
    

    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[123-138]
    src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[185-195]
    src/main/webapp/appointment/addappointment.jsp[570-589]
    src/main/webapp/documentManager/showDocument.js[23-44]

    Agent prompt
    The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
    
    ## Issue description
    `SearchDemographicAutoComplete2Action` HTML-encodes strings before returning JSON. These strings are then reused as raw values in many autocomplete consumers via `.val(...)`, causing encoded entities to appear in inputs and changing semantics of the returned data.
    ### Issue Context
    Some consumers render suggestions by concatenating strings into HTML (XSS sink), while also using the same fields as raw input values. Encoding should be applied at the HTML sink, not in the canonical JSON data fields.
    ### Fix Focus Areas
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[123-138]
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[185-197]
    - src/main/webapp/appointment/addappointment.jsp[570-589]
    - src/main/webapp/documentManager/showDocument.js[23-44]
    

    ⓘ 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 on lines +124 to +129
    for (Demographic demo : list) {
    HashMap<String, String> h = new HashMap<String, String>();
    h.put("fomattedDob", demo.getFormattedDob());
    h.put("formattedName", StringEscapeUtils.escapeJava(demo.getFormattedName().replaceAll("\"", "\\\"")));
    h.put("formattedName", Encode.forHtml(demo.getFormattedName()));
    h.put("demographicNo", String.valueOf(demo.getDemographicNo()));
    h.put("status", demo.getPatientStatus());
    h.put("status", Encode.forHtml(demo.getPatientStatus()));

    Choose a reason for hiding this comment

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

    Action required

    1. execute() lacks privilege check 📘 Rule violation ⛨ Security

    SearchDemographicAutoComplete2Action.execute() performs demographic search and returns
      patient/provider data without any SecurityInfoManager.hasPrivilege() authorization gate.
    • This allows the action to execute protected business logic without an explicit privilege check,
      which can enable unauthorized access if upstream protections are misconfigured or bypassed.
    • Add an early privilege check (before any DAO/search logic) and return an authorization error
      outcome when the privilege is missing.
    
    Agent Prompt
    ## Issue description
    `SearchDemographicAutoComplete2Action.execute()` performs demographic searches and returns patient/provider information without enforcing an explicit authorization check using `SecurityInfoManager.hasPrivilege()`.
    
    ## Issue Context
    Compliance requires that every Struts2 `*2Action` entry point performs a privilege check *before* any protected business logic. This action currently proceeds directly into DAO search and response generation.
    
    ## Fix Focus Areas
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[72-120]
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[171-181]
    

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

    import com.fasterxml.jackson.databind.ObjectMapper;

    import org.owasp.encoder.Encode;
    import org.apache.commons.text.StringEscapeUtils;

    Choose a reason for hiding this comment

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

    Action required

    2. Unused import fails build 🐞 Bug ⛯ Reliability

    StringEscapeUtils is still imported but no longer referenced after the change away from
      escapeJava(...).
    • Checkstyle is configured to fail the build on UnusedImports, so this will block CI/merge.
    
    Agent Prompt
    ### Issue description
    `SearchDemographicAutoComplete2Action.java` still imports `org.apache.commons.text.StringEscapeUtils` but no longer uses it, causing Checkstyle `UnusedImports` to fail the build.
    
    ### Issue Context
    The repository runs Checkstyle in `process-sources` with `failsOnError=true`, and `UnusedImports` is enabled.
    
    ### Fix Focus Areas
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[41-46]
    - utils/checkstyle.xml[93-97]
    - pom.xml[1589-1645]
    

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

    Comment on lines +127 to +137
    h.put("formattedName", Encode.forHtml(demo.getFormattedName()));
    h.put("demographicNo", String.valueOf(demo.getDemographicNo()));
    h.put("status", demo.getPatientStatus());
    h.put("status", Encode.forHtml(demo.getPatientStatus()));


    Provider p = rx.getProvider(demo.getProviderNo());
    if (demo.getProviderNo() != null) {
    h.put("providerNo", demo.getProviderNo());
    }
    if (p.getSurname() != null && p.getFirstName() != null) {
    h.put("providerName", p.getSurname() + ", " + p.getFirstName());
    h.put("providerName", Encode.forHtml(p.getSurname() + ", " + p.getFirstName()));

    Choose a reason for hiding this comment

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

    Action required

    3. Html encoding breaks autocomplete values 🐞 Bug ✓ Correctness

    • The action now applies Encode.forHtml(...) to formattedName, status, and provider names
      before returning them in JSON.
    • Multiple UIs set these JSON fields directly into form inputs via .val(...); with the server now
      returning HTML entities (e.g., &#x27;, &amp;), the input value becomes the encoded text, causing
      visible regressions and potential mismatches where raw values are expected.
    • If the goal is XSS prevention, the safer fix is contextual escaping at the HTML rendering sink
      (e.g., render with .text()/text nodes) or providing separate *Html fields, rather than mutating
      canonical data fields used as form values.
    
    Agent Prompt
    ### Issue description
    `SearchDemographicAutoComplete2Action` HTML-encodes strings before returning JSON. These strings are then reused as raw values in many autocomplete consumers via `.val(...)`, causing encoded entities to appear in inputs and changing semantics of the returned data.
    
    ### Issue Context
    Some consumers render suggestions by concatenating strings into HTML (XSS sink), while also using the same fields as raw input values. Encoding should be applied at the HTML sink, not in the canonical JSON data fields.
    
    ### Fix Focus Areas
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[123-138]
    - src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java[185-197]
    - src/main/webapp/appointment/addappointment.jsp[570-589]
    - src/main/webapp/documentManager/showDocument.js[23-44]
    

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

    Copy link
    Contributor

    @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: 4

    🤖 Fix all issues with AI agents
    In
    `@src/main/java/io/github/carlos_emr/carlos/commn/web/SearchDemographicAutoComplete2Action.java`:
    - Around line 75-76: In SearchDemographicAutoComplete2Action (method handling
    the request) replace the early return of "noPrivilege" in the privilege check
    (the block using securityInfoManager.hasPrivilege and loggedInInfo) with
    throwing a SecurityException("missing required sec object") so the request is
    definitively rejected and the exception propagates; ensure you do not catch and
    swallow SecurityException elsewhere in this action per guidelines and keep the
    change inside the privilege-check branch of
    SearchDemographicAutoComplete2Action.
    - Line 201: The label construction in SearchDemographicAutoComplete2Action uses
    String concatenation with values from the map `record` which can be null and
    result in the literal "null" in the UI; update the code that puts into `h` (the
    line using h.put("label", Encode.forHtml(...))) to build the label null-safely
    by calling a null-coalescing helper (e.g.,
    Objects.toString(record.get("formattedName"), ""), or an inline ternary) for
    `formattedName`, `fomattedDob`, and `status`, trim/omit empty parts and only add
    surrounding punctuation (space, parentheses) when those parts are non-empty,
    then pass the sanitized concatenated string to Encode.forHtml.
    - Around line 72-79: In execute() of SearchDemographicAutoComplete2Action adjust
    the privilege check: call SecurityInfoManager.hasPrivilege(loggedInInfo,
    "_demographic", SecurityInfoManager.READ, null) (use the underscore-prefixed
    object name and null for non-patient-specific demographic checks) and, instead
    of returning the "noPrivilege" string, throw a SecurityException with a clear
    message like "missing required sec object (_demographic)" when the check fails
    so the error flow follows the security guidelines.
    - Around line 197-210: In formatJSON, replace the unencoded fields so the JSON
    emits the pre-encoded HTML variants: update the h.put("provider", ...) call to
    use record.get("providerNameHtml") and update the h.put("formattedName", ...)
    call to use record.get("formattedNameHtml"); these changes in the formatJSON
    method ensure the JSON returns the existing OWASP-encoded values
    (providerNameHtml and formattedNameHtml) instead of raw providerName and
    formattedName.
    

    @yingbull
    Copy link
    Collaborator

    yingbull commented Feb 7, 2026

    Thanks for the contribution! There is a lot of legacy code that has areas to improve on, so the bots tend to be busy. Core contribution looks good, I am just reviewing now.

    @yingbull
    Copy link
    Collaborator

    yingbull commented Feb 7, 2026

    @claude review this PR and all bot and reviewer comments, identify all valid identified issues and any unidentified issues and provide a review with recommendations.

    @github-actions
    Copy link
    Contributor

    github-actions bot commented Feb 7, 2026

    Claude Code is working…

    I'll analyze this and get back to you.

    View job run

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    security: Fix XSS vulnerability in SearchDemographicAutoComplete2Action

    2 participants