Merge branch 'rs-pick-security-into-master' into 'master'
Update master with 9.5.4 security patches See merge request !14131
This commit is contained in:
commit
bce1c50928
23 changed files with 311 additions and 165 deletions
|
@ -1 +1 @@
|
||||||
0.5.0
|
0.5.1
|
||||||
|
|
|
@ -111,11 +111,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
toggleFolder(folder, folderUrl) {
|
toggleFolder(folder) {
|
||||||
this.store.toggleFolder(folder);
|
this.store.toggleFolder(folder);
|
||||||
|
|
||||||
if (!folder.isOpen) {
|
if (!folder.isOpen) {
|
||||||
this.fetchChildEnvironments(folder, folderUrl, true);
|
this.fetchChildEnvironments(folder, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -143,10 +143,10 @@ export default {
|
||||||
.catch(this.errorCallback);
|
.catch(this.errorCallback);
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchChildEnvironments(folder, folderUrl, showLoader = false) {
|
fetchChildEnvironments(folder, showLoader = false) {
|
||||||
this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
|
this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
|
||||||
|
|
||||||
this.service.getFolderContent(folderUrl)
|
this.service.getFolderContent(folder.folder_path)
|
||||||
.then(resp => resp.json())
|
.then(resp => resp.json())
|
||||||
.then(response => this.store.setfolderContent(folder, response.environments))
|
.then(response => this.store.setfolderContent(folder, response.environments))
|
||||||
.then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
|
.then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
|
||||||
|
@ -173,12 +173,7 @@ export default {
|
||||||
// We need to verify if any folder is open to also update it
|
// We need to verify if any folder is open to also update it
|
||||||
const openFolders = this.store.getOpenFolders();
|
const openFolders = this.store.getOpenFolders();
|
||||||
if (openFolders.length) {
|
if (openFolders.length) {
|
||||||
openFolders.forEach((folder) => {
|
openFolders.forEach(folder => this.fetchChildEnvironments(folder));
|
||||||
// TODO - Move this to the backend
|
|
||||||
const folderUrl = `${window.location.pathname}/folders/${folder.folderName}`;
|
|
||||||
|
|
||||||
return this.fetchChildEnvironments(folder, folderUrl);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -410,20 +410,11 @@ export default {
|
||||||
this.hasStopAction ||
|
this.hasStopAction ||
|
||||||
this.canRetry;
|
this.canRetry;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs folder URL based on the current location and the folder id.
|
|
||||||
*
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
folderUrl() {
|
|
||||||
return `${window.location.pathname}/folders/${this.model.folderName}`;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onClickFolder() {
|
onClickFolder() {
|
||||||
eventHub.$emit('toggleFolder', this.model, this.folderUrl);
|
eventHub.$emit('toggleFolder', this.model);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1272,16 +1272,16 @@ export default class Notes {
|
||||||
`<li id="${uniqueId}" class="note being-posted fade-in-half timeline-entry">
|
`<li id="${uniqueId}" class="note being-posted fade-in-half timeline-entry">
|
||||||
<div class="timeline-entry-inner">
|
<div class="timeline-entry-inner">
|
||||||
<div class="timeline-icon">
|
<div class="timeline-icon">
|
||||||
<a href="/${currentUsername}">
|
<a href="/${_.escape(currentUsername)}">
|
||||||
<img class="avatar s40" src="${currentUserAvatar}" />
|
<img class="avatar s40" src="${currentUserAvatar}" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="timeline-content ${discussionClass}">
|
<div class="timeline-content ${discussionClass}">
|
||||||
<div class="note-header">
|
<div class="note-header">
|
||||||
<div class="note-header-info">
|
<div class="note-header-info">
|
||||||
<a href="/${currentUsername}">
|
<a href="/${_.escape(currentUsername)}">
|
||||||
<span class="hidden-xs">${currentUserFullname}</span>
|
<span class="hidden-xs">${_.escape(currentUsername)}</span>
|
||||||
<span class="note-headline-light">@${currentUsername}</span>
|
<span class="note-headline-light">${_.escape(currentUsername)}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1295,6 +1295,9 @@ export default class Notes {
|
||||||
</li>`
|
</li>`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$tempNote.find('.hidden-xs').text(_.escape(currentUserFullname));
|
||||||
|
$tempNote.find('.note-headline-light').text(`@${_.escape(currentUsername)}`);
|
||||||
|
|
||||||
return $tempNote;
|
return $tempNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ function UsersSelect(currentUser, els) {
|
||||||
|
|
||||||
if (currentUserInfo) {
|
if (currentUserInfo) {
|
||||||
input.value = currentUserInfo.id;
|
input.value = currentUserInfo.id;
|
||||||
input.dataset.meta = currentUserInfo.name;
|
input.dataset.meta = _.escape(currentUserInfo.name);
|
||||||
} else if (_this.currentUser) {
|
} else if (_this.currentUser) {
|
||||||
input.value = _this.currentUser.id;
|
input.value = _this.currentUser.id;
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ function UsersSelect(currentUser, els) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
$value.html(assigneeTemplate(user));
|
$value.html(assigneeTemplate(user));
|
||||||
$collapsedSidebar.attr('title', user.name).tooltip('fixTitle');
|
$collapsedSidebar.attr('title', _.escape(user.name)).tooltip('fixTitle');
|
||||||
return $collapsedSidebar.html(collapsedAssigneeTemplate(user));
|
return $collapsedSidebar.html(collapsedAssigneeTemplate(user));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -506,7 +506,7 @@ function UsersSelect(currentUser, els) {
|
||||||
|
|
||||||
img = "";
|
img = "";
|
||||||
if (user.beforeDivider != null) {
|
if (user.beforeDivider != null) {
|
||||||
`<li><a href='#' class='${selected === true ? 'is-active' : ''}'>${user.name}</a></li>`;
|
`<li><a href='#' class='${selected === true ? 'is-active' : ''}'>${_.escape(user.name)}</a></li>`;
|
||||||
} else {
|
} else {
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
|
img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
|
||||||
|
@ -518,7 +518,7 @@ function UsersSelect(currentUser, els) {
|
||||||
<a href='#' class='dropdown-menu-user-link ${selected === true ? 'is-active' : ''}'>
|
<a href='#' class='dropdown-menu-user-link ${selected === true ? 'is-active' : ''}'>
|
||||||
${img}
|
${img}
|
||||||
<strong class='dropdown-menu-user-full-name'>
|
<strong class='dropdown-menu-user-full-name'>
|
||||||
${user.name}
|
${_.escape(user.name)}
|
||||||
</strong>
|
</strong>
|
||||||
${username ? `<span class='dropdown-menu-user-username'>${username}</span>` : ''}
|
${username ? `<span class='dropdown-menu-user-username'>${username}</span>` : ''}
|
||||||
</a>
|
</a>
|
||||||
|
@ -643,11 +643,11 @@ UsersSelect.prototype.formatResult = function(user) {
|
||||||
} else {
|
} else {
|
||||||
avatar = gon.default_avatar_url;
|
avatar = gon.default_avatar_url;
|
||||||
}
|
}
|
||||||
return "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar avatar-inline s32' src='" + avatar + "'></div> <div class='user-name dropdown-menu-user-full-name'>" + user.name + "</div> <div class='user-username dropdown-menu-user-username'>" + (!user.invite ? "@" + _.escape(user.username) : "") + "</div> </div>";
|
return "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar avatar-inline s32' src='" + avatar + "'></div> <div class='user-name dropdown-menu-user-full-name'>" + _.escape(user.name) + "</div> <div class='user-username dropdown-menu-user-username'>" + (!user.invite ? "@" + _.escape(user.username) : "") + "</div> </div>";
|
||||||
};
|
};
|
||||||
|
|
||||||
UsersSelect.prototype.formatSelection = function(user) {
|
UsersSelect.prototype.formatSelection = function(user) {
|
||||||
return user.name;
|
return _.escape(user.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
UsersSelect.prototype.user = function(user_id, callback) {
|
UsersSelect.prototype.user = function(user_id, callback) {
|
||||||
|
|
|
@ -137,7 +137,7 @@ module CommitsHelper
|
||||||
|
|
||||||
text =
|
text =
|
||||||
if options[:avatar]
|
if options[:avatar]
|
||||||
%Q{<span class="commit-#{options[:source]}-name">#{person_name}</span>}
|
content_tag(:span, person_name, class: "commit-#{options[:source]}-name")
|
||||||
else
|
else
|
||||||
person_name
|
person_name
|
||||||
end
|
end
|
||||||
|
@ -148,9 +148,9 @@ module CommitsHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.nil?
|
if user.nil?
|
||||||
mail_to(source_email, text.html_safe, options)
|
mail_to(source_email, text, options)
|
||||||
else
|
else
|
||||||
link_to(text.html_safe, user_path(user), options)
|
link_to(text, user_path(user), options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -82,12 +82,7 @@ class Environment < ActiveRecord::Base
|
||||||
def set_environment_type
|
def set_environment_type
|
||||||
names = name.split('/')
|
names = name.split('/')
|
||||||
|
|
||||||
self.environment_type =
|
self.environment_type = names.many? ? names.first : nil
|
||||||
if names.many?
|
|
||||||
names.first
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def includes_commit?(commit)
|
def includes_commit?(commit)
|
||||||
|
@ -101,7 +96,7 @@ class Environment < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_merge_request_metrics?
|
def update_merge_request_metrics?
|
||||||
(environment_type || name) == "production"
|
folder_name == "production"
|
||||||
end
|
end
|
||||||
|
|
||||||
def first_deployment_for(commit)
|
def first_deployment_for(commit)
|
||||||
|
@ -223,6 +218,10 @@ class Environment < ActiveRecord::Base
|
||||||
format: :json)
|
format: :json)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def folder_name
|
||||||
|
self.environment_type || self.name
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Slugifying a name may remove the uniqueness guarantee afforded by it being
|
# Slugifying a name may remove the uniqueness guarantee afforded by it being
|
||||||
|
|
|
@ -26,5 +26,9 @@ class EnvironmentEntity < Grape::Entity
|
||||||
terminal_project_environment_path(environment.project, environment)
|
terminal_project_environment_path(environment.project, environment)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
expose :folder_path do |environment|
|
||||||
|
folder_project_environments_path(environment.project, environment.folder_name)
|
||||||
|
end
|
||||||
|
|
||||||
expose :created_at, :updated_at
|
expose :created_at, :updated_at
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,9 +36,9 @@ class EnvironmentSerializer < BaseSerializer
|
||||||
private
|
private
|
||||||
|
|
||||||
def itemize(resource)
|
def itemize(resource)
|
||||||
items = resource.order('folder_name ASC')
|
items = resource.order('folder ASC')
|
||||||
.group('COALESCE(environment_type, name)')
|
.group('COALESCE(environment_type, name)')
|
||||||
.select('COALESCE(environment_type, name) AS folder_name',
|
.select('COALESCE(environment_type, name) AS folder',
|
||||||
'COUNT(*) AS size', 'MAX(id) AS last_id')
|
'COUNT(*) AS size', 'MAX(id) AS last_id')
|
||||||
|
|
||||||
# It makes a difference when you call `paginate` method, because
|
# It makes a difference when you call `paginate` method, because
|
||||||
|
@ -49,7 +49,7 @@ class EnvironmentSerializer < BaseSerializer
|
||||||
environments = resource.where(id: items.map(&:last_id)).index_by(&:id)
|
environments = resource.where(id: items.map(&:last_id)).index_by(&:id)
|
||||||
|
|
||||||
items.map do |item|
|
items.map do |item|
|
||||||
Item.new(item.folder_name, item.size, environments[item.last_id])
|
Item.new(item.folder, item.size, environments[item.last_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,4 +6,4 @@
|
||||||
This Route Map is invalid:
|
This Route Map is invalid:
|
||||||
= viewer.validation_message
|
= viewer.validation_message
|
||||||
|
|
||||||
= link_to 'Learn more', help_page_path('ci/environments', anchor: 'route-map')
|
= link_to 'Learn more', help_page_path('ci/environments', anchor: 'go-directly-from-source-files-to-public-pages-on-the-environment')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
= icon('spinner spin fw')
|
= icon('spinner spin fw')
|
||||||
Validating Route Map…
|
Validating Route Map…
|
||||||
|
|
||||||
= link_to 'Learn more', help_page_path('ci/environments', anchor: 'route-map')
|
= link_to 'Learn more', help_page_path('ci/environments', anchor: 'go-directly-from-source-files-to-public-pages-on-the-environment')
|
||||||
|
|
|
@ -446,8 +446,7 @@ and/or `production`) you can see this information in the merge request itself.
|
||||||
|
|
||||||
![Environment URLs in merge request](img/environments_link_url_mr.png)
|
![Environment URLs in merge request](img/environments_link_url_mr.png)
|
||||||
|
|
||||||
### <a name="route-map"></a>Go directly from source files to public pages on the environment
|
### Go directly from source files to public pages on the environment
|
||||||
|
|
||||||
|
|
||||||
> Introduced in GitLab 8.17.
|
> Introduced in GitLab 8.17.
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ log_bin_trust_function_creators=1
|
||||||
|
|
||||||
### MySQL utf8mb4 support
|
### MySQL utf8mb4 support
|
||||||
|
|
||||||
After installation or upgrade, remember to [convert any new tables](#convert) to `utf8mb4`/`utf8mb4_general_ci`.
|
After installation or upgrade, remember to [convert any new tables](#tables-and-data-conversion-to-utf8mb4) to `utf8mb4`/`utf8mb4_general_ci`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -230,7 +230,6 @@ We need to check, enable and probably convert your existing GitLab DB tables to
|
||||||
> Now, ensure that [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) are **persisted** in your `my.cnf` file.
|
> Now, ensure that [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) are **persisted** in your `my.cnf` file.
|
||||||
|
|
||||||
#### Tables and data conversion to utf8mb4
|
#### Tables and data conversion to utf8mb4
|
||||||
<a name="convert"></a>
|
|
||||||
|
|
||||||
Now that you have a persistent MySQL setup, you can safely upgrade tables after setup or upgrade time:
|
Now that you have a persistent MySQL setup, you can safely upgrade tables after setup or upgrade time:
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ module Banzai
|
||||||
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
|
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
|
||||||
class SanitizationFilter < HTML::Pipeline::SanitizationFilter
|
class SanitizationFilter < HTML::Pipeline::SanitizationFilter
|
||||||
UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze
|
UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze
|
||||||
|
TABLE_ALIGNMENT_PATTERN = /text-align: (?<alignment>center|left|right)/
|
||||||
|
|
||||||
def whitelist
|
def whitelist
|
||||||
whitelist = super
|
whitelist = super
|
||||||
|
@ -24,7 +25,8 @@ module Banzai
|
||||||
# Only push these customizations once
|
# Only push these customizations once
|
||||||
return if customized?(whitelist[:transformers])
|
return if customized?(whitelist[:transformers])
|
||||||
|
|
||||||
# Allow table alignment
|
# Allow table alignment; we whitelist specific style properties in a
|
||||||
|
# transformer below
|
||||||
whitelist[:attributes]['th'] = %w(style)
|
whitelist[:attributes]['th'] = %w(style)
|
||||||
whitelist[:attributes]['td'] = %w(style)
|
whitelist[:attributes]['td'] = %w(style)
|
||||||
|
|
||||||
|
@ -43,6 +45,9 @@ module Banzai
|
||||||
whitelist[:elements].push('abbr')
|
whitelist[:elements].push('abbr')
|
||||||
whitelist[:attributes]['abbr'] = %w(title)
|
whitelist[:attributes]['abbr'] = %w(title)
|
||||||
|
|
||||||
|
# Disallow `name` attribute globally
|
||||||
|
whitelist[:attributes][:all].delete('name')
|
||||||
|
|
||||||
# Allow any protocol in `a` elements...
|
# Allow any protocol in `a` elements...
|
||||||
whitelist[:protocols].delete('a')
|
whitelist[:protocols].delete('a')
|
||||||
|
|
||||||
|
@ -52,6 +57,9 @@ module Banzai
|
||||||
# Remove `rel` attribute from `a` elements
|
# Remove `rel` attribute from `a` elements
|
||||||
whitelist[:transformers].push(self.class.remove_rel)
|
whitelist[:transformers].push(self.class.remove_rel)
|
||||||
|
|
||||||
|
# Remove any `style` properties not required for table alignment
|
||||||
|
whitelist[:transformers].push(self.class.remove_unsafe_table_style)
|
||||||
|
|
||||||
whitelist
|
whitelist
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -81,6 +89,21 @@ module Banzai
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remove_unsafe_table_style
|
||||||
|
lambda do |env|
|
||||||
|
node = env[:node]
|
||||||
|
|
||||||
|
return unless node.name == 'th' || node.name == 'td'
|
||||||
|
return unless node.has_attribute?('style')
|
||||||
|
|
||||||
|
if node['style'] =~ TABLE_ALIGNMENT_PATTERN
|
||||||
|
node['style'] = "text-align: #{$~[:alignment]}"
|
||||||
|
else
|
||||||
|
node.remove_attribute('style')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
module Gitlab
|
module Gitlab
|
||||||
module Middleware
|
module Middleware
|
||||||
class Go
|
class Go
|
||||||
|
include ActionView::Helpers::TagHelper
|
||||||
|
|
||||||
|
PROJECT_PATH_REGEX = %r{\A(#{Gitlab::PathRegex.full_namespace_route_regex}/#{Gitlab::PathRegex.project_route_regex})/}.freeze
|
||||||
|
|
||||||
def initialize(app)
|
def initialize(app)
|
||||||
@app = app
|
@app = app
|
||||||
end
|
end
|
||||||
|
@ -10,17 +14,20 @@ module Gitlab
|
||||||
def call(env)
|
def call(env)
|
||||||
request = Rack::Request.new(env)
|
request = Rack::Request.new(env)
|
||||||
|
|
||||||
if go_request?(request)
|
render_go_doc(request) || @app.call(env)
|
||||||
render_go_doc(request)
|
|
||||||
else
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render_go_doc(request)
|
def render_go_doc(request)
|
||||||
body = go_body(request)
|
return unless go_request?(request)
|
||||||
|
|
||||||
|
path = project_path(request)
|
||||||
|
return unless path
|
||||||
|
|
||||||
|
body = go_body(path)
|
||||||
|
return unless body
|
||||||
|
|
||||||
response = Rack::Response.new(body, 200, { 'Content-Type' => 'text/html' })
|
response = Rack::Response.new(body, 200, { 'Content-Type' => 'text/html' })
|
||||||
response.finish
|
response.finish
|
||||||
end
|
end
|
||||||
|
@ -29,11 +36,13 @@ module Gitlab
|
||||||
request["go-get"].to_i == 1 && request.env["PATH_INFO"].present?
|
request["go-get"].to_i == 1 && request.env["PATH_INFO"].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def go_body(request)
|
def go_body(path)
|
||||||
project_url = URI.join(Gitlab.config.gitlab.url, project_path(request))
|
project_url = URI.join(Gitlab.config.gitlab.url, path)
|
||||||
import_prefix = strip_url(project_url.to_s)
|
import_prefix = strip_url(project_url.to_s)
|
||||||
|
|
||||||
"<!DOCTYPE html><html><head><meta content='#{import_prefix} git #{project_url}.git' name='go-import'></head></html>\n"
|
meta_tag = tag :meta, name: 'go-import', content: "#{import_prefix} git #{project_url}.git"
|
||||||
|
head_tag = content_tag :head, meta_tag
|
||||||
|
content_tag :html, head_tag
|
||||||
end
|
end
|
||||||
|
|
||||||
def strip_url(url)
|
def strip_url(url)
|
||||||
|
@ -44,6 +53,10 @@ module Gitlab
|
||||||
path_info = request.env["PATH_INFO"]
|
path_info = request.env["PATH_INFO"]
|
||||||
path_info.sub!(/^\//, '')
|
path_info.sub!(/^\//, '')
|
||||||
|
|
||||||
|
project_path_match = "#{path_info}/".match(PROJECT_PATH_REGEX)
|
||||||
|
return unless project_path_match
|
||||||
|
path = project_path_match[1]
|
||||||
|
|
||||||
# Go subpackages may be in the form of `namespace/project/path1/path2/../pathN`.
|
# Go subpackages may be in the form of `namespace/project/path1/path2/../pathN`.
|
||||||
# In a traditional project with a single namespace, this would denote repo
|
# In a traditional project with a single namespace, this would denote repo
|
||||||
# `namespace/project` with subpath `path1/path2/../pathN`, but with nested
|
# `namespace/project` with subpath `path1/path2/../pathN`, but with nested
|
||||||
|
@ -51,7 +64,7 @@ module Gitlab
|
||||||
# `path2/../pathN`, for example.
|
# `path2/../pathN`, for example.
|
||||||
|
|
||||||
# We find all potential project paths out of the path segments
|
# We find all potential project paths out of the path segments
|
||||||
path_segments = path_info.split('/')
|
path_segments = path.split('/')
|
||||||
simple_project_path = path_segments.first(2).join('/')
|
simple_project_path = path_segments.first(2).join('/')
|
||||||
|
|
||||||
# If the path is at most 2 segments long, it is a simple `namespace/project` path and we're done
|
# If the path is at most 2 segments long, it is a simple `namespace/project` path and we're done
|
||||||
|
|
|
@ -288,8 +288,6 @@ describe 'Copy as GFM', js: true do
|
||||||
'SanitizationFilter',
|
'SanitizationFilter',
|
||||||
|
|
||||||
<<-GFM.strip_heredoc
|
<<-GFM.strip_heredoc
|
||||||
<a name="named-anchor"></a>
|
|
||||||
|
|
||||||
<sub>sub</sub>
|
<sub>sub</sub>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
|
|
@ -10,26 +10,23 @@ feature 'Environments page', :js do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
given!(:environment) { }
|
|
||||||
given!(:deployment) { }
|
|
||||||
given!(:action) { }
|
|
||||||
|
|
||||||
before do
|
|
||||||
visit_environments(project)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'page tabs' do
|
describe 'page tabs' do
|
||||||
scenario 'shows "Available" and "Stopped" tab with links' do
|
it 'shows "Available" and "Stopped" tab with links' do
|
||||||
|
visit_environments(project)
|
||||||
|
|
||||||
expect(page).to have_link('Available')
|
expect(page).to have_link('Available')
|
||||||
expect(page).to have_link('Stopped')
|
expect(page).to have_link('Stopped')
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with one available environment' do
|
describe 'with one available environment' do
|
||||||
given(:environment) { create(:environment, project: project, state: :available) }
|
before do
|
||||||
|
create(:environment, project: project, state: :available)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'in available tab page' do
|
describe 'in available tab page' do
|
||||||
it 'should show one environment' do
|
it 'should show one environment' do
|
||||||
visit project_environments_path(project, scope: 'available')
|
visit_environments(project, scope: 'available')
|
||||||
|
|
||||||
expect(page).to have_css('.environments-container')
|
expect(page).to have_css('.environments-container')
|
||||||
expect(page.all('.environment-name').length).to eq(1)
|
expect(page.all('.environment-name').length).to eq(1)
|
||||||
end
|
end
|
||||||
|
@ -37,7 +34,8 @@ feature 'Environments page', :js do
|
||||||
|
|
||||||
describe 'in stopped tab page' do
|
describe 'in stopped tab page' do
|
||||||
it 'should show no environments' do
|
it 'should show no environments' do
|
||||||
visit project_environments_path(project, scope: 'stopped')
|
visit_environments(project, scope: 'stopped')
|
||||||
|
|
||||||
expect(page).to have_css('.environments-container')
|
expect(page).to have_css('.environments-container')
|
||||||
expect(page).to have_content('You don\'t have any environments right now')
|
expect(page).to have_content('You don\'t have any environments right now')
|
||||||
end
|
end
|
||||||
|
@ -45,11 +43,14 @@ feature 'Environments page', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with one stopped environment' do
|
describe 'with one stopped environment' do
|
||||||
given(:environment) { create(:environment, project: project, state: :stopped) }
|
before do
|
||||||
|
create(:environment, project: project, state: :stopped)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'in available tab page' do
|
describe 'in available tab page' do
|
||||||
it 'should show no environments' do
|
it 'should show no environments' do
|
||||||
visit project_environments_path(project, scope: 'available')
|
visit_environments(project, scope: 'available')
|
||||||
|
|
||||||
expect(page).to have_css('.environments-container')
|
expect(page).to have_css('.environments-container')
|
||||||
expect(page).to have_content('You don\'t have any environments right now')
|
expect(page).to have_content('You don\'t have any environments right now')
|
||||||
end
|
end
|
||||||
|
@ -57,7 +58,8 @@ feature 'Environments page', :js do
|
||||||
|
|
||||||
describe 'in stopped tab page' do
|
describe 'in stopped tab page' do
|
||||||
it 'should show one environment' do
|
it 'should show one environment' do
|
||||||
visit project_environments_path(project, scope: 'stopped')
|
visit_environments(project, scope: 'stopped')
|
||||||
|
|
||||||
expect(page).to have_css('.environments-container')
|
expect(page).to have_css('.environments-container')
|
||||||
expect(page.all('.environment-name').length).to eq(1)
|
expect(page.all('.environment-name').length).to eq(1)
|
||||||
end
|
end
|
||||||
|
@ -66,86 +68,84 @@ feature 'Environments page', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'without environments' do
|
context 'without environments' do
|
||||||
scenario 'does show no environments' do
|
before do
|
||||||
expect(page).to have_content('You don\'t have any environments right now.')
|
visit_environments(project)
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does show 0 as counter for environments in both tabs' do
|
it 'does not show environments and counters are set to zero' do
|
||||||
|
expect(page).to have_content('You don\'t have any environments right now.')
|
||||||
|
|
||||||
expect(page.find('.js-available-environments-count').text).to eq('0')
|
expect(page.find('.js-available-environments-count').text).to eq('0')
|
||||||
expect(page.find('.js-stopped-environments-count').text).to eq('0')
|
expect(page.find('.js-stopped-environments-count').text).to eq('0')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when showing the environment' do
|
describe 'environments table' do
|
||||||
given(:environment) { create(:environment, project: project) }
|
given!(:environment) do
|
||||||
|
create(:environment, project: project, state: :available)
|
||||||
scenario 'does show environment name' do
|
|
||||||
expect(page).to have_link(environment.name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does show number of available and stopped environments' do
|
context 'when there are no deployments' do
|
||||||
expect(page.find('.js-available-environments-count').text).to eq('1')
|
before do
|
||||||
expect(page.find('.js-stopped-environments-count').text).to eq('0')
|
visit_environments(project)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'without deployments' do
|
it 'shows environments names and counters' do
|
||||||
scenario 'does show no deployments' do
|
expect(page).to have_link(environment.name)
|
||||||
|
|
||||||
|
expect(page.find('.js-available-environments-count').text).to eq('1')
|
||||||
|
expect(page.find('.js-stopped-environments-count').text).to eq('0')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not show deployments' do
|
||||||
expect(page).to have_content('No deployments yet')
|
expect(page).to have_content('No deployments yet')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for available environment' do
|
it 'does not show stip button when environment is not stoppable' do
|
||||||
given(:environment) { create(:environment, project: project, state: :available) }
|
expect(page).not_to have_selector('.stop-env-link')
|
||||||
|
|
||||||
scenario 'does not shows stop button' do
|
|
||||||
expect(page).not_to have_selector('.stop-env-link')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for stopped environment' do
|
|
||||||
given(:environment) { create(:environment, project: project, state: :stopped) }
|
|
||||||
|
|
||||||
scenario 'does not shows stop button' do
|
|
||||||
expect(page).not_to have_selector('.stop-env-link')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with deployments' do
|
context 'when there are deployments' do
|
||||||
given(:project) { create(:project, :repository) }
|
given(:project) { create(:project, :repository) }
|
||||||
|
|
||||||
given(:deployment) do
|
given!(:deployment) do
|
||||||
create(:deployment, environment: environment,
|
create(:deployment, environment: environment,
|
||||||
sha: project.commit.id)
|
sha: project.commit.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does show deployment SHA' do
|
it 'shows deployment SHA and internal ID' do
|
||||||
expect(page).to have_link(deployment.short_sha)
|
visit_environments(project)
|
||||||
end
|
|
||||||
|
|
||||||
scenario 'does show deployment internal id' do
|
expect(page).to have_link(deployment.short_sha)
|
||||||
expect(page).to have_content(deployment.iid)
|
expect(page).to have_content(deployment.iid)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with build and manual actions' do
|
context 'when builds and manual actions are present' do
|
||||||
given(:pipeline) { create(:ci_pipeline, project: project) }
|
given!(:pipeline) { create(:ci_pipeline, project: project) }
|
||||||
given(:build) { create(:ci_build, pipeline: pipeline) }
|
given!(:build) { create(:ci_build, pipeline: pipeline) }
|
||||||
|
|
||||||
given(:action) do
|
given!(:action) do
|
||||||
create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production')
|
create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production')
|
||||||
end
|
end
|
||||||
|
|
||||||
given(:deployment) do
|
given!(:deployment) do
|
||||||
create(:deployment, environment: environment,
|
create(:deployment, environment: environment,
|
||||||
deployable: build,
|
deployable: build,
|
||||||
sha: project.commit.id)
|
sha: project.commit.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does show a play button' do
|
before do
|
||||||
|
visit_environments(project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows a play button' do
|
||||||
find('.js-dropdown-play-icon-container').click
|
find('.js-dropdown-play-icon-container').click
|
||||||
|
|
||||||
expect(page).to have_content(action.name.humanize)
|
expect(page).to have_content(action.name.humanize)
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does allow to play manual action', js: true do
|
it 'allows to play a manual action', js: true do
|
||||||
expect(action).to be_manual
|
expect(action).to be_manual
|
||||||
|
|
||||||
find('.js-dropdown-play-icon-container').click
|
find('.js-dropdown-play-icon-container').click
|
||||||
|
@ -155,19 +155,19 @@ feature 'Environments page', :js do
|
||||||
.not_to change { Ci::Pipeline.count }
|
.not_to change { Ci::Pipeline.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does show build name and id' do
|
it 'shows build name and id' do
|
||||||
expect(page).to have_link("#{build.name} ##{build.id}")
|
expect(page).to have_link("#{build.name} ##{build.id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does not show stop button' do
|
it 'shows a stop button' do
|
||||||
expect(page).not_to have_selector('.stop-env-link')
|
expect(page).not_to have_selector('.stop-env-link')
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does not show external link button' do
|
it 'does not show external link button' do
|
||||||
expect(page).not_to have_css('external-url')
|
expect(page).not_to have_css('external-url')
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does not show terminal button' do
|
it 'does not show terminal button' do
|
||||||
expect(page).not_to have_terminal_button
|
expect(page).not_to have_terminal_button
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ feature 'Environments page', :js do
|
||||||
given(:build) { create(:ci_build, pipeline: pipeline) }
|
given(:build) { create(:ci_build, pipeline: pipeline) }
|
||||||
given(:deployment) { create(:deployment, environment: environment, deployable: build) }
|
given(:deployment) { create(:deployment, environment: environment, deployable: build) }
|
||||||
|
|
||||||
scenario 'does show an external link button' do
|
it 'shows an external link button' do
|
||||||
expect(page).to have_link(nil, href: environment.external_url)
|
expect(page).to have_link(nil, href: environment.external_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -192,34 +192,34 @@ feature 'Environments page', :js do
|
||||||
on_stop: 'close_app')
|
on_stop: 'close_app')
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does show stop button' do
|
it 'shows a stop button' do
|
||||||
expect(page).to have_selector('.stop-env-link')
|
expect(page).to have_selector('.stop-env-link')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for reporter' do
|
context 'when user is a reporter' do
|
||||||
let(:role) { :reporter }
|
let(:role) { :reporter }
|
||||||
|
|
||||||
scenario 'does not show stop button' do
|
it 'does not show stop button' do
|
||||||
expect(page).not_to have_selector('.stop-env-link')
|
expect(page).not_to have_selector('.stop-env-link')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with terminal' do
|
context 'when kubernetes terminal is available' do
|
||||||
let(:project) { create(:kubernetes_project, :test_repo) }
|
let(:project) { create(:kubernetes_project, :test_repo) }
|
||||||
|
|
||||||
context 'for project master' do
|
context 'for project master' do
|
||||||
let(:role) { :master }
|
let(:role) { :master }
|
||||||
|
|
||||||
scenario 'it shows the terminal button' do
|
it 'shows the terminal button' do
|
||||||
expect(page).to have_terminal_button
|
expect(page).to have_terminal_button
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for developer' do
|
context 'when user is a developer' do
|
||||||
let(:role) { :developer }
|
let(:role) { :developer }
|
||||||
|
|
||||||
scenario 'does not show terminal button' do
|
it 'does not show terminal button' do
|
||||||
expect(page).not_to have_terminal_button
|
expect(page).not_to have_terminal_button
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -228,59 +228,77 @@ feature 'Environments page', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'does have a New environment button' do
|
it 'does have a new environment button' do
|
||||||
|
visit_environments(project)
|
||||||
|
|
||||||
expect(page).to have_link('New environment')
|
expect(page).to have_link('New environment')
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when creating a new environment' do
|
describe 'creating a new environment' do
|
||||||
before do
|
before do
|
||||||
visit_environments(project)
|
visit_environments(project)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when logged as developer' do
|
context 'user is a developer' do
|
||||||
before do
|
given(:role) { :developer }
|
||||||
within(".top-area") do
|
|
||||||
click_link 'New environment'
|
scenario 'developer creates a new environment with a valid name' do
|
||||||
end
|
within(".top-area") { click_link 'New environment' }
|
||||||
|
fill_in('Name', with: 'production')
|
||||||
|
click_on 'Save'
|
||||||
|
|
||||||
|
expect(page).to have_content('production')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for valid name' do
|
scenario 'developer creates a new environmetn with invalid name' do
|
||||||
before do
|
within(".top-area") { click_link 'New environment' }
|
||||||
fill_in('Name', with: 'production')
|
fill_in('Name', with: 'name,with,commas')
|
||||||
click_on 'Save'
|
click_on 'Save'
|
||||||
end
|
|
||||||
|
|
||||||
scenario 'does create a new pipeline' do
|
expect(page).to have_content('Name can contain only letters')
|
||||||
expect(page).to have_content('production')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for invalid name' do
|
|
||||||
before do
|
|
||||||
fill_in('Name', with: 'name,with,commas')
|
|
||||||
click_on 'Save'
|
|
||||||
end
|
|
||||||
|
|
||||||
scenario 'does show errors' do
|
|
||||||
expect(page).to have_content('Name can contain only letters')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when logged as reporter' do
|
context 'user is a reporter' do
|
||||||
given(:role) { :reporter }
|
given(:role) { :reporter }
|
||||||
|
|
||||||
scenario 'does not have a New environment link' do
|
scenario 'reporters tries to create a new environment' do
|
||||||
expect(page).not_to have_link('New environment')
|
expect(page).not_to have_link('New environment')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'environments folders' do
|
||||||
|
before do
|
||||||
|
create(:environment, project: project,
|
||||||
|
name: 'staging/review-1',
|
||||||
|
state: :available)
|
||||||
|
create(:environment, project: project,
|
||||||
|
name: 'staging/review-2',
|
||||||
|
state: :available)
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'users unfurls an environment folder' do
|
||||||
|
visit_environments(project)
|
||||||
|
|
||||||
|
expect(page).not_to have_content 'review-1'
|
||||||
|
expect(page).not_to have_content 'review-2'
|
||||||
|
expect(page).to have_content 'staging 2'
|
||||||
|
|
||||||
|
within('.folder-row') do
|
||||||
|
find('.folder-name', text: 'staging').click
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).to have_content 'review-1'
|
||||||
|
expect(page).to have_content 'review-2'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def have_terminal_button
|
def have_terminal_button
|
||||||
have_link(nil, href: terminal_project_environment_path(project, environment))
|
have_link(nil, href: terminal_project_environment_path(project, environment))
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_environments(project)
|
def visit_environments(project, **opts)
|
||||||
visit project_environments_path(project)
|
visit project_environments_path(project, **opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,17 @@ describe CommitsHelper do
|
||||||
expect(helper.commit_author_link(commit))
|
expect(helper.commit_author_link(commit))
|
||||||
.not_to include('onmouseover="alert(1)"')
|
.not_to include('onmouseover="alert(1)"')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'escapes the author name' do
|
||||||
|
user = build_stubbed(:user, name: 'Foo <script>alert("XSS")</script>')
|
||||||
|
|
||||||
|
commit = double(author: user, author_name: '', author_email: '')
|
||||||
|
|
||||||
|
expect(helper.commit_author_link(commit))
|
||||||
|
.to include('Foo <script>')
|
||||||
|
expect(helper.commit_author_link(commit, avatar: true))
|
||||||
|
.to include('commit-author-name', 'Foo <script>')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'commit_committer_link' do
|
describe 'commit_committer_link' do
|
||||||
|
@ -25,6 +36,17 @@ describe CommitsHelper do
|
||||||
expect(helper.commit_committer_link(commit))
|
expect(helper.commit_committer_link(commit))
|
||||||
.not_to include('onmouseover="alert(1)"')
|
.not_to include('onmouseover="alert(1)"')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'escapes the commiter name' do
|
||||||
|
user = build_stubbed(:user, name: 'Foo <script>alert("XSS")</script>')
|
||||||
|
|
||||||
|
commit = double(committer: user, committer_name: '', committer_email: '')
|
||||||
|
|
||||||
|
expect(helper.commit_committer_link(commit))
|
||||||
|
.to include('Foo <script>')
|
||||||
|
expect(helper.commit_committer_link(commit, avatar: true))
|
||||||
|
.to include('commit-committer-name', 'Foo <script>')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#view_on_environment_button' do
|
describe '#view_on_environment_button' do
|
||||||
|
|
|
@ -770,6 +770,20 @@ import '~/notes';
|
||||||
expect($tempNote.prop('nodeName')).toEqual('LI');
|
expect($tempNote.prop('nodeName')).toEqual('LI');
|
||||||
expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy();
|
expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return a escaped user name', () => {
|
||||||
|
const currentUserFullnameXSS = 'Foo <script>alert("XSS")</script>';
|
||||||
|
const $tempNote = this.notes.createPlaceholderNote({
|
||||||
|
formContent: sampleComment,
|
||||||
|
uniqueId,
|
||||||
|
isDiscussionNote: false,
|
||||||
|
currentUsername,
|
||||||
|
currentUserFullname: currentUserFullnameXSS,
|
||||||
|
currentUserAvatar,
|
||||||
|
});
|
||||||
|
const $tempNoteHeader = $tempNote.find('.note-header');
|
||||||
|
expect($tempNoteHeader.find('.hidden-xs').text().trim()).toEqual('Foo <script>alert("XSS")</script>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createPlaceholderSystemNote', () => {
|
describe('createPlaceholderSystemNote', () => {
|
||||||
|
|
|
@ -49,7 +49,7 @@ describe Banzai::Filter::SanitizationFilter do
|
||||||
instance = described_class.new('Foo')
|
instance = described_class.new('Foo')
|
||||||
3.times { instance.whitelist }
|
3.times { instance.whitelist }
|
||||||
|
|
||||||
expect(instance.whitelist[:transformers].size).to eq 4
|
expect(instance.whitelist[:transformers].size).to eq 5
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sanitizes `class` attribute from all elements' do
|
it 'sanitizes `class` attribute from all elements' do
|
||||||
|
@ -63,8 +63,8 @@ describe Banzai::Filter::SanitizationFilter do
|
||||||
expect(filter(act).to_html).to eq %q{<span>def</span>}
|
expect(filter(act).to_html).to eq %q{<span>def</span>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows `style` attribute on table elements' do
|
it 'allows `text-align` property in `style` attribute on table elements' do
|
||||||
html = <<-HTML.strip_heredoc
|
html = <<~HTML
|
||||||
<table>
|
<table>
|
||||||
<tr><th style="text-align: center">Head</th></tr>
|
<tr><th style="text-align: center">Head</th></tr>
|
||||||
<tr><td style="text-align: right">Body</th></tr>
|
<tr><td style="text-align: right">Body</th></tr>
|
||||||
|
@ -77,6 +77,20 @@ describe Banzai::Filter::SanitizationFilter do
|
||||||
expect(doc.at_css('td')['style']).to eq 'text-align: right'
|
expect(doc.at_css('td')['style']).to eq 'text-align: right'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'disallows other properties in `style` attribute on table elements' do
|
||||||
|
html = <<~HTML
|
||||||
|
<table>
|
||||||
|
<tr><th style="text-align: foo">Head</th></tr>
|
||||||
|
<tr><td style="position: fixed; height: 50px; width: 50px; background: red; z-index: 999; font-size: 36px; text-align: center">Body</th></tr>
|
||||||
|
</table>
|
||||||
|
HTML
|
||||||
|
|
||||||
|
doc = filter(html)
|
||||||
|
|
||||||
|
expect(doc.at_css('th')['style']).to be_nil
|
||||||
|
expect(doc.at_css('td')['style']).to eq 'text-align: center'
|
||||||
|
end
|
||||||
|
|
||||||
it 'allows `span` elements' do
|
it 'allows `span` elements' do
|
||||||
exp = act = %q{<span>Hello</span>}
|
exp = act = %q{<span>Hello</span>}
|
||||||
expect(filter(act).to_html).to eq exp
|
expect(filter(act).to_html).to eq exp
|
||||||
|
@ -87,6 +101,18 @@ describe Banzai::Filter::SanitizationFilter do
|
||||||
expect(filter(act).to_html).to eq exp
|
expect(filter(act).to_html).to eq exp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'disallows the `name` attribute globally' do
|
||||||
|
html = <<~HTML
|
||||||
|
<img name="getElementById" src="">
|
||||||
|
<span name="foo" class="bar">Hi</span>
|
||||||
|
HTML
|
||||||
|
|
||||||
|
doc = filter(html)
|
||||||
|
|
||||||
|
expect(doc.at_css('img')).not_to have_attribute('name')
|
||||||
|
expect(doc.at_css('span')).not_to have_attribute('name')
|
||||||
|
end
|
||||||
|
|
||||||
it 'allows `summary` elements' do
|
it 'allows `summary` elements' do
|
||||||
exp = act = '<summary>summary line</summary>'
|
exp = act = '<summary>summary line</summary>'
|
||||||
expect(filter(act).to_html).to eq exp
|
expect(filter(act).to_html).to eq exp
|
||||||
|
|
|
@ -79,12 +79,28 @@ describe Gitlab::Middleware::Go do
|
||||||
it_behaves_like 'a nested project'
|
it_behaves_like 'a nested project'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with a subpackage that is not a valid project path' do
|
||||||
|
let(:path) { "#{project.full_path}/---subpackage" }
|
||||||
|
|
||||||
|
it_behaves_like 'a nested project'
|
||||||
|
end
|
||||||
|
|
||||||
context 'without subpackages' do
|
context 'without subpackages' do
|
||||||
let(:path) { project.full_path }
|
let(:path) { project.full_path }
|
||||||
|
|
||||||
it_behaves_like 'a nested project'
|
it_behaves_like 'a nested project'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with a bogus path' do
|
||||||
|
let(:path) { "http:;url=http://www.example.com'http-equiv='refresh'x='?go-get=1" }
|
||||||
|
|
||||||
|
it 'skips go-import generation' do
|
||||||
|
expect(app).to receive(:call).and_return('no-go')
|
||||||
|
|
||||||
|
go
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def go
|
def go
|
||||||
|
@ -100,7 +116,7 @@ describe Gitlab::Middleware::Go do
|
||||||
def expect_response_with_path(response, path)
|
def expect_response_with_path(response, path)
|
||||||
expect(response[0]).to eq(200)
|
expect(response[0]).to eq(200)
|
||||||
expect(response[1]['Content-Type']).to eq('text/html')
|
expect(response[1]['Content-Type']).to eq('text/html')
|
||||||
expected_body = "<!DOCTYPE html><html><head><meta content='#{Gitlab.config.gitlab.host}/#{path} git http://#{Gitlab.config.gitlab.host}/#{path}.git' name='go-import'></head></html>\n"
|
expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git http://#{Gitlab.config.gitlab.host}/#{path}.git" /></head></html>}
|
||||||
expect(response[2].body).to eq([expected_body])
|
expect(response[2].body).to eq([expected_body])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,6 +54,28 @@ describe Environment do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#folder_name' do
|
||||||
|
context 'when it is inside a folder' do
|
||||||
|
subject(:environment) do
|
||||||
|
create(:environment, name: 'staging/review-1')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a top-level folder name' do
|
||||||
|
expect(environment.folder_name).to eq 'staging'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the environment if a top-level item itself' do
|
||||||
|
subject(:environment) do
|
||||||
|
create(:environment, name: 'production')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an environment name' do
|
||||||
|
expect(environment.folder_name).to eq 'production'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#nullify_external_url' do
|
describe '#nullify_external_url' do
|
||||||
it 'replaces a blank url with nil' do
|
it 'replaces a blank url with nil' do
|
||||||
env = build(:environment, external_url: "")
|
env = build(:environment, external_url: "")
|
||||||
|
|
|
@ -16,6 +16,10 @@ describe EnvironmentEntity do
|
||||||
expect(subject).to include(:id, :name, :state, :environment_path)
|
expect(subject).to include(:id, :name, :state, :environment_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'exposes folder path' do
|
||||||
|
expect(subject).to include(:folder_path)
|
||||||
|
end
|
||||||
|
|
||||||
context 'metrics disabled' do
|
context 'metrics disabled' do
|
||||||
before do
|
before do
|
||||||
allow(environment).to receive(:has_metrics?).and_return(false)
|
allow(environment).to receive(:has_metrics?).and_return(false)
|
||||||
|
|
Loading…
Reference in a new issue