Nothing
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")
expect_equal(cookieToStr("abc", 123, http=TRUE, secure=TRUE, same_site="None"), "abc=123; HttpOnly; Secure; SameSite=None")
now <- force(Sys.time())
cookieToStr_ <- function(expiration, ...) {
cookieToStr("abc", 123, expiration = expiration, ..., now = now)
}
cookie_match <- function(expirationStr, expiresSec) {
# difftime is exclusive, so the Max-Age may be off by one on positive time diffs.
# Using a regex from 0 to 9 incase a slow machine is encountered
# match from 0 to 9 seconds
paste0("abc=123; Expires= ", expirationStr, "; Max-Age= ", expiresSec)
}
expect_cookie <- function(expiresSec) {
expires <- now + expiresSec
expyStr <- format(expires, format="%a, %e %b %Y %T", tz="GMT", usetz=TRUE)
# When given as a number of seconds
expect_equal(cookieToStr_(expiresSec), cookie_match(expyStr, expiresSec), label = "Raw seconds expiration cookie")
# When given as a POSIXct
expect_equal(cookieToStr_(expires), cookie_match(expyStr, expiresSec), label = "POSIXct expiration cookie")
}
# Test date in the future
expect_cookie(9)
# Works with a negative number of seconds
expect_cookie(-8)
})
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"
)
expect_equal(
removeCookieStr("asdf", path = "/", http = TRUE, secure = TRUE, same_site = "None"),
"asdf=; Path=/; HttpOnly; Secure; SameSite=None; 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(random_cookie_key())), "")
xVals <- list(
list(),
"",
list(a = 4, b = 3),
rep("1234567890", 100) %>% paste0(collapse = "")
)
keys <- list(
NULL, # no key
asCookieKey(random_cookie_key()), # random key
asCookieKey(random_cookie_key()) # 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)
}) # error from jsonlite::parse_json()
# garbage in, key
expect_error({
decodeCookie(garbage, asCookieKey(random_cookie_key()))
}, "Could not separate")
infoList <- list(
# different cookies
list(
a = asCookieKey(random_cookie_key()),
b = asCookieKey(random_cookie_key()),
error = "Failed to decrypt"
),
# not encrypted, try to decrypt
list(
a = NULL,
b = asCookieKey(random_cookie_key()),
error = "Could not separate"
),
# encrypted, no decryption
list(
a = asCookieKey(random_cookie_key()),
b = NULL
# error from jsonlite::parse_json()
)
)
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")
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.