← MCP server and skill grades

mcp-atlassian

sooperset/mcp-atlassian

D
Type
MCP server
Scanned
Oct 29, 2026
Version
0a5d2427144d
Files read
418

This MCP server connects AI assistants to Atlassian Jira and Confluence so they can search, read, create, and update issues and pages. The biggest risk is that untrusted content in a ticket or page could trick the AI into bypassing project restrictions, altering your Atlassian data, or uploading sensitive files (like .env secrets) from your working directory.

What it can reach

Network
Connects to configured Jira and Confluence servers, Atlassian OAuth services, and optional proxies, and can start a local server that listens on all network interfaces (0.0.0.0:8000) by default.
Files
Reads and writes files in the current working directory for attachments and page content, reads .env and certificate files, and stores OAuth tokens in ~/.mcp-atlassian/.
Secrets
Reads Jira and Confluence API tokens, Personal Access Tokens, and OAuth credentials from environment variables, .env files, incoming HTTP headers, and the system keyring.
Browser
Opens your default web browser during the interactive OAuth login setup.

Findings

4 high1 medium2 low
  1. HighBypass of JIRA_PROJECTS_FILTER allowlist via ORDER BY quote smuggling in JQL

    src/mcp_atlassian/jira/search.py:62

    To enforce the operator's JIRA_PROJECTS_FILTER allowlist, _and_projects_filter searches the caller's JQL for a match to \s+(ORDER\s+BY\s+.*)$ and inserts the AND (project = ...) restriction immediately before it. Because the regular expression does not skip quoted string literals, a prompt-injected query such as 'summary ~ " order by " ) OR project = SECRET' splits inside the open double-quoted string. The resulting JQL becomes '(summary ~ ") AND (project = ALLOWED) order by " ) OR project = SECRET', trapping the project filter inside a string literal and allowing the caller to read issues from any project.

    Fix: Parse the JQL outside of single- and double-quoted string literals before locating a trailing ORDER BY clause, or reject JQL queries containing unbalanced quotes in the portion preceding ORDER BY.

  2. HighBypass of CONFLUENCE_SPACES_FILTER allowlist via ORDER BY quote smuggling in CQL

    src/mcp_atlassian/confluence/search.py:40

    Like the Jira search filter, _and_spaces_filter uses re.search(r"\s+(ORDER\s+BY\s+.*)$", cql, re.IGNORECASE) without accounting for quoted string literals. A caller-supplied CQL query such as 'text ~ " order by " ) OR space = SECRET' causes the space filter to be spliced inside the quoted string literal ('(text ~ ") AND (space = ALLOWED) order by " ) OR space = SECRET'), neutralizing CONFLUENCE_SPACES_FILTER and allowing access to restricted spaces. Additionally, direct page retrieval tools such as confluence_get_page do not check CONFLUENCE_SPACES_FILTER.

    Fix: Ensure ORDER BY detection ignores matches inside quoted strings so that the appended AND (space = ...) clause cannot be enclosed in a string literal, and enforce CONFLUENCE_SPACES_FILTER on direct page lookups.

  3. HighLocal workspace file and .env secret exfiltration via content_file and attachment upload tools

    src/mcp_atlassian/servers/confluence.py:175

    The confluence_create_page and confluence_update_page tools accept a content_file parameter (and confluence_upload_attachment / jira_update_issue accept file_path / attachments parameters) that reads any file inside the current working directory (validated via validate_safe_path). Because the server loads .env from the current working directory by default and typically runs inside a project workspace, a prompt injection attack can instruct the agent to pass content_file=".env" (with include_content=True) or upload .env as an attachment, exposing local credentials and workspace files to the attacker or publishing them to Confluence/Jira.

    Fix: Block access to dotfiles and sensitive configuration files (such as .env, .git/*, and *.pem/*.key), or require an explicit, dedicated upload directory configured by the operator rather than defaulting to the entire current working directory.

  4. HighURL path injection in Confluence and Jira REST API endpoints via unencoded ID arguments

    src/mcp_atlassian/confluence/permissions.py:74

    Multiple methods interpolate tool arguments (such as content_id, attachment_id, space_id, issue_key, and form_id) directly into REST API URL paths without URL-encoding or strict format validation. In check_content_permissions—which is exposed as a read-only tool and permitted even when READ_ONLY_MODE=true—content_id is interpolated into '/rest/api/content/{content_id}/permission/check' and sent via HTTP POST. Supplying a value containing path traversal or a query delimiter (e.g., '12345/copy?' or '../...') turns the trailing path into a query string and sends an authenticated POST request to an arbitrary Confluence endpoint. Similar unencoded path interpolations exist in confluence/attachments.py (delete_attachment) and jira/forms_api.py (_make_forms_api_request).

    Fix: URL-encode all path parameters using urllib.parse.quote(value, safe="") (as done in get_page_template) and validate resource identifiers against strict patterns before constructing request URLs.

  5. MediumHTTP and SSE server transports bind to 0.0.0.0 by default

    src/mcp_atlassian/__init__.py:151

    When running with --transport sse or --transport streamable-http, the server defaults to binding on 0.0.0.0 (all network interfaces) rather than 127.0.0.1. If a user starts the HTTP server locally without specifying --host 127.0.0.1 (especially with ALLOW_GLOBAL_CRED_FALLBACK=true), other machines on the local network can reach the MCP endpoint.

    Fix: Change the default host to 127.0.0.1 and require users to explicitly opt in to 0.0.0.0 for container or remote deployments.

  6. LowReflected XSS and plaintext secret logging in standalone OAuth helper script

    scripts/oauth_authorize.py:95

    Unlike src/mcp_atlassian/utils/oauth_setup.py (which escapes HTML), the standalone developer script scripts/oauth_authorize.py interpolates the callback_error query parameter directly into the HTML response without escaping, and logs ATLASSIAN_OAUTH_CLIENT_SECRET in plaintext. Because this script is not run by the installed MCP server, its impact is limited to manual script execution.

    Fix: Escape the message with html.escape() before rendering it in _send_response and redact client_secret in log output.

  7. LowUnpinned remote install script in devcontainer Dockerfile

    .devcontainer/Dockerfile:2

    The development container Dockerfile downloads and executes the uv installation script directly from astral.sh over curl piped to sh without pinning a version or verifying a checksum. While this file is only used by developers opening the repository in a devcontainer and is never run by users who install the package, compromising the upstream script or domain would execute arbitrary code during devcontainer builds.

    Fix: Pin a specific version of uv or copy the binary from a pinned official container image (such as ghcr.io/astral-sh/uv) with a sha256 digest.