mirror of
https://github.com/caddyserver/caddy.git
synced 2026-10-20 19:04:36 +00:00
map: reject malformed destination placeholders (#8074)
Some checks failed
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, aix) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, linux) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, windows) (push) Has been cancelled
Lint / lint (push) Has been cancelled
Lint / lint-1 (push) Has been cancelled
Lint / lint-2 (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
Lint / dependency-review (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Some checks failed
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, aix) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, linux) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Has been cancelled
Cross-Build / build (~1.26.0, 1.26, windows) (push) Has been cancelled
Lint / lint (push) Has been cancelled
Lint / lint-1 (push) Has been cancelled
Lint / lint-2 (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
Lint / dependency-review (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Provision previously only checked for a single opening brace at the
start of a destination, so values like {result}suffix, {result, and
{result}} passed validation. strings.Trim(dest, "{}") then silently
stored a wrong placeholder name (e.g. result}suffix), making the
intended value unavailable.
Require the destination to be exactly one placeholder: a single
opening brace at the start, a single closing brace at the end, and a
non-empty name.
Fixes #8073
This commit is contained in:
parent
ea1e94b899
commit
256df3c8ad
3 changed files with 96 additions and 3 deletions
|
|
@ -65,6 +65,51 @@ func TestMapRespondWithDefault(t *testing.T) {
|
|||
tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost unknown")
|
||||
}
|
||||
|
||||
func TestMapInvalidDestination(t *testing.T) {
|
||||
// see https://github.com/caddyserver/caddy/issues/8073
|
||||
failureCases := []struct {
|
||||
name string
|
||||
destination string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "trailing text",
|
||||
destination: "{dest-name}suffix",
|
||||
expectedError: "destination 0 must be a placeholder and only a placeholder, but got '{dest-name}suffix'",
|
||||
},
|
||||
{
|
||||
name: "missing closing brace",
|
||||
destination: "{dest-name",
|
||||
expectedError: "destination 0 must be a placeholder and only a placeholder, but got '{dest-name'",
|
||||
},
|
||||
{
|
||||
name: "double closing brace",
|
||||
destination: "{dest-name}}",
|
||||
expectedError: "destination 0 must be a placeholder and only a placeholder, but got '{dest-name}}'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range failureCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
caddytest.AssertLoadError(t, `
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
}
|
||||
|
||||
localhost:9080 {
|
||||
map {http.request.uri.path} `+tc.destination+` {
|
||||
/foo mapped
|
||||
}
|
||||
respond "{result}"
|
||||
}
|
||||
`, "caddyfile", tc.expectedError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapAsJSON(t *testing.T) {
|
||||
// arrange
|
||||
tester := caddytest.NewTester(t)
|
||||
|
|
|
|||
|
|
@ -64,10 +64,12 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
|
|||
// Provision sets up h.
|
||||
func (h *Handler) Provision(_ caddy.Context) error {
|
||||
for j, dest := range h.Destinations {
|
||||
if strings.Count(dest, "{") != 1 || !strings.HasPrefix(dest, "{") {
|
||||
return fmt.Errorf("destination must be a placeholder and only a placeholder")
|
||||
if !strings.HasPrefix(dest, "{") || !strings.HasSuffix(dest, "}") ||
|
||||
strings.Count(dest, "{") != 1 || strings.Count(dest, "}") != 1 ||
|
||||
len(dest) < 3 {
|
||||
return fmt.Errorf("destination %d must be a placeholder and only a placeholder, but got '%s'", j, dest)
|
||||
}
|
||||
h.Destinations[j] = strings.Trim(dest, "{}")
|
||||
h.Destinations[j] = dest[1 : len(dest)-1]
|
||||
}
|
||||
|
||||
for i, m := range h.Mappings {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,52 @@ func TestValidateDuplicateInputs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProvisionDestinations(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
destination string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "valid placeholder", destination: "{output}", want: "output"},
|
||||
{name: "trailing text", destination: "{output}suffix", wantErr: true},
|
||||
{name: "leading text", destination: "prefix{output}", wantErr: true},
|
||||
{name: "missing opening brace", destination: "output}", wantErr: true},
|
||||
{name: "missing closing brace", destination: "{output", wantErr: true},
|
||||
{name: "double closing brace", destination: "{output}}", wantErr: true},
|
||||
{name: "double opening brace", destination: "{{output}", wantErr: true},
|
||||
{name: "nested braces", destination: "{out{put}}", wantErr: true},
|
||||
{name: "empty braces", destination: "{}", wantErr: true},
|
||||
{name: "no braces", destination: "output", wantErr: true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := Handler{
|
||||
Source: "{http.request.uri.path}",
|
||||
Destinations: []string{tc.destination},
|
||||
Mappings: []Mapping{
|
||||
{
|
||||
Input: "/foo",
|
||||
Outputs: []any{"FOO"},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := h.Provision(caddy.Context{})
|
||||
if tc.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for destination %q, but got none (stored %q)", tc.destination, h.Destinations[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for destination %q: %v", tc.destination, err)
|
||||
}
|
||||
if h.Destinations[0] != tc.want {
|
||||
t.Fatalf("expected destination %q, got %q", tc.want, h.Destinations[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
handler Handler
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue