Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plumber() and Plumber$new() will read source file as UTF-8 encoded #312

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ plumber 0.4.7
--------------------------------------------------------------------------------
* Add support for swagger for mounted routers (@bradleyhd, [#274](https://github.com/trestletech/plumber/issues/274)).
* BUGFIX: A multiline POST body is now collapsed to a single line ([#270](https://github.com/trestletech/plumber/issues/270)).
* The source files used in plumber must use the UTF-8 encoding if they contain
non-ASCII characters (@shrektan, [#312](https://github.com/trestletech/plumber/pull/312)).
* SECURITY: Wrap `jsonlite::fromJSON` to ensure that `jsonlite` never reads
input as a remote address (such as a file path or URL) and attempts to parse
that. The only known way to exploit this behavior in plumber unless an
API were using encrypted cookies and an attacker knew the encryption key in
order to craft arbitrary cookies.
order to craft arbitrary cookies. ([#325](https://github.com/trestletech/plumber/pull/325))


plumber 0.4.6
--------------------------------------------------------------------------------
Expand Down
24 changes: 19 additions & 5 deletions R/plumber.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
#' @import stringi
NULL

# Hard code UTF-8 file encoding
# Removes encoding headache at minor cost of setting encoding in editor
# https://github.com/trestletech/plumber/pull/312
utf8Encoding <- "UTF-8"

# used to identify annotation flags.
verbs <- c("GET", "PUT", "POST", "DELETE", "HEAD", "OPTIONS", "PATCH")
enumerateVerbs <- function(v){
Expand Down Expand Up @@ -50,7 +55,7 @@ plumb <- function(file, dir="."){
on.exit(setwd(old))

# Expect that entrypoint will provide us with the router
x <- source(entrypoint)
x <- source(entrypoint, encoding=utf8Encoding)

# source returns a list with value and visible elements, we want the (visible) value object.
pr <- x$value
Expand Down Expand Up @@ -177,10 +182,19 @@ plumber <- R6Class(
private$notFoundHandler <- default404Handler

if (!is.null(file)){
private$lines <- readLines(file)
private$parsed <- parse(file, keep.source=TRUE)

source(file, local=private$envir, echo=FALSE, keep.source=TRUE)
con <- file(file, encoding=utf8Encoding)
on.exit(close(con), add=TRUE)

# Read lines directly
private$lines <- readLines(con)
# "...produces an object of the descendant class ‘srcfilecopy’,
# which saves the source lines in a character vector" (?srcfilecopy)
srcfile <- srcfilecopy(file, private$lines, isFile=TRUE)
# "When ‘keep.source’ is ‘TRUE’, if ‘text’ is used,
# ‘srcfile’ will be set to a ‘srcfilecopy’ containing the text" (?parse)
private$parsed <- parse(text=private$lines, srcfile=srcfile, keep.source=TRUE)

source(con, local=private$envir, echo=FALSE, keep.source=TRUE)

for (i in 1:length(private$parsed)){
e <- private$parsed[i]
Expand Down
4 changes: 4 additions & 0 deletions tests/testthat/files/multibytes.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#* @get /echo
function() {
"中文消息"
}
12 changes: 12 additions & 0 deletions tests/testthat/test-multibytes.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
context("multibytes source file")

test_that("support files with multibytes", {
# on Windows, the default encoding is not UTF-8. So, plumber has to
# tell R to use UTF-8 encoding when reading the source file.
r <- plumber$new("files/multibytes.R")
req <- make_req("GET", "/echo")
res <- PlumberResponse$new()
out <- r$serve(req, res)$body
expect_identical(out, jsonlite::toJSON("\u4e2d\u6587\u6d88\u606f"))
expect_equal(Encoding(out), utf8Encoding)
})