Skip to content
Snippets Groups Projects
Commit b9a33e93 authored by GitLab Release Tools Bot's avatar GitLab Release Tools Bot
Browse files

Merge remote-tracking branch 'dev/12-7-stable' into 12-7-stable

parents efed756a 2c062854
No related branches found
No related tags found
No related merge requests found
Showing
with 475 additions and 110 deletions
# frozen_string_literal: true
module Gitlab
module Git
class CrossRepoComparer
attr_reader :source_repo, :target_repo
def initialize(source_repo, target_repo)
@source_repo = source_repo
@target_repo = target_repo
end
def compare(source_ref, target_ref, straight:)
ensuring_ref_in_source(target_ref) do |target_commit_id|
Gitlab::Git::Compare.new(
source_repo,
target_commit_id,
source_ref,
straight: straight
)
end
end
private
def ensuring_ref_in_source(ref, &blk)
return yield ref if source_repo == target_repo
# If the commit doesn't exist in the target, there's nothing we can do
commit_id = target_repo.commit(ref)&.sha
return unless commit_id
# The commit pointed to by ref may exist in the source even when they
# are different repositories. This is particularly true of close forks,
# but may also be the case if a temporary ref for this comparison has
# already been created in the past, and the result hasn't been GC'd yet.
return yield commit_id if source_repo.commit(commit_id)
# Worst case: the commit is not in the source repo so we need to fetch
# it. Use a temporary ref and clean up afterwards
with_commit_in_source_tmp(commit_id, &blk)
end
# Fetch the ref into source_repo from target_repo, using a temporary ref
# name that will be deleted once the method completes. This is a no-op if
# fetching the source branch fails
def with_commit_in_source_tmp(commit_id, &blk)
tmp_ref = "refs/tmp/#{SecureRandom.hex}"
yield commit_id if source_repo.fetch_source_branch!(target_repo, commit_id, tmp_ref)
ensure
source_repo.delete_refs(tmp_ref) # best-effort
end
end
end
end
Loading
Loading
@@ -746,29 +746,9 @@ module Gitlab
end
 
def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
reachable_ref =
if source_repository == self
source_branch_name
else
# If a tmp ref was created before for a separate repo comparison (forks),
# we're able to short-circuit the tmp ref re-creation:
# 1. Take the SHA from the source repo
# 2. Read that in the current "target" repo
# 3. If that SHA is still known (readable), it means GC hasn't
# cleaned it up yet, so we can use it instead re-writing the tmp ref.
source_commit_id = source_repository.commit(source_branch_name)&.sha
commit(source_commit_id)&.sha if source_commit_id
end
return compare(target_branch_name, reachable_ref, straight: straight) if reachable_ref
tmp_ref = "refs/tmp/#{SecureRandom.hex}"
return unless fetch_source_branch!(source_repository, source_branch_name, tmp_ref)
compare(target_branch_name, tmp_ref, straight: straight)
ensure
delete_refs(tmp_ref) if tmp_ref
CrossRepoComparer
.new(source_repository, self)
.compare(source_branch_name, target_branch_name, straight: straight)
end
 
def write_ref(ref_path, ref, old_ref: nil)
Loading
Loading
@@ -1046,13 +1026,6 @@ module Gitlab
 
private
 
def compare(base_ref, head_ref, straight:)
Gitlab::Git::Compare.new(self,
base_ref,
head_ref,
straight: straight)
end
def empty_diff_stats
Gitlab::Git::DiffStatsCollection.new([])
end
Loading
Loading
# frozen_string_literal: true
module Gitlab
module NoCacheHeaders
DEFAULT_GITLAB_NO_CACHE_HEADERS = {
'Cache-Control' => "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store, no-cache",
'Pragma' => 'no-cache', # HTTP 1.0 compatibility
'Expires' => 'Fri, 01 Jan 1990 00:00:00 GMT'
}.freeze
def no_cache_headers
raise "#no_cache_headers is not implemented for this object"
end
end
end
Loading
Loading
@@ -6,11 +6,16 @@ module Gitlab
REFERABLES = %i(user issue label milestone mentioned_user mentioned_group mentioned_project
merge_request snippet commit commit_range directly_addressed_user epic).freeze
attr_accessor :project, :current_user, :author
# This counter is increased by a number of references filtered out by
# banzai reference exctractor. Note that this counter is stateful and
# not idempotent and is increased whenever you call `references`.
attr_reader :stateful_not_visible_counter
 
def initialize(project, current_user = nil)
@project = project
@current_user = current_user
@references = {}
@stateful_not_visible_counter = 0
 
super()
end
Loading
Loading
@@ -20,11 +25,15 @@ module Gitlab
end
 
def references(type)
super(type, project, current_user)
refs = super(type, project, current_user)
@stateful_not_visible_counter += refs[:not_visible].count
refs[:visible]
end
 
def reset_memoized_values
@references = {}
@stateful_not_visible_counter = 0
super()
end
 
Loading
Loading
Loading
Loading
@@ -94,6 +94,7 @@
"jszip": "^3.1.3",
"jszip-utils": "^0.0.2",
"katex": "^0.10.0",
"lodash": "^4.17.15",
"marked": "^0.3.12",
"mermaid": "^8.4.5",
"monaco-editor": "^0.18.1",
Loading
Loading
Loading
Loading
@@ -59,5 +59,48 @@ module QA
a_hash_including(message: '202 Accepted')
)
end
describe 'raw file access' do
let(:svg_file) do
<<-SVG
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert("surprise");
</script>
</svg>
SVG
end
it 'sets no-cache headers as expected' do
create_project_request = Runtime::API::Request.new(@api_client, '/projects')
post create_project_request.url, path: project_name, name: project_name
create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg")
post create_file_request.url, branch: 'master', content: svg_file, commit_message: 'Add test.svg'
get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: 'master')
3.times do
response = get get_file_request.url
# Subsequent responses aren't cached, so headers should match from
# request to request, especially a 200 response rather than a 304
# (indicating a cached response.) Further, :content_disposition
# should include `attachment` for all responses.
#
expect(response.headers[:cache_control]).to include("no-store")
expect(response.headers[:cache_control]).to include("no-cache")
expect(response.headers[:pragma]).to eq("no-cache")
expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
expect(response.headers[:content_disposition]).to include("attachment")
expect(response.headers[:content_disposition]).not_to include("inline")
expect(response.headers[:content_type]).to include("image/svg+xml")
end
end
end
end
end
Loading
Loading
@@ -23,6 +23,47 @@ describe DashboardController do
end
end
 
describe "GET activity as JSON" do
render_views
let(:user) { create(:user) }
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
before do
create(:event, :created, project: project, target: create(:issue))
sign_in(user)
request.cookies[:event_filter] = 'all'
end
context 'when user has permission to see the event' do
before do
project.add_developer(user)
end
it 'returns count' do
get :activity, params: { format: :json }
expect(json_response['count']).to eq(1)
end
end
context 'when user has no permission to see the event' do
it 'filters out invisible event' do
get :activity, params: { format: :json }
expect(json_response['html']).to include(_('No activities found'))
end
it 'filters out invisible event when calculating the count' do
get :activity, params: { format: :json }
expect(json_response['count']).to eq(0)
end
end
end
it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first
it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics
 
Loading
Loading
Loading
Loading
@@ -47,7 +47,7 @@ describe GroupsController do
 
it 'assigns events for all the projects in the group', :sidekiq_might_not_need_inline do
subject
expect(assigns(:events)).to contain_exactly(event)
expect(assigns(:events).map(&:id)).to contain_exactly(event.id)
end
end
end
Loading
Loading
@@ -119,12 +119,12 @@ describe GroupsController do
describe 'GET #activity' do
render_views
 
before do
sign_in(user)
project
end
context 'as json' do
before do
sign_in(user)
project
end
it 'includes events from all projects in group and subgroups', :sidekiq_might_not_need_inline do
2.times do
project = create(:project, group: group)
Loading
Loading
@@ -141,6 +141,31 @@ describe GroupsController do
expect(assigns(:projects).limit_value).to be_nil
end
end
context 'when user has no permission to see the event' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:project_with_restricted_access) do
create(:project, :public, issues_access_level: ProjectFeature::PRIVATE, group: group)
end
before do
create(:event, project: project)
create(:event, :created, project: project_with_restricted_access, target: create(:issue))
group.add_guest(user)
sign_in(user)
end
it 'filters out invisible event' do
get :activity, params: { id: group.to_param }, format: :json
expect(json_response['count']).to eq(1)
end
end
end
 
describe 'POST #create' do
Loading
Loading
Loading
Loading
@@ -64,6 +64,46 @@ describe ProjectsController do
end
end
 
describe "GET #activity as JSON" do
render_views
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
before do
create(:event, :created, project: project, target: create(:issue))
sign_in(user)
request.cookies[:event_filter] = 'all'
end
context 'when user has permission to see the event' do
before do
project.add_developer(user)
end
it 'returns count' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['count']).to eq(1)
end
end
context 'when user has no permission to see the event' do
it 'filters out invisible event' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['html']).to eq("\n")
end
it 'filters out invisible event when calculating the count' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['count']).to eq(0)
end
end
end
describe "GET show" do
context "user not project member" do
before do
Loading
Loading
Loading
Loading
@@ -935,14 +935,14 @@ describe ProjectsHelper do
helper.instance_variable_set(:@project, project)
end
 
subject { helper.grafana_integration_token }
subject { helper.grafana_integration_masked_token }
 
it { is_expected.to eq(nil) }
 
context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) }
 
it { is_expected.to eq(grafana_integration.token) }
it { is_expected.to eq(grafana_integration.masked_token) }
end
end
 
Loading
Loading
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { isMobile, getTopFrequentItems, updateExistingFrequentItem } from '~/frequent_items/utils';
import {
isMobile,
getTopFrequentItems,
updateExistingFrequentItem,
sanitizeItem,
} from '~/frequent_items/utils';
import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants';
import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data';
 
Loading
Loading
@@ -92,4 +97,16 @@ describe('Frequent Items utils spec', () => {
expect(result.frequency).toBe(mockedProject.frequency);
});
});
describe('sanitizeItem', () => {
it('strips HTML tags for name and namespace', () => {
const input = {
name: '<br><b>test</b>',
namespace: '<br>test',
id: 1,
};
expect(sanitizeItem(input)).toEqual({ name: 'test', namespace: 'test', id: 1 });
});
});
});
Loading
Loading
@@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
it 'returns empty array' do
link['data-group'] = project.group.id.to_s
 
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
 
Loading
Loading
@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
end
 
it 'returns groups' do
expect(subject.gather_references([link])).to eq([group])
expect_gathered_references(subject.gather_references([link]), [group], 0)
end
end
 
Loading
Loading
@@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
it 'returns an empty Array' do
link['data-group'] = 'test-non-existing'
 
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
end
Loading
Loading
Loading
Loading
@@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
it 'returns empty Array' do
link['data-project'] = project.id.to_s
 
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
 
Loading
Loading
@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
end
 
it 'returns an Array of referenced projects' do
expect(subject.gather_references([link])).to eq([project])
expect_gathered_references(subject.gather_references([link]), [project], 0)
end
end
 
Loading
Loading
@@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
it 'returns an empty Array' do
link['data-project'] = 'inexisting-project-id'
 
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
end
Loading
Loading
Loading
Loading
@@ -22,7 +22,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
end
 
it 'returns empty list of users' do
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 0)
end
end
end
Loading
Loading
@@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
end
 
it 'returns empty list of users' do
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 0)
end
end
end
Loading
Loading
@@ -44,7 +44,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
it 'returns an Array of users' do
link['data-user'] = user.id.to_s
 
expect(subject.referenced_by([link])).to eq([user])
expect_gathered_references(subject.gather_references([link]), [user], 0)
end
end
end
Loading
Loading
Loading
Loading
@@ -17,7 +17,7 @@ describe Banzai::ReferenceParser::ProjectParser do
it 'returns an Array of projects' do
link['data-project'] = project.id.to_s
 
expect(subject.gather_references([link])).to eq([project])
expect_gathered_references(subject.gather_references([link]), [project], 0)
end
end
 
Loading
Loading
@@ -25,7 +25,7 @@ describe Banzai::ReferenceParser::ProjectParser do
it 'returns an empty Array' do
link['data-project'] = ''
 
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
 
Loading
Loading
@@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::ProjectParser do
 
link['data-project'] = private_project.id.to_s
 
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
 
it 'returns an Array when authorized' do
Loading
Loading
@@ -43,7 +43,7 @@ describe Banzai::ReferenceParser::ProjectParser do
 
link['data-project'] = private_project.id.to_s
 
expect(subject.gather_references([link])).to eq([private_project])
expect_gathered_references(subject.gather_references([link]), [private_project], 0)
end
end
end
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
require 'nokogiri'
describe Gitlab::Asciidoc::IncludeProcessor do
let_it_be(:project) { create(:project, :repository) }
let(:processor_context) do
{
project: project,
max_includes: max_includes,
ref: ref
}
end
let(:ref) { project.repository.root_ref }
let(:max_includes) { 10 }
let(:reader) { Asciidoctor::PreprocessorReader.new(document, lines, 'file.adoc') }
let(:document) { Asciidoctor::Document.new(lines) }
subject(:processor) { described_class.new(processor_context) }
let(:a_blob) { double(:Blob, readable_text?: true, data: a_data) }
let(:a_data) { StringIO.new('include::b.adoc[]') }
let(:lines) { [':max-include-depth: 1000'] + Array.new(10, 'include::a.adoc[]') }
before do
allow(project.repository).to receive(:blob_at).with(ref, 'a.adoc').and_return(a_blob)
end
describe '#include_allowed?' do
it 'allows the first include' do
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy
end
it 'disallows the Nth + 1 include' do
max_includes.times { processor.send(:read_blob, ref, 'a.adoc') }
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_falsey
end
end
end
Loading
Loading
@@ -425,6 +425,24 @@ module Gitlab
create_file(current_file, "= AsciiDoc\n")
end
 
def many_includes(target)
Array.new(10, "include::#{target}[]").join("\n")
end
context 'cyclic imports' do
before do
create_file('doc/api/a.adoc', many_includes('b.adoc'))
create_file('doc/api/b.adoc', many_includes('a.adoc'))
end
let(:include_path) { 'a.adoc' }
let(:requested_path) { 'doc/api/README.md' }
it 'completes successfully' do
is_expected.to include('<p>Include this:</p>')
end
end
context 'with path to non-existing file' do
let(:include_path) { 'not-exists.adoc' }
 
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Git::CrossRepoComparer do
let(:source_project) { create(:project, :repository) }
let(:target_project) { create(:project, :repository) }
let(:source_repo) { source_project.repository.raw_repository }
let(:target_repo) { target_project.repository.raw_repository }
let(:source_branch) { 'feature' }
let(:target_branch) { 'master' }
let(:straight) { false }
let(:source_commit) { source_repo.commit(source_branch) }
let(:target_commit) { source_repo.commit(target_branch) }
subject(:result) { described_class.new(source_repo, target_repo).compare(source_branch, target_branch, straight: straight) }
describe '#compare' do
context 'within a single repository' do
let(:target_project) { source_project }
context 'a non-straight comparison' do
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect_compare(result, from: source_commit, to: target_commit)
expect(result.straight).to eq(false)
end
end
context 'a straight comparison' do
let(:straight) { true }
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect_compare(result, from: source_commit, to: target_commit)
expect(result.straight).to eq(true)
end
end
end
context 'across two repositories' do
context 'target ref exists in source repo' do
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
expect_compare(result, from: source_commit, to: target_commit)
end
end
context 'target ref does not exist in source repo' do
it 'compares in the source repo by fetching from the target to a temporary ref' do
new_commit_id = create_commit(target_project.owner, target_repo, target_branch)
new_commit = target_repo.commit(new_commit_id)
# This is how the temporary ref is generated
expect(SecureRandom).to receive(:hex).at_least(:once).and_return('foo')
expect(source_repo)
.to receive(:fetch_source_branch!)
.with(target_repo, new_commit_id, 'refs/tmp/foo')
.and_call_original
expect(source_repo).to receive(:delete_refs).with('refs/tmp/foo').and_call_original
expect_compare(result, from: source_commit, to: new_commit)
end
end
context 'source ref does not exist in source repo' do
let(:source_branch) { 'does-not-exist' }
it 'returns an empty comparison' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
expect(result).to be_a(::Gitlab::Git::Compare)
expect(result.commits.size).to eq(0)
end
end
context 'target ref does not exist in target repo' do
let(:target_branch) { 'does-not-exist' }
it 'returns nil' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
is_expected.to be_nil
end
end
end
end
def expect_compare(of, from:, to:)
expect(of).to be_a(::Gitlab::Git::Compare)
expect(from).to be_a(::Gitlab::Git::Commit)
expect(to).to be_a(::Gitlab::Git::Commit)
expect(of.commits).not_to be_empty
expect(of.head).to eq(from)
expect(of.base).to eq(to)
end
def create_commit(user, repo, branch)
action = { action: :create, file_path: '/FILE', content: 'content' }
result = repo.multi_action(user, branch_name: branch, message: 'Commit', actions: [action])
result.newrev
end
end
Loading
Loading
@@ -1962,66 +1962,15 @@ describe Gitlab::Git::Repository, :seed_helper do
end
 
describe '#compare_source_branch' do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') }
context 'within same repository' do
it 'does not create a temp ref' do
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', repository, 'feature', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
it 'returns empty commits when source ref does not exist' do
compare = repository.compare_source_branch('master', repository, 'non-existent-branch', straight: false)
it 'delegates to Gitlab::Git::CrossRepoComparer' do
expect_next_instance_of(::Gitlab::Git::CrossRepoComparer) do |instance|
expect(instance.source_repo).to eq(:source_repository)
expect(instance.target_repo).to eq(repository)
 
expect(compare.commits).to be_empty
expect(instance).to receive(:compare).with('feature', 'master', straight: :straight)
end
end
 
context 'with different repositories' do
context 'when ref is known by source repo, but not by target' do
before do
mutable_repository.write_ref('another-branch', 'feature')
end
it 'creates temp ref' do
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
end
context 'when ref is known by source and target repos' do
before do
mutable_repository.write_ref('another-branch', 'feature')
repository.write_ref('another-branch', 'feature')
end
it 'does not create a temp ref' do
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
end
context 'when ref is unknown by source repo' do
it 'returns nil when source ref does not exist' do
expect(repository).to receive(:fetch_source_branch!).and_call_original
expect(repository).to receive(:delete_refs).and_call_original
compare = repository.compare_source_branch('master', mutable_repository, 'non-existent-branch', straight: false)
expect(compare).to be_nil
end
end
repository.compare_source_branch('master', :source_repository, 'feature', straight: :straight)
end
end
 
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::NoCacheHeaders do
class NoCacheTester
include Gitlab::NoCacheHeaders
end
describe "#no_cache_headers" do
subject { NoCacheTester.new }
it "raises a RuntimeError" do
expect { subject.no_cache_headers }.to raise_error(RuntimeError)
end
end
end
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