CVE-2026-77244: Mcp atlassian AtlassianOpaqueTokenVerifier Accepts Any Non-empty Token Vulnerability

CRITICAL
Advisory Identifier: GHSA-wrhw-j3f9-8vc6
CVSS 10.0
CWE-287 CWE-303 CWE-862
Published
Advisory Summary

CVE-2026-77244 is a CRITICAL (CVSS 10.0) atlassianopaquetokenverifier accepts any non-empty token vulnerability (CWE-287: Improper Authentication) affecting Mcp atlassian libraries in the PyPI ecosystem. The issue involves atlassianopaquetokenverifier accepts any non-empty token. Remediation is available by upgrading affected packages to version 0.22.0.

CVE-2026-77244 at a Glance

CVE Identifier CVE-2026-77244
GHSA Identifier GHSA-wrhw-j3f9-8vc6
Severity CRITICAL
CVSS v3.1 Base Score 10.0
CWE Classification CWE-287: Improper Authentication
Affected Product Mcp atlassian
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 10.0

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality High (C:H)
Integrity High (I:H)
Availability None (A:N)

How to Fix CVE-2026-77244

To remediate this vulnerability, upgrade all affected Mcp atlassian dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

mcp-atlassian fixed: 0.22.0
pip install mcp-atlassian>=0.22.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show mcp-atlassian

How to Check if You Are Affected

To determine whether your Python environment is affected by CVE-2026-77244, inspect your installed packages using pip:

pip show mcp-atlassian

Analyze the terminal output to locate any version of Mcp atlassian in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.22.0), your application is affected.

Note: The pip show mcp-atlassian command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-287: Improper Authentication, where when an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct. In Mcp atlassian, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

**Description** mcp-atlassian deploys in two common patterns: Pattern A (single-user, server-side credentials): operator sets JIRA_USERNAME + JIRA_API_TOKEN (or CONFLUENCE_USERNAME + CONFLUENCE_API_TOKEN) in environment variables. Server uses these to call Jira/Confluence. This is the documented quickstart pattern. Pattern B (multi-user, OAuth or per-request PAT): operator sets up OAuth proxy or accepts per-user tokens via Authorization or service headers. The authentication mechanism in HTTP transport has two issues that combine to permit unauthenticated access to Pattern A deployments: 1. AtlassianOpaqueTokenVerifier.verify_token() at `src/mcp_atlassian/utils/token_verifier.py` accepts any non-empty string as a valid token: async def verify_token(self, token: str) -> AccessToken | None: if not token: return None scopes = self.required_scopes or [] return AccessToken( token=token, client_id="atlassian", scopes=scopes, expires_at=int(time.time()) + 86400 * 30, ) The docstring documents this: "we accept non-empty tokens and attach the required scopes." 2. The default deployment does NOT enable the OAuth proxy auth provider (OAUTH_PROXY_ENABLE_ENV defaults to false; main.py:726). When `_build_auth_provider()` returns None, FastMCP HTTP transport accepts requests with no authentication challenge. 3. `UserTokenMiddleware._parse_auth_header` (main.py:601-664) extracts tokens from Authorization headers and stores them in scope state. If NO Authorization header is present (main.py:584-595), the middleware does not reject the request — it simply does not populate `user_atlassian_token`. 4. JiraFetcher / ConfluenceFetcher fall back to `JiraConfig.from_env()` when no user-supplied token is in scope state. `from_env()` reads `JIRA_API_TOKEN` and `JIRA_USERNAME` from environment and uses them as the API credentials. Composition: an attacker who reaches the HTTP transport (e.g., server exposed on a port reachable from attacker — direct bind, Docker port mapping, reverse proxy without auth, container in a network the attacker joined) can: - Send no Authorization header at all, OR - Send any garbage Bearer token Either request reaches tool handlers. The tool handlers, finding no user-supplied token, use the server's env-var credentials to call Jira / Confluence. The attacker has full operator-level access to the operator's Atlassian instance. This is the same vulnerability class as CVE-2026-27825 (Arctic Wolf, unauthenticated RCE+SSRF in Atlassian MCP). The previous CVE was for a different code path; this report concerns the auth verifier and middleware behavior present in the current main branch. ``` **Steps to Reproduce** Source-level demonstration: 1. Verify the verifier accepts arbitrary tokens: cd src/ python -c " import asyncio from mcp_atlassian.utils.token_verifier import AtlassianOpaqueTokenVerifier v = AtlassianOpaqueTokenVerifier(required_scopes=['read:jira-work']) result = asyncio.run(v.verify_token('anything-at-all')) print('Accepted:', result is not None) print('Token stored:', result.token if result else None) print('Scopes granted:', result.scopes if result else None) " Expected: Accepted: True Token stored: anything-at-all Scopes granted: ['read:jira-work'] End-to-end (researcher's own Atlassian sandbox): 1. Start mcp-atlassian in HTTP mode against a researcher-owned Atlassian Cloud instance with JIRA_API_TOKEN configured: export JIRA_URL=https://researcher.atlassian.net export JIRA_USERNAME=researcher@example.com export JIRA_API_TOKEN=<researcher's-real-token> export MCP_TRANSPORT=streamable-http export PORT=3000 # Do NOT set OAUTH_PROXY_ENABLE_ENV — leave it default (false) mcp-atlassian 2. From another machine (or curl on localhost), with no auth: curl -X POST http://localhost:3000/mcp \ -H "content-type: application/json" \ -H "accept: application/json, text/event-stream" \ -d '{ "jsonrpc":"2.0", "id":1, "method":"tools/call", "params":{ "name":"jira_get_issue", "arguments":{"issue_key":"PROJ-1"} } }' Expected: returns the Jira issue payload — using the server's JIRA_API_TOKEN to authenticate to Atlassian. No client-side token provided. 3. Optional: same call with a garbage Bearer for completeness: curl ... -H "Authorization: Bearer anything-at-all" ... Same result. **Impact**: Attacker profile: any party with network reach to the HTTP transport. No credentials, no prior account, no privileged position required. Typical deployment patterns at risk: - Docker compose with port exposed (very common in mcp-atlassian's docs and community deployments) - Cloud-deployed MCP server behind a load balancer where the LB doesn't enforce auth (delegates to the application) - Internal corporate network where any employee can reach the server - Misconfigured Kubernetes ingress - Tunneled MCP server via ngrok / Cloudflare Tunnel for development that gets left exposed Security impact after exploitation: 1. Full Jira read access. Every project, every issue, every comment, every attachment, every user — using the operator's API token. 2. Full Jira write access. Create, edit, delete issues. Add comments under the operator's identity. Move issues across boards. Bulk-edit. 3. Full Confluence read/write access. Same surface — pages, spaces, attachments, permissions, restricted spaces visible to the operator's identity. 4. Audit trail names the operator. Every API call is signed with the operator's token. From Atlassian's logging side, the operator is the actor — covering the attacker's tracks and shifting blame. 5. Pivot. Attachments often contain credentials, infrastructure diagrams, customer data. Confluence pages often store secrets in plaintext under the assumption of access control. 6. Persistence. Attacker can create new Jira webhooks, automation rules, or Confluence integrations that survive beyond the MCP session. CVE-2026-27825 (Arctic Wolf, May 2026) was scored CVSS 9.8 Critical for unauth RCE+SSRF in this same code surface. This report is the auth-bypass component of the same class against the current main branch. **Suggested Fix** The most direct fix is the standard MCP-server-with-env-creds pattern: 1. When OAUTH_PROXY_ENABLE_ENV is not set, REFUSE to start the HTTP transport unless an explicit "single-user mode" flag is set: SINGLE_USER_MODE = is_env_truthy("MCP_ATLASSIAN_SINGLE_USER") if MCP_TRANSPORT == "streamable-http" and not auth_provider and not SINGLE_USER_MODE: raise SystemExit( "HTTP transport requires either OAUTH_PROXY_ENABLE=true " "or MCP_ATLASSIAN_SINGLE_USER=true (acknowledges that env " "credentials will be used for any incoming request)." ) 2. Even with SINGLE_USER_MODE, bind the HTTP transport to 127.0.0.1 by default unless the operator overrides with an explicit MCP_ATLASSIAN_BIND_PUBLIC=true. 3. Document the multi-tenant pattern as requiring OAuth proxy or per-request user-token middleware with a verifier that actually verifies (not the opaque-accept-anything stub). 4. Replace AtlassianOpaqueTokenVerifier with a verifier that performs a token-info or whoami call to Atlassian. The fact that Atlassian tokens are opaque does not preclude verification — a /rest/api/3/myself call validates the token and returns the associated user, which the verifier can attach to the AccessToken's scopes and user_id fields. Defense in depth: the README quickstart should not encourage exposing the HTTP transport without auth. The docker-compose.yml in the repo should bind to 127.0.0.1 only by default.

CVE-2026-77244 Affected Mcp atlassian Versions

1 component(s)
Fixed Versions:
0.22.0
Product / Package Ecosystem Affected Versions Fixed Version
mcp-atlassian
PyPI
< 0.22.0
0.22.0

References

Frequently Asked Questions

What is CVE-2026-77244?
CVE-2026-77244 is a security vulnerability in Mcp atlassian involving AtlassianOpaqueTokenVerifier accepts any non-empty token under CWE-287: Improper Authentication.
Which Mcp atlassian versions are affected?
Affected versions include releases prior to 0.22.0 across affected packages: mcp-atlassian.
What version fixes CVE-2026-77244?
Upgrading to 0.22.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is mcp-atlassian affected?
Yes, mcp-atlassian is affected in releases prior to 0.22.0. Upgrade your dependency to 0.22.0 or later.
What is the CVSS score of CVE-2026-77244?
CVE-2026-77244 has a CVSS v3.1 base score of 10.0 (CRITICAL) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N.
What CWE is associated with CVE-2026-77244?
CVE-2026-77244 is classified under CWE-287: Improper Authentication (When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.).
How can I check PyPI dependencies for CVE-2026-77244?
Run the dependency tree command: 'pip show mcp-atlassian'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-wrhw-j3f9-8vc6",
  "modified" : "2026-09-22T20:36:26Z",
  "published" : "2026-09-22T20:36:26Z",
  "aliases" : [ "CVE-2026-77244" ],
  "summary" : "[mcp-atlassian] Authentication bypass in HTTP transport: AtlassianOpaqueTokenVerifier accepts any non-empty token",
  "details" : "**Description**\n\nmcp-atlassian deploys in two common patterns:\n\n  Pattern A (single-user, server-side credentials): operator sets\n    JIRA_USERNAME + JIRA_API_TOKEN (or CONFLUENCE_USERNAME + CONFLUENCE_API_TOKEN)\n    in environment variables. Server uses these to call Jira/Confluence.\n    This is the documented quickstart pattern.\n\n  Pattern B (multi-user, OAuth or per-request PAT): operator sets up OAuth\n    proxy or accepts per-user tokens via Authorization or service headers.\n\nThe authentication mechanism in HTTP transport has two issues that combine\nto permit unauthenticated access to Pattern A deployments:\n\n1. AtlassianOpaqueTokenVerifier.verify_token() at\n   `src/mcp_atlassian/utils/token_verifier.py` accepts any non-empty string\n   as a valid token:\n\n       async def verify_token(self, token: str) -> AccessToken | None:\n           if not token:\n               return None\n           scopes = self.required_scopes or []\n           return AccessToken(\n               token=token,\n               client_id=\"atlassian\",\n               scopes=scopes,\n               expires_at=int(time.time()) + 86400 * 30,\n           )\n\n   The docstring documents this: \"we accept non-empty tokens and attach\n   the required scopes.\"\n\n2. The default deployment does NOT enable the OAuth proxy auth provider\n   (OAUTH_PROXY_ENABLE_ENV defaults to false; main.py:726). When\n   `_build_auth_provider()` returns None, FastMCP HTTP transport accepts\n   requests with no authentication challenge.\n\n3. `UserTokenMiddleware._parse_auth_header` (main.py:601-664) extracts\n   tokens from Authorization headers and stores them in scope state. If\n   NO Authorization header is present (main.py:584-595), the middleware\n   does not reject the request — it simply does not populate\n   `user_atlassian_token`.\n\n4. JiraFetcher / ConfluenceFetcher fall back to `JiraConfig.from_env()`\n   when no user-supplied token is in scope state. `from_env()` reads\n   `JIRA_API_TOKEN` and `JIRA_USERNAME` from environment and uses them\n   as the API credentials.\n\nComposition: an attacker who reaches the HTTP transport (e.g., server\nexposed on a port reachable from attacker — direct bind, Docker port\nmapping, reverse proxy without auth, container in a network the attacker\njoined) can:\n\n  - Send no Authorization header at all, OR\n  - Send any garbage Bearer token\n\nEither request reaches tool handlers. The tool handlers, finding no\nuser-supplied token, use the server's env-var credentials to call\nJira / Confluence. The attacker has full operator-level access to the\noperator's Atlassian instance.\n\nThis is the same vulnerability class as CVE-2026-27825 (Arctic Wolf,\nunauthenticated RCE+SSRF in Atlassian MCP). The previous CVE was for a\ndifferent code path; this report concerns the auth verifier and middleware\nbehavior present in the current main branch.\n```\n**Steps to Reproduce**\n\nSource-level demonstration:\n\n1. Verify the verifier accepts arbitrary tokens:\n\n       cd src/\n       python -c \"\n       import asyncio\n       from mcp_atlassian.utils.token_verifier import AtlassianOpaqueTokenVerifier\n       v = AtlassianOpaqueTokenVerifier(required_scopes=['read:jira-work'])\n       result = asyncio.run(v.verify_token('anything-at-all'))\n       print('Accepted:', result is not None)\n       print('Token stored:', result.token if result else None)\n       print('Scopes granted:', result.scopes if result else None)\n       \"\n\n   Expected:\n       Accepted: True\n       Token stored: anything-at-all\n       Scopes granted: ['read:jira-work']\n\nEnd-to-end (researcher's own Atlassian sandbox):\n\n1. Start mcp-atlassian in HTTP mode against a researcher-owned Atlassian\n   Cloud instance with JIRA_API_TOKEN configured:\n\n       export JIRA_URL=https://researcher.atlassian.net\n       export JIRA_USERNAME=researcher@example.com\n       export JIRA_API_TOKEN=<researcher's-real-token>\n       export MCP_TRANSPORT=streamable-http\n       export PORT=3000\n       # Do NOT set OAUTH_PROXY_ENABLE_ENV — leave it default (false)\n       mcp-atlassian\n\n2. From another machine (or curl on localhost), with no auth:\n\n       curl -X POST http://localhost:3000/mcp \\\n            -H \"content-type: application/json\" \\\n            -H \"accept: application/json, text/event-stream\" \\\n            -d '{\n              \"jsonrpc\":\"2.0\", \"id\":1, \"method\":\"tools/call\",\n              \"params\":{\n                \"name\":\"jira_get_issue\",\n                \"arguments\":{\"issue_key\":\"PROJ-1\"}\n              }\n            }'\n\n   Expected: returns the Jira issue payload — using the server's\n   JIRA_API_TOKEN to authenticate to Atlassian. No client-side token\n   provided.\n\n3. Optional: same call with a garbage Bearer for completeness:\n\n       curl ... -H \"Authorization: Bearer anything-at-all\" ...\n\n   Same result.\n   \n   \n   **Impact**:\n   \n   Attacker profile: any party with network reach to the HTTP transport.\nNo credentials, no prior account, no privileged position required.\n\nTypical deployment patterns at risk:\n\n  - Docker compose with port exposed (very common in mcp-atlassian's\n    docs and community deployments)\n  - Cloud-deployed MCP server behind a load balancer where the LB\n    doesn't enforce auth (delegates to the application)\n  - Internal corporate network where any employee can reach the server\n  - Misconfigured Kubernetes ingress\n  - Tunneled MCP server via ngrok / Cloudflare Tunnel for development\n    that gets left exposed\n\nSecurity impact after exploitation:\n\n1. Full Jira read access. Every project, every issue, every comment,\n   every attachment, every user — using the operator's API token.\n\n2. Full Jira write access. Create, edit, delete issues. Add comments\n   under the operator's identity. Move issues across boards. Bulk-edit.\n\n3. Full Confluence read/write access. Same surface — pages, spaces,\n   attachments, permissions, restricted spaces visible to the operator's\n   identity.\n\n4. Audit trail names the operator. Every API call is signed with the\n   operator's token. From Atlassian's logging side, the operator is the\n   actor — covering the attacker's tracks and shifting blame.\n\n5. Pivot. Attachments often contain credentials, infrastructure\n   diagrams, customer data. Confluence pages often store secrets in\n   plaintext under the assumption of access control.\n\n6. Persistence. Attacker can create new Jira webhooks, automation rules,\n   or Confluence integrations that survive beyond the MCP session.\n\nCVE-2026-27825 (Arctic Wolf, May 2026) was scored CVSS 9.8 Critical for\nunauth RCE+SSRF in this same code surface. This report is the auth-bypass\ncomponent of the same class against the current main branch.\n\n\n**Suggested Fix**\n\nThe most direct fix is the standard MCP-server-with-env-creds pattern:\n\n  1. When OAUTH_PROXY_ENABLE_ENV is not set, REFUSE to start the HTTP\n     transport unless an explicit \"single-user mode\" flag is set:\n\n       SINGLE_USER_MODE = is_env_truthy(\"MCP_ATLASSIAN_SINGLE_USER\")\n       if MCP_TRANSPORT == \"streamable-http\" and not auth_provider and not SINGLE_USER_MODE:\n           raise SystemExit(\n               \"HTTP transport requires either OAUTH_PROXY_ENABLE=true \"\n               \"or MCP_ATLASSIAN_SINGLE_USER=true (acknowledges that env \"\n               \"credentials will be used for any incoming request).\"\n           )\n\n  2. Even with SINGLE_USER_MODE, bind the HTTP transport to 127.0.0.1\n     by default unless the operator overrides with an explicit\n     MCP_ATLASSIAN_BIND_PUBLIC=true.\n\n  3. Document the multi-tenant pattern as requiring OAuth proxy or\n     per-request user-token middleware with a verifier that actually\n     verifies (not the opaque-accept-anything stub).\n\n  4. Replace AtlassianOpaqueTokenVerifier with a verifier that performs\n     a token-info or whoami call to Atlassian. The fact that Atlassian\n     tokens are opaque does not preclude verification — a\n     /rest/api/3/myself call validates the token and returns the\n     associated user, which the verifier can attach to the AccessToken's\n     scopes and user_id fields.\n\nDefense in depth: the README quickstart should not encourage exposing\nthe HTTP transport without auth. The docker-compose.yml in the repo\nshould bind to 127.0.0.1 only by default.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "mcp-atlassian"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.22.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-wrhw-j3f9-8vc6"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-77244"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/sooperset/mcp-atlassian/pull/1448"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/sooperset/mcp-atlassian"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/sooperset/mcp-atlassian/releases/tag/v0.22.0"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-287", "CWE-303", "CWE-862" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-09-22T20:36:26Z",
    "nvd_published_at" : "2026-09-22T18:17:17Z"
  }
}