mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-12 11:24:08 +00:00
fix(http): decode HTML entities in filenames in one pass (#1651)
unescapeHTMLEntities applied its replacements while ranging over a map, so the order changed from run to run. An escaped ampersand such as "a&lt;b.txt" came out as "a<b.txt" or, when & was replaced first, as the double-decoded "a<b.txt". Replace all entities in a single pass with strings.NewReplacer and cover the escaped-ampersand cases in TestUnescapeHTMLEntities, running each case repeatedly so an order-dependent implementation fails.
This commit is contained in:
1 parent
89dfbe4c45
commit
6cd95507fd
2 files changed
+28
-18
No files matched your search
@@ -125,13 +125,26 @@ func TestUnescapeHTMLEntities(t *testing.T) {
|
||||
in: "normal.txt",
|
||||
want: "normal.txt",
|
||||
},
|
||||
{
|
||||
name: "escaped ampersand before entity name is decoded once",
|
||||
in: "a&lt;b.txt",
|
||||
want: "a<b.txt",
|
||||
},
|
||||
{
|
||||
name: "escaped ampersand before quote entity is decoded once",
|
||||
in: "a&quot;b.txt",
|
||||
want: "a"b.txt",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := unescapeHTMLEntities(tt.in)
|
||||
if got != tt.want {
|
||||
t.Errorf("unescapeHTMLEntities() = %q, want %q", got, tt.want)
|
||||
// Run repeatedly so that an order-dependent implementation fails reliably.
|
||||
for i := 0; i < 200; i++ {
|
||||
got := unescapeHTMLEntities(tt.in)
|
||||
if got != tt.want {
|
||||
t.Fatalf("unescapeHTMLEntities() = %q, want %q (run %d)", got, tt.want, i)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -408,24 +408,21 @@ func decodeFilenameParam(filename string) string {
|
||||
return decoded
|
||||
}
|
||||
|
||||
// htmlEntityReplacer replaces every entity in a single left-to-right pass, so an
|
||||
// escaped ampersand such as "&lt;" becomes "<" and is not decoded twice.
|
||||
var htmlEntityReplacer = strings.NewReplacer(
|
||||
"&", "&",
|
||||
"<", "<",
|
||||
">", ">",
|
||||
""", "\"",
|
||||
"'", "'",
|
||||
"'", "'",
|
||||
)
|
||||
|
||||
// unescapeHTMLEntities unescapes common HTML entities in filenames
|
||||
// This handles cases where servers HTML-encode special characters like & to &
|
||||
func unescapeHTMLEntities(s string) string {
|
||||
// Common HTML entities that might appear in filenames
|
||||
replacements := map[string]string{
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
""": "\"",
|
||||
"'": "'",
|
||||
"'": "'",
|
||||
}
|
||||
|
||||
result := s
|
||||
for entity, char := range replacements {
|
||||
result = strings.ReplaceAll(result, entity, char)
|
||||
}
|
||||
return result
|
||||
return htmlEntityReplacer.Replace(s)
|
||||
}
|
||||
|
||||
// tryDecodeGBK attempts to decode string as GBK encoding
|
||||
|
||||
Reference in new issue
Block a user