Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mask password/token in log messages. #205

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Conversation

jdamata
Copy link
Owner

@jdamata jdamata commented Oct 3, 2023

@jdamata jdamata linked an issue Oct 3, 2023 that may be closed by this pull request
@tbutler-qontigo
Copy link
Contributor

@jdamata How about adding a test or two to prove that your masking actually works? :)

@jdamata jdamata assigned freeranger and unassigned freeranger Oct 20, 2023
@jdamata jdamata requested a review from freeranger October 20, 2023 02:36
{
errorMessage: errors.New("Error updating SonarQube user: failed to execute http request: POST https://PASSWORD:@sonarqube.example.com/api/users/update_identity_provider?login=gitlab-john-doe&newExternalIdentity=john-doe&newExternalProvider=gitlab giving up after 5 attempt(s). Request: &{0xab1940 0xc00021c600}"),
token: "PASSWORD",
expected: "Error updating SonarQube user: failed to execute http request: POST https://********:@sonarqube.example.com/api/users/update_identity_provider?login=gitlab-john-doe&newExternalIdentity=john-doe&newExternalProvider=gitlab giving up after 5 attempt(s). Request: &{0xab1940 0xc00021c600}",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I would have such a "complex" string in the test - things like the number of attempts, the request id etc are not relevant to the test - wouldn't a simple GET request to an endpoint suffice?
I'm a big fan of keeping tests simple.

I would also argue that the test is a bit brittle because it is testing too much.
Is the purpose of the test to ensure that the sensitive information is masked out or to ensure that the function returns the original error message but with the sensitive info masked?
I would argue the former and therefore all you should be testing here is that the sensitive info is NOT included in the output - this is the key part of the functionality and should be tested in isolation IMO.

You don't want the test to fail because for example some future refactor truncated the message. That's a different problem and would have a different test :)

And indeed ideally you would have a test which has no sensitive info to check that you do get back the original string.

func censorError(err error, secret string) error {
// Replace the secret with asterisks of the same length
asterisks := strings.Repeat("*", len(secret))
result := strings.Replace(err.Error(), secret, asterisks, -1)
Copy link
Collaborator

@freeranger freeranger Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with this (admittedly a small one) is that you are still giving away information about the secret - ie the length of it because you return the same number of characters as the original.
Why not simply replace with say four asterisks - **** - in all cases so that you give nothing away?

@@ -26,17 +29,17 @@ type Paging struct {
}

// helper function to make api request to sonarqube
func httpRequestHelper(client *retryablehttp.Client, method string, sonarqubeURL string, expectedResponseCode int, errormsg string) (http.Response, error) {
func httpRequestHelper(client *retryablehttp.Client, method string, sonarqubeURL url.URL, expectedResponseCode int, errormsg string) (http.Response, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not objecting to the change but why make this a url now and then have to .String() it everywhere?

// https://github.com/jdamata/terraform-provider-sonarqube/issues/201
// go-retryablehttp error contains the token/user:pass in plaintext.
// We want to censor that secret before logging the error
func censorError(err error, secret string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are more types of sensitive data than just the token used to connect to sonar - for example what if there is an error in creating a user? Or simply a debug statement logging the params - Would we be emitting the user details, including the password into the logs?

I wouldn't necessarily "limit" this to errors (which you do by the name of the function) and perhaps consider how would this be used if it is a user request that fails - would you call this function multiple times, once for the API token and again to mask the password? Or pass in an array of sensitive data strings so you can make one call and sensor both?

I think ensuring that all logs are masked is something to consider, perhaps for a later issue - but here ensuring that all errors are masked (including any supplied password) should be the goal of this PR.

Copy link
Collaborator

@freeranger freeranger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdamata Some comments inline - some of it is no biggie, the main thing I think is exposing the number of characters in the secret and also not masking user passwords if trying to manage user resources

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.

Error message expose API token
3 participants