gitlab-ce/spec/frontend/lib/utils/secret_detection_spec.js

172 lines
6.1 KiB
JavaScript

import { detectAndConfirmSensitiveTokens } from '~/lib/utils/secret_detection';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { InternalEvents } from '~/tracking';
import { sensitiveMessages, nonSensitiveMessages, secretDetectionFindings } from './mock_data';
jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
const mockConfirmAction = ({ confirmed }) => {
confirmAction.mockResolvedValueOnce(confirmed);
};
const trackingEventName = 'show_client_side_secret_detection_warning';
const trackingEventPayload = {
label: 'comment',
property: 'GitLab personal access token',
value: 0,
};
describe('detectAndConfirmSensitiveTokens', () => {
beforeEach(() => {
jest.spyOn(InternalEvents, 'trackEvent');
});
afterEach(() => {
jest.resetAllMocks();
});
describe('content without sensitive tokens', () => {
it.each(nonSensitiveMessages)(
'returns true and does not show warning for message: %s',
async (message) => {
const result = await detectAndConfirmSensitiveTokens({ content: message });
expect(result).toBe(true);
expect(confirmAction).not.toHaveBeenCalled();
},
);
it('does not trigger event tracking', () => {
const message = 'This is a test message';
detectAndConfirmSensitiveTokens({ content: message });
expect(InternalEvents.trackEvent).not.toHaveBeenCalled();
});
});
describe('content with sensitive tokens', () => {
describe.each(sensitiveMessages)('for message: %s', (message) => {
it('should show warning', async () => {
await detectAndConfirmSensitiveTokens({ content: message });
expect(confirmAction).toHaveBeenCalled();
});
it('should return true when confirmed is true', async () => {
mockConfirmAction({ confirmed: true });
const result = await detectAndConfirmSensitiveTokens({ content: message });
expect(result).toBe(true);
});
it('should return false when confirmed is false', async () => {
mockConfirmAction({ confirmed: false });
const result = await detectAndConfirmSensitiveTokens({ content: message });
expect(result).toBe(false);
});
});
describe('event tracking', () => {
const [message] = sensitiveMessages;
it('should track correct event when warning is dismissed', async () => {
mockConfirmAction({ confirmed: false });
await detectAndConfirmSensitiveTokens({ content: message });
expect(InternalEvents.trackEvent).toHaveBeenCalledWith(trackingEventName, {
...trackingEventPayload,
value: 0,
});
});
it('should track correct event when warning is accepted', async () => {
mockConfirmAction({ confirmed: true });
await detectAndConfirmSensitiveTokens({ content: message });
expect(InternalEvents.trackEvent).toHaveBeenCalledWith(trackingEventName, {
...trackingEventPayload,
value: 1,
});
});
});
});
describe('when custom pat prefix is set', () => {
beforeEach(() => {
gon.pat_prefix = 'specpat-';
});
const validTokenMessage = 'token: specpat-mGYFaXBmNLvLmrEb7xdf';
const invalidTokenMessage = 'token: glpat-mGYFaXBmNLvLmrEb7xdf';
it('should detect the valid token', async () => {
await detectAndConfirmSensitiveTokens({ content: validTokenMessage });
expect(confirmAction).toHaveBeenCalled();
});
it('should not detect the invalid token', async () => {
await detectAndConfirmSensitiveTokens({ content: invalidTokenMessage });
expect(confirmAction).not.toHaveBeenCalled();
});
});
describe('warning modal', () => {
const findings = secretDetectionFindings;
const baseConfirmActionParams = {
primaryBtnVariant: 'danger',
primaryBtnText: 'Add comment',
secondaryBtnText: 'Edit comment',
hideCancel: true,
modalHtmlMessage: expect.any(String),
};
describe('with single findings', () => {
const [{ message, type, redactedString }] = findings;
it('should call confirmAction with correct parameters', async () => {
await detectAndConfirmSensitiveTokens({ content: message });
const confirmActionArgs = confirmAction.mock.calls[0][1];
expect(confirmActionArgs).toMatchObject(baseConfirmActionParams);
expect(confirmActionArgs.title).toBe('Warning: Potential secret detected');
expect(confirmActionArgs.modalHtmlMessage).toContain(`${type}: ${redactedString}`);
});
});
describe('with multiple findings', () => {
const combinedMessage = findings.map(({ message }) => message).join(' ');
it('should call confirmAction with correct parameters', async () => {
await detectAndConfirmSensitiveTokens({ content: combinedMessage });
const confirmActionArgs = confirmAction.mock.calls[0][1];
expect(confirmActionArgs).toMatchObject(baseConfirmActionParams);
expect(confirmActionArgs.title).toBe('Warning: Potential secrets detected');
findings.forEach(({ type, redactedString }) => {
expect(confirmActionArgs.modalHtmlMessage).toContain(`${type}: ${redactedString}`);
});
});
});
describe('with different content type', () => {
const testCases = [
[
'comment',
'This comment appears to have the following secret in it. Are you sure you want to add this comment?',
],
[
'description',
'This description appears to have the following secret in it. Are you sure you want to add this description?',
],
];
it.each(testCases)('content type: %s', async (contentType, expectedMessage) => {
const [{ message }] = findings;
await detectAndConfirmSensitiveTokens({ content: message, contentType });
const confirmActionArgs = confirmAction.mock.calls[0][1];
expect(confirmActionArgs.modalHtmlMessage).toContain(expectedMessage);
expect(InternalEvents.trackEvent).toHaveBeenCalledWith(trackingEventName, {
...trackingEventPayload,
label: contentType,
});
});
});
});
});