Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.system.NodeId;
import org.graylog2.shared.utilities.StringUtils;
import org.graylog2.system.urlallowlist.UrlAllowlistValidator;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -57,21 +58,24 @@ public class SlackEventNotification implements EventNotification {
private final SlackClient slackClient;
private final TemplateModelProvider templateModelProvider;
private final EventProcedureProvider eventProcedureProvider;
private final UrlAllowlistValidator urlAllowlistValidator;

@Inject
public SlackEventNotification(EventNotificationService notificationCallbackService,
Engine templateEngine,
NotificationService notificationService,
NodeId nodeId, SlackClient slackClient,
TemplateModelProvider templateModelProvider,
EventProcedureProvider eventProcedureProvider) {
EventProcedureProvider eventProcedureProvider,
UrlAllowlistValidator urlAllowlistValidator) {
this.notificationCallbackService = notificationCallbackService;
this.templateEngine = requireNonNull(templateEngine);
this.notificationService = requireNonNull(notificationService);
this.nodeId = requireNonNull(nodeId);
this.slackClient = requireNonNull(slackClient);
this.templateModelProvider = templateModelProvider;
this.eventProcedureProvider = eventProcedureProvider;
this.urlAllowlistValidator = requireNonNull(urlAllowlistValidator);
}

/**
Expand All @@ -84,6 +88,7 @@ public void execute(EventNotificationContext ctx) throws EventNotificationExcept
LOG.debug("SlackEventNotification backlog size in method execute is [{}]", config.backlogSize());

try {
urlAllowlistValidator.validateUrl(config.webhookUrl(), ctx);
SlackMessage slackMessage = createSlackMessage(ctx, config);
slackClient.send(slackMessage, config.webhookUrl());
} catch (JsonProcessingException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.system.NodeId;
import org.graylog2.shared.bindings.providers.ObjectMapperProvider;
import org.graylog2.system.urlallowlist.UrlAllowlistValidator;
import org.graylog2.web.customization.CustomizationConfig;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
Expand All @@ -59,6 +60,7 @@ public class TeamsEventNotification implements EventNotification {
private final RequestClient requestClient;
private final TemplateModelProvider templateModelProvider;
private final CustomizationConfig customizationConfig;
private final UrlAllowlistValidator urlAllowlistValidator;

@Inject
public TeamsEventNotification(EventNotificationService notificationCallbackService,
Expand All @@ -67,7 +69,8 @@ public TeamsEventNotification(EventNotificationService notificationCallbackServi
NotificationService notificationService,
NodeId nodeId, RequestClient requestClient,
TemplateModelProvider templateModelProvider,
CustomizationConfig customizationConfig) {
CustomizationConfig customizationConfig,
UrlAllowlistValidator urlAllowlistValidator) {
this.notificationCallbackService = notificationCallbackService;
this.objectMapperProvider = requireNonNull(objectMapperProvider);
this.templateEngine = requireNonNull(templateEngine);
Expand All @@ -76,6 +79,7 @@ public TeamsEventNotification(EventNotificationService notificationCallbackServi
this.requestClient = requireNonNull(requestClient);
this.templateModelProvider = templateModelProvider;
this.customizationConfig = customizationConfig;
this.urlAllowlistValidator = requireNonNull(urlAllowlistValidator);
}

/**
Expand All @@ -88,6 +92,7 @@ public void execute(EventNotificationContext ctx) throws EventNotificationExcept
LOG.debug("TeamsEventNotification backlog size in method execute is [{}]", config.backlogSize());

try {
urlAllowlistValidator.validateUrl(config.webhookUrl(), ctx);
TeamsMessage teamsMessage = createTeamsMessage(ctx, config);
requestClient.send(objectMapperProvider.getForTimeZone(config.timeZone()).writeValueAsString(teamsMessage), config.webhookUrl());
} catch (TemporaryEventNotificationException exp) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.graylog2.plugin.Tools;
import org.graylog2.plugin.system.NodeId;
import org.graylog2.shared.utilities.StringUtils;
import org.graylog2.system.urlallowlist.UrlAllowlistValidator;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -55,19 +56,22 @@ public class TeamsEventNotificationV2 implements EventNotification {
private final NodeId nodeId;
private final RequestClient requestClient;
private final TemplateModelProvider templateModelProvider;
private final UrlAllowlistValidator urlAllowlistValidator;

@Inject
public TeamsEventNotificationV2(EventNotificationService notificationCallbackService,
@Named("JsonSafe") Engine jsonTemplateEngine,
NotificationService notificationService,
NodeId nodeId, RequestClient requestClient,
TemplateModelProvider templateModelProvider) {
TemplateModelProvider templateModelProvider,
UrlAllowlistValidator urlAllowlistValidator) {
this.notificationCallbackService = notificationCallbackService;
this.jsonTemplateEngine = requireNonNull(jsonTemplateEngine);
this.notificationService = requireNonNull(notificationService);
this.nodeId = requireNonNull(nodeId);
this.requestClient = requireNonNull(requestClient);
this.templateModelProvider = templateModelProvider;
this.urlAllowlistValidator = requireNonNull(urlAllowlistValidator);
}

/**
Expand All @@ -82,6 +86,7 @@ public void execute(EventNotificationContext ctx) throws EventNotificationExcept
LOG.debug("TeamsEventNotificationV2 backlog size in method execute is [{}]", config.backlogSize());

try {
urlAllowlistValidator.validateUrl(config.webhookUrl(), ctx);
final String requestBody = generateBody(ctx, config);
requestClient.send(requestBody, config.webhookUrl());
} catch (TemporaryEventNotificationException exp) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog2.system.urlallowlist;

import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.graylog.events.notifications.EventNotificationContext;
import org.graylog.events.notifications.NotificationTestData;
import org.graylog.events.notifications.TemporaryEventNotificationException;
import org.graylog.events.processor.EventDefinitionDto;

@Singleton
public class UrlAllowlistValidator {
private final UrlAllowlistService allowlistService;
private final UrlAllowlistNotificationService allowlistNotificationService;

@Inject
public UrlAllowlistValidator(UrlAllowlistService allowlistService,
UrlAllowlistNotificationService allowlistNotificationService) {
this.allowlistService = allowlistService;
this.allowlistNotificationService = allowlistNotificationService;
}

public void validateUrl(String url, EventNotificationContext ctx) throws TemporaryEventNotificationException {
if (!allowlistService.isAllowlisted(url)) {
if (!NotificationTestData.TEST_NOTIFICATION_ID.equals(ctx.notificationId())) {
final String eventDefTitle = ctx.eventDefinition().map(EventDefinitionDto::title).orElse("Unnamed");
final String description = "The alert notification \"" + eventDefTitle +
"\" is trying to access a URL which is not allowlisted. Please check your configuration. [url: \"" +
url + "\"]";
allowlistNotificationService.publishAllowlistFailure(description);
}
throw new TemporaryEventNotificationException("URL <" + url + "> is not allowlisted.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.graylog2.configuration.HttpConfiguration;
import org.graylog2.notifications.NotificationImpl;
import org.graylog2.notifications.NotificationService;
import org.graylog2.system.urlallowlist.UrlAllowlistValidator;
import org.graylog2.plugin.MessageFactory;
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.TestMessageFactory;
Expand Down Expand Up @@ -89,6 +90,9 @@ public class SlackEventNotificationTest {
@Mock
EventProcedureProvider mockEventProcedureProvider;

@Mock
UrlAllowlistValidator mockUrlAllowlistValidator;

private SlackEventNotificationConfig slackEventNotificationConfig;
private EventNotificationContext eventNotificationContext;
private final MessageFactory messageFactory = new TestMessageFactory();
Expand All @@ -113,7 +117,8 @@ public void setUp() {
nodeId,
mockSlackClient,
new TemplateModelProvider(CustomizationConfig.empty(), new ObjectMapperProvider(), new HttpConfiguration()),
mockEventProcedureProvider);
mockEventProcedureProvider,
mockUrlAllowlistValidator);
}

private void getDummySlackNotificationConfig() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.graylog2.configuration.HttpConfiguration;
import org.graylog2.notifications.NotificationImpl;
import org.graylog2.notifications.NotificationService;
import org.graylog2.system.urlallowlist.UrlAllowlistValidator;
import org.graylog2.plugin.MessageFactory;
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.TestMessageFactory;
Expand Down Expand Up @@ -85,6 +86,9 @@ public class TeamsEventNotificationTest {
@Mock
EventNotificationService notificationCallbackService;

@Mock
UrlAllowlistValidator mockUrlAllowlistValidator;

private TeamsEventNotificationConfig teamsEventNotificationConfig;
private EventNotificationContext eventNotificationContext;
private final MessageFactory messageFactory = new TestMessageFactory();
Expand All @@ -108,7 +112,8 @@ public void setUp() {
nodeId,
mockrequestClient,
new TemplateModelProvider(customizationConfig, new ObjectMapperProvider(), new HttpConfiguration()),
customizationConfig);
customizationConfig,
mockUrlAllowlistValidator);
}

private void getDummyTeamsNotificationConfig() {
Expand Down Expand Up @@ -354,7 +359,8 @@ public void testProductNameInDefaultMessage() throws EventNotificationException
nodeId,
mockrequestClient,
new TemplateModelProvider(customizationConfig, new ObjectMapperProvider(), new HttpConfiguration()),
customizationConfig);
customizationConfig,
mockUrlAllowlistValidator);

TeamsMessage actual = teamsEventNotification.createTeamsMessage(eventNotificationContext, TeamsEventNotificationConfig.builder().iconUrl(expectedImage).build());
assertThat(actual.sections())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.graylog2.configuration.HttpConfiguration;
import org.graylog2.notifications.NotificationImpl;
import org.graylog2.notifications.NotificationService;
import org.graylog2.system.urlallowlist.UrlAllowlistValidator;
import org.graylog2.plugin.MessageFactory;
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.TestMessageFactory;
Expand Down Expand Up @@ -183,6 +184,9 @@ public class TeamsEventNotificationV2Test {
@Mock
EventNotificationService notificationCallbackService;

@Mock
UrlAllowlistValidator mockUrlAllowlistValidator;

private TeamsEventNotificationConfigV2 notificationConfig;
private EventNotificationContext eventNotificationContext;

Expand All @@ -198,7 +202,8 @@ public void setUp() {
mockNotificationService,
nodeId,
mockrequestClient,
new TemplateModelProvider(CustomizationConfig.empty(), new ObjectMapperProvider(), new HttpConfiguration()));
new TemplateModelProvider(CustomizationConfig.empty(), new ObjectMapperProvider(), new HttpConfiguration()),
mockUrlAllowlistValidator);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
InputGroup,
Row,
} from 'components/bootstrap';
import { ColorPickerPopover, TimezoneSelect } from 'components/common';
import { ColorPickerPopover, TimezoneSelect, URLAllowListInput } from 'components/common';
import ColorLabel from 'components/sidecars/common/ColorLabel';
import DocumentationLink from 'components/support/DocumentationLink';
import usePluggableLicenseCheck from 'hooks/usePluggableLicenseCheck';
Expand Down Expand Up @@ -154,6 +154,10 @@
propagateChange(name, getValueFromInput(event.target));
};

const handleWebhookUrlChange = (event) => {
propagateChange('webhook_url', getValueFromInput(event.target));
};

const handleTimeZoneChange = (nextValue) => {
propagateChange('time_zone', nextValue);
};
Expand Down Expand Up @@ -183,16 +187,13 @@
</div>
<HelpBlock>Choose a color to use for this configuration.</HelpBlock>
</FormGroup>
<Input
id="notification-webhookUrl"
name="webhook_url"
<URLAllowListInput
label="Webhook URL"
type="text"
bsStyle={validation.errors.webhook_url ? 'error' : null}
help={validation?.errors?.webhook_url?.[0] || 'Slack "Incoming Webhook" URL'}
value={config.webhook_url || ''}
onChange={handleChange}
required
onChange={handleWebhookUrlChange}
validationState={validation.errors.webhook_url ? 'error' : null}
validationMessage={validation?.errors?.webhook_url?.[0] || 'Slack "Incoming Webhook" URL'}
url={config.webhook_url || ''}
autofocus={false}
/>
<Input
id="notification-channel"
Expand Down Expand Up @@ -236,12 +237,12 @@
<ControlLabel>Message Backlog Limit (optional)</ControlLabel>
<InputGroup>
<InputGroup.Addon>
<input
id="toggle_backlog_size"
type="checkbox"
checked={isBacklogSizeEnabled}
onChange={toggleBacklogSize}
/>

Check warning on line 245 in graylog2-web-interface/src/integrations/event-notifications/event-notification-types/SlackNotificationForm.tsx

View workflow job for this annotation

GitHub Actions / Reviewbot

A control must be associated with a text label.

No further rule information available.
</InputGroup.Addon>
<FormControl
type="number"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import { getValueFromInput } from 'util/FormsUtils';
import { Input, Button, ControlLabel, FormControl, FormGroup, HelpBlock, InputGroup } from 'components/bootstrap';
import { ColorPickerPopover, TimezoneSelect } from 'components/common';
import { ColorPickerPopover, TimezoneSelect, URLAllowListInput } from 'components/common';
import ColorLabel from 'components/sidecars/common/ColorLabel';
import DocsHelper from 'util/DocsHelper';
import DocumentationLink from 'components/support/DocumentationLink';
Expand Down Expand Up @@ -118,6 +118,10 @@
propagateChange(name, getValueFromInput(event.target));
};

const handleWebhookUrlChange = (event: any) => {
propagateChange('webhook_url', getValueFromInput(event.target));
};

const element = (
<p>
Custom message to be appended below the alert title. See{' '}
Expand All @@ -143,16 +147,13 @@
</div>
<HelpBlock>Choose a color to use for this configuration.</HelpBlock>
</FormGroup>
<Input
id="notification-webhookUrl"
name="webhook_url"
<URLAllowListInput
label="Webhook URL"
type="text"
bsStyle={validation.errors.webhook_url ? 'error' : null}
help={validation?.errors?.webhook_url?.[0] ?? 'Teams "Incoming Webhook" URL'}
value={config.webhook_url || ''}
onChange={handleChange}
required
onChange={handleWebhookUrlChange}
validationState={validation.errors.webhook_url ? 'error' : null}
validationMessage={validation?.errors?.webhook_url?.[0] ?? 'Teams "Incoming Webhook" URL'}
url={config.webhook_url || ''}
autofocus={false}
/>
<Input
id="notification-customMessage"
Expand Down Expand Up @@ -181,12 +182,12 @@
<ControlLabel>Message Backlog Limit (optional)</ControlLabel>
<InputGroup>
<InputGroup.Addon>
<input
id="toggle_backlog_size"
type="checkbox"
checked={isBacklogSizeEnabled}
onChange={toggleBacklogSize}
/>

Check warning on line 190 in graylog2-web-interface/src/integrations/event-notifications/event-notification-types/TeamsNotificationForm.tsx

View workflow job for this annotation

GitHub Actions / Reviewbot

A control must be associated with a text label.

No further rule information available.
</InputGroup.Addon>
<FormControl
type="number"
Expand Down
Loading