From b47af6ef04f818f41932e4152836d43a9be8fe07 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Mon, 15 Nov 2021 14:41:19 -0700 Subject: [PATCH] caddyfile: Copy input before parsing (fix #4422) --- caddyconfig/caddyfile/formatter_test.go | 5 ++++ caddyconfig/caddyfile/parse.go | 39 +++++++++++++++---------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/caddyconfig/caddyfile/formatter_test.go b/caddyconfig/caddyfile/formatter_test.go index 64cf6d21..8e5b3686 100644 --- a/caddyconfig/caddyfile/formatter_test.go +++ b/caddyconfig/caddyfile/formatter_test.go @@ -179,6 +179,11 @@ d { {$F} }`, }, + { + description: "env var placeholders with port", + input: `:{$PORT}`, + expect: `:{$PORT}`, + }, { description: "comments", input: `#a "\n" diff --git a/caddyconfig/caddyfile/parse.go b/caddyconfig/caddyfile/parse.go index 8591759b..1e694f9f 100755 --- a/caddyconfig/caddyfile/parse.go +++ b/caddyconfig/caddyfile/parse.go @@ -37,7 +37,13 @@ import ( // Environment variables in {$ENVIRONMENT_VARIABLE} notation // will be replaced before parsing begins. func Parse(filename string, input []byte) ([]ServerBlock, error) { - tokens, err := allTokens(filename, input) + // unfortunately, we must copy the input because parsing must + // remain a read-only operation, but we have to expand environment + // variables before we parse, which changes the underlying array (#4422) + inputCopy := make([]byte, len(input)) + copy(inputCopy, input) + + tokens, err := allTokens(filename, inputCopy) if err != nil { return nil, err } @@ -51,7 +57,23 @@ func Parse(filename string, input []byte) ([]ServerBlock, error) { return p.parseAll() } +// allTokens lexes the entire input, but does not parse it. +// It returns all the tokens from the input, unstructured +// and in order. It may mutate input as it expands env vars. +func allTokens(filename string, input []byte) ([]Token, error) { + inputCopy, err := replaceEnvVars(input) + if err != nil { + return nil, err + } + tokens, err := Tokenize(inputCopy, filename) + if err != nil { + return nil, err + } + return tokens, nil +} + // replaceEnvVars replaces all occurrences of environment variables. +// It mutates the underlying array and returns the updated slice. func replaceEnvVars(input []byte) ([]byte, error) { var offset int for { @@ -96,21 +118,6 @@ func replaceEnvVars(input []byte) ([]byte, error) { return input, nil } -// allTokens lexes the entire input, but does not parse it. -// It returns all the tokens from the input, unstructured -// and in order. -func allTokens(filename string, input []byte) ([]Token, error) { - input, err := replaceEnvVars(input) - if err != nil { - return nil, err - } - tokens, err := Tokenize(input, filename) - if err != nil { - return nil, err - } - return tokens, nil -} - type parser struct { *Dispenser block ServerBlock // current server block being parsed