Skip to content

Conversation

@SradhaMohanty5899
Copy link
Contributor

@SradhaMohanty5899 SradhaMohanty5899 commented Dec 5, 2025

Masking Sensitive Information (Client-secret key, Password, pwd, token, refreshtoken) etc. From Log.

  • Added LogMaskingUtil to mask sensitive fields (password, token, clientSecret) in logs.
  • Implemented a custom RestAssured masking filter to replace .log().all() with secure logging.
  • Ensured both request and response bodies are masked before logging.
  • Added logic to auto-extract IDs from the request when test case contains _reqSid.
  • Added support for dynamic expiry-date tokens ($EXPIRYDATE$, $EXPIRYDATE_YEARS$).

Summary by CodeRabbit

  • New Features

    • Sensitive-data masking utility and a masking-aware logging filter for API request/response logs.
    • Utilities to compute and insert UTC expiry timestamps into test payloads.
    • Automatic capture and persistence of auto-generated IDs from request payloads to support test flows.
  • Enhancements

    • Dedicated API logger and expanded runtime/configuration constants for clearer, privacy-aware logs.

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

Warning

Rate limit exceeded

@SradhaMohanty5899 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 56 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 79f7ba2 and 1ed0503.

📒 Files selected for processing (1)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (8 hunks)

Walkthrough

Adds masked-logging utilities and a RestAssured logging filter; integrates masked logging across RestClient; adds expiry/timestamp and request-ID extraction helpers in AdminTestUtil; expands GlobalConstants with many constants; and introduces API_LOGGER config in log4j.properties.

Changes

Cohort / File(s) Change Summary
Log4j config
apitest-commons/src/main/resources/log4j.properties, apitest-commons/src/main/java/io/mosip/testrig/apirig/testrunner/BaseTestCase.java
Adds API_LOGGER (INFO) with APIConsole ConsoleAppender (%m%n) and disables additivity for API_LOGGER.
Masking + Logging filter
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java, apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java
Adds LogMaskingUtil for recursive masking of JSON, headers, cookies and params plus safeLogInfo(...); adds RestAssuredPrettyLogger.getMaskingFilter() to log requests/responses with masking.
RestClient integration
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java
Replaces log().all() and direct response logs with RestAssuredPrettyLogger.getMaskingFilter() and LogMaskingUtil.safeLogInfo(...) across HTTP wrappers to enable masked logging.
Admin utilities
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java
Adds writeAutoGeneratedIdFromRequest(...), postWithBodyAndCookieForAutoGeneratedIdFromRequest(...), getUtcTimestamp(long, ChronoUnit); adds expiry-token handling ($EXPIRYDATE$, $EXPIRYDATE_10YEARS$) and request-ID extraction flows.
Constants expansion
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalConstants.java
Introduces many new public static final String constants (e.g., ERROR_STRING_4, WRITE_STRING_1, TRANSACTION_ID, TIMESTAMP, PARTNER_ID, service/environment identifiers).
Formatting
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalMethods.java
Minor whitespace/formatting change only; no behavior change.

Sequence Diagram

sequenceDiagram
    autonumber
    actor Client
    participant RestClient
    participant Filter as RestAssuredPrettyLogger
    participant Mask as LogMaskingUtil
    participant API_LOGGER as API_LOGGER
    participant Remote as RemoteAPI

    Client->>RestClient: call HTTP method
    RestClient->>Filter: attach getMaskingFilter()
    Filter->>Mask: mask request headers/body/cookies/params
    Mask-->>Filter: masked request
    Filter->>API_LOGGER: log masked request
    Filter->>Remote: execute HTTP request
    Remote-->>Filter: response
    Filter->>Mask: mask response headers/body
    Mask-->>Filter: masked response
    Filter->>API_LOGGER: log masked response
    Filter-->>RestClient: return response
    RestClient-->>Client: return response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus areas:
    • AdminTestUtil: expiry-token substitution, ChronoUnit/UTC formatting, request-ID extraction and caching.
    • LogMaskingUtil: recursive traversal correctness, JSON edge cases, set-cookie parsing, masking rules for short values.
    • RestAssuredPrettyLogger & RestClient: ensure filter preserves request semantics, concurrency/thread-safety, and parity with prior logging behavior.
    • GlobalConstants: review for naming collisions and usages.

Poem

🐰 I nibbled keys and hid the trace,
I stitched the logs with gentle grace.
Requests now whisper, cookies dream,
timestamps hop and IDs find seam.
— a happy rabbit, masking gleam.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.42% 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 directly describes the main change: implementing masking of sensitive information in logs, which is reflected throughout the PR with new masking utilities and logging enhancements.

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

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

Caution

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

⚠️ Outside diff range comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (1)

778-783: Inconsistent masking: sensitive data may leak in non-debug mode.

The else branch on line 781 still uses .log().all() which will log unmasked sensitive data. This defeats the purpose of the masking filter added in the debug branch.

 		} else {
 			postResponse = given().config(config).relaxedHTTPSValidation().headers(headers).body(body)
 					.contentType(contentHeader)
 					.cookie(GlobalConstants.XSRF_TOKEN, cookieMap.get(GlobalConstants.XSRF_TOKEN))
-					.cookie(key, cookieMap.get(key)).accept(acceptHeader).when().post(url).then().log().all()
+					.cookie(key, cookieMap.get(key)).accept(acceptHeader).when().post(url).then()
 					.extract().response();
 		}
🧹 Nitpick comments (4)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (1)

7324-7349: Expiry-date helper works but can be simplified and made more robust

The loop in addCustomExpiryDate correctly replaces all $EXPIRYDATE:amount$-style tokens using ChronoUnit, and getUtcTimestamp produces UTC timestamps in the existing format.

Two optional improvements:

  • Use DateTimeFormatter on ZonedDateTime instead of converting back to Date/SimpleDateFormat to stay fully in java.time.
  • Optionally accept a Clock (or pass in ZonedDateTime now) for easier deterministic testing of time-based behaviour.

These are non-blocking and can be deferred.

apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)

15-85: Consider making the filter instance a singleton.

getMaskingFilter() creates a new Filter instance on every call. Since the filter is stateless, consider caching a single instance for better performance.

+    private static final Filter MASKING_FILTER = createMaskingFilter();
+
 	public static Filter getMaskingFilter() {
+        return MASKING_FILTER;
+    }
+
+    private static Filter createMaskingFilter() {
         return new Filter() {
             // ... existing implementation
         };
     }
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (2)

12-15: Consider case-insensitive key matching and additional sensitive keys.

The current implementation uses case-sensitive matching which won't catch variations like "Password", "TOKEN", or "CLIENT_SECRET". Also consider adding commonly used sensitive field names.

-	private static final List<String> SENSITIVE_KEYS = Arrays.asList(
-            "clientSecret", "client_secret", "password", "pwd", 
-            "token", "access_token", "refresh_token", "refreshToken"
-    );
+	private static final List<String> SENSITIVE_KEYS = Arrays.asList(
+            "clientSecret", "client_secret", "password", "pwd", 
+            "token", "access_token", "refresh_token", "refreshToken",
+            "secret", "apiKey", "api_key", "credential", "auth_token",
+            "private_key", "privateKey", "authorization"
+    );

For case-insensitive matching, modify the check in mask():

-                if (SENSITIVE_KEYS.contains(key)) {
+                if (SENSITIVE_KEYS.stream().anyMatch(k -> k.equalsIgnoreCase(key))) {

50-57: Masking reveals last 3 characters which may be too revealing for short secrets.

For short tokens or passwords (e.g., 6-8 characters), showing the last 3 characters reveals a significant portion. Consider showing fewer characters or using a fixed mask.

     private static String maskValue(String value) {
-        if (value.length() <= 4) {
+        if (value.length() <= 6) {
             return "****";
         }
-        int unmasked = 3; // last 3 digits shown
+        int unmasked = Math.min(2, value.length() / 4); // show at most 2 chars or 25%
         String stars = "*".repeat(value.length() - unmasked);
         return stars + value.substring(value.length() - unmasked);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38c54a2 and 10523a6.

📒 Files selected for processing (8)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/testrunner/BaseTestCase.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (6 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalConstants.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalMethods.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (89 hunks)
  • apitest-commons/src/main/resources/log4j.properties (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)
  • LogMaskingUtil (10-68)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (4)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)
  • RestAssuredPrettyLogger (11-87)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)
  • LogMaskingUtil (10-68)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalConstants.java (1)
  • GlobalConstants (3-284)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/ConfigManager.java (1)
  • ConfigManager (15-281)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalConstants.java (1)
  • GlobalConstants (3-284)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-maven-authentication-demo-service / maven-build
  • GitHub Check: build-maven-apitest-commons / maven-build
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (10)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalMethods.java (4)

76-83: Formatting improvement adds clarity.

The inserted blank line between getRunContext() and main() improves code readability through better method separation.


269-294: Sensitive data masking implementation is solid.

The maskOutSensitiveInfo() method appropriately masks sensitive fields across JSON payloads with case-insensitive matching, debug-mode bypass, and specialized handling for biometric data.


308-328: Request masking is properly integrated.

The reportRequest() methods correctly apply maskOutSensitiveInfo() before logging request bodies, maintaining consistency with the PR's security objectives.


330-356: Unable to verify the review comment due to repository access failure. Manual verification required to confirm:

  • Whether response bodies are currently masked before logging
  • If PR objectives explicitly require response masking
  • Whether RestAssuredPrettyLogger or other downstream filters handle masking
  • The exact locations where masking should be applied
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (2)

43-50: Time/Chrono imports correctly support new expiry utilities

The added java.time/ChronoUnit imports line up with getUtcTimestamp usage; no issues from a correctness perspective.

Also applies to: 67-73


1251-1253: _reqsid flow integration looks consistent with existing _sid handling

Wiring _reqsid to writeAutoGeneratedIdFromRequest here mirrors the existing _sid/response-based flow and happens after all keyword/OTP preprocessing, which is appropriate.

apitest-commons/src/main/java/io/mosip/testrig/apirig/testrunner/BaseTestCase.java (1)

362-368: LGTM!

The programmatic Log4j configuration for API_LOGGER is correctly implemented. The minimal conversion pattern (%m%n) is appropriate for the masking filter's pre-formatted output.

apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (2)

289-290: Masking filter applied unconditionally outside debug check.

Line 290 applies the masking filter outside the IsDebugEnabled() check, meaning the filter runs even when debug logging is disabled. This is inconsistent with other methods where the filter is only applied in debug mode. Consider whether this is intentional.


314-315: Same inconsistency: masking filter applied outside debug check.

Similar to line 290, the filter is applied before entering the debug block at line 315.

apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalConstants.java (1)

179-205: LGTM!

The new constants are well-organized and follow existing naming conventions. They properly support the logging, transaction tracking, and masking infrastructure introduced in this PR.

Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Copy link

@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

Caution

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

⚠️ Outside diff range comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (1)

781-781: Inconsistent logging: .log().all() bypasses masking in non-debug branch.

Line 781 uses .log().all() in the else (non-debug) branch, which logs the response without masking. This is inconsistent with other methods in this file where the else branch omits explicit logging.

Apply this diff to maintain consistency:

                	postResponse = given().config(config).relaxedHTTPSValidation().headers(headers).body(body)
                			.contentType(contentHeader)
                			.cookie(GlobalConstants.XSRF_TOKEN, cookieValue.get(GlobalConstants.XSRF_TOKEN))
-               			.cookie(key, cookieValue.get(key)).accept(acceptHeader).when().post(url).then().log().all()
+               			.cookie(key, cookieValue.get(key)).accept(acceptHeader).when().post(url).then()
                			.extract().response();
♻️ Duplicate comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)

51-63: Critical: Cookies with session/auth tokens are not being masked.

Similar to the headers issue, LogMaskingUtil.maskSensitiveData() expects JSON input. Cookie values are plain strings, so sensitive cookies (including AUTHORIZATION, session tokens, etc.) are logged without masking.

This is a critical security issue that undermines the PR's objective.

Apply this diff to implement proper cookie masking:

                // Cookies
                apiLogger.info("Cookies:\t");

                if (req.getCookies() == null || req.getCookies().asList().isEmpty()) {
                    apiLogger.info("\t<none>");
                } else {
                    req.getCookies().asList().forEach(cookie -> {
-                        // cookie.getName() -> cookie name
-                        // cookie.getValue() -> cookie value (String)
-                        String maskedValue = LogMaskingUtil.maskSensitiveData(cookie.getValue());
+                        String value = cookie.getValue();
+                        String cookieName = cookie.getName().toLowerCase();
+                        // Mask auth/session cookies
+                        if (cookieName.contains("authorization") || 
+                            cookieName.contains("auth") || 
+                            cookieName.contains("session") ||
+                            cookieName.contains("token")) {
+                            value = (value != null && value.length() > 4) 
+                                ? "****" + value.substring(value.length() - 4) 
+                                : "****";
+                        }
-                        apiLogger.info("\t" + cookie.getName() + "=" + maskedValue);
+                        apiLogger.info("\t" + cookie.getName() + "=" + value);
                    });
                }
🧹 Nitpick comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (1)

1248-1250: Request-based auto-generated ID extraction looks correct; minor robustness tweaks possible

The _reqsid hook and writeAutoGeneratedIdFromRequest implementation correctly:

  • Parse the request JSON,
  • Safely handle missing "request" objects, and
  • Store extracted IDs while masking logged payloads via LogMaskingUtil.

Two small robustness improvements to consider:

  • Trim field names after idKeyName.split(",") to tolerate "field1, field2" style inputs.
  • In the catch block, log the exception with the throwable (e.g., logger.error("...", e)) so stack traces are available when debugging template issues.

These don’t affect current behavior but would make failures easier to diagnose.

Also applies to: 3252-3277

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10523a6 and 4788925.

📒 Files selected for processing (4)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (7 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (89 hunks)
  • apitest-commons/src/main/resources/log4j.properties (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apitest-commons/src/main/resources/log4j.properties
🧰 Additional context used
🧬 Code graph analysis (2)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)
  • LogMaskingUtil (10-68)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (4)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)
  • RestAssuredPrettyLogger (11-107)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)
  • LogMaskingUtil (10-68)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/GlobalConstants.java (1)
  • GlobalConstants (3-284)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/ConfigManager.java (1)
  • ConfigManager (15-281)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-maven-apitest-commons / maven-build
  • GitHub Check: build-maven-authentication-demo-service / maven-build
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (3)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (2)

3350-3353: Good: response body now masked in error logs

The changes in writeAutoGeneratedId(Response, ...) to wrap response.asString() with LogMaskingUtil.maskSensitiveData(...) ensure that error logs no longer contain raw response bodies, aligning this helper with the PR’s log-masking goals.


7167-7195: Masked logging in checkResponseUTCTime aligns with log-masking objectives

Switching the initial log in checkResponseUTCTime to:

logger.info(LogMaskingUtil.maskSensitiveData(response.asString()));

ensures this diagnostic helper no longer prints raw responses, while leaving the parsing logic intact. This is consistent with the rest of the masking changes in the PR.

apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestClient.java (1)

55-55: LGTM: Masking filter and safe logging correctly applied.

The changes consistently apply RestAssuredPrettyLogger.getMaskingFilter() to request specifications and use LogMaskingUtil.safeLogInfo() for response body logging. The integration pattern is correct throughout the file.

Note: The effectiveness of this masking depends on fixing the issues identified in RestAssuredPrettyLogger.java regarding header and cookie masking.

Also applies to: 82-86, 290-301, 1982-1983

Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Copy link

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

♻️ Duplicate comments (4)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (4)

46-49: Critical: Request headers containing tokens are logged without masking.

LogMaskingUtil.maskSensitiveData() expects JSON input and returns the raw string unchanged when JSON parsing fails (see lines 14-17 in LogMaskingUtil). Since header values are plain strings (e.g., "Bearer eyJ...", "Basic dXNlcjpw..."), they are logged without any masking. This exposes authentication tokens, API keys, and credentials in logs, directly contradicting the PR's security objective.

Apply this diff to implement proper header value masking:

                // Headers
            	apiLogger.info("Headers:\t");
                req.getHeaders().asList()
-                        .forEach(h -> apiLogger.info("\t" + h.getName() + "=" + LogMaskingUtil.maskSensitiveData(h.getValue())));
+                        .forEach(h -> {
+                            String value = h.getValue();
+                            String headerNameLower = h.getName().toLowerCase();
+                            // Mask sensitive header values
+                            if (headerNameLower.contains("authorization") || 
+                                headerNameLower.contains("token") || 
+                                headerNameLower.contains("secret") ||
+                                headerNameLower.contains("api-key") ||
+                                headerNameLower.contains("cookie")) {
+                                value = (value != null && value.length() > 4) 
+                                    ? "****" + value.substring(value.length() - 4) 
+                                    : "****";
+                            }
+                            apiLogger.info("\t" + h.getName() + "=" + value);
+                        });

51-59: Critical: Cookies containing session/auth tokens are logged without masking.

Cookie values are plain strings, not JSON. LogMaskingUtil.maskSensitiveData() will fail to parse them as JSON and return the original unmasked values. This exposes session tokens, authentication cookies (e.g., Authorization, JSESSIONID), and other sensitive cookie data in logs.

Apply this diff to implement proper cookie value masking:

                // Cookies
-                String cookiesLog = (req.getCookies() == null || req.getCookies().asList().isEmpty()) 
-                        ? "<none>" 
-                        : req.getCookies().asList().stream()
-                            .map(c -> c.getName() + "=" + LogMaskingUtil.maskSensitiveData(c.getValue()))
-                            .reduce((a, b) -> a + ", " + b)
-                            .orElse("<none>");
+                String cookiesLog;
+                if (req.getCookies() == null || req.getCookies().asList().isEmpty()) {
+                    cookiesLog = "<none>";
+                } else {
+                    cookiesLog = req.getCookies().asList().stream()
+                        .map(c -> {
+                            String value = c.getValue();
+                            String cookieNameLower = c.getName().toLowerCase();
+                            // Mask sensitive cookies
+                            if (cookieNameLower.contains("auth") || 
+                                cookieNameLower.contains("session") ||
+                                cookieNameLower.contains("token") ||
+                                cookieNameLower.equals("authorization")) {
+                                value = (value != null && value.length() > 4) 
+                                    ? "****" + value.substring(value.length() - 4) 
+                                    : "****";
+                            }
+                            return c.getName() + "=" + value;
+                        })
+                        .reduce((a, b) -> a + ", " + b)
+                        .orElse("<none>");
+                }

                apiLogger.info("Cookies:\t" + cookiesLog);

62-76: Major: Multipart fields with sensitive data are not properly masked.

The current implementation has two issues:

  1. File content logged via String.valueOf() produces unhelpful output like File@hashcode
  2. Plain string multipart field values won't be masked since LogMaskingUtil.maskSensitiveData() expects JSON

This exposes sensitive form fields (e.g., password, secret, token) and provides poor debugging information for file uploads.

Apply this diff to properly handle multipart logging:

                // Multiparts (one line)
                if (req.getMultiPartParams() == null || req.getMultiPartParams().isEmpty()) {
                    apiLogger.info("Multiparts:\t<none>");
                } else {
                    StringBuilder mpLog = new StringBuilder();
                    req.getMultiPartParams().forEach(mp -> {
                        String key = mp.getControlName(); // field name
                        Object value = mp.getContent();   // value (file or string)
-                        String maskedValue = LogMaskingUtil.maskSensitiveData(String.valueOf(value));
+                        String displayValue;
+                        if (value instanceof java.io.File) {
+                            displayValue = "<file: " + ((java.io.File) value).getName() + ">";
+                        } else {
+                            String keyLower = key.toLowerCase();
+                            displayValue = String.valueOf(value);
+                            // Mask sensitive field names
+                            if (keyLower.contains("password") || keyLower.contains("secret") || 
+                                keyLower.contains("token") || keyLower.contains("key")) {
+                                displayValue = "****";
+                            }
+                        }
                        if (mpLog.length() > 0) {
                            mpLog.append(", ");
                        }
-                        mpLog.append(key).append("=").append(maskedValue);
+                        mpLog.append(key).append("=").append(displayValue);
                    });
                    apiLogger.info("Multiparts:\t" + mpLog.toString());
                }

90-93: Critical: Response headers with sensitive tokens are logged without masking.

Response headers suffer from the same issue as request headers. LogMaskingUtil.maskSensitiveData() cannot mask plain string header values. This exposes Set-Cookie, Authorization, and other sensitive response headers containing session tokens and credentials.

Apply this diff to implement proper response header masking:

                // Response headers
                response.getHeaders().forEach(
-                        h -> apiLogger.info(h.getName() + ": " + LogMaskingUtil.maskSensitiveData(h.getValue()))
+                        h -> {
+                            String value = h.getValue();
+                            String headerNameLower = h.getName().toLowerCase();
+                            // Mask sensitive response headers
+                            if (headerNameLower.contains("authorization") || 
+                                headerNameLower.contains("token") || 
+                                headerNameLower.contains("secret") ||
+                                headerNameLower.contains("set-cookie") ||
+                                headerNameLower.contains("api-key") ||
+                                headerNameLower.contains("cookie")) {
+                                value = (value != null && value.length() > 4) 
+                                    ? "****" + value.substring(value.length() - 4) 
+                                    : "****";
+                            }
+                            apiLogger.info(h.getName() + ": " + value);
+                        }
                );
🧹 Nitpick comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)

86-97: Add null safety for response handling.

While unlikely, the response from ctx.next() could theoretically be null or response.asString() could throw an exception. Adding null checks would improve robustness.

Apply this diff to add defensive null checks:

                // Execute the actual request
                Response response = ctx.next(req, res);
+                
+                if (response == null) {
+                    apiLogger.error("Response is null");
+                    return null;
+                }

                apiLogger.info(response.getStatusLine());

                // Response headers
                response.getHeaders().forEach(
                        h -> apiLogger.info(h.getName() + ": " + LogMaskingUtil.maskSensitiveData(h.getValue()))
                );

                // Response body
-                String maskedResponse = LogMaskingUtil.maskSensitiveData(response.asString());
-                apiLogger.info("\n" + maskedResponse);
+                try {
+                    String maskedResponse = LogMaskingUtil.maskSensitiveData(response.asString());
+                    apiLogger.info("\n" + maskedResponse);
+                } catch (Exception e) {
+                    apiLogger.error("Failed to log response body: " + e.getMessage());
+                }

                return response;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4788925 and 9d3989f.

📒 Files selected for processing (1)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-maven-authentication-demo-service / maven-build
  • GitHub Check: build-maven-apitest-commons / maven-build
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)

3-3: Verify Log4j version and consider upgrading to Log4j 2.x if not required.

The code imports org.apache.log4j.Logger (Log4j 1.x), which reached end-of-life in 2015 and has known security vulnerabilities. Check the project's dependency configuration to confirm whether this is an intentional requirement or if the project should migrate to Log4j 2.x (org.apache.logging.log4j.Logger).

Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Copy link

@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

♻️ Duplicate comments (2)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)

20-30: Root-level JSON arrays and nested arrays are not masked, leaking secrets in array bodies.

maskSensitiveData(String json) assumes a JSON object root and returns the raw input on parse failure, so payloads like ["token1", "token2"] or [{"password":"p"}, {"token":"t"}] will be logged unmasked. mask(JSONObject) also only walks a single-level JSONArray of JSONObject items and skips nested arrays.

Given this PR’s goal, array payloads should be treated the same as objects.

Consider extending the logic to handle both object and array roots and to recurse through arrays:

-    public static String maskSensitiveData(String json) {
-        if (json == null) return null;
-
-        try {
-            JSONObject obj = new JSONObject(json);
-            mask(obj);
-            return obj.toString(4); // pretty print
-        } catch (Exception e) {
-            return json; // In case the body is not JSON, just return raw
-        }
-    }
+    public static String maskSensitiveData(String json) {
+        if (json == null) {
+            return null;
+        }
+
+        String trimmed = json.trim();
+        try {
+            if (trimmed.startsWith("{")) {
+                JSONObject obj = new JSONObject(trimmed);
+                mask(obj);
+                return obj.toString(4);
+            } else if (trimmed.startsWith("[")) {
+                JSONArray arr = new JSONArray(trimmed);
+                maskArray(arr);
+                return arr.toString(4);
+            }
+            // Not a JSON object/array, return as-is
+            return json;
+        } catch (Exception e) {
+            // In case the body is not valid JSON, just return raw
+            return json;
+        }
+    }
@@
-            } else if (value instanceof JSONArray) {
-                JSONArray arr = (JSONArray) value;
-                for (int i = 0; i < arr.length(); i++) {
-                    if (arr.get(i) instanceof JSONObject) {
-                        mask(arr.getJSONObject(i));
-                    }
-                }
-            } else {
+            } else if (value instanceof JSONArray) {
+                maskArray((JSONArray) value);
+            } else {
@@
-    }
+    }
+
+    private static void maskArray(JSONArray arr) {
+        for (int i = 0; i < arr.length(); i++) {
+            Object element = arr.get(i);
+            if (element instanceof JSONObject) {
+                mask((JSONObject) element);
+            } else if (element instanceof JSONArray) {
+                maskArray((JSONArray) element);
+            }
+        }
+    }

This ensures both request and response bodies that are arrays (including nested arrays) get the same masking guarantees as objects.

Also applies to: 36-44

apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (1)

43-49: Expiry-date token handling is sound; refine error reporting and trimming in addCustomExpiryDate

The $EXPIRYDATE: / $EXPIRYDATE_YEARS: support wired through inputJsonKeyWordHandeler and implemented via addCustomExpiryDate + getUtcTimestamp looks functionally correct and UTC-safe.

Two small issues in addCustomExpiryDate:

  • The NumberFormatException branch message is truncated and doesn’t show the offending numeric segment:
    throw new IllegalArgumentException("Invalid number for token '" + token);
  • Parsing Long.parseLong(json.substring(start, end)) without trimming means templates like $EXPIRYDATE: 5$ will fail even though a human would consider them valid.

A minimal, clearer, and still payload-safe improvement:

-            long amount;
-            try {
-                amount = Long.parseLong(json.substring(start, end));
-            } catch (NumberFormatException e) {
-                throw new IllegalArgumentException("Invalid number for token '" + token);
-            }
+            final String rawAmount = json.substring(start, end).trim();
+            final long amount;
+            try {
+                amount = Long.parseLong(rawAmount);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException(
+                        "Invalid numeric amount for token '" + token + "': '" + rawAmount + "'", e);
+            }
  • Keeps error messages self-contained and readable.
  • Accepts minor whitespace in templates.
  • Continues to avoid embedding the full JSON payload, consistent with earlier review feedback.

Also applies to: 3668-3673, 7321-7351

🧹 Nitpick comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)

76-90: Avoid != for Strings and harden Set-Cookie regex masking.

The header-masking loop uses reference comparison and an unescaped regex key:

if (headerName.equalsIgnoreCase(sensitiveKey) && sensitiveKey != "set-cookie") {
    return maskValue(value);
}
...
if ("set-cookie".equalsIgnoreCase(headerName)) {
    maskedValue = maskedValue.replaceAll("(?i)" + sensitiveKey + "=[^;]*", sensitiveKey + "=********");
}

Issues:

  • sensitiveKey != "set-cookie" relies on String interning and is a brittle Java anti-pattern.
  • Directly concatenating sensitiveKey into a regex will break if future keys contain regex metacharacters.

Suggested fix:

-            // If header name itself is sensitive -> mask fully
-            if (headerName.equalsIgnoreCase(sensitiveKey) && sensitiveKey != "set-cookie") {
-                return maskValue(value);
-            }
-
-            // If Set-Cookie header -> mask sensitive keys inside cookie value
-            if ("set-cookie".equalsIgnoreCase(headerName)) {
-                maskedValue = maskedValue.replaceAll("(?i)" + sensitiveKey + "=[^;]*", sensitiveKey + "=********");
-            }
+            // If header name itself is sensitive (except Set-Cookie) -> mask fully
+            if (!"set-cookie".equalsIgnoreCase(headerName)
+                    && headerName.equalsIgnoreCase(sensitiveKey)) {
+                return maskValue(value);
+            }
+
+            // If Set-Cookie header -> mask sensitive keys inside cookie value
+            if ("set-cookie".equalsIgnoreCase(headerName)) {
+                String quotedKey = Pattern.quote(sensitiveKey);
+                maskedValue = maskedValue.replaceAll("(?i)" + quotedKey + "=[^;]*",
+                        sensitiveKey + "=********");
+            }

This keeps the current behavior while avoiding subtle String/regex pitfalls.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d3989f and 4a7e13a.

📒 Files selected for processing (3)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (7 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)
  • LogMaskingUtil (11-92)
🔇 Additional comments (3)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (3)

1248-1250: Request-side auto-generated ID extraction is null-safe and masking-aware

The new _reqsid handling plus writeAutoGeneratedIdFromRequest correctly:

  • Guards against missing "request" object (optJSONObject + null check).
  • Caches per-field values via getAutogenIdKeyName/writeToCache.
  • Uses LogMaskingUtil.maskSensitiveData both for individual field values and entire request context in error logs.

This aligns with the PR’s masking goals and reuses existing ID-caching patterns cleanly.

Also applies to: 3252-3277


3350-3354: Masked response-body logging on ID extraction failures

Switching the error logs in writeAutoGeneratedId(Response, ...) to:

LogMaskingUtil.maskSensitiveData(response.asString())

prevents raw response payloads (tokens, secrets, PII) from being dumped into logs while still giving enough context for troubleshooting. This is exactly in line with the masking objectives.


7167-7195: Response-time logging now masked before UTC check

checkResponseUTCTime now logs the response via LogMaskingUtil.maskSensitiveData(response.asString()), which avoids raw body leakage while preserving the existing UTC validation logic. This is a safe, targeted change aligned with the masking requirements.

Copy link

@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

♻️ Duplicate comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)

22-32: JSON arrays (root-level and under sensitive keys) can leak secrets; extend masking logic

Right now maskSensitiveData(String json) assumes a JSON object; if the body is a valid JSON array (e.g. ["secret1","secret2"]), new JSONObject(json) will throw and the catch block returns the raw string, so nothing is masked. Similarly, in mask(JSONObject jsonObject), when a value is a JSONArray, you recurse only into JSONObject elements and never mask primitive elements, even if the key itself is in SENSITIVE_KEYS (e.g. {"token": ["secret1","secret2"]}).

This leaves several realistic cases (root-level arrays and arrays under sensitive keys) unmasked and defeats the purpose of this utility.

You can address both points by:

  • Detecting whether the input starts with { or [ and parsing as JSONObject or JSONArray accordingly.
  • Introducing a shared maskArray(JSONArray arr, boolean maskPrimitiveValues) helper that recurses into nested objects/arrays and masks primitive elements when the parent key (or the root) is considered sensitive.

For example:

@@
-    public static String maskSensitiveData(String json) {
-        if (json == null) return null;
-
-        try {
-            JSONObject obj = new JSONObject(json);
-            mask(obj);
-            return obj.toString(4); // pretty print
-        } catch (Exception e) {
-            return json; // In case the body is not JSON, just return raw
-        }
-    }
+    public static String maskSensitiveData(String json) {
+        if (json == null) {
+            return null;
+        }
+
+        try {
+            String trimmed = json.trim();
+            if (trimmed.startsWith("{")) {
+                JSONObject obj = new JSONObject(trimmed);
+                mask(obj);
+                return obj.toString(4); // pretty print
+            } else if (trimmed.startsWith("[")) {
+                JSONArray arr = new JSONArray(trimmed);
+                // Root-level arrays are treated as fully sensitive
+                maskArray(arr, true);
+                return arr.toString(4); // pretty print
+            }
+            // Not JSON, just return raw
+            return json;
+        } catch (Exception e) {
+            // In case the body is not JSON, just return raw
+            return json;
+        }
+    }
+
+    private static void maskArray(JSONArray arr, boolean maskPrimitiveValues) {
+        for (int i = 0; i < arr.length(); i++) {
+            Object element = arr.get(i);
+            if (element instanceof JSONObject) {
+                mask((JSONObject) element);
+            } else if (element instanceof JSONArray) {
+                maskArray((JSONArray) element, maskPrimitiveValues);
+            } else if (maskPrimitiveValues) {
+                arr.put(i, maskValue(String.valueOf(element)));
+            }
+        }
+    }
@@
-            } else if (value instanceof JSONArray) {
-                JSONArray arr = (JSONArray) value;
-                for (int i = 0; i < arr.length(); i++) {
-                    if (arr.get(i) instanceof JSONObject) {
-                        mask(arr.getJSONObject(i));
-                    }
-                }
+            } else if (value instanceof JSONArray) {
+                JSONArray arr = (JSONArray) value;
+                // If the key is sensitive, also mask primitive array elements
+                boolean maskPrimitives = SENSITIVE_KEYS.contains(key);
+                maskArray(arr, maskPrimitives);
             } else {
                 if (SENSITIVE_KEYS.contains(key)) {
                     jsonObject.put(key, maskValue(value.toString()));
                 }
             }

This way:

  • Root-level arrays are masked (including primitive strings).
  • Arrays under sensitive keys (like token, password) will have both nested objects and primitive elements masked.
  • Arrays under non-sensitive keys retain non-sensitive primitive values.

Also applies to: 34-52

🧹 Nitpick comments (2)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (2)

64-71: Clarify expectations for safeLogInfo input format

safeLogInfo passes the whole message through maskSensitiveData(message), which only masks when message is valid JSON (object/array). If callers ever pass mixed log lines such as "Request failed: " + jsonBody, the parser will treat it as non‑JSON and return the original string, leaking secrets.

Either:

  • Document that safeLogInfo must be called with raw JSON only (and keep composition outside), or
  • Add a defensive fallback (e.g., detect and extract JSON segment, or apply header/param-style masking to the whole message).

Given this is a “safe” helper, tightening or documenting the contract would avoid surprises.


15-20: Consider using a Set for SENSITIVE_KEYS for faster lookups

SENSITIVE_KEYS is a List, and lookups use SENSITIVE_KEYS.contains(key) in tight loops over JSON keys and headers. The list is small, so this is not a blocker, but converting to an unmodifiable Set would give O(1) membership checks and slightly cleaner intent:

-import java.util.List;
+import java.util.List;
+import java.util.Set;
@@
-    private static final List<String> SENSITIVE_KEYS = Arrays.asList(
+    private static final Set<String> SENSITIVE_KEYS = Set.of(

(Not essential, but worth considering while this utility is new.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a7e13a and c444b73.

📒 Files selected for processing (2)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/RestAssuredPrettyLogger.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-maven-authentication-demo-service / maven-build
  • GitHub Check: build-maven-apitest-commons / maven-build
  • GitHub Check: Analyze (java-kotlin)

Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Copy link

@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

♻️ Duplicate comments (2)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (2)

20-30: Root-level JSON arrays remain unmasked despite previous resolution.

The past review comment on lines 20-30 flagged that root-level JSON arrays are not masked and was marked as resolved, but the suggested fix is not present in the current code. If the input JSON is an array (e.g., ["secret1", "secret2"]), parsing as JSONObject on line 24 will throw an exception and return the raw, unmasked content on line 28.

The suggested fix from the previous review should be applied:

 public static String maskSensitiveData(String json) {
     if (json == null) return null;

     try {
-        JSONObject obj = new JSONObject(json);
-        mask(obj);
-        return obj.toString(4); // pretty print
+        // Try parsing as object first
+        if (json.trim().startsWith("{")) {
+            JSONObject obj = new JSONObject(json);
+            mask(obj);
+            return obj.toString(4);
+        } else if (json.trim().startsWith("[")) {
+            JSONArray arr = new JSONArray(json);
+            maskArray(arr);
+            return arr.toString(4);
+        }
+        return json;
     } catch (Exception e) {
         return json; // In case the body is not JSON, just return raw
     }
 }
+
+private static void maskArray(JSONArray arr) {
+    for (int i = 0; i < arr.length(); i++) {
+        if (arr.get(i) instanceof JSONObject) {
+            mask(arr.getJSONObject(i));
+        } else if (arr.get(i) instanceof JSONArray) {
+            maskArray(arr.getJSONArray(i));
+        }
+    }
+}

38-44: Primitive string values in arrays are not masked.

The array handling logic only masks JSONObject elements within arrays (lines 41-43) but ignores primitive values. If a sensitive key contains an array of strings (e.g., {"tokens": ["secret1", "secret2"]}), the string elements will remain unmasked.

Consider extending the masking logic to handle primitive string values:

 } else if (value instanceof JSONArray) {
     JSONArray arr = (JSONArray) value;
     for (int i = 0; i < arr.length(); i++) {
-        if (arr.get(i) instanceof JSONObject) {
+        Object element = arr.get(i);
+        if (element instanceof JSONObject) {
             mask(arr.getJSONObject(i));
+        } else if (element instanceof JSONArray) {
+            // Recursively handle nested arrays
+            mask(new JSONObject().put("temp", element));
+        } else if (SENSITIVE_KEYS.contains(key) && element instanceof String) {
+            arr.put(i, maskValue(element.toString()));
         }
     }
 }
🧹 Nitpick comments (3)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1)

12-12: Consider adding a private constructor and JavaDoc.

As a utility class with only static methods, consider:

  1. Adding a private constructor to prevent instantiation
  2. Adding JavaDoc comments to document the masking behavior and usage
+/**
+ * Utility class for masking sensitive information in logs.
+ * Masks fields like passwords, tokens, and client secrets in JSON bodies,
+ * HTTP headers, and request parameters.
+ */
 public class LogMaskingUtil {
+    
+    // Private constructor to prevent instantiation
+    private LogMaskingUtil() {
+        throw new UnsupportedOperationException("Utility class");
+    }
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (2)

43-49: Expiry-date helper and tokens look correct; optional cleanup to stay purely on java.time

The new $EXPIRYDATE$ / $EXPIRYDATE_10YEARS$ handling and getUtcTimestamp helper correctly generate UTC timestamps in the same string format as existing timestamp utilities, so behavior is consistent.

If you want to simplify later, you could avoid the Date/SimpleDateFormat bridge and format ZonedDateTime directly via a DateTimeFormatter, but that’s purely cosmetic here.

Also applies to: 3781-3786, 7436-7443


3365-3391: writeAutoGeneratedIdFromRequest correctly handles missing request; trim field names for robustness

This helper now safely uses optJSONObject("request"), guards the null case, and routes both success/error logs through LogMaskingUtil, which aligns well with the PR’s masking goals.

To make it a bit more tolerant of configuration typos, you could trim each field name after splitting on commas, so "id1, id2" still works:

-        String[] fieldNames = idKeyName.split(",");
-        for (String filedName : fieldNames) {
+        String[] fieldNames = idKeyName.split(",");
+        for (String filedName : fieldNames) {
+            filedName = filedName.trim();
+            if (filedName.isEmpty()) {
+                continue;
+            }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dcea49 and e0d70bf.

📒 Files selected for processing (2)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (7 hunks)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-maven-apitest-commons / maven-build
  • GitHub Check: build-maven-authentication-demo-service / maven-build
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (5)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/LogMaskingUtil.java (2)

71-90: Verify the fallback masking strategy for unmatched headers.

On line 89, if the header name doesn't match any sensitive key and isn't set-cookie, the method falls back to maskSensitiveData(value), which attempts to parse value as JSON. If value is a plain string (e.g., a bearer token or API key), it will be returned unmasked since it's not valid JSON.

Please confirm whether this is the intended behavior. If unrecognized headers should remain unmasked (allow-list approach), the current logic is correct. However, if the goal is to mask all potentially sensitive headers defensively (deny-list approach), consider updating the fallback logic:

-    return maskedValue.equals(value) ? maskSensitiveData(value) : maskedValue;
+    // If no masking was applied and value looks sensitive, mask it
+    return maskedValue.equals(value) && value.length() > 10 ? maskValue(value) : maskedValue;

Alternatively, you can verify the intended behavior by checking how headers are used in RestAssuredPrettyLogger and RestClient to ensure the masking policy aligns with security requirements.


92-100: Clean implementation of parameter masking.

The method correctly handles null/empty maps and leverages the header masking logic to mask parameter values. The stream-based formatting is clear and concise.

apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (3)

3452-3467: Masked logging in writeAutoGeneratedId(Response…) is aligned with log‑masking objectives

Replacing raw response.asString() with LogMaskingUtil.maskSensitiveData(response.asString()) in the error paths ensures whole-response logs are redacted without changing functional behavior.

Looks good and consistent with the rest of the PR.


7282-7310: checkResponseUTCTime now logs masked responses; behavior unchanged

The added call to LogMaskingUtil.maskSensitiveData(response.asString()) protects the response payload in this diagnostic log while preserving the existing UTC‑validation logic.

No further changes needed here.


7436-7443: getUtcTimestamp implementation is correct and thread‑safe

The helper computes now + amountToAdd in UTC and formats it with a fresh SimpleDateFormat, so there are no shared mutable date-format instances and no timezone surprises.

All good as implemented.

Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Copy link

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

♻️ Duplicate comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (1)

1265-1334: Fix userDefinedCookie branch to remove COOKIE_NAME before re-serializing the body

In postWithBodyAndCookieForAutoGeneratedIdFromRequest, the userDefinedCookie branch still does:

  • inputJson = request.toString(); before removing GlobalConstants.COOKIE_NAME, so the outgoing JSON body can retain cookieName, unlike other helpers that strip both COOKIE and COOKIE_NAME.

This is the same issue already raised in a prior review and should be aligned with the fixed pattern above in postWithBodyAndCookie.

Suggested fix for this method:

-    } else if (role.equals("userDefinedCookie")) {
-        JSONObject request = new JSONObject(inputJson);
-        if (request.has(GlobalConstants.COOKIE)) {
-            token = request.get(GlobalConstants.COOKIE).toString();
-            request.remove(GlobalConstants.COOKIE);
-            inputJson = request.toString();
-            if (request.has(GlobalConstants.COOKIE_NAME)) {
-                cookieName = request.get(GlobalConstants.COOKIE_NAME).toString();
-                request.remove(GlobalConstants.COOKIE_NAME);
-            }
-        }
-    } else {
+    } else if (role.equals("userDefinedCookie")) {
+        JSONObject request = new JSONObject(inputJson);
+        if (request.has(GlobalConstants.COOKIE)) {
+            token = request.get(GlobalConstants.COOKIE).toString();
+            request.remove(GlobalConstants.COOKIE);
+            if (request.has(GlobalConstants.COOKIE_NAME)) {
+                cookieName = request.get(GlobalConstants.COOKIE_NAME).toString();
+                request.remove(GlobalConstants.COOKIE_NAME);
+            }
+            inputJson = request.toString();
+        }
+    } else {

Also consider applying the same ordering fix to the earlier postWithBodyAndCookieForAutoGeneratedId method, which has the same pattern.

🧹 Nitpick comments (3)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (3)

3736-3741: Expiry placeholders and getUtcTimestamp are consistent; verify token naming and duration choices

The new handling:

  • Replaces $EXPIRYDATE$ with getUtcTimestamp(365, ChronoUnit.DAYS) and $EXPIRYDATE_10YEARS$ with getUtcTimestamp(10, ChronoUnit.YEARS), and
  • getUtcTimestamp produces a UTC ISO‑8601-style string (yyyy-MM-dd'T'HH:mm:ss.SSS'Z'), matching the format of existing timestamp helpers.

Behavior looks correct, but two minor points to confirm:

  • The PR description mentions $EXPIRYDATE_YEARS$ while the code uses $EXPIRYDATE_10YEARS$; ensure test templates and docs use the same token names.
  • For $EXPIRYDATE$, you hard-code 365 days rather than 1 calendar year; if leap-year accuracy matters even in tests, you may prefer ChronoUnit.YEARS with amount 1.

If these semantics match your test data, no further changes are needed.

Also applies to: 7390-7397


4755-4759: KEYWORD_DATA translation path mirrors FILTERS logic and looks sound

The updated isDataRequired branch:

  • Rebuilds the first KEYWORD_DATA object with valueToConvert replaced by translatedValue.
  • Wraps it back into a singleton array and reassigns GlobalConstants.KEYWORD_DATA on langjson.

This mirrors the existing FILTERS handling and keeps the structure [ { ...translated fields... } ]. Only caveat is that toString().replace(...) will also replace incidental substring matches, but that’s consistent with prior code.


7390-7397: getUtcTimestamp implementation is correct; consider reusing java.time formatting

getUtcTimestamp correctly:

  • Starts from ZonedDateTime.now(ZoneOffset.UTC),
  • Adds amountToAdd in the provided ChronoUnit, and
  • Formats the result as a UTC 'Z' timestamp.

Functionality is fine; if you ever revisit this, you could simplify by using DateTimeFormatter on the ZonedDateTime directly and avoid bouncing through Date/SimpleDateFormat, but that’s purely a style/maintainability tweak.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0d70bf and 951053c.

📒 Files selected for processing (1)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-maven-apitest-commons / maven-build
  • GitHub Check: build-maven-authentication-demo-service / maven-build
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (2)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (2)

3320-3346: Request-ID extraction from request body is now safe and masked; behavior looks correct

The new writeAutoGeneratedIdFromRequest helper:

  • Guards against a missing "request" block (requestObj != null ? ... : reqJson.toString()), avoiding the NPE risk called out in a prior review.
  • Supports multiple comma-separated field names and writes each to the existing cache using getAutogenIdKeyName, consistent with the response-based variant.
  • Logs both successes and failures through LogMaskingUtil.maskSensitiveData, so even when logging the whole request/root JSON, secrets are masked.

The corresponding changes in writeAutoGeneratedId(Response, ...) to mask the logged response body also align with the PR’s log-masking goal. No functional issues spotted here.

Also applies to: 3417-3422


7236-7264: Masking full response body in checkResponseUTCTime aligns with logging goals

checkResponseUTCTime now logs:

logger.info(LogMaskingUtil.maskSensitiveData(response.asString()));

instead of the raw body, which reduces accidental leakage of tokens/secrets in this diagnostic method. Time comparison logic is unchanged. No further issues here.

Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Signed-off-by: SradhaMohanty5899 <mohantysradha10@gmail.com>
Copy link

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

♻️ Duplicate comments (1)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (1)

1265-1334: userDefinedCookie branch still leaves COOKIE_NAME in request JSON body

In postWithBodyAndCookieForAutoGeneratedIdFromRequest, inputJson is set to request.toString() before removing COOKIE_NAME, so the outgoing JSON can still contain that field. This is the same issue previously flagged for this method and can cause schema/validation mismatches.

Consider aligning with the pattern used in postWithBodyAndCookie by moving the serialization after both removals:

        } else if (role.equals("userDefinedCookie")) {
            JSONObject request = new JSONObject(inputJson);
            if (request.has(GlobalConstants.COOKIE)) {
                token = request.get(GlobalConstants.COOKIE).toString();
                request.remove(GlobalConstants.COOKIE);
-                inputJson = request.toString();
                if (request.has(GlobalConstants.COOKIE_NAME)) {
                    cookieName = request.get(GlobalConstants.COOKIE_NAME).toString();
                    request.remove(GlobalConstants.COOKIE_NAME);
                }
+                inputJson = request.toString();
            }
        } else {

You may also want to mirror this fix in the existing postWithBodyAndCookieForAutoGeneratedId method above, which currently has the same sequencing.

🧹 Nitpick comments (3)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (3)

3367-3393: Request‑ID extraction helper is correct and masks logs; consider trimming field names

The new writeAutoGeneratedIdFromRequest correctly:

  • Reads from an optional nested "request" object.
  • Caches each requested field under the computed key.
  • Uses LogMaskingUtil.maskSensitiveData for both success and error logs, avoiding raw payload leakage.

Functionally this looks good. Optionally, to be robust against "idKeyName" strings like "id1, id2", you could trim() each split field name and skip empty entries before lookup.


3783-3788: Expiry tokens wired correctly; verify intended semantics for 1‑year vs 365‑day token

$EXPIRYDATE$ is now resolved as now + 365 DAYS and $EXPIRYDATE_10YEARS$ as now + 10 YEARS via getUtcTimestamp, which is functionally sound. If callers conceptually treat $EXPIRYDATE$ as “+1 year” rather than “+365 days”, you may want to switch that call to ChronoUnit.YEARS for consistency with the 10‑year variant; otherwise the current behavior is fine.


7438-7445: getUtcTimestamp implementation is correct; could be simplified with java.time formatting

The helper correctly computes now(UTC) + amountToAdd in the given ChronoUnit and formats it as an ISO‑like UTC string compatible with existing timestamp fields. If you ever want to reduce java.util.Date usage, you could instead format the ZonedDateTime directly with a cached DateTimeFormatter, but that’s an optional cleanup and not required for correctness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 951053c and 79f7ba2.

📒 Files selected for processing (1)
  • apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-maven-apitest-commons / maven-build
  • GitHub Check: build-maven-authentication-demo-service / maven-build
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (3)
apitest-commons/src/main/java/io/mosip/testrig/apirig/utils/AdminTestUtil.java (3)

42-49: Time-related imports align with new utilities

Additional java.time imports are consistent with usages in getCurrentUTCTime / getUtcTimestamp; no issues from an API or dependency standpoint.


3465-3469: Masked error logging in writeAutoGeneratedId(Response,…)

Wrapping response.asString() with LogMaskingUtil.maskSensitiveData in the error paths ensures full response bodies aren’t logged in clear text when IDs can’t be found, which aligns with the log‑masking objectives.


7284-7312: UTC response‑time check now logs masked payloads

Changing checkResponseUTCTime to log LogMaskingUtil.maskSensitiveData(response.asString()) instead of the raw body prevents unmasked responses from appearing in info‑level logs while preserving the existing UTC validation logic.

@mohanachandran-s mohanachandran-s merged commit 362bf41 into mosip:develop Dec 16, 2025
9 checks passed
@SradhaMohanty5899 SradhaMohanty5899 changed the title MOSIP-29075: Masking Sensitive Information (Client-secret key, Password, pwd, token, refreshtoken) etc. From Log. MOSIP-29075 : Remove secrets from API testrig logs when debug is disabled. Jan 5, 2026
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.

2 participants