Skip to content

Conversation

@5Amogh
Copy link
Member

@5Amogh 5Amogh commented Nov 17, 2025

📋 Description

JIRA ID: AMM-1927

This PR transforms the CORS implementation from an overly restrictive, maintenance-heavy configuration to a secure, environment-aware, and developer-friendly approach. By removing endpoint-specific restrictions and fixing critical security vulnerabilities, we achieve:

Better Security: Environment-controlled origins, no hardcoded localhost, immutable configuration
Simpler Code: No endpoint-specific configuration, reduced maintenance
Proper Architecture: CORS handles browser security, endpoints handle authorization
Standard Compliance: Full REST API support including PATCH method
Result: A production-safe, maintainable CORS implementation that follows security best practices and reduces operational overhead.

✅ Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)

Summary by CodeRabbit

  • Security
    • Enhanced cross-origin request handling with configurable origin validation, improved preflight request processing, and better credentials management for API security.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 17, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR enhances CORS security by implementing explicit origin validation across two request-handling components. Configuration-driven allowed origins are enforced for both OPTIONS preflight and regular requests, replacing unconditional wildcard headers with origin-specific conditional responses while preserving existing authentication flow.

Changes

Cohort / File(s) Summary
CORS Preflight & Origin Validation in JWT Filter
src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
Adds explicit CORS preflight OPTIONS request handling with origin validation; enforces origin checks for all requests with HTTP 403 rejection for unauthorized origins; expands allowed CORS headers to include multiple serverAuthorization token variants; adds userId cookie logging and clearing; maintains existing JWT authentication flow with enhanced origin validation regex.
Origin-Based CORS Configuration in HTTP Interceptor
src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java
Introduces configuration-driven origin validation by loading allowed origins from cors.allowed-origins property; replaces unconditional wildcard Access-Control-Allow-Origin header with conditional origin matching; adds isOriginAllowed() helper method for regex-based origin validation; applies origin checks in error-response handling and logs warnings for disallowed origins.

Sequence Diagram

sequenceDiagram
    participant Client
    participant JwtFilter as JWT Filter
    participant Interceptor as HTTP Interceptor
    participant App as Application
    
    Client->>JwtFilter: OPTIONS Request + Origin header
    activate JwtFilter
    JwtFilter->>JwtFilter: Validate origin against allowed list
    alt Origin Allowed
        JwtFilter-->>Client: 200 + CORS Headers + Methods, Headers, Credentials
        JwtFilter->>JwtFilter: Short-circuit (no further processing)
    else Origin Not Allowed
        JwtFilter-->>Client: 403 Forbidden
    end
    deactivate JwtFilter
    
    Client->>JwtFilter: GET/POST/etc + Origin header
    activate JwtFilter
    JwtFilter->>JwtFilter: Validate origin
    alt Origin Allowed
        JwtFilter->>JwtFilter: Validate JWT Token
        alt Valid JWT
            JwtFilter->>Interceptor: Forward Request
            activate Interceptor
            Interceptor->>Interceptor: Validate origin (configured)
            Interceptor-->>Client: Response + Conditional CORS Headers
            deactivate Interceptor
        else Invalid JWT
            JwtFilter-->>Client: 401 Unauthorized
        end
    else Origin Not Allowed
        JwtFilter-->>Client: 403 Forbidden
    end
    deactivate JwtFilter
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

  • Origin validation regex logic in both components—verify wildcard-to-regex transformation is consistent and correctly matches intended origins
  • CORS header expansion in JWT filter—ensure all added serverAuthorization token variants don't introduce unintended exposure
  • Configuration property integration—confirm cors.allowed-origins format (comma-separated list) and parsing logic are robust
  • OPTIONS request short-circuiting—ensure preflight responses don't bypass necessary security checks
  • userId cookie handling—verify logging and clearing doesn't affect subsequent authentication

Possibly related PRs

  • CORS Configuration for TM API Services  #89: Implements identical CORS configuration changes (introducing cors.allowed-origins, passing allowedOrigins into JwtUserIdValidationFilter, and adding origin-based handling), making this a direct code-level continuation or alternative approach to the same CORS security enhancement.

Suggested reviewers

  • drtechie
  • vanitha1822

Poem

🐰 A filter hops through CORS with care,
Origins checked with origin-aware flair,
No more wildcards wild and free,
Only trusted sources three,
Security's dance, both proper and rare! 🔐✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: implementing CORS response headers based on allowed origins, which is the primary focus of both modified files.

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.

@5Amogh
Copy link
Member Author

5Amogh commented Nov 17, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 17, 2025

✅ 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
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: 3

♻️ Duplicate comments (1)
src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (1)

163-179: Code duplication - see comment on HTTPRequestInterceptor.java lines 149-162.

This isOriginAllowed method is identical to the one in HTTPRequestInterceptor.java. Please extract to a shared utility class as suggested in the previous file review.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc84bd9 and 5e5aca1.

📒 Files selected for processing (2)
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (2 hunks)
  • src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-22T09:27:49.422Z
Learnt from: ravishanigarapu
Repo: PSMRI/TM-API PR: 79
File: src/main/java/com/iemr/tm/utils/UserAgentContext.java:3-18
Timestamp: 2025-05-22T09:27:49.422Z
Learning: JwtUserIdValidationFilter.java already includes a try-finally block to properly clear UserAgentContext's ThreadLocal resources after use to prevent memory leaks.

Applied to files:

  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
📚 Learning: 2024-12-16T11:46:15.606Z
Learnt from: ravishanigarapu
Repo: PSMRI/TM-API PR: 58
File: src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java:93-97
Timestamp: 2024-12-16T11:46:15.606Z
Learning: In the `HTTPRequestInterceptor` class (`src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java`), the `validator` is mandatory and always initialized, so null checks are unnecessary.

Applied to files:

  • src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java
🔇 Additional comments (10)
src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java (3)

24-24: LGTM - Necessary imports added.

The new imports support the CORS configuration injection and origin validation logic.

Also applies to: 31-31


46-47: Configuration property verified—code changes are acceptable.

The cors.allowed-origins property is defined in your configuration files (common_example.properties and common_ci.properties) with appropriate values. The @Value annotation will inject the property correctly at runtime.


104-110: The review comment is incorrect—CORS headers ARE consistently applied to all responses.

The HTTPRequestInterceptor's CORS logic (lines 104-110) handles only error responses, but this is intentional and coordinated. The primary CORS handling occurs in JwtUserIdValidationFilter, which sets the full CORS headers for all requests with allowed origins (lines 68-74 and 80-86). Both components use identical origin validation logic (isOriginAllowed()), confirming this is a deliberate layered approach:

  • Filter layer: Applies CORS headers to all successful responses (lines 68-74)
  • Interceptor layer: Fallback that applies CORS headers to error responses (lines 104-110)

The review's premise that "most requests will lack proper CORS headers" overlooks the filter's role and mischaracterizes the architecture as incomplete when it is, in fact, properly coordinated across both components.

Likely an incorrect or invalid review comment.

src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (7)

40-41: LGTM - Variables support enhanced logging.

These variables enable better context in logging statements throughout the method.


45-63: Good security practice - OPTIONS requests require origin validation.

This block correctly enforces that:

  1. OPTIONS requests must include an Origin header
  2. The origin must be in the allowed list

This prevents unauthorized preflight requests and is a security improvement.


68-78: LGTM - Proper origin-based CORS header configuration.

The logic correctly:

  • Sets origin-specific headers (never wildcard)
  • Includes credentials support
  • Logs validation results

70-70: PATCH method support added successfully.

The inclusion of PATCH in allowed methods addresses a stated PR objective.


72-72: Comprehensive authorization header variants included.

The expanded list of serverAuthorization variants (with various capitalizations) ensures compatibility with different client implementations.


74-75: Enhanced CORS configuration with caching and detailed logging.

  • Access-Control-Max-Age: 3600 reduces preflight request overhead by caching for 1 hour
  • Enhanced logging provides better observability for CORS validation

174-174: Regex pattern change is correct and intentional—no issues found.

Verification confirms:

  1. Pattern conversion is correct: http://localhost:* properly converts to regex http://localhost:.*, which matches all localhost ports (8080, 3000, 8089, etc.) while correctly rejecting non-localhost origins.

  2. Configuration adequacy confirmed: The cors.allowed-origins=http://localhost:* setting in properties files correctly handles multiple localhost services across varying ports referenced in the configuration.

The simplified pattern using standard wildcard-to-regex conversion (escape dots first, then convert * to .*) is sound and sufficient. No special-case handling is needed; the generic solution works correctly.

Comment on lines 149 to 162
private boolean isOriginAllowed(String origin) {
if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) {
return false;
}

return Arrays.stream(allowedOrigins.split(","))
.map(String::trim)
.anyMatch(pattern -> {
String regex = pattern
.replace(".", "\\.")
.replace("*", ".*");
return origin.matches(regex);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated origin validation to a shared utility.

The isOriginAllowed method is duplicated in both HTTPRequestInterceptor and JwtUserIdValidationFilter (line 163-179 in that file). This violates DRY principles and increases maintenance burden.

Create a shared utility class for CORS validation:

package com.iemr.tm.utils.cors;

import java.util.Arrays;

public class CorsUtil {
    
    /**
     * Check if the given origin is allowed based on configured allowedOrigins.
     * 
     * @param origin The origin to validate
     * @param allowedOrigins Comma-separated list of allowed origin patterns
     * @return true if origin is allowed, false otherwise
     */
    public static boolean isOriginAllowed(String origin, String allowedOrigins) {
        if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) {
            return false;
        }

        return Arrays.stream(allowedOrigins.split(","))
                .map(String::trim)
                .anyMatch(pattern -> {
                    String regex = pattern
                            .replace(".", "\\.")
                            .replace("*", ".*");
                    return origin.matches(regex);
                });
    }
}

Then update both classes to use CorsUtil.isOriginAllowed(origin, allowedOrigins).

🤖 Prompt for AI Agents
In src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java around lines
149 to 162, the origin validation logic is duplicated in
JwtUserIdValidationFilter; extract this into a new shared utility class (e.g.,
package com.iemr.tm.utils.cors.CorsUtil) with a static method
isOriginAllowed(String origin, String allowedOrigins) that implements the same
null/empty checks and pattern matching, then replace the local isOriginAllowed
implementations in both HTTPRequestInterceptor and JwtUserIdValidationFilter to
call CorsUtil.isOriginAllowed(origin, allowedOrigins); ensure imports are added
and remove the duplicated private methods.

Comment on lines +65 to +66
String path = request.getRequestURI();
String contextPath = request.getContextPath();
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 17, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused variables.

The path and contextPath variables declared here appear to be unused in the immediate context. The path variable is redeclared at line 92 where it's actually used.

Apply this diff:

-		String path = request.getRequestURI();
-		String contextPath = request.getContextPath();
-

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java around lines
65-66, the local variables "path" and "contextPath" are declared but never used
(the "path" is redeclared later at line 92); remove these two unused variable
declarations to avoid redundancy and dead code, keeping the later correct "path"
declaration at line 92 intact.

Copy link
Member Author

Choose a reason for hiding this comment

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

tolerable for readbility

Copy link
Contributor

Choose a reason for hiding this comment

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

@5Amogh, understood! I'll keep the variables as they are for readability. Thanks for the clarification.


🧠 Learnings used
Learnt from: ravishanigarapu
Repo: PSMRI/TM-API PR: 79
File: src/main/java/com/iemr/tm/utils/UserAgentContext.java:3-18
Timestamp: 2025-05-22T09:27:49.422Z
Learning: JwtUserIdValidationFilter.java already includes a try-finally block to properly clear UserAgentContext's ThreadLocal resources after use to prevent memory leaks.

Comment on lines 80 to 90
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
// OPTIONS (preflight) - respond with full allowed methods
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers",
"Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
logger.info("OPTIONS request - skipping JWT validation");
response.setStatus(HttpServletResponse.SC_OK);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical security flaw and redundant logic.

This OPTIONS handling block has multiple serious issues:

  1. Security vulnerability: Lines 82-86 unconditionally set CORS headers including the origin without validation. This occurs AFTER the origin has already been validated and blocked if unauthorized (lines 45-63). However, the logic flow is confusing and could allow bypass scenarios.

  2. Unreachable code: This entire block is redundant because:

    • Valid OPTIONS requests already have CORS headers set at lines 68-78
    • Invalid OPTIONS requests are already blocked and returned at lines 47-62
    • This block should never be reached for unauthorized origins, making lines 82-86 dead code
  3. Logic error: Even if an unauthorized origin somehow reaches this block, setting Access-Control-Allow-Origin with that origin would be a security breach.

Remove this entire redundant OPTIONS block:

-		if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
-			// OPTIONS (preflight) - respond with full allowed methods
-			response.setHeader("Access-Control-Allow-Origin", origin);
-			response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
-			response.setHeader("Access-Control-Allow-Headers",
-					"Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization");
-			response.setHeader("Access-Control-Allow-Credentials", "true");
-			logger.info("OPTIONS request - skipping JWT validation");
-			response.setStatus(HttpServletResponse.SC_OK);
-			return;
-		}
-

The OPTIONS handling at lines 45-63 combined with the CORS headers at lines 68-78 already provide complete and secure OPTIONS request handling.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
// OPTIONS (preflight) - respond with full allowed methods
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers",
"Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
logger.info("OPTIONS request - skipping JWT validation");
response.setStatus(HttpServletResponse.SC_OK);
return;
}
🤖 Prompt for AI Agents
In src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java around lines
80 to 90, remove the redundant OPTIONS handling block (the unconditional setting
of CORS headers and early return) because OPTIONS is already handled securely
earlier (lines ~45-63 and ~68-78); delete this dead code to avoid re-applying
Access-Control-Allow-Origin after origin validation and ensure the filter relies
on the existing validated CORS/header logic and early-return behavior instead.

String regex = pattern
.replace(".", "\\.")
.replace("*", ".*")
.replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port
Copy link
Member

Choose a reason for hiding this comment

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

@vishwab1 so should this remain as is?

Copy link
Member

@vishwab1 vishwab1 Nov 17, 2025

Choose a reason for hiding this comment

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

@drtechie @5Amogh yes we can keep as it is

@sonarqubecloud
Copy link

Copy link
Member

@vishwab1 vishwab1 left a comment

Choose a reason for hiding this comment

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

please check the OPTIONS logic

logger.debug("Incoming Origin: {}", origin);
logger.debug("Allowed Origins Configured: {}", allowedOrigins);
if ("OPTIONS".equalsIgnoreCase(method)) {
if (origin == null) {
Copy link
Member

Choose a reason for hiding this comment

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

The current implementation returns 200 OK for OPTIONS requests, which is the correct behavior for CORS preflight requests. Could you please check it

@5Amogh 5Amogh merged commit eb786bf into release-3.6.1 Nov 20, 2025
2 of 3 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.

4 participants