@@ -51,12 +51,22 @@ func allTokens(input io.Reader) ([]Token, error) {
5151
5252type parser struct {
5353 Dispenser
54- block ServerBlock // current server block being parsed
55- validDirectives []string // a directive must be valid or it's an error
56- eof bool // if we encounter a valid EOF in a hard place
57- definedSnippets map [string ][]Token
54+ block ServerBlock // current server block being parsed
55+ validDirectives []string // a directive must be valid or it's an error
56+ eof bool // if we encounter a valid EOF in a hard place
57+ definedSnippets map [string ][]Token
58+ snippetExpansions int // counts snippet imports expanded during this parse
59+ fileExpansions int // counts file/glob imports expanded during this parse
5860}
5961
62+ // maxSnippetExpansions is a hard cap to prevent excessively deep or cyclic snippet imports.
63+ // set as a variable to allow modifications for testing
64+ var maxSnippetExpansions = 10000
65+
66+ // maxFileExpansions is a hard cap to prevent excessively deep or cyclic file imports.
67+ // set as a variable to allow modifications for testing
68+ var maxFileExpansions = 100000
69+
6070func (p * parser ) parseAll () ([]ServerBlock , error ) {
6171 var blocks []ServerBlock
6272
@@ -108,6 +118,16 @@ func (p *parser) begin() error {
108118 if err != nil {
109119 return err
110120 }
121+ // minimal guard: detect trivial self-import in snippet body
122+ for i := 0 ; i + 1 < len (tokens ); i ++ {
123+ if tokens [i ].Text == "import" {
124+ // Only consider it an import directive if at start of a line
125+ atLineStart := i == 0 || tokens [i - 1 ].File != tokens [i ].File || tokens [i - 1 ].Line != tokens [i ].Line
126+ if atLineStart && replaceEnvVars (tokens [i + 1 ].Text ) == name {
127+ return p .Errf ("maximum snippet import depth (%d) exceeded" , maxSnippetExpansions )
128+ }
129+ }
130+ }
111131 p .definedSnippets [name ] = tokens
112132 // empty block keys so we don't save this block as a real server.
113133 p .block .Keys = nil
@@ -245,10 +265,18 @@ func (p *parser) doImport() error {
245265 tokensAfter := p .tokens [p .cursor + 1 :]
246266 var importedTokens []Token
247267
248- // first check snippets. That is a simple, non-recursive replacement
268+ // first check snippets. Count expansion and enforce cap.
249269 if p .definedSnippets != nil && p .definedSnippets [importPattern ] != nil {
270+ if p .snippetExpansions >= maxSnippetExpansions {
271+ return p .Errf ("maximum snippet import depth (%d) exceeded" , maxSnippetExpansions )
272+ }
273+ p .snippetExpansions ++
250274 importedTokens = p .definedSnippets [importPattern ]
251275 } else {
276+ if p .fileExpansions >= maxFileExpansions {
277+ return p .Errf ("maximum file import depth (%d) exceeded" , maxSnippetExpansions )
278+ }
279+ p .fileExpansions ++
252280 // make path relative to the file of the _token_ being processed rather
253281 // than current working directory (issue #867) and then use glob to get
254282 // list of matching filenames
0 commit comments