Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3b406199b5
commit
6a20140614
|
@ -60,6 +60,13 @@
|
||||||
@include icon-styles($gray-500, $gray-100);
|
@include icon-styles($gray-500, $gray-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.password-status-icon-success {
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
fill: $green-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.icon-link {
|
.icon-link {
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -56,10 +56,8 @@ class Environment < ApplicationRecord
|
||||||
|
|
||||||
validates :external_url,
|
validates :external_url,
|
||||||
length: { maximum: 255 },
|
length: { maximum: 255 },
|
||||||
allow_nil: true
|
allow_nil: true,
|
||||||
|
addressable_url: true
|
||||||
validates :external_url, addressable_url: true, unless: :soft_validation_on_external_url_enabled?
|
|
||||||
validate :safe_external_url, if: :soft_validation_on_external_url_enabled?
|
|
||||||
|
|
||||||
delegate :manual_actions, :other_manual_actions, to: :last_deployment, allow_nil: true
|
delegate :manual_actions, :other_manual_actions, to: :last_deployment, allow_nil: true
|
||||||
delegate :auto_rollback_enabled?, to: :project
|
delegate :auto_rollback_enabled?, to: :project
|
||||||
|
@ -495,26 +493,6 @@ class Environment < ApplicationRecord
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def soft_validation_on_external_url_enabled?
|
|
||||||
::Feature.enabled?(:soft_validation_on_external_url, project)
|
|
||||||
end
|
|
||||||
|
|
||||||
# We deliberately avoid using AddressableUrlValidator to allow users to update their environments even if they have
|
|
||||||
# misconfigured `environment:url` keyword. The external URL is presented as a clickable link on UI and not consumed
|
|
||||||
# in GitLab internally, thus we sanitize the URL before the persistence to make sure the rendered link is XSS safe.
|
|
||||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/337417
|
|
||||||
def safe_external_url
|
|
||||||
return unless self.external_url.present?
|
|
||||||
|
|
||||||
new_external_url = Addressable::URI.parse(self.external_url)
|
|
||||||
|
|
||||||
if Gitlab::Utils::SanitizeNodeLink::UNSAFE_PROTOCOLS.include?(new_external_url.normalized_scheme)
|
|
||||||
errors.add(:external_url, "#{new_external_url.normalized_scheme} scheme is not allowed")
|
|
||||||
end
|
|
||||||
rescue Addressable::URI::InvalidURIError
|
|
||||||
errors.add(:external_url, 'URI is invalid')
|
|
||||||
end
|
|
||||||
|
|
||||||
def rollout_status_available?
|
def rollout_status_available?
|
||||||
has_terminals?
|
has_terminals?
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,7 +33,8 @@
|
||||||
- else
|
- else
|
||||||
.form-group.gl-form-group{ role: 'group' }
|
.form-group.gl-form-group{ role: 'group' }
|
||||||
= f.label :password, _('Password'), class: 'gl-display-block col-form-label'
|
= f.label :password, _('Password'), class: 'gl-display-block col-form-label'
|
||||||
= f.password_field :password, disabled: f.object.force_random_password, autocomplete: 'new-password', class: 'form-control gl-form-input'
|
= f.password_field :password, disabled: f.object.force_random_password, autocomplete: 'new-password', class: 'form-control gl-form-input js-password-complexity-validation'
|
||||||
|
= render_if_exists 'shared/password_requirements_list'
|
||||||
.form-group.gl-form-group{ role: 'group' }
|
.form-group.gl-form-group{ role: 'group' }
|
||||||
= f.label :password_confirmation, _('Password confirmation'), class: 'gl-display-block col-form-label'
|
= f.label :password_confirmation, _('Password confirmation'), class: 'gl-display-block col-form-label'
|
||||||
= f.password_field :password_confirmation, disabled: f.object.force_random_password, autocomplete: 'new-password', class: 'form-control gl-form-input'
|
= f.password_field :password_confirmation, disabled: f.object.force_random_password, autocomplete: 'new-password', class: 'form-control gl-form-input'
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
= f.hidden_field :reset_password_token
|
= f.hidden_field :reset_password_token
|
||||||
.form-group.gl-px-5
|
.form-group.gl-px-5
|
||||||
= f.label _('New password'), for: "user_password"
|
= f.label _('New password'), for: "user_password"
|
||||||
= f.password_field :password, autocomplete: 'new-password', class: "form-control gl-form-input top", required: true, title: _('This field is required.'), data: { qa_selector: 'password_field'}
|
= f.password_field :password, autocomplete: 'new-password', class: "form-control gl-form-input top js-password-complexity-validation", required: true, title: _('This field is required.'), data: { qa_selector: 'password_field'}
|
||||||
|
= render_if_exists 'shared/password_requirements_list'
|
||||||
.form-group.gl-px-5
|
.form-group.gl-px-5
|
||||||
= f.label _('Confirm new password'), for: "user_password_confirmation"
|
= f.label _('Confirm new password'), for: "user_password_confirmation"
|
||||||
= f.password_field :password_confirmation, autocomplete: 'new-password', class: "form-control gl-form-input bottom", title: _('This field is required.'), data: { qa_selector: 'password_confirmation_field' }, required: true
|
= f.password_field :password_confirmation, autocomplete: 'new-password', class: "form-control gl-form-input bottom", title: _('This field is required.'), data: { qa_selector: 'password_confirmation_field' }, required: true
|
||||||
|
|
|
@ -55,13 +55,14 @@
|
||||||
.form-group.gl-mb-5#password-strength
|
.form-group.gl-mb-5#password-strength
|
||||||
= f.label :password, class: 'label-bold'
|
= f.label :password, class: 'label-bold'
|
||||||
= f.password_field :password,
|
= f.password_field :password,
|
||||||
class: 'form-control gl-form-input bottom',
|
class: 'form-control gl-form-input bottom js-password-complexity-validation',
|
||||||
data: { qa_selector: 'new_user_password_field' },
|
data: { qa_selector: 'new_user_password_field' },
|
||||||
autocomplete: 'new-password',
|
autocomplete: 'new-password',
|
||||||
required: true,
|
required: true,
|
||||||
pattern: ".{#{@minimum_password_length},}",
|
pattern: ".{#{@minimum_password_length},}",
|
||||||
title: s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
|
title: s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
|
||||||
%p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
|
%p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
|
||||||
|
= render_if_exists 'shared/password_requirements_list'
|
||||||
= render_if_exists 'devise/shared/phone_verification', form: f
|
= render_if_exists 'devise/shared/phone_verification', form: f
|
||||||
%div
|
%div
|
||||||
- if show_recaptcha_sign_up?
|
- if show_recaptcha_sign_up?
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
= _('You must provide your current password in order to change it.')
|
= _('You must provide your current password in order to change it.')
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :new_password, _('New password'), class: 'label-bold'
|
= f.label :new_password, _('New password'), class: 'label-bold'
|
||||||
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'new_password_field' }
|
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input js-password-complexity-validation', data: { qa_selector: 'new_password_field' }
|
||||||
|
= render_if_exists 'shared/password_requirements_list'
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
|
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
|
||||||
= f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' }
|
= f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' }
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
.col-sm-2.col-form-label
|
.col-sm-2.col-form-label
|
||||||
= f.label :new_password, _('New password')
|
= f.label :new_password, _('New password')
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'new_password_field' }
|
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input js-password-complexity-validation', data: { qa_selector: 'new_password_field' }
|
||||||
|
= render_if_exists 'shared/password_requirements_list'
|
||||||
.form-group.row
|
.form-group.row
|
||||||
.col-sm-2.col-form-label
|
.col-sm-2.col-form-label
|
||||||
= f.label :password_confirmation, _('Password confirmation')
|
= f.label :password_confirmation, _('Password confirmation')
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
name: soft_validation_on_external_url
|
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91970
|
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367206
|
|
||||||
milestone: '15.2'
|
|
||||||
type: development
|
|
||||||
group: group::release
|
|
||||||
default_enabled: false
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveNotNullConstraintsFromRequirements < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
change_column_null :requirements, :created_at, true
|
||||||
|
change_column_null :requirements, :updated_at, true
|
||||||
|
change_column_null :requirements, :title, true
|
||||||
|
change_column_null :requirements, :state, true
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# No OP
|
||||||
|
# The columns could have nil values again at this point. Rolling back
|
||||||
|
# would cause an exception, also we cannot insert data and modify the schema within the same migration.
|
||||||
|
# More details at https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91611#note_1017066470
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
7c9b8b433553e83bb05208e62541e3d51bcc1083ff33d1146e93b92d954f9cb0
|
|
@ -20270,14 +20270,14 @@ ALTER SEQUENCE required_code_owners_sections_id_seq OWNED BY required_code_owner
|
||||||
|
|
||||||
CREATE TABLE requirements (
|
CREATE TABLE requirements (
|
||||||
id bigint NOT NULL,
|
id bigint NOT NULL,
|
||||||
created_at timestamp with time zone NOT NULL,
|
created_at timestamp with time zone,
|
||||||
updated_at timestamp with time zone NOT NULL,
|
updated_at timestamp with time zone,
|
||||||
project_id integer NOT NULL,
|
project_id integer NOT NULL,
|
||||||
author_id integer,
|
author_id integer,
|
||||||
iid integer NOT NULL,
|
iid integer NOT NULL,
|
||||||
cached_markdown_version integer,
|
cached_markdown_version integer,
|
||||||
state smallint DEFAULT 1 NOT NULL,
|
state smallint DEFAULT 1,
|
||||||
title character varying(255) NOT NULL,
|
title character varying(255),
|
||||||
title_html text,
|
title_html text,
|
||||||
description text,
|
description text,
|
||||||
description_html text,
|
description_html text,
|
||||||
|
|
|
@ -220,6 +220,12 @@ on the Gitaly server matches the one on Gitaly client. If it doesn't match,
|
||||||
update the secrets file on the Gitaly server to match the Gitaly client, then
|
update the secrets file on the Gitaly server to match the Gitaly client, then
|
||||||
[reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
[reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||||
|
|
||||||
|
If you've confirmed that your `gitlab-secrets.json` file is the same on all Gitaly servers and clients,
|
||||||
|
the application might be fetching this secret from a different file. Your Gitaly server's
|
||||||
|
`config.toml file` indicates the secrets file in use.
|
||||||
|
If that setting is missing, GitLab defaults to using `.gitlab_shell_secret` under
|
||||||
|
`/opt/gitlab/embedded/service/gitlab-rails/.gitlab_shell_secret`.
|
||||||
|
|
||||||
### Repository pushes fail with a `deny updating a hidden ref` error
|
### Repository pushes fail with a `deny updating a hidden ref` error
|
||||||
|
|
||||||
Due to [a change](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3426)
|
Due to [a change](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3426)
|
||||||
|
|
|
@ -107,6 +107,9 @@ grep -2 search_term <filename>
|
||||||
# Search on all files in directory (recursively)
|
# Search on all files in directory (recursively)
|
||||||
grep -r search_term <directory>
|
grep -r search_term <directory>
|
||||||
|
|
||||||
|
# Grep namespace/project/name of a GitLab repository
|
||||||
|
grep 'fullpath' /var/opt/gitlab/git-data/repositories/@hashed/<repo hash>/.git/config
|
||||||
|
|
||||||
# search through *.gz files is the same except with zgrep
|
# search through *.gz files is the same except with zgrep
|
||||||
zgrep search_term <filename>
|
zgrep search_term <filename>
|
||||||
|
|
||||||
|
@ -126,6 +129,7 @@ history
|
||||||
# Search through command history
|
# Search through command history
|
||||||
<ctrl>-R
|
<ctrl>-R
|
||||||
|
|
||||||
|
|
||||||
# Execute last command with sudo
|
# Execute last command with sudo
|
||||||
sudo !!
|
sudo !!
|
||||||
```
|
```
|
||||||
|
|
|
@ -374,8 +374,6 @@ To retry or rollback a deployment:
|
||||||
|
|
||||||
### Environment URL
|
### Environment URL
|
||||||
|
|
||||||
> [Fixed](https://gitlab.com/gitlab-org/gitlab/-/issues/337417) to persist arbitrary URLs in GitLab 15.2 [with a flag](../../administration/feature_flags.md) named `soft_validation_on_external_url`. Disabled by default.
|
|
||||||
|
|
||||||
The [environment URL](../yaml/index.md#environmenturl) is displayed in a few
|
The [environment URL](../yaml/index.md#environmenturl) is displayed in a few
|
||||||
places in GitLab:
|
places in GitLab:
|
||||||
|
|
||||||
|
|
|
@ -387,3 +387,28 @@ When you are using GDK, you can set it up with:
|
||||||
1. Start the database: `gdk start db`
|
1. Start the database: `gdk start db`
|
||||||
1. Load the environment from GDK: `eval $(cd ../gitaly && gdk env)`
|
1. Load the environment from GDK: `eval $(cd ../gitaly && gdk env)`
|
||||||
1. Create the database: `createdb --encoding=UTF8 --locale=C --echo praefect_test`
|
1. Create the database: `createdb --encoding=UTF8 --locale=C --echo praefect_test`
|
||||||
|
|
||||||
|
## Git references used by Gitaly
|
||||||
|
|
||||||
|
Gitaly uses many Git references ([refs](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefrefaref)) to provide Git services to GitLab.
|
||||||
|
|
||||||
|
### Standard Git references
|
||||||
|
|
||||||
|
These standard Git references are used by GitLab (through Gitaly) in any Git repository:
|
||||||
|
|
||||||
|
- `refs/heads/`. Used for branches. See the [`git branch`](https://git-scm.com/docs/git-branch) documentation.
|
||||||
|
- `refs/tags/`. Used for tags. See the [`git tag`](https://git-scm.com/docs/git-tag) documentation.
|
||||||
|
|
||||||
|
### GitLab-specific references
|
||||||
|
|
||||||
|
These GitLab-specific references are used exclusively by GitLab (through Gitaly):
|
||||||
|
|
||||||
|
- `refs/keep-around/<object-id>`. References to commits that have pipeline jobs or merge requests. The `object-id` points to the commit the pipeline was run on.
|
||||||
|
- `refs/merge-requests/<merge-request-iid>/`. [Merges](https://git-scm.com/docs/git-merge) merge two histories together. This ref namespace tracks information about a
|
||||||
|
merge using the following refs under it:
|
||||||
|
- `head`. Current `HEAD` of the merge request.
|
||||||
|
- `merge`. Commit for the merge request. Every merge request creates a commit object under `refs/keep-around`.
|
||||||
|
- If [merge trains are enabled](../ci/pipelines/merge_trains.md): `train`. Commit for the merge train.
|
||||||
|
- `refs/pipelines/<pipeline-iid>`. References to pipelines. Temporarily used to store the pipeline commit object ID.
|
||||||
|
- `refs/environments/<environment-slug>`. References to commits where deployments to environments were performed.
|
||||||
|
- `refs/heads/revert-<source-commit-short-object-id>`. References to the commit's object ID created when [reverting changes](../user/project/merge_requests/revert_changes.md).
|
||||||
|
|
|
@ -35,6 +35,11 @@ other internal references (refs) that are automatically created by GitLab. These
|
||||||
|
|
||||||
These refs are not automatically downloaded and hidden refs are not advertised, but we can remove these refs using a project export.
|
These refs are not automatically downloaded and hidden refs are not advertised, but we can remove these refs using a project export.
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
This process is not suitable for removing sensitive data like password or keys from your repository.
|
||||||
|
Information about commits, including file content, is cached in the database, and remain
|
||||||
|
visible even after they have been removed from the repository.
|
||||||
|
|
||||||
To purge files from a GitLab repository:
|
To purge files from a GitLab repository:
|
||||||
|
|
||||||
1. Install either [`git filter-repo`](https://github.com/newren/git-filter-repo/blob/main/INSTALL.md) or
|
1. Install either [`git filter-repo`](https://github.com/newren/git-filter-repo/blob/main/INSTALL.md) or
|
||||||
|
@ -247,11 +252,6 @@ increased, your only option is to:
|
||||||
1. Prune all the unneeded stuff locally.
|
1. Prune all the unneeded stuff locally.
|
||||||
1. Create a new project on GitLab and start using that instead.
|
1. Create a new project on GitLab and start using that instead.
|
||||||
|
|
||||||
WARNING:
|
|
||||||
This process is not suitable for removing sensitive data like password or keys from your repository.
|
|
||||||
Information about commits, including file content, is cached in the database, and remain
|
|
||||||
visible even after they have been removed from the repository.
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Incorrect repository statistics shown in the GUI
|
### Incorrect repository statistics shown in the GUI
|
||||||
|
|
|
@ -233,7 +233,7 @@ RSpec.describe Projects::EnvironmentsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when environment params are invalid" do
|
context "when environment params are invalid" do
|
||||||
let(:params) { environment_params.merge(environment: { external_url: 'javascript:alert("hello")' }) }
|
let(:params) { environment_params.merge(environment: { name: '/foo/', external_url: '/git.gitlab.com' }) }
|
||||||
|
|
||||||
it 'returns bad request' do
|
it 'returns bad request' do
|
||||||
subject
|
subject
|
||||||
|
|
|
@ -34,90 +34,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
|
||||||
|
|
||||||
it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
|
it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
|
||||||
|
|
||||||
describe 'validate and sanitize external url' do
|
|
||||||
let_it_be_with_refind(:environment) { create(:environment) }
|
|
||||||
|
|
||||||
where(:source_external_url, :expected_error_message) do
|
|
||||||
'http://example.com' | nil
|
|
||||||
'example.com' | nil
|
|
||||||
'www.example.io' | nil
|
|
||||||
'http://$URL' | nil
|
|
||||||
'http://$(URL)' | nil
|
|
||||||
'custom://example.com' | nil
|
|
||||||
'1.1.1.1' | nil
|
|
||||||
'$BASE_URL/${CI_COMMIT_REF_NAME}' | nil
|
|
||||||
'$ENVIRONMENT_URL' | nil
|
|
||||||
'https://$SUB.$MAIN' | nil
|
|
||||||
'https://$SUB-$REGION.$MAIN' | nil
|
|
||||||
'https://example.com?param={()}' | nil
|
|
||||||
'http://XSS?x=<script>alert(1)</script>' | nil
|
|
||||||
'https://user:${VARIABLE}@example.io' | nil
|
|
||||||
'https://example.com/test?param={data}' | nil
|
|
||||||
'http://${URL}' | 'URI is invalid'
|
|
||||||
'https://${URL}.example/test' | 'URI is invalid'
|
|
||||||
'http://test${CI_MERGE_REQUEST_IID}.example.com' | 'URI is invalid'
|
|
||||||
'javascript:alert("hello")' | 'javascript scheme is not allowed'
|
|
||||||
end
|
|
||||||
with_them do
|
|
||||||
it 'sets an external URL or an error' do
|
|
||||||
environment.external_url = source_external_url
|
|
||||||
|
|
||||||
environment.valid?
|
|
||||||
|
|
||||||
if expected_error_message
|
|
||||||
expect(environment.errors[:external_url].first).to eq(expected_error_message)
|
|
||||||
else
|
|
||||||
expect(environment.errors[:external_url]).to be_empty,
|
|
||||||
"There were unexpected errors: #{environment.errors.full_messages}"
|
|
||||||
expect(environment.external_url).to eq(source_external_url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when soft_validation_on_external_url feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(soft_validation_on_external_url: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
where(:source_external_url, :expected_error_message) do
|
|
||||||
'http://example.com' | nil
|
|
||||||
'example.com' | 'is blocked: Only allowed schemes are http, https'
|
|
||||||
'www.example.io' | 'is blocked: Only allowed schemes are http, https'
|
|
||||||
'http://$URL' | 'is blocked: Hostname or IP address invalid'
|
|
||||||
'http://$(URL)' | 'is blocked: Hostname or IP address invalid'
|
|
||||||
'custom://example.com' | 'is blocked: Only allowed schemes are http, https'
|
|
||||||
'1.1.1.1' | 'is blocked: Only allowed schemes are http, https'
|
|
||||||
'$BASE_URL/${CI_COMMIT_REF_NAME}' | 'is blocked: Only allowed schemes are http, https'
|
|
||||||
'$ENVIRONMENT_URL' | 'is blocked: Only allowed schemes are http, https'
|
|
||||||
'https://$SUB.$MAIN' | 'is blocked: Hostname or IP address invalid'
|
|
||||||
'https://$SUB-$REGION.$MAIN' | 'is blocked: Hostname or IP address invalid'
|
|
||||||
'https://example.com?param={()}' | nil
|
|
||||||
'http://XSS?x=<script>alert(1)</script>' | nil
|
|
||||||
'https://user:${VARIABLE}@example.io' | nil
|
|
||||||
'https://example.com/test?param={data}' | nil
|
|
||||||
'http://${URL}' | 'is blocked: URI is invalid'
|
|
||||||
'https://${URL}.example/test' | 'is blocked: URI is invalid'
|
|
||||||
'http://test${CI_MERGE_REQUEST_IID}.example.com' | 'is blocked: URI is invalid'
|
|
||||||
'javascript:alert("hello")' | 'is blocked: Only allowed schemes are http, https'
|
|
||||||
end
|
|
||||||
with_them do
|
|
||||||
it 'sets an external URL or an error' do
|
|
||||||
environment.external_url = source_external_url
|
|
||||||
|
|
||||||
environment.valid?
|
|
||||||
|
|
||||||
if expected_error_message
|
|
||||||
expect(environment.errors[:external_url].first).to eq(expected_error_message)
|
|
||||||
else
|
|
||||||
expect(environment.errors[:external_url]).to be_empty,
|
|
||||||
"There were unexpected errors: #{environment.errors.full_messages}"
|
|
||||||
expect(environment.external_url).to eq(source_external_url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.before_save' do
|
describe '.before_save' do
|
||||||
it 'ensures environment tier when a new object is created' do
|
it 'ensures environment tier when a new object is created' do
|
||||||
environment = build(:environment, name: 'gprd', tier: nil)
|
environment = build(:environment, name: 'gprd', tier: nil)
|
||||||
|
|
|
@ -112,7 +112,7 @@ RSpec.describe Deployments::UpdateEnvironmentService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when external URL is invalid' do
|
context 'when external URL is invalid' do
|
||||||
let(:external_url) { 'javascript:alert("hello")' }
|
let(:external_url) { 'google.com' }
|
||||||
|
|
||||||
it 'fails to update the tier due to validation error' do
|
it 'fails to update the tier due to validation error' do
|
||||||
expect { subject.execute }.not_to change { environment.tier }
|
expect { subject.execute }.not_to change { environment.tier }
|
||||||
|
@ -123,7 +123,7 @@ RSpec.describe Deployments::UpdateEnvironmentService do
|
||||||
.with(an_instance_of(described_class::EnvironmentUpdateFailure),
|
.with(an_instance_of(described_class::EnvironmentUpdateFailure),
|
||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
environment_id: environment.id,
|
environment_id: environment.id,
|
||||||
reason: %q{External url javascript scheme is not allowed})
|
reason: %q{External url is blocked: Only allowed schemes are http, https})
|
||||||
.once
|
.once
|
||||||
|
|
||||||
subject.execute
|
subject.execute
|
||||||
|
|
Loading…
Reference in New Issue