Commit be86a9e3 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Validate PagerDuty payload using json_schemer

Allows to prevent parsing of invalid payload
parent bbb89ddf
......@@ -34,7 +34,7 @@ module IncidentManagement
strong_memoize(:pager_duty_processable_events) do
::PagerDuty::WebhookPayloadParser
.call(params.to_h)
.filter { |msg| msg['event'].in?(PAGER_DUTY_PROCESSABLE_EVENT_TYPES) }
.filter { |msg| msg['event'].to_s.in?(PAGER_DUTY_PROCESSABLE_EVENT_TYPES) }
end
end
......
{
"type": "object",
"required": ["messages"],
"properties": {
"messages": {
"type": "array",
"items": {
"type": "object",
"required": ["event", "incident"],
"properties": {
"event": {
"type": "string",
"enum": [
"incident.trigger",
"incident.acknowledge",
"incident.unacknowledge",
"incident.resolve",
"incident.assign",
"incident.escalate",
"incident.delegate",
"incident.annotate"
]
},
"incident": {
"type": "object",
"required": [
"html_url",
"incident_number",
"title",
"status",
"created_at",
"urgency",
"incident_key"
],
"properties": {
"html_url": { "type": "string" },
"incindent_number": { "type": "integer" },
"title": { "type": "string" },
"status": { "type": "string" },
"created_at": { "type": "string" },
"urgency": { "type": "string", "enum": ["high", "low"] },
"incident_key": { "type": ["string", "null"] },
"assignments": {
"type": "array",
"items": {
"assignee": {
"type": "array",
"items": {
"summary": { "type": "string" },
"html_url": { "type": "string" }
}
}
}
},
"impacted_services": {
"type": "array",
"items": {
"summary": { "type": "string" },
"html_url": { "type": "string" }
}
}
}
}
}
}
}
}
}
......@@ -2,6 +2,8 @@
module PagerDuty
class WebhookPayloadParser
SCHEMA_PATH = File.join('lib', 'pager_duty', 'validator', 'schemas', 'incident_trigger.json')
def initialize(payload)
@payload = payload
end
......@@ -19,6 +21,8 @@ module PagerDuty
attr_reader :payload
def parse_message(message)
return {} unless valid_payload?
{
'event' => message['event'],
'incident' => parse_incident(message['incident'])
......@@ -26,8 +30,6 @@ module PagerDuty
end
def parse_incident(incident)
return {} if incident.blank?
{
'url' => incident['html_url'],
'incident_number' => incident['incident_number'],
......@@ -62,5 +64,9 @@ module PagerDuty
def reject_empty(entities)
Array(entities).reject { |e| e['summary'].blank? && e['url'].blank? }
end
def valid_payload?
::JSONSchemer.schema(Pathname.new(SCHEMA_PATH)).valid?(payload)
end
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
require 'json_schemer'
RSpec.describe PagerDuty::WebhookPayloadParser do
describe '.call' do
......@@ -69,11 +70,11 @@ RSpec.describe PagerDuty::WebhookPayloadParser do
end
end
context 'when payload has no incidents' do
context 'when payload schema is invalid' do
let(:payload) { { 'messages' => [{ 'event' => 'incident.trigger' }] } }
it 'returns payload with blank incidents' do
is_expected.to eq([{ 'event' => 'incident.trigger', 'incident' => {} }])
is_expected.to eq([{}])
end
end
end
......
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