diff --git a/changelogs/unreleased/sh-fix-cpp-templates-404.yml b/changelogs/unreleased/sh-fix-cpp-templates-404.yml new file mode 100644 index 00000000000..ac958d84099 --- /dev/null +++ b/changelogs/unreleased/sh-fix-cpp-templates-404.yml @@ -0,0 +1,5 @@ +--- +title: Fix 404s when C++ .gitignore template selected +merge_request: 25416 +author: +type: fixed diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb index d05ddad7466..119902a189c 100644 --- a/lib/api/project_templates.rb +++ b/lib/api/project_templates.rb @@ -36,7 +36,10 @@ module API optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses' optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses' end - get ':id/templates/:type/:name', requirements: { name: /[\w\.-]+/ } do + # The regex is needed to ensure a period (e.g. agpl-3.0) + # isn't confused with a format type. We also need to allow encoded + # values (e.g. C%2B%2B for C++), so allow % and + as well. + get ':id/templates/:type/:name', requirements: { name: /[\w%.+-]+/ } do template = TemplateFinder .build(params[:type], user_project, name: params[:name]) .execute diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb index ab5d4de7ff7..80e5033dab4 100644 --- a/spec/requests/api/project_templates_spec.rb +++ b/spec/requests/api/project_templates_spec.rb @@ -92,6 +92,22 @@ describe API::ProjectTemplates do 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(200) + 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(200) + 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") @@ -125,6 +141,18 @@ describe API::ProjectTemplates do expect(response).to have_gitlab_http_status(200) expect(response).to match_response_schema('public_api/v4/license') end + + shared_examples 'path traversal attempt' do |template_type| + 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(500) + end + end + + TemplateFinder::VENDORED_TEMPLATES.each do |template_type, _| + it_behaves_like 'path traversal attempt', template_type + end end describe 'GET /projects/:id/templates/licenses/:key' do