Skip to content

🩹 fix: Cache middleware hasDirective misses directives with trailing space/tab/=#4144

Open
mango766 wants to merge 2 commits intogofiber:mainfrom
mango766:fix/cache-hasDirective-trailing-space
Open

🩹 fix: Cache middleware hasDirective misses directives with trailing space/tab/=#4144
mango766 wants to merge 2 commits intogofiber:mainfrom
mango766:fix/cache-hasDirective-trailing-space

Conversation

@mango766
Copy link

Summary

Fixes #4143

The hasDirective function in the cache middleware only treated , and end-of-string as valid terminators after matching a directive name. This caused it to miss directives followed by:

  • Trailing space — e.g. Pragma: no-cache or Cache-Control: private (common with some HTTP servers/proxies that pad headers)
  • Tab character — e.g. no-cache\t
  • Equals sign — e.g. no-cache="Set-Cookie" (RFC 9111 §5.2.2.4 allows no-cache with field names)

This could result in private or no-cache responses being incorrectly cached and served to other users.

Changes

  • middleware/cache/cache.go: Updated hasDirective to also accept ' ', '\t', and '=' as valid directive terminators after a match
  • middleware/cache/cache_test.go: Added comprehensive Test_hasDirective unit test with 19 test cases covering all the edge cases

Before (bug)

// Only comma or end-of-string was accepted as terminator
if i+dirLen == ccLen || cc[i+dirLen] == ',' {
    return true
}

After (fix)

// Space, tab, and '=' are also valid terminators per RFC 9111
end := i + dirLen
if end == ccLen || cc[end] == ',' || cc[end] == ' ' || cc[end] == '=' || cc[end] == '\t' {
    return true
}

Test plan

  • All 19 new Test_hasDirective test cases pass
  • All existing cache middleware tests pass (go test ./middleware/cache/ -count=1)
  • No regressions in existing behavior — the fix only adds new valid terminators

…space/tab/=

The hasDirective function only checked for comma or end-of-string after a
matched directive name. This caused it to miss directives followed by a space,
tab, or '=' (for directives with arguments like no-cache="field").

For example, `Pragma: no-cache ` (with trailing whitespace) or
`Cache-Control: no-cache="Set-Cookie"` were not recognized, potentially
causing private or no-cache responses to be incorrectly cached.

The fix adds space (' '), tab ('\t'), and equals ('=') as valid directive
terminators, aligning with RFC 9111 Cache-Control syntax.

Fixes gofiber#4143

Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2026

Walkthrough

The PR fixes Cache-Control directive detection in the cache middleware by treating directives as standalone tokens, recognizing directives followed by whitespace, tabs, or = (for arguments), and expanding valid surrounding characters per RFC 9111. Tests were added to validate the behavior.

Changes

Cohort / File(s) Summary
Cache directive parsing refinement
middleware/cache/cache.go
Refined hasDirective to require a preceding start/comma/whitespace and allow trailing end/comma/whitespace/= so directives with arguments or trailing whitespace are recognized.
Directive detection test coverage
middleware/cache/cache_test.go
Added Test_hasDirective exercising exact matches, comma/tab/space-separated lists, directives with = values, case-insensitivity, and rejection of partial matches.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

📜 RFC Compliance

Suggested reviewers

  • sixcolors
  • efectn
  • ReneWerner87

Poem

🐰 I sniffed the header, found a stray space,

no-cache hid shyly in an awkward place.
Now tokens stand tidy, equals and tabs too,
I hop with a carrot — cache rules true! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the bug being fixed: hasDirective missing directives with trailing space/tab/= in Cache middleware.
Description check ✅ Passed The PR description includes a clear summary of the issue, the changes made, before/after code comparisons, and a test plan section confirming all tests pass.
Linked Issues check ✅ Passed The PR fully addresses issue #4143 by updating hasDirective to recognize space, tab, and '=' as valid terminators and adding comprehensive test coverage for all edge cases mentioned in the issue.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the hasDirective bug: modifications to the function logic and addition of corresponding test cases; no unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can approve the review once all CodeRabbit's comments are resolved.

Enable the reviews.request_changes_workflow setting to automatically approve the review once all CodeRabbit's comments are resolved.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical caching bug by enhancing the hasDirective function within the cache middleware. Previously, certain valid Cache-Control header formats could lead to incorrect caching behavior, potentially exposing private data or serving stale content. The changes ensure that directives are properly recognized, preventing misinterpretation and improving the reliability of the caching mechanism.

Highlights

  • Cache Middleware Fix: The hasDirective function in the cache middleware was updated to correctly parse Cache-Control headers, addressing an issue where directives followed by trailing spaces, tabs, or an equals sign were not recognized.
  • Enhanced Header Parsing: The parsing logic now correctly identifies directive terminators including spaces, tabs, and the equals sign, aligning with RFC 9111 specifications for no-cache and private directives.
  • Comprehensive Unit Tests: A new Test_hasDirective unit test suite was added with 19 test cases to cover various scenarios, including edge cases, ensuring the robustness of the updated parsing logic.
Changelog
  • middleware/cache/cache.go
    • Updated the hasDirective function to correctly identify directive terminators, including spaces, tabs, and the equals sign.
    • Expanded the documentation for hasDirective to clarify the new parsing rules.
  • middleware/cache/cache_test.go
    • Added a new Test_hasDirective function with 19 test cases to validate the updated hasDirective logic across various scenarios, including edge cases and RFC compliance.
Activity
  • Resolved issue 🐛 [Bug]: Cache middleware hasDirective misses directives followed by space, tab, or '=' #4143 related to incorrect hasDirective parsing.
  • Implemented changes to middleware/cache/cache.go to broaden directive termination recognition.
  • Introduced a comprehensive Test_hasDirective in middleware/cache/cache_test.go with 19 new test cases.
  • Verified that all new Test_hasDirective test cases pass.
  • Confirmed that all existing cache middleware tests pass without regressions.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ReneWerner87 ReneWerner87 added this to v3 Mar 16, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Mar 16, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly fixes an issue in the hasDirective function where it failed to recognize certain valid terminators for cache directives, such as trailing spaces, tabs, or equals signs. This is a crucial fix to prevent incorrect caching behavior. The addition of a comprehensive unit test suite for hasDirective is also a significant improvement. I've identified a related edge case where a tab character preceding a directive is not handled correctly, and I've suggested an additional test case to highlight this issue.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
middleware/cache/cache.go (1)

920-923: ⚠️ Potential issue | 🟡 Minor

Allow HTAB as a valid preceding boundary for directives.

hasDirective still rejects directives preceded by a tab, so valid inputs like "public,\tno-cache" or "\tno-cache" are missed (Line 922). That keeps a false-negative path for cache-control detection.

🐛 Proposed fix
 		if i > 0 {
 			prev := cc[i-1]
-			if prev != ' ' && prev != ',' {
+			if prev != ' ' && prev != '\t' && prev != ',' {
 				continue
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/cache/cache.go` around lines 920 - 923, In hasDirective
(middleware/cache/cache.go) the check that skips matches when the preceding
character isn't a space or comma erroneously rejects HTAB; update the
preceding-boundary check around variable cc and prev in that loop to treat '\t'
as a valid boundary as well (i.e., allow prev == '\t' in addition to ' ' and
',') so directives like "\tno-cache" or "public,\tno-cache" are accepted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@middleware/cache/cache.go`:
- Around line 920-923: In hasDirective (middleware/cache/cache.go) the check
that skips matches when the preceding character isn't a space or comma
erroneously rejects HTAB; update the preceding-boundary check around variable cc
and prev in that loop to treat '\t' as a valid boundary as well (i.e., allow
prev == '\t' in addition to ' ' and ',') so directives like "\tno-cache" or
"public,\tno-cache" are accepted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fa26c701-10ea-46b8-b5de-6e7fa4174bb2

📥 Commits

Reviewing files that changed from the base of the PR and between 83b0069 and 2b0017d.

📒 Files selected for processing (2)
  • middleware/cache/cache.go
  • middleware/cache/cache_test.go

@gaby gaby changed the title 🩹 Fix: cache middleware hasDirective misses directives with trailing space/tab/= 🩹 fix: Cache middleware hasDirective misses directives with trailing space/tab/= Mar 16, 2026
@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.12%. Comparing base (83b0069) to head (2b0017d).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4144      +/-   ##
==========================================
+ Coverage   91.07%   91.12%   +0.05%     
==========================================
  Files         122      122              
  Lines       11550    11551       +1     
==========================================
+ Hits        10519    10526       +7     
+ Misses        651      647       -4     
+ Partials      380      378       -2     
Flag Coverage Δ
unittests 91.12% <100.00%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gaby
Copy link
Member

gaby commented Mar 17, 2026

@mango766 check the review comments

@ReneWerner87
Copy link
Member

@mango766 pls add the tab case and then its ready for a merge

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@middleware/cache/cache_test.go`:
- Line 5115: The test fails because hasDirective only treats space and comma as
valid preceding characters; modify the preceding-character check inside
hasDirective to also accept tab ('\t') so strings like "public,\tno-cache"
match; locate the condition that currently reads "if prev != ' ' && prev != ','
{ continue }" and add '\t' to that allowed set (or equivalently accept
unicode.IsSpace(prev)), then run tests to confirm the "tab before directive"
case passes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 51216787-c0ae-4063-86d2-dc72cce1c0b8

📥 Commits

Reviewing files that changed from the base of the PR and between 2b0017d and ad54cd8.

📒 Files selected for processing (1)
  • middleware/cache/cache_test.go

{name: "last in list", cc: "public, no-cache", directive: "no-cache", expected: true},
{name: "middle in list", cc: "public, no-cache, no-store", directive: "no-cache", expected: true},
{name: "no spaces", cc: "public,no-cache,no-store", directive: "no-cache", expected: true},
{name: "tab before directive", cc: "public,\tno-cache", directive: "no-cache", expected: true},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if the implementation supports tab as a preceding character
rg -n "prev != " middleware/cache/cache.go -A2 -B2

Repository: gofiber/fiber

Length of output: 161


🏁 Script executed:

# Get the complete hasDirective function
rg -n "func.*hasDirective" middleware/cache/cache.go -A 50

Repository: gofiber/fiber

Length of output: 1377


🏁 Script executed:

# Get the complete test case context around line 5115
sed -n '5110,5120p' middleware/cache/cache_test.go

Repository: gofiber/fiber

Length of output: 859


🏁 Script executed:

# Check if there are any updates to the preceding character validation logic
rg -n "prev.*==" middleware/cache/cache.go

Repository: gofiber/fiber

Length of output: 39


Test case "tab before directive" will fail against the current implementation.

The hasDirective implementation (line 922) only allows space ' ' or comma ',' as valid preceding characters:

if prev != ' ' && prev != ',' {
    continue
}

For the test case cc: "public,\tno-cache" with directive: "no-cache", the tab '\t' at position 7 (before the directive) will not match this condition, causing the function to return false instead of the expected true.

While the implementation does support tab as a following character (line 927: cc[end] == '\t'), tabs are not accepted as preceding characters. Either:

  1. Add '\t' to the preceding character check (line 922) to accept tabs before directives, or
  2. Update the test expectation to false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/cache/cache_test.go` at line 5115, The test fails because
hasDirective only treats space and comma as valid preceding characters; modify
the preceding-character check inside hasDirective to also accept tab ('\t') so
strings like "public,\tno-cache" match; locate the condition that currently
reads "if prev != ' ' && prev != ',' { continue }" and add '\t' to that allowed
set (or equivalently accept unicode.IsSpace(prev)), then run tests to confirm
the "tab before directive" case passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: Cache middleware hasDirective misses directives followed by space, tab, or '='

3 participants