Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8212423bb8
commit
a908d3a2c8
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||

|
||||
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.
|
||||

|
||||
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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
```
|
||||
|
|
@ -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 |
|
||||
|----------------------------------------------| ----------------| --------------------------------- | ------------|
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
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.
|
||||

|
||||
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.
|
||||

|
||||
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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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') }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
Loading…
Reference in New Issue