Skip to content

Commit

Permalink
fix: PC-9592 Validate opsgenie and slack URLs at submission (#623)
Browse files Browse the repository at this point in the history
## Motivation

Described in [PC-9592 ](https://nobl9.atlassian.net/browse/PC-9592)

## Summary

Added validation for opsgenie and slack URLs, the validation is the same
as the validation in corresponding notifications services:
*
[opsgenie](https://github.com/nobl9/n9/blob/main/notifications/notificationsopsgenie/internal/notifier/notificationsopsgenie.go#L92)
*
[slack](https://github.com/nobl9/n9/blob/main/notifications/notificationsslack/internal/notifier/notificationsslack.go#L83)

## Related changes

None

## Testing

-  Added unit tests

## Release Notes

Improved URL validation for Opsgenie and Slack alert methods:
* Slack URLs must now begin with `https://hooks.slack.com/services/`.
* Opsgenie URLs must start with either `https://api.opsgenie.com` or
`https://api.eu.opsgenie.com`.
  • Loading branch information
piotrkwarcinski authored Jan 15, 2025
1 parent a6eef55 commit 1080309
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 14 deletions.
22 changes: 20 additions & 2 deletions manifest/v1alpha/alertmethod/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,13 @@ var pagerDutyValidation = govy.New[PagerDutyAlertMethod](
Rules(rules.StringMaxLength(32)),
)

const validSlackURLPrefix = "https://hooks.slack.com/services/"

var slackValidation = govy.New[SlackAlertMethod](
govy.For(func(s SlackAlertMethod) string { return s.URL }).
WithName("url").
HideValue().
Include(optionalUrlValidation()),
Include(optionalUrlWithPrefixValidation(validSlackURLPrefix)),
)

var discordValidation = govy.New[DiscordAlertMethod](
Expand All @@ -179,10 +181,15 @@ var discordValidation = govy.New[DiscordAlertMethod](
Include(optionalUrlValidation()),
)

const (
validOpsgenieURL = "https://api.opsgenie.com"
validOpsgenieEuURL = "https://api.eu.opsgenie.com"
)

var opsgenieValidation = govy.New[OpsgenieAlertMethod](
govy.For(func(o OpsgenieAlertMethod) string { return o.URL }).
WithName("url").
Include(optionalUrlValidation()),
Include(optionalUrlWithPrefixValidation(validOpsgenieURL, validOpsgenieEuURL)),
govy.For(func(o OpsgenieAlertMethod) string { return o.Auth }).
WithName("auth").
HideValue().
Expand Down Expand Up @@ -270,6 +277,17 @@ var emailValidation = govy.New[EmailAlertMethod](
Rules(rules.SliceMaxLength[[]string](maxEmailRecipients)),
)

func optionalUrlWithPrefixValidation(prefixes ...string) govy.Validator[string] {
return govy.New[string](
govy.For(govy.GetSelf[string]()).
When(
func(v string) bool { return !isHiddenValue(v) },
govy.WhenDescription("is empty or equal to '%s'", v1alpha.HiddenValue),
).
Rules(rules.StringURL(), rules.StringStartsWith(prefixes...)),
)
}

func optionalUrlValidation() govy.Validator[string] {
return govy.New[string](
govy.For(govy.GetSelf[string]()).
Expand Down
35 changes: 30 additions & 5 deletions manifest/v1alpha/alertmethod/validation_opsgenie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,21 @@ import (

func TestValidate_Spec_OpsgenieAlertMethod(t *testing.T) {
for name, spec := range map[string]OpsgenieAlertMethod{
"passes with valid http url": {
URL: "http://example.com",
"passes with valid opsgenie http url": {
URL: "https://api.opsgenie.com",
Auth: "Basic token",
},
"passes with valid https url": {
URL: "https://example.com",
"passes with valid opsgenie eu http url": {
URL: "https://api.eu.opsgenie.com",
Auth: "Basic token",
},
"passes with valid opsgenie http url and suffix": {
URL: "https://api.opsgenie.com/test",
Auth: "Basic token",
},
"passes with valid opsgenie eu http url and suffix": {
URL: "https://api.eu.opsgenie.com/test",
Auth: "Basic token",
},
"passes with undefined url": {},
"passes with empty url": {
Expand Down Expand Up @@ -52,17 +61,33 @@ func TestValidate_Spec_OpsgenieAlertMethod(t *testing.T) {
AlertMethod OpsgenieAlertMethod
}{
"fails with invalid url": {
ExpectedErrorsCount: 1,
ExpectedErrorsCount: 2,
ExpectedErrors: []testutils.ExpectedError{
{
Prop: "spec.opsgenie.url",
Code: rules.ErrorCodeStringURL,
},
{
Prop: "spec.opsgenie.url",
Code: rules.ErrorCodeStringStartsWith,
},
},
AlertMethod: OpsgenieAlertMethod{
URL: "example.com",
},
},
"fails with invalid prefix": {
ExpectedErrorsCount: 1,
ExpectedErrors: []testutils.ExpectedError{
{
Prop: "spec.opsgenie.url",
Code: rules.ErrorCodeStringStartsWith,
},
},
AlertMethod: OpsgenieAlertMethod{
URL: "https://opsgenie.com/test",
},
},
"fails with invalid auth": {
ExpectedErrorsCount: 1,
ExpectedErrors: []testutils.ExpectedError{
Expand Down
26 changes: 21 additions & 5 deletions manifest/v1alpha/alertmethod/validation_slack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (

func TestValidate_Spec_SlackAlertMethod(t *testing.T) {
for name, spec := range map[string]SlackAlertMethod{
"passes with valid http url": {
URL: "http://example.com",
"passes with valid slack http url": {
URL: "https://hooks.slack.com/services/",
},
"passes with valid https url": {
URL: "https://example.com",
"passes with slack https url and suffix": {
URL: "https://hooks.slack.com/services/test",
},
"passes with undefined url": {},
"passes with empty url": {
Expand All @@ -41,17 +41,33 @@ func TestValidate_Spec_SlackAlertMethod(t *testing.T) {
AlertMethod SlackAlertMethod
}{
"fails with invalid url": {
ExpectedErrorsCount: 1,
ExpectedErrorsCount: 2,
ExpectedErrors: []testutils.ExpectedError{
{
Prop: "spec.slack.url",
Code: rules.ErrorCodeStringURL,
},
{
Prop: "spec.slack.url",
Code: rules.ErrorCodeStringStartsWith,
},
},
AlertMethod: SlackAlertMethod{
URL: "example.com",
},
},
"fails with invalid prefix": {
ExpectedErrorsCount: 1,
ExpectedErrors: []testutils.ExpectedError{
{
Prop: "spec.slack.url",
Code: rules.ErrorCodeStringStartsWith,
},
},
AlertMethod: SlackAlertMethod{
URL: "https://slack.com/services/test",
},
},
} {
t.Run(name, func(t *testing.T) {
alertMethod := validAlertMethod()
Expand Down
4 changes: 2 additions & 2 deletions manifest/v1alpha/alertmethod/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func TestValidate_Spec(t *testing.T) {
alertMethod := validAlertMethod()
alertMethod.Spec = Spec{
Slack: &SlackAlertMethod{
URL: "https://example.com",
URL: "https://hooks.slack.com/services/321/123/secret",
},
Teams: &TeamsAlertMethod{
URL: "https://example.com",
Expand All @@ -113,7 +113,7 @@ func validAlertMethod() AlertMethod {
},
Spec{
Slack: &SlackAlertMethod{
URL: "https://example.com",
URL: "https://hooks.slack.com/services/321/123/secret",
},
},
)
Expand Down

0 comments on commit 1080309

Please sign in to comment.