-
Notifications
You must be signed in to change notification settings - Fork 255
/
test-cookies.R
267 lines (222 loc) · 7.33 KB
/
test-cookies.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
context("Cookies")
skip_if_no_cookie_support <- function() {
skip_if_not_installed("sodium")
skip_if_not_installed("base64enc")
}
test_that("cookies are parsed", {
cookies <- parseCookies("spaced=cookie%2C%20here; another=2")
expect_equal(names(cookies), c("spaced", "another"))
expect_equal(cookies$spaced, "cookie, here")
expect_equal(cookies$another, "2")
cookies <- parseCookies("a=zxcv=asdf; missingVal=; b=qwer=ttyui")
expect_equal(names(cookies), c("a", "missingVal", "b"))
expect_equal(cookies$a, "zxcv=asdf")
expect_equal(cookies$missingVal, "")
expect_equal(cookies$b, "qwer=ttyui")
})
test_that("missing cookies are an empty list", {
cookies <- parseCookies("")
expect_equal(cookies, list())
})
cookieReq <- function(cookieStr) {
req <- new.env()
req$HTTP_COOKIE <- cookieStr
cookieFilter(req)
req
}
test_that("the cookies list is set", {
req <- cookieReq("abc=123")
expect_equal(req$cookies$abc, "123")
})
test_that("missing cookie values are empty string", {
req <- cookieReq("abc=")
expect_equal(req$cookies$abc, "")
})
test_that("cookies can convert to string", {
testthat::skip_on_cran()
expect_equal(cookieToStr("abc", 123), "abc=123")
expect_equal(cookieToStr("complex", "string with spaces"), "complex=string%20with%20spaces")
expect_equal(cookieToStr("complex2", "forbidden:,%/"), "complex2=forbidden%3A%2C%25%2F")
expect_equal(cookieToStr("abc", 123, path="/somepath"), "abc=123; Path=/somepath")
expect_equal(cookieToStr("abc", 123, http=TRUE, secure=TRUE), "abc=123; HttpOnly; Secure")
# Test date in the future
expiresSec <- 10
expires <- Sys.time() + expiresSec
expyStr <- format(expires, format="%a, %e %b %Y %T", tz="GMT", usetz=TRUE)
# TODO: this test is vulnerable to Sys.time() crossing over a second boundary in between the
# line above and below.
# When given as a number of seconds
expect_equal(cookieToStr("abc", 123, expiration=expiresSec),
paste0("abc=123; Expires= ", expyStr, "; Max-Age= ", expiresSec))
# When given as a POSIXct
# difftime is exclusive, so the Max-Age may be off by one on positive time diffs.
expect_equal(cookieToStr("abc", 123, expiration=expires),
paste0("abc=123; Expires= ", expyStr, "; Max-Age= ", expiresSec-1))
# Works with a negative number of seconds
expiresSec <- -10
expires <- Sys.time() + expiresSec
expyStr <- format(expires, format="%a, %e %b %Y %T", tz="GMT", usetz=TRUE)
# TODO: this test is vulnerable to Sys.time() crossing over a second boundary in between the
# line above and below.
# When given as a number of seconds
expect_equal(cookieToStr("abc", 123, expiration=expiresSec),
paste0("abc=123; Expires= ", expyStr, "; Max-Age= ", expiresSec))
# When given as a POSIXct
expect_equal(cookieToStr("abc", 123, expiration=expires),
paste0("abc=123; Expires= ", expyStr, "; Max-Age= ", expiresSec))
})
test_that("remove cookie string works", {
expect_equal(
removeCookieStr("asdf"),
"asdf=; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
)
expect_equal(
removeCookieStr("asdf", path = "/"),
"asdf=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
)
expect_equal(
removeCookieStr("asdf", http = TRUE),
"asdf=; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
)
expect_equal(
removeCookieStr("asdf", secure = TRUE),
"asdf=; Secure; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
)
expect_equal(
removeCookieStr("asdf", path = "/", http = TRUE, secure = TRUE),
"asdf=; Path=/; HttpOnly; Secure; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
)
})
test_that("asCookieKey conforms entropy", {
skip_if_no_cookie_support()
secretFromStr <- function(val, count) {
rep(val, count) %>%
paste0(collapse = "")
}
expect_cookie_key <- function(key) {
expect_type(key, "raw")
expect_length(key, 32)
}
expect_invalid_cookie <- function(secret) {
expect_error({
asCookieKey(secret)
}, "Illegal cookie")
}
expect_legacy_cookie <- function(secret) {
expect_warning({
cookie <- asCookieKey(secret)
}, "Legacy cookie secret")
ret <- expect_cookie_key(cookie)
invisible(ret)
}
expect_warning({
expect_null(asCookieKey(NULL))
}, "Cookies will not be encrypted")
# not char
expect_invalid_cookie(42)
expect_invalid_cookie(sodium::random(31))
expect_invalid_cookie(sodium::random(100))
# legacy cookie
# convert non hexadecimal to hexadecimal
expect_legacy_cookie(secretFromStr("a", 63))
expect_legacy_cookie(secretFromStr("a", 65))
expect_legacy_cookie(secretFromStr("/", 64))
# Used as 64 digit hex bin
## lower case a
char <- secretFromStr("a", 64)
key <- asCookieKey(char)
expect_cookie_key(key)
expect_equal(key, sodium::hex2bin(char))
## upper case a
char <- secretFromStr("A", 64)
key <- asCookieKey(char)
expect_cookie_key(key)
expect_equal(key, sodium::hex2bin(char))
## hex char input
randomRaw <- sodium::random(32) %>% sodium::bin2hex()
key <- asCookieKey(randomRaw)
expect_cookie_key(key)
expect_equal(sodium::bin2hex(key), randomRaw)
})
test_that("cookie encryption works", {
skip_if_no_cookie_support()
# check that you can't encode a NULL value
expect_equal(encodeCookie(NULL, NULL), "")
expect_equal(encodeCookie(NULL, asCookieKey(randomCookieKey())), "")
xVals <- list(
list(),
"",
list(a = 4, b = 3),
rep("1234567890", 100) %>% paste0(collapse = "")
)
keys <- list(
NULL, # no key
asCookieKey(randomCookieKey()), # random key
asCookieKey(randomCookieKey()) # different random key
)
for (key in keys) {
for (x in xVals) {
encrypted <- encodeCookie(x, key)
encryptedStr <- cookieToStr("cookieVal", encrypted)
encryptedParsed <- parseCookies(encryptedStr)
maybeX <- decodeCookie(encryptedParsed$cookieVal, key)
expect_equal(x, maybeX)
}
}
})
test_that("cookie encyption fails smoothly", {
skip_if_no_cookie_support()
x <- list(x = 4, y = 5)
# garbage in
garbage <- x %>%
serialize(NULL) %>%
sodium::hash() %>% # make "garbage"
base64enc::base64encode()
# garbage in, no key
expect_error({
decodeCookie(garbage, NULL)
}, "not a valid JSON string")
# garbage in, key
expect_error({
decodeCookie(garbage, asCookieKey(randomCookieKey()))
}, "Could not separate")
infoList <- list(
# different cookies
list(
a = asCookieKey(randomCookieKey()),
b = asCookieKey(randomCookieKey()),
error = "Failed to decrypt"
),
# not encrypted, try to decrypt
list(
a = NULL,
b = asCookieKey(randomCookieKey()),
error = "Could not separate"
),
# encrypted, no decryption
list(
a = asCookieKey(randomCookieKey()),
b = NULL,
error = "(not a valid JSON string|embedded nul in string)"
)
)
for (info in infoList) {
keyA <- info$a
keyB <- info$b
err <- info$error
expect_error({
encrypted <- encodeCookie(x, keyA)
encryptedStr <- cookieToStr("cookieVal", encrypted)
encryptedParsed <- parseCookies(encryptedStr)
maybeX <- decodeCookie(encryptedParsed$cookieVal, keyB)
}, err)
}
})
test_that("large cookie size makes warning", {
skip_if_no_cookie_support()
largeObj <- rbind(iris, iris)
encrypted <- encodeCookie(largeObj, NULL)
expect_warning({
cookieToStr("cookieVal", encrypted)
}, "Cookie being saved is too large")
})