Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
33 changes: 28 additions & 5 deletions caddyfile/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,17 @@ func allTokens(input io.Reader) ([]Token, error) {

type parser struct {
Dispenser
block ServerBlock // current server block being parsed
validDirectives []string // a directive must be valid or it's an error
eof bool // if we encounter a valid EOF in a hard place
definedSnippets map[string][]Token
block ServerBlock // current server block being parsed
validDirectives []string // a directive must be valid or it's an error
eof bool // if we encounter a valid EOF in a hard place
definedSnippets map[string][]Token
snippetExpansions int // counts snippet imports expanded during this parse
fileExpansions int // counts file/glob imports expanded during this parse
}

// maxSnippetExpansions is a hard cap to prevent excessively deep or cyclic snippet imports.
const maxSnippetExpansions = 1000

func (p *parser) parseAll() ([]ServerBlock, error) {
var blocks []ServerBlock

Expand Down Expand Up @@ -108,6 +113,16 @@ func (p *parser) begin() error {
if err != nil {
return err
}
// minimal guard: detect trivial self-import in snippet body
for i := 0; i+1 < len(tokens); i++ {
if tokens[i].Text == "import" {
// Only consider it an import directive if at start of a line
atLineStart := i == 0 || tokens[i-1].File != tokens[i].File || tokens[i-1].Line != tokens[i].Line
if atLineStart && replaceEnvVars(tokens[i+1].Text) == name {
return p.Errf("maximum snippet import depth (%d) exceeded", maxSnippetExpansions)
}
}
}
p.definedSnippets[name] = tokens
// empty block keys so we don't save this block as a real server.
p.block.Keys = nil
Expand Down Expand Up @@ -245,10 +260,18 @@ func (p *parser) doImport() error {
tokensAfter := p.tokens[p.cursor+1:]
var importedTokens []Token

// first check snippets. That is a simple, non-recursive replacement
// first check snippets. Count expansion and enforce cap.
if p.definedSnippets != nil && p.definedSnippets[importPattern] != nil {
if p.snippetExpansions >= maxSnippetExpansions {
return p.Errf("maximum snippet import depth (%d) exceeded", maxSnippetExpansions)
}
p.snippetExpansions++
importedTokens = p.definedSnippets[importPattern]
} else {
if p.fileExpansions >= maxSnippetExpansions {
return p.Errf("maximum file import depth (%d) exceeded", maxSnippetExpansions)
}
p.fileExpansions++
// make path relative to the file of the _token_ being processed rather
// than current working directory (issue #867) and then use glob to get
// list of matching filenames
Expand Down
57 changes: 57 additions & 0 deletions caddyfile/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,3 +744,60 @@ func TestSnippetAcrossMultipleFiles(t *testing.T) {
t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual)
}
}

func TestSnippetImportCycle(t *testing.T) {
tests := []struct {
name string
input string
}{
{
name: "direct self-import",
input: `
(loop) {
import loop
}
import loop
`,
},
{
name: "self-import via recursion",
input: `
(loop) {
{ }
import loop
}
import loop
`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := testParser(tt.input)
_, err := p.parseAll()
if err == nil {
t.Fatalf("expected error for snippet self-import cycle, got nil")
}
})
}
}

func TestFileImportCycleError(t *testing.T) {
fileA := writeStringToTempFileOrDie(t, "")
defer os.Remove(fileA)
fileB := writeStringToTempFileOrDie(t, "")
defer os.Remove(fileB)

if err := ioutil.WriteFile(fileA, []byte("import "+fileB), 0644); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(fileB, []byte("import "+fileA), 0644); err != nil {
t.Fatal(err)
}

p := testParser("import " + fileA)
_, err := p.parseAll()
if err == nil {
t.Fatalf("expected error for file import cycle, got nil")
}
}