{"record":{"id":"d381b90d9dd6dc52","repo":"makeplane/plane","slug":"invalid-schema-only-http-and-https-are-allowed","errorCode":null,"errorMessage":"Invalid schema. Only HTTP and HTTPS are allowed.","messagePattern":"Invalid schema\\. Only HTTP and HTTPS are allowed\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"apps/api/plane/db/models/webhook.py","lineNumber":24,"sourceCode":"from uuid import uuid4\nfrom urllib.parse import urlparse\n\n# Django imports\nfrom django.db import models\nfrom django.core.exceptions import ValidationError\n\n# Module imports\nfrom plane.db.models import BaseModel, ProjectBaseModel\n\n\ndef generate_token():\n    return \"plane_wh_\" + uuid4().hex\n\n\ndef validate_schema(value):\n    parsed_url = urlparse(value)\n    if parsed_url.scheme not in [\"http\", \"https\"]:\n        raise ValidationError(\"Invalid schema. Only HTTP and HTTPS are allowed.\")\n\n\ndef validate_domain(value):\n    parsed_url = urlparse(value)\n    domain = parsed_url.netloc\n    if domain in [\"localhost\", \"127.0.0.1\"]:\n        raise ValidationError(\"Local URLs are not allowed.\")\n\n\nclass Webhook(BaseModel):\n    workspace = models.ForeignKey(\"db.Workspace\", on_delete=models.CASCADE, related_name=\"workspace_webhooks\")\n    url = models.URLField(validators=[validate_schema, validate_domain], max_length=1024)\n    is_active = models.BooleanField(default=True)\n    secret_key = models.CharField(max_length=255, default=generate_token)\n    project = models.BooleanField(default=False)\n    issue = models.BooleanField(default=False)\n    module = models.BooleanField(default=False)\n    cycle = models.BooleanField(default=False)","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/db/models/webhook.py#L6-L42","documentation":"Django ValidationError raised by `validate_schema` (webhook.py:18) when `urlparse(value).scheme` is not `http` or `https`. It is one of two validators attached to `Webhook.url` (a URLField). It blocks any non-web scheme such as ftp, file, gopher, or javascript.","triggerScenarios":"Creating or updating a Webhook whose `url` uses a scheme other than http/https (e.g. `ftp://host`, `file:///etc/passwd`, `gopher://`, or a schemeless/malformed URL that urlparse cannot parse to http/https).","commonSituations":"User pastes a webhook target missing the `https://` prefix; integrating with an internal service exposed only via a non-http protocol; typos like `htp://`; testing SSRF payloads against the webhook endpoint.","solutions":["Supply a fully-qualified `http://` or `https://` URL for the webhook target.","Normalize on the client by prefixing `https://` when the user omits a scheme before submitting.","If a non-web protocol is genuinely needed, you must change `validate_schema` - it cannot be satisfied otherwise.","Validate the parsed scheme client-side before POSTing to the webhook create/update API."],"exampleFix":"// before\nurl: \"webhooks.example.com/hook\"\n\n// after\nurl: \"https://webhooks.example.com/hook\"","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef is_valid_webhook_scheme(url: str) -> bool:\n    # mirrors webhook.py:20 validate_schema\n    return urlparse(url).scheme in ('http', 'https')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always prefix webhook URLs with https:// (or http:// for local).","Normalize user input on the client: prepend https:// if no scheme.","Validate scheme before POSTing the webhook create/update form."],"tags":["webhook","validation","url","ssrf"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}