From c83d4598de48883dccfcc51d7aaf9792f5d0c819 Mon Sep 17 00:00:00 2001 From: Jana Cupova Date: Feb 07 2022 08:36:59 +0000 Subject: Escape html values Fixes: https://pagure.io/koji/issue/3155 --- diff --git a/www/kojiweb/archiveinfo.chtml b/www/kojiweb/archiveinfo.chtml index 13ce2c0..9acb5e0 100644 --- a/www/kojiweb/archiveinfo.chtml +++ b/www/kojiweb/archiveinfo.chtml @@ -6,7 +6,7 @@ #attr _PASSTHROUGH = ['archiveID', 'fileOrder', 'fileStart', 'buildrootOrder', 'buildrootStart'] #include "includes/header.chtml" -

Information for archive $archive.filename

+

Information for archive $util.escapeHTML($archive.filename)

@@ -16,7 +16,7 @@ #if $wininfo #else - + #end if #if $archive.metadata_only @@ -25,7 +25,7 @@ #end if - + @@ -97,7 +97,7 @@ #for $file in $files - + #end for
File Name$koji.pathinfo.winfile($archive)File Name$archive.filenameFile Name$util.escapeHTML($archive.filename)
File Type$archive_type.descriptionFile Type$util.escapeHTML($archive_type.description)
Build$koji.buildLabel($build)
$file.name$util.formatNatural($file.size)$util.escapeHTML($file.name)$util.formatNatural($file.size)
diff --git a/www/kojiweb/archivelist.chtml b/www/kojiweb/archivelist.chtml index 8d11857..c9f3149 100644 --- a/www/kojiweb/archivelist.chtml +++ b/www/kojiweb/archivelist.chtml @@ -13,7 +13,7 @@ buildrootID=$buildroot.id #slurp #if $type == 'component'

Component Archives of buildroot $util.brLabel($buildroot)

#elif $type == 'image' -

Archives installed in $image.filename

+

Archives installed in $util.escapeHTML($image.filename)

#else

Archives built in buildroot $util.brLabel($buildroot)

#end if @@ -52,8 +52,8 @@ buildrootID=$buildroot.id #slurp #if $len($archives) > 0 #for $archive in $archives - $archive.filename - $archive.type_name + $util.escapeHTML($archive.filename) + $util.escapeHTML($archive.type_name) #if $type == 'component' #set $project = $archive.project and 'yes' or 'no' $util.imageTag($project) diff --git a/www/kojiweb/buildinfo.chtml b/www/kojiweb/buildinfo.chtml index ff0d574..d905007 100644 --- a/www/kojiweb/buildinfo.chtml +++ b/www/kojiweb/buildinfo.chtml @@ -13,7 +13,7 @@ ID$build.id - Package Name$build.package_name + Package Name$util.escapeHTML($build.package_name) Version$build.version @@ -61,7 +61,7 @@ #end if - Built by$build.owner_name + Built by$util.escapeHTML($build.owner_name) #set $stateName = $util.stateName($build.state) @@ -76,7 +76,7 @@ Volume - $build.volume_name + $util.escapeHTML($build.volume_name) Started$util.formatTimeLong($start_ts) @@ -94,7 +94,7 @@ #end if #if $build.cg_id - Content generator$build.cg_name + Content generator$util.escapeHTML($build.cg_name) #end if #if $task @@ -114,7 +114,7 @@ #for $tag in $tags - + #end for
$tag.name$util.escapeHTML($tag.name)
@@ -219,7 +219,7 @@ - $loginfo.name + $util.escapeHTML($loginfo.name) #end for diff --git a/www/kojiweb/buildrootinfo.chtml b/www/kojiweb/buildrootinfo.chtml index 7d1d4de..00ef53d 100644 --- a/www/kojiweb/buildrootinfo.chtml +++ b/www/kojiweb/buildrootinfo.chtml @@ -8,10 +8,10 @@ - + - + @@ -32,7 +32,7 @@ - + diff --git a/www/kojiweb/buildrootinfo_cg.chtml b/www/kojiweb/buildrootinfo_cg.chtml index 8d2933d..2f2a9b4 100644 --- a/www/kojiweb/buildrootinfo_cg.chtml +++ b/www/kojiweb/buildrootinfo_cg.chtml @@ -11,19 +11,19 @@ - + - + - + - + - + #if $buildroot.get('extra') diff --git a/www/kojiweb/builds.chtml b/www/kojiweb/builds.chtml index 843955d..50fb113 100644 --- a/www/kojiweb/builds.chtml +++ b/www/kojiweb/builds.chtml @@ -5,7 +5,7 @@ #include "includes/header.chtml" -

#if $latest then 'Latest ' else ''##if $state != None then $util.stateName($state).capitalize() + ' ' else ''##if $type then $type.capitalize() + ' ' else ''#Builds#if $package then ' of %s' % ($package.id, $package.name) else ''##if $prefix then ' starting with "%s"' % $prefix else ''##if $user then ' by %s' % ($user.id, $user.name) else ''##if $tag then ' in tag %s' % ($tag.id, $tag.name) else ''#

+

#if $latest then 'Latest ' else ''##if $state != None then $util.stateName($state).capitalize() + ' ' else ''##if $type then $type.capitalize() + ' ' else ''#Builds#if $package then ' of %s' % ($package.id, $util.escapeHTML($package.name)) else ''##if $prefix then ' starting with "%s"' % $prefix else ''##if $user then ' by %s' % ($user.id, $util.escapeHTML($user.name)) else ''##if $tag then ' in tag %s' % ($tag.id, $util.escapeHTML($tag.name)) else ''#

Host$buildroot.host_nameHost$util.escapeHTML($buildroot.host_name)
Arch$buildroot.archArch$util.escapeHTML($buildroot.arch)
ID$buildroot.idRepo ID$buildroot.repo_id
Repo Tag$buildroot.tag_nameRepo Tag$util.escapeHTML($buildroot.tag_name)
Repo State$util.imageTag($util.repoStateName($buildroot.repo_state))ID$buildroot.id
Host OS$buildroot.host_osHost OS$util.escapeHTML($buildroot.host_os)
Host Arch$buildroot.host_archHost Arch$util.escapeHTML($buildroot.host_arch)
Content Generator$buildroot.cg_name ($buildroot.cg_version)Content Generator$util.escapeHTML($buildroot.cg_name) ($buildroot.cg_version)
Container Type$buildroot.container_typeContainer Type$util.escapeHTML($buildroot.container_type)
Container Arch$buildroot.container_archContainer Arch$util.escapeHTML($buildroot.container_arch)
@@ -38,7 +38,7 @@ #end if #for $userOption in $users - + #end for @@ -124,9 +124,9 @@ #if $tag - + #end if - + #set $stateName = $util.stateName($build.state) diff --git a/www/kojiweb/buildsbytarget.chtml b/www/kojiweb/buildsbytarget.chtml index 97a7e7b..84f286b 100644 --- a/www/kojiweb/buildsbytarget.chtml +++ b/www/kojiweb/buildsbytarget.chtml @@ -1,4 +1,5 @@ #from kojiweb import util +#from urllib.parse import quote #def printOption(value, label=None) #if not $label @@ -61,7 +62,7 @@ #if $len($targets) > 0 #for $target in $targets - + diff --git a/www/kojiweb/buildsbyuser.chtml b/www/kojiweb/buildsbyuser.chtml index 002f645..ad523fc 100644 --- a/www/kojiweb/buildsbyuser.chtml +++ b/www/kojiweb/buildsbyuser.chtml @@ -35,7 +35,7 @@ #if $len($userBuilds) > 0 #for $userBuild in $userBuilds - + diff --git a/www/kojiweb/buildtargetedit.chtml b/www/kojiweb/buildtargetedit.chtml index 647a87c..b024d36 100644 --- a/www/kojiweb/buildtargetedit.chtml +++ b/www/kojiweb/buildtargetedit.chtml @@ -3,7 +3,7 @@ #include "includes/header.chtml" #if $target -

Edit target $target.name

+

Edit target $util.escapeHTML($target.name)

#else

Create build target

#end if @@ -17,7 +17,7 @@ #if $target @@ -31,7 +31,7 @@ @@ -42,7 +42,7 @@ diff --git a/www/kojiweb/buildtargetinfo.chtml b/www/kojiweb/buildtargetinfo.chtml index 42a51f6..55b1053 100644 --- a/www/kojiweb/buildtargetinfo.chtml +++ b/www/kojiweb/buildtargetinfo.chtml @@ -2,20 +2,20 @@ #include "includes/header.chtml" -

Information for target $target.name

+

Information for target $util.escapeHTML($target.name)

$build.build_id $koji.buildLabel($build)$build.tag_name$util.escapeHTML($build.tag_name)$build.owner_name$util.escapeHTML($build.owner_name) $util.formatTime($build.completion_time)$util.stateImage($build.state)
$target.name$util.escapeHTML($target.name) graph row $target.builds
$userBuild.name$util.escapeHTML($userBuild.name) graph row $userBuild.builds
Name - +
- + - + - + #if 'admin' in $perms diff --git a/www/kojiweb/buildtargets.chtml b/www/kojiweb/buildtargets.chtml index ad02cbd..08eeab2 100644 --- a/www/kojiweb/buildtargets.chtml +++ b/www/kojiweb/buildtargets.chtml @@ -35,7 +35,7 @@ #for $target in $targets - + #end for #else diff --git a/www/kojiweb/channelinfo.chtml b/www/kojiweb/channelinfo.chtml index 2207365..63a2d47 100644 --- a/www/kojiweb/channelinfo.chtml +++ b/www/kojiweb/channelinfo.chtml @@ -2,7 +2,7 @@ #include "includes/header.chtml" -

Information for channel $channel.name

+

Information for channel $util.escapeHTML($channel.name)

Name$target.nameName$util.escapeHTML($target.name)
ID$target.id
Build Tag$buildTag.nameBuild Tag$util.escapeHTML($buildTag.name)
Destination Tag$destTag.nameDestination Tag$util.escapeHTML($destTag.name)
$target.id$target.name$util.escapeHTML($target.name)
@@ -39,7 +39,7 @@ #for $host in $hosts - + diff --git a/www/kojiweb/clusterhealth.chtml b/www/kojiweb/clusterhealth.chtml index 5f1ab61..be4862d 100644 --- a/www/kojiweb/clusterhealth.chtml +++ b/www/kojiweb/clusterhealth.chtml @@ -59,7 +59,7 @@ #for $channel in $channels
$host.name$util.escapeHTML($host.name) #if $host.enabled then $util.imageTag('yes') else $util.imageTag('no')# #if $host.ready then $util.imageTag('yes') else $util.imageTag('no')#
- $channel['name'] + $util.escapeHTML($channel['name']) #if not $channel['enabled_channel'] [disabled] #end if diff --git a/www/kojiweb/externalrepoinfo.chtml b/www/kojiweb/externalrepoinfo.chtml index 140331a..aa48ae6 100644 --- a/www/kojiweb/externalrepoinfo.chtml +++ b/www/kojiweb/externalrepoinfo.chtml @@ -2,24 +2,24 @@ #include "includes/header.chtml" -

Information for external repo $extRepo.name

+

Information for external repo $util.escapeHTML($extRepo.name)

- + - +
Name$extRepo.nameName$util.escapeHTML($extRepo.name)
ID$extRepo.id
URL$extRepo.urlURL$util.escapeHTML($extRepo.url)
Tags using this external repo #if $len($repoTags) #for $tag in $repoTags - $tag.tag_name
+ $util.escapeHTML($tag.tag_name)
#end for #else No tags diff --git a/www/kojiweb/fileinfo.chtml b/www/kojiweb/fileinfo.chtml index 4631c78..2a19b6b 100644 --- a/www/kojiweb/fileinfo.chtml +++ b/www/kojiweb/fileinfo.chtml @@ -4,14 +4,14 @@ #include "includes/header.chtml" #if $rpm -

Information for file $file.name

+

Information for file $util.escapeHTML($file.name)

#elif $archive -

Information for file $file.name

+

Information for file $util.escapeHTML($file.name)

#end if - + #if $rpm @@ -28,12 +28,12 @@ #end if #if 'user' in $file and $file.user - + #end if #if 'group' in $file and $file.group - + #end if #if 'mode' in $file and $file.mode @@ -56,7 +56,7 @@ #elif $archive - + #end if
Name$file.nameName$util.escapeHTML($file.name)
User$file.userUser$util.escapeHTML($file.user)
Group$file.groupGroup$util.escapeHTML($file.group)
Archive$archive.filenameArchive$util.escapeHTML($archive.filename)
diff --git a/www/kojiweb/hostedit.chtml b/www/kojiweb/hostedit.chtml index 00a64c9..bdb342d 100644 --- a/www/kojiweb/hostedit.chtml +++ b/www/kojiweb/hostedit.chtml @@ -2,14 +2,14 @@ #include "includes/header.chtml" -

Edit host $host.name

+

Edit host $util.escapeHTML($host.name)

$util.authToken($self, form=True) - + @@ -20,7 +20,7 @@ - + @@ -43,7 +43,7 @@ diff --git a/www/kojiweb/hostinfo.chtml b/www/kojiweb/hostinfo.chtml index 92d335b..883df3f 100644 --- a/www/kojiweb/hostinfo.chtml +++ b/www/kojiweb/hostinfo.chtml @@ -2,17 +2,17 @@ #include "includes/header.chtml" -

Information for host $host.name

+

Information for host $util.escapeHTML($host.name)

Name$host.name$util.escapeHTML($host.name)
ID
Arches
Capacity
- + - + @@ -51,7 +51,7 @@ #for $buildroot in $buildroots - + diff --git a/www/kojiweb/hosts.chtml b/www/kojiweb/hosts.chtml index 4eceb66..30cf775 100644 --- a/www/kojiweb/hosts.chtml +++ b/www/kojiweb/hosts.chtml @@ -58,7 +58,7 @@ in $channel channel @@ -120,11 +120,11 @@ in $channel channel #for $host in $hosts - + diff --git a/www/kojiweb/imageinfo.chtml b/www/kojiweb/imageinfo.chtml index d490435..4bbc849 100644 --- a/www/kojiweb/imageinfo.chtml +++ b/www/kojiweb/imageinfo.chtml @@ -5,23 +5,23 @@ #include "includes/header.chtml" -

Information for image $image.filename

+

Information for image $util.escapeHTML($image.filename)

Name$host.nameName$util.escapeHTML($host.name)
ID$host.id
Arches$host.archesArches$util.escapeHTML($host.arches)
Capacity$host.capacityChannels #for $channel in $channels - $channel.name
+ $util.escapeHTML($channel.name)
#end for #if not $channels No channels @@ -68,7 +68,7 @@
$buildroot.tag_name-$buildroot.id-$buildroot.repo_id$util.escapeHTML($buildroot.tag_name)-$buildroot.id-$buildroot.repo_id $util.formatTime($buildroot.create_event_time) $util.imageTag($util.brStateName($buildroot.state))
$host.id$host.name$util.escapeHTML($host.name) $host.arches #for $channame, $chan_id, $chan_enabled in zip($host.channels, $host.channels_id, $host.channels_enabled) - $channame + $util.escapeHTML($channame) #end for #if $host.enabled then $util.imageTag('yes') else $util.imageTag('no')#
- + - + - + #if $len($image.hash) == 32 @@ -42,7 +42,7 @@ - + diff --git a/www/kojiweb/index.chtml b/www/kojiweb/index.chtml index ab14d0c..9aab309 100644 --- a/www/kojiweb/index.chtml +++ b/www/kojiweb/index.chtml @@ -20,9 +20,9 @@ #set $stateName = $util.stateName($build.state) - + #if not $user - + #end if @@ -58,9 +58,9 @@ #if not $user #end if @@ -111,8 +111,8 @@ #for $package in $packages - - + + #set $included = $package.blocked and 'no' or 'yes' @@ -140,8 +140,8 @@ #for $notif in $notifs - - + + diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 8525c8f..2538cd4 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -43,11 +43,6 @@ def _sortbyname(x): return x['name'] -# regexps for input checking -_VALID_SEARCH_CHARS = r"""a-zA-Z0-9""" -_VALID_SEARCH_SYMS = r""" @.,_/\()%+-~*?|[]^$""" -_VALID_SEARCH_RE = re.compile('^[' + _VALID_SEARCH_CHARS + re.escape(_VALID_SEARCH_SYMS) + ']+$') - _VALID_ARCH_RE = re.compile(r'^[\w-]+$', re.ASCII) @@ -61,14 +56,12 @@ def _validate_arch(arch): raise koji.GenericError("No such arch: %r" % arch) -def _validate_name_or_id(value): - # integer ID or label, it is unicode alnum + search symbols (reasonable expectation?) +def _convert_if_int(value): + # if value is digit, converts value to integer, otherwise it returns raw value if value.isdigit(): return int(value) - elif _VALID_SEARCH_RE.match(value): - return value else: - raise koji.GenericError("Invalid int/label value: %r" % value) + return value # loggers @@ -540,7 +533,7 @@ def tasks(environ, owner=None, state='active', view='tree', method='all', hostID opts = {'decode': True} if owner: - owner = _validate_name_or_id(owner) + owner = _convert_if_int(owner) ownerObj = server.getUser(owner, strict=True) opts['owner'] = ownerObj['id'] values['owner'] = ownerObj['name'] @@ -598,7 +591,7 @@ def tasks(environ, owner=None, state='active', view='tree', method='all', hostID values['state'] = state if hostID: - hostID = int(hostID) + hostID = _convert_if_int(hostID) host = server.getHost(hostID, strict=True) opts['host_id'] = host['id'] values['host'] = host @@ -608,7 +601,7 @@ def tasks(environ, owner=None, state='active', view='tree', method='all', hostID values['hostID'] = None if channelID: - channelID = _validate_name_or_id(channelID) + channelID = _convert_if_int(channelID) channel = server.getChannel(channelID, strict=True) opts['channel_id'] = channel['id'] values['channel'] = channel @@ -916,13 +909,13 @@ def packages(environ, tagID=None, userID=None, order='package_name', start=None, server = _getServer(environ) tag = None if tagID is not None: - tagID = _validate_name_or_id(tagID) + tagID = _convert_if_int(tagID) tag = server.getTag(tagID, strict=True) values['tagID'] = tagID values['tag'] = tag user = None if userID is not None: - userID = _validate_name_or_id(userID) + userID = _convert_if_int(userID) user = server.getUser(userID, strict=True) values['userID'] = userID values['user'] = user @@ -952,7 +945,7 @@ def packageinfo(environ, packageID, tagOrder='name', tagStart=None, buildOrder=' values = _initValues(environ, 'Package Info', 'packages') server = _getServer(environ) - packageID = _validate_name_or_id(packageID) + packageID = _convert_if_int(packageID) package = server.getPackage(packageID) if package is None: raise koji.GenericError('No such package ID: %s' % packageID) @@ -976,7 +969,7 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N values = _initValues(environ, 'Tag Info', 'tags') server = _getServer(environ) - tagID = _validate_name_or_id(tagID) + tagID = _convert_if_int(tagID) tag = server.getTag(tagID, strict=True) values['title'] = tag['name'] + ' | Tag Info' @@ -1016,7 +1009,8 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N child = None if childID is not None: - child = server.getTag(int(childID), strict=True) + childID = _convert_if_int(childID) + child = server.getTag(childID, strict=True) values['child'] = child if environ['koji.currentUser']: @@ -1193,7 +1187,7 @@ def externalrepoinfo(environ, extrepoID): values = _initValues(environ, 'External Repo Info', 'tags') server = _getServer(environ) - extrepoID = _validate_name_or_id(extrepoID) + extrepoID = _convert_if_int(extrepoID) extRepo = server.getExternalRepo(extrepoID, strict=True) repoTags = server.getTagExternalRepos(repo_info=extRepo['id']) @@ -1349,7 +1343,7 @@ def builds(environ, userID=None, tagID=None, packageID=None, state=None, order=' user = None if userID: - userID = _validate_name_or_id(userID) + userID = _convert_if_int(userID) user = server.getUser(userID, strict=True) values['userID'] = userID values['user'] = user @@ -1361,14 +1355,14 @@ def builds(environ, userID=None, tagID=None, packageID=None, state=None, order=' tag = None if tagID: - tagID = _validate_name_or_id(tagID) + tagID = _convert_if_int(tagID) tag = server.getTag(tagID, strict=True) values['tagID'] = tagID values['tag'] = tag package = None if packageID: - packageID = _validate_name_or_id(packageID) + packageID = _convert_if_int(packageID) package = server.getPackage(packageID, strict=True) values['packageID'] = packageID values['package'] = package @@ -1454,13 +1448,14 @@ def userinfo(environ, userID, packageOrder='package_name', packageStart=None, values = _initValues(environ, 'User Info', 'users') server = _getServer(environ) - userID = _validate_name_or_id(userID) + userID = _convert_if_int(userID) user = server.getUser(userID, strict=True) values['title'] = user['name'] + ' | User Info' values['user'] = user values['userID'] = userID + values['owner'] = user['name'] values['taskCount'] = server.listTasks(opts={'owner': user['id'], 'parent': None}, queryOpts={'countOnly': True}) @@ -1708,12 +1703,12 @@ def hostinfo(environ, hostID=None, userID=None): server = _getServer(environ) if hostID: - hostID = _validate_name_or_id(hostID) + hostID = _convert_if_int(hostID) host = server.getHost(hostID) if host is None: raise koji.GenericError('No such host ID: %s' % hostID) elif userID: - userID = int(userID) + userID = _convert_if_int(userID) hosts = server.listHosts(userID=userID) host = None if hosts: @@ -2426,17 +2421,17 @@ def recentbuilds(environ, user=None, tag=None, package=None): tagObj = None if tag is not None: - tag = _validate_name_or_id(tag) + tag = _convert_if_int(tag) tagObj = server.getTag(tag, strict=True) userObj = None if user is not None: - user = _validate_name_or_id(user) + user = _convert_if_int(user) userObj = server.getUser(user, strict=True) packageObj = None if package: - package = _validate_name_or_id(package) + package = _convert_if_int(package) packageObj = server.getPackage(package, strict=True) if tagObj is not None: @@ -2532,13 +2527,6 @@ def search(environ, start=None, order=None): if match not in ('glob', 'regexp', 'exact'): raise koji.GenericError("No such match type: %r" % match) - if not _VALID_SEARCH_RE.match(terms): - values['error'] = 'Invalid search terms
' + \ - 'Search terms may contain only these characters: ' + \ - _VALID_SEARCH_CHARS + _VALID_SEARCH_SYMS - values['terms'] = '' - return _genHTML(environ, 'search.chtml') - if match == 'regexp': try: re.compile(terms) @@ -2617,8 +2605,8 @@ def repoinfo(environ, repoID): values = _initValues(environ, 'Repo Info', 'tags') server = _getServer(environ) - repoID = _validate_name_or_id(repoID) values['repo_id'] = repoID + repoID = _convert_if_int(repoID) repo_info = server.repoInfo(repoID, strict=False) values['repo'] = repo_info if repo_info: diff --git a/www/kojiweb/notificationedit.chtml b/www/kojiweb/notificationedit.chtml index 12aaf3d..b0fe3ba 100644 --- a/www/kojiweb/notificationedit.chtml +++ b/www/kojiweb/notificationedit.chtml @@ -20,7 +20,7 @@ @@ -31,7 +31,7 @@ diff --git a/www/kojiweb/packageinfo.chtml b/www/kojiweb/packageinfo.chtml index 0e3838e..7e885d1 100644 --- a/www/kojiweb/packageinfo.chtml +++ b/www/kojiweb/packageinfo.chtml @@ -2,11 +2,11 @@ #include "includes/header.chtml" -

Information for package $package.name

+

Information for package $util.escapeHTML($package.name)

ID$image.id
File Name$image.filenameFile Name$util.escapeHTML($image.filename)
File Size$util.formatNatural($image.filesize)
Arch$image.archArch$util.escapeHTML($image.arch)
Media Type$image.mediatypeMedia Type$util.escapeHTML($image.mediatype)
Task$koji.taskLabel($task)
Buildroot/var/lib/mock/$buildroot.tag_name-$buildroot.id-$buildroot.repo_idBuildroot$util.escapeHTML(/var/lib/mock/$buildroot.tag_name-$buildroot.id-$buildroot.repo_id)
Included RPMs
$build.build_id$build.nvr$util.escapeHTML($build.nvr)$build.owner_name$util.escapeHTML($build.owner_name)$util.formatTime($build.completion_ts) $util.stateImage($build.state) #if $task.owner_type == $koji.USERTYPES['HOST'] - $task.owner_name + $util.escapeHTML($task.owner_name) #else - $task.owner_name + $util.escapeHTML($task.owner_name) #end if
$package.package_name$package.tag_name$util.escapeHTML($package.package_name)$util.escapeHTML($package.tag_name)$util.imageTag($included)
#if $notif.package then $notif.package.name else 'all'##if $notif.tag then $notif.tag.name else 'all'##if $notif.package then $util.escapeHTML($notif.package.name) else 'all'##if $notif.tag then $util.escapeHTML($notif.tag.name) else 'all'# #if $notif.success_only then 'success only' else 'all'# edit delete
- + @@ -46,8 +46,8 @@ #for $build in $builds - - + + #set $stateName = $util.stateName($build.state) @@ -101,8 +101,8 @@ #for $tag in $tags - - + + #set $included = $tag.blocked and 'no' or 'yes' diff --git a/www/kojiweb/packages.chtml b/www/kojiweb/packages.chtml index 08cae84..238b13e 100644 --- a/www/kojiweb/packages.chtml +++ b/www/kojiweb/packages.chtml @@ -4,7 +4,7 @@ #include "includes/header.chtml" -

Packages#if $prefix then ' starting with "%s"' % $prefix else ''##if $tag then ' in tag %s' % ($tag.id, $tag.name) else ''##if $user then ' owned by %s' % ($user.id, $user.name) else ''#

+

Packages#if $prefix then ' starting with "%s"' % $prefix else ''##if $tag then ' in tag %s' % ($tag.id, $util.escapeHTML($tag.name)) else ''##if $user then ' owned by %s' % ($user.id, $util.escapeHTML($user.name)) else ''#

Name$package.nameName$util.escapeHTML($package.name)
ID$package.id
$build.nvr$build.owner_name$util.escapeHTML($build.nvr)$util.escapeHTML($build.owner_name) $util.formatTime($build.completion_ts)$util.stateImage($build.state)
$tag.name$tag.owner_name$util.escapeHTML($tag.name)$util.escapeHTML($tag.owner_name)$util.imageTag($included) $tag.extra_arches
#if $tag @@ -75,10 +75,10 @@ #for $package in $packages - + #if $tag or $user - - + + #end if diff --git a/www/kojiweb/packagesbyuser.chtml b/www/kojiweb/packagesbyuser.chtml index c4e5713..2c552fc 100644 --- a/www/kojiweb/packagesbyuser.chtml +++ b/www/kojiweb/packagesbyuser.chtml @@ -35,7 +35,7 @@ #if $len($users) > 0 #for $user in $users - + diff --git a/www/kojiweb/recentbuilds.chtml b/www/kojiweb/recentbuilds.chtml index 28e8eb5..48fdc6d 100644 --- a/www/kojiweb/recentbuilds.chtml +++ b/www/kojiweb/recentbuilds.chtml @@ -22,18 +22,18 @@ - $siteName: recent builds#if $package then ' of package ' + $package.name else ''##if $tag then ' into tag ' + $tag.name else ''##if $user then ' by user ' + $user.name else ''# + $siteName: recent builds#if $package then ' of package ' + $util.escapeHTML($package.name) else ''##if $tag then ' into tag ' + $util.escapeHTML($tag.name) else ''##if $user then ' by user ' + $util.escapeHTML($user.name) else ''# $linkURL() A list of the most recent builds #if $package - of package $package.name + of package $util.escapeHTML($package.name) #end if #if $tag - into tag $tag.name + into tag $util.escapeHTML($tag.name) #end if #if $user - by user $user.name + by user $util.escapeHTML($user.name) #end if in the $siteName Build System. The list is sorted in reverse chronological order by build completion time. diff --git a/www/kojiweb/repoinfo.chtml b/www/kojiweb/repoinfo.chtml index 3379559..6cb9d5a 100644 --- a/www/kojiweb/repoinfo.chtml +++ b/www/kojiweb/repoinfo.chtml @@ -8,12 +8,12 @@ #if $repo
$package.package_id$package.package_name$util.escapeHTML($package.package_name)$package.tag_name$package.owner_name$util.escapeHTML($package.tag_name)$util.escapeHTML($package.owner_name) #if $package.blocked then $util.imageTag('no') else $util.imageTag('yes')#
$user.name$util.escapeHTML($user.name) graph row $user.packages
- + #if $repo.task_id #end if #set $state = $util.repoState($repo.state) - + #if $repo.state != koji.REPO_STATES['DELETED'] diff --git a/www/kojiweb/rpminfo.chtml b/www/kojiweb/rpminfo.chtml index ea0210e..0240c60 100644 --- a/www/kojiweb/rpminfo.chtml +++ b/www/kojiweb/rpminfo.chtml @@ -8,7 +8,7 @@ #include "includes/header.chtml" #set $epoch = ($rpm.epoch != None and $str($rpm.epoch) + ':' or '') -

Information for RPM $rpm.name-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpm

+

Information for RPM $util.escapeHTML($rpm.name)-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpm

ID$repo.id
Tag$repo.tag_name
Tag$util.escapeHTML($repo.tag_name)
Task ID$repo.task_id
State$state
State$util.escapeHTML($state)
Event$repo.create_event ($util.formatTimeLong($repo.create_ts))
URLrepodata
@@ -16,9 +16,9 @@ #if $build - + #else - + #end if @@ -35,7 +35,7 @@ - + #if $rpm.external_repo_id == 0 @@ -55,7 +55,7 @@ #end if #if $rpm.external_repo_id - + #end if diff --git a/www/kojiweb/rpmlist.chtml b/www/kojiweb/rpmlist.chtml index 0e8e554..6ba83aa 100644 --- a/www/kojiweb/rpmlist.chtml +++ b/www/kojiweb/rpmlist.chtml @@ -23,7 +23,7 @@ colspan="2" #slurp #if $type == 'component'

Component RPMs of buildroot $util.brLabel($buildroot)

#elif $type == 'image' -

RPMs installed in $image.filename

+

RPMs installed in $util.escapeHTML($image.filename)

#else

RPMs built in buildroot $util.brLabel($buildroot)

#end if @@ -65,12 +65,12 @@ colspan="2" #slurp #for $rpm in $rpms #set $epoch = ($rpm.epoch != None and $str($rpm.epoch) + ':' or '') - + #if $type in ['component', 'image'] #if $rpm.external_repo_id == 0 #else - + #end if #end if #if $type == 'component' diff --git a/www/kojiweb/rpmsbyhost.chtml b/www/kojiweb/rpmsbyhost.chtml index 2c63368..4ce41cf 100644 --- a/www/kojiweb/rpmsbyhost.chtml +++ b/www/kojiweb/rpmsbyhost.chtml @@ -67,7 +67,7 @@ #if $len($hosts) > 0 #for $host in $hosts - + diff --git a/www/kojiweb/search.chtml b/www/kojiweb/search.chtml index d67e9ea..2ccae34 100644 --- a/www/kojiweb/search.chtml +++ b/www/kojiweb/search.chtml @@ -86,7 +86,7 @@ #for $result in $results - + #end for #else diff --git a/www/kojiweb/tagedit.chtml b/www/kojiweb/tagedit.chtml index 920e3a1..b2ad1e8 100644 --- a/www/kojiweb/tagedit.chtml +++ b/www/kojiweb/tagedit.chtml @@ -14,7 +14,7 @@ - + @@ -34,7 +34,7 @@ diff --git a/www/kojiweb/taginfo.chtml b/www/kojiweb/taginfo.chtml index a644cb9..7c182da 100644 --- a/www/kojiweb/taginfo.chtml +++ b/www/kojiweb/taginfo.chtml @@ -1,24 +1,25 @@ #from kojiweb import util +#from urllib.parse import quote #import pprint #include "includes/header.chtml" -

Information for tag $tag.name

+

Information for tag $util.escapeHTML($tag.name)

Name$rpm.nameName$util.escapeHTML($rpm.name)Name$rpm.nameName$util.escapeHTML($rpm.name)
Epoch$rpm.epoch
Arch$rpm.archArch$util.escapeHTML($rpm.arch)
External Repository$rpm.external_repo_nameExternal Repository$util.escapeHTML($rpm.external_repo_name)
$rpm.name-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpm$util.escapeHTML($rpm.name)-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpminternal$rpm.external_repo_name$util.escapeHTML($rpm.external_repo_name)
$host.name$util.escapeHTML($host.name) graph row $host.rpms
$result.id$result.name$util.escapeHTML($result.name)
Name - + #if $tag #end if @@ -22,7 +22,7 @@
Arches
Locked
#if $child and 'admin' in $perms - + #end if - + - + @@ -37,7 +38,7 @@ diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index f2956ba..3db7017 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -20,7 +20,7 @@ $util.imageTag($childState) - $koji.taskLabel($child) + $util.escapeHTML($koji.taskLabel($child)) $printChildren($child.id, $childMap) @@ -32,7 +32,7 @@ #include "includes/header.chtml" -

Information for task $koji.taskLabel($task)

+

Information for task $util.escapeHTML($koji.taskLabel($task))

Add $tag.name as parent of $child.nameAdd $util.escapeHTML($tag.name) as parent of $util.escapeHTML($child.name)
Name$tag.nameName$util.escapeHTML($tag.name)
ID$tag.id
Arches$tag.archesArches$util.escapeHTML($tag.arches)
Locked#if $tag.locked then 'yes' else 'no'#
Inheritance - $tag.name + $util.escapeHTML($tag.name) #set $numParents = $len($inheritance) #set $iter = 0 #set $maxDepth = 0 @@ -61,7 +62,7 @@ #silent $tagsByChild[$parent.child_id].pop() - $parent.name + $util.escapeHTML($parent.name) #if $depth == 1 and 'admin' in $perms (edit) (remove) #end if @@ -102,9 +103,9 @@ External repos #for $external_repo in $external_repos - $external_repo.external_repo_name [$external_repo.merge_mode] + $util.escapeHTML($external_repo.external_repo_name) [$external_repo.merge_mode] #if $external_repo.tag_id != $tag.id - (inherited from $external_repo.tag_name) + (inherited from $util.escapeHTML($external_repo.tag_name)) #end if
#end for @@ -136,7 +137,7 @@
#if $len($srcTargets) #for $target in $srcTargets - $target.name
+ $util.escapeHTML($target.name)
#end for #else No build targets @@ -148,7 +149,7 @@
#if $len($destTargets) #for $target in $destTargets - $target.name
+ $util.escapeHTML($target.name)
#end for #else No build targets diff --git a/www/kojiweb/tagparent.chtml b/www/kojiweb/tagparent.chtml index cd391f2..b38c05b 100644 --- a/www/kojiweb/tagparent.chtml +++ b/www/kojiweb/tagparent.chtml @@ -15,14 +15,14 @@
Tag Name - $tag.name + $util.escapeHTML($tag.name)
Parent Tag Name - $parent.name + $util.escapeHTML($parent.name)
@@ -67,13 +67,13 @@ #if $taskBuild - + #end if #if $taskBuilds #for $build in $taskBuilds - + #end for #end if @@ -116,9 +116,9 @@ @@ -127,7 +127,7 @@ @@ -135,12 +135,12 @@ - + #if $buildroots @@ -156,7 +156,7 @@ diff --git a/www/kojiweb/taskinfo_params.chtml b/www/kojiweb/taskinfo_params.chtml index f75f1c6..6cdd371 100644 --- a/www/kojiweb/taskinfo_params.chtml +++ b/www/kojiweb/taskinfo_params.chtml @@ -40,9 +40,9 @@ $value #if $len($params) > 1 Build Tag: #if $buildTag.id -$buildTag.name +$util.escapeHTML($buildTag.name) #else -$buildTag.name +$util.escapeHTML($buildTag.name) #end if
#end if @@ -55,9 +55,9 @@ $printOpts($params[2]) SRPM: $params[0]
Build Tag: #if $buildTag.id -$buildTag.name +$util.escapeHTML($buildTag.name) #else -$buildTag.name +$util.escapeHTML($buildTag.name) #end if
Arch: $params[2]
@@ -66,15 +66,15 @@ $buildTag.name $printOpts($params[4]) #end if #elif $task.method == 'tagBuild' -Destination Tag:$destTag.name
-Build:$koji.buildLabel($build) +Destination Tag:$util.escapeHTML($destTag.name)
+Build:$util.escapeHTML($koji.buildLabel($build)) #elif $task.method == 'buildNotification' #set $build = $params[1] #set $buildTarget = $params[2] Recipients: $printValue('', $params[0])
-Build:$koji.buildLabel($build)
+Build:$util.escapeHTML($koji.buildLabel($build))
#if $buildTarget -Build Target:$buildTarget.name
+Build Target:$util.escapeHTML($buildTarget.name)
#else Build Target: (no build target)
#end if @@ -83,32 +83,32 @@ $printOpts($params[4]) Recipients: $printValue('', $params[0])
Successful?: #if $params[1] then 'yes' else 'no'#
#if $destTag -Tagged Into:$destTag.name
+Tagged Into:$util.escapeHTML($destTag.name)
#end if #if $srcTag -#if $destTag then 'Moved From:' else 'Untagged From:'#$srcTag.name
+#if $destTag then 'Moved From:' else 'Untagged From:'#$util.escapeHTML($srcTag.name)
#end if -Build:$koji.buildLabel($build)
-#if $destTag then 'Tagged By:' else 'Untagged By:'#$user.name
+Build:$util.escapeHTML($koji.buildLabel($build))
+#if $destTag then 'Tagged By:' else 'Untagged By:'#$util.escapeHTML($user.name)
Ignore Success?: #if $params[6] then 'yes' else 'no'#
#if $params[7] Failure Message: $params[7] #end if #elif $task.method == 'build' Source: $params[0]
-Build Target:$params[1]
+Build Target:$util.escapeHTML($params[1])
$printOpts($params[2]) #elif $task.method == 'maven' -SCM URL: $params[0]
-Build Target:$params[1]
+SCM URL: $util.escapeHTML($params[0])
+Build Target:$util.escapeHTML($params[1])
$printOpts($params[2]) #elif $task.method == 'buildMaven' -SCM URL: $params[0]
+SCM URL: $util.escapeHTML($params[0])
Build Tag: #if $buildTag.id -$buildTag.name +$util.escapeHTML($buildTag.name) #else -$buildTag.name +$util.escapeHTML($buildTag.name) #end if
#if $len($params) > 2 @@ -120,13 +120,13 @@ $printOpts($params[2]) #set $buildTag = $buildTarget.name Build Tag: #if $buildTag.id -$buildTag.name +$util.escapeHTML($buildTag.name) #else -$buildTag.name +$util.escapeHTML($buildTag.name) #end if
#else -Build Target:$buildTarget.name
+Build Target:$util.escapeHTML($buildTarget.name)
#end if #if $params[2] Build:$koji.buildLabel($params[2])
@@ -144,53 +144,53 @@ $printOpts($params[4]) #end for
Build$koji.buildLabel($taskBuild)Build$util.escapeHTML($koji.buildLabel($taskBuild))
Build$koji.buildLabel($build)Build$util.escapeHTML($koji.buildLabel($build))
#if $owner #if $owner.usertype == $koji.USERTYPES['HOST'] - $owner.name + $util.escapeHTML($owner.name) #else - $owner.name + $util.escapeHTML($owner.name) #end if #end if Channel #if $task.channel_id - $channelName + $util.escapeHTML($channelName) #end if
Host #if $task.host_id - $hostName + $util.escapeHTML($hostName) #end if
Arch$task.archArch$util.escapeHTML($task.arch)
Parent #if $parent - $koji.taskLabel($parent) + $util.escapeHTML($koji.taskLabel($parent)) #end if
$key:$printMap($val)
-Build Target: $params[1]
+Build Target: $util.escapeHTML($params[1])
#if $len($params) > 2 $printOpts($params[2]) #end if #elif $task.method == 'livecd' or $task.method == 'appliance' or $task.method == 'livemedia' -Name: $params[0]
-Version: $params[1]
-Arch: $params[2]
-Build Target: $params[3]
-Kickstart File: $params[4]
+Name: $util.escapeHTML($params[0])
+Version: $util.escapeHTML($params[1])
+Arch: $util.escapeHTML($params[2])
+Build Target: $util.escapeHTML($params[3])
+Kickstart File: $util.escapeHTML($params[4])
$printOpts($params[5]) #elif $task.method == 'image' Arches: #echo ', '.join($params[2])#
-Build Target: $params[3]
+Build Target: $util.escapeHTML($params[3])
Installation Tree: $params[4]
$printOpts($params[5]) #elif $task.method == 'createLiveCD' or $task.method == 'createAppliance' or $task.method == 'createLiveMedia' #if $len($params) > 4: ## new method signature -Arch: $params[3]
-Kickstart File: $params[7]
+Arch: $util.escapeHTML($params[3])
+Kickstart File: $util.escapeHTML($params[7])
#if $len($params) > 8 $printOpts($params[8]) #end if #else ## old method signature -Arch: $params[0]
-Build Target: $params[1]
-Kickstart File: $params[2]
+Arch: $util.escapeHTML($params[0])
+Build Target: $util.escapeHTML($params[1])
+Kickstart File: $util.escapeHTML($params[2])
#if $len($params) > 3 $printOpts($params[3]) #end if #end if #elif $task.method == 'createImage' #set $target = $params[4] -Build Target: $target.name
-Install Tree: $params[7]
+Build Target: $util.escapeHTML($target.name)
+Install Tree: $util.escapeHTML($params[7])
$printOpts($params[8]) #elif $task.method == 'winbuild' -VM: $params[0]
-SCM URL: $params[1]
-Build Target: $params[2]
+VM: $util.escapeHTML($params[0])
+SCM URL: $util.escapeHTML($params[1])
+Build Target: $util.escapeHTML($params[2])
#if $len($params) > 3 $printOpts($params[3]) #end if #elif $task.method == 'vmExec' -VM: $params[0]
+VM: $util.escapeHTML($params[0])
Exec Params:
#for $info in $params[1] #if $isinstance($info, dict) @@ -203,20 +203,20 @@ $printMap($info, '    ') $printOpts($params[2]) #end if #elif $task.method == 'newRepo' -Tag: $tag.name
+Tag: $util.escapeHTML($tag.name)
#if $len($params) > 1 $printOpts($params[1]) #end if #elif $task.method == 'distRepo' -Tag: $tag.name
+Tag: $util.escapeHTML($tag.name)
Repo ID: $params[1]
Keys: $printValue(0, $params[2])
$printOpts($params[3]) #elif $task.method == 'prepRepo' -Tag: $params[0].name +Tag: $util.escapeHTML($params[0].name) #elif $task.method == 'createrepo' Repo ID: $params[0]
-Arch: $params[1]
+Arch: $util.escapeHTML($params[1])
#set $oldrepo = $params[2] #if $oldrepo Old Repo ID: $oldrepo.id
@@ -226,7 +226,7 @@ $printOpts($params[3]) External Repos: $printValue(None, [ext['external_repo_name'] for ext in $params[3]])
#end if #elif $task.method == 'createdistrepo' -Tag: $tag.name
+Tag: $util.escapeHTML($tag.name)
Repo ID: $params[1]
Arch: $printValue(0, $params[2])
Keys: $printValue(0, $params[3])
@@ -253,27 +253,27 @@ $printMap($subtask[2], '    ') #set $groupNum += 1   $groupNum: #echo ', '.join($urls)#
#end for -Build Target: $params[1]
+Build Target: $util.escapeHTML($params[1])
$printOpts($params[2]) #elif $task.method == 'waitrepo' -Build Tag: $params[0]
+Build Tag: $util.escapeHTML($params[0])
#if $params[1] -Newer Than: $params[1]
+Newer Than: $util.escapeHTML($params[1])
#end if #if $params[2] NVRs: $printValue('', $params[2]) #end if #elif $task.method == 'restart' -Host: $params[0].name
+Host: $util.escapeHTML($params[0].name)
#elif $task.method == 'restartVerify' -Host: $params[1].name
+Host: $util.escapeHTML($params[1].name)
Restart Task: $koji.taskLabel($rtask)
#elif $task.method == 'runroot' Build Tag: $params[0]
-Arch: $params[1]
+Arch: $util.escapeHTML($params[1])
$printOpts($params[3]) -Commands: $params[2]
+Commands: $util.escapeHTML($params[2])
#else $params #end if diff --git a/www/kojiweb/tasks.chtml b/www/kojiweb/tasks.chtml index 0482512..00833ba 100644 --- a/www/kojiweb/tasks.chtml +++ b/www/kojiweb/tasks.chtml @@ -16,7 +16,7 @@ #set $childState = $util.taskState($child.state) - $koji.taskLabel($child) + $util.escapeHTML($koji.taskLabel($child)) $printChildren($child.id, $childMap) @@ -40,7 +40,7 @@ All #include "includes/header.chtml" -

$headerPrefix($state) #if $view == 'toplevel' then 'toplevel' else ''# #if $method != 'all' then $method else ''# Tasks#if $ownerObj then ' owned by %s' % ($ownerObj.id, $ownerObj.name) else ''##if $host then ' on host %s' % ($host.id, $host.name) else ''# #if $channel then ' in channel %s' % ($channel.id, $channel.name) else ''#

+

$headerPrefix($state) #if $view == 'toplevel' then 'toplevel' else ''# #if $method != 'all' then $method else ''# Tasks#if $ownerObj then ' owned by %s' % ($ownerObj.id, $util.escapeHTML($ownerObj.name)) else ''##if $host then ' on host %s' % ($host.id, $util.escapeHTML($host.name)) else ''# #if $channel then ' in channel %s' % ($channel.id, $util.escapeHTML($channel.name)) else ''#

@@ -69,7 +69,7 @@ All #end if #for $user in $users - + #end for @@ -159,9 +159,9 @@ All diff --git a/www/kojiweb/tasksbyhost.chtml b/www/kojiweb/tasksbyhost.chtml index 4efa19e..337466f 100644 --- a/www/kojiweb/tasksbyhost.chtml +++ b/www/kojiweb/tasksbyhost.chtml @@ -51,7 +51,7 @@ #if $len($hosts) > 0 #for $host in $hosts - + diff --git a/www/kojiweb/tasksbyuser.chtml b/www/kojiweb/tasksbyuser.chtml index 843ea18..87620ec 100644 --- a/www/kojiweb/tasksbyuser.chtml +++ b/www/kojiweb/tasksbyuser.chtml @@ -35,7 +35,7 @@ #if $len($users) > 0 #for $user in $users - + diff --git a/www/kojiweb/userinfo.chtml b/www/kojiweb/userinfo.chtml index 88db1a3..a97d616 100644 --- a/www/kojiweb/userinfo.chtml +++ b/www/kojiweb/userinfo.chtml @@ -2,17 +2,17 @@ #include "includes/header.chtml" -

Information for user $user.name

+

Information for user $util.escapeHTML($user.name)

#if $task.owner_type == $koji.USERTYPES['HOST'] - $task.owner_name + $util.escapeHTML($task.owner_name) #else - $task.owner_name + $util.escapeHTML($task.owner_name) #end if $task.arch
$host.name$util.escapeHTML($host.name) graph row $host.tasks
$user.name$util.escapeHTML($user.name) graph row $user.tasks
- + - + @@ -47,8 +47,8 @@ #for $package in $packages - - + + #end for @@ -92,7 +92,7 @@ #for $build in $builds #set $stateName = $util.stateName($build.state) - + diff --git a/www/kojiweb/users.chtml b/www/kojiweb/users.chtml index e8cafec..340c0e8 100644 --- a/www/kojiweb/users.chtml +++ b/www/kojiweb/users.chtml @@ -1,4 +1,5 @@ #from kojiweb import util +#from urllib.parse import quote #include "includes/header.chtml" @@ -55,10 +56,10 @@ #for $user in $users - - - - + + + + #end for #else diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index b9f1f55..bad6566 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -25,6 +25,7 @@ import os import re import ssl import stat +import urllib import xmlrpc.client # a bunch of exception classes that explainError needs from socket import error as socket_error @@ -232,6 +233,11 @@ def passthrough(template, *vars): for var in vars: value = template.getVar(var, default=None) if value is not None: + if isinstance(value, str): + if value.isdigit(): + pass + else: + value = urllib.parse.quote(value) result.append('%s=%s' % (var, value)) if result: return '&' + '&'.join(result)
Name$user.nameName$util.escapeHTML($user.name)
ID$user.id
Tasks$taskCountTasks$taskCount
Packages
$package.package_name$package.tag_name$util.escapeHTML($package.package_name)$util.escapeHTML($package.tag_name) #if $package.blocked then $util.imageTag('no') else $util.imageTag('yes')#
$build.nvr$util.escapeHTML($build.nvr) $util.formatTime($build.completion_ts) $util.stateImage($build.state)
$user.id$user.nameviewviewview$util.escapeHTML($user.name)viewviewview