@@ -35,6 +35,12 @@ const (
3535 Toml = "toml"
3636)
3737
38+ // maxTextScanTokenSize bounds how large a single line the "file" parser will buffer.
39+ const maxTextScanTokenSize = 64 * 1024 * 1024
40+
41+ // maxConfigFileSize caps how large a configuration file we'll attempt to parse.
42+ const maxConfigFileSize = 64 * 1024 * 1024
43+
3844type ReplaceValue struct {
3945 value []byte
4046 valueType jsonparser.ValueType
@@ -234,6 +240,16 @@ func newTemplatableConfig(c *config.Configuration) templatableConfig {
234240func (f * ConfigurationFile ) Parse (file ufs.File ) error {
235241 //log.WithField("path", path).WithField("parser", f.Parser.String()).Debug("parsing server configuration file")
236242
243+ // Refuse to parse files larger than the cap. Every parser below buffers the
244+ // whole file in memory, and the contents are untrusted server-owned input, so
245+ // this guards the daemon against being OOM'd by an oversized config. The
246+ // server is still free to boot with the file as-is; we just don't rewrite it.
247+ if info , err := file .Stat (); err != nil {
248+ return err
249+ } else if info .Size () > maxConfigFileSize {
250+ return errors .Errorf ("parser: refusing to parse configuration file %q: size %d exceeds limit of %d bytes" , file .Name (), info .Size (), maxConfigFileSize )
251+ }
252+
237253 // What the fuck is going on here?
238254 if mb , err := json .Marshal (newTemplatableConfig (config .Get ())); err != nil {
239255 return err
@@ -265,7 +281,7 @@ func (f *ConfigurationFile) Parse(file ufs.File) error {
265281// Parses an xml file.
266282func (f * ConfigurationFile ) parseXmlFile (file ufs.File ) error {
267283 doc := etree .NewDocument ()
268- if _ , err := doc .ReadFrom (file ); err != nil {
284+ if _ , err := doc .ReadFrom (io . LimitReader ( file , maxConfigFileSize ) ); err != nil {
269285 return err
270286 }
271287
@@ -345,7 +361,7 @@ func (f *ConfigurationFile) parseXmlFile(file ufs.File) error {
345361// Parses an ini file.
346362func (f * ConfigurationFile ) parseIniFile (file ufs.File ) error {
347363 // Wrap the file in a NopCloser so the ini package doesn't close the file.
348- cfg , err := ini .Load (io .NopCloser (file ))
364+ cfg , err := ini .Load (io .NopCloser (io . LimitReader ( file , maxConfigFileSize ) ))
349365 if err != nil {
350366 return err
351367 }
@@ -429,7 +445,7 @@ func (f *ConfigurationFile) parseIniFile(file ufs.File) error {
429445// value is set regardless in the file. See the commentary in parseYamlFile for more details
430446// about what is happening during this process.
431447func (f * ConfigurationFile ) parseJsonFile (file ufs.File ) error {
432- b , err := io .ReadAll (file )
448+ b , err := io .ReadAll (io . LimitReader ( file , maxConfigFileSize ) )
433449 if err != nil {
434450 return err
435451 }
@@ -462,7 +478,7 @@ func (f *ConfigurationFile) parseJsonFile(file ufs.File) error {
462478// Parses a yaml file and updates any matching key/value pairs before persisting
463479// it back to the disk.
464480func (f * ConfigurationFile ) parseYamlFile (file ufs.File ) error {
465- b , err := io .ReadAll (file )
481+ b , err := io .ReadAll (io . LimitReader ( file , maxConfigFileSize ) )
466482 if err != nil {
467483 return err
468484 }
@@ -517,7 +533,7 @@ func (f *ConfigurationFile) parseYamlFile(file ufs.File) error {
517533// Parses a toml file and updates any matching key/value pairs before persisting
518534// it back to the disk.
519535func (f * ConfigurationFile ) parseTomlFile (file ufs.File ) error {
520- b , err := io .ReadAll (file )
536+ b , err := io .ReadAll (io . LimitReader ( file , maxConfigFileSize ) )
521537 if err != nil {
522538 return err
523539 }
@@ -640,7 +656,8 @@ func normalizeTomlTypes(value interface{}) interface{} {
640656// than this function where possible.
641657func (f * ConfigurationFile ) parseTextFile (file ufs.File ) error {
642658 b := bytes .NewBuffer (nil )
643- s := bufio .NewScanner (file )
659+ s := bufio .NewScanner (io .LimitReader (file , maxConfigFileSize ))
660+ s .Buffer (make ([]byte , 0 , 64 * 1024 ), maxTextScanTokenSize )
644661 var replaced bool
645662 for s .Scan () {
646663 line := s .Bytes ()
@@ -651,14 +668,6 @@ func (f *ConfigurationFile) parseTextFile(file ufs.File) error {
651668 if ! bytes .HasPrefix (line , []byte (replace .Match )) {
652669 continue
653670 }
654- // If an if_value is set, only replace when the remainder of the line matches.
655- // Trim trailing \r\n so Windows line endings do not break the comparison.
656- if replace .IfValue != "" {
657- remainder := bytes .TrimRight (bytes .TrimPrefix (line , []byte (replace .Match )), "\r \n " )
658- if string (remainder ) != replace .IfValue {
659- continue
660- }
661- }
662671 b .Write (replace .ReplaceWith .Bytes ())
663672 replaced = true
664673 }
@@ -667,6 +676,9 @@ func (f *ConfigurationFile) parseTextFile(file ufs.File) error {
667676 }
668677 b .WriteByte ('\n' )
669678 }
679+ if err := s .Err (); err != nil {
680+ return errors .Wrap (err , "parser: failed to scan text file for configuration update" )
681+ }
670682
671683 if _ , err := file .Seek (0 , io .SeekStart ); err != nil {
672684 return err
@@ -709,7 +721,7 @@ func (f *ConfigurationFile) parseTextFile(file ufs.File) error {
709721// @see https://github.com/pterodactyl/panel/issues/2308 (original)
710722// @see https://github.com/pterodactyl/panel/issues/3009 ("bug" introduced as result)
711723func (f * ConfigurationFile ) parsePropertiesFile (file ufs.File ) error {
712- b , err := io .ReadAll (file )
724+ b , err := io .ReadAll (io . LimitReader ( file , maxConfigFileSize ) )
713725 if err != nil {
714726 return err
715727 }
0 commit comments