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

DEVPROD-10509 Update git.clone project command to redact for modules #8330

Merged
merged 23 commits into from
Sep 25, 2024

Conversation

ZackarySantana
Copy link
Contributor

@ZackarySantana ZackarySantana commented Sep 20, 2024

DEVPROD-10509

Description

This ticket cleans up some of the redacting logic and adds redaction to the generated tokens.

It also fixes a bug where modules would still generate a token even if the user passed in an access token.

Testing

Existing unit tests

This task on staging after the fix, the token for git clone ... github-merge-queue-sandbox is redacted.

@ZackarySantana ZackarySantana requested a review from a team September 20, 2024 15:54
@ZackarySantana ZackarySantana self-assigned this Sep 20, 2024

logger.Execution().Info("Fetching source from git...")
redactedCmds := fetchScript
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Our redaction logic takes care of this

out := stdErr.String()
if out != "" {
if opts.token != "" {
out = strings.Replace(out, opts.token, "[redacted github token]", -1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Our redaction logic takes care of this

@@ -725,17 +716,19 @@ func (c *gitFetchProject) fetchModuleSource(ctx context.Context,
return errors.Wrap(err, "setting location to clone from")
}

if opts.method == cloneMethodOAuth {
if opts.method == cloneMethodOAuth || opts.method == cloneMethodAccessToken {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This fixes the problem where modules always generate an installation token regardless if the user passed in an access token

// Otherwise, create an installation token for to clone the module.
// Fallback to the legacy global token if the token cannot be created.
appToken, err := comm.CreateInstallationToken(ctx, td, opts.owner, opts.repo)
if err == nil {
opts.token = appToken
opts.method = cloneMethodAccessToken

// After generating, redact the token from the logs.
conf.NewExpansions.Redact("EVERGREEN_GENERATED_GITHUB_TOKEN", appToken)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Always after creating an installation token on the fly, we should redact it

@@ -791,9 +784,7 @@ func (c *gitFetchProject) fetchModuleSource(ctx context.Context,

errOutput := stdErr.String()
if errOutput != "" {
if opts.token != "" {
errOutput = strings.Replace(errOutput, opts.token, "[redacted github token]", -1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Our redaction logic should take care of this.

if opts.token != "" {
errOutput = strings.Replace(errOutput, opts.token, "[redacted github token]", -1)
}
errOutput = strings.ReplaceAll(errOutput, "\n", fmt.Sprintf("\n%s: ", module.Name))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added this line to append every line with the module name for easier debugging

@@ -403,7 +403,7 @@ func (c *baseCommunicator) makeSender(ctx context.Context, tsk *task.Task, confi
levelInfo := send.LevelInfo{Default: level.Info, Threshold: level.Debug}
var senders []send.Sender
if config.SendToGlobalSender {
senders = append(senders, grip.GetSender())
senders = append(senders, redactor.NewRedactingSender(grip.GetSender(), config.RedactorOpts))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This adds the redactor to a global sender, just in case

@@ -620,11 +620,6 @@ func (c *PluginCommandConf) unmarshalParams() error {
return nil
}

type ArtifactInstructions struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Random unused code

@ZackarySantana ZackarySantana changed the title DEVPROD-10509 Update global sender to redact logs as well DEVPROD-10509 Update git.clone project command to redact for modules Sep 20, 2024
@ZackarySantana
Copy link
Contributor Author

I'm waiting for a review to update the agent version because of merge conflicts

agent/util/expansions.go Outdated Show resolved Hide resolved
agent/util/expansions.go Show resolved Hide resolved
agent/command/git.go Outdated Show resolved Hide resolved
@@ -461,8 +462,8 @@ func (s *GitGetProjectSuite) TestBuildHTTPCloneCommand() {
s.Require().Len(cmds, 5)
s.True(utility.ContainsOrderedSubset(cmds, []string{
"set +o xtrace",
"echo \"git clone https://[redacted github token]:[email protected]/evergreen-ci/sample.git 'dir' --branch 'main'\"",
"git clone https://PROJECTTOKEN:[email protected]/evergreen-ci/sample.git 'dir' --branch 'main'",
fmt.Sprintf("echo \"git clone https://%s:[email protected]/evergreen-ci/sample.git 'dir' --branch 'main'\"", projectGitHubToken),
Copy link
Contributor

Choose a reason for hiding this comment

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

Since these now return the unredacted commands, are there other tests that cover the functionality that GetLoggerProducer loggers redact sensitive secrets? Reading through some of the logic, it looks like the relies on some slightly risky assumptions, like the fact that the expansions must be passed as a pointer to the redaction opts so that the memory is shared between the task config and the redacting sender.

If not, we should either add new tests or make a separate ticket to add tests that the loggers do redact correctly because it's critical functionality at this point and we shouldn't rely on manual testing to verify that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I improved the test in this file (here) and I added another more specific test here to the base communicator.

The git test uses the mock communicator, so it isn't a full on test of how it would work in production.

The test on the base communicator is more of a test on how it will work on production.

Copy link
Contributor

@Kimchelly Kimchelly left a comment

Choose a reason for hiding this comment

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

LGTM mod couple nits.

@@ -23,6 +29,88 @@ func TestEvergreenCommunicatorConstructor(t *testing.T) {
assert.Equal(t, defaultTimeoutMax, c.retry.MaxDelay)
}

func TestLoggerProducerRedactorOptions(t *testing.T) {
secret := "super_soccer_ball"
Copy link
Contributor

Choose a reason for hiding this comment

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

findTokenInRedacted := func() bool {
for _, redacted := range conf.NewExpansions.GetRedacted() {
if redacted.Key == generatedTokenKey {
if redacted.Value == token {
Copy link
Contributor

Choose a reason for hiding this comment

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

mega nit: could combine the two ifs into one if redacted.Key == generatedTokenKey && redacted.Value == token

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I originally did two different booleans (found and then correctValue) but this is an artifact from that

t.Run("LeaksWithoutRedactor", func(t *testing.T) {
task := createTask()
comm := newBaseCommunicator("whatever", map[string]string{})
logger, err := comm.GetLoggerProducer(context.Background(), task, nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: use a context.WithCancel if possible rather than plain context.Background().

Same with the test below.


data := readLogs(t, task)
assert.NotContains(t, data, secret)
assert.Contains(t, data, "secret_key")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: might be clearer that this is checking for the redacted string by explicitly looking for <REDACTED:secret_key>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll update it, I was only worried about if we changed the redacted format (which I think the test would be easy to amend then anyways)

assert.Contains(t, data, "More fluff")
assert.Contains(t, data, "Even more fluff")
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The redacting test is great, love the helper functions and clear test cases. Thanks for adding it! 😎

@ZackarySantana ZackarySantana merged commit dc993be into evergreen-ci:main Sep 25, 2024
3 of 4 checks passed
@ZackarySantana ZackarySantana deleted the DEVPROD-10509 branch September 25, 2024 18:23
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.

2 participants