Repository navigation
feat/CUS-11210-Added class to get the attachement file using subject filter - #363
Conversation
📝 WalkthroughWalkthroughThe PR updates Maven dependencies to newer versions (Selenium 4.37.0, Appium 9.4.0, TestSigma SDK 1.2.24_cloud, and others) and introduces three new Gmail attachment retrieval action classes for Android, iOS, and Web platforms. Each class connects to Gmail via IMAP, filters messages by subject, and downloads the latest received attachment. Changes
Sequence DiagramsequenceDiagram
participant Test as Test Framework
participant Action as GetLatestReceivedAttachment
participant IMAP as Gmail IMAP Server
participant FS as File System
Test->>Action: execute(username, passcode, subject-filter, attachment-index)
activate Action
Action->>IMAP: Connect via IMAPS
activate IMAP
IMAP-->>Action: Connection established
Action->>IMAP: Authenticate (username, passcode)
IMAP-->>Action: Authentication successful
Action->>IMAP: Open INBOX (read-only)
IMAP-->>Action: INBOX opened
Action->>IMAP: Fetch latest message
IMAP-->>Action: Message retrieved
deactivate IMAP
Action->>Action: Apply subject filter (if provided)
alt Subject matches filter
Action->>Action: Extract multipart attachments
Action->>Action: Validate attachment index
alt Attachment exists at index
Action->>FS: Download attachment to temp directory
FS-->>Action: File saved (path returned)
Action->>Test: Store file path in runtime variable
Test-->>Action: Variable stored
Action-->>Test: Result.SUCCESS
else Index out of range
Action-->>Test: Result.FAILED (attachment index error)
end
else Subject does not match
Action-->>Test: Result.FAILED (subject mismatch error)
else Inbox empty or no attachments
Action-->>Test: Result.FAILED (inbox/attachment error)
end
deactivate Action
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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/GetLatestReceivedAttachment.java`:
- Around line 204-206: Sanitize the attachment filename returned by
target.getFileName() before building filePath: strip any path separators and
parent traversal sequences, reject or normalize absolute paths, allow only a
safe whitelist of characters (or replace others), and ensure the resolved path
is inside downloadDir (use path normalization and a check that
Paths.get(downloadDir).resolve(safeName).normalize().startsWith(downloadDir)).
Also prevent overwrites by generating a unique filename (append timestamp or
UUID) if the sanitized name already exists, then pass that safe path to
target.saveFile; apply these changes around the MimeBodyPart target / filePath
creation and saveFile call.
- Around line 176-180: The current attachment detection logic using
bodyPart.getFileName() is unreliable and non-recursive; update the traversal in
GetLatestReceivedAttachment (the loop over multipart and the attachments
List<MimeBodyPart>) to instead check part.getDisposition() == Part.ATTACHMENT
(or Part.INLINE if you want inline attachments) when classifying attachments and
implement a recursive helper that walks any Multipart/BodyPart boundaries
(handling nested Multipart instances such as multipart/related and
multipart/alternative) to collect all MimeBodyPart attachments into the
attachments list so attachment-index is stable across MIME structures.
In `@gmail_api/src/main/java/com/ios/GetLatestReceivedAttachment.java`:
- Around line 204-206: The code directly uses target.getFileName() to build
filePath and call target.saveFile(...), which allows path traversal or
overwrites; sanitize and validate the attachment name before saving by
extracting only the base name (strip any path separators, remove "..", and
reject absolute paths), normalize the result (e.g., remove unsafe characters),
then construct the destination path using a safe join (use downloadDir +
File.separator + sanitizedName or a safe path join API) and ensure uniqueness
(append a counter, timestamp, or use a temp file API) if the file exists; update
the code around MimeBodyPart target, target.getFileName(), filePath, and
target.saveFile(...) to perform these checks and use the sanitized/unique
filename for saving.
- Around line 82-84: The regex compilation in GetLatestReceivedAttachment
(subjectPattern = Pattern.compile(filterRaw, ...)) can throw
PatternSyntaxException; update the code to catch PatternSyntaxException around
the Pattern.compile call (or expand the surrounding IMAP try/catch to handle
it), and when caught set Result.FAILED and populate errorMessage with the
exception message (and ensure the step returns/aborts normally instead of
crashing); reference the subjectPattern assignment and the main IMAP try/catch
so the fix locates where to add the catch and error handling.
- Around line 176-180: The current attachment collection in
GetLatestReceivedAttachment.java incorrectly relies on BodyPart.getFileName()
and a flat loop, causing inline images to be treated as attachments and nested
multipart attachments to be missed; update the logic that builds the attachments
List<MimeBodyPart> to inspect bodyPart.getDisposition() and only treat parts
with Part.ATTACHMENT (and optionally Part.INLINE if you want inlined files) as
attachments, and implement a recursive traversal that, when a part's content is
itself a Multipart (use instanceof Multipart and multipart.getBodyPart(...)),
descends into child multiparts to collect nested attachments, adding only
MimeBodyPart instances with the correct disposition to the attachments list.
In `@gmail_api/src/main/java/com/web/GetLatestReceivedAttachment.java`:
- Around line 204-206: Sanitize the attachment filename before constructing
filePath: extract the raw name from MimeBodyPart target.getFileName(), strip any
path separators and sequences like "../", remove or replace unsafe characters,
and enforce a safe fallback (e.g., unique suffix or UUID) to avoid collisions;
then build the destination using a safe join (resolve/normalize) against
downloadDir and verify the resolved path startsWith the intended downloadDir
before calling target.saveFile. Reference: MimeBodyPart target,
target.getFileName(), downloadDir, filePath, target.saveFile(...), attachments
and requestedIndex.
- Around line 176-180: The current loop that adds to attachments by checking
BodyPart.getFileName() is too naive; update the logic (the code manipulating
attachments and iterating multipart) to recursively traverse nested Multipart
parts and treat a part as an attachment only when its disposition is
Part.ATTACHMENT (and not Part.INLINE) or when it has a filename and a non-inline
disposition; use MimeBodyPart APIs to inspect getDisposition(), getFileName(),
and Content-Type/Content-Disposition headers for edge cases, descending into
nested Multipart bodies when a body part’s content is itself a Multipart to
ensure attachments in multipart/related or deeper nests are collected correctly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 481ea8c9-ac34-456a-ac09-aa5730f6db94
📒 Files selected for processing (4)
gmail_api/pom.xmlgmail_api/src/main/java/com/android/GetLatestReceivedAttachment.javagmail_api/src/main/java/com/ios/GetLatestReceivedAttachment.javagmail_api/src/main/java/com/web/GetLatestReceivedAttachment.java
| List<MimeBodyPart> attachments = new ArrayList<>(); | ||
| for (int i = 0; i < multipart.getCount(); i++) { | ||
| BodyPart bodyPart = multipart.getBodyPart(i); | ||
| if (bodyPart.getFileName() != null) { | ||
| attachments.add((MimeBodyPart) bodyPart); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "GetLatestReceivedAttachment.java" | head -20Repository: testsigmahq/testsigma-addons
Length of output: 1907
🏁 Script executed:
# Let's search for the file in the repository
fd -t f "GetLatestReceivedAttachment.java"Repository: testsigmahq/testsigma-addons
Length of output: 1907
🏁 Script executed:
# Check directory structure
git ls-files | grep -i "getlatestreceivedattachment" | head -10Repository: testsigmahq/testsigma-addons
Length of output: 1907
🏁 Script executed:
# Broader search for Gmail API files
git ls-files | grep -i "gmail" | head -20Repository: testsigmahq/testsigma-addons
Length of output: 1907
🌐 Web query:
BodyPart getFileName MIME attachment inline images Gmail
💡 Result:
In JavaMail, for a javax.mail.BodyPart (typically MimeBodyPart) from a Gmail message fetched via IMAP, bodyPart.getFileName retrieves the "filename" parameter from the Content-Disposition header, falling back to the "name" parameter from the Content-Type header if absent. It returns null if neither is present or if parsing fails due to malformed headers (e.g., line-wrapped parameters, RFC 2231 encoded filenames, or non-ASCII characters). Key distinctions: - Attachments typically have Content-Disposition: attachment; filename="example.jpg", so getFileName returns "example.jpg". - Inline images (embedded in HTML via Content-ID) often have Content-Disposition: inline without a filename parameter, so getFileName returns null. Use getContentID instead for identification. - Some messages lack filenames entirely (e.g., message/rfc822 parts), returning null. Common issues with Gmail/IMAP: - Malformed or line-wrapped headers (e.g., long filenames split across lines) cause parsing failures, returning null. Gmail's IMAP BODYSTRUCTURE may report incorrect sizes or structures. - RFC 2231 splitting (filename0, filename1) or encoded filenames may not decode properly without properties like mail.mime.decodeparameters=true or mail.mime.decodetext.strict=false. - Gmail-specific bugs (e.g., truncated Base64 body parts, incorrect BODYSTRUCTURE for attachments). To reliably get filenames: 1. Check part.getDisposition == Part.ATTACHMENT. 2. Use part.getFileName; if null, manually parse headers: String cd = part.getHeader("Content-Disposition"); // Parse for filename=... (handle quoting, encoding). 3. Fallback to Content-Type name parameter. 4. For inline images, they intentionally lack filenames; treat getFileName == null && Content-ID present as inline. Example code: if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition) || part.getFileName != null) { String filename = part.getFileName; if (filename == null) { // Fallback: parse Content-Disposition or Content-Type manually ParameterList params = new ContentDisposition(part.getDisposition).getParameterList; filename = params.get("filename"); if (filename == null) filename = MimeUtility.decodeText(params.get("name")); } // Use filename } Set Session properties for better parsing: props.setProperty("mail.mime.decodeparameters", "true"); props.setProperty("mail.mime.decodetext.strict", "false"); Sources confirm getFileName extracts from standard MIME parameters but can fail on edge cases common in Gmail emails.
Citations:
- 1: https://docs.oracle.com/javaee/7/api/javax/mail/internet/MimeBodyPart.html
- 2: https://bethecoder.com/open-source/mail/mail-1.4.1/javax/mail/internet/MimeBodyPart.java.html
- 3: https://github.com/javaee/javamail/blob/master/mail/src/main/java/javax/mail/internet/MimeBodyPart.java
- 4: https://javaee.github.io/javamail/docs/api/javax/mail/BodyPart.html
- 5: https://www.gnu.org/software/classpathx/javamail/javadoc/javax/mail/internet/MimeBodyPart.html
- 6: https://stackoverflow.com/questions/14566422/issue-retrieving-attachment-filename-with-javamail
- 7: https://stackoverflow.com/questions/69591584/part-getfilename-returning-null-for-split-filename
- 8: https://stackoverflow.com/questions/49437992/javax-mail-bodypart-getfilename-returns-null
- 9: https://stackoverflow.com/questions/60875112/javax-mail-bodypart-getfilename-returns-null-java-mail
- 10: https://javaee.github.io/javamail/Gmail
🌐 Web query:
email MIME multipart inline vs attachment Content-Disposition
💡 Result:
In email MIME multipart messages, the Content-Disposition header determines how a part is presented: "inline" for automatic display within the message body (e.g., embedded images), or "attachment" for download requiring user action. Key differences: - Content-Disposition: inline: Intended for immediate display when the message is viewed. Body parts are presented in order. Often used with Content-ID for HTML img src="cid:...". Example: Content-Disposition: inline; filename="image.jpg" (RFC 2183, sections 2.1, 3). - Content-Disposition: attachment: Requires user action to view/open/save. Suggests a filename for saving. Example: Content-Disposition: attachment; filename="document.pdf" (RFC 2183, sections 2.2, 3). In multipart structures: - multipart/related: For inline content related to the root body (e.g., HTML + images). - multipart/mixed: For attachments, or mixing inline and attachments (outer mixed containing related + attachments). If absent, defaults to inline behavior in many clients, but explicit use is recommended for clarity. This applies to any MIME body part.
Citations:
- 1: https://datatracker.ietf.org/doc/html/rfc2183
- 2: https://tools.ietf.org/html/rfc2183.html
- 3: https://en.wikipedia.org/wiki/MIME
- 4: https://stackoverflow.com/questions/30351465/html-email-with-inline-attachments-and-non-inline-attachments
- 5: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Disposition
🌐 Web query:
Gmail MIME structure nested multiparts attachments
💡 Result:
Gmail uses standard MIME structures for emails with nested multiparts and attachments, following RFC 2045-2049 and RFC 5322. Emails are parsed into a hierarchical structure accessible via the Gmail API's payload field or as raw RFC 2822/base64url encoded data viewable via "Show Original". Key MIME types in Gmail: - text/plain: Plain text body. - text/html: HTML body. - multipart/alternative: Contains alternative versions (plain text and HTML). - multipart/related: Groups HTML body with embedded images (often nested inside other multiparts). - multipart/mixed: Top-level container for body + attachments (often wraps multipart/alternative or multipart/related + attachment parts). Typical nested structure for HTML email with embedded image and attachment: multipart/mixed (top-level) ├── multipart/related │ └── multipart/alternative │ ├── text/plain │ └── text/html (references embedded images via Content-ID) │ └── image/* (embedded images, Content-Disposition: inline) └── application/octet-stream (attachment, Content-Disposition: attachment; filename=*) Attachments appear as leaf parts with Content-Disposition: attachment and filename. They can be nested at any level (legal per MIME specs). In Gmail API format=full, this appears as nested MessagePart objects with mimeType, parts[], body.data (base64url-encoded), or body.attachmentId. For large attachments, use users.messages.attachments.get. To view raw MIME: In Gmail web, open email > ⋮ > Show original (RFC 2822 format with boundaries). No Gmail-specific deviations from standard MIME; structures match common clients (Outlook, Apple Mail). Parse recursively: traverse parts, decode body.data (base64url), check Content-Disposition for attachments.
Citations:
- 1: https://www.ehfeng.com/gmail-api-mime-types/
- 2: https://stackoverflow.com/questions/37445865/where-to-find-body-of-email-depending-of-mimetype/37462718
- 3: https://developers.google.com/gmail/api/reference/rest/v1/users.messages
- 4: https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages.attachments
- 5: https://developers.google.com/workspace/gmail/api/guides/sending
- 6: https://www.mimehotel.com.br
- 7: https://www.engmail.com
- 8: https://stackoverflow.com/questions/24428246/retrieve-email-message-body-in-html-using-gmail-api
- 9: https://sigparser.com/developers/email-parsing/parse-raw-email/
- 10: https://wingu.github.io/mime_structures/
- 11: https://mdtools.one/articles/email-mime-structure-multipart-explained
Check for Part.ATTACHMENT disposition instead of just getFileName() != null, and recursively handle nested multiparts.
BodyPart.getFileName() != null is insufficient for reliable attachment detection and misses nested multipart structures. The proper check requires part.getDisposition() == Part.ATTACHMENT. Additionally, your flat loop only examines top-level body parts; it skips attachments nested within multipart/related or multipart/alternative structures, which are common in Gmail layouts. This causes attachment-index to be unstable across different MIME structures. Consider recursively traversing all multipart boundaries and verifying Content-Disposition: attachment.
🤖 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/GetLatestReceivedAttachment.java` around
lines 176 - 180, The current attachment detection logic using
bodyPart.getFileName() is unreliable and non-recursive; update the traversal in
GetLatestReceivedAttachment (the loop over multipart and the attachments
List<MimeBodyPart>) to instead check part.getDisposition() == Part.ATTACHMENT
(or Part.INLINE if you want inline attachments) when classifying attachments and
implement a recursive helper that walks any Multipart/BodyPart boundaries
(handling nested Multipart instances such as multipart/related and
multipart/alternative) to collect all MimeBodyPart attachments into the
attachments list so attachment-index is stable across MIME structures.
| MimeBodyPart target = attachments.get(requestedIndex - 1); | ||
| filePath = downloadDir + target.getFileName(); | ||
| target.saveFile(new File(filePath)); |
There was a problem hiding this comment.
Sanitize the attachment name before saving it locally.
target.getFileName() is sender-controlled. Concatenating it into downloadDir allows ../, absolute paths, or duplicate names to escape the temp folder or overwrite existing files on the runner.
🤖 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/GetLatestReceivedAttachment.java` around
lines 204 - 206, Sanitize the attachment filename returned by
target.getFileName() before building filePath: strip any path separators and
parent traversal sequences, reject or normalize absolute paths, allow only a
safe whitelist of characters (or replace others), and ensure the resolved path
is inside downloadDir (use path normalization and a check that
Paths.get(downloadDir).resolve(safeName).normalize().startsWith(downloadDir)).
Also prevent overwrites by generating a unique filename (append timestamp or
UUID) if the sanitized name already exists, then pass that safe path to
target.saveFile; apply these changes around the MimeBodyPart target / filePath
creation and saveFile call.
| if ("regex".equalsIgnoreCase(matchType) && !filterRaw.isEmpty()) { | ||
| subjectPattern = Pattern.compile(filterRaw, Pattern.CASE_INSENSITIVE); | ||
| } |
There was a problem hiding this comment.
❓ Verification inconclusive
🏁 Script executed:
git ls-files | grep -E "GetLatestReceivedAttachment" | head -20Repository: testsigmahq/testsigma-addons
Length of output: 1907
🏁 Script executed:
fd "GetLatestReceivedAttachment" --type fRepository: testsigmahq/testsigma-addons
Length of output: 1907
Return a normal step failure when the regex is invalid.
Pattern.compile(...) may throw PatternSyntaxException if filterRaw is malformed. Verify whether this exception is caught by the main IMAP try/catch block or if it aborts the step without setting Result.FAILED and errorMessage.
🤖 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/GetLatestReceivedAttachment.java` around
lines 82 - 84, The regex compilation in GetLatestReceivedAttachment
(subjectPattern = Pattern.compile(filterRaw, ...)) can throw
PatternSyntaxException; update the code to catch PatternSyntaxException around
the Pattern.compile call (or expand the surrounding IMAP try/catch to handle
it), and when caught set Result.FAILED and populate errorMessage with the
exception message (and ensure the step returns/aborts normally instead of
crashing); reference the subjectPattern assignment and the main IMAP try/catch
so the fix locates where to add the catch and error handling.
| List<MimeBodyPart> attachments = new ArrayList<>(); | ||
| for (int i = 0; i < multipart.getCount(); i++) { | ||
| BodyPart bodyPart = multipart.getBodyPart(i); | ||
| if (bodyPart.getFileName() != null) { | ||
| attachments.add((MimeBodyPart) bodyPart); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "GetLatestReceivedAttachment.java" | head -20Repository: testsigmahq/testsigma-addons
Length of output: 1907
🌐 Web query:
JavaMail BodyPart.getFileName() inline images attachments MIME
💡 Result:
In JavaMail, BodyPart.getFileName (implemented by MimeBodyPart) retrieves the filename associated with a MIME body part from email headers. It first checks the "filename" parameter in the Content-Disposition header. If not found, it falls back to the "name" parameter in the Content-Type header. If neither is present, it returns null. This behavior applies uniformly to both regular attachments (Content-Disposition: attachment) and inline images/embedded content (Content-Disposition: inline), as both are MIME body parts. For inline images: - They typically use Content-Disposition: inline with a Content-ID header for HTML referencing (e.g., ). - The filename may or may not be set; if present (e.g., via setFileName), getFileName returns it. - Examples show inline images often have filenames set during construction (e.g., imagePart.setFileName("image.jpg")), which getFileName retrieves. - Inline parts without filename parameters return null, just like attachments without filenames. To distinguish inline from attachments, use BodyPart.getDisposition: - Returns Part.ATTACHMENT, Part.INLINE, or null. - Example: if (Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition)) for attachments. Code example to process parts: Multipart multipart = (Multipart) message.getContent; for (int i = 0; i < multipart.getCount; i++) { BodyPart bodyPart = multipart.getBodyPart(i); String disposition = bodyPart.getDisposition; String fileName = bodyPart.getFileName; // Works for both inline and attachment if (Part.ATTACHMENT.equalsIgnoreCase(disposition)) { // Handle attachment } else if (Part.INLINE.equalsIgnoreCase(disposition)) { // Handle inline (e.g., image) } } Note: Filenames may be RFC 2047/2231 encoded; JavaMail handles decoding (controlled by properties like mail.mime.decodefilename). For split/encoded params, ensure proper JavaMail config (e.g., mail.mime.decodetext.strict=false).
Citations:
- 1: https://www.cs.auckland.ac.nz/references/java/javamail/javax/mail/internet/MimeBodyPart.html
- 2: https://docs.oracle.com/javaee/7/api/javax/mail/internet/MimeBodyPart.html
- 3: https://javaee.github.io/javamail/docs/api/javax/mail/Part.html
- 4: https://javaee.github.io/javamail/docs/api/javax/mail/internet/MimeBodyPart.html
- 5: https://github.com/javaee/javamail/blob/master/mail/src/main/java/javax/mail/internet/MimeBodyPart.java
- 6: https://bethecoder.com/open-source/mail/mail-1.4.1/javax/mail/internet/MimeBodyPart.java.html
- 7: https://javadoc.io/static/com.sun.mail/javax.mail/1.6.2/javax/mail/Part.html
- 8: https://www.baeldung.com/java-mail-inline-images
- 9: https://stackoverflow.com/questions/2996514/inline-images-in-email-using-javamail
- 10: https://stackoverflow.com/questions/30722543/how-can-i-get-inline-images-from-an-email
- 11: https://stackoverflow.com/questions/26173888/java-mail-polling-read-inline-or-embedded-images-smileys-from-the-mail
- 12: https://forums.oracle.com/ords/apexds/post/how-to-save-inline-images-and-attachments-separatley-6483
- 13: https://stackoverflow.com/questions/52373411/how-to-receive-and-distinguish-between-regular-attachments-and-inline-attachment
- 14: https://stackoverflow.com/questions/69591584/part-getfilename-returning-null-for-split-filename
- 15: https://stackoverflow.com/questions/14566422/issue-retrieving-attachment-filename-with-javamail
🌐 Web query:
Gmail MIME structure nested multiparts attachment handling
💡 Result:
Gmail handles MIME structures for nested multiparts and attachments according to standard RFC 2045-2049 and RFC 2822 specifications, preserving the full hierarchical structure without modification. Emails received or sent via Gmail maintain their original MIME tree, including nested multipart/mixed, multipart/alternative, multipart/related containers with attachments at any level. Key aspects: - MIME Structure: Top-level often multipart/mixed for body + attachments, with nested multipart/alternative (text/plain + text/html), multipart/related (embedded images), and leaf parts like application/octet-stream for attachments. Attachments can be nested deeply, including message/rfc822 (forwarded emails). - Viewing Raw Structure: Use "Show original" in Gmail web interface to see the complete RFC 2822 raw MIME source with boundaries, headers, and nested parts. - Gmail API Handling: messages.get with format=full returns parsed payload as nested MessagePart objects. Multipart parts have recursive 'parts' array; attachments identified by filename or Content-Disposition: attachment, with body.data (base64url) or body.attachmentId (fetch separately). Recursive traversal required to access all parts. - Sending: messages.send accepts raw RFC 2822 base64url-encoded MIME, fully supporting nested structures and attachments. - Parsing Examples: Common hierarchies include multipart/mixed > multipart/alternative > text/* for body with attachments; Gmail displays attachments correctly regardless of nesting depth. No Gmail-specific deviations from MIME standards; it processes and displays nested attachments faithfully, as confirmed by API docs and raw message inspection.
Citations:
- 1: https://docs.mspintegrations.com/documentation/troubleshooting/email-structures/understanding-multipart-emails
- 2: https://stackoverflow.com/questions/33689000/can-attachments-be-in-a-nested-multipart-in-mime
- 3: https://developers.google.com/gmail/api/reference/rest/v1/users.messages
- 4: https://stackoverflow.com/questions/37445865/where-to-find-body-of-email-depending-of-mimetype/37462718
- 5: https://stackoverflow.com/questions/24428246/retrieve-email-message-body-in-html-using-gmail-api
- 6: https://ehfeng.com/gmail-api-mime-types/
- 7: https://ladedu.com/how-to-view-an-email-source-in-gmail-desktop-and-mobile/
- 8: https://developers.google.com/workspace/gmail/api/guides/sending
- 9: https://developers.google.com/workspace/gmail/api/guides/uploads
- 10: https://www.mimestream.com
- 11: https://www.multipartsdenhaag.nl
- 12: https://linkedin.com/company/le-mime-helados
- 13: https://stackoverflow.com/questions/27956885/gmail-api-read-decode-message-c
- 14: https://stackoverflow.com/questions/37787767/fetch-imap-body-message-by-telnet/37794152
- 15: https://stackoverflow.com/questions/36965101/problems-parsing-eml-attachments-in-java-mail-using-imaps-google
- 16: https://developers.google.com/gmail/api/reference/rest/v1/users.messages.attachments
- 17: https://www.ehfeng.com/gmail-api-mime-types/
🌐 Web query:
Content-Disposition inline vs attachment JavaMail BodyPart
💡 Result:
In JavaMail, the Content-Disposition header for a BodyPart (typically MimeBodyPart) determines how email clients handle the part: "inline" for display within the message body or "attachment" for download as a file. Key differences: - INLINE: The part is intended to be rendered inline, e.g., images embedded in HTML via cid: references. Use MimeBodyPart.setDisposition(MimeBodyPart.INLINE) or Part.INLINE. - ATTACHMENT: The part appears as a downloadable attachment. Use MimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT) or Part.ATTACHMENT. Often set automatically by attachFile without explicit INLINE. For inline images: 1. Create MimeBodyPart for image. 2. Set DataHandler (e.g., attachFile). 3. imagePart.setContentID(""); 4. imagePart.setDisposition(MimeBodyPart.INLINE); 5. Add to MimeMultipart("related"). 6. Reference in HTML body as . Example: MimeBodyPart imagePart = new MimeBodyPart; imagePart.attachFile("image.jpg"); imagePart.setContentID(""); imagePart.setDisposition(MimeBodyPart.INLINE); Body text/HTML parts default to inline unless explicitly set to ATTACHMENT. Setting INLINE on main body is acceptable but rarely changes behavior. This follows RFC 2183, as implemented in javax.mail.Part interface.
Citations:
- 1: https://stackoverflow.com/questions/48975613/content-disposition-for-email-body
- 2: https://stackoverflow.com/questions/2996514/inline-images-in-email-using-javamail
- 3: https://docs.oracle.com/javaee/5/api/javax/mail/Part.html
- 4: https://www.baeldung.com/java-mail-inline-images
- 5: https://docs.oracle.com/javaee/7/api/javax/mail/internet/MimeBodyPart.html
Use getDisposition() to distinguish attachments from inline parts, and recursively traverse nested multiparts.
BodyPart.getFileName() != null alone is insufficient because:
- It returns non-null for both
Content-Disposition: attachmentandContent-Disposition: inlineparts (e.g., inline images with filenames) - The flat top-level loop skips attachments nested in child multiparts (multipart/alternative, multipart/related, etc.), which are common in Gmail MIME structures
This causes both false positives (inline images counted as attachments) and false negatives (missed nested attachments), making attachment-index unstable.
Use bodyPart.getDisposition() to check for Part.ATTACHMENT and implement recursive traversal for nested multiparts.
🤖 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/GetLatestReceivedAttachment.java` around
lines 176 - 180, The current attachment collection in
GetLatestReceivedAttachment.java incorrectly relies on BodyPart.getFileName()
and a flat loop, causing inline images to be treated as attachments and nested
multipart attachments to be missed; update the logic that builds the attachments
List<MimeBodyPart> to inspect bodyPart.getDisposition() and only treat parts
with Part.ATTACHMENT (and optionally Part.INLINE if you want inlined files) as
attachments, and implement a recursive traversal that, when a part's content is
itself a Multipart (use instanceof Multipart and multipart.getBodyPart(...)),
descends into child multiparts to collect nested attachments, adding only
MimeBodyPart instances with the correct disposition to the attachments list.
| MimeBodyPart target = attachments.get(requestedIndex - 1); | ||
| filePath = downloadDir + target.getFileName(); | ||
| target.saveFile(new File(filePath)); |
There was a problem hiding this comment.
Sanitize the attachment name before saving it locally.
target.getFileName() is sender-controlled. Concatenating it into downloadDir allows ../, absolute paths, or duplicate names to escape the temp folder or overwrite existing files on the runner.
🤖 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/GetLatestReceivedAttachment.java` around
lines 204 - 206, The code directly uses target.getFileName() to build filePath
and call target.saveFile(...), which allows path traversal or overwrites;
sanitize and validate the attachment name before saving by extracting only the
base name (strip any path separators, remove "..", and reject absolute paths),
normalize the result (e.g., remove unsafe characters), then construct the
destination path using a safe join (use downloadDir + File.separator +
sanitizedName or a safe path join API) and ensure uniqueness (append a counter,
timestamp, or use a temp file API) if the file exists; update the code around
MimeBodyPart target, target.getFileName(), filePath, and target.saveFile(...) to
perform these checks and use the sanitized/unique filename for saving.
| List<MimeBodyPart> attachments = new ArrayList<>(); | ||
| for (int i = 0; i < multipart.getCount(); i++) { | ||
| BodyPart bodyPart = multipart.getBodyPart(i); | ||
| if (bodyPart.getFileName() != null) { | ||
| attachments.add((MimeBodyPart) bodyPart); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "GetLatestReceivedAttachment.java" | head -5Repository: testsigmahq/testsigma-addons
Length of output: 1907
🌐 Web query:
javax.mail BodyPart getFileName() inline images embedded resources MIME
💡 Result:
In JavaMail, BodyPart.getFileName (inherited by MimeBodyPart) retrieves the filename for embedded resources like inline images in MIME emails as follows: - It first checks the "filename" parameter in the Content-Disposition header. - If absent, it falls back to the "name" parameter in the Content-Type header. - Returns null if neither is present. For inline images (embedded via multipart/related, referenced by Content-ID in HTML img src="cid:..."), the Content-Disposition is typically "inline" (or absent), but a filename parameter can still be specified. Examples from emails show inline images with headers like: Content-Type: image/gif; name="02629422.gif" Content-Disposition: inline; filename="02629422.gif" Thus, getFileName will return "02629422.gif" if present. If no filename/name parameter exists (possible per MIME spec), it returns null. To distinguish inline from attachments, check getDisposition == Part.INLINE, but use getFileName regardless for the suggested filename when saving/extracting. To extract inline images: - Iterate Multipart parts. - For image/* MIME types with Content-ID header (getHeader("Content-ID") != null), it's likely embedded. - Use part.getInputStream or part.saveFile with part.getFileName or a generated name if null. Code example: Multipart mp = (Multipart) message.getContent; for (int i = 0; i < mp.getCount; i++) { BodyPart part = mp.getBodyPart(i); if (part.isMimeType("image/*") && part.getHeader("Content-ID") != null) { String fname = part.getFileName; // may be null if (fname == null) fname = "embedded_image_" + i + ".png"; part.saveFile(new File("images/" + fname)); } } This handles both cases where filename is provided or absent.
Citations:
- 1: https://javaee.github.io/javamail/docs/api/javax/mail/BodyPart.html
- 2: https://www.baeldung.com/java-mail-inline-images
- 3: https://www.cs.auckland.ac.nz/references/java/javamail/javax/mail/internet/MimeBodyPart.html
- 4: https://jakarta.ee/specifications/platform/8/apidocs/javax/mail/internet/mimebodypart
- 5: https://stackoverflow.com/questions/43834042/mimepartbody-getdisposition-returning-null-for-inline-parts
- 6: http://docs.oracle.com/javaee/7/api/javax/mail/Part.html
- 7: https://docs.oracle.com/javaee/7/api/javax/mail/internet/MimeBodyPart.html
🌐 Web query:
javax.mail nested multipart handling attachment detection
💡 Result:
In javax.mail (now Jakarta Mail), handling nested Multipart structures and detecting attachments requires a recursive traversal of Parts, as emails can have deeply nested MIME structures like multipart/mixed containing multipart/alternative, or message/rfc822 parts embedding entire messages. Key approach: - Treat everything as a Part (Message and BodyPart implement Part). - Recursively process content: - If Multipart, iterate BodyParts and recurse. - If Message (e.g., contentType "message/rfc822" for EML attachments), cast to Message and process as top-level. - For IMAP-specific nested messages, check instanceof IMAPNestedMessage and recurse. - Attachments typically have disposition "attachment" or non-text contentType with filename; use part.getDisposition, part.isMimeType("application/"), part.getFileName != null. Example recursive method to find attachments: private void processPart(Part part) throws MessagingException, IOException { if (part.isMimeType("multipart/")) { Multipart mp = (Multipart) part.getContent; for (int i = 0; i < mp.getCount; i++) { processPart(mp.getBodyPart(i)); } } else if (part.getContent instanceof Message) { // Nested message (e.g., EML attachment) processPart((Part) part.getContent); } else if (part.getDisposition != null && (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition) || (part.getFileName != null && !part.isMimeType("text/*")))) { // Save attachment String fileName = part.getFileName; if (fileName != null) { fileName = MimeUtility.decodeText(fileName); try (InputStream is = part.getInputStream; FileOutputStream fos = new FileOutputStream(fileName)) { is.transferTo(fos); } } } } Call with: processPart(message); This handles arbitrary nesting per MIME spec and JavaMail patterns. For IMAP, add check: if (part.getContent instanceof IMAPNestedMessage) { processMessage((IMAPNestedMessage)part.getContent); } Official docs confirm Multipart holds BodyParts; recurse via getContent instanceof Multipart.
Citations:
- 1: https://stackoverflow.com/questions/51405004/javax-mail-get-nested-attachments-in-eml-attachment
- 2: https://stackoverflow.com/questions/51405004/javax-mail-get-nested-attachments-in-eml-attachment/51444502#51444502
- 3: https://stackoverflow.com/questions/13331989/how-to-handle-multipart-alternative-mail-with-javamail
- 4: https://javaee.github.io/javamail/docs/api/javax/mail/Multipart.html
- 5: https://docs.oracle.com/javaee/7/api/javax/mail/Multipart.html
- 6: https://docs.oracle.com/javaee/7/api/javax/mail/internet/MimeMultipart.html
- 7: https://stackoverflow.com/questions/38874376/javamail-also-extract-attachments-of-encapsulated-message-content-type-message
- 8: https://mikedesjardins.net/2008/03/13/using-javamail-to-read-and-extract/
- 9: https://jakartaee.github.io/mail-api/
- 10: https://jakartaee.github.io/mail-api/docs/api/jakarta.mail/jakarta/mail/package-summary.html
🌐 Web query:
Gmail MIME structure nested multiparts how to properly detect attachments
💡 Result:
To properly detect attachments in Gmail's MIME structure, which supports nested multiparts per RFC 2045/2046/5322, traverse the entire MIME tree recursively. Attachments are identified by: 1. Content-Disposition: attachment (primary indicator, RFC 2183) 2. Non-text filename present 3. Non-inline image/media parts (e.g., not referenced by CID in HTML) Gmail follows standard MIME: messages are multipart/mixed (attachments + body), with body often multipart/alternative (text/plain + text/html), and html may contain multipart/related (inline images). ## Via Gmail API (Recommended for Gmail) Fetch message with format=full (parses MIME tree) or raw (full RFC2822). - Use payload.parts recursively. - Check part.filename (non-empty indicates attachment). - Or headers for Content-Disposition: attachment. - Large attachments have attachmentId; fetch via attachments.get. Example (JavaScript): function findAttachments(payload) { let attachments = []; function recurse(part) { if (part.filename) attachments.push(part); if (part.parts) part.parts.forEach(recurse); // Also check headers: part.headers.find(h => h.name === 'Content-Disposition')?.value.includes('attachment') } recurse(payload); return attachments; } Official docs confirm filename present only for attachments. For raw, parse with MIME library. ## Via Raw MIME (Python email stdlib) Use msg.walk - skips multipart containers, visits leaf parts recursively. import email from email import policy msg = email.message_from_bytes(raw_data, policy=policy.default) attachments = [] for part in msg.walk: if part.get_content_maintype == 'multipart': continue # Skip containers disp = str(part.get('Content-Disposition', '')).lower filename = part.get_filename if filename or 'attachment' in disp: # Save attachment data = part.get_payload(decode=True) attachments.append((filename or 'unknown', data)) This handles arbitrary nesting (e.g., S/MIME signed messages, message/rfc822). Gmail "Show Original" reveals full structure - attachments anywhere in tree with proper headers. Inline images (multipart/related) typically have Content-Disposition: inline or Content-ID referenced in HTML - exclude if 'inline' in disp and no filename. Sources confirm recursive walk necessary; top-level only misses nested attachments.
Citations:
- 1: https://stackoverflow.com/questions/33689000/can-attachments-be-in-a-nested-multipart-in-mime
- 2: https://docs.mspintegrations.com/documentation/troubleshooting/email-structures/understanding-multipart-emails
- 3: https://stackoverflow.com/questions/38805067/grabbing-message-body-with-gmail-api
- 4: https://stackoverflow.com/questions/37445865/where-to-find-body-of-email-depending-of-mimetype
- 5: https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages
- 6: https://developers.google.com/gmail/api/reference/rest/v1/users.messages
- 7: https://docs.python.org/3/library/email.parser.html
- 8: https://gist.github.com/urschrei/5258588
- 9: https://rfc-editor.org/rfc/rfc2183.txt
Fix attachment detection to distinguish inline images and handle nested multiparts.
BodyPart.getFileName() != null alone is insufficient—inline images with filename parameters in Content-Type or Content-Disposition headers will be incorrectly counted as attachments. Use getDisposition() to filter for Part.ATTACHMENT and exclude Part.INLINE.
Additionally, a flat loop only processes top-level multipart parts. Gmail's standard structure (multipart/mixed → multipart/alternative → multipart/related for inline images) means attachments in nested structures are skipped, and inline images in multipart/related sections get miscounted. Implement recursive traversal to handle arbitrary MIME nesting and properly identify attachments.
🤖 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/GetLatestReceivedAttachment.java` around
lines 176 - 180, The current loop that adds to attachments by checking
BodyPart.getFileName() is too naive; update the logic (the code manipulating
attachments and iterating multipart) to recursively traverse nested Multipart
parts and treat a part as an attachment only when its disposition is
Part.ATTACHMENT (and not Part.INLINE) or when it has a filename and a non-inline
disposition; use MimeBodyPart APIs to inspect getDisposition(), getFileName(),
and Content-Type/Content-Disposition headers for edge cases, descending into
nested Multipart bodies when a body part’s content is itself a Multipart to
ensure attachments in multipart/related or deeper nests are collected correctly.
| MimeBodyPart target = attachments.get(requestedIndex - 1); | ||
| filePath = downloadDir + target.getFileName(); | ||
| target.saveFile(new File(filePath)); |
There was a problem hiding this comment.
Sanitize the attachment name before saving it locally.
target.getFileName() is sender-controlled. Concatenating it into downloadDir allows ../, absolute paths, or duplicate names to escape the temp folder or overwrite existing files on the runner.
🛡️ Proposed fix
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
...
- String downloadDir = System.getProperty("java.io.tmpdir") + "/testsigma_attachments/";
- new File(downloadDir).mkdirs();
+ Path downloadDir = Files.createDirectories(
+ Paths.get(System.getProperty("java.io.tmpdir"), "testsigma_attachments"));
...
- filePath = downloadDir + target.getFileName();
- target.saveFile(new File(filePath));
+ String safeName = Paths.get(target.getFileName()).getFileName().toString();
+ Path targetPath = Files.createTempFile(downloadDir, "gmail_", "_" + safeName);
+ target.saveFile(targetPath.toFile());
+ filePath = targetPath.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/GetLatestReceivedAttachment.java` around
lines 204 - 206, Sanitize the attachment filename before constructing filePath:
extract the raw name from MimeBodyPart target.getFileName(), strip any path
separators and sequences like "../", remove or replace unsafe characters, and
enforce a safe fallback (e.g., unique suffix or UUID) to avoid collisions; then
build the destination using a safe join (resolve/normalize) against downloadDir
and verify the resolved path startsWith the intended downloadDir before calling
target.saveFile. Reference: MimeBodyPart target, target.getFileName(),
downloadDir, filePath, target.saveFile(...), attachments and requestedIndex.
Publish this addon as PUBLIC
Addon Name: GmailAPI
Jarvis Link: https://jarvis.testsigma.com/ui/tenants/2817/addons
Jira : https://testsigma.atlassian.net/browse/CUS-11210
Added class to get the attachement file using subject filter
Summary by CodeRabbit
New Features
Chores