diff options
39 files changed, 634 insertions, 369 deletions
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ce6900d1779..c2d3e25ed93 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -187,14 +187,14 @@ module ApplicationHelper css_classes << html_class unless html_class.blank? content_tag :time, l(time, format: "%b %d, %Y"), - class: css_classes.join(' '), - title: l(time.to_time.in_time_zone, format: :timeago_tooltip), - datetime: time.to_time.getutc.iso8601, - data: { - toggle: 'tooltip', - placement: placement, - container: 'body' - } + class: css_classes.join(' '), + title: l(time.to_time.in_time_zone, format: :timeago_tooltip), + datetime: time.to_time.getutc.iso8601, + data: { + toggle: 'tooltip', + placement: placement, + container: 'body' + } end def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false) @@ -279,7 +279,15 @@ module ApplicationHelper end def stylesheet_link_tag_defer(path) - stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil) + if startup_css_enabled? + stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil) + else + stylesheet_link_tag(path, crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil) + end + end + + def startup_css_enabled? + !params.has_key?(:no_startup_css) end def outdated_browser? diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 2ac926a7fc3..61b8dd146e3 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -18,7 +18,13 @@ = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png' - = render 'layouts/startup_css', { startup_filename: local_assigns.fetch(:startup_filename, nil) } + - if startup_css_enabled? + = render 'layouts/startup_css', { startup_filename: local_assigns.fetch(:startup_filename, nil) } + - else + - diffs_colors = user_diffs_colors + = stylesheet_link_tag "themes/#{user_application_theme_css_filename}" + = render 'layouts/diffs_colors_css', diffs_colors if diffs_colors.present? || request.path == profile_preferences_path + - if user_application_theme == 'gl-dark' %meta{ name: 'color-scheme', content: 'dark light' } = stylesheet_link_tag_defer "application_dark" @@ -33,7 +39,8 @@ = stylesheet_link_tag_defer "highlight/themes/#{user_color_scheme}" - = render 'layouts/startup_css_activation' + - if startup_css_enabled? + = render 'layouts/startup_css_activation' = stylesheet_link_tag 'performance_bar' if performance_bar_enabled? diff --git a/doc/api/license.md b/doc/api/license.md index 72589710590..ca9d9cf386d 100644 --- a/doc/api/license.md +++ b/doc/api/license.md @@ -97,6 +97,55 @@ Returns: - `200 OK` with response containing the licenses in JSON format. This is an empty JSON array if there are no licenses. - `403 Forbidden` if the current user in not permitted to read the licenses. +## Retrieve information about a single license + +```plaintext +GET /license/:id +``` + +Supported attributes: + +| Attribute | Type | Required | Description | +|-----------|---------|----------|---------------------------| +| `id` | integer | yes | ID of the GitLab license. | + +Returns the following status codes: + +- `200 OK`: Response contains the licenses in JSON format. +- `404 Not Found`: The requested license doesn't exist. +- `403 Forbidden`: The current user is not permitted to read the licenses. + +Example request: + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/license/:id" +``` + +Example response: + +```json +{ + "id": 1, + "plan": "premium", + "created_at": "2018-02-27T23:21:58.674Z", + "starts_at": "2018-01-27", + "expires_at": "2022-01-27", + "historical_max": 300, + "maximum_user_count": 300, + "expired": false, + "overage": 200, + "user_limit": 100, + "active_users": 50, + "licensee": { + "Name": "John Doe1" + }, + "add_ons": { + "GitLab_FileLocks": 1, + "GitLab_Auditor_User": 1 + } +} +``` + ## Add a new license ```plaintext diff --git a/doc/user/application_security/dast/authentication.md b/doc/user/application_security/dast/authentication.md new file mode 100644 index 00000000000..31bbdca8122 --- /dev/null +++ b/doc/user/application_security/dast/authentication.md @@ -0,0 +1,359 @@ +--- +stage: Secure +group: Dynamic Analysis +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +type: reference, howto +--- + +# Authentication (DAST) **(ULTIMATE)** + +WARNING: +**Never** run an authenticated scan against a production server. +Authenticated scans may perform *any* function that the authenticated user can, +including modifying or deleting data, submitting forms, and following links. +Only run an authenticated scan against a test server. + +Authentication logs a user in before a DAST scan so that the analyzer can test +as much of the application as possible when searching for vulnerabilities. + +DAST uses a browser to authenticate the user so that the login form has the necessary JavaScript +and styling required to submit the form. DAST finds the username and password fields and fills them with their respective values. +The login form is submitted, and when the response returns, a series of checks verify if authentication was successful. +DAST saves the credentials for reuse when crawling the target application. + +If DAST fails to authenticate, the scan halts and the CI job fails. + +Authentication supports single-step login forms, multi-step login forms, single sign-on, and authenticating to URLs outside of the configured target URL. + +## Getting started + +NOTE: +We recommend periodically confirming that the analyzer's authentication is still working, as this tends to break over +time due to changes to the application. + +To run a DAST authenticated scan: + +- Read the [prerequisite](#prerequisites) conditions for authentication. +- [Update your target website](#update-the-target-website) to a landing page of an authenticated user. +- If your login form has the username, password and submit button on a single page, use the [CI/CD variables](#available-cicd-variables) to configure [single-step](#configuration-for-a-single-step-login-form) login form authentication. +- If your login form has the username and password fields on different pages, use the [CI/CD variables](#available-cicd-variables) to configure [multi-step](#configuration-for-a-multi-step-login-form) login form authentication. +- Make sure the user isn't [logged out](#excluding-logout-urls) during the scan. + +### Prerequisites + +- You are using either the [DAST proxy-based analyzer](proxy-based.md) or the [DAST browser-based analyzer](browser_based.md). +- You know the URL of the login form of your application. Alternatively, you know how to navigate to the login form from the authentication URL (see [clicking to navigate to the login form](#clicking-to-navigate-to-the-login-form)). +- You have the username and password of the user you would like to authenticate as during the scan. +- You know the [selectors](#finding-an-elements-selector) of the username and password HTML fields that DAST will use to input the respective values. +- You know the element's [selector](#finding-an-elements-selector) that will submit the login form when selected. +- You have thought about how you can [verify](#verifying-authentication-is-successful) whether or not authentication was successful. +- You have checked the [known limitations](#known-limitations) to ensure DAST can authenticate to your application. + +### Available CI/CD variables + +| CI/CD variable | Type | Description | +|:-----------------------------------------------|:--------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DAST_AUTH_REPORT` | boolean | Used in combination with exporting the `gl-dast-debug-auth-report.html` artifact to aid in debugging authentication issues. | +| `DAST_AUTH_URL` <sup>1</sup> | URL | The URL of the page containing the sign-in HTML form on the target website. `DAST_USERNAME` and `DAST_PASSWORD` are submitted with the login form to create an authenticated scan. Example: `https://login.example.com`. | +| `DAST_AUTH_VERIFICATION_LOGIN_FORM` | boolean | Verifies successful authentication by checking for the absence of a login form once the login form has been submitted. | +| `DAST_AUTH_VERIFICATION_SELECTOR` | selector | Verifies successful authentication by checking for presence of a selector once the login form has been submitted. Example: `css:.user-photo`. | +| `DAST_AUTH_VERIFICATION_URL` <sup>1</sup> | URL | Verifies successful authentication by checking the URL in the browser once the login form has been submitted. Example: `"https://example.com/loggedin_page"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/207335) in GitLab 13.8. | +| `DAST_BROWSER_PATH_TO_LOGIN_FORM` <sup>1</sup> | selector | Comma-separated list of selectors that are selected prior to attempting to enter `DAST_USERNAME` and `DAST_PASSWORD` into the login form. Example: `"css:.navigation-menu,css:.login-menu-item"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326633) in GitLab 14.1. | +| `DAST_EXCLUDE_URLS` <sup>1</sup> | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. | +| `DAST_FIRST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when selected submits the username form of a multi-page login process. For example, `css:button[type='user-submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. | +| `DAST_PASSWORD` <sup>1</sup> | string | The password to authenticate to in the website. Example: `P@55w0rd!` | +| `DAST_PASSWORD_FIELD` | string | The selector of password field at the sign-in HTML form. Example: `id:password` | +| `DAST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when selected submits the login form or the password form of a multi-page login process. For example, `css:button[type='submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. | +| `DAST_USERNAME` <sup>1</sup> | string | The username to authenticate to in the website. Example: `admin` | +| `DAST_USERNAME_FIELD` <sup>1</sup> | string | The selector of username field at the sign-in HTML form. Example: `name:username` | + +1. Available to an on-demand proxy-based DAST scan. + +### Update the target website + +The target website, defined using the CI/CD variable `DAST_WEBSITE`, is the URL DAST uses to begin crawling your application. + +For best crawl results on an authenticated scan, the target website should be a URL accessible only after the user is authenticated. +Often, this is the URL of the page the user lands on after they're logged in. + +For example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com/dashboard/welcome" + DAST_AUTH_URL: "https://example.com/login" +``` + +### Configuration for a single-step login form + +A single-step login form has all login form elements on a single page. +Configuration requires the CI/CD variables `DAST_AUTH_URL`, `DAST_USERNAME`, `DAST_USERNAME_FIELD`, `DAST_PASSWORD`, `DAST_PASSWORD_FIELD`, and `DAST_SUBMIT_FIELD` to be defined for the DAST job. + +It is recommended to set up the URL and selectors of fields in the job definition YAML, for example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com" + DAST_AUTH_URL: "https://example.com/login" + DAST_USERNAME_FIELD: "css:[name=username]" + DAST_PASSWORD_FIELD: "css:[name=password]" + DAST_SUBMIT_FIELD: "css:button[type=submit]" +``` + +Do **not** define `DAST_USERNAME` and `DAST_PASSWORD` in the YAML job definition file as this could present a security risk. Instead, create them as masked CI/CD variables using the GitLab UI. +See [Custom CI/CI variables](../../../ci/variables/index.md#custom-cicd-variables) for more information. + +### Configuration for a multi-step login form + +A multi-step login form has two pages. The first page has a form with the username and a next submit button. +If the username is valid, a second form on the subsequent page has the password and the form submit button. + +Configuration requires the CI/CD variables `DAST_AUTH_URL`, `DAST_USERNAME`, `DAST_USERNAME_FIELD`, `DAST_FIRST_SUBMIT_FIELD`, `DAST_PASSWORD`, `DAST_PASSWORD_FIELD`, and `DAST_SUBMIT_FIELD` to be defined for the DAST job. + +It is recommended to set up the URL and selectors of fields in the job definition YAML, for example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com" + DAST_AUTH_URL: "https://example.com/login" + DAST_USERNAME_FIELD: "css:[name=username]" + DAST_FIRST_SUBMIT_FIELD: "css:button[name=next]" + DAST_PASSWORD_FIELD: "css:[name=password]" + DAST_SUBMIT_FIELD: "css:button[type=submit]" +``` + +Do **not** define `DAST_USERNAME` and `DAST_PASSWORD` in the YAML job definition file as this could present a security risk. Instead, create them as masked CI/CD variables using the GitLab UI. +See [Custom CI/CI variables](../../../ci/variables/index.md#custom-cicd-variables) for more information. + +### Configuration for Single Sign-On (SSO) + +If a user can log into an application, then in most cases, DAST will also be able to log in. +This is the case even when an application uses Single Sign-on. Applications using SSO solutions should configure DAST +authentication using the [single-step](#configuration-for-a-single-step-login-form) or [multi-step](#configuration-for-a-multi-step-login-form) login form configuration guides. + +DAST supports authentication processes where a user is redirected to an external Identity Provider's site to log in. +Check the [known limitations](#known-limitations) of DAST authentication to determine if your SSO authentication process is supported. + +### Clicking to navigate to the login form + +Define `DAST_BROWSER_PATH_TO_LOGIN_FORM` to provide a path of elements to click on from the `DAST_AUTH_URL` so that DAST can access the +login form. This is useful for applications that show the login form in a pop-up (modal) window or when the login form does not +have a unique URL. + +For example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com" + DAST_AUTH_URL: "https://example.com/login" + DAST_BROWSER_PATH_TO_LOGIN_FORM: "css:.navigation-menu,css:.login-menu-item" +``` + +### Excluding logout URLs + +If DAST crawls the logout URL while running an authenticated scan, the user will be logged out, resulting in the remainder of the scan being unauthenticated. +It is therefore recommended to exclude logout URLs using the CI/CD variable `DAST_EXCLUDE_URLS`. DAST will not access any excluded URLs, ensuring the user remains logged in. + +Provided URLs can be either absolute URLs, or regular expressions of URL paths relative to the base path of the `DAST_WEBSITE`. For example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com/welcome/home" + DAST_EXCLUDE_URLS: "https://example.com/logout,/user/.*/logout" +``` + +### Finding an element's selector + +Selectors are used by CI/CD variables to specify the location of an element displayed on a page in a browser. +Selectors have the format `type`:`search string`. DAST searches for the selector using the search string based on the type. + +| Selector type | Example | Description | +| ------------- | ---------------------------------- | ----------- | +| `css` | `css:.password-field` | Searches for a HTML element having the supplied CSS selector. Selectors should be as specific as possible for performance reasons. | +| `id` | `id:element` | Searches for an HTML element with the provided element ID. | +| `name` | `name:element` | Searches for an HTML element with the provided element name. | +| `xpath` | `xpath://input[@id="my-button"]/a` | Searches for a HTML element with the provided XPath. Note that XPath searches are expected to be less performant than other searches. | +| None provided | `a.click-me` | Defaults to searching using a CSS selector. | + +#### Find selectors with Google Chrome + +Chrome DevTools element selector tool is an effective way to find a selector. + +1. Open Chrome and navigate to the page where you would like to find a selector, for example, the login page for your site. +1. Open the `Elements` tab in Chrome DevTools with the keyboard shortcut `Command + Shift + c` in macOS or `Ctrl + Shift + c` in Windows. +1. Select the `Select an element in the page to select it` tool. + ![search-elements](img/dast_auth_browser_scan_search_elements.png) +1. Select the field on your page that you would like to know the selector for. +1. Once the tool is active, highlight a field you wish to view the details of. + ![highlight](img/dast_auth_browser_scan_highlight.png) +1. Once highlighted, you can see the element's details, including attributes that would make a good candidate for a selector. + +In this example, the `id="user_login"` appears to be a good candidate. You can use this as a selector as the DAST username field by setting +`DAST_USERNAME_FIELD: "id:user_login"`. + +#### Choose the right selector + +Judicious choice of selector leads to a scan that is resilient to the application changing. + +In order of preference, it is recommended to choose as selectors: + +- `id` fields. These are generally unique on a page, and rarely change. +- `name` fields. These are generally unique on a page, and rarely change. +- `class` values specific to the field, such as the selector `"css:.username"` for the `username` class on the username field. +- Presence of field specific data attributes, such as the selector, `"css:[data-username]"` when the `data-username` field has any value on the username field. +- Multiple `class` hierarchy values, such as the selector `"css:.login-form .username"` when there are multiple elements with class `username` but only one nested inside the element with the class `login-form`. + +When using selectors to locate specific fields we recommend you avoid searching on: + +- Any `id`, `name`, `attribute`, `class` or `value` that is dynamically generated. +- Generic class names, such as `column-10` and `dark-grey`. +- XPath searches as they are less performant than other selector searches. +- Unscoped searches, such as those beginning with `css:*` and `xpath://*`. + +## Verifying authentication is successful + +Once DAST has submitted the login form, a verification process takes place +to determine if authentication succeeded. The scan will halt with an error if authentication is unsuccessful. + +Following the submission of the login form, authentication is determined to be unsuccessful when: + +- The login submit HTTP response has a `400` or `500` series status code. +- Any [verification check](#verification-checks) fails. +- An [authentication token](#authentication-tokens) with a sufficiently random value is not set during the authentication process. + +### Verification checks + +Verification checks run checks on the state of the browser once authentication is complete +to determine further if authentication succeeded. + +DAST will test for the absence of a login form if no verification checks are configured. + +#### Verify based on the URL + +Define `DAST_AUTH_VERIFICATION_URL` as the URL displayed in the browser tab once the login form is successfully submitted. + +DAST will compare the verification URL to the URL in the browser after authentication. +If they are not the same, authentication is unsuccessful. + +For example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com" + DAST_AUTH_VERIFICATION_URL: "https://example.com/user/welcome" +``` + +#### Verify based on presence of an element + +Define `DAST_AUTH_VERIFICATION_SELECTOR` as a [selector](#finding-an-elements-selector) that will find one or many elements on the page +displayed once the login form is successfully submitted. If no element is found, authentication is unsuccessful. +Searching for the selector on the page displayed when login fails should return no elements. + +For example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com" + DAST_AUTH_VERIFICATION_SELECTOR: "css:.welcome-user" +``` + +#### Verify based on absence of a login form + +Define `DAST_AUTH_VERIFICATION_LOGIN_FORM` as `"true"` to indicate that DAST should search for the login form on the +page displayed once the login form is successfully submitted. If a login form is still present after logging in, authentication is unsuccessful. + +For example: + +```yaml +include: + - template: DAST.gitlab-ci.yml + +dast: + variables: + DAST_WEBSITE: "https://example.com" + DAST_AUTH_VERIFICATION_LOGIN_FORM: "true" +``` + +### Authentication tokens + +DAST records authentication tokens set during the authentication process. +Authentication tokens are loaded into new browsers when DAST opens them so the user can remain logged in throughout the scan. + +To record tokens, DAST takes a snapshot of cookies, local storage, and session storage values set by the application before +the authentication process. DAST does the same after authentication and uses the difference to determine which were created +by the authentication process. + +DAST considers cookies, local storage and session storage values set with sufficiently "random" values to be authentication tokens. +For example, `sessionID=HVxzpS8GzMlPAc2e39uyIVzwACIuGe0H` would be viewed as an authentication token, while `ab_testing_group=A1` would not. + +## Known limitations + +- DAST cannot bypass a CAPTCHA if the authentication flow includes one. Please turn these off in the testing environment for the application being scanned. +- DAST cannot handle multi-factor authentication like one-time passwords (OTP) by using SMS, biometrics, or authenticator apps. Please turn these off in the testing environment for the application being scanned. +- DAST cannot authenticate to applications that do not set an [authentication token](#authentication-tokens) during login. +- DAST cannot authenticate to applications that require more than two inputs to be filled out. Two inputs must be supplied, username and password. + +## Troubleshooting + +### Read the logs + +The console output of the DAST CI/CD job shows information about the authentication process using the `AUTH` log module. +For example, the following log shows failed authentication for a multi-step login form. +Authentication failed because a home page should be displayed after login. Instead, the login form was still present. + +```plaintext +2022-11-16T13:43:02.000 INF AUTH attempting to authenticate +2022-11-16T13:43:02.000 INF AUTH loading login page LoginURL=https://example.com/login +2022-11-16T13:43:10.000 INF AUTH multi-step authentication detected +2022-11-16T13:43:20.000 INF AUTH verifying if login attempt was successful true_when="no login form found (no element found when searching using selector css:[id=email] or css:[id=password] or css:[id=submit])" +2022-11-16T13:43:21.000 INF AUTH requirement is unsatisfied, login form was found want="no login form found (no element found when searching using selector css:[id=email] or css:[id=password] or css:[id=submit])" +2022-11-16T13:43:21.000 INF AUTH login attempt failed error="authentication failed: failed to authenticate user" +``` + +### Configure the authentication debug report + +An authentication report can be saved as a CI/CD job artifact to assist with understanding the cause of an authentication failure. + +The report contains steps during the login process, HTTP requests and responses, the Document Object Model (DOM) and screenshots. + +![dast-auth-report](img/dast_auth_report.jpg) + +An example configuration where the authentication debug report is exported may look like the following: + +```yaml +dast: + variables: + DAST_WEBSITE: "https://example.com" + DAST_AUTH_REPORT: "true" + artifacts: + paths: [gl-dast-debug-auth-report.html] + when: always +``` diff --git a/doc/user/application_security/dast/browser_based.md b/doc/user/application_security/dast/browser_based.md index c0a97c0ff92..909490fa6e5 100644 --- a/doc/user/application_security/dast/browser_based.md +++ b/doc/user/application_security/dast/browser_based.md @@ -52,9 +52,14 @@ dast: DAST_BROWSER_SCAN: "true" ``` +## Authentication + +The browser-based analyzer can authenticate a user prior to a scan. See [Authentication](authentication.md) for configuration instructions. + ### Available CI/CD variables -The browser-based crawler can be configured using CI/CD variables. +These CI/CD variables are specific to DAST. They can be used to customize the behavior of DAST to your requirements. +For authentication CI/CD variables, see [Authentication](authentication.md). | CI/CD variable | Type | Example | Description | |----------------------------------------------| ----------------| --------------------------------- | ------------| diff --git a/doc/user/application_security/dast/proxy-based.md b/doc/user/application_security/dast/proxy-based.md index ed6ee99aa52..7d8a3ee412c 100644 --- a/doc/user/application_security/dast/proxy-based.md +++ b/doc/user/application_security/dast/proxy-based.md @@ -248,6 +248,10 @@ When using `DAST_PATHS` and `DAST_PATHS_FILE`, note the following: To perform a [full scan](#full-scan) on the listed paths, use the `DAST_FULL_SCAN_ENABLED` CI/CD variable. +## Authentication + +The proxy-based analyzer uses the browser-based analyzer to authenticate a user prior to a scan. See [Authentication](authentication.md) for configuration instructions. + ## Customize DAST settings You can customize the behavior of DAST using both CI/CD variables and command-line options. Use of CI/CD @@ -339,61 +343,49 @@ To enable Mutual TLS: #### Available CI/CD variables These CI/CD variables are specific to DAST. They can be used to customize the behavior of DAST to your requirements. +For authentication CI/CD variables, see [Authentication](authentication.md). WARNING: All customization of GitLab security scanning tools should be tested in a merge request before merging these changes to the default branch. Failure to do so can give unexpected results, including a large number of false positives. -| CI/CD variable | Type | Description | -|:-------------------------------------------------|:--------------|:------------------------------| -| `DAST_ADVERTISE_SCAN` | boolean | Set to `true` to add a `Via` header to every request sent, advertising that the request was sent as part of a GitLab DAST scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334947) in GitLab 14.1. | -| `DAST_AGGREGATE_VULNERABILITIES` | boolean | Vulnerability aggregation is set to `true` by default. To disable this feature and see each vulnerability individually set to `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254043) in GitLab 14.0. | -| `DAST_API_HOST_OVERRIDE` <sup>1</sup> | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. Replaced by [DAST API scan](../dast_api/index.md#available-cicd-variables). Used to override domains defined in API specification files. Only supported when importing the API specification from a URL. Example: `example.com:8080`. | -| `DAST_API_SPECIFICATION` <sup>1</sup> | URL or string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. Replaced by [DAST API scan](../dast_api/index.md#available-cicd-variables). The API specification to import. The specification can be hosted at a URL, or the name of a file present in the `/zap/wrk` directory. The variable `DAST_WEBSITE` must be specified if this is omitted. | -| `DAST_AUTH_REPORT` <sup>2</sup> | boolean | Used in combination with exporting the `gl-dast-debug-auth-report.html` artifact to aid in debugging authentication issues. | -| `DAST_AUTH_EXCLUDE_URLS` <sup>2</sup> | URLs | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/289959)** in GitLab 14.0. Replaced by `DAST_EXCLUDE_URLS`. The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. | -| `DAST_AUTH_URL` <sup>1,2</sup> | URL | The URL of the page containing the sign-in HTML form on the target website. `DAST_USERNAME` and `DAST_PASSWORD` are submitted with the login form to create an authenticated scan. Example: `https://login.example.com`. | -| `DAST_AUTH_VERIFICATION_LOGIN_FORM` <sup>2</sup> | boolean | Verifies successful authentication by checking for the lack of a login form once the login form has been submitted. | -| `DAST_AUTH_VERIFICATION_SELECTOR` <sup>2</sup> | selector | Verifies successful authentication by checking for presence of a selector once the login form has been submitted. Example: `css:.user-photo`. | -| `DAST_AUTH_VERIFICATION_URL` <sup>1,2</sup> | URL | A URL only accessible to logged in users that DAST can use to confirm successful authentication. If provided, DAST exits if it cannot access the URL. Example: `"http://example.com/loggedin_page"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/207335) in GitLab 13.8. | -| `DAST_AUTO_UPDATE_ADDONS` | boolean | ZAP add-ons are pinned to specific versions in the DAST Docker image. Set to `true` to download the latest versions when the scan starts. Default: `false`. | -| `DAST_BROWSER_PATH_TO_LOGIN_FORM` <sup>1,2</sup> | selector | Comma-separated list of selectors that are selected prior to attempting to enter `DAST_USERNAME` and `DAST_PASSWORD` into the login form. Example: `"css:.navigation-menu,css:.login-menu-item"`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326633) in GitLab 14.1. | -| `DAST_DEBUG` <sup>1</sup> | boolean | Enable debug message output. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_EXCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to exclude them from running during the scan. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). For example, `HTTP Parameter Override` has a rule ID of `10026`. Cannot be used when `DAST_ONLY_INCLUDE_RULES` is set. **Note:** In earlier versions of GitLab the excluded rules were executed but vulnerabilities they generated were suppressed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118641) in GitLab 12.10. | -| `DAST_EXCLUDE_URLS` <sup>1,2</sup> | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. Example, `http://example.com/sign-out`. | -| `DAST_FIRST_SUBMIT_FIELD` <sup>2</sup> | string | The `id` or `name` of the element that when selected submits the username form of a multi-page login process. For example, `css:button[type='user-submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. | -| `DAST_FULL_SCAN_DOMAIN_VALIDATION_REQUIRED` | boolean | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/293595)** in GitLab 14.0. Set to `true` to require domain validation when running DAST full scans. Default: `false` | -| `DAST_FULL_SCAN_ENABLED` <sup>1</sup> | boolean | Set to `true` to run a [ZAP Full Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) instead of a [ZAP Baseline Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan). Default: `false` | -| `DAST_HTML_REPORT` | string | The filename of the HTML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_INCLUDE_ALPHA_VULNERABILITIES` | boolean | Set to `true` to include alpha passive and active scan rules. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_MARKDOWN_REPORT` | string | The filename of the Markdown report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_MASK_HTTP_HEADERS` | string | Comma-separated list of request and response headers to be masked (GitLab 13.1). Must contain **all** headers to be masked. Refer to [list of headers that are masked by default](#hide-sensitive-information). | -| `DAST_MAX_URLS_PER_VULNERABILITY` | number | The maximum number of URLs reported for a single vulnerability. `DAST_MAX_URLS_PER_VULNERABILITY` is set to `50` by default. To list all the URLs set to `0`. [Introduced](https://gitlab.com/gitlab-org/security-products/dast/-/merge_requests/433) in GitLab 13.12. | -| `DAST_ONLY_INCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to configure the scan to run only them. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). Cannot be used when `DAST_EXCLUDE_RULES` is set. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250651) in GitLab 13.12. | -| `DAST_PASSWORD` <sup>1,2</sup> | string | The password to authenticate to in the website. Example: `P@55w0rd!` | -| `DAST_PASSWORD_FIELD` <sup>1,2</sup> | string | The selector of password field at the sign-in HTML form. Example: `id:password` | -| `DAST_PATHS` | string | Set to a comma-separated list of URLs for DAST to scan. For example, `/page1.html,/category1/page3.html,/page2.html`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214120) in GitLab 13.4. | -| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. | -| `DAST_PKCS12_CERTIFICATE_BASE64` | string | The PKCS12 certificate used for sites that require Mutual TLS. Must be encoded as base64 text. | -| `DAST_PKCS12_PASSWORD` | string | The password of the certificate used in `DAST_PKCS12_CERTIFICATE_BASE64`. | -| `DAST_REQUEST_HEADERS` <sup>1</sup> | string | Set to a comma-separated list of request header names and values. Headers are added to every request made by DAST. For example, `Cache-control: no-cache,User-Agent: DAST/1.0` | -| `DAST_SKIP_TARGET_CHECK` | boolean | Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229067) in GitLab 13.8. | -| `DAST_SPIDER_MINS` <sup>1</sup> | number | The maximum duration of the spider scan in minutes. Set to `0` for unlimited. Default: One minute, or unlimited when the scan is a full scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_SPIDER_START_AT_HOST` | boolean | Set to `false` to prevent DAST from resetting the target to its host before scanning. When `true`, non-host targets `http://test.site/some_path` is reset to `http://test.site` before scan. Default: `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258805) in GitLab 13.6. | -| `DAST_SUBMIT_FIELD` <sup>2</sup> | string | The `id` or `name` of the element that when selected submits the login form or the password form of a multi-page login process. For example, `css:button[type='submit']`. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. | -| `DAST_TARGET_AVAILABILITY_TIMEOUT` <sup>1</sup> | number | Time limit in seconds to wait for target availability. | -| `DAST_USE_AJAX_SPIDER` <sup>1</sup> | boolean | Set to `true` to use the AJAX spider in addition to the traditional spider, useful for crawling sites that require JavaScript. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_USERNAME` <sup>1,2</sup> | string | The username to authenticate to in the website. Example: `admin` | -| `DAST_USERNAME_FIELD` <sup>1,2</sup> | string | The selector of username field at the sign-in HTML form. Example: `name:username` | -| `DAST_XML_REPORT` | string | The filename of the XML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_WEBSITE` <sup>1</sup> | URL | The URL of the website to scan. | -| `DAST_ZAP_CLI_OPTIONS` | string | ZAP server command-line options. For example, `-Xmx3072m` would set the Java maximum memory allocation pool size. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | -| `DAST_ZAP_LOG_CONFIGURATION` | string | Set to a semicolon-separated list of additional log4j properties for the ZAP Server. Example: `logger.httpsender.name=org.parosproxy.paros.network.HttpSender;logger.httpsender.level=debug;logger.sitemap.name=org.parosproxy.paros.model.SiteMap;logger.sitemap.level=debug;` | -| `SECURE_ANALYZERS_PREFIX` | URL | Set the Docker registry base address from which to download the analyzer. | +| CI/CD variable | Type | Description | +|:------------------------------------------------|:--------------|:------------------------------| +| `DAST_ADVERTISE_SCAN` | boolean | Set to `true` to add a `Via` header to every request sent, advertising that the request was sent as part of a GitLab DAST scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334947) in GitLab 14.1. | +| `DAST_AGGREGATE_VULNERABILITIES` | boolean | Vulnerability aggregation is set to `true` by default. To disable this feature and see each vulnerability individually set to `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254043) in GitLab 14.0. | +| `DAST_API_HOST_OVERRIDE` <sup>1</sup> | string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. Replaced by [DAST API scan](../dast_api/index.md#available-cicd-variables). Used to override domains defined in API specification files. Only supported when importing the API specification from a URL. Example: `example.com:8080`. | +| `DAST_API_SPECIFICATION` <sup>1</sup> | URL or string | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383467)** in GitLab 15.7. Replaced by [DAST API scan](../dast_api/index.md#available-cicd-variables). The API specification to import. The specification can be hosted at a URL, or the name of a file present in the `/zap/wrk` directory. The variable `DAST_WEBSITE` must be specified if this is omitted. | +| `DAST_AUTH_EXCLUDE_URLS` | URLs | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/289959)** in GitLab 14.0. Replaced by `DAST_EXCLUDE_URLS`. The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. | +| `DAST_AUTO_UPDATE_ADDONS` | boolean | ZAP add-ons are pinned to specific versions in the DAST Docker image. Set to `true` to download the latest versions when the scan starts. Default: `false`. | +| `DAST_DEBUG` <sup>1</sup> | boolean | Enable debug message output. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_EXCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to exclude them from running during the scan. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). For example, `HTTP Parameter Override` has a rule ID of `10026`. Cannot be used when `DAST_ONLY_INCLUDE_RULES` is set. **Note:** In earlier versions of GitLab the excluded rules were executed but vulnerabilities they generated were suppressed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118641) in GitLab 12.10. | +| `DAST_EXCLUDE_URLS` <sup>1</sup> | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. Example, `http://example.com/sign-out`. | +| `DAST_FULL_SCAN_DOMAIN_VALIDATION_REQUIRED` | boolean | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/293595)** in GitLab 14.0. Set to `true` to require domain validation when running DAST full scans. Default: `false` | +| `DAST_FULL_SCAN_ENABLED` <sup>1</sup> | boolean | Set to `true` to run a [ZAP Full Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) instead of a [ZAP Baseline Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan). Default: `false` | +| `DAST_HTML_REPORT` | string | The filename of the HTML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_INCLUDE_ALPHA_VULNERABILITIES` | boolean | Set to `true` to include alpha passive and active scan rules. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_MARKDOWN_REPORT` | string | The filename of the Markdown report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_MASK_HTTP_HEADERS` | string | Comma-separated list of request and response headers to be masked (GitLab 13.1). Must contain **all** headers to be masked. Refer to [list of headers that are masked by default](#hide-sensitive-information). | +| `DAST_MAX_URLS_PER_VULNERABILITY` | number | The maximum number of URLs reported for a single vulnerability. `DAST_MAX_URLS_PER_VULNERABILITY` is set to `50` by default. To list all the URLs set to `0`. [Introduced](https://gitlab.com/gitlab-org/security-products/dast/-/merge_requests/433) in GitLab 13.12. | +| `DAST_ONLY_INCLUDE_RULES` | string | Set to a comma-separated list of Vulnerability Rule IDs to configure the scan to run only them. Rule IDs are numbers and can be found from the DAST log or on the [ZAP project](https://www.zaproxy.org/docs/alerts/). Cannot be used when `DAST_EXCLUDE_RULES` is set. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250651) in GitLab 13.12. | +| `DAST_PATHS` | string | Set to a comma-separated list of URLs for DAST to scan. For example, `/page1.html,/category1/page3.html,/page2.html`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214120) in GitLab 13.4. | +| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. | +| `DAST_PKCS12_CERTIFICATE_BASE64` | string | The PKCS12 certificate used for sites that require Mutual TLS. Must be encoded as base64 text. | +| `DAST_PKCS12_PASSWORD` | string | The password of the certificate used in `DAST_PKCS12_CERTIFICATE_BASE64`. | +| `DAST_REQUEST_HEADERS` <sup>1</sup> | string | Set to a comma-separated list of request header names and values. Headers are added to every request made by DAST. For example, `Cache-control: no-cache,User-Agent: DAST/1.0` | +| `DAST_SKIP_TARGET_CHECK` | boolean | Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229067) in GitLab 13.8. | +| `DAST_SPIDER_MINS` <sup>1</sup> | number | The maximum duration of the spider scan in minutes. Set to `0` for unlimited. Default: One minute, or unlimited when the scan is a full scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_SPIDER_START_AT_HOST` | boolean | Set to `false` to prevent DAST from resetting the target to its host before scanning. When `true`, non-host targets `http://test.site/some_path` is reset to `http://test.site` before scan. Default: `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258805) in GitLab 13.6. | +| `DAST_TARGET_AVAILABILITY_TIMEOUT` <sup>1</sup> | number | Time limit in seconds to wait for target availability. | +| `DAST_USE_AJAX_SPIDER` <sup>1</sup> | boolean | Set to `true` to use the AJAX spider in addition to the traditional spider, useful for crawling sites that require JavaScript. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_XML_REPORT` | string | The filename of the XML report written at the end of a scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_WEBSITE` <sup>1</sup> | URL | The URL of the website to scan. | +| `DAST_ZAP_CLI_OPTIONS` | string | ZAP server command-line options. For example, `-Xmx3072m` would set the Java maximum memory allocation pool size. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | +| `DAST_ZAP_LOG_CONFIGURATION` | string | Set to a semicolon-separated list of additional log4j properties for the ZAP Server. Example: `logger.httpsender.name=org.parosproxy.paros.network.HttpSender;logger.httpsender.level=debug;logger.sitemap.name=org.parosproxy.paros.model.SiteMap;logger.sitemap.level=debug;` | +| `SECURE_ANALYZERS_PREFIX` | URL | Set the Docker registry base address from which to download the analyzer. | 1. Available to an on-demand DAST scan. -1. Used for authentication. ### Customize DAST using command-line options @@ -440,242 +432,6 @@ variables: DAST_ZAP_CLI_OPTIONS: "-config replacer.full_list(0).description=auth -config replacer.full_list(0).enabled=true -config replacer.full_list(0).matchtype=REQ_HEADER -config replacer.full_list(0).matchstr=Authorization -config replacer.full_list(0).regex=false -config replacer.full_list(0).replacement=TOKEN" ``` -## Authentication - -NOTE: -We highly recommend you configure the scanner to authenticate to the application. If you don't, it cannot check most of the application for security risks, as most -of your application is likely not accessible without authentication. We also recommend -you periodically confirm the scanner's authentication is still working, as this tends to break over -time due to authentication changes to the application. - -Create masked CI/CD variables to pass the credentials that DAST uses. -To create masked variables for the username and password, see [Create a custom variable in the UI](../../../ci/variables/index.md#custom-cicd-variables). -The key of the username variable must be `DAST_USERNAME`, -and the key of the password variable must be `DAST_PASSWORD`. - -After DAST has authenticated with the application, all cookies are collected from the web browser. -For each cookie a matching session token is created for use by ZAP. This ensures ZAP is recognized -by the application as correctly authenticated. - -Authentication supports single form logins, multi-step login forms, and authenticating to URLs outside of the configured target URL. - -WARNING: -**Never** run an authenticated scan against a production server. When an authenticated -scan is run, it may perform *any* function that the authenticated user can. This -includes actions like modifying and deleting data, submitting forms, and following links. -Only run an authenticated scan against a test server. - -### SSO - -DAST can authenticate to websites making use of SSO, with the following restrictions: - -- DAST cannot bypass a CAPTCHA if the authentication flow includes one. -- DAST cannot handle multi-factor authentication like one-time passwords (OTP) by using SMS or authenticator apps. -- DAST must get a cookie, or a local or session storage, with a sufficiently random value. - -The [authentication debug output](#configure-the-authentication-debug-output) can be helpful for troubleshooting SSO authentication -with DAST. - -### Log in using automatic detection of the login form - -By providing a `DAST_USERNAME`, `DAST_PASSWORD`, and `DAST_AUTH_URL`, DAST attempts to authenticate to the -target application by locating the login form based on a determination about whether or not the form contains username or password fields. - -Automatic detection is "best-effort", and depending on the application being scanned may provide either a resilient login experience or one that fails to authenticate the user. - -Login process: - -1. The `DAST_AUTH_URL` is loaded into the browser, and any forms on the page are located. - 1. If a form contains a username and password field, `DAST_USERNAME` and `DAST_PASSWORD` is inputted into the respective fields, the form submit button is selected and the user is logged in. - 1. If a form contains only a username field, it is assumed that the login form is multi-step. - 1. The `DAST_USERNAME` is inputted into the username field and the form submit button is selected. - 1. The subsequent pages loads where it is expected that a form exists and contains a password field. If found, `DAST_PASSWORD` is inputted, form submit button is selected and the user is logged in. - -### Log in using explicit selection of the login form - -By providing a `DAST_USERNAME_FIELD`, `DAST_PASSWORD_FIELD`, and `DAST_SUBMIT_FIELD`, in addition to the fields required for automatic login, -DAST attempts to authenticate to the target application by locating the login form based on the selectors provided. -Most applications benefit from this approach to authentication. - -Login process: - -1. The `DAST_AUTH_URL` is loaded into the browser, and any forms on the page are located. - 1. If the `DAST_FIRST_SUBMIT_FIELD` is not defined, then `DAST_USERNAME` is inputted into `DAST_USERNAME_FIELD`, `DAST_PASSWORD` is inputted into `DAST_PASSWORD_FIELD`, `DAST_SUBMIT_FIELD` is selected and the user is logged in. - 1. If the `DAST_FIRST_SUBMIT_FIELD` is defined, then it is assumed that the login form is multi-step. - 1. The `DAST_USERNAME` is inputted into the `DAST_USERNAME_FIELD` field and the `DAST_FIRST_SUBMIT_FIELD` is selected. - 1. The subsequent pages loads where the `DAST_PASSWORD` is inputted into the `DAST_PASSWORD_FIELD` field, the `DAST_SUBMIT_FIELD` is selected and the user is logged in. - -### Verifying successful login - -Once the login form has been submitted, DAST determines if the login was successful. Unsuccessful attempts at authentication cause the scan to halt. - -Following the submission of the login form, authentication is determined to be unsuccessful when: - -- A `400` or `500` series HTTP response status code is returned. -- A new cookie/browser storage value determined to be sufficiently random has not been set. - -In addition to these checks, the user can configure their own verification checks. -Each of the following checks can be used in conjunction with one another, if none are configured by default the presence of a login form is checked. - -#### Verifying based on the URL - -When `DAST_AUTH_VERIFICATION_URL` is configured, the URL displayed in the browser tab post login form submission is directly compared to the URL in the CI/CD variable. -If these are not exactly the same, authentication is deemed to be unsuccessful. - -For example: - -```yaml -include: - - template: DAST.gitlab-ci.yml - -dast: - variables: - DAST_WEBSITE: "https://example.com" - DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler - ... - DAST_AUTH_VERIFICATION_URL: "https://example.com/user/welcome" -``` - -#### Verify based on presence of an element - -When `DAST_AUTH_VERIFICATION_SELECTOR` is configured, the page displayed in the browser tab is searched for an element described by the selector in the CI/CD variable. -If no element is found, authentication is deemed to be unsuccessful. - -For example: - -```yaml -include: - - template: DAST.gitlab-ci.yml - -dast: - variables: - DAST_WEBSITE: "https://example.com" - DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler - ... - DAST_AUTH_VERIFICATION_SELECTOR: "css:.welcome-user" -``` - -#### Verify based on presence of a login form - -When `DAST_AUTH_VERIFICATION_LOGIN_FORM` is configured, the page displayed in the browser tab is searched for a form that is detected to be a login form. -If any such form is found, authentication is deemed to be unsuccessful. - -For example: - -```yaml -include: - - template: DAST.gitlab-ci.yml - -dast: - variables: - DAST_WEBSITE: "https://example.com" - DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler - ... - DAST_AUTH_VERIFICATION_LOGIN_FORM: "true" -``` - -### View the login form - -Many web applications show the user the login form in a pop-up (modal) window. -For these applications, navigating to the form requires both: - -- A starting URL. -- A list of elements to select to display the modal window. - -When `DAST_BROWSER_PATH_TO_LOGIN_FORM` is present, like in this example: - -```yaml -include: - - template: DAST.gitlab-ci.yml - -dast: - variables: - DAST_WEBSITE: "https://my.site.com" - DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler - ... - DAST_AUTH_URL: "https://my.site.com/admin" - DAST_BROWSER_PATH_TO_LOGIN_FORM: "css:.navigation-menu,css:.login-menu-item" -``` - -DAST performs these actions: - -1. Load the `DAST_AUTH_URL` page, such as `https://my.site.com/admin`. -1. After the page loads, DAST selects elements found by the selectors described - in `DAST_BROWSER_PATH_TO_LOGIN_FORM`. This example opens the navigation menu - and selects the login menu, to display the login modal window. -1. To continue the authentication process, DAST fills in the username and password - on the login form. - -### Configure the authentication debug output - -It is often difficult to understand the cause of an authentication failure when running DAST in a CI/CD pipeline. -To assist users in debugging authentication issues, a debug report can be generated and saved as a job artifact. -This HTML report contains all steps made during the login process, along with HTTP requests and responses, the Document Object Model (DOM) and screenshots. - -![dast-auth-report](img/dast_auth_report.jpg) - -An example configuration where the authentication debug report is exported may look like the following: - -```yaml -dast: - variables: - DAST_WEBSITE: "https://example.com" - DAST_BROWSER_SCAN: "true" # use the browser-based GitLab DAST crawler - ... - DAST_AUTH_REPORT: "true" - artifacts: - paths: [gl-dast-debug-auth-report.html] - when: always -``` - -### Selectors - -Selectors are used by CI/CD variables to specify the location of an element displayed on a page in a browser. -Selectors have the format `type`:`search string`. The crawler searches for the selector using the search string based on the type. - -| Selector type | Example | Description | -| ------------- | ---------------------------------- | ----------- | -| `css` | `css:.password-field` | Searches for a HTML element having the supplied CSS selector. Selectors should be as specific as possible for performance reasons. | -| `id` | `id:element` | Searches for an HTML element with the provided element ID. | -| `name` | `name:element` | Searches for an HTML element with the provided element name. | -| `xpath` | `xpath://input[@id="my-button"]/a` | Searches for a HTML element with the provided XPath. Note that XPath searches are expected to be less performant than other searches. | -| None provided | `a.click-me` | Defaults to searching using a CSS selector. | - -#### Find selectors with Google Chrome - -Chrome DevTools element selector tool is an effective way to find a selector. - -1. Open Chrome and navigate to the page where you would like to find a selector, for example, the login page for your site. -1. Open the `Elements` tab in Chrome DevTools with the keyboard shortcut `Command + Shift + c` in macOS or `Ctrl + Shift + c` in Windows. -1. Select the `Select an element in the page to select it` tool. - ![search-elements](img/dast_auth_browser_scan_search_elements.png) -1. Select the field on your page that you would like to know the selector for. -1. Once the tool is active, highlight a field you wish to view the details of. - ![highlight](img/dast_auth_browser_scan_highlight.png) -1. Once highlighted, you can see the element's details, including attributes that would make a good candidate for a selector. - -In this example, the `id="user_login"` appears to be a good candidate. You can use this as a selector as the DAST username field by setting -`DAST_USERNAME_FIELD: "id:user_login"`. - -#### Choose the right selector - -Judicious choice of selector leads to a scan that is resilient to the application changing. - -In order of preference, it is recommended to choose as selectors: - -- `id` fields. These are generally unique on a page, and rarely change. -- `name` fields. These are generally unique on a page, and rarely change. -- `class` values specific to the field, such as the selector `"css:.username"` for the `username` class on the username field. -- Presence of field specific data attributes, such as the selector, `"css:[data-username]"` when the `data-username` field has any value on the username field. -- Multiple `class` hierarchy values, such as the selector `"css:.login-form .username"` when there are multiple elements with class `username` but only one nested inside the element with the class `login-form`. - -When using selectors to locate specific fields we recommend you avoid searching on: - -- Any `id`, `name`, `attribute`, `class` or `value` that is dynamically generated. -- Generic class names, such as `column-10` and `dark-grey`. -- XPath searches as they are less performant than other selector searches. -- Unscoped searches, such as those beginning with `css:*` and `xpath://*`. - ### Bleeding-edge vulnerability definitions ZAP first creates rules in the `alpha` class. After a testing period with @@ -1147,9 +903,7 @@ and DAST site profiles are included in the [audit log](../../../administration/a The DAST tool outputs a `gl-dast-report.json` report file containing details of the scan and its results. This file is included in the job's artifacts. JSON is the default format, but you can output the report in Markdown, HTML, and XML formats. To specify an alternative -format, use a [CI/CD variable](#available-cicd-variables). You can also use a CI/CD variable -to configure the job to output the `gl-dast-debug-auth-report.html` file which helps when debugging -authentication issues. +format, use a [CI/CD variable](#available-cicd-variables). For details of the report's schema, see the [schema for DAST reports](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/dast-report-format.json). Example reports can be found in the [DAST repository](https://gitlab.com/gitlab-org/security-products/dast/-/tree/main/test/end-to-end/expect). diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb index fa2537bcfc4..2e493eee7f8 100644 --- a/lib/api/helm_packages.rb +++ b/lib/api/helm_packages.rb @@ -67,7 +67,7 @@ module API package_file = Packages::Helm::PackageFilesFinder.new(project, params[:channel], file_name: "#{params[:file_name]}.tgz").most_recent! - track_package_event('pull_package', :helm, project: project, namespace: project.namespace) + track_package_event('pull_package', :helm, project: project, namespace: project.namespace, property: 'i_package_helm_user') present_package_file!(package_file) end @@ -110,7 +110,8 @@ module API package, chart_params.merge(build: current_authenticated_job) ).execute - track_package_event('push_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace) + track_package_event('push_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace, + property: 'i_package_helm_user') ::Packages::Helm::ExtractionWorker.perform_async(params[:channel], chart_package_file.id) # rubocop:disable CodeReuse/Worker diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb index 96a10d43401..8d913268405 100644 --- a/lib/api/helpers/packages_helpers.rb +++ b/lib/api/helpers/packages_helpers.rb @@ -78,10 +78,18 @@ module API end end - def track_package_event(event_name, scope, **args) - ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute + def track_package_event(action, scope, **args) + ::Packages::CreateEventService.new(nil, current_user, event_name: action, scope: scope).execute category = args.delete(:category) || self.options[:for].name - ::Gitlab::Tracking.event(category, event_name.to_s, **args) + event_name = "i_package_#{scope}_user" + ::Gitlab::Tracking.event( + category, + action.to_s, + property: event_name, + label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly', + context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context], + **args + ) end def present_package_file!(package_file, supports_direct_download: true) diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb index 494b493f5e0..544fb440e09 100644 --- a/lib/api/npm_project_packages.rb +++ b/lib/api/npm_project_packages.rb @@ -33,7 +33,7 @@ module API package_file = ::Packages::PackageFileFinder .new(package, params[:file_name]).execute! - track_package_event('pull_package', package, category: 'API::NpmPackages', project: project, namespace: project.namespace) + track_package_event('pull_package', :npm, category: 'API::NpmPackages', project: project, namespace: project.namespace) present_package_file!(package_file) end diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb index 6c649483da1..0707a0b0ec4 100644 --- a/lib/api/pypi_packages.rb +++ b/lib/api/pypi_packages.rb @@ -123,7 +123,7 @@ module API package = Packages::Pypi::PackageFinder.new(current_user, group, { filename: filename, sha256: params[:sha256] }).execute package_file = ::Packages::PackageFileFinder.new(package, filename, with_file_name_like: false).execute - track_package_event('pull_package', :pypi) + track_package_event('pull_package', :pypi, namespace: group, project: package.project) present_package_file!(package_file, supports_direct_download: true) end diff --git a/lib/gitlab/tracking/service_ping_context.rb b/lib/gitlab/tracking/service_ping_context.rb index 393cd647e7f..3177dca4ef7 100644 --- a/lib/gitlab/tracking/service_ping_context.rb +++ b/lib/gitlab/tracking/service_ping_context.rb @@ -20,6 +20,13 @@ module Gitlab def to_context SnowplowTracker::SelfDescribingJson.new(SCHEMA_URL, @payload) end + + def to_h + { + schema: SCHEMA_URL, + data: @payload + } + end end end end diff --git a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb index 5b4b00106cb..f1ca9e11a1a 100644 --- a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb +++ b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb @@ -233,7 +233,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do end it_behaves_like 'a successful manifest pull' - it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest' + it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest', false context 'with workhorse response' do let(:pull_response) { { status: :success, manifest: nil, from_cache: false } } @@ -303,7 +303,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do end it_behaves_like 'a successful blob pull' - it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache' + it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache', false context 'when cache entry does not exist' do let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' } @@ -387,7 +387,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do group.add_guest(user) end - it_behaves_like 'a package tracking event', described_class.name, 'pull_blob' + it_behaves_like 'a package tracking event', described_class.name, 'pull_blob', false it 'creates a blob' do expect { subject }.to change { group.dependency_proxy_blobs.count }.by(1) @@ -445,7 +445,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do group.add_guest(user) end - it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest' + it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest', false it_behaves_like 'with invalid path' context 'with no existing manifest' do diff --git a/spec/controllers/groups/registry/repositories_controller_spec.rb b/spec/controllers/groups/registry/repositories_controller_spec.rb index 62c15201a95..efdafcd2657 100644 --- a/spec/controllers/groups/registry/repositories_controller_spec.rb +++ b/spec/controllers/groups/registry/repositories_controller_spec.rb @@ -114,7 +114,7 @@ RSpec.describe Groups::Registry::RepositoriesController do it_behaves_like 'with name parameter' - it_behaves_like 'a package tracking event', described_class.name, 'list_repositories' + it_behaves_like 'a package tracking event', described_class.name, 'list_repositories', false context 'with project in subgroup' do let_it_be(:test_group) { create(:group, parent: group) } diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 84b076ec1d2..7571ae75ab5 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -683,4 +683,16 @@ RSpec.describe ApplicationHelper do end end end + + describe 'stylesheet_link_tag_defer' do + it 'uses print stylesheet by default' do + expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="print" href="/stylesheets/test.css" />') + end + + it 'uses regular stylesheet when no_startup_css param present' do + allow(helper.controller).to receive(:params).and_return({ no_startup_css: '' }) + + expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="screen" href="/stylesheets/test.css" />') + end + end end diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb index b9c887b3e16..a3b21059334 100644 --- a/spec/lib/api/helpers/packages_helpers_spec.rb +++ b/spec/lib/api/helpers/packages_helpers_spec.rb @@ -238,4 +238,26 @@ RSpec.describe API::Helpers::PackagesHelpers do end end end + + describe '#track_package_event' do + before do + allow(helper).to receive(:current_user).and_return(user) + end + + it_behaves_like 'Snowplow event tracking with RedisHLL context' do + let(:action) { 'push_package' } + let(:scope) { :terraform_module } + let(:category) { described_class.name } + let(:namespace) { project.namespace } + let(:user) { project.creator } + let(:feature_flag_name) { nil } + let(:label) { 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly' } + let(:property) { 'i_package_terraform_module_user' } + + subject(:package_action) do + args = { category: category, namespace: namespace, user: user, project: project } + helper.track_package_event(action, scope, **args) + end + end + end end diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb index 55c9347677b..4be488fc2f7 100644 --- a/spec/requests/api/composer_packages_spec.rb +++ b/spec/requests/api/composer_packages_spec.rb @@ -14,7 +14,10 @@ RSpec.describe API::ComposerPackages do let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) } let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } + let(:snowplow_gitlab_standard_context) do + { project: project, namespace: project.namespace, user: user, property: 'i_package_composer_user' } + end + let(:headers) { {} } using RSpec::Parameterized::TableSyntax @@ -491,7 +494,6 @@ RSpec.describe API::ComposerPackages do with_them do let(:token) { user_token ? personal_access_token.token : 'wrong' } let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } before do project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) @@ -501,6 +503,10 @@ RSpec.describe API::ComposerPackages do include_context 'Composer user type', params[:user_role], params[:member] do if params[:expected_status] == :success + let(:snowplow_gitlab_standard_context) do + { project: project, namespace: project.namespace, property: 'i_package_composer_user' } + end + it_behaves_like 'a package tracking event', described_class.name, 'pull_package' else it_behaves_like 'not a package tracking event' diff --git a/spec/requests/api/conan_instance_packages_spec.rb b/spec/requests/api/conan_instance_packages_spec.rb index b343e0cfc97..d3ab676d7ee 100644 --- a/spec/requests/api/conan_instance_packages_spec.rb +++ b/spec/requests/api/conan_instance_packages_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe API::ConanInstancePackages do - let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) { { user: user, project: project, namespace: project.namespace, property: 'i_package_conan_user' } } include_context 'conan api setup' diff --git a/spec/requests/api/conan_project_packages_spec.rb b/spec/requests/api/conan_project_packages_spec.rb index 4e6af9942ef..3ab43727ccf 100644 --- a/spec/requests/api/conan_project_packages_spec.rb +++ b/spec/requests/api/conan_project_packages_spec.rb @@ -5,7 +5,6 @@ RSpec.describe API::ConanProjectPackages do include_context 'conan api setup' let(:project_id) { project.id } - let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } } describe 'GET /api/v4/projects/:id/packages/conan/v1/ping' do let(:url) { "/projects/#{project.id}/packages/conan/v1/ping" } diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb index 0478e123086..c1faa722d30 100644 --- a/spec/requests/api/generic_packages_spec.rb +++ b/spec/requests/api/generic_packages_spec.rb @@ -19,7 +19,7 @@ RSpec.describe API::GenericPackages do let(:user) { personal_access_token.user } let(:ci_build) { create(:ci_build, :running, user: user, project: project) } - let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) { { user: user, project: project, namespace: project.namespace, property: 'i_package_generic_user' } } def auth_header return {} if user_role == :anonymous @@ -408,8 +408,6 @@ RSpec.describe API::GenericPackages do end context 'event tracking' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } - subject { upload_file(params, workhorse_headers.merge(personal_access_token_header)) } it_behaves_like 'a package tracking event', described_class.name, 'push_package' @@ -645,8 +643,6 @@ RSpec.describe API::GenericPackages do end context 'event tracking' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } - before do project.add_developer(user) end diff --git a/spec/requests/api/group_container_repositories_spec.rb b/spec/requests/api/group_container_repositories_spec.rb index 82daab0e5e8..fcb7085a17a 100644 --- a/spec/requests/api/group_container_repositories_spec.rb +++ b/spec/requests/api/group_container_repositories_spec.rb @@ -35,7 +35,9 @@ RSpec.describe API::GroupContainerRepositories do describe 'GET /groups/:id/registry/repositories' do let(:url) { "/groups/#{group.id}/registry/repositories" } - let(:snowplow_gitlab_standard_context) { { user: api_user, namespace: group } } + let(:snowplow_gitlab_standard_context) do + { user: api_user, namespace: group, property: 'i_package_container_user' } + end subject { get api(url, api_user) } diff --git a/spec/requests/api/helm_packages_spec.rb b/spec/requests/api/helm_packages_spec.rb index 6bd81f64913..249ab0e7302 100644 --- a/spec/requests/api/helm_packages_spec.rb +++ b/spec/requests/api/helm_packages_spec.rb @@ -17,6 +17,8 @@ RSpec.describe API::HelmPackages do let_it_be(:package_file2_2) { create(:helm_package_file, package: package2, file_sha256: 'file2', file_name: 'filename2.tgz', channel: 'test', description: 'hello from test channel') } let_it_be(:other_package) { create(:npm_package, project: project) } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_helm_user' } } + describe 'GET /api/v4/projects/:id/packages/helm/:channel/index.yaml' do let(:project_id) { project.id } let(:channel) { 'stable' } @@ -63,7 +65,6 @@ RSpec.describe API::HelmPackages do with_them do let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } before do project.update!(visibility: visibility.to_s) @@ -74,8 +75,6 @@ RSpec.describe API::HelmPackages do end context 'with access to package registry for everyone' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } - before do project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE) project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC) @@ -152,7 +151,6 @@ RSpec.describe API::HelmPackages do let(:params) { { chart: temp_file(file_name) } } let(:file_key) { :chart } let(:send_rewritten_field) { true } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } subject do workhorse_finalize( diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb index ac8c4aacdf2..4bb78eaab16 100644 --- a/spec/requests/api/maven_packages_spec.rb +++ b/spec/requests/api/maven_packages_spec.rb @@ -22,7 +22,8 @@ RSpec.describe API::MavenPackages do let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) } let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_maven_user' } } + let(:package_name) { 'com/example/my-app' } let(:headers) { workhorse_headers } let(:headers_with_token) { headers.merge('Private-Token' => personal_access_token.token) } @@ -98,8 +99,6 @@ RSpec.describe API::MavenPackages do context 'with jar file' do let_it_be(:package_file) { jar_file } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } - it_behaves_like 'a package tracking event', described_class.name, 'pull_package' end end @@ -900,6 +899,8 @@ RSpec.describe API::MavenPackages do it_behaves_like 'package workhorse uploads' context 'event tracking' do + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_maven_user' } } + it_behaves_like 'a package tracking event', described_class.name, 'push_package' context 'when the package file fails to be created' do diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb index 3af4215052e..6fc3d0ceb48 100644 --- a/spec/requests/api/npm_project_packages_spec.rb +++ b/spec/requests/api/npm_project_packages_spec.rb @@ -55,7 +55,6 @@ RSpec.describe API::NpmProjectPackages do end describe 'GET /api/v4/projects/:id/packages/npm/*package_name/-/*file_name' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } let(:package_file) { package.package_files.first } let(:headers) { {} } @@ -215,7 +214,7 @@ RSpec.describe API::NpmProjectPackages do let_it_be(:version) { '1.2.3' } let(:params) { upload_params(package_name: package_name, package_version: version) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_npm_user' } } shared_examples 'handling upload with different authentications' do context 'with access token' do diff --git a/spec/requests/api/nuget_group_packages_spec.rb b/spec/requests/api/nuget_group_packages_spec.rb index c1375288809..a678d2efff8 100644 --- a/spec/requests/api/nuget_group_packages_spec.rb +++ b/spec/requests/api/nuget_group_packages_spec.rb @@ -12,6 +12,7 @@ RSpec.describe API::NugetGroupPackages do let_it_be(:deploy_token) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) } let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token, group: group) } + let(:snowplow_gitlab_standard_context) { { namespace: project.group, property: 'i_package_nuget_user' } } let(:target_type) { 'groups' } shared_examples 'handling all endpoints' do @@ -46,7 +47,6 @@ RSpec.describe API::NugetGroupPackages do let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token, group: subgroup) } let(:target) { subgroup } - let(:snowplow_gitlab_standard_context) { { namespace: subgroup } } it_behaves_like 'handling all endpoints' @@ -58,7 +58,7 @@ RSpec.describe API::NugetGroupPackages do context 'a group' do let(:target) { group } - let(:snowplow_gitlab_standard_context) { { namespace: group } } + let(:snowplow_gitlab_standard_context) { { namespace: target, property: 'i_package_nuget_user' } } it_behaves_like 'handling all endpoints' diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb index 4617583493d..4866185cd12 100644 --- a/spec/requests/api/nuget_project_packages_spec.rb +++ b/spec/requests/api/nuget_project_packages_spec.rb @@ -13,6 +13,7 @@ RSpec.describe API::NugetProjectPackages do let(:target) { project } let(:target_type) { 'projects' } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_nuget_user' } } shared_examples 'accept get request on private project with access to package registry for everyone' do subject { get api(url) } @@ -28,9 +29,7 @@ RSpec.describe API::NugetProjectPackages do describe 'GET /api/v4/projects/:id/packages/nuget' do let(:url) { "/projects/#{target.id}/packages/nuget/index.json" } - it_behaves_like 'handling nuget service requests' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } - end + it_behaves_like 'handling nuget service requests' it_behaves_like 'accept get request on private project with access to package registry for everyone' end @@ -58,9 +57,7 @@ RSpec.describe API::NugetProjectPackages do describe 'GET /api/v4/projects/:id/packages/nuget/query' do let(:url) { "/projects/#{target.id}/packages/nuget/query?#{query_parameters.to_query}" } - it_behaves_like 'handling nuget search requests' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } - end + it_behaves_like 'handling nuget search requests' it_behaves_like 'accept get request on private project with access to package registry for everyone' do let_it_be(:query_parameters) { { q: 'query', take: 5, skip: 0, prerelease: true } } @@ -152,7 +149,6 @@ RSpec.describe API::NugetProjectPackages do with_them do let(:token) { user_token ? personal_access_token.token : 'wrong' } let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } subject { get api(url), headers: headers } diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index 52ec06d76a9..94aceffc6fa 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -33,7 +33,10 @@ RSpec.describe API::ProjectContainerRepositories do let(:method) { :get } let(:params) { {} } - let(:snowplow_gitlab_standard_context) { { user: api_user, project: project, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) do + { user: api_user, project: project, namespace: project.namespace, + property: 'i_package_container_user' } + end before_all do project.add_maintainer(maintainer) @@ -414,6 +417,9 @@ RSpec.describe API::ProjectContainerRepositories do context 'for developer', :snowplow do let(:api_user) { developer } + let(:service_ping_context) do + [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'i_package_container_user').to_h] + end context 'when there are multiple tags' do before do @@ -427,7 +433,10 @@ RSpec.describe API::ProjectContainerRepositories do subject expect(response).to have_gitlab_http_status(:ok) - expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, user: api_user, namespace: project.namespace) + expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, + user: api_user, namespace: project.namespace.reload, + label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly', + property: 'i_package_container_user', context: service_ping_context) end end @@ -443,7 +452,10 @@ RSpec.describe API::ProjectContainerRepositories do subject expect(response).to have_gitlab_http_status(:ok) - expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, user: api_user, namespace: project.namespace) + expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, + user: api_user, namespace: project.namespace.reload, + label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly', + property: 'i_package_container_user', context: service_ping_context) end end end diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb index 12091158a02..2b022dc8f62 100644 --- a/spec/requests/api/pypi_packages_spec.rb +++ b/spec/requests/api/pypi_packages_spec.rb @@ -14,6 +14,7 @@ RSpec.describe API::PypiPackages do let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } } let(:headers) { {} } @@ -25,7 +26,7 @@ RSpec.describe API::PypiPackages do describe 'GET /api/v4/groups/:id/-/packages/pypi/simple' do let(:url) { "/groups/#{group.id}/-/packages/pypi/simple" } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } } it_behaves_like 'pypi simple index API endpoint' it_behaves_like 'rejects PyPI access with unknown group id' @@ -63,7 +64,7 @@ RSpec.describe API::PypiPackages do describe 'GET /api/v4/projects/:id/packages/pypi/simple' do let(:package_name) { package.name } let(:url) { "/projects/#{project.id}/packages/pypi/simple" } - let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group } } + let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group, property: 'i_package_pypi_user' } } it_behaves_like 'pypi simple index API endpoint' it_behaves_like 'rejects PyPI access with unknown project id' @@ -81,13 +82,13 @@ RSpec.describe API::PypiPackages do context 'simple package API endpoint' do let_it_be(:package) { create(:pypi_package, project: project) } + let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group, property: 'i_package_pypi_user' } } subject { get api(url), headers: headers } describe 'GET /api/v4/groups/:id/-/packages/pypi/simple/:package_name' do let(:package_name) { package.name } let(:url) { "/groups/#{group.id}/-/packages/pypi/simple/#{package_name}" } - let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group } } it_behaves_like 'pypi simple API endpoint' it_behaves_like 'rejects PyPI access with unknown group id' @@ -125,7 +126,7 @@ RSpec.describe API::PypiPackages do describe 'GET /api/v4/projects/:id/packages/pypi/simple/:package_name' do let(:package_name) { package.name } let(:url) { "/projects/#{project.id}/packages/pypi/simple/#{package_name}" } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } } it_behaves_like 'pypi simple API endpoint' it_behaves_like 'rejects PyPI access with unknown project id' @@ -202,7 +203,7 @@ RSpec.describe API::PypiPackages do let(:base_params) { { requires_python: requires_python, version: '1.0.0', name: 'sample-project', sha256_digest: '1' * 64, md5_digest: '1' * 32 } } let(:params) { base_params.merge(content: temp_file(file_name)) } let(:send_rewritten_field) { true } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_pypi_user' } } subject do workhorse_finalize( @@ -366,7 +367,6 @@ RSpec.describe API::PypiPackages do describe 'GET /api/v4/groups/:id/-/packages/pypi/files/:sha256/*file_identifier' do let(:url) { "/groups/#{group.id}/-/packages/pypi/files/#{package.package_files.first.file_sha256}/#{package_name}-1.0.0.tar.gz" } - let(:snowplow_gitlab_standard_context) { {} } it_behaves_like 'pypi file download endpoint' it_behaves_like 'rejects PyPI access with unknown group id' @@ -375,7 +375,6 @@ RSpec.describe API::PypiPackages do describe 'GET /api/v4/projects/:id/packages/pypi/files/:sha256/*file_identifier' do let(:url) { "/projects/#{project.id}/packages/pypi/files/#{package.package_files.first.file_sha256}/#{package_name}-1.0.0.tar.gz" } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } it_behaves_like 'pypi file download endpoint' it_behaves_like 'rejects PyPI access with unknown project id' diff --git a/spec/requests/api/rpm_project_packages_spec.rb b/spec/requests/api/rpm_project_packages_spec.rb index 68511795c94..f367ad8c59f 100644 --- a/spec/requests/api/rpm_project_packages_spec.rb +++ b/spec/requests/api/rpm_project_packages_spec.rb @@ -136,7 +136,7 @@ RSpec.describe API::RpmProjectPackages do end describe 'GET /api/v4/projects/:id/packages/rpm/:package_file_id/:filename' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: group } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, property: 'i_package_rpm_user' } } let(:url) { "/projects/#{project.id}/packages/rpm/#{package_file_id}/#{package_name}" } subject { get api(url), headers: headers } @@ -148,7 +148,10 @@ RSpec.describe API::RpmProjectPackages do end describe 'POST /api/v4/projects/:project_id/packages/rpm' do - let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, user: user } } + let(:snowplow_gitlab_standard_context) do + { project: project, namespace: group, user: user, property: 'i_package_rpm_user' } + end + let(:url) { "/projects/#{project.id}/packages/rpm" } let(:file_upload) { fixture_file_upload('spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm') } diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb index a7d461781b8..ee43aaa6ead 100644 --- a/spec/requests/api/rubygem_packages_spec.rb +++ b/spec/requests/api/rubygem_packages_spec.rb @@ -15,7 +15,7 @@ RSpec.describe API::RubygemPackages do let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:headers) { {} } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_rubygems_user' } } let(:tokens) do { @@ -164,7 +164,7 @@ RSpec.describe API::RubygemPackages do with_them do let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' } let(:headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_rubygems_user' } } before do project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s)) @@ -323,7 +323,7 @@ RSpec.describe API::RubygemPackages do let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' } let(:user_headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } } let(:headers) { user_headers.merge(workhorse_headers) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user, property: 'i_package_rubygems_user' } } let(:snowplow_user) do case token_type when :deploy_token diff --git a/spec/requests/api/terraform/modules/v1/packages_spec.rb b/spec/requests/api/terraform/modules/v1/packages_spec.rb index ae61017f5bb..c2af3a134f4 100644 --- a/spec/requests/api/terraform/modules/v1/packages_spec.rb +++ b/spec/requests/api/terraform/modules/v1/packages_spec.rb @@ -418,7 +418,8 @@ RSpec.describe API::Terraform::Modules::V1::Packages do { project: project, user: user_role == :anonymous ? nil : user, - namespace: project.namespace + namespace: project.namespace, + property: 'i_package_terraform_module_user' } end @@ -583,7 +584,10 @@ RSpec.describe API::Terraform::Modules::V1::Packages do with_them do let(:user_headers) { user_role == :anonymous ? {} : { token_header => token } } let(:headers) { user_headers.merge(workhorse_headers) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user } } + let(:snowplow_gitlab_standard_context) do + { project: project, namespace: project.namespace, user: snowplow_user, property: 'i_package_terraform_module_user' } + end + let(:snowplow_user) do case token_type when :deploy_token diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb index 3974338238a..7c37e5189f1 100644 --- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb @@ -28,6 +28,10 @@ RSpec.shared_context 'conan api setup' do ) end + let(:snowplow_gitlab_standard_context) do + { user: user, project: project, namespace: project.namespace, property: 'i_package_conan_user' } + end + before do project.add_developer(user) allow(Settings).to receive(:attr_encrypted_db_key_base).and_return(base_secret) diff --git a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb index 89f290d8d68..1e50505162d 100644 --- a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb @@ -17,6 +17,7 @@ RSpec.shared_context 'npm api setup' do let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let(:package_name) { package.name } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_npm_user' } } before do # create a duplicated package without triggering model validation errors diff --git a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb index 6749ebd471f..7e99066110d 100644 --- a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb +++ b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb @@ -16,12 +16,14 @@ RSpec.shared_examples 'Snowplow event tracking' do |overrides: {}| let(:extra) { {} } - it 'is not emitted if FF is disabled' do - stub_feature_flags(feature_flag_name => false) + if try(:feature_flag_name) + it 'is not emitted if FF is disabled' do + stub_feature_flags(feature_flag_name => false) - subject + subject - expect_no_snowplow_event(category: category, action: action) + expect_no_snowplow_event(category: category, action: action) + end end it 'is emitted' do diff --git a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb index dab9ee27dab..c76bc7c3107 100644 --- a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb @@ -593,7 +593,7 @@ RSpec.shared_examples 'delete package endpoint' do project.add_maintainer(user) end - it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'delete_package' + it_behaves_like 'a package tracking event', 'API::ConanPackages', 'delete_package' it 'deletes a package' do expect { subject }.to change { Packages::Package.count }.from(2).to(1) @@ -708,7 +708,7 @@ RSpec.shared_examples 'package file download endpoint' do context 'tracking the conan_package.tgz download' do let(:package_file) { package.package_files.find_by(file_name: ::Packages::Conan::FileMetadatum::PACKAGE_BINARY) } - it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'pull_package' + it_behaves_like 'a package tracking event', 'API::ConanPackages', 'pull_package' end end @@ -781,7 +781,7 @@ RSpec.shared_examples 'workhorse package file upload endpoint' do context 'tracking the conan_package.tgz upload' do let(:file_name) { ::Packages::Conan::FileMetadatum::PACKAGE_BINARY } - it_behaves_like 'a gitlab tracking event', 'API::ConanPackages', 'push_package' + it_behaves_like 'a package tracking event', 'API::ConanPackages', 'push_package' end end diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb index 0eae689ab01..bace570e47a 100644 --- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb @@ -495,7 +495,7 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false| let(:token) { user_token ? personal_access_token.token : 'wrong' } let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) } let(:headers) { user_headers.merge(workhorse_headers) } - let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace, property: 'i_package_nuget_user' } } before do update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb index 860cb1b1d86..98264baa61d 100644 --- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb @@ -142,15 +142,26 @@ RSpec.shared_examples 'job token for package uploads' do |authorize_endpoint: fa end end -RSpec.shared_examples 'a package tracking event' do |category, action| +RSpec.shared_examples 'a package tracking event' do |category, action, service_ping_context = true| before do stub_feature_flags(collect_package_events: true) end + let(:context) do + [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, + event: snowplow_gitlab_standard_context[:property]).to_h] + end + it "creates a gitlab tracking event #{action}", :snowplow, :aggregate_failures do expect { subject }.to change { Packages::Event.count }.by(1) - expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context) + if service_ping_context + expect_snowplow_event(category: category, action: action, + label: "redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly", + context: context, **snowplow_gitlab_standard_context) + else + expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context) + end end end diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb index e74841a44e7..a267476b7cb 100644 --- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb @@ -273,7 +273,7 @@ RSpec.shared_examples 'pypi simple API endpoint' do let(:url) { "/projects/#{project.id}/packages/pypi/simple/my-package" } let(:headers) { basic_auth_header(user.username, personal_access_token.token) } - let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, property: 'i_package_pypi_user' } } it_behaves_like 'PyPI package versions', :developer, :success end diff --git a/workhorse/go.mod b/workhorse/go.mod index bc2586d3189..59d6e639bb5 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -7,7 +7,7 @@ require ( github.com/BurntSushi/toml v1.2.1 github.com/FZambia/sentinel v1.1.1 github.com/alecthomas/chroma/v2 v2.3.0 - github.com/aws/aws-sdk-go v1.44.136 + github.com/aws/aws-sdk-go v1.44.142 github.com/disintegration/imaging v1.6.2 github.com/getsentry/raven-go v0.2.0 github.com/golang-jwt/jwt/v4 v4.4.2 diff --git a/workhorse/go.sum b/workhorse/go.sum index 2c19e5f2a2e..ac9fe36406c 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -227,8 +227,8 @@ github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4 github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.45/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.68/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.44.136 h1:J1KJJssa8pjU8jETYUxwRS37KTcxjACfKd9GK8t+5ZU= -github.com/aws/aws-sdk-go v1.44.136/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.142 h1:KZ1/FDwCSft1DuNllFaBtWpcG0CW2NgQjvOrE1TdlXE= +github.com/aws/aws-sdk-go v1.44.142/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.16.8 h1:gOe9UPR98XSf7oEJCcojYg+N2/jCRm4DdeIsP85pIyQ= github.com/aws/aws-sdk-go-v2 v1.16.8/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= |