tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestReportToParser.cpp (17989B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "gtest/gtest.h"
      8 #include "mozilla/dom/ReportingHeader.h"
      9 #include "nsIURI.h"
     10 #include "nsNetUtil.h"
     11 
     12 using namespace mozilla;
     13 using namespace mozilla::dom;
     14 
     15 TEST(ReportToParser, Basic)
     16 {
     17  nsCOMPtr<nsIURI> uri;
     18 
     19  nsresult rv = NS_NewURI(getter_AddRefs(uri), "https://example.com");
     20  ASSERT_EQ(NS_OK, rv);
     21 
     22  bool urlEqual = false;
     23 
     24  // Empty header.
     25  UniquePtr<ReportingHeader::Client> client =
     26      ReportingHeader::ParseReportToHeader(nullptr, uri, ""_ns);
     27  ASSERT_TRUE(!client);
     28 
     29  // Empty header.
     30  client = ReportingHeader::ParseReportToHeader(nullptr, uri, "    "_ns);
     31  ASSERT_TRUE(!client);
     32 
     33  // No minimal attributes
     34  client = ReportingHeader::ParseReportToHeader(nullptr, uri, "{}"_ns);
     35  ASSERT_TRUE(!client);
     36 
     37  // Single client
     38  client = ReportingHeader::ParseReportToHeader(
     39      nullptr, uri,
     40      nsLiteralCString(
     41          "{\"max_age\": 42, \"endpoints\": [{\"url\": "
     42          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
     43  ASSERT_TRUE(!!client);
     44  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
     45  ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
     46  ASSERT_FALSE(client->mGroups.ElementAt(0).mIncludeSubdomains);
     47  ASSERT_EQ(42, client->mGroups.ElementAt(0).mTTL);
     48  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
     49  ASSERT_TRUE(
     50      NS_SUCCEEDED(
     51          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
     52              uri, &urlEqual)) &&
     53      urlEqual);
     54  ASSERT_EQ((uint32_t)1,
     55            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
     56  ASSERT_EQ((uint32_t)2,
     57            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
     58 
     59  // 2 clients, same group name.
     60  client = ReportingHeader::ParseReportToHeader(
     61      nullptr, uri,
     62      nsLiteralCString(
     63          "{\"max_age\": 43, \"endpoints\": [{\"url\": "
     64          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
     65          "{\"max_age\": 44, \"endpoints\": [{\"url\": "
     66          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
     67  ASSERT_TRUE(!!client);
     68  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
     69  ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
     70  ASSERT_EQ(43, client->mGroups.ElementAt(0).mTTL);
     71 
     72  // 2 clients, the first one with an invalid group name.
     73  client = ReportingHeader::ParseReportToHeader(
     74      nullptr, uri,
     75      nsLiteralCString(
     76          "{\"max_age\": 43, \"group\": 123, \"endpoints\": [{\"url\": "
     77          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
     78          "{\"max_age\": 44, \"endpoints\": [{\"url\": "
     79          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
     80  ASSERT_TRUE(!!client);
     81  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
     82  ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
     83  ASSERT_EQ(44, client->mGroups.ElementAt(0).mTTL);
     84 
     85  // 2 clients, the first one with an invalid group name.
     86  client = ReportingHeader::ParseReportToHeader(
     87      nullptr, uri,
     88      nsLiteralCString(
     89          "{\"max_age\": 43, \"group\": null, \"endpoints\": [{\"url\": "
     90          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
     91          "{\"max_age\": 44, \"endpoints\": [{\"url\": "
     92          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
     93  ASSERT_TRUE(!!client);
     94  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
     95  ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
     96  ASSERT_EQ(44, client->mGroups.ElementAt(0).mTTL);
     97 
     98  // 2 clients, the first one with an invalid group name.
     99  client = ReportingHeader::ParseReportToHeader(
    100      nullptr, uri,
    101      nsLiteralCString(
    102          "{\"max_age\": 43, \"group\": {}, \"endpoints\": [{\"url\": "
    103          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
    104          "{\"max_age\": 44, \"endpoints\": [{\"url\": "
    105          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    106  ASSERT_TRUE(!!client);
    107  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    108  ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
    109  ASSERT_EQ(44, client->mGroups.ElementAt(0).mTTL);
    110 
    111  // Single client: optional params
    112  client = ReportingHeader::ParseReportToHeader(
    113      nullptr, uri,
    114      nsLiteralCString(
    115          "{\"max_age\": 45, \"group\": \"foobar\", \"include_subdomains\": "
    116          "true, \"endpoints\": [{\"url\": \"https://example.com\", "
    117          "\"priority\": 1, \"weight\": 2}]}"));
    118  ASSERT_TRUE(!!client);
    119  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    120  ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("foobar"));
    121  ASSERT_TRUE(client->mGroups.ElementAt(0).mIncludeSubdomains);
    122  ASSERT_EQ(45, client->mGroups.ElementAt(0).mTTL);
    123 
    124  // 2 clients, the first incomplete: missing max_age.
    125  client = ReportingHeader::ParseReportToHeader(
    126      nullptr, uri,
    127      nsLiteralCString(
    128          "{\"endpoints\": [{\"url\": \"https://example.com\", \"priority\": "
    129          "1, \"weight\": 2}]},"
    130          "{\"max_age\": 46, \"endpoints\": [{\"url\": "
    131          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    132  ASSERT_TRUE(!!client);
    133  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    134  ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
    135 
    136  // 2 clients, the first incomplete: invalid max_age.
    137  client = ReportingHeader::ParseReportToHeader(
    138      nullptr, uri,
    139      nsLiteralCString(
    140          "{\"max_age\": null, \"endpoints\": [{\"url\": "
    141          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
    142          "{\"max_age\": 46, \"endpoints\": [{\"url\": "
    143          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    144  ASSERT_TRUE(!!client);
    145  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    146  ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
    147 
    148  // 2 clients, the first incomplete: invalid max_age.
    149  client = ReportingHeader::ParseReportToHeader(
    150      nullptr, uri,
    151      nsLiteralCString(
    152          "{\"max_age\": \"foobar\", \"endpoints\": [{\"url\": "
    153          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
    154          "{\"max_age\": 46, \"endpoints\": [{\"url\": "
    155          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    156  ASSERT_TRUE(!!client);
    157  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    158  ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
    159 
    160  // 2 clients, the first incomplete: invalid max_age.
    161  client = ReportingHeader::ParseReportToHeader(
    162      nullptr, uri,
    163      nsLiteralCString(
    164          "{\"max_age\": {}, \"endpoints\": [{\"url\": "
    165          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
    166          "{\"max_age\": 46, \"endpoints\": [{\"url\": "
    167          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    168  ASSERT_TRUE(!!client);
    169  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    170  ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
    171 
    172  // 2 clients, the first incomplete: missing endpoints
    173  client = ReportingHeader::ParseReportToHeader(
    174      nullptr, uri,
    175      nsLiteralCString(
    176          "{\"max_age\": 47},"
    177          "{\"max_age\": 48, \"endpoints\": [{\"url\": "
    178          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    179  ASSERT_TRUE(!!client);
    180  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    181  ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
    182 
    183  // 2 clients, the first incomplete: invalid endpoints
    184  client = ReportingHeader::ParseReportToHeader(
    185      nullptr, uri,
    186      nsLiteralCString(
    187          "{\"max_age\": 47, \"endpoints\": null },"
    188          "{\"max_age\": 48, \"endpoints\": [{\"url\": "
    189          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    190  ASSERT_TRUE(!!client);
    191  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    192  ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
    193 
    194  // 2 clients, the first incomplete: invalid endpoints
    195  client = ReportingHeader::ParseReportToHeader(
    196      nullptr, uri,
    197      nsLiteralCString(
    198          "{\"max_age\": 47, \"endpoints\": \"abc\" },"
    199          "{\"max_age\": 48, \"endpoints\": [{\"url\": "
    200          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    201  ASSERT_TRUE(!!client);
    202  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    203  ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
    204 
    205  // 2 clients, the first incomplete: invalid endpoints
    206  client = ReportingHeader::ParseReportToHeader(
    207      nullptr, uri,
    208      nsLiteralCString(
    209          "{\"max_age\": 47, \"endpoints\": 42 },"
    210          "{\"max_age\": 48, \"endpoints\": [{\"url\": "
    211          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    212  ASSERT_TRUE(!!client);
    213  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    214  ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
    215 
    216  // 2 clients, the first incomplete: invalid endpoints
    217  client = ReportingHeader::ParseReportToHeader(
    218      nullptr, uri,
    219      nsLiteralCString(
    220          "{\"max_age\": 47, \"endpoints\": {} },"
    221          "{\"max_age\": 48, \"endpoints\": [{\"url\": "
    222          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    223  ASSERT_TRUE(!!client);
    224  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    225  ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
    226 
    227  // 2 clients, the first incomplete: empty endpoints
    228  client = ReportingHeader::ParseReportToHeader(
    229      nullptr, uri,
    230      nsLiteralCString(
    231          "{\"max_age\": 49, \"endpoints\": []},"
    232          "{\"max_age\": 50, \"endpoints\": [{\"url\": "
    233          "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
    234  ASSERT_TRUE(!!client);
    235  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    236  ASSERT_EQ(50, client->mGroups.ElementAt(0).mTTL);
    237 
    238  // 2 endpoints, the first incomplete: missing url
    239  client = ReportingHeader::ParseReportToHeader(
    240      nullptr, uri,
    241      nsLiteralCString("{\"max_age\": 51, \"endpoints\": ["
    242                       " {\"priority\": 1, \"weight\": 2},"
    243                       " {\"url\": \"https://example.com\", \"priority\": 1, "
    244                       "\"weight\": 2}]}"));
    245  ASSERT_TRUE(!!client);
    246  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    247  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    248  ASSERT_TRUE(
    249      NS_SUCCEEDED(
    250          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    251              uri, &urlEqual)) &&
    252      urlEqual);
    253  ASSERT_EQ((uint32_t)1,
    254            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    255  ASSERT_EQ((uint32_t)2,
    256            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    257 
    258  // 2 endpoints, the first incomplete: invalid url
    259  client = ReportingHeader::ParseReportToHeader(
    260      nullptr, uri,
    261      nsLiteralCString("{\"max_age\": 51, \"endpoints\": ["
    262                       " {\"url\": 42, \"priority\": 1, \"weight\": 2},"
    263                       " {\"url\": \"https://example.com\", \"priority\": 1, "
    264                       "\"weight\": 2}]}"));
    265  ASSERT_TRUE(!!client);
    266  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    267  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    268  ASSERT_TRUE(
    269      NS_SUCCEEDED(
    270          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    271              uri, &urlEqual)) &&
    272      urlEqual);
    273  ASSERT_EQ((uint32_t)1,
    274            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    275  ASSERT_EQ((uint32_t)2,
    276            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    277 
    278  // 2 endpoints, the first incomplete: invalid url
    279  client = ReportingHeader::ParseReportToHeader(
    280      nullptr, uri,
    281      nsLiteralCString(
    282          "{\"max_age\": 51, \"endpoints\": ["
    283          " {\"url\": \"something here\", \"priority\": 1, \"weight\": 2},"
    284          " {\"url\": \"https://example.com\", \"priority\": 1, \"weight\": "
    285          "2}]}"));
    286  ASSERT_TRUE(!!client);
    287  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    288  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    289  ASSERT_TRUE(
    290      NS_SUCCEEDED(
    291          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    292              uri, &urlEqual)) &&
    293      urlEqual);
    294  ASSERT_EQ((uint32_t)1,
    295            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    296  ASSERT_EQ((uint32_t)2,
    297            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    298 
    299  // 2 endpoints, the first incomplete: invalid url
    300  client = ReportingHeader::ParseReportToHeader(
    301      nullptr, uri,
    302      nsLiteralCString("{\"max_age\": 51, \"endpoints\": ["
    303                       " {\"url\": {}, \"priority\": 1, \"weight\": 2},"
    304                       " {\"url\": \"https://example.com\", \"priority\": 1, "
    305                       "\"weight\": 2}]}"));
    306  ASSERT_TRUE(!!client);
    307  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    308  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    309  ASSERT_TRUE(
    310      NS_SUCCEEDED(
    311          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    312              uri, &urlEqual)) &&
    313      urlEqual);
    314  ASSERT_EQ((uint32_t)1,
    315            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    316  ASSERT_EQ((uint32_t)2,
    317            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    318 
    319  // 2 endpoints, the first incomplete: missing priority
    320  client = ReportingHeader::ParseReportToHeader(
    321      nullptr, uri,
    322      nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
    323                       " {\"url\": \"https://example.com\", \"weight\": 3}]}"));
    324  ASSERT_TRUE(!!client);
    325  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    326  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    327  ASSERT_TRUE(
    328      NS_SUCCEEDED(
    329          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    330              uri, &urlEqual)) &&
    331      urlEqual);
    332  ASSERT_EQ((uint32_t)1,
    333            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    334  ASSERT_EQ((uint32_t)3,
    335            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    336 
    337  // 2 endpoints, the first incomplete: invalid priority
    338  client = ReportingHeader::ParseReportToHeader(
    339      nullptr, uri,
    340      nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
    341                       " {\"url\": \"https://example.com\", \"priority\": "
    342                       "{}, \"weight\": 2},"
    343                       " {\"url\": \"https://example.com\", \"priority\": 2, "
    344                       "\"weight\": 3}]}"));
    345  ASSERT_TRUE(!!client);
    346  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    347  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    348  ASSERT_TRUE(
    349      NS_SUCCEEDED(
    350          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    351              uri, &urlEqual)) &&
    352      urlEqual);
    353  ASSERT_EQ((uint32_t)2,
    354            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    355  ASSERT_EQ((uint32_t)3,
    356            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    357 
    358  // 2 endpoints, the first incomplete: invalid priority
    359  client = ReportingHeader::ParseReportToHeader(
    360      nullptr, uri,
    361      nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
    362                       " {\"url\": \"https://example.com\", \"priority\": "
    363                       "\"ok\", \"weight\": 2},"
    364                       " {\"url\": \"https://example.com\", \"priority\": 2, "
    365                       "\"weight\": 3}]}"));
    366  ASSERT_TRUE(!!client);
    367  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    368  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    369  ASSERT_TRUE(
    370      NS_SUCCEEDED(
    371          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    372              uri, &urlEqual)) &&
    373      urlEqual);
    374  ASSERT_EQ((uint32_t)2,
    375            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    376  ASSERT_EQ((uint32_t)3,
    377            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    378 
    379  // 2 endpoints, the first incomplete: missing weight
    380  client = ReportingHeader::ParseReportToHeader(
    381      nullptr, uri,
    382      nsLiteralCString(
    383          "{\"max_age\": 52, \"endpoints\": ["
    384          " {\"url\": \"https://example.com\", \"priority\": 5}]}"));
    385  ASSERT_TRUE(!!client);
    386  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    387  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    388  ASSERT_TRUE(
    389      NS_SUCCEEDED(
    390          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    391              uri, &urlEqual)) &&
    392      urlEqual);
    393  ASSERT_EQ((uint32_t)5,
    394            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    395  ASSERT_EQ((uint32_t)1,
    396            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    397 
    398  // 2 endpoints, the first incomplete: invalid weight
    399  client = ReportingHeader::ParseReportToHeader(
    400      nullptr, uri,
    401      nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
    402                       " {\"url\": \"https://example.com\", \"priority\": 4, "
    403                       "\"weight\": []},"
    404                       " {\"url\": \"https://example.com\", \"priority\": 5, "
    405                       "\"weight\": 6}]}"));
    406  ASSERT_TRUE(!!client);
    407  ASSERT_EQ((uint32_t)1, client->mGroups.Length());
    408  ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
    409  ASSERT_TRUE(
    410      NS_SUCCEEDED(
    411          client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
    412              uri, &urlEqual)) &&
    413      urlEqual);
    414  ASSERT_EQ((uint32_t)5,
    415            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
    416  ASSERT_EQ((uint32_t)6,
    417            client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
    418 }