CVE-2024-31461 (Python, published 2024-04-10T18:15Z)
Description: value: Plane, an open-source project management tool, has a Server-Side Request Forgery (SSRF) vulnerability in versions prior to 0.17-dev. This issue may allow an attacker to send arbitrary requests from the server hosting the application, potentially leading to unauthorized access to internal systems. The impact of this vulnerability includes, but is not limited to, unauthorized access to internal services accessible from the server, potential leakage of sensitive information from internal services, manipulation of internal systems by interacting with internal APIs. Version 0.17-dev contains a patch for this issue. Those who are unable to update immediately may mitigate the issue by restricting outgoing network connections from servers hosting the application to essential services only and/or implementing strict input validation on URLs or parameters that are used to generate server-side requests.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/makeplane/plane
Files changed:
.github/workflows/codeql.yml
apiserver/plane/app/views/importer.py
apiserver/plane/utils/importers/jira.py
web/components/integration/single-integration-card.tsx
web/services/project/project.service.ts
Commit message: fix: jira importer validations (#3323)

* fix: jira importer validations

* dev: update validation for cloud hostname

* dev: update the function to be used externally

* dev: update codeql workflow

* dev: update repository selection api

Vulnerable code:
    branches: [ 'develop', 'hot-fix', 'stage-release' ]
    branches: [ 'develop' ]
from plane.utils.importers.jira import jira_project_issue_summary

        importer = Importer.objects.get(
            pk=pk, service=service, workspace__slug=slug
        )
        importer = Importer.objects.get(
            pk=pk, service=service, workspace__slug=slug
        )
                        url=module_data.get("link", {}).get(
                            "url", "https://plane.so"
                        ),
        issue_url = f"https://{hostname}/rest/api/3/search?jql=project={project_key} AND issuetype=Story"
        status_url = f"https://{hostname}/rest/api/3/status/?jql=project={project_key}"
                handleRemoveIntegration;
    return this.request(url)

Fixed code:
    branches: [ 'develop', 'preview', 'master' ]
    branches: [ 'develop', 'preview', 'master' ]
from plane.utils.importers.jira import jira_project_issue_summary, is_allowed_hostname


            cloud_hostname = metadata.get("cloud_hostname", False)

            if not cloud_hostname:
                return Response(
                    {"error": "Cloud hostname is required"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if not is_allowed_hostname(cloud_hostname):
                return Response(
                    {"error": "Hostname is not a valid hostname."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        importer = Importer.objects.get(pk=pk, service=service, workspace__slug=slug)
        importer = Importer.objects.get(pk=pk, service=service, workspace__slug=slug)
                        url=module_data.get("link", {}).get("url", "https://plane.so"),
from urllib.parse import urlparse

def is_allowed_hostname(hostname):
    allowed_lists = ["atl-paas.net", "atlassian.com", "atlassian.net", "jira.com"]
    # Extract the base domain from the hostname
    parsed_uri = urlparse(f"https://{hostname}")  # Add scheme for urlparse to work properly
    domain = parsed_uri.netloc.split(":")[0]  # Removes port number if included
    base_domain = ".".join(domain.split(".")[-2:])  # Extract base domain

    # Check if the base domain is in the allowed list
    return base_domain in allowed_lists

        

        if not is_allowed_hostname(hostname):
            print("Errored Hostname")
            return {"error": "Invalid or unauthorized hostname"}

        issue_url = f"https://{hostname}/rest/api/3/search?jql=project={project_key} AND issuetype!=Epic"
        status_url = f"https://{hostname}/rest/api/3/project/${project_key}/statuses"
                handleRemoveIntegration();
    return this.request({
      method: "get",
      url,
      headers: this.getAccessToken() ? this.getHeaders() : {},
    })