mirror of
https://github.com/caddyserver/caddy.git
synced 2026-10-22 20:03:33 +00:00
Merge commit from fork
* fileserver: add failing test for calculateEtag collision calculateEtag concatenates base36(mtime) and base36(size) with no separator, so distinct (mtime, size) pairs can yield identical digit strings and thus identical ETags. * fileserver: prevent ETag collisions by separating mtime and size components calculateEtag concatenated base36(mtime) and base36(size) with no separator, so distinct (mtime, size) pairs could decode to the same digit string and produce identical ETags.
This commit is contained in:
parent
947087cadd
commit
d6f7f18b04
2 changed files with 29 additions and 0 deletions
|
|
@ -768,6 +768,7 @@ func calculateEtag(d os.FileInfo) string {
|
|||
var sb strings.Builder
|
||||
sb.WriteRune('"')
|
||||
sb.WriteString(strconv.FormatInt(mtime.UnixNano(), 36))
|
||||
sb.WriteRune('-')
|
||||
sb.WriteString(strconv.FormatInt(d.Size(), 36))
|
||||
sb.WriteRune('"')
|
||||
return sb.String()
|
||||
|
|
|
|||
|
|
@ -189,6 +189,34 @@ func check_validator_headers(modTime time.Time, expect_headers bool, t *testing.
|
|||
}
|
||||
}
|
||||
|
||||
// calculateEtag concatenates the base-36 mtime and size with no separator,
|
||||
// so distinct (mtime, size) pairs can produce the same digit string and
|
||||
// therefore the same ETag.
|
||||
func TestCalculateEtagCollision(t *testing.T) {
|
||||
fileA := fakeFileInfo{size: 75, modTime: time.Unix(2, 0)}
|
||||
fileB := fakeFileInfo{size: 3, modTime: time.Unix(72, 2)}
|
||||
|
||||
etagA := calculateEtag(fileA)
|
||||
etagB := calculateEtag(fileB)
|
||||
|
||||
if etagA == etagB {
|
||||
t.Fatalf("etag collision: distinct files (size=%d mtime=%s) and (size=%d mtime=%s) both produced ETag %s",
|
||||
fileA.size, fileA.modTime, fileB.size, fileB.modTime, etagA)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeFileInfo struct {
|
||||
size int64
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (f fakeFileInfo) Name() string { return "fake" }
|
||||
func (f fakeFileInfo) Size() int64 { return f.size }
|
||||
func (f fakeFileInfo) Mode() os.FileMode { return 0 }
|
||||
func (f fakeFileInfo) ModTime() time.Time { return f.modTime }
|
||||
func (f fakeFileInfo) IsDir() bool { return false }
|
||||
func (f fakeFileInfo) Sys() any { return nil }
|
||||
|
||||
func TestPrecompressedRangeResponse(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(root, "range.txt"), []byte("original response body"), 0o600); err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue