• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacklok / toolhive / 28232691304
67%

Build:
DEFAULT BRANCH: main
Ran 26 Jun 2026 10:40AM UTC
Jobs 1
Files 760
Run time 2min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

26 Jun 2026 10:34AM UTC coverage: 67.327% (-0.01%) from 67.341%
28232691304

push

github

web-flow
Fail closed on unpersisted rotated refresh token (#5636)

RefreshAndStore redeemed the stored refresh token at the IdP, then
persisted the result, but swallowed any storage-write failure and
returned the refreshed tokens anyway. With refresh-token rotation
(the common case) that strands a dead token in storage and gets the
user's whole token family revoked:

  1. read stored row            -> RT(v1)
  2. provider.RefreshTokens(RT v1)
        -> IdP rotates: RT(v1) now DEAD, returns AT', RT(v2)
           (irreversible -- it happened at the IdP)
  3. StoreUpstreamTokens(RT v2) FAILS   (Redis blip, timeout, ...)
  4. old code: log + `return updated, nil`  -- swallows the failure
        caller gets AT'/RT(v2), request succeeds,
        but STORAGE STILL HOLDS RT(v1)        -- poisoned
        ----- time passes -----
  5. next refresh: read stored row -> RT(v1)
  6. provider.RefreshTokens(RT v1)
        -> IdP: "RT(v1) already used -> REPLAY -> breach"
           revokes the whole family (v1, v2, ...)
  7. user hard-logged-out of the upstream; full re-auth required

The damage is done at step 2 (rotation is irreversible) but only
detonates at step 6 when the stale row is replayed. No concurrency
is needed -- one failed write plus any later refresh.

Fix, replacing steps 3-4:
  - Detect rotation (provider returned a new, non-empty refresh token
    that differs from the stored one).
  - Not rotated: keep today's behavior -- the stored token is still
    valid, so proceed and let the next request retry.
  - Rotated + store failed: retry the store briefly; if it still
    fails, best-effort delete the stale per-provider row so the next
    read misses and forces a clean re-auth instead of replaying a
    dead token, then return an error so callers fail closed.

Add DeleteUpstreamTokensForProvider to the storage interface (memory
+ redis); deleting an absent row is a no-op and siblings are
untouched.

Residual window: a crash between the IdP redemption and t... (continued)

76 of 107 new or added lines in 4 files covered. (71.03%)

24 existing lines in 6 files now uncovered.

69578 of 103343 relevant lines covered (67.33%)

65.82 hits per line

Uncovered Changes

Lines Coverage ∆ File
10
58.31
-0.25% pkg/authserver/storage/mocks/mock_storage.go
9
83.5
-0.43% pkg/authserver/storage/redis.go
8
91.3
-6.06% pkg/authserver/refresher.go
4
96.03
-0.44% pkg/authserver/storage/memory.go

Coverage Regressions

Lines Coverage ∆ File
6
20.11
-3.45% pkg/client/manager.go
6
72.15
-1.9% pkg/runner/config.go
4
61.6
-0.35% pkg/workloads/manager.go
3
97.37
-0.53% pkg/authz/authorizers/cedar/core.go
3
73.79
-2.91% pkg/state/local.go
2
82.29
-0.21% pkg/vmcp/composer/workflow_engine.go
Jobs
ID Job ID Ran Files Coverage
1 28232691304.1 26 Jun 2026 10:40AM UTC 760
67.33
GitHub Action Run
Source Files on build 28232691304
  • Tree
  • List 760
  • Changed 13
  • Source Changed 5
  • Coverage Changed 12
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Repo
  • Github Actions Build #28232691304
  • 7b871f7b on github
  • Prev Build on main (#28202472143)
  • Next Build on main (#28237268463)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc