Commit 54cd7639 authored by Steve Abrams's avatar Steve Abrams Committed by Andreas Brandl

Add package max file size limits

Add file upload size limits for conan, npm,
nuget, pypi, and maven.
parent 534ef27f
......@@ -7,6 +7,7 @@ module Packages
def execute
return error('Version is empty.', 400) if version.blank?
return error('Package already exists.', 403) if current_package_exists?
return error('File is too large.', 400) if file_size_exceeded?
ActiveRecord::Base.transaction { create_package! }
end
......@@ -86,6 +87,10 @@ module Packages
_version, versions_data = params[:versions].first
versions_data
end
def file_size_exceeded?
project.actual_limits.exceeded?(:npm_max_file_size, attachment['length'].to_i)
end
end
end
end
---
title: Add package file size limits to plan limits
merge_request: 39633
author:
type: added
# frozen_string_literal: true
class AddPackageMaxFileSizeToPlanLimits < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column(:plan_limits, :conan_max_file_size, :bigint, default: 50.megabytes, null: false)
add_column(:plan_limits, :maven_max_file_size, :bigint, default: 50.megabytes, null: false)
add_column(:plan_limits, :npm_max_file_size, :bigint, default: 50.megabytes, null: false)
add_column(:plan_limits, :nuget_max_file_size, :bigint, default: 50.megabytes, null: false)
add_column(:plan_limits, :pypi_max_file_size, :bigint, default: 50.megabytes, null: false)
end
end
987f316571f41ad679cad54089bc523f62d04691c10e5cf1957cf60edd71f889
\ No newline at end of file
......@@ -14103,7 +14103,12 @@ CREATE TABLE public.plan_limits (
ci_max_artifact_size_coverage_fuzzing integer DEFAULT 0 NOT NULL,
ci_max_artifact_size_browser_performance integer DEFAULT 0 NOT NULL,
ci_max_artifact_size_load_performance integer DEFAULT 0 NOT NULL,
ci_needs_size_limit integer DEFAULT 50 NOT NULL
ci_needs_size_limit integer DEFAULT 50 NOT NULL,
conan_max_file_size bigint DEFAULT 52428800 NOT NULL,
maven_max_file_size bigint DEFAULT 52428800 NOT NULL,
npm_max_file_size bigint DEFAULT 52428800 NOT NULL,
nuget_max_file_size bigint DEFAULT 52428800 NOT NULL,
pypi_max_file_size bigint DEFAULT 52428800 NOT NULL
);
CREATE SEQUENCE public.plan_limits_id_seq
......
......@@ -514,3 +514,38 @@ Total number of changes (branches or tags) in a single push to determine whether
individual push events or bulk push event will be created.
More information can be found in the [Push event activities limit and bulk push events documentation](../user/admin_area/settings/push_event_activities_limit.md).
## Package Registry Limits
### File Size Limits
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218017) in GitLab 13.4.
On GitLab.com, the maximum file size for a package that's uploaded to the [GitLab Package Registry](../user/packages/package_registry/index.md)
is 50 megabytes.
Limits are set per package type.
To set this limit on a self-managed installation, run the following in the
[GitLab Rails console](troubleshooting/debug.md#starting-a-rails-console-session):
```ruby
# File size limit is stored in bytes
# For Conan Packages
Plan.default.actual_limits.update!(conan_max_file_size: 100.megabytes)
# For NPM Packages
Plan.default.actual_limits.update!(npm_max_file_size: 100.megabytes)
# For NuGet Packages
Plan.default.actual_limits.update!(nuget_max_file_size: 100.megabytes)
# For Maven Packages
Plan.default.actual_limits.update!(maven_max_file_size: 100.megabytes)
# For PyPI Packages
Plan.default.actual_limits.update!(pypi_max_file_size: 100.megabytes)
```
Set the limit to `0` to allow any file size.
......@@ -293,7 +293,7 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
put 'authorize' do
authorize_workhorse!(subject: project)
authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
end
end
......@@ -320,7 +320,7 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
put 'authorize' do
authorize_workhorse!(subject: project)
authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
end
desc 'Upload package files' do
......
......@@ -155,6 +155,7 @@ module API
def upload_package_file(file_type)
authorize_upload!(project)
bad_request!('File is too large') if project.actual_limits.exceeded?(:conan_max_file_size, params['file.size'].to_i)
current_package = find_or_create_package
......
......@@ -200,7 +200,7 @@ module API
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
::Packages::PackageFileUploader.workhorse_authorize(has_length: true)
::Packages::PackageFileUploader.workhorse_authorize(has_length: true, maximum_size: user_project.actual_limits.maven_max_file_size)
end
desc 'Upload the maven package file' do
......@@ -214,6 +214,7 @@ module API
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
put ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
authorize_upload!
bad_request!('File is too large') if user_project.actual_limits.exceeded?(:maven_max_file_size, params[:file].size)
file_name, format = extract_format(params[:file_name])
......
......@@ -92,6 +92,7 @@ module API
put do
authorize_upload!(authorized_user_project)
bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
file_params = params.merge(
file: params[:package],
......@@ -118,7 +119,11 @@ module API
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
put 'authorize' do
authorize_workhorse!(subject: authorized_user_project, has_length: false)
authorize_workhorse!(
subject: authorized_user_project,
has_length: false,
maximum_size: authorized_user_project.actual_limits.nuget_max_file_size
)
end
params do
......
......@@ -120,6 +120,7 @@ module API
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true
post do
authorize_upload!(authorized_user_project)
bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:pypi_max_file_size, params[:content].size)
track_event('push_package')
......@@ -136,7 +137,11 @@ module API
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true
post 'authorize' do
authorize_workhorse!(subject: authorized_user_project, has_length: false)
authorize_workhorse!(
subject: authorized_user_project,
has_length: false,
maximum_size: authorized_user_project.actual_limits.pypi_max_file_size
)
end
end
end
......
......@@ -482,4 +482,17 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to contain_exactly(*tags) }
end
end
describe 'plan_limits' do
Packages::Package.package_types.keys.without('composer').each do |pt|
context "File size limits for #{pt}" do
let(:package) { create("#{pt}_package") }
it "plan_limits includes column #{pt}_max_file_size" do
expect { package.project.actual_limits.send("#{pt}_max_file_size") }
.not_to raise_error(NoMethodError)
end
end
end
end
end
......@@ -681,6 +681,18 @@ RSpec.describe API::ConanPackages do
let(:recipe_path) { "foo/bar/#{project.full_path.tr('/', '+')}/baz"}
shared_examples 'uploads a package file' do
context 'file size above maximum limit' do
before do
params['file.size'] = project.actual_limits.conan_max_file_size + 1
end
it 'handles as a local file' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'with object storage disabled' do
context 'without a file from workhorse' do
let(:params) { { file: nil } }
......
......@@ -528,6 +528,18 @@ RSpec.describe API::MavenPackages do
context 'when params from workhorse are correct' do
let(:params) { { file: file_upload } }
context 'file size is too large' do
it 'rejects the request' do
allow_next_instance_of(UploadedFile) do |uploaded_file|
allow(uploaded_file).to receive(:size).and_return(project.actual_limits.maven_max_file_size + 1)
end
upload_file_with_token(params)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
it 'rejects a malicious request' do
put api("/projects/#{project.id}/packages/maven/com/example/my-app/#{version}/%2e%2e%2f.ssh%2fauthorized_keys"), params: params, headers: headers_with_token
......
......@@ -220,6 +220,18 @@ RSpec.describe API::NugetPackages do
it_behaves_like 'rejects nuget access with unknown project id'
it_behaves_like 'rejects nuget access with invalid project id'
context 'file size above maximum limit' do
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
before do
allow_next_instance_of(UploadedFile) do |uploaded_file|
allow(uploaded_file).to receive(:size).and_return(project.actual_limits.nuget_max_file_size + 1)
end
end
it_behaves_like 'returning response status', :bad_request
end
end
end
......
......@@ -185,6 +185,18 @@ RSpec.describe API::PypiPackages do
it_behaves_like 'deploy token for package uploads'
it_behaves_like 'rejects PyPI access with unknown project id'
context 'file size above maximum limit' do
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
before do
allow_next_instance_of(UploadedFile) do |uploaded_file|
allow(uploaded_file).to receive(:size).and_return(project.actual_limits.pypi_max_file_size + 1)
end
end
it_behaves_like 'returning response status', :bad_request
end
end
describe 'GET /api/v4/projects/:id/packages/pypi/files/:sha256/*file_identifier' do
......
......@@ -61,6 +61,15 @@ RSpec.describe Packages::Npm::CreatePackageService do
it { expect(subject[:message]).to be 'Package already exists.' }
end
context 'file size above maximum limit' do
before do
params['_attachments']["#{package_name}-#{version}.tgz"]['length'] = project.actual_limits.npm_max_file_size + 1
end
it { expect(subject[:http_status]).to eq 400 }
it { expect(subject[:message]).to be 'File is too large.' }
end
context 'with incorrect namespace' do
let(:package_name) { '@my_other_namespace/my-app' }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment