Skip to content
Snippets Groups Projects
Commit 535bd574 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets
Browse files

Merge branch '48132-display-output-from-pre-receive-scripts' into 'master'

Allow custom hooks errors to appear in GitLab UI

Closes #48132

See merge request gitlab-org/gitlab-ce!25625
parents 81fe9e9b dc14af6b
No related branches found
No related tags found
No related merge requests found
Showing
with 72 additions and 26 deletions
Loading
Loading
@@ -80,7 +80,7 @@ export default {
<status-icon :show-disabled-button="true" status="warning" />
<div class="media-body space-children">
<span class="bold">
<span v-if="mr.mergeError" class="has-error-message"> {{ mergeError }}. </span>
<span v-if="mr.mergeError" class="has-error-message"> {{ mergeError }} </span>
<span v-else> {{ s__('mrWidget|Merge failed.') }} </span>
<span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span>
</span>
Loading
Loading
Loading
Loading
@@ -76,8 +76,8 @@ module MergeRequests
def try_merge
repository.merge(current_user, source, merge_request, commit_message)
rescue Gitlab::Git::PreReceiveError => e
handle_merge_error(log_message: e.message)
raise_error('Something went wrong during merge pre-receive hook')
raise MergeError,
"Something went wrong during merge pre-receive hook. #{e.message}".strip
rescue => e
handle_merge_error(log_message: e.message)
raise_error('Something went wrong during merge')
Loading
Loading
---
title: "Allow failed custom hook script errors to safely appear in GitLab UI by filtering error messages by the prefix GL-HOOK-ERR:"
merge_request: 25625
author:
type: changed
Loading
Loading
@@ -51,7 +51,7 @@ Hooks can be also placed in `hooks/<hook_name>.d` (global) or
execution of the hooks.
 
NOTE: **Note:** `<hook_name>.d` would need to be either `pre-receive.d`,
`post-receive.d`, or `update.d` to work properly. Any other names will be ignored.
`post-receive.d`, or `update.d` to work properly. Any other names will be ignored.
 
To look in a different directory for the global custom hooks (those in
`hooks/<hook_name.d>`), set `custom_hooks_dir` in gitlab-shell config. For
Loading
Loading
@@ -76,9 +76,21 @@ first script exiting with a non-zero value.
 
> [Introduced][5073] in GitLab 8.10.
 
If the commit is declined or an error occurs during the Git hook check,
the STDERR or STDOUT message of the hook will be present in GitLab's UI.
STDERR takes precedence over STDOUT.
To have custom error messages appear in GitLab's UI when the commit is
declined or an error occurs during the Git hook, your script should:
- Send the custom error messages to either the script's `stdout` or `stderr`.
- Prefix each message with `GL-HOOK-ERR:` with no characters appearing before the prefix.
### Example custom error message
This hook script written in bash will generate the following message in GitLab's UI:
```bash
#!/bin/sh
echo "GL-HOOK-ERR: My custom error message.";
exit 1
```
 
![Custom message from custom Git hook](img/custom_hooks_error_msg.png)
 
Loading
Loading
doc/administration/img/custom_hooks_error_msg.png

43.8 KiB | W: 850px | H: 375px

doc/administration/img/custom_hooks_error_msg.png

78.6 KiB | W: 850px | H: 351px

doc/administration/img/custom_hooks_error_msg.png
doc/administration/img/custom_hooks_error_msg.png
doc/administration/img/custom_hooks_error_msg.png
doc/administration/img/custom_hooks_error_msg.png
  • 2-up
  • Swipe
  • Onion skin
Loading
Loading
@@ -4,19 +4,38 @@ module Gitlab
module Git
#
# PreReceiveError is special because its message gets displayed to users
# in the web UI. To prevent XSS we sanitize the message on
# initialization.
# in the web UI. Because of this, we:
# - Only display errors that have been marked as safe with a prefix.
# This is to prevent leaking of stacktraces, or other sensitive info.
# - Sanitize the string of any XSS
class PreReceiveError < StandardError
def initialize(msg = '')
super(nlbr(msg))
SAFE_MESSAGE_PREFIXES = [
'GitLab:', # Messages from gitlab-shell
'GL-HOOK-ERR:' # Messages marked as safe by user
].freeze
SAFE_MESSAGE_REGEX = /^(#{SAFE_MESSAGE_PREFIXES.join('|')})\s*(?<safe_message>.+)/
def initialize(message = '')
super(sanitize(message))
end
 
private
 
# In gitaly-ruby we override this method to do nothing, so that
# sanitization happens in gitlab-rails only.
def nlbr(str)
Gitlab::Utils.nlbr(str)
def sanitize(message)
return message if message.blank?
safe_messages = message.split("\n").map do |msg|
if (match = msg.match(SAFE_MESSAGE_REGEX))
match[:safe_message].presence
end
end
safe_messages = safe_messages.compact.join("\n")
Gitlab::Utils.nlbr(safe_messages)
end
end
end
Loading
Loading
Loading
Loading
@@ -37,7 +37,7 @@ describe 'Maintainer deletes tag' do
context 'when pre-receive hook fails', :js do
before do
allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag)
.and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
.and_raise(Gitlab::Git::PreReceiveError, 'GitLab: Do not delete tags')
end
 
it 'shows the error message' do
Loading
Loading
Loading
Loading
@@ -120,7 +120,7 @@ describe('MRWidgetFailedToMerge', () => {
 
it('renders given error', () => {
expect(vm.$el.querySelector('.has-error-message').textContent.trim()).toEqual(
'Merge error happened.',
'Merge error happened',
);
});
 
Loading
Loading
require 'spec_helper'
 
describe Gitlab::Git::PreReceiveError do
it 'makes its message HTML-friendly' do
ex = described_class.new("hello\nworld\n")
Gitlab::Git::PreReceiveError::SAFE_MESSAGE_PREFIXES.each do |prefix|
context "error messages prefixed with #{prefix}" do
it 'accepts only errors lines with the prefix' do
ex = described_class.new("#{prefix} Hello,\nworld!")
 
expect(ex.message).to eq('hello<br>world<br>')
expect(ex.message).to eq('Hello,')
end
it 'makes its message HTML-friendly' do
ex = described_class.new("#{prefix} Hello,\n#{prefix} world!\n")
expect(ex.message).to eq('Hello,<br>world!')
end
end
end
end
Loading
Loading
@@ -39,7 +39,7 @@ describe Gitlab::GitalyClient::OperationService do
 
context "when pre_receive_error is present" do
let(:response) do
Gitaly::UserCreateBranchResponse.new(pre_receive_error: "something failed")
Gitaly::UserCreateBranchResponse.new(pre_receive_error: "GitLab: something failed")
end
 
it "throws a PreReceive exception" do
Loading
Loading
@@ -80,7 +80,7 @@ describe Gitlab::GitalyClient::OperationService do
 
context "when pre_receive_error is present" do
let(:response) do
Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "something failed")
Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "GitLab: something failed")
end
 
it "throws a PreReceive exception" do
Loading
Loading
@@ -117,7 +117,7 @@ describe Gitlab::GitalyClient::OperationService do
 
context "when pre_receive_error is present" do
let(:response) do
Gitaly::UserDeleteBranchResponse.new(pre_receive_error: "something failed")
Gitaly::UserDeleteBranchResponse.new(pre_receive_error: "GitLab: something failed")
end
 
it "throws a PreReceive exception" do
Loading
Loading
@@ -175,7 +175,7 @@ describe Gitlab::GitalyClient::OperationService do
 
shared_examples 'cherry pick and revert errors' do
context 'when a pre_receive_error is present' do
let(:response) { response_class.new(pre_receive_error: "something failed") }
let(:response) { response_class.new(pre_receive_error: "GitLab: something failed") }
 
it 'raises a PreReceiveError' do
expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
Loading
Loading
@@ -313,7 +313,7 @@ describe Gitlab::GitalyClient::OperationService do
end
 
context 'when a pre_receive_error is present' do
let(:response) { Gitaly::UserCommitFilesResponse.new(pre_receive_error: "something failed") }
let(:response) { Gitaly::UserCommitFilesResponse.new(pre_receive_error: "GitLab: something failed") }
 
it 'raises a PreReceiveError' do
expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
Loading
Loading
Loading
Loading
@@ -72,7 +72,7 @@ describe MergeRequests::FfMergeService do
it 'logs and saves error if there is an PreReceiveError exception' do
error_message = 'error message'
 
allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message)
allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, "GitLab: #{error_message}")
allow(service).to receive(:execute_hooks)
 
service.execute(merge_request)
Loading
Loading
Loading
Loading
@@ -239,7 +239,7 @@ describe MergeRequests::MergeService do
it 'logs and saves error if there is an PreReceiveError exception' do
error_message = 'error message'
 
allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message)
allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, "GitLab: #{error_message}")
allow(service).to receive(:execute_hooks)
 
service.execute(merge_request)
Loading
Loading
Loading
Loading
@@ -41,7 +41,7 @@ describe Tags::CreateService do
it 'returns an error' do
expect(repository).to receive(:add_tag)
.with(user, 'v1.1.0', 'master', 'Foo')
.and_raise(Gitlab::Git::PreReceiveError, 'something went wrong')
.and_raise(Gitlab::Git::PreReceiveError, 'GitLab: something went wrong')
 
response = service.execute('v1.1.0', 'master', 'Foo')
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment