jenkinsci / jenkinsci/credentials-binding-plugin
Option to re-resolve (not cache) bound credentials per step, for long builds where the credential rotates (e.g. GitHub App tokens)
- Dominant language
- Java
- Stars
- 57
- Forks
- 108
- PR merge metrics
- No merged PRs in 30d
Description
## Problem
`withCredentials` / `environment { X = credentials(...) }` resolve each binding eagerly in `BindingStep` and store the plaintext in a static `Overrider` (`EnvironmentExpander`) that lives for the whole block; the masking `ConsoleLogFilter` is compiled once as well. For most credentials this is fine.
However, a **GitHub App** credential (from `github-branch-source`) issues an **installation token valid for 1 hour**, and that token is refreshed only when the credential is *resolved* (`GitHubAppCredentials` marks it stale ~45 min before expiry). In builds that run longer than the token TTL, the value captured at bind time goes **stale between steps**, and later Git/GitHub operations fail with 401/403 — even though re-resolving the credential would have produced a fresh token.
This is awkward to work around today because it requires editing every affected Jenkinsfile to narrow the `withCredentials` scope around each late Git/GitHub operation.
## Proposal A (opt-in, minimal surface)
A global configuration listing **credential IDs** whose bindings should **re-resolve on each `expand()`** instead of being cached, with **dynamic "union" masking** so rotated values stay masked on **both** the console and the exception/failure surface (the `MaskedException` that populates `ErrorAction` / `/wfapi` / `catch (e) { e.getMessage() }`, which does not go through the `ConsoleLogFilter`). An empty list means byte-for-byte the current behavior, so it is fully backwards compatible and off by default.
## Proposal B (cleaner, larger)
Introduce a small SPI so a credential *type* can declare itself "refreshable", letting `GitHubAppCredentials` opt in without an explicit ID allowlist.
## What we have
We have a working implementation of Proposal A as a fork of this plugin, currently running in production, with tests covering: per-step re-resolution, union masking, failure-surface masking, and CPS/agent serialization (durability across a controller restart). The stock code path is unchanged when the allowlist is empty (the upstream test suite stays green).
## Ask
Before opening a PR: which direction (A, B, or something else) would you be willing to accept for a security-sensitive core plugin? We are happy to submit the PR against `master` in whatever shape you prefer, with tests, and sign the CLA.
Contributor guide
Research direction
Start by reading BindingStep, the static Overrider/EnvironmentExpander path, and the ConsoleLogFilter behavior described in the issue. Review the stated tests for per-step re-resolution, union masking, failure-surface masking, and CPS/agent serialization; done requires maintainer agreement on Proposal A, B, or another design before a PR is opened.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100