Skip to content

Commit

Permalink
Httpd: improve handling of arguments in directives
Browse files Browse the repository at this point in the history
* handle '>""' properly
* make space between quoted args optional
* do not get confused by quoted strings in bare arguments

Fixes hercules-team#429
Fixes hercules-team#435
Fixes hercules-team#470
  • Loading branch information
lutter committed Sep 26, 2017
1 parent a9ba44f commit ff6052c
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 8 deletions.
42 changes: 34 additions & 8 deletions lenses/httpd.aug
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ let comment =


(* borrowed from shellvars.aug *)
let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'|\\\\ /
let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'|\\\\ /
let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/

Expand All @@ -116,25 +115,52 @@ let comp = /[<>=]?=/
* Attributes
*****************************************************************)

let arg_dir = [ label "arg" . store (char_arg_dir+|dquot|squot) ]
(* The arguments for a directive come in two flavors: quoted with single or
* double quotes, or bare. Bare arguments may not start with a single or
* double quote; since we also treat "word lists" special, i.e. lists
* enclosed in curly braces, bare arguments may not start with those,
* either.
*
* Bare arguments may not contain unescaped spaces, but we allow escaping
* with '\\'. Quoted arguments can contain anything, though the quote must
* be escaped with '\\'.
*)
let bare = /[^{"' \t\n\r]([^ \t\n\r]|\\\\.)*[^ \t\n\r\\]|[^{"' \t\n\r\\]/

let arg_quoted = [ label "arg" . store (dquot|squot) ]
let arg_bare = [ label "arg" . store bare ]

(* message argument starts with " but ends at EOL *)
let arg_dir_msg = [ label "arg" . store dquot_msg ]
let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ]
let arg_wl = [ label "arg" . store (char_arg_wl+|dquot|squot) ]

(* comma-separated wordlist as permitted in the SSLRequire directive *)
let arg_wordlist =
let wl_start = Util.del_str "{" in
let wl_end = Util.del_str "}" in
let wl_start = dels "{" in
let wl_end = dels "}" in
let wl_sep = del /[ \t]*,[ \t]*/ ", "
in [ label "wordlist" . wl_start . arg_wl . (wl_sep . arg_wl)* . wl_end ]

let argv (l:lens) = l . (sep_spc . l)*

(* the arguments of a directive. We use this once we have parsed the name
* of the directive, and the space right after it. When dir_args is used,
* we also know that we have at least one argument. We need to be careful
* with the spacing between arguments: quoted arguments and word lists do
* not need to have space between them, but bare arguments do.
*
* Apache apparently is also happy if the last argument starts with a double
* quote, but has no corresponding closing duoble quote, which is what
* arg_dir_msg handles
*)
let dir_args =
let arg_nospc = arg_quoted|arg_wordlist in
(arg_bare . sep_spc | arg_nospc . sep_osp)* . (arg_bare|arg_nospc|arg_dir_msg)

let directive =
(* arg_dir_msg may be the last or only argument *)
let dir_args = (argv (arg_dir|arg_wordlist) . (sep_spc . arg_dir_msg)?) | arg_dir_msg
in [ indent . label "directive" . store word . (sep_spc . dir_args)? . eol ]
[ indent . label "directive" . store word . (sep_spc . dir_args)? . eol ]

let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ]

let section (body:lens) =
(* opt_eol includes empty lines *)
Expand Down
34 changes: 34 additions & 0 deletions lenses/tests/test_httpd.aug
Original file line number Diff line number Diff line change
Expand Up @@ -593,3 +593,37 @@ test Httpd.lns get "# \\\n\n" = { }
test Httpd.comment get "# a\\\n\n" = { "#comment" = "a" }
test Httpd.comment get "# \\\na\\\n\n" = { "#comment" = "a" }
test Httpd.comment get "# \\\n\\\na \\\n\\\n\n" = { "#comment" = "a" }

(* Comparison with empty string did not work. Issue #429 *)
test Httpd.dir_args get ">\"a\"" = { "arg" = ">\"a\"" }
test Httpd.dir_args get ">\"\"" = { "arg" = ">\"\"" }
test Httpd.directive get "RewriteCond ${movedPageMap:$1} >\"a\"\n" =
{ "directive" = "RewriteCond"
{ "arg" = "${movedPageMap:$1}" }
{ "arg" = ">\"a\"" }}
test Httpd.directive get "RewriteCond ${movedPageMap:$1} >\"\"\n" =
{ "directive" = "RewriteCond"
{ "arg" = "${movedPageMap:$1}" }
{ "arg" = ">\"\"" }}

(* Quoted arguments may or may not have space spearating them. Issue #435 *)
test Httpd.directive get
"ProxyPassReverse \"/js\" \"http://127.0.0.1:8123/js\"\n" =
{ "directive" = "ProxyPassReverse"
{ "arg" = "\"/js\"" }
{ "arg" = "\"http://127.0.0.1:8123/js\"" } }

test Httpd.directive get
"ProxyPassReverse \"/js\"\"http://127.0.0.1:8123/js\"\n" =
{ "directive" = "ProxyPassReverse"
{ "arg" = "\"/js\"" }
{ "arg" = "\"http://127.0.0.1:8123/js\"" } }

(* Don't get confused by quoted strings inside bare arguments. Issue #470 *)
test Httpd.directive get
"RequestHeader set X-Forwarded-Proto https expr=(%{HTTP:CF-Visitor}='{\"scheme\":\"https\"}')\n" =
{ "directive" = "RequestHeader"
{ "arg" = "set" }
{ "arg" = "X-Forwarded-Proto" }
{ "arg" = "https" }
{ "arg" = "expr=(%{HTTP:CF-Visitor}='{\"scheme\":\"https\"}')" } }

0 comments on commit ff6052c

Please sign in to comment.