352 lines
15 KiB
Ruby
352 lines
15 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe API::ProjectTemplates, feature_category: :source_code_management do
|
|
let_it_be(:public_project) { create(:project, :public, :repository, create_templates: :merge_request, path: 'path.with.dot') }
|
|
let_it_be(:private_project) { create(:project, :private, :repository, create_templates: :issue) }
|
|
let_it_be(:reporter) { create(:user) }
|
|
let_it_be(:guest) { create(:user) }
|
|
|
|
let(:url_encoded_path) { "#{public_project.namespace.path}%2F#{public_project.path}" }
|
|
|
|
before do
|
|
stub_feature_flags(remove_monitor_metrics: false)
|
|
private_project.add_developer(reporter)
|
|
private_project.add_guest(guest)
|
|
end
|
|
|
|
shared_examples 'accepts project paths with dots' do
|
|
it do
|
|
subject
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
end
|
|
end
|
|
|
|
describe 'GET /projects/:id/templates/:type' do
|
|
it_behaves_like 'accepts project paths with dots' do
|
|
subject { get api("/projects/#{url_encoded_path}/templates/dockerfiles") }
|
|
end
|
|
|
|
it 'returns dockerfiles' do
|
|
get api("/projects/#{public_project.id}/templates/dockerfiles")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to include_pagination_headers
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
expect(json_response).to satisfy_one { |template| template['key'] == 'Binary' }
|
|
end
|
|
|
|
it 'returns gitignores' do
|
|
get api("/projects/#{public_project.id}/templates/gitignores")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to include_pagination_headers
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
expect(json_response).to satisfy_one { |template| template['key'] == 'Actionscript' }
|
|
end
|
|
|
|
it 'returns gitlab_ci_ymls' do
|
|
get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to include_pagination_headers
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
expect(json_response).to satisfy_one { |template| template['key'] == 'Android' }
|
|
end
|
|
|
|
it 'returns licenses' do
|
|
get api("/projects/#{public_project.id}/templates/licenses")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to include_pagination_headers
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
expect(json_response).to satisfy_one { |template| template['key'] == '0bsd' }
|
|
end
|
|
|
|
it 'returns issue templates' do
|
|
get api("/projects/#{private_project.id}/templates/issues", reporter)
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to include_pagination_headers
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
expect(json_response.map { |t| t['key'] }).to match_array(%w[bug feature_proposal template_test (test)])
|
|
end
|
|
|
|
it 'returns merge request templates' do
|
|
get api("/projects/#{public_project.id}/templates/merge_requests")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to include_pagination_headers
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
expect(json_response.map { |t| t['key'] }).to match_array(%w[bug feature_proposal template_test (test)])
|
|
end
|
|
|
|
it 'returns 400 for an unknown template type' do
|
|
get api("/projects/#{public_project.id}/templates/unknown")
|
|
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
|
end
|
|
|
|
it 'denies access to an anonymous user on a private project' do
|
|
get api("/projects/#{private_project.id}/templates/licenses")
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
end
|
|
|
|
it 'permits access to a developer on a private project' do
|
|
get api("/projects/#{private_project.id}/templates/licenses", reporter)
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
end
|
|
|
|
context 'when a guest has no permission to a template' do
|
|
it 'denies access to the dockerfile' do
|
|
get api("/projects/#{private_project.id}/templates/dockerfiles", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the dockerfiles template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the gitignore' do
|
|
get api("/projects/#{private_project.id}/templates/gitignores", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the gitignores template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the gitlab_ci_yml' do
|
|
get api("/projects/#{private_project.id}/templates/gitlab_ci_ymls", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the gitlab_ci_ymls template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the license' do
|
|
get api("/projects/#{private_project.id}/templates/licenses", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the licenses template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the merge request' do
|
|
get api("/projects/#{private_project.id}/templates/merge_requests", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the merge_requests template. Contact your project administrator for assistance.")
|
|
end
|
|
end
|
|
|
|
context 'when a guest has permission to an issues template' do
|
|
it 'returns an issue template' do
|
|
get api("/projects/#{private_project.id}/templates/issues", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to include_pagination_headers
|
|
expect(response).to match_response_schema('public_api/v4/template_list')
|
|
expect(json_response.map { |t| t['key'] }).to match_array(%w[bug feature_proposal template_test (test)])
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET /projects/:id/templates/:type/:name' do
|
|
it 'returns a specific dockerfile' do
|
|
get api("/projects/#{public_project.id}/templates/dockerfiles/Binary")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('Binary')
|
|
end
|
|
|
|
it 'returns a specific gitignore' do
|
|
get api("/projects/#{public_project.id}/templates/gitignores/Actionscript")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('Actionscript')
|
|
end
|
|
|
|
it 'returns C++ gitignore' do
|
|
get api("/projects/#{public_project.id}/templates/gitignores/C++")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('C++')
|
|
end
|
|
|
|
it 'returns C++ gitignore for URL-encoded names' do
|
|
get api("/projects/#{public_project.id}/templates/gitignores/C%2B%2B")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('C++')
|
|
end
|
|
|
|
it 'returns a specific gitlab_ci_yml' do
|
|
get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls/Android")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('Android')
|
|
end
|
|
|
|
it 'returns a specific license' do
|
|
get api("/projects/#{public_project.id}/templates/licenses/mit")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/license')
|
|
end
|
|
|
|
it 'returns a specific issue template' do
|
|
get api("/projects/#{private_project.id}/templates/issues/bug", reporter)
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('bug')
|
|
expect(json_response['content']).to eq('something valid')
|
|
end
|
|
|
|
context 'when issue template uses parentheses' do
|
|
it 'returns a specific issue template' do
|
|
get api("/projects/#{private_project.id}/templates/issues/(test)", reporter)
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('(test)')
|
|
expect(json_response['content']).to eq('parentheses')
|
|
end
|
|
end
|
|
|
|
it 'returns a specific merge request template' do
|
|
get api("/projects/#{public_project.id}/templates/merge_requests/feature_proposal")
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('feature_proposal')
|
|
expect(json_response['content']).to eq('feature_proposal') # Content is identical to filename here
|
|
end
|
|
|
|
it 'returns 404 for an unknown specific template' do
|
|
get api("/projects/#{public_project.id}/templates/licenses/unknown")
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
end
|
|
|
|
it 'returns 404 for an unknown issue template' do
|
|
get api("/projects/#{public_project.id}/templates/issues/unknown")
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
end
|
|
|
|
it 'returns 404 for an unknown merge request template' do
|
|
get api("/projects/#{public_project.id}/templates/merge_requests/unknown")
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
end
|
|
|
|
it 'denies access to an anonymous user on a private project' do
|
|
get api("/projects/#{private_project.id}/templates/licenses/mit")
|
|
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
end
|
|
|
|
it 'permits access to a developer on a private project' do
|
|
get api("/projects/#{private_project.id}/templates/licenses/mit", reporter)
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/license')
|
|
end
|
|
|
|
it_behaves_like 'accepts project paths with dots' do
|
|
subject { get api("/projects/#{url_encoded_path}/templates/gitlab_ci_ymls/Android") }
|
|
end
|
|
|
|
shared_examples 'path traversal attempt' do |template_type|
|
|
before do
|
|
# TODO: remove spec once the feature flag is removed
|
|
# https://gitlab.com/gitlab-org/gitlab/-/issues/415460
|
|
stub_feature_flags(check_path_traversal_middleware_reject_requests: false)
|
|
end
|
|
|
|
it 'rejects invalid filenames' do
|
|
get api("/projects/#{public_project.id}/templates/#{template_type}/%2e%2e%2fPython%2ea")
|
|
|
|
expect(response).to have_gitlab_http_status(:internal_server_error)
|
|
end
|
|
end
|
|
|
|
TemplateFinder::VENDORED_TEMPLATES.each do |template_type, _|
|
|
it_behaves_like 'path traversal attempt', template_type
|
|
end
|
|
|
|
context 'when a guest has no permission to a template' do
|
|
it 'denies access to the dockerfile' do
|
|
get api("/projects/#{private_project.id}/templates/dockerfiles/Binary", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the dockerfiles template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the gitignore' do
|
|
get api("/projects/#{private_project.id}/templates/gitignores/Actionscript", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the gitignores template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the gitlab_ci_yml' do
|
|
get api("/projects/#{private_project.id}/templates/gitlab_ci_ymls/Android", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the gitlab_ci_ymls template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the license' do
|
|
get api("/projects/#{private_project.id}/templates/licenses/mit", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the licenses template. Contact your project administrator for assistance.")
|
|
end
|
|
|
|
it 'denies access to the merge request' do
|
|
get api("/projects/#{private_project.id}/templates/merge_requests/feature_proposal", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
|
expect(json_response['message']).to eq("403 Forbidden - Your current role does not have the required permissions to access the merge_requests template. Contact your project administrator for assistance.")
|
|
end
|
|
end
|
|
|
|
context 'when a guest has permission to an issues template' do
|
|
it 'returns an issue template' do
|
|
get api("/projects/#{private_project.id}/templates/issues/bug", guest)
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/template')
|
|
expect(json_response['name']).to eq('bug')
|
|
expect(json_response['content']).to eq('something valid')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET /projects/:id/templates/licenses/:key' do
|
|
it 'fills placeholders in the license' do
|
|
get api("/projects/#{public_project.id}/templates/licenses/agpl-3.0"),
|
|
params: { project: 'Project Placeholder', fullname: 'Fullname Placeholder' }
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
expect(response).to match_response_schema('public_api/v4/license')
|
|
|
|
content = json_response['content']
|
|
|
|
expect(content).to include('Project Placeholder')
|
|
expect(content).to include("Copyright (C) #{Time.now.year} Fullname Placeholder")
|
|
end
|
|
|
|
it_behaves_like 'accepts project paths with dots' do
|
|
subject { get api("/projects/#{url_encoded_path}/templates/licenses/mit") }
|
|
end
|
|
end
|
|
end
|