1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2016 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 #include <gtest/gtest.h>
18 #include <xattr/key_validator.h>
19 #include <cctype>
20 #include <locale>
21 
22 #include "daemon/subdocument_validators.h"
23 #include <platform/sized_buffer.h>
24 
25 /**
26  * Ensure that we don't accept empty keys
27  */
TEST(XattrKeyValidator, Empty)28 TEST(XattrKeyValidator, Empty) {
29     EXPECT_FALSE(is_valid_xattr_key({nullptr, 0}));
30     EXPECT_FALSE(is_valid_xattr_key({".", 1}));
31 }
32 
33 /**
34  * Ensure that we accept keys without a dot (the path is empty)
35  */
TEST(XattrKeyValidator, FullXattr)36 TEST(XattrKeyValidator, FullXattr) {
37     std::string key = "mydata";
38     EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()}));
39 }
40 
41 /**
42  * Ensure that we enforce the max limit
43  */
TEST(XattrKeyValidator, KeyLengthWithoutPath)44 TEST(XattrKeyValidator, KeyLengthWithoutPath) {
45     std::string key = "The Three Strikes and You're Out";
46     for (auto ii = key.length(); ii > 0; --ii) {
47         if (ii >= SUBDOC_MAX_XATTR_LENGTH) {
48             EXPECT_FALSE(is_valid_xattr_key({key.data(), ii}));
49         } else {
50             EXPECT_TRUE(is_valid_xattr_key({key.data(), ii}));
51         }
52     }
53 }
54 
55 /**
56  * Ensure that we enforce the max limit with a path element..
57  */
TEST(XattrKeyValidator, KeyLengthWithPath)58 TEST(XattrKeyValidator, KeyLengthWithPath) {
59     std::string key = "The Three Strikes and You're Out";
60     for (auto ii = key.length(); ii > 1; --ii) {
61         // Just make a copy and inject a dot ;)
62         std::string copy = key;
63         const_cast<char*>(copy.data())[ii - 1] = '.';
64         if (ii > SUBDOC_MAX_XATTR_LENGTH) {
65             EXPECT_FALSE(is_valid_xattr_key({copy.data(), copy.size()}))
66                     << "[" << copy << "]";
67         } else {
68             EXPECT_TRUE(is_valid_xattr_key({copy.data(), copy.size()}))
69                     << "[" << copy << "]";
70         }
71     }
72 }
73 
74 /**
75  * Ensure that we accept keys with a path
76  */
TEST(XattrKeyValidator, PartialXattr)77 TEST(XattrKeyValidator, PartialXattr) {
78     std::string key = "mydata.foobar";
79     EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()}));
80 }
81 
TEST(XattrKeyValidator, FullWithArrayIndex)82 TEST(XattrKeyValidator, FullWithArrayIndex) {
83     std::string key = "mydata[0]";
84     EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()}));
85 }
86 
87 
88 /**
89  * X-Keys starting with a leading underscore ('_', 0x5F) are considered system
90  * Such keys must be at least two characters
91  */
TEST(XattrKeyValidator, SystemXattr)92 TEST(XattrKeyValidator, SystemXattr) {
93     std::string key = "_sync";
94     EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()}));
95 
96     key = "_";
97     EXPECT_FALSE(is_valid_xattr_key({key.data(), key.size()}));
98 }
99 
100 /**
101  * X-Keys starting with a leading dollar sign ('$', 0x24) are considered
102  * virtual xattrs
103  */
TEST(XattrKeyValidator, VirtualXattr)104 TEST(XattrKeyValidator, VirtualXattr) {
105     std::string key = "$document";
106     EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()}));
107 
108     key = "$XTOC";
109     EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()}));
110 
111     key = "$";
112     EXPECT_FALSE(is_valid_xattr_key({key.data(), key.size()}));
113 }
114 
115 /**
116  * There is no restrictions on the characters that may be inside the xattr
117  * key. It'll take way too long time to test all of the possible values, so
118  * lets just validate with all 7 bit ASCII characters.
119  */
TEST(XattrKeyValidator, AllCharachtersInXattr)120 TEST(XattrKeyValidator, AllCharachtersInXattr) {
121     std::vector<char> key(2);
122     key[0] = 'a';
123 
124     // 0 is not allowed according to (should be using the two byte encoding)
125     key[1] = 0;
126     EXPECT_FALSE(is_valid_xattr_key({key.data(), key.size()}));
127 
128     for (int ii = 1; ii < 0x80; ++ii) {
129         key[1] = char(ii);
130         EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()})) << ii;
131     }
132 }
133 
134 /**
135  * X-Keys starting with the following characters are reserved and
136  * cannot be used:
137  *    * ispunct(), excluding underscore (!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~)
138  *    * iscntrl()
139  */
TEST(XattrKeyValidator, RestrictedXattrPrefix)140 TEST(XattrKeyValidator, RestrictedXattrPrefix) {
141     std::locale loc("C");
142     std::vector<char> key(2);
143     key[1] = 'b';
144 
145     for (int ii = 0; ii < 0x80; ++ii) { // values over 0x80 == multibyte UTF8
146         key[0] = char(ii);
147         if ((std::ispunct(key[0], loc) && (key[0] != '_' && key[0] != '$')) ||
148             std::iscntrl(key[0], loc)) {
149             EXPECT_FALSE(is_valid_xattr_key({key.data(), key.size()}));
150         } else {
151             EXPECT_TRUE(is_valid_xattr_key({key.data(), key.size()}));
152         }
153     }
154 }
155 
156 /**
157  * XATTRS should be UTF8
158  */
159 
testInvalidUtf(char magic, int nbytes)160 static void testInvalidUtf(char magic, int nbytes) {
161     std::vector<char> data;
162     data.push_back(magic);
163 
164     for (int ii = 0; ii < nbytes; ++ii) {
165         EXPECT_FALSE(is_valid_xattr_key({data.data(), data.size()}));
166         data.push_back(char(0xbf));
167     }
168     EXPECT_TRUE(is_valid_xattr_key({data.data(), data.size()}));
169 
170     for (int ii = 1; ii < nbytes + 1; ++ii) {
171         data[ii] = char(0xff);
172         EXPECT_FALSE(is_valid_xattr_key({data.data(), data.size()})) << ii;
173         data[ii] = char(0xbf);
174         EXPECT_TRUE(is_valid_xattr_key({data.data(), data.size()})) << ii;
175     }
176 }
177 
TEST(XattrKeyValidator, InvalidUTF8_2Bytes)178 TEST(XattrKeyValidator, InvalidUTF8_2Bytes) {
179     testInvalidUtf(char(0xDF), 1);
180 }
181 
TEST(XattrKeyValidator, InvalidUTF8_3Bytes)182 TEST(XattrKeyValidator, InvalidUTF8_3Bytes) {
183     testInvalidUtf(char(0xEF), 2);
184 }
185 
TEST(XattrKeyValidator, InvalidUTF8_4Bytes)186 TEST(XattrKeyValidator, InvalidUTF8_4Bytes) {
187     testInvalidUtf(char(0xF7), 3);
188 }
189