Skip to content

fix(telemetry): scope consent cache by auth token#751

Open
rexxars wants to merge 5 commits intomainfrom
rexxars/telemetry-consent-scope
Open

fix(telemetry): scope consent cache by auth token#751
rexxars wants to merge 5 commits intomainfrom
rexxars/telemetry-consent-scope

Conversation

@rexxars
Copy link
Member

@rexxars rexxars commented Mar 20, 2026

Scope the telemetry consent cache key to the current auth token, ensuring each user gets an isolated cache entry. This prevents one user from inheriting another user's cached consent status when tokens change due to login/logout.

The cache now uses token-scoped keys like 'telemetryConsent:a1b2c3d4e5f6' (token hash suffix), eliminating the race condition where detached flush workers could write consent for the wrong user.

Includes comprehensive tests verifying cache isolation between different tokens and correct behavior when logged out.

Prevent users from inheriting each other's cached telemetry consent by
scoping the cache key to a hash of the current auth token. This ensures
that switching users (via login/logout) always results in a cache miss,
eliminating the race condition where a detached flush worker could write
consent for the wrong user.

The cache now uses keys like 'telemetryConsent:a1b2c3d4e5f6' (with the
token hash as a suffix), so each user has an isolated cache entry.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Mar 20, 2026

Claude finished @rexxars's task in 1m 5s —— View job


Review

  • Read changed files
  • Analyze diff
  • Post review

Cache entry accumulation (telemetry.ts:80–87)

Each unique token creates a new config store entry under telemetryConsent:<hash>. Old entries are never cleaned up, so the store accumulates stale entries as tokens rotate (login/logout cycles). For a CLI this is minor but worth being aware of — configstore writes a JSON file to disk on each set, so this gradually grows. Consider pruning entries with the telemetryConsent: prefix when a new scoped key is written, or documenting this as a known trade-off.

Deduplication comment is slightly inaccurate (telemetry.ts:101–104)

The comment says deduplication "does not work across concurrent calls to fetchTelemetryConsent()." More precisely: the createExpiringConfig instance (and its currentFetch closure) is thrown away after each call, so concurrent callers each create their own instance and can't share the in-flight request. The comment describes the symptom correctly but the root cause is worth being explicit about if someone reads it later. This is pre-existing though, not introduced by this PR.

getTelemetryConsentCacheKey export

The function is exported for testing, but callers outside the module have no legitimate use for it — they'd need to replicate getCliToken() logic to use it correctly. If test-only, consider keeping it internal and using a different test strategy (e.g., inspecting the store keys via the clear/get interface already on testConfigStore). This is minor since the test file imports it directly, but it widens the public API surface unnecessarily.

No bugs, security issues, or logic errors found.

rexxars added 2 commits March 20, 2026 10:32
Add a clear() method to the in-memory config store and use it in beforeEach
to completely clear the cache between tests. This prevents cache pollution
from tests that use different tokens (token-a, token-b) from affecting
subsequent tests.

Previously, only the base key and test-token key were cleared, leaving
entries from other tokens in the store. While tests still passed due to
unique cache keys per token, this was brittle.
Remove mockGetCliToken.mockResolvedValue('test-token') from the first two
tests since it's already set in beforeEach. Tests that need different tokens
(token-a, token-b, undefined) still set them explicitly.
@rexxars rexxars marked this pull request as ready for review March 20, 2026 17:37
@rexxars rexxars requested a review from a team as a code owner March 20, 2026 17:37
@rexxars rexxars requested review from binoy14 and removed request for a team March 20, 2026 17:37
@github-actions
Copy link

github-actions bot commented Mar 20, 2026

Coverage Delta

File Statements
packages/@sanity/cli/src/services/telemetry.ts 78.3% (+ 7.7%)

Comparing 1 changed file against main @ 7af617a3f19983756f93edca8f939e19bdbb4d2b

Overall Coverage

Metric Coverage
Statements 83.0% (±0%)
Branches 72.7% (+ 0.0%)
Functions 83.2% (+ 0.0%)
Lines 83.5% (+ 0.0%)

@rexxars rexxars enabled auto-merge (squash) March 20, 2026 18:33
rexxars added 2 commits March 20, 2026 12:41
Remove vi.spyOn that re-implements getCliToken with a custom function.
Instead, use vi.resetModules() + dynamic import in beforeEach to get a
fresh module and reset the module-level cachedToken between tests.

This tests the actual implementation rather than a spy, and each test
gets a clean slate for the token cache.
…ade-off

Remove @internal from getTelemetryConsentCacheKey since it's exported for
test use.

Add comment documenting that in-flight request deduplication doesn't work
across concurrent calls to fetchTelemetryConsent() since createExpiringConfig
is instantiated on every call. This is a pre-existing design trade-off
(favoring simpler code over deduplication) that's worth documenting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant