mirror of
https://git.yoctoproject.org/poky
synced 2026-05-30 00:20:08 +00:00
bitbake: toastergui: fix XSS injection points in projects page
We close XSS injection points in Projects page. * modify the json filter to properly escape HTML tags in strings * enable $sanitize to automatically sanitize dangerous HTML in user-supplied input * clean dangerous characters in targets field, as that field contents will be directly passed to a shell command Based on the vulnerability discovered and the patch provided by Michael Wood. (Bitbake rev: 23c440db9c076ca37e651bdbbdbefee54998e1dc) Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
committed by
Richard Purdie
parent
326d5b1a28
commit
c5d19aae55
@@ -101,7 +101,7 @@ function _diffArrays(existingArray, newArray, compareElements, onAdded, onDelete
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var projectApp = angular.module('project', ['ngCookies', 'ngAnimate', 'ui.bootstrap' ], angular_formpost);
|
var projectApp = angular.module('project', ['ngCookies', 'ngAnimate', 'ui.bootstrap', 'ngRoute', 'ngSanitize'], angular_formpost);
|
||||||
|
|
||||||
// modify the template tag markers to prevent conflicts with Django
|
// modify the template tag markers to prevent conflicts with Django
|
||||||
projectApp.config(function($interpolateProvider) {
|
projectApp.config(function($interpolateProvider) {
|
||||||
@@ -128,7 +128,7 @@ projectApp.filter('timediff', function() {
|
|||||||
|
|
||||||
|
|
||||||
// main controller for the project page
|
// main controller for the project page
|
||||||
projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $location, $cookies, $q, $sce, $anchorScroll, $animate) {
|
projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $location, $cookies, $q, $sce, $anchorScroll, $animate, $sanitize) {
|
||||||
|
|
||||||
$scope.getSuggestions = function(type, currentValue) {
|
$scope.getSuggestions = function(type, currentValue) {
|
||||||
var deffered = $q.defer();
|
var deffered = $q.defer();
|
||||||
@@ -475,6 +475,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
|
|||||||
var alertText = undefined;
|
var alertText = undefined;
|
||||||
var alertZone = undefined;
|
var alertZone = undefined;
|
||||||
var oldLayers = [];
|
var oldLayers = [];
|
||||||
|
|
||||||
switch(elementid) {
|
switch(elementid) {
|
||||||
case '#select-machine':
|
case '#select-machine':
|
||||||
alertText = "You have changed the machine to: <strong>" + $scope.machineName + "</strong>";
|
alertText = "You have changed the machine to: <strong>" + $scope.machineName + "</strong>";
|
||||||
@@ -594,7 +595,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
|
|||||||
var crtid = zone.maxid ++;
|
var crtid = zone.maxid ++;
|
||||||
angular.forEach(zone, function (o) { o.close() });
|
angular.forEach(zone, function (o) { o.close() });
|
||||||
o = {
|
o = {
|
||||||
id: crtid, text: $sce.trustAsHtml(text), type: type,
|
id: crtid, text: text, type: type,
|
||||||
close: function() {
|
close: function() {
|
||||||
zone.splice((function(id){ for (var i = 0; i < zone.length; i++) if (id == zone[i].id) { return i}; return undefined;})(crtid), 1);
|
zone.splice((function(id){ for (var i = 0; i < zone.length; i++) if (id == zone[i].id) { return i}; return undefined;})(crtid), 1);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ vim: expandtab tabstop=2
|
|||||||
<script src="{% static "js/angular.min.js" %}"></script>
|
<script src="{% static "js/angular.min.js" %}"></script>
|
||||||
<script src="{% static "js/angular-animate.min.js" %}"></script>
|
<script src="{% static "js/angular-animate.min.js" %}"></script>
|
||||||
<script src="{% static "js/angular-cookies.min.js" %}"></script>
|
<script src="{% static "js/angular-cookies.min.js" %}"></script>
|
||||||
|
<script src="{% static "js/angular-route.min.js" %}"></script>
|
||||||
|
<script src="{% static "js/angular-sanitize.min.js" %}"></script>
|
||||||
<script src="{% static "js/ui-bootstrap-tpls-0.11.0.js" %}"></script>
|
<script src="{% static "js/ui-bootstrap-tpls-0.11.0.js" %}"></script>
|
||||||
|
|
||||||
|
|
||||||
@@ -365,13 +367,13 @@ angular.element(document).ready(function() {
|
|||||||
scope.urls.layers = "{% url 'layers' %}";
|
scope.urls.layers = "{% url 'layers' %}";
|
||||||
scope.urls.targets = "{% url 'targets' %}";
|
scope.urls.targets = "{% url 'targets' %}";
|
||||||
scope.urls.importlayer = "{% url 'importlayer'%}"
|
scope.urls.importlayer = "{% url 'importlayer'%}"
|
||||||
scope.project = {{prj|safe}};
|
scope.project = {{prj|json}};
|
||||||
scope.builds = {{builds|safe}};
|
scope.builds = {{builds|json}};
|
||||||
scope.layers = {{layers|safe}};
|
scope.layers = {{layers|json}};
|
||||||
scope.targets = {{targets|safe}};
|
scope.targets = {{targets|json}};
|
||||||
scope.frequenttargets = {{freqtargets|safe}};
|
scope.frequenttargets = {{freqtargets|json}};
|
||||||
scope.machine = {{machine|safe}};
|
scope.machine = {{machine|json}};
|
||||||
scope.releases = {{releases|safe}};
|
scope.releases = {{releases|json}};
|
||||||
|
|
||||||
var now = (new Date()).getTime();
|
var now = (new Date()).getTime();
|
||||||
scope.todaydate = now - (now % 86400000);
|
scope.todaydate = now - (now % 86400000);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from django import template
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.template.defaultfilters import filesizeformat
|
from django.template.defaultfilters import filesizeformat
|
||||||
import json as JsonLib
|
import json as JsonLib
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@@ -49,7 +50,10 @@ def mapselect(value, argument):
|
|||||||
|
|
||||||
@register.filter(name = "json")
|
@register.filter(name = "json")
|
||||||
def json(value):
|
def json(value):
|
||||||
return JsonLib.dumps(value)
|
# JSON spec says that "\/" is functionally identical to "/" to allow for HTML-tag embedding in JSON strings
|
||||||
|
# unfortunately, I can't find any option in the json module to turn on forward-slash escaping, so we do
|
||||||
|
# it manually here
|
||||||
|
return mark_safe(JsonLib.dumps(value, ensure_ascii=False).replace('</', '<\\/'))
|
||||||
|
|
||||||
@register.assignment_tag
|
@register.assignment_tag
|
||||||
def query(qs, **kwargs):
|
def query(qs, **kwargs):
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from django.utils import timezone
|
|||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
|
from toastergui.templatetags.projecttags import json as jsonfilter
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@@ -871,7 +872,7 @@ def _get_dir_entries(build_id, target_id, start):
|
|||||||
# sort by directories first, then by name
|
# sort by directories first, then by name
|
||||||
rsorted = sorted(response, key=lambda entry : entry['name'])
|
rsorted = sorted(response, key=lambda entry : entry['name'])
|
||||||
rsorted = sorted(rsorted, key=lambda entry : entry['isdir'], reverse=True)
|
rsorted = sorted(rsorted, key=lambda entry : entry['isdir'], reverse=True)
|
||||||
return json.dumps(rsorted, cls=LazyEncoder)
|
return json.dumps(rsorted, cls=LazyEncoder).replace('</', '<\\/')
|
||||||
|
|
||||||
def dirinfo(request, build_id, target_id, file_path=None):
|
def dirinfo(request, build_id, target_id, file_path=None):
|
||||||
template = "dirinfo.html"
|
template = "dirinfo.html"
|
||||||
@@ -1981,25 +1982,25 @@ if toastermain.settings.MANAGED:
|
|||||||
context = {
|
context = {
|
||||||
"project" : prj,
|
"project" : prj,
|
||||||
"completedbuilds": Build.objects.filter(project = prj).exclude(outcome = Build.IN_PROGRESS),
|
"completedbuilds": Build.objects.filter(project = prj).exclude(outcome = Build.IN_PROGRESS),
|
||||||
"prj" : json.dumps({"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}}),
|
"prj" : {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}},
|
||||||
#"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED),
|
#"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED),
|
||||||
"builds" : json.dumps(_project_recent_build_list(prj)),
|
"builds" : _project_recent_build_list(prj),
|
||||||
"layers" : json.dumps(map(lambda x: {
|
"layers" : map(lambda x: {
|
||||||
"id": x.layercommit.pk,
|
"id": x.layercommit.pk,
|
||||||
"orderid": x.pk,
|
"orderid": x.pk,
|
||||||
"name" : x.layercommit.layer.name,
|
"name" : x.layercommit.layer.name,
|
||||||
"url": x.layercommit.layer.layer_index_url,
|
"url": x.layercommit.layer.layer_index_url,
|
||||||
"layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)),
|
"layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)),
|
||||||
"branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}},
|
"branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}},
|
||||||
prj.projectlayer_set.all().order_by("id"))),
|
prj.projectlayer_set.all().order_by("id")),
|
||||||
"targets" : json.dumps(map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all())),
|
"targets" : map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all()),
|
||||||
"freqtargets": json.dumps(freqtargets),
|
"freqtargets": freqtargets,
|
||||||
"releases": json.dumps(map(lambda x: {"id": x.pk, "name": x.name}, Release.objects.all())),
|
"releases": map(lambda x: {"id": x.pk, "name": x.name}, Release.objects.all()),
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
context["machine"] = json.dumps({"name": prj.projectvariable_set.get(name="MACHINE").value})
|
context["machine"] = {"name": prj.projectvariable_set.get(name="MACHINE").value}
|
||||||
except ProjectVariable.DoesNotExist:
|
except ProjectVariable.DoesNotExist:
|
||||||
context["machine"] = json.dumps(None)
|
context["machine"] = None
|
||||||
try:
|
try:
|
||||||
context["distro"] = prj.projectvariable_set.get(name="DISTRO").value
|
context["distro"] = prj.projectvariable_set.get(name="DISTRO").value
|
||||||
except ProjectVariable.DoesNotExist:
|
except ProjectVariable.DoesNotExist:
|
||||||
@@ -2035,7 +2036,8 @@ if toastermain.settings.MANAGED:
|
|||||||
|
|
||||||
if 'targets' in request.POST:
|
if 'targets' in request.POST:
|
||||||
ProjectTarget.objects.filter(project = prj).delete()
|
ProjectTarget.objects.filter(project = prj).delete()
|
||||||
for t in request.POST['targets'].strip().split(" "):
|
s = str(request.POST['targets'])
|
||||||
|
for t in s.translate(None, ";%|\"").split(" "):
|
||||||
if ":" in t:
|
if ":" in t:
|
||||||
target, task = t.split(":")
|
target, task = t.split(":")
|
||||||
else:
|
else:
|
||||||
@@ -2045,11 +2047,11 @@ if toastermain.settings.MANAGED:
|
|||||||
|
|
||||||
br = prj.schedule_build()
|
br = prj.schedule_build()
|
||||||
|
|
||||||
return HttpResponse(json.dumps({"error":"ok",
|
return HttpResponse(jsonfilter({"error":"ok",
|
||||||
"builds" : _project_recent_build_list(prj),
|
"builds" : _project_recent_build_list(prj),
|
||||||
}), content_type = "application/json")
|
}), content_type = "application/json")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
||||||
|
|
||||||
def xhr_projectedit(request, pid):
|
def xhr_projectedit(request, pid):
|
||||||
try:
|
try:
|
||||||
@@ -2088,7 +2090,7 @@ if toastermain.settings.MANAGED:
|
|||||||
machinevar.save()
|
machinevar.save()
|
||||||
|
|
||||||
# return all project settings
|
# return all project settings
|
||||||
return HttpResponse(json.dumps( {
|
return HttpResponse(jsonfilter( {
|
||||||
"error": "ok",
|
"error": "ok",
|
||||||
"layers" : map(lambda x: {"id": x.layercommit.pk, "orderid" : x.pk, "name" : x.layercommit.layer.name, "url": x.layercommit.layer.layer_index_url, "layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)), "branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}}, prj.projectlayer_set.all().order_by("id")),
|
"layers" : map(lambda x: {"id": x.layercommit.pk, "orderid" : x.pk, "name" : x.layercommit.layer.name, "url": x.layercommit.layer.layer_index_url, "layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)), "branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}}, prj.projectlayer_set.all().order_by("id")),
|
||||||
"builds" : _project_recent_build_list(prj),
|
"builds" : _project_recent_build_list(prj),
|
||||||
@@ -2098,7 +2100,7 @@ if toastermain.settings.MANAGED:
|
|||||||
}), content_type = "application/json")
|
}), content_type = "application/json")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
||||||
|
|
||||||
|
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
@@ -2112,7 +2114,7 @@ if toastermain.settings.MANAGED:
|
|||||||
prj = Project.objects.get(pk = request.session['project_id'])
|
prj = Project.objects.get(pk = request.session['project_id'])
|
||||||
queryset_all = queryset_all.filter(up_branch__release = prj.release).exclude(pk__in = map(lambda x: x.layercommit_id, prj.projectlayer_set.all()))
|
queryset_all = queryset_all.filter(up_branch__release = prj.release).exclude(pk__in = map(lambda x: x.layercommit_id, prj.projectlayer_set.all()))
|
||||||
queryset_all = queryset_all.filter(layer__name__icontains=request.GET.get('value',''))
|
queryset_all = queryset_all.filter(layer__name__icontains=request.GET.get('value',''))
|
||||||
return HttpResponse(json.dumps( { "error":"ok",
|
return HttpResponse(jsonfilter( { "error":"ok",
|
||||||
"list" : map( lambda x: {"id": x.pk, "name": x.layer.name, "detail": "(" + x.layer.layer_source.name + (")" if x.up_branch == None else " | "+x.up_branch.name+")")},
|
"list" : map( lambda x: {"id": x.pk, "name": x.layer.name, "detail": "(" + x.layer.layer_source.name + (")" if x.up_branch == None else " | "+x.up_branch.name+")")},
|
||||||
queryset_all[:8])
|
queryset_all[:8])
|
||||||
}), content_type = "application/json")
|
}), content_type = "application/json")
|
||||||
@@ -2127,7 +2129,7 @@ if toastermain.settings.MANAGED:
|
|||||||
|
|
||||||
queryset_all.order_by("-up_id");
|
queryset_all.order_by("-up_id");
|
||||||
|
|
||||||
return HttpResponse(json.dumps( { "error":"ok",
|
return HttpResponse(jsonfilter( { "error":"ok",
|
||||||
"list" : map(
|
"list" : map(
|
||||||
lambda x: {"id": x.pk, "name": x.layer.name, "detail": "(" + x.layer.layer_source.name + (")" if x.up_branch == None else " | "+x.up_branch.name+")"),
|
lambda x: {"id": x.pk, "name": x.layer.name, "detail": "(" + x.layer.layer_source.name + (")" if x.up_branch == None else " | "+x.up_branch.name+")"),
|
||||||
"layerdetailurl" : reverse('layerdetails', args=(x.pk,))},
|
"layerdetailurl" : reverse('layerdetails', args=(x.pk,))},
|
||||||
@@ -2146,7 +2148,7 @@ if toastermain.settings.MANAGED:
|
|||||||
if lv.count() != 1: # there is no layer_version with the new release id, and the same name
|
if lv.count() != 1: # there is no layer_version with the new release id, and the same name
|
||||||
retval.append(i)
|
retval.append(i)
|
||||||
|
|
||||||
return HttpResponse(json.dumps( {"error":"ok",
|
return HttpResponse(jsonfilter( {"error":"ok",
|
||||||
"list": map(
|
"list": map(
|
||||||
lambda x: {"id": x.layercommit.pk, "name": x.layercommit.layer.name, "detail": "(" + x.layercommit.layer.layer_source.name + (")" if x.layercommit.up_branch == None else " | "+x.layercommit.up_branch.name+")")},
|
lambda x: {"id": x.layercommit.pk, "name": x.layercommit.layer.name, "detail": "(" + x.layercommit.layer.layer_source.name + (")" if x.layercommit.up_branch == None else " | "+x.layercommit.up_branch.name+")")},
|
||||||
retval) }), content_type = "application/json")
|
retval) }), content_type = "application/json")
|
||||||
@@ -2156,7 +2158,7 @@ if toastermain.settings.MANAGED:
|
|||||||
queryset_all = Recipe.objects.all()
|
queryset_all = Recipe.objects.all()
|
||||||
if 'project_id' in request.session:
|
if 'project_id' in request.session:
|
||||||
queryset_all = queryset_all.filter(layer_version__layer__in = map(lambda x: x.layercommit.layer, ProjectLayer.objects.filter(project_id=request.session['project_id'])))
|
queryset_all = queryset_all.filter(layer_version__layer__in = map(lambda x: x.layercommit.layer, ProjectLayer.objects.filter(project_id=request.session['project_id'])))
|
||||||
return HttpResponse(json.dumps({ "error":"ok",
|
return HttpResponse(jsonfilter({ "error":"ok",
|
||||||
"list" : map ( lambda x: {"id": x.pk, "name": x.name, "detail":"[" + x.layer_version.layer.name+ (" | " + x.layer_version.up_branch.name + "]" if x.layer_version.up_branch is not None else "]")},
|
"list" : map ( lambda x: {"id": x.pk, "name": x.name, "detail":"[" + x.layer_version.layer.name+ (" | " + x.layer_version.up_branch.name + "]" if x.layer_version.up_branch is not None else "]")},
|
||||||
queryset_all.filter(name__icontains=request.GET.get('value',''))[:8]),
|
queryset_all.filter(name__icontains=request.GET.get('value',''))[:8]),
|
||||||
|
|
||||||
@@ -2166,7 +2168,7 @@ if toastermain.settings.MANAGED:
|
|||||||
queryset_all = Machine.objects.all()
|
queryset_all = Machine.objects.all()
|
||||||
if 'project_id' in request.session:
|
if 'project_id' in request.session:
|
||||||
queryset_all = queryset_all.filter(layer_version__layer__in = map(lambda x: x.layercommit.layer, ProjectLayer.objects.filter(project_id=request.session['project_id'])))
|
queryset_all = queryset_all.filter(layer_version__layer__in = map(lambda x: x.layercommit.layer, ProjectLayer.objects.filter(project_id=request.session['project_id'])))
|
||||||
return HttpResponse(json.dumps({ "error":"ok",
|
return HttpResponse(jsonfilter({ "error":"ok",
|
||||||
"list" : map ( lambda x: {"id": x.pk, "name": x.name, "detail":"[" + x.layer_version.layer.name+ (" | " + x.layer_version.up_branch.name + "]" if x.layer_version.up_branch is not None else "]")},
|
"list" : map ( lambda x: {"id": x.pk, "name": x.name, "detail":"[" + x.layer_version.layer.name+ (" | " + x.layer_version.up_branch.name + "]" if x.layer_version.up_branch is not None else "]")},
|
||||||
queryset_all.filter(name__icontains=request.GET.get('value',''))[:8]),
|
queryset_all.filter(name__icontains=request.GET.get('value',''))[:8]),
|
||||||
|
|
||||||
@@ -2174,7 +2176,7 @@ if toastermain.settings.MANAGED:
|
|||||||
|
|
||||||
raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied"))
|
raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2216,7 +2218,7 @@ if toastermain.settings.MANAGED:
|
|||||||
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'projectlayerset' : json.dumps(map(lambda x: x.layercommit.id, prj.projectlayer_set.all())),
|
'projectlayerset' : jsonfilter(map(lambda x: x.layercommit.id, prj.projectlayer_set.all())),
|
||||||
'objects' : layer_info,
|
'objects' : layer_info,
|
||||||
'objectname' : "layers",
|
'objectname' : "layers",
|
||||||
'default_orderby' : 'layer__name:+',
|
'default_orderby' : 'layer__name:+',
|
||||||
@@ -2309,7 +2311,7 @@ if toastermain.settings.MANAGED:
|
|||||||
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'projectlayerset' : json.dumps(map(lambda x: x.layercommit.id, prj.projectlayer_set.all())),
|
'projectlayerset' : jsonfilter(map(lambda x: x.layercommit.id, prj.projectlayer_set.all())),
|
||||||
'objects' : target_info,
|
'objects' : target_info,
|
||||||
'objectname' : "targets",
|
'objectname' : "targets",
|
||||||
'default_orderby' : 'name:+',
|
'default_orderby' : 'name:+',
|
||||||
|
|||||||
Reference in New Issue
Block a user