Commit 976d9cf0 authored by David O'Regan's avatar David O'Regan Committed by Andrew Fontaine

Make Alert details table attributes clickable

Allow a selection of Alert details
attributes to be clickable in the
alert details table and add checks
for possible XSS attacks

Changelog: changed
parent 646179ae
<script>
import { GlLoadingIcon, GlTable } from '@gitlab/ui';
import { GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui';
import { reduce } from 'lodash';
import {
capitalizeFirstCharacter,
convertToSentenceCase,
splitCamelCase,
} from '~/lib/utils/text_utility';
import { isSafeURL } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { PAGE_CONFIG } from '~/vue_shared/alert_details/constants';
......@@ -30,6 +31,7 @@ const allowedFields = [
export default {
components: {
GlLink,
GlLoadingIcon,
GlTable,
},
......@@ -94,6 +96,9 @@ export default {
isAllowed(fieldName) {
return allowedFields.includes(fieldName);
},
isValidLink(value) {
return typeof value === 'string' && isSafeURL(value);
},
},
};
</script>
......@@ -109,5 +114,11 @@ export default {
<template #table-busy>
<gl-loading-icon size="lg" color="dark" class="gl-mt-5" />
</template>
<template #cell(value)="{ item: { value } }">
<span v-if="!isValidLink(value)">{{ value }}</span>
<gl-link v-else :href="value" target="_blank">
{{ value }}
</gl-link>
</template>
</gl-table>
</template>
import { GlLoadingIcon, GlTable } from '@gitlab/ui';
import { GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
......@@ -7,6 +7,9 @@ const mockAlert = {
title: 'SyntaxError: Invalid or unexpected token',
severity: 'CRITICAL',
eventCount: 7,
service: 'https://gitlab.com',
// eslint-disable-next-line no-script-url
description: 'javascript:alert("XSS")',
createdAt: '2020-04-17T23:18:14.996Z',
startedAt: '2020-04-17T23:18:14.996Z',
endedAt: '2020-04-17T23:18:14.996Z',
......@@ -43,7 +46,7 @@ describe('AlertDetails', () => {
wrapper = null;
});
const findTableComponent = () => wrapper.find(GlTable);
const findTableComponent = () => wrapper.findComponent(GlTable);
const findTableKeys = () => findTableComponent().findAll('tbody td:first-child');
const findTableFieldValueByKey = (fieldKey) =>
findTableComponent()
......@@ -52,6 +55,7 @@ describe('AlertDetails', () => {
.at(0)
.find('td:nth-child(2)');
const findTableField = (fields, fieldName) => fields.filter((row) => row.text() === fieldName);
const findTableLinks = () => wrapper.findAllComponents(GlLink);
describe('Alert details', () => {
describe('empty state', () => {
......@@ -88,7 +92,16 @@ describe('AlertDetails', () => {
it('should show allowed alert fields', () => {
const fields = findTableKeys();
['Iid', 'Title', 'Severity', 'Status', 'Hosts', 'Environment'].forEach((field) => {
[
'Iid',
'Title',
'Severity',
'Status',
'Hosts',
'Environment',
'Service',
'Description',
].forEach((field) => {
expect(findTableField(fields, field).exists()).toBe(true);
});
});
......@@ -99,6 +112,12 @@ describe('AlertDetails', () => {
expect(findTableField(fields, field).exists()).toBe(false);
});
});
it('should render a clickable URL if safe', () => {
expect(findTableLinks().wrappers).toHaveLength(1);
expect(findTableLinks().at(0).props('isUnsafeLink')).toBe(false);
expect(findTableLinks().at(0).attributes('href')).toBe(mockAlert.service);
});
});
describe('environment', () => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment