Skip to content

Commit 0839b54

Browse files
committed
rewrite: Add option to force modifying the query
1 parent 2182270 commit 0839b54

File tree

4 files changed

+58
-6
lines changed

4 files changed

+58
-6
lines changed

caddytest/integration/caddyfile_adapt/reverse_proxy_handle_response.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ reverse_proxy 127.0.0.1:65535 {
1111

1212
@accel header X-Accel-Redirect *
1313
handle_response @accel {
14-
respond "Header X-Accel-Redirect!"
14+
rewrite * {rp.header.X-Accel-Redirect} {
15+
modify_query
16+
}
1517
}
1618

1719
@another {
@@ -104,10 +106,12 @@ reverse_proxy 127.0.0.1:65535 {
104106
},
105107
"routes": [
106108
{
109+
"group": "group0",
107110
"handle": [
108111
{
109-
"body": "Header X-Accel-Redirect!",
110-
"handler": "static_response"
112+
"handler": "rewrite",
113+
"modify_query": true,
114+
"uri": "{http.reverse_proxy.header.X-Accel-Redirect}"
111115
}
112116
]
113117
}

modules/caddyhttp/rewrite/caddyfile.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ func init() {
3434

3535
// parseCaddyfileRewrite sets up a basic rewrite handler from Caddyfile tokens. Syntax:
3636
//
37-
// rewrite [<matcher>] <to>
37+
// rewrite [<matcher>] <to> {
38+
// modify_query
39+
// }
3840
//
3941
// Only URI components which are given in <to> will be set in the resulting URI.
4042
// See the docs for the rewrite handler for more information.
@@ -48,6 +50,16 @@ func parseCaddyfileRewrite(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler,
4850
if h.NextArg() {
4951
return nil, h.ArgErr()
5052
}
53+
54+
for nesting := h.Nesting(); h.NextBlock(nesting); {
55+
switch h.Val() {
56+
case "modify_query":
57+
rewr.ModifyQuery = true
58+
59+
default:
60+
return nil, h.Errf("unknown subdirective: %s", h.Val())
61+
}
62+
}
5163
}
5264
return rewr, nil
5365
}

modules/caddyhttp/rewrite/rewrite.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,17 @@ type Rewrite struct {
8888
// Performs regular expression replacements on the URI path.
8989
PathRegexp []*regexReplacer `json:"path_regexp,omitempty"`
9090

91+
// If true, the rewrite will be forced to also apply to the
92+
// query part of the URL. This is only needed if the configured
93+
// URI does not include a '?' character which is normally used
94+
// to determine whether the query should be modified. In other
95+
// words, this allows rewriting both the path and query when
96+
// using a placeholder as the replacement value, whereas otherwise
97+
// only the path would be rewritten because the placeholder itself
98+
// does not contain a '?' character. Only use this if the placeholder
99+
// is trusted to not be vulnerable to query injections.
100+
ModifyQuery bool `json:"modify_query,omitempty"`
101+
91102
logger *zap.Logger
92103
}
93104

@@ -216,10 +227,15 @@ func (rewr Rewrite) Rewrite(r *http.Request, repl *caddy.Replacer) bool {
216227
// recompute; new path contains a query string
217228
var injectedQuery string
218229
newPath, injectedQuery = before, after
219-
// don't overwrite explicitly-configured query string
220-
if query == "" {
230+
231+
// don't overwrite explicitly-configured query string,
232+
// unless configured explicitly to do so
233+
if query == "" || rewr.ModifyQuery {
221234
query = injectedQuery
222235
}
236+
if rewr.ModifyQuery {
237+
qsStart = 0
238+
}
223239
}
224240

225241
if query != "" {

modules/caddyhttp/rewrite/rewrite_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,23 @@ func TestRewrite(t *testing.T) {
214214
input: newRequest(t, "GET", "/foo#fragFirst?c=d"),
215215
expect: newRequest(t, "GET", "/bar#fragFirst?c=d"),
216216
},
217+
{
218+
rule: Rewrite{URI: "{test.path_and_query}"},
219+
input: newRequest(t, "GET", "/"),
220+
expect: newRequest(t, "GET", "/foo"),
221+
},
222+
{
223+
// TODO: This might be an incorrect result, since it also replaces
224+
// the path with empty string when that might not be the intent.
225+
rule: Rewrite{URI: "{test.query}", ModifyQuery: true},
226+
input: newRequest(t, "GET", "/foo"),
227+
expect: newRequest(t, "GET", "?bar=1"),
228+
},
229+
{
230+
rule: Rewrite{URI: "{test.path_and_query}", ModifyQuery: true},
231+
input: newRequest(t, "GET", "/"),
232+
expect: newRequest(t, "GET", "/foo?bar=1"),
233+
},
217234

218235
{
219236
rule: Rewrite{StripPathPrefix: "/prefix"},
@@ -343,6 +360,9 @@ func TestRewrite(t *testing.T) {
343360
repl.Set("http.request.uri", tc.input.RequestURI)
344361
repl.Set("http.request.uri.path", tc.input.URL.Path)
345362
repl.Set("http.request.uri.query", tc.input.URL.RawQuery)
363+
repl.Set("test.path", "/foo")
364+
repl.Set("test.query", "?bar=1")
365+
repl.Set("test.path_and_query", "/foo?bar=1")
346366

347367
// we can't directly call Provision() without a valid caddy.Context
348368
// (TODO: fix that) so here we ad-hoc compile the regex

0 commit comments

Comments
 (0)