label-studio vulnerable to Cross-Site Scripting (Reflected) via the label_config parameter.
Description
Label Studio is a multi-type data labeling and annotation tool. A vulnerability in versions prior to 1.18.0 allows an attacker to inject a malicious script into the context of a web page, which can lead to data theft, session hijacking, unauthorized actions on behalf of the user, and other attacks. The vulnerability is reproducible when sending a properly formatted request to the POST /projects/upload-example/ endpoint. In the source code, the vulnerability is located at label_studio/projects/views.py. Version 1.18.0 contains a patch for the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
label-studioPyPI | < 1.18.0 | 1.18.0 |
Affected products
1- Range: < 1.18.0
Patches
197db9e7b1678fix: LEAP-2058: delete unused upload-example endpoint (#7440)
4 files changed · +0 −143
label_studio/core/all_urls.json+0 −6 modified@@ -131,12 +131,6 @@ "name": "projects:project-settings-anything", "decorators": "" }, - { - "url": "/projects/upload-example/", - "module": "projects.views.upload_example_using_config", - "name": "projects:project-upload-example-using-config", - "decorators": "" - }, { "url": "/api/projects/", "module": "projects.api.ProjectListAPI",
label_studio/projects/urls.py+0 −1 modified@@ -11,7 +11,6 @@ path('', views.project_list, name='project-index'), path('<int:pk>/settings/', views.project_settings, name='project-settings', kwargs={'sub_path': ''}), path('<int:pk>/settings/<sub_path>', views.project_settings, name='project-settings-anything'), - path('upload-example/', views.upload_example_using_config, name='project-upload-example-using-config'), ] # reverse for projects:api:name
label_studio/projects/views.py+0 −40 modified@@ -1,19 +1,9 @@ """This file and its contents are licensed under the Apache License 2.0. Please see the included NOTICE for copyright information and LICENSE for a copy of the license. """ -import json import logging -import lxml.etree -from core.label_config import get_sample_task -from core.utils.common import get_organization_from_request from django.contrib.auth.decorators import login_required -from django.http import HttpResponse from django.shortcuts import render -from django.views.decorators.http import require_http_methods -from organizations.models import Organization -from projects.models import Project -from rest_framework import generics, status -from rest_framework.exceptions import ValidationError logger = logging.getLogger(__name__) @@ -26,33 +16,3 @@ def project_list(request): @login_required def project_settings(request, pk, sub_path): return render(request, 'projects/settings.html') - - -def playground_replacements(request, task_data): - if request.GET.get('playground', '0') == '1': - for key in task_data: - if '/samples/time-series.csv' in task_data[key]: - task_data[key] = 'https://app.heartex.ai' + task_data[key] - return task_data - - -@require_http_methods(['POST']) -def upload_example_using_config(request): - """Generate upload data example by config only""" - config = request.POST.get('label_config', '') - - org_pk = get_organization_from_request(request) - secure_mode = False - if org_pk is not None: - org = generics.get_object_or_404(Organization, pk=org_pk) - secure_mode = org.secure_mode - - try: - Project.validate_label_config(config) - task_data, _, _ = get_sample_task(config, secure_mode) - task_data = playground_replacements(request, task_data) - except (ValueError, ValidationError, lxml.etree.Error): - response = HttpResponse('error while example generating', status=status.HTTP_400_BAD_REQUEST) - else: - response = HttpResponse(json.dumps(task_data)) - return response
label_studio/tests/sample_tasks.tavern.yml+0 −96 removed@@ -1,96 +0,0 @@ ---- -test_name: get_paragraphs_sample_task -strict: false -marks: -- usefixtures: - - django_live_url -stages: -- id: signup - type: ref -- name: stage - request: - data: - label_config: "<View>\n <ParagraphLabels name=\"actions\" toName=\"dialogue\"\ - >\n <Label value=\"Inform\"></Label>\n <Label value=\"Request\"\ - ></Label>\n <Label value=\"Negate\"></Label>\n <Label value=\"Affirm\"\ - ></Label>\n </ParagraphLabels>\n\n <Paragraphs\n audioUrl=\"$audio\"\ - \n name=\"dialogue\"\n value=\"$dialogue\"\n layout=\"\ - dialogue\"\n savetextresult=\"yes\"\n nameKey=\"speaker\"\ - \n textKey=\"phrase\"\n />\n</View>\n" - headers: - content-type: application/x-www-form-urlencoded - method: POST - url: '{django_live_url}/projects/upload-example' - response: - json: - dialogue: - - phrase: 'Sample: Text #1' - speaker: Alice - - phrase: 'Sample: Text #2' - speaker: Bob - - phrase: 'Sample: Text #3' - speaker: Alice - - phrase: 'Sample: Text #4' - speaker: Bob - - phrase: 'Sample: Text #5' - speaker: Alice - status_code: 200 - ---- -test_name: get_paragraphs_sample_task_value_type_url -strict: false -marks: -- usefixtures: - - django_live_url -stages: -- id: signup - type: ref -- name: stage - request: - data: - label_config: "<View>\n <ParagraphLabels name=\"actions\" toName=\"dialogue\"\ - >\n <Label value=\"Inform\"></Label>\n <Label value=\"Request\"\ - ></Label>\n <Label value=\"Negate\"></Label>\n <Label value=\"Affirm\"\ - ></Label>\n </ParagraphLabels>\n\n <Paragraphs\n audioUrl=\"$audio\"\ - \n name=\"dialogue\"\n value=\"$dialogue\"\n layout=\"\ - dialogue\"\n savetextresult=\"yes\"\n nameKey=\"speaker\"\ - \n textKey=\"phrase\"\n valueType=\"url\" />\n</View>\n" - headers: - content-type: application/x-www-form-urlencoded - method: POST - url: '{django_live_url}/projects/upload-example' - response: - json: - dialogue: /samples/paragraphs.json?nameKey=speaker&textKey=phrase - status_code: 200 - ---- -test_name: get_image_sample_task_with_value_list -strict: false -marks: - - usefixtures: - - django_live_url -stages: - - id: signup - type: ref - - name: stage - request: - data: - label_config: | - <View> - <Image name="image" valueList="$images"/> - <RectangleLabels name="labels" toName="image"> - <Label value="Label1"/> - <Label value="Label2"/> - <Label value="Label3"/> - </RectangleLabels> - </View> - headers: - content-type: application/x-www-form-urlencoded - method: POST - url: '{django_live_url}/projects/upload-example' - response: - json: - images: - - "/static/samples/sample.jpg" - status_code: 200
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.