JakeChampion / JakeChampion/trafficserver

[audit][standards] Qualified private="field"/no-cache="field" directives are discarded entirely, allowing shared-cache storage of private responses

Open
#36 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:standards audit severity:high
Dominant language
C++
Stars
0
Forks
0
Avg merge
8h 2m
Merged PRs (30d)
21

Description

Severity: high · Category: standards-gap
Location: src/proxy/hdrs/MIME.cc:3833

What's wrong

In MIMEHdrImpl::recompute_cooked_stuff, after a well-known Cache-Control token is recognized and its mask bit set (line 3765), any trailing non-whitespace content causes the just-set mask bits to be cleared (lines 3833-3841). This 'ignore unrecognized directive' logic (added for issue #12029, semicolon separators) also fires for the RFC-valid qualified forms 'private="Set-Cookie"' and 'no-cache="Set-Cookie"': the token parses as private/no-cache, then c points at '=', which is non-whitespace, so csv_value_mask is reverted. RFC 9111 Section 5.2.2.7 says a shared cache that does not implement the field-name form of private MUST NOT store the response, and Section 5.2.2.4 says the same for no-cache (must not be used without revalidation). ATS instead drops the directive completely: response_cacheable_indicated_by_cc (src/proxy/http/HttpTransact.cc:6561-6563) then sees no CC_PRIVATE bit, so 'Cache-Control: private="Set-Cookie", max-age=600' is judged cacheable (+1 via max-age) and a per-user response can be cached and served to other users. Only the exact string 'private,no-cache' is special-cased on the fastpath (line 3738). There is no unit-test coverage for the qualified forms in src/proxy/hdrs/unit_tests/test_HdrUtils.cc.

Evidence
src/proxy/hdrs/MIME.cc:3830-3841:
            while (c < e && ParseRules::is_ws(*c)) { ++c; }
            if (c < e) {
              // "A cache MUST ignore unrecognized cache directives." (RFC 7234 5.2)
              if (csv_value_mask != 0) {
                m_cooked_stuff.m_cache_control.m_mask &= ~csv_value_mask;
              }
            }
src/proxy/http/HttpTransact.cc:6561-6563:
  cc_mask = MIME_COOKED_MASK_CC_PRIVATE | (ignore_no_store_and_no_cache_directives ? 0 : MIME_COOKED_MASK_CC_NO_STORE);
  if (response->get_cooked_cc_mask() & cc_mask) { return -1;
Suggested fix

In the trailing-content check, special-case directives whose qualified (field-name argument) form is defined by RFC 9111 — private and no-cache — and fall back to treating them as unqualified (keep the mask bit set) instead of clearing it. Add unit tests for 'Cache-Control: private="Set-Cookie"' and 'no-cache="Set-Cookie"' asserting CC_PRIVATE/CC_NO_CACHE remain set.


Filed from an automated multi-lens codebase audit. Full report: CODEBASE_AUDIT.md / audit-report.html on branch claude/codebase-audit-review-9nw7vz.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/proxy/hdrs/MIME.cc at MIMEHdrImpl::recompute_cooked_stuff and review the trailing-content handling around lines 3833-3841, then inspect response_cacheable_indicated_by_cc in src/proxy/http/HttpTransact.cc. Run the relevant tests in src/proxy/hdrs/unit_tests/test_HdrUtils.cc. Done means qualified private and no-cache directives retain their expected mask bits and the new cases pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.