From afd24ba01af3b888ad183c31e9f522cfbc103853 Mon Sep 17 00:00:00 2001 From: Enrique Alcantara Date: Wed, 9 Jul 2025 16:38:25 +0200 Subject: [PATCH] Add CORS headers to git HTTP protocol routes --- app/helpers/application_settings_helper.rb | 1 + app/models/application_setting.rb | 3 ++- app/models/application_setting_implementation.rb | 1 + ...ion_setting_vscode_extension_marketplace.json | 5 +++++ config/application.rb | 16 ++++++++++++++++ lib/web_ide/extension_marketplace.rb | 4 ++++ spec/models/application_setting_spec.rb | 1 + workhorse/internal/api/api.go | 16 ++++++++++++++++ 8 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index f178073dd37c49..84d3dbc6fbcf58 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -631,6 +631,7 @@ def visible_attributes :minimum_language_server_version, :vscode_extension_marketplace, :vscode_extension_marketplace_enabled, + :vscode_extension_marketplace_extension_host_domain, :reindexing_minimum_index_size, :reindexing_minimum_relative_bloat_size, :anonymous_searches_allowed, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 951e5c76135902..02062c8ac9d5e1 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -1006,7 +1006,8 @@ def self.kroki_formats_attributes json_schema: { filename: "application_setting_vscode_extension_marketplace", detail_errors: true } jsonb_accessor :vscode_extension_marketplace, - vscode_extension_marketplace_enabled: [:boolean, { default: false, store_key: :enabled }] + vscode_extension_marketplace_enabled: [:boolean, { default: false, store_key: :enabled }], + vscode_extension_marketplace_extension_host_domain: [:string, { default: false, store_key: :extension_host_domain }] jsonb_accessor :editor_extensions, enable_language_server_restrictions: [:boolean, { default: false }], diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index f4a7b26dad42d9..2327d63184980e 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -343,6 +343,7 @@ def defaults # rubocop:disable Metrics/AbcSize top_level_group_creation_enabled: true, ropc_without_client_credentials: true, vscode_extension_marketplace_enabled: false, + vscode_extension_marketplace_extension_host_domain: '.cdn.web-ide.gitlab-static.net', reindexing_minimum_index_size: 1.gigabyte, reindexing_minimum_relative_bloat_size: 0.2, git_push_pipeline_limit: 4, diff --git a/app/validators/json_schemas/application_setting_vscode_extension_marketplace.json b/app/validators/json_schemas/application_setting_vscode_extension_marketplace.json index 54c0c0ae07cb6e..5dc7bd999569f8 100644 --- a/app/validators/json_schemas/application_setting_vscode_extension_marketplace.json +++ b/app/validators/json_schemas/application_setting_vscode_extension_marketplace.json @@ -9,6 +9,11 @@ "default": false, "description": "Should the VSCode Extension Marketplace be enabled for Web IDE and Workspaces" }, + "extension_host_domain": { + "type": "string", + "default": false, + "description": "Wildcard domain pointing to the HTTP server hosting the Web IDE VSCode Workbench assets used to run extensions." + }, "preset": { "enum": [ "custom", diff --git a/config/application.rb b/config/application.rb index a1667dea3ce52a..796c2e74537064 100644 --- a/config/application.rb +++ b/config/application.rb @@ -526,6 +526,22 @@ class Application < Rails::Application credentials: false, methods: %i[get head] end + + allow do + origins { |source, env| source.starts_with?('https://') && source.ends_with?(WebIde::ExtensionMarketplace.extension_host_domain) } + resource '*/info/refs', + credentials: false, + methods: %i[get], + headers: %w[Authorization] + end + + allow do + origins { |source, env| source.starts_with?('https://') && source.ends_with?(WebIde::ExtensionMarketplace.extension_host_domain) } + resource '*/git-upload-pack', + credentials: false, + methods: %i[options post], + headers: %w[Authorization] + end end # Use caching across all environments diff --git a/lib/web_ide/extension_marketplace.rb b/lib/web_ide/extension_marketplace.rb index c6c0fd7ee21576..e49f27c91b91e9 100644 --- a/lib/web_ide/extension_marketplace.rb +++ b/lib/web_ide/extension_marketplace.rb @@ -35,5 +35,9 @@ def self.help_preferences_url def self.webide_extension_marketplace_settings(user:) Settings.get_single_setting(:vscode_extension_marketplace_view_model, user: user) end + + def self.extension_host_domain + Gitlab::CurrentSettings.vscode_extension_marketplace_extension_host_domain + end end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 858a2405fd8129..ac031c56fbcaf9 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -294,6 +294,7 @@ valid_runner_registrars: ApplicationSettingImplementation::VALID_RUNNER_REGISTRAR_TYPES, vscode_extension_marketplace: { 'enabled' => false }, vscode_extension_marketplace_enabled?: false, + vscode_extension_marketplace_extension_host_domain: '.cdn.web-ide.gitlab-static.net', whats_new_variant: 'all_tiers', # changed from 0 to "all_tiers" due to enum conversion wiki_asciidoc_allow_uri_includes: false, wiki_page_max_content_bytes: 50.megabytes diff --git a/workhorse/internal/api/api.go b/workhorse/internal/api/api.go index 682891cbc60bcf..97f341f394a809 100644 --- a/workhorse/internal/api/api.go +++ b/workhorse/internal/api/api.go @@ -445,6 +445,7 @@ func (api *API) PreAuthorizeHandler(next HandleFunc, suffix string) http.Handler defer func() { _ = httpResponse.Body.Close() }() copyAuthHeader(httpResponse, w) + copyCorsHeaders(httpResponse, w) next(w, r, authResponse) }) @@ -479,6 +480,21 @@ func copyAuthHeader(httpResponse *http.Response, w http.ResponseWriter) { } } +func copyCorsHeaders(httpResponse *http.Response, w http.ResponseWriter) { + // Negotiate authentication (Kerberos) may need to return a WWW-Authenticate + // header to the client even in case of success as per RFC4559. + var allowedHeaders = map[string]bool{ + "access-control-allow-origin": true, + "access-control-allow-methods": true, + } + for k, v := range httpResponse.Header { + // Case-insensitive comparison as per RFC7230 + if allowedHeaders[strings.ToLower(k)] { + w.Header()[k] = v + } + } +} + // These are internal headers that may be returned by the rails backend. They should not be passed back to the client. var sensitiveHeaders = map[string]bool{ "gitlab-workhorse-send-data": true, -- GitLab