Skip to content
Snippets Groups Projects
Commit 31b093db authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖
Browse files

Automatic merge of gitlab-org/gitlab master

parents 75f7e64e 54a91d67
No related branches found
No related tags found
1 merge request!170053Security patch upgrade alert: Only expose to admins 17-4
......@@ -200,7 +200,7 @@ job_with_secrets:
### `400: missing token` status code
This error indicates that one or more basic components necessary for ID tokens are
either missing or not configured as expect.
either missing or not configured as expected.
To find the problem, an administrator can look for more details in the instance's
`exceptions_json.log` for the specific method that failed.
......
......@@ -17,6 +17,7 @@ GitLab can check your application for security vulnerabilities and that it meets
| [Set up a merge request approval policy](scan_result_policy/index.md) | Learn how to configure a merge request approval policy that takes action based on scan results. | **{star}** |
| [Set up a scan execution policy](scan_execution_policy/index.md) | Learn how to create a scan execution policy to enforce security scanning of your project. | **{star}** |
| [Scan a Docker container for vulnerabilities](container_scanning/index.md) | Learn how to use container scanning templates to add container scanning to your projects. | **{star}** |
| [Protect your project with secret push protection](../user/application_security/secret_detection/push_protection_tutorial.md) | Enable secret push protection in your project. | **{star}** |
| [Remove a secret from your commits](../user/application_security/secret_detection/remove_secrets_tutorial.md) | Learn how to remove a secret from your commit history. | **{star}** |
| [Get started with GitLab application security](../user/application_security/get-started-security.md) | Follow recommended steps to set up security tools. | |
| [GitLab Security Essentials](https://university.gitlab.com/courses/security-essentials) | Learn about the essential security capabilities of GitLab in this self-paced course. | |
---
stage: Secure
group: Secret Detection
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Tutorial: Protect your project with secret push protection
If your application uses external resources, you usually need to authenticate your
application with a **secret**, like a token or key. If a secret is pushed to a
remote repository, anyone with access to the repository can impersonate you or your
application.
With secret push protection, if GitLab detects a secret in the commit history,
it can block a push to prevent a leak. Enabling secret push protection is a good
way to reduce the amount of time you spend reviewing your commits for sensitive data
and remediating leaks if they occur.
In this tutorial, you'll configure secret push protection and see what happens when you try to commit a fake secret.
You'll also learn how to skip secret push protection, in case you need to bypass a false positive.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
This tutorial is adapted from the following GitLab Unfiltered videos:
- [Introduction to Secret Push Protection](https://www.youtube.com/watch?v=SFVuKx3hwNI)
<!-- Video published on 2024-06-21 -->
- [Configuration - Enabling Secret Push Protection for your project](https://www.youtube.com/watch?v=t1DJN6Vsmp0)
<!-- Video published on 2024-06-23 -->
- [Skip Secret Push Protection](https://www.youtube.com/watch?v=wBAhe_d2DkQ)
<!-- Video published on 2024-06-04 -->
## Before you begin
Make sure you have the following before you complete this tutorial:
- A GitLab Ultimate subscription.
- A test project. You can use any project you like, but consider creating a test project specifically for this tutorial.
- Some familiarity with command-line Git.
Additionally, on self-managed GitLab only, ensure secret push protection is
[enabled on the instance](secret_push_protection/index.md#allow-the-use-of-secret-push-protection-in-your-gitlab-instance).
## Enable secret push protection
To use secret push protection, you need to enable it for each project you want to protect.
Let's start by enabling it in a test project.
1. On the left sidebar, select **Search or go to** and find your project.
1. On the left sidebar, select **Secure > Security configuration**.
1. Turn on the **Secret push protection** toggle.
Next, you'll test secret push protection.
## Try pushing a secret to your project
GitLab identifies secrets by matching specific patterns of letters, digits, and symbols. These patterns
are also used to identify the type of secret.
Let's test this feature by adding the fake secret `glpat-12345678901234567890` to our project:
1. In the project, check out a new branch:
```shell
git checkout -b push-protection-tutorial
```
1. Create a new file with the following content:
```plaintext
hello, world!
glpat-12345678901234567890
```
1. Commit the file to your branch:
```shell
git add .
git commit -m "Add fake secret"
```
The secret is now entered into the commit history. Note that secret push protection doesn't stop you from committing a secret; it only alerts you when you push.
1. Push the changes to GitLab. You should see something like this:
```shell
$ git push
remote: GitLab:
remote: PUSH BLOCKED: Secrets detected in code changes
remote:
remote: Secret push protection found the following secrets in commit: 123abc
remote: -- myFile.txt:2 | GitLab Personal Access Token
remote:
remote: To push your changes you must remove the identified secrets.
To gitlab.com:
! [remote rejected] push-protection-tutorial -> main (pre-receive hook declined)
```
GitLab detects the secret and blocks the push. From the error report, we can see:
- The commit that contains the secret (`123abc`)
- The file and line number that contains the secret (`myFile.txt:2`)
- The type of secret (`GitLab Personal Access Token`)
If we had successfully pushed our changes, we would need to spend considerable time and effort to revoke and replace the secret.
Instead, we can [remove the secret from the commit history](remove_secrets_tutorial.md) and rest easy knowing we stopped the
secret from being leaked.
## Skip secret push protection
Sometimes you need to push a commit, even if secret push protection has identified a secret. This can happen when GitLab detects a false positive.
To demonstrate, we'll push our last commit to GitLab.
### With a push option
You can use a push option to skip secret detection:
- Push your commit with the `secret_detection.skip_all` option:
```shell
git push -o secret_detection.skip_all
```
Secret detection is skipped, and the changes are pushed to the remote.
### With a commit message
If you don't have access to the command line, or you don't want to use a push option:
- Add the string `[skip secret push protection]` to the commit message. For example:
```shell
git commit --amend -m "Add fake secret [skip secret push protection]"
```
You only need to add `[skip secret push protection]` to one of the commit messages in order to push your changes, even if there are multiple commits.
## Next steps
Consider enabling [pipeline secret detection](pipeline/index.md) to further improve the security of your projects.
......@@ -28,9 +28,9 @@ Make sure you have the following before you complete this tutorial:
## Commit a secret
In GitLab, most secrets consist of a standard prefix followed by a string of random characters.
The prefix makes it easier to identify secrets. For example, the fake secret `glpat-12345678901234567890`
is a personal access token because it has the prefix `glpat-`.
GitLab identifies secrets by matching specific patterns of letters, digits, and symbols. These patterns
are also used to identify the type of secret. For example, the fake secret `glpat-12345678901234567890`
is a personal access token because it begins with the string `glpat-`.
Although many secrets can be identified by format, you might accidentally commit a secret while you're working in a repository.
Let's simulate accidentally committing a secret:
......
......@@ -81,6 +81,7 @@ export default {
error: '',
errorMessages: [],
errorSources: [],
extraMergeRequestInput: null,
isCreating: false,
isDeleting: false,
policy: null,
......@@ -143,7 +144,8 @@ export default {
// Process error to pass to specific component
this.errorSources = [...newErrorSources, ...parseError(error)];
},
async handleSave({ action, policy, isActiveRuleMode = false }) {
async handleSave({ action, extraMergeRequestInput = null, policy, isActiveRuleMode = false }) {
this.extraMergeRequestInput = extraMergeRequestInput;
this.policyModificationAction = action || this.policyActionName;
this.policy = policy;
this.isActiveRuleMode = isActiveRuleMode;
......@@ -172,6 +174,7 @@ export default {
await goToPolicyMR({
action: this.policyModificationAction,
assignedPolicyProject,
extraMergeRequestInput: this.extraMergeRequestInput,
name: this.originalName || fromYaml({ manifest: this.policy })?.name,
namespacePath: this.namespacePath,
yamlEditorValue: this.policy,
......
......@@ -165,10 +165,14 @@ export default {
},
async handleModifyPolicy(act) {
this.policyModificationAction = act || this.policyActionName;
const extraMergeRequestInput = getMergeRequestConfig(queryToObject(window.location.search), {
namespacePath: this.namespacePath,
});
if (this.glFeatures.securityPoliciesProjectBackgroundWorker) {
this.$emit('save', {
action: this.policyModificationAction,
extraMergeRequestInput,
policy: this.yamlEditorValue,
});
return;
......@@ -179,12 +183,6 @@ export default {
try {
const assignedPolicyProject = await this.getSecurityPolicyProject();
const extraMergeRequestInput = getMergeRequestConfig(
queryToObject(window.location.search),
{
namespacePath: this.namespacePath,
},
);
await goToPolicyMR({
action: this.policyModificationAction,
assignedPolicyProject,
......
......@@ -229,6 +229,7 @@ describe('EditorWrapper component', () => {
expect(goToPolicyMR).toHaveBeenCalledWith({
action,
assignedPolicyProject: existingAssignedPolicyProject,
extraMergeRequestInput: null,
name: mockDastScanExecutionObject.name,
namespacePath: defaultProjectPath,
yamlEditorValue: mockDastScanExecutionManifest,
......@@ -236,6 +237,29 @@ describe('EditorWrapper component', () => {
});
});
describe('compliance framework migration', () => {
it('passes extra merge request input to goToPolicyMR', async () => {
factory({
provide: { assignedPolicyProject: existingAssignedPolicyProject },
subscriptionMock: getSecurityPolicyProjectSubscriptionErrorHandlerMock,
});
const extraMergeRequestInput = {
title: 'test',
description: 'test',
};
findScanExecutionPolicyEditor().vm.$emit('save', {
action: undefined,
policy: mockDastScanExecutionManifest,
extraMergeRequestInput,
});
await waitForPromises();
expect(goToPolicyMR).toHaveBeenCalledTimes(1);
expect(goToPolicyMR).toHaveBeenCalledWith(
expect.objectContaining({ extraMergeRequestInput }),
);
});
});
describe('error handling', () => {
const createError = (cause) => ({ message: 'There was an error', cause });
const approverCause = { field: 'approvers_ids' };
......
......@@ -91,7 +91,7 @@ describe('EditorComponent', () => {
const findActionSection = () => wrapper.findComponent(ActionSection);
const findRuleSection = () => wrapper.findComponent(RuleSection);
describe('when url params are passed', () => {
describe('when url params are passed w/ securityPoliciesProjectBackgroundWorker false', () => {
beforeEach(() => {
Object.defineProperty(window, 'location', {
writable: true,
......@@ -148,6 +148,61 @@ describe('EditorComponent', () => {
});
});
describe('when url params are passed w/ securityPoliciesProjectBackgroundWorker true', () => {
beforeEach(() => {
Object.defineProperty(window, 'location', {
writable: true,
value: { search: '' },
});
window.location.search = new URLSearchParams(Object.entries(customYamlUrlParams)).toString();
factory({ provide: { glFeatures: { securityPoliciesProjectBackgroundWorker: true } } });
});
it('configures initial policy from passed url params', () => {
expect(findPolicyEditorLayout().props('policy')).toMatchObject({
type: customYamlUrlParams.type,
content: {
include: [{ file: 'foo', project: 'bar' }],
},
pipeline_config_strategy: 'override_project_ci',
metadata: {
compliance_pipeline_migration: true,
},
});
});
it('saves a new policy with correct title and description', async () => {
findPolicyEditorLayout().vm.$emit('save-policy');
await waitForPromises();
expect(wrapper.emitted('save')[0]).toHaveLength(1);
expect(wrapper.emitted('save')[0][0]).toMatchObject({
extraMergeRequestInput: expect.objectContaining({
title: 'Compliance pipeline migration to pipeline execution policy',
description: expect.stringContaining('This merge request migrates compliance pipeline'),
}),
});
});
it('uses absolute links in description', async () => {
findPolicyEditorLayout().vm.$emit('save-policy');
await waitForPromises();
expect(wrapper.emitted('save')[0][0]).toMatchObject({
extraMergeRequestInput: expect.objectContaining({
description: expect.stringContaining(
`[Foo](http://test.host/groups/path/to/project/-/security/compliance_dashboard/frameworks/1)`,
),
}),
});
});
afterEach(() => {
window.location.search = '';
});
});
describe('rule mode', () => {
it('renders the editor', () => {
factory();
......@@ -220,7 +275,9 @@ describe('EditorComponent', () => {
factoryFn({ provide: { glFeatures: { securityPoliciesProjectBackgroundWorker: true } } });
findPolicyEditorLayout().vm.$emit(event);
await waitForPromises();
expect(wrapper.emitted('save')).toEqual([[{ action, policy: yamlEditorValue }]]);
expect(wrapper.emitted('save')).toEqual([
[{ action, extraMergeRequestInput: null, policy: yamlEditorValue }],
]);
});
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment