Skip to content

Commit 01b6574

Browse files
committed
🐛 fix(cache): detect directives with OWS and value params
1 parent 4acf465 commit 01b6574

File tree

2 files changed

+61
-12
lines changed

2 files changed

+61
-12
lines changed

middleware/cache/cache.go

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -907,21 +907,41 @@ func New(config ...Config) fiber.Handler {
907907

908908
// hasDirective checks if a cache-control header contains a directive (case-insensitive)
909909
func hasDirective(cc, directive string) bool {
910-
ccLen := len(cc)
911-
dirLen := len(directive)
912-
for i := 0; i <= ccLen-dirLen; i++ {
913-
if !utils.EqualFold(cc[i:i+dirLen], directive) {
914-
continue
910+
start := 0
911+
for start < len(cc) {
912+
end := start
913+
for end < len(cc) && cc[end] != ',' {
914+
end++
915915
}
916-
if i > 0 {
917-
prev := cc[i-1]
918-
if prev != ' ' && prev != ',' {
919-
continue
920-
}
916+
917+
partStart, partEnd := start, end
918+
for partStart < partEnd && (cc[partStart] == ' ' || cc[partStart] == '\t') {
919+
partStart++
921920
}
922-
if i+dirLen == ccLen || cc[i+dirLen] == ',' {
923-
return true
921+
for partEnd > partStart && (cc[partEnd-1] == ' ' || cc[partEnd-1] == '\t') {
922+
partEnd--
924923
}
924+
925+
if partStart < partEnd {
926+
keyEnd := partStart
927+
for keyEnd < partEnd && cc[keyEnd] != '=' {
928+
keyEnd++
929+
}
930+
931+
keyStart := partStart
932+
for keyStart < keyEnd && (cc[keyStart] == ' ' || cc[keyStart] == '\t') {
933+
keyStart++
934+
}
935+
for keyEnd > keyStart && (cc[keyEnd-1] == ' ' || cc[keyEnd-1] == '\t') {
936+
keyEnd--
937+
}
938+
939+
if keyStart < keyEnd && utils.EqualFold(cc[keyStart:keyEnd], directive) {
940+
return true
941+
}
942+
}
943+
944+
start = end + 1
925945
}
926946

927947
return false

middleware/cache/cache_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3046,6 +3046,35 @@ func Test_AllowsSharedCache(t *testing.T) {
30463046
})
30473047
}
30483048

3049+
func Test_HasDirective(t *testing.T) {
3050+
t.Parallel()
3051+
3052+
tests := []struct {
3053+
header string
3054+
directive string
3055+
expect bool
3056+
}{
3057+
{header: "no-cache", directive: "no-cache", expect: true},
3058+
{header: "NO-CACHE", directive: "no-cache", expect: true},
3059+
{header: "no-cache ", directive: "no-cache", expect: true},
3060+
{header: "no-cache\t", directive: "no-cache", expect: true},
3061+
{header: "no-cache=\"Set-Cookie\"", directive: "no-cache", expect: true},
3062+
{header: "private ", directive: "private", expect: true},
3063+
{header: "public, private ", directive: "private", expect: true},
3064+
{header: "my-no-cache", directive: "no-cache", expect: false},
3065+
{header: "private-ish", directive: "private", expect: false},
3066+
}
3067+
3068+
for _, tt := range tests {
3069+
tt := tt
3070+
t.Run(tt.header+"_"+tt.directive, func(t *testing.T) {
3071+
t.Parallel()
3072+
got := hasDirective(tt.header, tt.directive)
3073+
require.Equal(t, tt.expect, got)
3074+
})
3075+
}
3076+
}
3077+
30493078
func TestCacheSkipsAuthorizationByDefault(t *testing.T) {
30503079
t.Parallel()
30513080

0 commit comments

Comments
 (0)