diff --git a/app/assets/javascripts/static_site_editor/components/edit_area.vue b/app/assets/javascripts/static_site_editor/components/edit_area.vue index e602f26acdf..56f1a26f005 100644 --- a/app/assets/javascripts/static_site_editor/components/edit_area.vue +++ b/app/assets/javascripts/static_site_editor/components/edit_area.vue @@ -37,6 +37,10 @@ export default { required: false, default: '', }, + mounts: { + type: Array, + required: true, + }, imageRoot: { type: String, required: false, diff --git a/app/assets/javascripts/static_site_editor/graphql/index.js b/app/assets/javascripts/static_site_editor/graphql/index.js index cc68bc57bb0..a13f7d3ad53 100644 --- a/app/assets/javascripts/static_site_editor/graphql/index.js +++ b/app/assets/javascripts/static_site_editor/graphql/index.js @@ -25,11 +25,15 @@ const createApolloProvider = appData => { }, ); + // eslint-disable-next-line @gitlab/require-i18n-strings + const mounts = appData.mounts.map(mount => ({ __typename: 'Mount', ...mount })); + defaultClient.cache.writeData({ data: { appData: { __typename: 'AppData', ...appData, + mounts, }, }, }); diff --git a/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql b/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql index 9f4b0afe55f..3b377b9288a 100644 --- a/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql +++ b/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql @@ -6,5 +6,9 @@ query appData { sourcePath username returnUrl + mounts { + source + target + } } } diff --git a/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql b/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql index 0ded1722d26..b8808f4e425 100644 --- a/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql +++ b/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql @@ -14,6 +14,11 @@ type SavedContentMeta { branch: SavedContentField! } +type Mount { + source: String! + target: String +} + type AppData { isSupportedContent: Boolean! hasSubmittedChanges: Boolean! @@ -21,6 +26,7 @@ type AppData { returnUrl: String sourcePath: String! username: String! + mounts: [Mount]! } input HasSubmittedChangesInput { diff --git a/app/assets/javascripts/static_site_editor/index.js b/app/assets/javascripts/static_site_editor/index.js index fceef8f9084..ceb1d820c83 100644 --- a/app/assets/javascripts/static_site_editor/index.js +++ b/app/assets/javascripts/static_site_editor/index.js @@ -20,9 +20,6 @@ const initStaticSiteEditor = el => { imageUploadPath, mounts, } = el.dataset; - // NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object. - // eslint-disable-next-line no-unused-vars - const mountsObject = JSON.parse(mounts); const { current_username: username } = window.gon; const returnUrl = el.dataset.returnUrl || null; const router = createRouter(baseUrl); @@ -30,6 +27,7 @@ const initStaticSiteEditor = el => { isSupportedContent: parseBoolean(isSupportedContent), hasSubmittedChanges: false, project: `${namespace}/${project}`, + mounts: JSON.parse(mounts), // NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object. returnUrl, sourcePath, username, diff --git a/app/assets/javascripts/static_site_editor/pages/home.vue b/app/assets/javascripts/static_site_editor/pages/home.vue index 27bd1c99ae2..d7e52ef9c45 100644 --- a/app/assets/javascripts/static_site_editor/pages/home.vue +++ b/app/assets/javascripts/static_site_editor/pages/home.vue @@ -138,6 +138,7 @@ export default { :content="sourceContent.content" :saving-changes="isSavingChanges" :return-url="appData.returnUrl" + :mounts="appData.mounts" @submit="onPrepareSubmit" /> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25998) in GitLab 12.9 diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md index 199b244b2c3..436a0eb93f1 100644 --- a/doc/api/api_resources.md +++ b/doc/api/api_resources.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # API resources Available resources for the [GitLab API](README.md) can be grouped in the following contexts: diff --git a/doc/api/appearance.md b/doc/api/appearance.md index 47a9d48a4ae..ad44c93ce32 100644 --- a/doc/api/appearance.md +++ b/doc/api/appearance.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Appearance API **(CORE ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16647) in GitLab 12.7. diff --git a/doc/api/applications.md b/doc/api/applications.md index 379f346c019..a3216bdddde 100644 --- a/doc/api/applications.md +++ b/doc/api/applications.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Applications API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8160) in GitLab 10.5. diff --git a/doc/api/audit_events.md b/doc/api/audit_events.md index 5f31919c52b..5fdf0c20f1a 100644 --- a/doc/api/audit_events.md +++ b/doc/api/audit_events.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Audit Events API ## Instance Audit Events **(PREMIUM ONLY)** diff --git a/doc/api/avatar.md b/doc/api/avatar.md index 223704d3e6c..aec1ba67d45 100644 --- a/doc/api/avatar.md +++ b/doc/api/avatar.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Avatar API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/19121) in GitLab 11.0. diff --git a/doc/api/broadcast_messages.md b/doc/api/broadcast_messages.md index 37156186d03..3d243b2a03f 100644 --- a/doc/api/broadcast_messages.md +++ b/doc/api/broadcast_messages.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Broadcast Messages API > Introduced in GitLab 8.12. diff --git a/doc/api/custom_attributes.md b/doc/api/custom_attributes.md index 07ece99f9b1..76c8474ee95 100644 --- a/doc/api/custom_attributes.md +++ b/doc/api/custom_attributes.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Custom Attributes API Every API call to custom attributes must be authenticated as administrator. diff --git a/doc/api/dependencies.md b/doc/api/dependencies.md index 56d33bf151a..2f65ff7b8d9 100644 --- a/doc/api/dependencies.md +++ b/doc/api/dependencies.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Dependencies API **(ULTIMATE)** CAUTION: **Caution:** diff --git a/doc/api/dependency_proxy.md b/doc/api/dependency_proxy.md index a379f1481c1..fb100cc90d8 100644 --- a/doc/api/dependency_proxy.md +++ b/doc/api/dependency_proxy.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Dependency Proxy API **(PREMIUM)** ## Purge the dependency proxy for a group diff --git a/doc/api/epic_links.md b/doc/api/epic_links.md index 19c8dc78aed..c4afb1e7299 100644 --- a/doc/api/epic_links.md +++ b/doc/api/epic_links.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Epic Links API **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9188) in GitLab 11.8. diff --git a/doc/api/events.md b/doc/api/events.md index 3f4f11b9786..4da4bfe8bd4 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Events ## Filter parameters diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md index 064bd26ee72..f198b4f4aa2 100644 --- a/doc/api/geo_nodes.md +++ b/doc/api/geo_nodes.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Geo Nodes API **(PREMIUM ONLY)** To interact with Geo node endpoints, you need to authenticate yourself as an diff --git a/doc/api/graphql/audit_report.md b/doc/api/graphql/audit_report.md index 36c3f44ff89..12663654026 100644 --- a/doc/api/graphql/audit_report.md +++ b/doc/api/graphql/audit_report.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Set up an Audit Report with GraphQL This page describes how you can use the GraphiQL explorer to set up an audit report diff --git a/doc/api/graphql/getting_started.md b/doc/api/graphql/getting_started.md index c2220403461..8501e76b5aa 100644 --- a/doc/api/graphql/getting_started.md +++ b/doc/api/graphql/getting_started.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Getting started with GitLab GraphQL API This guide demonstrates basic usage of GitLab's GraphQL API. diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md index bda24a7e90a..e0557db6567 100644 --- a/doc/api/graphql/index.md +++ b/doc/api/graphql/index.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # GraphQL API > - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/19008) in GitLab 11.0 (enabled by feature flag `graphql`). diff --git a/doc/api/graphql/sample_issue_boards.md b/doc/api/graphql/sample_issue_boards.md index 4ac9317b01a..27fd1f48aec 100644 --- a/doc/api/graphql/sample_issue_boards.md +++ b/doc/api/graphql/sample_issue_boards.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Identify issue boards with GraphQL This page describes how you can use the GraphiQL explorer to identify diff --git a/doc/api/group_activity_analytics.md b/doc/api/group_activity_analytics.md index 90920d1b25c..38394c6412d 100644 --- a/doc/api/group_activity_analytics.md +++ b/doc/api/group_activity_analytics.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Group Activity Analytics API > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26460) in GitLab 12.9. diff --git a/doc/api/group_badges.md b/doc/api/group_badges.md index 43e1944226d..e13b9633da9 100644 --- a/doc/api/group_badges.md +++ b/doc/api/group_badges.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Group badges API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17082) in GitLab 10.6. diff --git a/doc/api/group_import_export.md b/doc/api/group_import_export.md index 01d81eb62ac..dfd78e88233 100644 --- a/doc/api/group_import_export.md +++ b/doc/api/group_import_export.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + # Group Import/Export API > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20353) in GitLab 12.8. diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md index c09032bffb2..a9a307dd89a 100644 --- a/doc/user/group/epics/manage_epics.md +++ b/doc/user/group/epics/manage_epics.md @@ -23,9 +23,9 @@ selected group. From your group page: To create an epic from the epic list, in a group: 1. Go to **{epic}** **Epics**. -1. Click **New epic**. +1. Select **New epic**. 1. Enter a descriptive title. -1. Click **Create epic**. +1. Select **Create epic**. ### Access the New Epic form @@ -33,8 +33,8 @@ To create an epic from the epic list, in a group: There are two ways to get to the New Epic form and create an epic in the group you're in: -- From an epic in your group, click **New Epic**. -- From anywhere, in the top menu, click **plus** (**{plus-square}**) **> New epic**. +- From an epic in your group, select **New Epic**. +- From anywhere, in the top menu, select **plus** (**{plus-square}**) **> New epic**. ![New epic from an open epic](img/new_epic_from_groups_v13.2.png) @@ -63,13 +63,13 @@ After you create an epic, you can edit change the following details: To edit an epic's title or description: -1. Click the **Edit title and description** **{pencil}** button. +1. Select the **Edit title and description** **{pencil}** button. 1. Make your changes. -1. Click **Save changes**. +1. Select **Save changes**. To edit an epics' start date, due date, or labels: -1. Click **Edit** next to each section in the epic sidebar. +1. Select **Edit** next to each section in the epic sidebar. 1. Select the dates or labels for your epic. ## Bulk-edit epics @@ -82,7 +82,7 @@ You can edit multiple epics at once. To learn how to do it, visit NOTE: **Note:** To delete an epic, you need to be an [Owner](../../permissions.md#group-members-permissions) of a group/subgroup. -When editing the description of an epic, click the **Delete** button to delete the epic. +When editing the description of an epic, select the **Delete** button to delete the epic. A modal appears to confirm your action. Deleting an epic releases all existing issues from their associated epic in the system. @@ -92,7 +92,7 @@ Deleting an epic releases all existing issues from their associated epic in the Whenever you decide that there is no longer need for that epic, close the epic by: -- Clicking the **Close epic** button. +- Selecting the **Close epic** button. ![close epic - button](img/button_close_epic.png) @@ -129,7 +129,7 @@ that of Issues and Merge Requests) based on following parameters: ![epics search](img/epics_search.png) -To search, go to the list of epics and click the field **Search or filter results**. +To search, go to the list of epics and select the field **Search or filter results**. It will display a dropdown menu, from which you can add an author. You can also enter plain text to search by epic title or description. When done, press Enter on your keyboard to filter the list. @@ -168,7 +168,7 @@ To make an epic confidential: ### Add a new issue to an epic -You can add an existing issue to an epic, or, create a new issue that's +You can add an existing issue to an epic, or create a new issue that's automatically added to the epic. #### Add an existing issue to an epic @@ -183,15 +183,15 @@ current parent. To add a new issue to an epic: -1. Click the **Add** dropdown button. -1. Click **Add a new issue**. +1. On the epic's page, under **Epics and Issues**, select the **Add** dropdown button. +1. Select **Add an existing issue**. 1. Identify the issue to be added, using either of the following methods: - Paste the link of the issue. - Search for the desired issue by entering part of the issue's title, then selecting the desired match (introduced in [GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/-/issues/9126)). If there are multiple issues to be added, press Spacebar and repeat this step. -1. Click **Add**. +1. Select **Add**. #### Create an issue from an epic @@ -202,11 +202,11 @@ while dividing work into smaller parts. To create an issue from an epic: -1. On the epic's page, under **Epics and Issues**, click the **Add** dropdown button and select - **Create new issue**. +1. On the epic's page, under **Epics and Issues**, select the **Add** dropdown button. +1. Select **Add a new issue**. 1. Under **Title**, enter the title for the new issue. 1. From the **Project** dropdown, select the project in which the issue should be created. -1. Click **Create issue**. +1. Select **Create issue**. ### Remove an issue from an epic @@ -215,9 +215,9 @@ After you remove an issue from an epic, the issue will no longer be associated w To remove an issue from an epic: -1. Click the **Remove** (**{close}**) button next to the issue you want to remove. +1. Select the **Remove** (**{close}**) button next to the issue you want to remove. The **Remove issue** warning appears. -1. Click **Remove**. +1. Select **Remove**. ![List of issues assigned to an epic](img/issue_list_v13_1.png) @@ -285,15 +285,15 @@ For more on epic templates, see [Epic Templates - Repeatable sets of issues](htt To add a child epic to an epic: -1. Click the **Add** dropdown button. -1. Click **Add a new epic**. +1. Select the **Add** dropdown button. +1. Select **Add a new epic**. 1. Identify the epic to be added, using either of the following methods: - Paste the link of the epic. - Search for the desired issue by entering part of the epic's title, then selecting the desired match (introduced in [GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/-/issues/9126)). If there are multiple epics to be added, press Spacebar and repeat this step. -1. Click **Add**. +1. Select **Add**. ### Move child epics between epics @@ -325,5 +325,5 @@ To reorder child epics assigned to an epic: To remove a child epic from a parent epic: -1. Click on the x button in the parent epic's list of epics. -1. Click **Remove** in the **Remove epic** warning message. +1. Select the x button in the parent epic's list of epics. +1. Select **Remove** in the **Remove epic** warning message. diff --git a/lib/api/api.rb b/lib/api/api.rb index e6935d4e74a..358967c72d2 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -25,7 +25,8 @@ module API Gitlab::GrapeLogging::Loggers::QueueDurationLogger.new, Gitlab::GrapeLogging::Loggers::PerfLogger.new, Gitlab::GrapeLogging::Loggers::CorrelationIdLogger.new, - Gitlab::GrapeLogging::Loggers::ContextLogger.new + Gitlab::GrapeLogging::Loggers::ContextLogger.new, + Gitlab::GrapeLogging::Loggers::ContentLogger.new ] allow_access_with_scope :api diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml index 4418ff18d73..a51cb61da6d 100644 --- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml @@ -134,6 +134,7 @@ mobsf-android-sast: name: "$SAST_ANALYZER_IMAGE" variables: SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/mobsf:$SAST_ANALYZER_IMAGE_TAG" + MOBSF_API_KEY: key rules: - if: $SAST_DISABLED when: never @@ -152,6 +153,7 @@ mobsf-ios-sast: name: "$SAST_ANALYZER_IMAGE" variables: SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/mobsf:$SAST_ANALYZER_IMAGE_TAG" + MOBSF_API_KEY: key rules: - if: $SAST_DISABLED when: never diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb index 303e1a23e6b..81cfce6a3db 100644 --- a/lib/gitlab/etag_caching/middleware.rb +++ b/lib/gitlab/etag_caching/middleware.rb @@ -54,7 +54,13 @@ module Gitlab add_instrument_for_cache_hit(status_code, route, request) - [status_code, { 'ETag' => etag, 'X-Gitlab-From-Cache' => 'true' }, []] + new_headers = { + 'ETag' => etag, + 'X-Gitlab-From-Cache' => 'true', + ::Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER => route.feature_category + } + + [status_code, new_headers, []] end def track_cache_miss(if_none_match, cached_value_present, route) diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb index 17d9cf08367..769ac2784d1 100644 --- a/lib/gitlab/etag_caching/router.rb +++ b/lib/gitlab/etag_caching/router.rb @@ -3,7 +3,7 @@ module Gitlab module EtagCaching class Router - Route = Struct.new(:regexp, :name) + Route = Struct.new(:regexp, :name, :feature_category) # We enable an ETag for every request matching the regex. # To match a regex the path needs to match the following: # - Don't contain a reserved word (expect for the words used in the @@ -20,59 +20,73 @@ module Gitlab ROUTES = [ Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/noteable/issue/\d+/notes\z), - 'issue_notes' + 'issue_notes', + 'issue_tracking' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/noteable/merge_request/\d+/notes\z), - 'merge_request_notes' + 'merge_request_notes', + 'code_review' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/issues/\d+/realtime_changes\z), - 'issue_title' + 'issue_title', + 'issue_tracking' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/commit/\S+/pipelines\.json\z), - 'commit_pipelines' + 'commit_pipelines', + 'continuous_integration' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/merge_requests/new\.json\z), - 'new_merge_request_pipelines' + 'new_merge_request_pipelines', + 'continuous_integration' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/merge_requests/\d+/pipelines\.json\z), - 'merge_request_pipelines' + 'merge_request_pipelines', + 'continuous_integration' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/pipelines\.json\z), - 'project_pipelines' + 'project_pipelines', + 'continuous_integration' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/pipelines/\d+\.json\z), - 'project_pipeline' + 'project_pipeline', + 'continuous_integration' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/builds/\d+\.json\z), - 'project_build' + 'project_build', + 'continuous_integration' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/clusters/\d+/environments\z), - 'cluster_environments' + 'cluster_environments', + 'continuous_delivery' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/environments\.json\z), - 'environments' + 'environments', + 'continuous_delivery' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/import/github/realtime_changes\.json\z), - 'realtime_changes_import_github' + 'realtime_changes_import_github', + 'importers' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/import/gitea/realtime_changes\.json\z), - 'realtime_changes_import_gitea' + 'realtime_changes_import_gitea', + 'importers' ), Gitlab::EtagCaching::Router::Route.new( %r(#{RESERVED_WORDS_PREFIX}/merge_requests/\d+/cached_widget\.json\z), - 'merge_request_widget' + 'merge_request_widget', + 'code_review' ) ].freeze diff --git a/lib/gitlab/grape_logging/loggers/content_logger.rb b/lib/gitlab/grape_logging/loggers/content_logger.rb new file mode 100644 index 00000000000..658953adc80 --- /dev/null +++ b/lib/gitlab/grape_logging/loggers/content_logger.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Gitlab + module GrapeLogging + module Loggers + class ContentLogger < ::GrapeLogging::Loggers::Base + def parameters(request, _) + { + content_length: request.env['CONTENT_LENGTH'], + content_range: request.env['HTTP_CONTENT_RANGE'] + }.compact + end + end + end + end +end diff --git a/lib/gitlab/middleware/handle_null_bytes.rb b/lib/gitlab/middleware/handle_malformed_strings.rb similarity index 67% rename from lib/gitlab/middleware/handle_null_bytes.rb rename to lib/gitlab/middleware/handle_malformed_strings.rb index c88dfb6ee0b..5fe3e6a1c73 100644 --- a/lib/gitlab/middleware/handle_null_bytes.rb +++ b/lib/gitlab/middleware/handle_malformed_strings.rb @@ -2,9 +2,9 @@ module Gitlab module Middleware - # There is no valid reason for a request to contain a null byte (U+0000) + # There is no valid reason for a request to contain a malformed string # so just return HTTP 400 (Bad Request) if we receive one - class HandleNullBytes + class HandleMalformedStrings NULL_BYTE_REGEX = Regexp.new(Regexp.escape("\u0000")).freeze attr_reader :app @@ -14,18 +14,20 @@ module Gitlab end def call(env) - return [400, {}, ["Bad Request"]] if request_has_null_byte?(env) + return [400, { 'Content-Type' => 'text/plain' }, ['Bad Request']] if request_contains_malformed_string?(env) app.call(env) end private - def request_has_null_byte?(request) - return false if ENV['REJECT_NULL_BYTES'] == "1" + def request_contains_malformed_string?(request) + return false if ENV['DISABLE_REQUEST_VALIDATION'] == '1' request = Rack::Request.new(request) + return true if string_malformed?(request.path) + request.params.values.any? do |value| param_has_null_byte?(value) end @@ -39,7 +41,7 @@ module Gitlab depth += 1 if value.respond_to?(:match) - string_contains_null_byte?(value) + string_malformed?(value) elsif value.respond_to?(:values) value.values.any? do |hash_value| param_has_null_byte?(hash_value, depth) @@ -53,8 +55,11 @@ module Gitlab end end - def string_contains_null_byte?(string) + def string_malformed?(string) string.match?(NULL_BYTE_REGEX) + rescue ArgumentError + # If we're here, we caught a malformed string. Return true + true end end end diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb index 9268190c7e0..1e1888cd826 100644 --- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb +++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb @@ -304,7 +304,7 @@ RSpec.describe 'User comments on a diff', :js do wait_for_requests end - it 'suggestion is presented' do + it 'suggestion is presented', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/268240' do page.within('.diff-discussions') do expect(page).to have_button('Apply suggestion') expect(page).to have_content('Suggested change') diff --git a/spec/frontend/static_site_editor/components/edit_area_spec.js b/spec/frontend/static_site_editor/components/edit_area_spec.js index 7e90b53dd07..8013e712558 100644 --- a/spec/frontend/static_site_editor/components/edit_area_spec.js +++ b/spec/frontend/static_site_editor/components/edit_area_spec.js @@ -15,6 +15,7 @@ import { sourceContentHeaderObjYAML as headerSettings, sourceContentBody as body, returnUrl, + mounts, } from '../mock_data'; jest.mock('~/static_site_editor/services/formatter', () => jest.fn(str => `${str} format-pass`)); @@ -31,6 +32,7 @@ describe('~/static_site_editor/components/edit_area.vue', () => { title, content, returnUrl, + mounts, savingChanges, ...propsData, }, diff --git a/spec/frontend/static_site_editor/mock_data.js b/spec/frontend/static_site_editor/mock_data.js index 0b08e290227..ce7c52e8277 100644 --- a/spec/frontend/static_site_editor/mock_data.js +++ b/spec/frontend/static_site_editor/mock_data.js @@ -67,3 +67,10 @@ export const images = new Map([ ['path/to/image1.png', 'image1-content'], ['path/to/image2.png', 'image2-content'], ]); + +export const mounts = [ + { + source: 'some/source/', + target: '', + }, +]; diff --git a/spec/frontend/static_site_editor/pages/home_spec.js b/spec/frontend/static_site_editor/pages/home_spec.js index 2c69e884005..8fca8d0f2b9 100644 --- a/spec/frontend/static_site_editor/pages/home_spec.js +++ b/spec/frontend/static_site_editor/pages/home_spec.js @@ -23,6 +23,7 @@ import { submitChangesError, trackingCategory, images, + mounts, } from '../mock_data'; const localVue = createLocalVue(); @@ -41,6 +42,7 @@ describe('static_site_editor/pages/home', () => { project, username, sourcePath, + mounts, }; const hasSubmittedChangesMutationPayload = { data: { @@ -119,6 +121,7 @@ describe('static_site_editor/pages/home', () => { it('provides source content, returnUrl, and isSavingChanges to the edit area', () => { expect(findEditArea().props()).toMatchObject({ title, + mounts, content, returnUrl, savingChanges: false, diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb index 361b2329e15..45916e78532 100644 --- a/spec/lib/gitlab/etag_caching/middleware_spec.rb +++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::EtagCaching::Middleware do +RSpec.describe Gitlab::EtagCaching::Middleware, :clean_gitlab_redis_shared_state do let(:app) { double(:app) } let(:middleware) { described_class.new(app) } let(:app_status_code) { 200 } @@ -17,10 +17,12 @@ RSpec.describe Gitlab::EtagCaching::Middleware do mock_app_response end - it 'does not add ETag header' do + it 'does not add ETag headers' do _, headers, _ = middleware.call(build_request(path, if_none_match)) expect(headers['ETag']).to be_nil + expect(headers['X-Gitlab-From-Cache']).to be_nil + expect(headers[::Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER]).to be_nil end it 'passes status code from app' do @@ -68,7 +70,7 @@ RSpec.describe Gitlab::EtagCaching::Middleware do mock_value_in_store('123') end - it 'returns this value as header' do + it 'returns the correct headers' do _, headers, _ = middleware.call(build_request(path, if_none_match)) expect(headers['ETag']).to eq 'W/"123"' @@ -126,6 +128,13 @@ RSpec.describe Gitlab::EtagCaching::Middleware do expect(status).to eq 304 end + it 'sets correct headers' do + _, headers, _ = middleware.call(build_request(path, if_none_match)) + + expect(headers).to include('X-Gitlab-From-Cache' => 'true', + ::Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER => 'issue_tracking') + end + it_behaves_like 'sends a process_action.action_controller notification', 304 it 'returns empty body' do diff --git a/spec/lib/gitlab/etag_caching/router_spec.rb b/spec/lib/gitlab/etag_caching/router_spec.rb index 3e939e588ad..dbd9cc230f1 100644 --- a/spec/lib/gitlab/etag_caching/router_spec.rb +++ b/spec/lib/gitlab/etag_caching/router_spec.rb @@ -127,4 +127,12 @@ RSpec.describe Gitlab::EtagCaching::Router do expect(result).to be_present expect(result.name).to eq 'project_pipeline' end + + it 'has a valid feature category for every route', :aggregate_failures do + feature_categories = YAML.load_file(Rails.root.join('config', 'feature_categories.yml')).to_set + + described_class::ROUTES.each do |route| + expect(feature_categories).to include(route.feature_category), "#{route.name} has a category of #{route.feature_category}, which is not valid" + end + end end diff --git a/spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb b/spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb new file mode 100644 index 00000000000..6680720e403 --- /dev/null +++ b/spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'spec_helper' +require "rack/test" + +RSpec.describe Gitlab::Middleware::HandleMalformedStrings do + let(:null_byte) { "\u0000" } + let(:invalid_string) { "mal\xC0formed" } + let(:error_400) { [400, { 'Content-Type' => 'text/plain' }, ['Bad Request']] } + let(:app) { double(:app) } + + subject { described_class.new(app) } + + before do + allow(app).to receive(:call) do |args| + args + end + end + + def env_for(params = {}) + Rack::MockRequest.env_for('/', { params: params }) + end + + context 'in the URL' do + it 'rejects null bytes' do + # We have to create the env separately or Rack::MockRequest complains about invalid URI + env = env_for + env['PATH_INFO'] = "/someplace/witha#{null_byte}nullbyte" + + expect(subject.call(env)).to eq error_400 + end + + it 'rejects malformed strings' do + # We have to create the env separately or Rack::MockRequest complains about invalid URI + env = env_for + env['PATH_INFO'] = "/someplace/with_an/#{invalid_string}" + + expect(subject.call(env)).to eq error_400 + end + end + + context 'in params' do + shared_examples_for 'checks params' do + it 'rejects bad params in a top level param' do + env = env_for(name: "null#{problematic_input}byte") + + expect(subject.call(env)).to eq error_400 + end + + it "rejects bad params for hashes with strings" do + env = env_for(name: { inner_key: "I am #{problematic_input} bad" }) + + expect(subject.call(env)).to eq error_400 + end + + it "rejects bad params for arrays with strings" do + env = env_for(name: ["I am #{problematic_input} bad"]) + + expect(subject.call(env)).to eq error_400 + end + + it "rejects bad params for arrays containing hashes with string values" do + env = env_for(name: [ + { + inner_key: "I am #{problematic_input} bad" + } + ]) + + expect(subject.call(env)).to eq error_400 + end + + it "gives up and does not reject too deeply nested params" do + env = env_for(name: [ + { + inner_key: { deeper_key: [{ hash_inside_array_key: "I am #{problematic_input} bad" }] } + } + ]) + + expect(subject.call(env)).not_to eq error_400 + end + end + + context 'with null byte' do + it_behaves_like 'checks params' do + let(:problematic_input) { null_byte } + end + end + + context 'with malformed strings' do + it_behaves_like 'checks params' do + let(:problematic_input) { invalid_string } + end + end + end + + context 'without problematic input' do + it "does not error for strings" do + env = env_for(name: "safe name") + + expect(subject.call(env)).not_to eq error_400 + end + + it "does not error with no params" do + env = env_for + + expect(subject.call(env)).not_to eq error_400 + end + end +end diff --git a/spec/lib/gitlab/middleware/handle_null_bytes_spec.rb b/spec/lib/gitlab/middleware/handle_null_bytes_spec.rb deleted file mode 100644 index 76a5174817e..00000000000 --- a/spec/lib/gitlab/middleware/handle_null_bytes_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require "rack/test" - -RSpec.describe Gitlab::Middleware::HandleNullBytes do - let(:null_byte) { "\u0000" } - let(:error_400) { [400, {}, ["Bad Request"]] } - let(:app) { double(:app) } - - subject { described_class.new(app) } - - before do - allow(app).to receive(:call) do |args| - args - end - end - - def env_for(params = {}) - Rack::MockRequest.env_for('/', { params: params }) - end - - context 'with null bytes in params' do - it 'rejects null bytes in a top level param' do - env = env_for(name: "null#{null_byte}byte") - - expect(subject.call(env)).to eq error_400 - end - - it "responds with 400 BadRequest for hashes with strings" do - env = env_for(name: { inner_key: "I am #{null_byte} bad" }) - - expect(subject.call(env)).to eq error_400 - end - - it "responds with 400 BadRequest for arrays with strings" do - env = env_for(name: ["I am #{null_byte} bad"]) - - expect(subject.call(env)).to eq error_400 - end - - it "responds with 400 BadRequest for arrays containing hashes with string values" do - env = env_for(name: [ - { - inner_key: "I am #{null_byte} bad" - } - ]) - - expect(subject.call(env)).to eq error_400 - end - - it "gives up and does not 400 with too deeply nested params" do - env = env_for(name: [ - { - inner_key: { deeper_key: [{ hash_inside_array_key: "I am #{null_byte} bad" }] } - } - ]) - - expect(subject.call(env)).not_to eq error_400 - end - end - - context 'without null bytes in params' do - it "does not respond with a 400 for strings" do - env = env_for(name: "safe name") - - expect(subject.call(env)).not_to eq error_400 - end - - it "does not respond with a 400 with no params" do - env = env_for - - expect(subject.call(env)).not_to eq error_400 - end - end - - context 'when disabled via env flag' do - before do - stub_env('REJECT_NULL_BYTES', '1') - end - - it 'does not respond with a 400 no matter what' do - env = env_for(name: "null#{null_byte}byte") - - expect(subject.call(env)).not_to eq error_400 - end - end -end diff --git a/spec/requests/user_sends_malformed_strings_spec.rb b/spec/requests/user_sends_malformed_strings_spec.rb new file mode 100644 index 00000000000..b6eda9159bc --- /dev/null +++ b/spec/requests/user_sends_malformed_strings_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User sends malformed strings as params' do + let(:null_byte) { "\u0000" } + let(:invalid_string) { "mal\xC0formed" } + + it 'raises a 400 error with a null byte' do + post '/nonexistent', params: { a: "A #{null_byte} nasty string" } + + expect(response).to have_gitlab_http_status(:bad_request) + end + + it 'raises a 400 error with an invalid string' do + post '/nonexistent', params: { a: "A #{invalid_string} nasty string" } + + expect(response).to have_gitlab_http_status(:bad_request) + end +end diff --git a/spec/requests/user_sends_null_bytes_spec.rb b/spec/requests/user_sends_null_bytes_spec.rb deleted file mode 100644 index 1ddfad40996..00000000000 --- a/spec/requests/user_sends_null_bytes_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'User sends null bytes as params' do - let(:null_byte) { "\u0000" } - - it 'raises a 400 error' do - post '/nonexistent', params: { a: "A #{null_byte} nasty string" } - - expect(response).to have_gitlab_http_status(:bad_request) - expect(response.body).to eq('Bad Request') - end -end