fix(agent): keep passwd file content out of parse errors - #7002
Merged
Conversation
parsePasswdLine, parseGroupLine and their FreeBSD counterpart built their
errors by interpolating the field they had just failed to parse:
fmt.Errorf("passwd line had badly formatted uid %s", parts[2])
Every caller logs that error, so a field that does not parse as a number is
copied verbatim into the agent's log. In host mode the file is the host's own
/etc/passwd and this is merely untidy. In connector mode it is /etc/passwd
read out of the container being attached to, which the image author controls,
and a uid field of "$(...)\n level=fatal msg=..." lands in the log as written.
Whoever ships those logs to a collector — anything parsing logfmt, anything
rendering them in a browser — is the one who pays for it.
The field's content was never the useful part of the message; its position
was. The line parsers now return errMalformedUID or errMalformedGID, and the
readers wrap with the line number they already know:
passwd line 12: malformed uid field
which points at the offending entry without quoting it. Field-count mismatches
keep their counts: len(parts) is a number the parser derived, not bytes from
the file.
Two log calls in the connector authenticator passed WithError(err) where err
was provably nil — both sit after an if that returns on the non-nil case, so
they only ever recorded "error: null". One of them is the sink CodeQL reports
as alert #300, which is to say the reported leak was of a nil. Dropped.
Closes the five go/clear-text-logging alerts: #195, #196, #198, #240, #300.
TestParseErrorsDoNotEchoFileContent feeds a marker string through the uid,
gid and field-count paths of all three readers and asserts the error names the
line but not the content. Restoring any one interpolation turns it red.
GOOS=freebsd does not build on master either, for an unrelated reason:
backend.ListGroups returns []string where Backend wants []uint32
(auth_freebsd.go:45). The FreeBSD parser is changed here to match the others,
but it could not be compiled to prove it.
|
Claude finished @otavio's task in 2m 21s —— View job Code Review CompleteReviewed 5 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is. A few things I verified while reviewing:
On the FreeBSD note in the PR description: To request another review round, comment |
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.
Closes code scanning alerts #195, #196, #198, #240, #300 (
go/clear-text-logging).The leak
parsePasswdLine,parseGroupLineand the FreeBSDparseMasterPasswdLinebuilt their errors by interpolating the field they had just failed to parse:Every caller logs that error with
WithError(err), so a field that doesn't parse as a number is copied verbatim into the agent's log.In host mode the file is the host's own
/etc/passwd— untidy, no more. In connector mode it is/etc/passwdread out of the container being attached to (getPasswd→docker cp), which the image author controls. A uid field of$(...)\n level=fatal msg=...lands in the log as written. Whoever ships those logs onward — anything parsing logfmt, anything rendering them in a browser — pays for it.No password ever reached a log; CodeQL's "sensitive data" is the passwd bytes themselves. The reachable problem is log injection from a hostile image, which is why this is worth fixing rather than dismissing.
The fix
The field's content was never the useful part of the message — its position was. Line parsers now return
errMalformedUID/errMalformedGID, and the readers wrap with the line number they already track:Field-count mismatches keep their counts:
len(parts)is a number the parser derived, not bytes from the file.Also
Two log calls in the connector authenticator passed
WithError(err)whereerris provably nil — both sit after anif err != nil { ...; return false }, so they only ever recordederror: null. One of them is the sink CodeQL reports as #300, meaning the reported leak was of a nil. Dropped both.Testing
TestParseErrorsDoNotEchoFileContentfeeds a marker through the uid, gid and field-count paths of the passwd, group and shadow readers, asserting the error names the line but not the content. Mutation-checked — restoring one interpolation turns it red:Full
agentsuite green under-tags docker -race;gofmtandgolangci-lintclean.One thing I could not verify
GOOS=freebsddoes not build onmastereither, for an unrelated pre-existing reason —backend.ListGroupsreturns[]stringwhereBackendwants[]uint32(auth_freebsd.go:45). FreeBSD isn't in the CI matrix, so nothing catches it.auth_freebsd.gois changed here to match the other parsers, but it could not be compiled to prove it. Happy to open a separate issue for the FreeBSD backend.