test_api_rr_validation.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. from time import sleep
  2. from typing import List, Tuple
  3. import pytest
  4. from conftest import DeSECAPIV1Client, NSClient
  5. def generate_params(dict_value_lists_by_type: dict) -> List[Tuple[str, str]]:
  6. return [
  7. (rr_type, value)
  8. for rr_type in dict_value_lists_by_type.keys()
  9. for value in dict_value_lists_by_type[rr_type]
  10. ]
  11. VALID_RECORDS_CANONICAL = {
  12. 'A': ['127.0.0.1', '127.0.0.2'],
  13. 'AAAA': ['::1', '::2'],
  14. 'AFSDB': ['2 turquoise.femto.edu.'],
  15. 'CAA': [
  16. '128 issue "letsencrypt.org"', '128 iodef "mailto:desec@example.com"',
  17. '1 issue "letsencrypt.org"'
  18. ],
  19. 'CERT': ['6 0 0 sadfdQ=='],
  20. 'CNAME': ['example.com.'],
  21. 'DHCID': ['aaaaaaaaaaaa', 'xxxx'],
  22. 'DLV': [
  23. '39556 13 1 aabbccddeeff',
  24. ],
  25. 'DS': [
  26. '39556 13 1 aabbccddeeff',
  27. ],
  28. 'EUI48': ['aa-bb-cc-dd-ee-ff'],
  29. 'EUI64': ['aa-bb-cc-dd-ee-ff-00-11'],
  30. 'HINFO': ['"ARMv8-A" "Linux"'],
  31. # 'IPSECKEY': ['12 0 2 . asdfdf==', '03 1 1 127.0.00.1 asdfdf==', '12 3 1 example.com. asdfdf==',],
  32. 'KX': ['4 example.com.', '28 io.'],
  33. 'LOC': [
  34. '23 12 59.000 N 42 22 48.500 W 65.00m 20.00m 10.00m 10.00m',
  35. ],
  36. 'MX': ['10 example.com.', '20 1.1.1.1.'],
  37. 'NAPTR': [
  38. '100 50 "s" "z3950+I2L+I2C" "" _z3950._tcp.gatech.edu.',
  39. ],
  40. 'NS': ['ns1.example.com.'],
  41. 'OPENPGPKEY': [
  42. 'mG8EXtVIsRMFK4EEACIDAwQSZPNqE4tS xLFJYhX+uabSgMrhOqUizJhkLx82', # key incomplete due to 500 byte limit
  43. ],
  44. 'PTR': ['example.com.', '*.example.com.'],
  45. 'RP': ['hostmaster.example.com. .'],
  46. # 'SMIMEA': ['3 1 0 aabbccddeeff'],
  47. 'SPF': [
  48. '"v=spf1 ip4:10.1" ".1.1 ip4:127" ".0.0.0/16 ip4:192.168.0.0/27 include:example.com -all"',
  49. '"v=spf1 include:example.com ~all"',
  50. '"v=spf1 ip4:10.1.1.1 ip4:127.0.0.0/16 ip4:192.168.0.0/27 include:example.com -all"',
  51. '"spf2.0/pra,mfrom ip6:2001:558:fe14:76:68:87:28:0/120 -all"',
  52. ],
  53. 'SRV': ['0 0 0 .', '100 1 5061 example.com.'],
  54. 'SSHFP': ['2 2 aabbcceeddff'],
  55. 'TLSA': ['3 1 1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',],
  56. 'TXT': [
  57. '"foobar"',
  58. '"foo" "bar"',
  59. '"foo" "" "bar"',
  60. '"" "" "foo" "" "bar"',
  61. r'"new\010line"',
  62. r'"\000" "NUL byte yo"',
  63. f'"{"a" * 255}" "{"a" * 243}"', # 500 byte total wire length
  64. ],
  65. 'URI': ['10 1 "ftp://ftp1.example.com/public"'],
  66. }
  67. VALID_RECORDS_NON_CANONICAL = {
  68. 'A': ['127.0.0.3'],
  69. 'AAAA': ['0000::0000:0003'],
  70. 'AFSDB': ['03 turquoise.FEMTO.edu.'],
  71. 'CAA': ['0128 "issue" "letsencrypt.org"'],
  72. 'CERT': ['06 00 00 sadfee=='],
  73. 'CNAME': ['EXAMPLE.TEST.'],
  74. 'DHCID': ['aa aaa aaaa a a a', 'xxxx'],
  75. 'DLV': [
  76. '6454 8 2 5CBA665A006F6487625C6218522F09BD3673C25FA10F25CB18459AA1 0DF1F520',
  77. '6454 8 2 5C BA665A006F6487625C6218522F09BD3673C25FA10F25CB18459AA1 0DF1F520',
  78. ],
  79. 'DS': [
  80. '6454 8 2 5CBA665A006F6487625C6218522F09BD3673C25FA10F25CB18459AA1 0DF1F520',
  81. '6454 8 2 5C BA665A006F6487625C6218522F09BD3673C25FA10F25CB18459AA1 0DF1F520',
  82. ],
  83. 'EUI48': ['AA-BB-CC-DD-EE-F1'],
  84. 'EUI64': ['AA-BB-CC-DD-EE-FF-00-12'],
  85. 'HINFO': ['cpu os'],
  86. # 'IPSECKEY': ['12 0 2 . asdfdf==', '03 1 1 127.0.00.1 asdfdf==', '12 3 1 example.com. asdfdf==',],
  87. 'KX': ['012 example.TEST.'],
  88. 'LOC': [
  89. '023 012 59 N 042 022 48.500 W 65.00m 20.00m 10.00m 10.00m',
  90. ],
  91. 'MX': ['10 010.1.1.1.'],
  92. 'NAPTR': [
  93. '100 50 "s" "z3950+I2L+I2C" "" _z3950._tcp.gatech.edu.',
  94. ],
  95. 'NS': ['EXaMPLE.COM.'],
  96. 'OPENPGPKEY': [
  97. 'mG8EXtVIsRMFK4EEAC==',
  98. 'mG8EXtVIsRMFK4EEACIDAwQSZPNqE4tSxLFJYhX+uabSgMrhOqUizJhkLx82', # key incomplete due to 500 byte limit
  99. ],
  100. 'PTR': ['EXAMPLE.TEST.'],
  101. 'RP': ['hostmaster.EXAMPLE.com. .'],
  102. # 'SMIMEA': ['3 01 0 aabbccDDeeff'],
  103. 'SPF': [],
  104. 'SRV': ['100 01 5061 example.com.'],
  105. 'SSHFP': ['02 2 aabbcceeddff'],
  106. 'TLSA': ['3 0001 1 000AAAAAAABBAAAAAAAAAAAAAAAAAAAAAAAA',],
  107. 'TXT': [
  108. f'"{"a" * 498}"',
  109. '"🧥 👚 👕 👖 👔 👗 👙 👘 👠 👡 👢 👞 👟 🥾 🥿 🧦 🧤 🧣 🎩 🧢 👒 🎓 ⛑ 👑 👝 👛 👜 💼 🎒 "',
  110. '"🧥 👚 👕 👖 👔 👗 👙 👘 👠 👡 👢 👞 👟 🥾 🥿 🧦 🧤 🧣 🎩 🧢 👒 🎓 ⛑ 👑 👝 👛 👜 💼 🎒 👓 🕶 🥽 🥼 🌂 🧵"',
  111. ],
  112. 'URI': ['10 01 "ftp://ftp1.example.test/public"',],
  113. }
  114. INVALID_RECORDS = {
  115. 'A': ['127.0.0.999', '127.000.0.01', '127.0.0.256', '::1', 'foobar', '10.0.1', '10!'],
  116. 'AAAA': ['::g', '1:1:1:1:1:1:1:1:', '1:1:1:1:1:1:1:1:1'],
  117. 'AFSDB': ['example.com.', '1 1', '1 de'],
  118. 'CAA': ['43235 issue "letsencrypt.org"'],
  119. 'CERT': ['6 0 sadfdd=='],
  120. 'CNAME': ['example.com', '10 example.com.'],
  121. 'DHCID': ['x', 'xx', 'xxx'],
  122. 'DLV': ['-34 13 1 aabbccddeeff'],
  123. 'DS': ['-34 13 1 aabbccddeeff'],
  124. 'EUI48': ['aa-bb-ccdd-ee-ff', 'AA-BB-CC-DD-EE-GG'],
  125. 'EUI64': ['aa-bb-cc-dd-ee-ff-gg-11', 'AA-BB-C C-DD-EE-FF-00-11'],
  126. 'HINFO': ['"ARMv8-A"', f'"a" "{"b" * 256}"'],
  127. # 'IPSECKEY': [],
  128. 'KX': ['-1 example.com', '10 example.com'],
  129. 'LOC': ['23 12 61.000 N 42 22 48.500 W 65.00m 20.00m 10.00m 10.00m', 'foo', '1.1.1.1'],
  130. 'MX': ['10 example.com', 'example.com.', '-5 asdf.', '65537 asdf.'],
  131. 'NAPTR': ['100 50 "s" "z3950+I2L+I2C" "" _z3950._tcp.gatech.edu',
  132. '100 50 "s" "" _z3950._tcp.gatech.edu.',
  133. '100 50 3 2 "z3950+I2L+I2C" "" _z3950._tcp.gatech.edu.'],
  134. 'NS': ['ns1.example.com', '127.0.0.1'],
  135. 'OPENPGPKEY': ['1 2 3'],
  136. 'PTR': ['"example.com."', '10 *.example.com.'],
  137. 'RP': ['hostmaster.example.com.', '10 foo.'],
  138. # 'SMIMEA': ['3 1 0 aGVsbG8gd29ybGQh', 'x 0 0 aabbccddeeff'],
  139. 'SPF': ['"v=spf1', 'v=spf1 include:example.com ~all'],
  140. 'SRV': ['0 0 0 0', '100 5061 example.com.'],
  141. 'SSHFP': ['aabbcceeddff'],
  142. 'TLSA': ['3 1 1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'],
  143. 'TXT': [
  144. 'foob"ar',
  145. 'v=spf1 include:example.com ~all',
  146. '"foo\nbar"',
  147. '"' + 124 * '🧥' + '==="', # 501 byte total length
  148. '"\x00" "Django rejects literal NUL byte"',
  149. ],
  150. 'URI': ['"1" "2" "3"'],
  151. }
  152. INVALID_RECORDS_PARAMS = [(rr_type, value) for rr_type in INVALID_RECORDS.keys() for value in INVALID_RECORDS[rr_type]]
  153. def test_soundness():
  154. assert INVALID_RECORDS.keys() == VALID_RECORDS_CANONICAL.keys() == VALID_RECORDS_NON_CANONICAL.keys()
  155. @pytest.mark.parametrize("rr_type,value", generate_params(VALID_RECORDS_CANONICAL))
  156. def test_create_valid_canonical(api_user_domain: DeSECAPIV1Client, ns_lord: NSClient, rr_type: str, value: str):
  157. assert api_user_domain.rr_set_create(api_user_domain.domains[0], rr_type, [value], subname="a").status_code == 201
  158. assert ns_lord.query(f"a.{api_user_domain.domains[0]}", rr_type) == {value}
  159. @pytest.mark.parametrize("rr_type,value", generate_params(VALID_RECORDS_NON_CANONICAL))
  160. def test_create_valid_non_canonical(api_user_domain: DeSECAPIV1Client, ns_lord: NSClient, rr_type: str, value: str):
  161. assert api_user_domain.rr_set_create(api_user_domain.domains[0], rr_type, [value], subname="a").status_code == 201
  162. assert len(ns_lord.query(f"a.{api_user_domain.domains[0]}", rr_type)) == 1
  163. @pytest.mark.parametrize("rr_type,value", INVALID_RECORDS_PARAMS)
  164. def test_create_invalid(api_user_domain: DeSECAPIV1Client, rr_type: str, value: str):
  165. assert api_user_domain.rr_set_create(api_user_domain.domains[0], rr_type, [value]).status_code == 400