Repository navigation
feat: Mask secrets before sending logs to the AI provider, with a preview of what is sent - #263
Merged
Merged
Conversation
…f what is sent Built-in rules for tokens, keys, private keys, URL and Authorization credentials and secret-named keys, patterns of the administrator's own, an option to send nothing when something was masked, a preview next to the Explain Error button, and the masked count on the console, the explanation, the REST API and the FINE log. Closes #184.
The private key rule took time in proportion to the square of the log, which anyone able to write to a log could trigger through the preview. Every built-in rule now reads the text once. Key names are split into words, so that TokenService.java:42, io.jsonwebtoken:jjwt, max_tokens and docker run -u 1000:1000 are left alone, while XML elements, PGP keys, quoted options, curl -u, PGPASSWORD, sk- keys and prefixed key lines are masked. The preview counts what is masked in the custom context, the button counts lines after masking, and the docs say where the count and the reason for a blocked request are shown.
The preview reads and masks as many lines as the button may send, so it now takes a POST with the crumb: the console page submits it as a form to a new tab, and a GET gets 405. Tests of automatic explanations now wait until the explanation is done with the build, so that Jenkins is not torn down while it still saves the build.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes #184. Build logs often hold secrets that never went through a Jenkins credential: a token in an environment dump, a password in a clone URL, a key a script printed. Until now they were sent to the AI provider as they were.
LogRedactorreplaces them with[REDACTED]in the log lines, the Custom Context, the workspace files ofincludeWorkspaceContextand the auto-fix prompt, for automatic explanations, the Explain Error button on the console and Stages pages, and theexplainError()step. Built-in rules cover AWS access key IDs, GitHub/GitLab/Slack/Google/Stripe/OpenAI/Anthropic/npm tokens, JWTs, PEM and PGP private keys (also on lines with a timestamp or BuildKit prefix, inside JSON strings, and cut short),Authorizationand bearer credentials, the password of a URL or ofcurl -u, and values given to keys whose last word names a secret, asKEY=value,key: value, JSON, XML elements or command-line options (DB_PASSWORD=,"clientSecret":,<password>,--token=,-Dgpg.passphrase=,PGPASSWORD=). The key stays, so the model still sees what the value was for. Ordinary build output is left alone:TokenService.java:[142,17],io.jsonwebtoken:jjwt,token-service:,password_reset.py:104,max_tokens: 4096,docker run -u 1000:1000,Invalid token: expired,${PASSWORD},password=nulland values Jenkins already masked (****).Anyone who can write to a build log chooses what these rules run on, so every built-in rule reads the text once (possessive quantifiers, bounded look-aheads, key names checked in code): 1,000 lines of 2,000 characters built to make a rule backtrack take at most about 3 seconds.
secretmasks that group only. Validated on the form.null, and the console and the button say why. Off by default. Counted as a newblockedusage result.redactedCount, and theErrorExplainerlogger records each request atFINE(job, lines, provider, model, masked count).Mask secrets before sending is on by default. The issue proposed opt-in; on by default is proposed here because the rules only match token-shaped values and secret-named keys, so the explanation keeps the error itself, and a leak cannot be taken back. It can be turned off in the global configuration or with
redactSecrets: falsein CasC.Before / After
The same pipeline run against
main(f428464) and this branch, with a mock OpenAI-compatible server recording the HTTP request it receives:AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLEAWS_ACCESS_KEY_ID=[REDACTED]DB_PASSWORD=hunter2hunter2DB_PASSWORD=[REDACTED]https://x-access-token:ghp_a1B2…@github.com/…https://x-access-token:[REDACTED]@github.com/…ERROR: connection to db.internal:5432 refusedWith
FakeAIProviderand more kinds (bearer token, RSA private key,api_key=in the step'scustomContext): before, all six reach the provider; after, all six are[REDACTED]and the console says[explain-error] Masked 6 secrets in what is sent to the AI provider.Global configuration (Manage Jenkins → System):
Console page and explanation:
The preview (Preview what is sent):

On a phone:
Testing done
LogRedactorTest(118): every built-in rule and the formats above, keys kept, counts, administrator patterns with and without asecretgroup (and one that only looks like it has one), 40 lines of ordinary build output that must be left alone (compiler errors, stack traces, Maven/npm/pip coordinates, LLM usage JSON, docker-u, YAML), which key names name a secret, and 19 inputs of 1,000 × 2,000 characters aimed at each rule, each with a 60 s limit (≤3 s locally).RedactionTest(14): the step, custom context, workspace files (pom.xmlwith<password>and a URL password), masking off, administrator patterns with the built-in rules off, blocking (no provider call, no quota used, only ablockedusage event, auto-fix skipped), nothing to mask while blocking, the automatic explanation, the console button, the preview (Item/Read can, without it cannot, GET refused, the custom context counted but not shown, nothing sent), the preview button on the console page opening it in a new window (HtmlUnit, with the crumb), form validation, and the settings through the configuration form.ErrorRunListenerTest: tests of automatic explanations now wait until the explanation is done with the build before Jenkins is torn down (CI hitDirectoryNotEmptyExceptionwhen the background explanation was still saving the build).docker -u, missed formats, prefixed key lines, the preview ignoring the custom context, docs).mvn verify(JDK 21) on the review fixes: 1,055 tests, 1 failure inConnectionDiagnosticsTest.tlsHandshakeFailuresAreReported, a timing race on a local socket under heavy load that this PR does not touch (5 of 5 reruns pass); after the last fixes,mvn verifyofLogRedactorTest,RedactionTest,ErrorRunListenerTest,ConsoleToolbarTest,ExplainCardStatesTestandConsoleExplainErrorActionTest: 167 tests, 0 failures; SpotBugs 0 bugs.Submitter checklist
Was generative AI tooling used to co-author this PR?