Skip to content

[CUS-11560] added filters for gmail api addon.#366

Open
ManojTestsigma wants to merge 1 commit intodevfrom
CUS-11560
Open

[CUS-11560] added filters for gmail api addon.#366
ManojTestsigma wants to merge 1 commit intodevfrom
CUS-11560

Conversation

@ManojTestsigma
Copy link
Copy Markdown
Contributor

@ManojTestsigma ManojTestsigma commented Mar 27, 2026

please review this addon and publish as PUBLIC

Addon name : Gmail_api
Addon accont: https://jarvis.testsigma.com/ui/tenants/2817/addons
Jira: https://testsigma.atlassian.net/browse/CUS-11560

fix

Summary by CodeRabbit

Release Notes v1.1.8

  • New Features
    • Added ability to filter and retrieve Gmail emails by sender, recipient, or subject across Android, iOS, and Web platforms.
    • Added regex pattern matching to extract specific content from filtered Gmail emails and store results in variables.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

This PR increments the Maven version from 1.1.7 to 1.1.8 and refactors Gmail API actions across Android, iOS, and Web platforms to use a new centralized GmailUtils class. It adds two new action types—GetFilteredGmailContent and GetGmailContentByRegex—while consolidating repetitive Gmail connection, content extraction, and resource cleanup logic.

Changes

Cohort / File(s) Summary
Version Management
gmail_api/pom.xml
Incremented artifact version from 1.1.7 to 1.1.8.
Utility Support
gmail_api/src/main/java/utils/GmailUtils.java
New utility class with static methods for Gmail IMAP connectivity (connectToGmail), message content extraction (extractContent), search term construction (buildSearchTerm), credential sanitization, and resource cleanup.
Existing Gmail Actions Refactor
gmail_api/src/main/java/com/android/GetGmailContent.java, gmail_api/src/main/java/com/ios/GetGmailContent.java, gmail_api/src/main/java/com/web/GetGmailContent.java
Refactored to use GmailUtils methods for connection, extraction, and cleanup. Added explicit validation for empty inboxes and null/blank message content. Enhanced error handling with dedicated AuthenticationFailedException branch. Removed inline IMAP configuration and manual resource closing.
New Filtered Email Actions
gmail_api/src/main/java/com/android/GetFilteredGmailContent.java, gmail_api/src/main/java/com/ios/GetFilteredGmailContent.java, gmail_api/src/main/java/com/web/GetFilteredGmailContent.java
New action classes that search Gmail inbox by filter criteria (From, To, or Subject), extract the latest matching message content, and store it in a runtime variable. Includes comprehensive error handling for authentication, invalid arguments, and missing messages.
New Regex-based Email Actions
gmail_api/src/main/java/com/android/GetGmailContentByRegex.java, gmail_api/src/main/java/com/ios/GetGmailContentByRegex.java, gmail_api/src/main/java/com/web/GetGmailContentByRegex.java
New action classes that combine email filtering with regex pattern matching. Extract the latest matching message, apply a user-provided regex pattern, and store the first matched group in a runtime variable. Handle PatternSyntaxException and other errors appropriately.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • Ganesh-Testsigma
  • vigneshtestsigma

Poem

🐰 Inbox inbox, let's connect fast,
With filters and regex, searching at last,
From Android to web, one utility shines,
GmailUtils refactored—so clean are these lines!

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding filter functionality to the Gmail API addon, which aligns with the substantial additions of filtered-content retrieval classes.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch CUS-11560

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (2)
gmail_api/src/main/java/com/web/GetGmailContent.java (1)

42-50: Avoid fetching the whole inbox just to read one message.

inbox.getMessages() builds an array for every message even though only the newest one is used. getMessageCount() plus getMessage(count) keeps this path lighter on large mailboxes.

Proposed fix
-            Message[] messages = inbox.getMessages();
-            logger.info("messages: " + messages.length);
+            int messageCount = inbox.getMessageCount();
+            logger.info("messages: " + messageCount);
 
-            if (messages.length == 0) {
+            if (messageCount == 0) {
                 setErrorMessage("No emails found in the INBOX for '" + username + "'. The mailbox is empty.");
                 return Result.FAILED;
             }
 
-            Message latestMessage = messages[messages.length - 1];
+            Message latestMessage = inbox.getMessage(messageCount);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/com/web/GetGmailContent.java` around lines 42 - 50,
The code currently calls inbox.getMessages() which allocates an array for every
message; instead, call inbox.getMessageCount() and if zero call
setErrorMessage(...) and return Result.FAILED, otherwise fetch only the newest
message with inbox.getMessage(messageCount) and assign it to latestMessage.
Update the block around Message[] messages / latestMessage to use
getMessageCount() and getMessage() so only the single latest Message is
retrieved (refer to inbox.getMessages(), inbox.getMessageCount(),
inbox.getMessage(), latestMessage, setErrorMessage, Result.FAILED).
gmail_api/src/main/java/com/web/GetFilteredGmailContent.java (1)

55-88: Consider extracting the filtered fetch flow into GmailUtils.

The connect/open/search/latest-message/empty-body sequence here is almost identical to GetGmailContentByRegex. Pulling it behind one helper would keep filter behavior and failure handling aligned in one place.

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

In `@gmail_api/src/main/java/com/web/GetFilteredGmailContent.java` around lines 55
- 88, Extract the repeated INBOX fetch/search/latest-message logic into a new
GmailUtils helper method (e.g., fetchLatestFilteredMessage or
getLatestMessageBySearch) that encapsulates connectToGmail,
store.getFolder("INBOX"), inbox.open(...), inbox.search(SearchTerm), selecting
the latest Message and returning either the Message or a result object
containing the Message and error information; update
GetFilteredGmailContent.execute (or the method containing this diff) to call
that new GmailUtils method, handle the returned error cases exactly as now
(setErrorMessage/Result.FAILED when no messages or empty body), and use
GmailUtils.extractContent to obtain the body; ensure the new helper throws or
returns meaningful errors so both GetFilteredGmailContent and
GetGmailContentByRegex can use the same failure handling and logging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gmail_api/src/main/java/com/android/GetGmailContent.java`:
- Around line 65-67: The success message currently includes the full email body
via fullMessage, which leaks sensitive content; update the setSuccessMessage
call in GetGmailContent (the code that constructs the message using
setSuccessMessage, var1.getValue().toString(), and fullMessage) to remove
fullMessage from the output and only reference the runtime variable name (e.g.,
var1.getValue().toString()) or a non-sensitive placeholder (e.g., "email stored
in runtime variable") so the actual email body is not echoed in persisted step
output.

In `@gmail_api/src/main/java/com/android/GetGmailContentByRegex.java`:
- Around line 96-103: The code currently logs and echoes the sensitive regex
match (matchedContent) via logger.info and setSuccessMessage; remove the secret
from logs and success output: keep extracting matcher.group(0) and storing it in
runTimeData.setValue(matchedContent) and
runTimeData.setKey(var1.getValue().toString()), but change the logger.info call
to not include matchedContent (e.g., log a generic "Regex matched" or include
only non-sensitive metadata) and update setSuccessMessage to only mention the
target variable name (var1.getValue().toString()) without appending the
matchedContent; ensure no other references to matchedContent are logged or
returned.

In `@gmail_api/src/main/java/com/ios/GetGmailContent.java`:
- Around line 65-67: The success message currently echoes the full email body
via setSuccessMessage which concatenates fullMessage (see setSuccessMessage,
var1, fullMessage); remove the inclusion of fullMessage from the message and
only reference the runtime variable name/value placeholder (e.g., "Email content
stored in runtime variable: " + var1.getValue().toString()) so the email body
stored in runTimeData is not duplicated in persisted step output.

In `@gmail_api/src/main/java/com/ios/GetGmailContentByRegex.java`:
- Around line 96-103: The code currently logs and includes the extracted secret
(matcher.group(0)) in the success message; remove or redact that sensitive value
from logs and user-visible messages: stop calling logger.info with the full
matchedContent, keep storing the value via runTimeData.setValue(matchedContent)
and runTimeData.setKey(var1.getValue().toString()), and change setSuccessMessage
to a generic message like "Matched content stored in variable: <variableName>"
(no matchedContent). Update any other logging around matcher.group(0) to only
state that a match occurred without printing the match.

In `@gmail_api/src/main/java/com/web/GetGmailContent.java`:
- Around line 62-66: The success message currently includes the entire email
body (fullMessage) which may leak sensitive data; update the code in
GetGmailContent where runTimeData is set (runTimeData.setValue(fullMessage);
runTimeData.setKey(var1.getValue().toString()); setSuccessMessage(...)) so that
setSuccessMessage only mentions the runtime variable name
(var1.getValue().toString()) or a generic confirmation, and does NOT include
fullMessage or any portion of the email body — keep the fullMessage stored only
in runTimeData.

In `@gmail_api/src/main/java/com/web/GetGmailContentByRegex.java`:
- Around line 95-103: The current code logs and surfaces the full extracted
value (matchedContent) which may leak secrets; remove or disable the
logger.info("Regex matched: " + matchedContent) call and stop embedding
matchedContent into setSuccessMessage. Instead keep storing the raw value only
in com.testsigma.sdk.RunTimeData via runTimeData.setValue(matchedContent) and
set the public message via setSuccessMessage to a static or redacted string
(e.g., "Matched content stored in variable: <varName>") referencing
var1.getValue().toString() for context; ensure no other calls echo
matchedContent outside runTimeData.

In `@gmail_api/src/main/java/utils/GmailUtils.java`:
- Around line 38-52: The extractContent method currently only inspects
getBodyPart(0) and casts unsafely; replace it with a recursive MIME-tree walker
in GmailUtils.extractContent that traverses Multipart and Part nodes, checks
Part.isMimeType("text/html") and Part.isMimeType("text/plain") instead of blind
String casts, prefers text/html over text/plain when both exist (handle
multipart/alternative), skips attachments by checking Part.getDisposition() and
Part.getFileName(), and returns the first suitable body text found; ensure you
handle nested Multipart by recursing into each body part and safely call
getContent() only after confirming the part's mime type or traversing further.
- Around line 17-25: The Properties setup in GmailUtils (the Properties props
block) lacks IMAP timeouts causing potential indefinite hangs; add
mail.imaps.connectiontimeout and mail.imaps.timeout entries to props (set to
appropriate millisecond values, e.g. 15000 for connection timeout and 30000 for
read timeout) before the code that opens the store/connection (the code that
uses props to create the IMAP session and calls store.connect or
Session.getInstance). Ensure you use the same "mail.imaps" prefix as the other
properties so the IMAP client honors the timeouts.

---

Nitpick comments:
In `@gmail_api/src/main/java/com/web/GetFilteredGmailContent.java`:
- Around line 55-88: Extract the repeated INBOX fetch/search/latest-message
logic into a new GmailUtils helper method (e.g., fetchLatestFilteredMessage or
getLatestMessageBySearch) that encapsulates connectToGmail,
store.getFolder("INBOX"), inbox.open(...), inbox.search(SearchTerm), selecting
the latest Message and returning either the Message or a result object
containing the Message and error information; update
GetFilteredGmailContent.execute (or the method containing this diff) to call
that new GmailUtils method, handle the returned error cases exactly as now
(setErrorMessage/Result.FAILED when no messages or empty body), and use
GmailUtils.extractContent to obtain the body; ensure the new helper throws or
returns meaningful errors so both GetFilteredGmailContent and
GetGmailContentByRegex can use the same failure handling and logging.

In `@gmail_api/src/main/java/com/web/GetGmailContent.java`:
- Around line 42-50: The code currently calls inbox.getMessages() which
allocates an array for every message; instead, call inbox.getMessageCount() and
if zero call setErrorMessage(...) and return Result.FAILED, otherwise fetch only
the newest message with inbox.getMessage(messageCount) and assign it to
latestMessage. Update the block around Message[] messages / latestMessage to use
getMessageCount() and getMessage() so only the single latest Message is
retrieved (refer to inbox.getMessages(), inbox.getMessageCount(),
inbox.getMessage(), latestMessage, setErrorMessage, Result.FAILED).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9da0f4a9-fbe4-460e-9105-c3bcaded035d

📥 Commits

Reviewing files that changed from the base of the PR and between 5099e11 and dbc2693.

📒 Files selected for processing (11)
  • gmail_api/pom.xml
  • gmail_api/src/main/java/com/android/GetFilteredGmailContent.java
  • gmail_api/src/main/java/com/android/GetGmailContent.java
  • gmail_api/src/main/java/com/android/GetGmailContentByRegex.java
  • gmail_api/src/main/java/com/ios/GetFilteredGmailContent.java
  • gmail_api/src/main/java/com/ios/GetGmailContent.java
  • gmail_api/src/main/java/com/ios/GetGmailContentByRegex.java
  • gmail_api/src/main/java/com/web/GetFilteredGmailContent.java
  • gmail_api/src/main/java/com/web/GetGmailContent.java
  • gmail_api/src/main/java/com/web/GetGmailContentByRegex.java
  • gmail_api/src/main/java/utils/GmailUtils.java

Comment on lines +65 to +67
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
" and value: " + fullMessage);
return Result.SUCCESS;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not echo the full email body in the success message.

fullMessage is already stored in runTimeData. Repeating it here exposes the entire email in persisted step output, which is risky for a public Gmail addon.

🔒 Proposed fix
-            setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
-                    " and value: " + fullMessage);
+            setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString());
             return Result.SUCCESS;
📝 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
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
" and value: " + fullMessage);
return Result.SUCCESS;
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString());
return Result.SUCCESS;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/com/android/GetGmailContent.java` around lines 65 -
67, The success message currently includes the full email body via fullMessage,
which leaks sensitive content; update the setSuccessMessage call in
GetGmailContent (the code that constructs the message using setSuccessMessage,
var1.getValue().toString(), and fullMessage) to remove fullMessage from the
output and only reference the runtime variable name (e.g.,
var1.getValue().toString()) or a non-sensitive placeholder (e.g., "email stored
in runtime variable") so the actual email body is not echoed in persisted step
output.

Comment on lines +96 to +103
String matchedContent = matcher.group(0);
logger.info("Regex matched: " + matchedContent);

runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(matchedContent);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
", value: " + matchedContent);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep the extracted regex match out of logs and success output.

The match is usually the secret the caller wanted to capture. Logging it and echoing it in the success text leaks OTPs/tokens outside the runtime variable channel.

🔒 Proposed fix
                 String matchedContent = matcher.group(0);
-                logger.info("Regex matched: " + matchedContent);
+                logger.info("Regex match found. Length: " + matchedContent.length());

                 runTimeData = new com.testsigma.sdk.RunTimeData();
                 runTimeData.setValue(matchedContent);
                 runTimeData.setKey(var1.getValue().toString());
-                setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
-                        ", value: " + matchedContent);
+                setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString());
                 return Result.SUCCESS;
📝 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
String matchedContent = matcher.group(0);
logger.info("Regex matched: " + matchedContent);
runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(matchedContent);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
", value: " + matchedContent);
String matchedContent = matcher.group(0);
logger.info("Regex match found. Length: " + matchedContent.length());
runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(matchedContent);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/com/android/GetGmailContentByRegex.java` around lines
96 - 103, The code currently logs and echoes the sensitive regex match
(matchedContent) via logger.info and setSuccessMessage; remove the secret from
logs and success output: keep extracting matcher.group(0) and storing it in
runTimeData.setValue(matchedContent) and
runTimeData.setKey(var1.getValue().toString()), but change the logger.info call
to not include matchedContent (e.g., log a generic "Regex matched" or include
only non-sensitive metadata) and update setSuccessMessage to only mention the
target variable name (var1.getValue().toString()) without appending the
matchedContent; ensure no other references to matchedContent are logged or
returned.

Comment on lines +65 to +67
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
" and value: " + fullMessage);
return Result.SUCCESS;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not echo the full email body in the success message.

fullMessage is already stored in runTimeData. Repeating it here exposes the entire email in persisted step output, which is risky for a public Gmail addon.

🔒 Proposed fix
-            setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
-                    " and value: " + fullMessage);
+            setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString());
             return Result.SUCCESS;
📝 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
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
" and value: " + fullMessage);
return Result.SUCCESS;
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString());
return Result.SUCCESS;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/com/ios/GetGmailContent.java` around lines 65 - 67,
The success message currently echoes the full email body via setSuccessMessage
which concatenates fullMessage (see setSuccessMessage, var1, fullMessage);
remove the inclusion of fullMessage from the message and only reference the
runtime variable name/value placeholder (e.g., "Email content stored in runtime
variable: " + var1.getValue().toString()) so the email body stored in
runTimeData is not duplicated in persisted step output.

Comment on lines +96 to +103
String matchedContent = matcher.group(0);
logger.info("Regex matched: " + matchedContent);

runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(matchedContent);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
", value: " + matchedContent);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep the extracted regex match out of logs and success output.

The match is usually the secret the caller wanted to capture. Logging it and echoing it in the success text leaks OTPs/tokens outside the runtime variable channel.

🔒 Proposed fix
                 String matchedContent = matcher.group(0);
-                logger.info("Regex matched: " + matchedContent);
+                logger.info("Regex match found. Length: " + matchedContent.length());

                 runTimeData = new com.testsigma.sdk.RunTimeData();
                 runTimeData.setValue(matchedContent);
                 runTimeData.setKey(var1.getValue().toString());
-                setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
-                        ", value: " + matchedContent);
+                setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString());
                 return Result.SUCCESS;
📝 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
String matchedContent = matcher.group(0);
logger.info("Regex matched: " + matchedContent);
runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(matchedContent);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
", value: " + matchedContent);
String matchedContent = matcher.group(0);
logger.info("Regex match found. Length: " + matchedContent.length());
runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(matchedContent);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/com/ios/GetGmailContentByRegex.java` around lines 96
- 103, The code currently logs and includes the extracted secret
(matcher.group(0)) in the success message; remove or redact that sensitive value
from logs and user-visible messages: stop calling logger.info with the full
matchedContent, keep storing the value via runTimeData.setValue(matchedContent)
and runTimeData.setKey(var1.getValue().toString()), and change setSuccessMessage
to a generic message like "Matched content stored in variable: <variableName>"
(no matchedContent). Update any other logging around matcher.group(0) to only
state that a match occurred without printing the match.

Comment on lines 62 to +66
runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(FullMessage);
runTimeData.setValue(fullMessage);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() + "and value: " + FullMessage);

inbox.close(false);
store.close();

setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
" and value: " + fullMessage);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't include the email body in the success message.

This duplicates the full mailbox content into step/report output, which can leak OTPs or PII and create very large result payloads. Store the body only in runTimeData.

Proposed fix
             runTimeData = new com.testsigma.sdk.RunTimeData();
             runTimeData.setValue(fullMessage);
             runTimeData.setKey(var1.getValue().toString());
-            setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
-                    " and value: " + fullMessage);
+            setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString());
             return Result.SUCCESS;
📝 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
runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(FullMessage);
runTimeData.setValue(fullMessage);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() + "and value: " + FullMessage);
inbox.close(false);
store.close();
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString() +
" and value: " + fullMessage);
runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(fullMessage);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Email content stored in runtime variable: " + var1.getValue().toString());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/com/web/GetGmailContent.java` around lines 62 - 66,
The success message currently includes the entire email body (fullMessage) which
may leak sensitive data; update the code in GetGmailContent where runTimeData is
set (runTimeData.setValue(fullMessage);
runTimeData.setKey(var1.getValue().toString()); setSuccessMessage(...)) so that
setSuccessMessage only mentions the runtime variable name
(var1.getValue().toString()) or a generic confirmation, and does NOT include
fullMessage or any portion of the email body — keep the fullMessage stored only
in runTimeData.

Comment on lines +95 to +103
if (matcher.find()) {
String matchedContent = matcher.group(0);
logger.info("Regex matched: " + matchedContent);

runTimeData = new com.testsigma.sdk.RunTimeData();
runTimeData.setValue(matchedContent);
runTimeData.setKey(var1.getValue().toString());
setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
", value: " + matchedContent);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't echo the matched payload into routine output.

This action will often extract OTPs or tokens. Logging matchedContent and copying it into setSuccessMessage(...) leaks the secret into normal execution output; keep it only in runTimeData and use a static or redacted message instead.

Proposed fix
             if (matcher.find()) {
                 String matchedContent = matcher.group(0);
-                logger.info("Regex matched: " + matchedContent);
+                logger.info("Regex matched successfully");
 
                 runTimeData = new com.testsigma.sdk.RunTimeData();
                 runTimeData.setValue(matchedContent);
                 runTimeData.setKey(var1.getValue().toString());
-                setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString() +
-                        ", value: " + matchedContent);
+                setSuccessMessage("Matched content stored in variable: " + var1.getValue().toString());
                 return Result.SUCCESS;
Based on learnings, mailbox-debug logging is intentionally kept, but surfacing the extracted regex payload in normal output still needs redaction.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/com/web/GetGmailContentByRegex.java` around lines 95
- 103, The current code logs and surfaces the full extracted value
(matchedContent) which may leak secrets; remove or disable the
logger.info("Regex matched: " + matchedContent) call and stop embedding
matchedContent into setSuccessMessage. Instead keep storing the raw value only
in com.testsigma.sdk.RunTimeData via runTimeData.setValue(matchedContent) and
set the public message via setSuccessMessage to a static or redacted string
(e.g., "Matched content stored in variable: <varName>") referencing
var1.getValue().toString() for context; ensure no other calls echo
matchedContent outside runTimeData.

Comment on lines +17 to +25
Properties props = new Properties();
props.setProperty("mail.store.protocol", "imaps");
props.setProperty("mail.imaps.host", HOST);
props.setProperty("mail.imaps.port", PORT);
props.setProperty("mail.imaps.auth", "true");
props.setProperty("mail.imaps.starttls.enable", "true");
props.setProperty("mail.imap.ssl.protocols", "TLSv1.2");
props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "GmailUtils.java" -type f

Repository: testsigmahq/testsigma-addons

Length of output: 1887


🏁 Script executed:

# Once found, read the file to see full context
cat -n gmail_api/src/main/java/utils/GmailUtils.java | head -40

Repository: testsigmahq/testsigma-addons

Length of output: 1887


🌐 Web query:

JavaMail IMAPS timeout properties mail.imaps.connectiontimeout mail.imaps.timeout

💡 Result:

In JavaMail (and Jakarta Mail), the properties mail.imaps.connectiontimeout and mail.imaps.timeout control timeouts for IMAPS (IMAP over SSL) connections. mail.imaps.connectiontimeout: Socket connection timeout value in milliseconds. Implemented by java.net.Socket. Default is infinite timeout. Controls the time to establish the TCP connection to the server. mail.imaps.timeout: Socket read timeout value in milliseconds. Implemented by java.net.Socket. Default is infinite timeout. Controls the timeout for reading data from the server after connection. These properties are set in the Properties object used to create the Session, using the "imaps" prefix because they apply specifically to the IMAPS protocol. For example: Properties props = new Properties; props.setProperty("mail.imaps.host", "imap.example.com"); props.setProperty("mail.imaps.port", "993"); props.setProperty("mail.imaps.connectiontimeout", "30000"); // 30 seconds props.setProperty("mail.imaps.timeout", "60000"); // 60 seconds Session session = Session.getInstance(props); Note that for the plain "imap" protocol (non-SSL), use "mail.imap.connectiontimeout" and "mail.imap.timeout". The IMAPS provider shares the same property set as IMAP but with the protocol prefix. There is also mail.imaps.writetimeout for write timeouts (default infinite). These are documented in the official JavaMail IMAP provider documentation and confirmed in usage examples across Stack Overflow and other technical resources. As of 2026, these remain standard in Jakarta Mail implementations.

Citations:


Add IMAP connection and read timeouts.

This shared utility has no timeout configured, leaving connections vulnerable to indefinite hangs. Add mail.imaps.connectiontimeout and mail.imaps.timeout before the connection call:

Proposed fix
         props.setProperty("mail.imaps.auth", "true");
         props.setProperty("mail.imaps.starttls.enable", "true");
+        props.setProperty("mail.imaps.connectiontimeout", "10000");
+        props.setProperty("mail.imaps.timeout", "10000");
         props.setProperty("mail.imap.ssl.protocols", "TLSv1.2");
         props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
📝 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
Properties props = new Properties();
props.setProperty("mail.store.protocol", "imaps");
props.setProperty("mail.imaps.host", HOST);
props.setProperty("mail.imaps.port", PORT);
props.setProperty("mail.imaps.auth", "true");
props.setProperty("mail.imaps.starttls.enable", "true");
props.setProperty("mail.imap.ssl.protocols", "TLSv1.2");
props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
Properties props = new Properties();
props.setProperty("mail.store.protocol", "imaps");
props.setProperty("mail.imaps.host", HOST);
props.setProperty("mail.imaps.port", PORT);
props.setProperty("mail.imaps.auth", "true");
props.setProperty("mail.imaps.starttls.enable", "true");
props.setProperty("mail.imaps.connectiontimeout", "10000");
props.setProperty("mail.imaps.timeout", "10000");
props.setProperty("mail.imap.ssl.protocols", "TLSv1.2");
props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/utils/GmailUtils.java` around lines 17 - 25, The
Properties setup in GmailUtils (the Properties props block) lacks IMAP timeouts
causing potential indefinite hangs; add mail.imaps.connectiontimeout and
mail.imaps.timeout entries to props (set to appropriate millisecond values, e.g.
15000 for connection timeout and 30000 for read timeout) before the code that
opens the store/connection (the code that uses props to create the IMAP session
and calls store.connect or Session.getInstance). Ensure you use the same
"mail.imaps" prefix as the other properties so the IMAP client honors the
timeouts.

Comment on lines +38 to +52
public static String extractContent(Object content) throws Exception {
if (content instanceof String) {
return (String) content;
} else if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
BodyPart bodyPart = multipart.getBodyPart(0);
Object bodyContent = bodyPart.getContent();
if (bodyContent instanceof String) {
return (String) bodyContent;
} else if (bodyContent instanceof Multipart) {
Multipart bodyMultipart = (Multipart) bodyContent;
return (String) bodyMultipart.getBodyPart(0).getContent();
}
}
return null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "GmailUtils.java" | head -20

Repository: testsigmahq/testsigma-addons

Length of output: 1887


🌐 Web query:

Java Mail API multipart email extraction best practices MIME parts

💡 Result:

Best practices for extracting multipart email content using Jakarta Mail API (formerly JavaMail) involve recursively parsing the MimeMessage to handle nested multiparts like multipart/mixed, multipart/alternative, and multipart/related. The key is to use a recursive function that checks each Part's MIME type with isMimeType and processes text content, attachments, or nested Multiparts accordingly. Core approach from official docs and examples: 1. Call message.getContent to get the root content Object. 2. If it's a Multipart (e.g., MimeMultipart), iterate over getBodyPart(i) for i=0 to getCount-1. 3. For each BodyPart: - Check isMimeType("text/plain") or "text/html" and use getContent as String. - For attachments: check getDisposition == Part.ATTACHMENT or specific MIME types like "image/", use getInputStream or getFileName. - If isMimeType("multipart/"), recurse on (Multipart)part.getContent. 4. Prefer "text/html" over "text/plain" in multipart/alternative by checking subtype or processing last first. 5. Handle encoding issues: Set System property "mail.mime.parameters.strict" = "false" if charset parameters are malformed. Use part.getInputStream for raw bytes if getContent fails. Example recursive extraction function (adapted from JavaMail FAQ and StackOverflow high-score answers): private String getText(Part p) throws MessagingException, IOException { if (p.isMimeType("text/")) { String s = (String) p.getContent; textIsHtml = p.isMimeType("text/html"); return s; } if (p.isMimeType("multipart/alternative")) { // Prefer HTML over plain Multipart mp = (Multipart) p.getContent; String text = null; for (int i = 0; i < mp.getCount; i++) { Part bp = mp.getBodyPart(i); if (bp.isMimeType("text/plain")) { if (text == null) text = getText(bp); } else if (bp.isMimeType("text/html")) { htmlText = getText(bp); } } return (htmlText != null ? htmlText : text); } else if (p.isMimeType("multipart/")) { Multipart mp = (Multipart) p.getContent; for (int i = 0; i < mp.getCount; i++) { String s = getText(mp.getBodyPart(i)); if (s != null) return s; } } return null; } For attachments, collect in a separate pass or list: if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition) || part.isMimeType("application/") || part.isMimeType("image/")) { // Save using part.getFileName, part.getInputStream } Use libraries like Apache Commons Email MimeMessageParser for production to avoid reinventing recursion. Official MIME parsing properties (from Jakarta Mail docs): - mail.mime.multipart.allowempty=true (allow empty multiparts) - mail.mime.multipart.ignoremissingendboundary=false (strict parsing) Note: As of 2026, use Jakarta Mail (jakarta.mail:*), not legacy javax.mail. Always handle IOException and MessagingException.

Citations:


🌐 Web query:

JavaMail how to extract text body from multipart message with attachments

💡 Result:

To extract the text body from a multipart email message with attachments using JavaMail (Jakarta Mail), check if the message content is a Multipart. Iterate through the BodyParts, preferring text parts (text/plain or text/html) while skipping attachments (those with Content-Disposition: attachment). Use this recursive method from the official JavaMail FAQ, which handles common cases like multipart/mixed (body first, then attachments) and nested multipart/alternative (plain vs HTML): private boolean textIsHtml = false; private String getText(Part p) throws MessagingException, IOException { if (p.isMimeType("text/")) { String s = (String) p.getContent; textIsHtml = p.isMimeType("text/html"); return s; } if (p.isMimeType("multipart/alternative")) { // Prefer HTML over plain text Multipart mp = (Multipart) p.getContent; String text = null; for (int i = 0; i < mp.getCount; i++) { Part bp = mp.getBodyPart(i); if (bp.isMimeType("text/plain")) { if (text == null) text = getText(bp); continue; } else if (bp.isMimeType("text/html")) { String s = getText(bp); if (s != null) return s; } else { return getText(bp); } } return text; } else if (p.isMimeType("multipart/")) { Multipart mp = (Multipart) p.getContent; for (int i = 0; i < mp.getCount; i++) { String s = getText(mp.getBodyPart(i)); if (s != null) return s; } } return null; } Call it with: String body = getText(message); This returns the primary text content, detecting if it's HTML via textIsHtml. For attachments, continue iterating and check Part.getDisposition != null && Part.ATTACHMENT.equalsIgnoreCase(Part.getDisposition). Note: Use jakarta.mail.* for versions 2.0+ (post-Java EE).

Citations:


Walk the MIME tree instead of only getBodyPart(0).

The current implementation ignores all but the first part and blindly casts without type checking. This fails on:

  • Multipart emails where attachments precede the body
  • multipart/alternative containing both plain text and HTML versions
  • Nested multipart structures like multipart/related within multipart/mixed

The proposed recursive approach correctly iterates all parts and skips attachments. However, consider using isMimeType() to properly identify text parts (avoiding unsafe String casts) and handle multipart/alternative by preferring HTML over plain text.

📨 Proposed fix
     public static String extractContent(Object content) throws Exception {
         if (content instanceof String) {
             return (String) content;
-        } else if (content instanceof Multipart) {
-            Multipart multipart = (Multipart) content;
-            BodyPart bodyPart = multipart.getBodyPart(0);
-            Object bodyContent = bodyPart.getContent();
-            if (bodyContent instanceof String) {
-                return (String) bodyContent;
-            } else if (bodyContent instanceof Multipart) {
-                Multipart bodyMultipart = (Multipart) bodyContent;
-                return (String) bodyMultipart.getBodyPart(0).getContent();
+        }
+        if (content instanceof Multipart) {
+            Multipart multipart = (Multipart) content;
+            for (int i = 0; i < multipart.getCount(); i++) {
+                BodyPart bodyPart = multipart.getBodyPart(i);
+                if (Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {
+                    continue;
+                }
+                String extracted = extractContent(bodyPart.getContent());
+                if (extracted != null && !extracted.trim().isEmpty()) {
+                    return extracted;
+                }
             }
         }
         return null;
     }
📝 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
public static String extractContent(Object content) throws Exception {
if (content instanceof String) {
return (String) content;
} else if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
BodyPart bodyPart = multipart.getBodyPart(0);
Object bodyContent = bodyPart.getContent();
if (bodyContent instanceof String) {
return (String) bodyContent;
} else if (bodyContent instanceof Multipart) {
Multipart bodyMultipart = (Multipart) bodyContent;
return (String) bodyMultipart.getBodyPart(0).getContent();
}
}
return null;
public static String extractContent(Object content) throws Exception {
if (content instanceof String) {
return (String) content;
}
if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {
continue;
}
String extracted = extractContent(bodyPart.getContent());
if (extracted != null && !extracted.trim().isEmpty()) {
return extracted;
}
}
}
return null;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gmail_api/src/main/java/utils/GmailUtils.java` around lines 38 - 52, The
extractContent method currently only inspects getBodyPart(0) and casts unsafely;
replace it with a recursive MIME-tree walker in GmailUtils.extractContent that
traverses Multipart and Part nodes, checks Part.isMimeType("text/html") and
Part.isMimeType("text/plain") instead of blind String casts, prefers text/html
over text/plain when both exist (handle multipart/alternative), skips
attachments by checking Part.getDisposition() and Part.getFileName(), and
returns the first suitable body text found; ensure you handle nested Multipart
by recursing into each body part and safely call getContent() only after
confirming the part's mime type or traversing further.

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