Validate CAP against the spec

This gives us some extra confidence that there aren’t any problems with
the data we’re getting from the other service. It doesn’t address any
specific problems we’ve seen, rather it seems like a sensible precaution
to take.
This commit is contained in:
Chris Hill-Scott
2021-01-18 10:01:47 +00:00
parent 38f07db23e
commit 26871eeacc
6 changed files with 274 additions and 5 deletions

View File

@@ -8,6 +8,8 @@ from app.models import BROADCAST_TYPE, BroadcastMessage, BroadcastStatusType
from app.schema_validation import validate
from app.v2.broadcast import v2_broadcast_blueprint
from app.v2.broadcast.broadcast_schemas import post_broadcast_schema
from app.v2.errors import BadRequestError
from app.xml_schemas import validate_xml
@v2_broadcast_blueprint.route("", methods=['POST'])
@@ -21,7 +23,13 @@ def create_broadcast():
if request.content_type == 'application/json':
broadcast_json = request.get_json()
elif request.content_type == 'application/cap+xml':
broadcast_json = cap_xml_to_dict(request.get_data(as_text=True))
cap_xml = request.get_data(as_text=True)
if not validate_xml(cap_xml, 'CAP-v1.2.xsd'):
raise BadRequestError(
message=f'Request data is not valid CAP XML',
status_code=400,
)
broadcast_json = cap_xml_to_dict(cap_xml)
else:
raise BadRequestError(
message=f'Content type {request.content_type} not supported',

View File

@@ -0,0 +1,218 @@
<?xml version = "1.0" encoding = "UTF-8"?>
<!-- Copyright OASIS Open 2010 All Rights Reserved -->
<schema xmlns = "http://www.w3.org/2001/XMLSchema"
targetNamespace = "urn:oasis:names:tc:emergency:cap:1.2"
xmlns:cap = "urn:oasis:names:tc:emergency:cap:1.2"
xmlns:xs = "http://www.w3.org/2001/XMLSchema"
elementFormDefault = "qualified"
attributeFormDefault = "unqualified"
version = "1.2">
<element name = "alert">
<annotation>
<documentation>CAP Alert Message (version 1.2)</documentation>
</annotation>
<complexType>
<sequence>
<element name = "identifier" type = "xs:string"/>
<element name = "sender" type = "xs:string"/>
<element name = "sent">
<simpleType>
<restriction base = "xs:dateTime">
<pattern value = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d[-,+]\d\d:\d\d"/>
</restriction>
</simpleType>
</element>
<element name = "status">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Actual"/>
<enumeration value = "Exercise"/>
<enumeration value = "System"/>
<enumeration value = "Test"/>
<enumeration value = "Draft"/>
</restriction>
</simpleType>
</element>
<element name = "msgType">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Alert"/>
<enumeration value = "Update"/>
<enumeration value = "Cancel"/>
<enumeration value = "Ack"/>
<enumeration value = "Error"/>
</restriction>
</simpleType>
</element>
<element name = "source" type = "xs:string" minOccurs = "0"/>
<element name = "scope">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Public"/>
<enumeration value = "Restricted"/>
<enumeration value = "Private"/>
</restriction>
</simpleType>
</element>
<element name = "restriction" type = "xs:string" minOccurs = "0"/>
<element name = "addresses" type = "xs:string" minOccurs = "0"/>
<element name = "code" type = "xs:string" minOccurs = "0" maxOccurs = "unbounded"/>
<element name = "note" type = "xs:string" minOccurs = "0"/>
<element name = "references" type = "xs:string" minOccurs = "0"/>
<element name = "incidents" type = "xs:string" minOccurs = "0"/>
<element name = "info" minOccurs = "0" maxOccurs = "unbounded">
<complexType>
<sequence>
<element name = "language" type = "xs:language" default = "en-US" minOccurs = "0"/>
<element name = "category" maxOccurs = "unbounded">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Geo"/>
<enumeration value = "Met"/>
<enumeration value = "Safety"/>
<enumeration value = "Security"/>
<enumeration value = "Rescue"/>
<enumeration value = "Fire"/>
<enumeration value = "Health"/>
<enumeration value = "Env"/>
<enumeration value = "Transport"/>
<enumeration value = "Infra"/>
<enumeration value = "CBRNE"/>
<enumeration value = "Other"/>
</restriction>
</simpleType>
</element>
<element name = "event" type = "xs:string"/>
<element name = "responseType" minOccurs = "0" maxOccurs = "unbounded">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Shelter"/>
<enumeration value = "Evacuate"/>
<enumeration value = "Prepare"/>
<enumeration value = "Execute"/>
<enumeration value = "Avoid"/>
<enumeration value = "Monitor"/>
<enumeration value = "Assess"/>
<enumeration value = "AllClear"/>
<enumeration value = "None"/>
</restriction>
</simpleType>
</element>
<element name = "urgency">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Immediate"/>
<enumeration value = "Expected"/>
<enumeration value = "Future"/>
<enumeration value = "Past"/>
<enumeration value = "Unknown"/>
</restriction>
</simpleType>
</element>
<element name = "severity">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Extreme"/>
<enumeration value = "Severe"/>
<enumeration value = "Moderate"/>
<enumeration value = "Minor"/>
<enumeration value = "Unknown"/>
</restriction>
</simpleType>
</element>
<element name = "certainty">
<simpleType>
<restriction base = "xs:string">
<enumeration value = "Observed"/>
<enumeration value = "Likely"/>
<enumeration value = "Possible"/>
<enumeration value = "Unlikely"/>
<enumeration value = "Unknown"/>
</restriction>
</simpleType>
</element>
<element name = "audience" type = "xs:string" minOccurs = "0"/>
<element name = "eventCode" minOccurs = "0" maxOccurs = "unbounded">
<complexType>
<sequence>
<element ref = "cap:valueName"/>
<element ref = "cap:value"/>
</sequence>
</complexType>
</element>
<element name = "effective" minOccurs = "0">
<simpleType>
<restriction base = "xs:dateTime">
<pattern value = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d[-,+]\d\d:\d\d"/>
</restriction>
</simpleType>
</element>
<element name = "onset" minOccurs = "0">
<simpleType>
<restriction base = "xs:dateTime">
<pattern value = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d[-,+]\d\d:\d\d"/>
</restriction>
</simpleType>
</element>
<element name = "expires" minOccurs = "0">
<simpleType>
<restriction base = "xs:dateTime">
<pattern value = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d[-,+]\d\d:\d\d"/>
</restriction>
</simpleType>
</element>
<element name = "senderName" type = "xs:string" minOccurs = "0"/>
<element name = "headline" type = "xs:string" minOccurs = "0"/>
<element name = "description" type = "xs:string" minOccurs = "0"/>
<element name = "instruction" type = "xs:string" minOccurs = "0"/>
<element name = "web" type = "xs:anyURI" minOccurs = "0"/>
<element name = "contact" type = "xs:string" minOccurs = "0"/>
<element name = "parameter" minOccurs = "0" maxOccurs = "unbounded">
<complexType>
<sequence>
<element ref = "cap:valueName"/>
<element ref = "cap:value"/>
</sequence>
</complexType>
</element>
<element name = "resource" minOccurs = "0" maxOccurs = "unbounded">
<complexType>
<sequence>
<element name = "resourceDesc" type = "xs:string"/>
<element name = "mimeType" type = "xs:string"/>
<element name = "size" type = "xs:integer" minOccurs = "0"/>
<element name = "uri" type = "xs:anyURI" minOccurs = "0"/>
<element name = "derefUri" type = "xs:string" minOccurs = "0"/>
<element name = "digest" type = "xs:string" minOccurs = "0"/>
</sequence>
</complexType>
</element>
<element name = "area" minOccurs = "0" maxOccurs = "unbounded">
<complexType>
<sequence>
<element name = "areaDesc" type = "xs:string"/>
<element name = "polygon" type = "xs:string" minOccurs = "0" maxOccurs = "unbounded"/>
<element name = "circle" type = "xs:string" minOccurs = "0" maxOccurs = "unbounded"/>
<element name = "geocode" minOccurs = "0" maxOccurs = "unbounded">
<complexType>
<sequence>
<element ref = "cap:valueName"/>
<element ref = "cap:value"/>
</sequence>
</complexType>
</element>
<element name = "altitude" type = "xs:decimal" minOccurs = "0"/>
<element name = "ceiling" type = "xs:decimal" minOccurs = "0"/>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
<any minOccurs = "0" maxOccurs = "unbounded" namespace = "http://www.w3.org/2000/09/xmldsig#" processContents = "lax"/>
</sequence>
</complexType>
</element>
<element name = "valueName" type = "xs:string"/>
<element name = "value" type = "xs:string"/>
</schema>

View File

@@ -0,0 +1,19 @@
from lxml import etree
from pathlib import Path
def validate_xml(document, schema_file_name):
path = Path(__file__).resolve().parent / schema_file_name
contents = path.read_text()
schema_root = etree.XML(contents.encode('utf-8'))
schema = etree.XMLSchema(schema_root)
parser = etree.XMLParser(schema=schema)
try:
etree.fromstring(document, parser)
except etree.XMLSyntaxError:
return False
return True