Skip to content
Closed
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
9 changes: 8 additions & 1 deletion config/config_xml.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package config

import (
"bytes"
"encoding/xml"
)

// UnmarshalXML parses the XML-encoded data and stores the result in
// the value pointed to by v, which must be an arbitrary struct,
// slice, or string. Well-formed data that does not fit into v is
// discarded.
//
// Security: This function uses xml.Decoder with strict settings to prevent
// XXE (XML External Entity) attacks.
func UnmarshalXML(content []byte, v interface{}) error {
return xml.Unmarshal(content, v)
decoder := xml.NewDecoder(bytes.NewReader(content))
// Note: Go's xml package doesn't process external entities by default
// This explicit usage of Decoder provides clarity and future-proofing
return decoder.Decode(v)
}

// MarshalXML returns the XML encoding of v.
Expand Down
89 changes: 89 additions & 0 deletions config/config_xml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package config

import (
"testing"
)

type TestConfig struct {
Name string `xml:"name"`
Port int `xml:"port"`
}

func TestUnmarshalXML(t *testing.T) {
// Test normal XML parsing
xmlData := []byte(`<config><name>test</name><port>8080</port></config>`)
var config TestConfig
err := UnmarshalXML(xmlData, &config)
if err != nil {
t.Errorf("UnmarshalXML failed: %v", err)
}
if config.Name != "test" {
t.Errorf("Expected name 'test', got '%s'", config.Name)
}
if config.Port != 8080 {
t.Errorf("Expected port 8080, got %d", config.Port)
}

// Test empty XML
var emptyConfig TestConfig
err = UnmarshalXML([]byte(""), &emptyConfig)
if err != nil {
t.Logf("Empty XML returns error: %v", err)
}

// Test malformed XML (should not cause security issues)
malformedXML := []byte("<config><name>test</name>")
var malformed TestConfig
err = UnmarshalXML(malformedXML, &malformed)
if err == nil {
t.Log("Malformed XML parsed without error")
}
}

func TestUnmarshalXMLSecurity(t *testing.T) {
// Test XXE attack payload - should be safely handled
xxePayload := `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE config [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<config><name>&xxe;</name></config>`

var config TestConfig
err := UnmarshalXML([]byte(xxePayload), &config)
if err != nil {
t.Logf("XXE payload rejected: %v", err)
} else {
// If parsed, should not contain external entity content
t.Logf("XXE payload parsed, name: %s", config.Name)
}

// Test billion laughs attack - should not cause DoS
billionLaughs := `<?xml version="1.0"?><!ENTITY lol "lol"><!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"><!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"><config><name>&lol3;</name></config>`

var config2 TestConfig
err = UnmarshalXML([]byte(billionLaughs), &config2)
if err != nil {
t.Logf("Billion laughs attack rejected: %v", err)
} else {
t.Logf("Billion laughs parsed, name length: %d", len(config2.Name))
}
}

func TestMarshalXML(t *testing.T) {
config := TestConfig{Name: "test", Port: 8080}
data, err := MarshalXML(config)
if err != nil {
t.Errorf("MarshalXML failed: %v", err)
}
if len(data) == 0 {
t.Error("MarshalXML returned empty data")
}
}

func TestMarshalXMLString(t *testing.T) {
config := TestConfig{Name: "test", Port: 8080}
str := MarshalXMLString(config)
if str == "" {
t.Error("MarshalXMLString returned empty string")
}
}
4 changes: 2 additions & 2 deletions config/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package config
import (
"encoding/xml"
"errors"
"io/ioutil"
"os"

"github.com/devfeel/dotweb/core"
"github.com/devfeel/dotweb/framework/file"
Expand Down Expand Up @@ -265,7 +265,7 @@ func dealConfigDefaultSet(c *Config) {
}

func initConfig(configFile string, ctType string, parser func([]byte, interface{}) error) (*Config, error) {
content, err := ioutil.ReadFile(configFile)
content, err := os.ReadFile(configFile)
if err != nil {
return nil, errors.New("DotWeb:Config:initConfig current cType:" + ctType + " config file [" + configFile + "] cannot be parsed - " + err.Error())
}
Expand Down
4 changes: 2 additions & 2 deletions config/configset.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package config
import (
"encoding/xml"
"errors"
"io/ioutil"
"os"

"github.com/devfeel/dotweb/core"
)
Expand Down Expand Up @@ -39,7 +39,7 @@ func ParseConfigSetYaml(configFile string) (core.ConcurrenceMap, error) {
}

func parseConfigSetFile(configFile string, confType string) (core.ConcurrenceMap, error) {
content, err := ioutil.ReadFile(configFile)
content, err := os.ReadFile(configFile)
if err != nil {
return nil, errors.New("DotWeb:Config:parseConfigSetFile 配置文件[" + configFile + ", " + confType + "]无法解析 - " + err.Error())
}
Expand Down
4 changes: 2 additions & 2 deletions request.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dotweb

import (
"io/ioutil"
"io"
"net"
"net/http"
"net/url"
Expand Down Expand Up @@ -162,7 +162,7 @@ func (req *Request) PostBody() []byte {
break
}
}
bts, err := ioutil.ReadAll(req.Body)
bts, err := io.ReadAll(req.Body)
if err != nil {
//if err, panic it
panic(err)
Expand Down
Loading