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

Remove an incomplete workaround for database connection_url formatting #22224

Closed
wants to merge 1 commit into from

Conversation

maxb
Copy link
Contributor

@maxb maxb commented Aug 7, 2023

Instead fix the underlying issue of dbutil.QueryHelper making multiple passes over the input string, and potentially reprocessing placeholders inserted as a result of previous substitutions.

Follow-up to #13414

Instead fix the underlying issue of `dbutil.QueryHelper` making multiple
passes over the input string, and potentially reprocessing placeholders
inserted as a result of previous substitutions.

Follow-up to hashicorp#13414
Copy link
Contributor Author

@maxb maxb left a comment

Choose a reason for hiding this comment

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

Notes for reviewer:

@@ -69,7 +69,7 @@ func (c *mySQLConnectionProducer) Init(ctx context.Context, conf map[string]inte
password := c.Password

// QueryHelper doesn't do any SQL escaping, but if it starts to do so
// then maybe we won't be able to use it to do URL substitution any more.
// then maybe we won't be able to use it to do URL substitution anymore.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Grammar fix. Also, note that this is a forked copy of the sdk's SQLConnectionProducer that did not contain the workaround.

strings.Contains(c.Password, "{{password}}") {

return nil, fmt.Errorf("username and/or password cannot contain the template variables")
}
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 is the workaround being removed.

// Don't escape special characters for the username and password if the
// disable_escaping parameter is set to true. A user does this if the
// connection_url is actually not a URL, but database-specific other
// format, typically space or semicolon separated key=value pairs.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add clarity to comment on why disable_escaping exists.

username := c.Username
password := c.Password
if !c.DisableEscaping {
username = url.PathEscape(c.Username)
}
if (c.Type != "mysql") && !c.DisableEscaping {
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 mysql special case can be deleted - the mysql plugin is no longer using this code at all. As can be seen in the first file touched in this PR, it now has its own forked copy.

@@ -22,6 +20,9 @@ func TestSQLPasswordChars(t *testing.T) {
{"postgres", "pass/word"},
{"postgres", "p@ssword"},
{"postgres", "pass\"word\""},
// Much to my surprise, CREATE USER "{{password}}" PASSWORD 'foo' worked.
{"{{password}}", "foo"},
{"user", "{{username}}"},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Re-add test cases removed in #13414.

}
}
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove test that tests the workaround being removed.

assert.Equal(t, tc.expected, QueryHelper(tc.tpl, data),
"template processing produced unexpected result")
}
}
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 test passes before and after the change.

assert.Equal(t, "A{{a}}{{b}}{{c}}{{d}}B{{a}}{{b}}{{c}}{{d}}C{{a}}{{b}}{{c}}{{d}}D{{a}}{{b}}{{c}}{{d}}",
QueryHelper("{{a}}{{b}}{{c}}{{d}}", data),
"template processing produced unexpected result")
}
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 test fails when applied to the existing implementation of QueryHelper - it showcases the issues just discussed at the end of #13414.

for k, v := range data {
tpl = strings.ReplaceAll(tpl, fmt.Sprintf("{{%s}}", k), v)
}
var queryHelperRegex = regexp.MustCompile(`{{[^{}]+}}`)
Copy link
Contributor

@tomhjp tomhjp Aug 8, 2023

Choose a reason for hiding this comment

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

I don't have the original context so I won't do a full review, but if we do go with a regex I think we should be extremely prescriptive with it to more precisely match what we're looking for, e.g. here's a little Go playground test case for a key that has a } in it (which probably isn't valid for other reasons, but it's just to illustrate the point) https://go.dev/play/p/qUXqc397xkY.

So I'd rather see a regex that specifically looks for the keys we know how to replace, e.g.

fmt.Sprintf(`{{(%s)}}`, strings.Join(keys, "|"))

Copy link
Contributor Author

@maxb maxb Aug 8, 2023

Choose a reason for hiding this comment

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

The major point of this change is to do only one pass over the input string, not one per replaceable parameter, so the regex needs to stay as is.

However, I recognise I should make it clearer in the documentation that replacement names may not contain { or } themselves.

EDIT: Oops, responding from phone, and half the suggested code was off screen... I'll have a think about it. I'm not sure this is a case we really need to care about though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Having thought on this some more, I am of the opinion that we should not make this change, because it makes the code more complex (and less efficient, though this is not a particularly a hotspot), only to support an edge case of placeholder keys containing { or } which they realistically never will.

The keys here are all specified by the coders of plugins and never by users, and it would be mad to define a {{xxx}} substitution that contained internal { or } as part of the xxx - it would be a supremely confusing UI.

Also, if we dynamically build the regex, we then need to account for quoting regex metacharacters in key names - the . character could plausibly appear in a future key, for example.

I do, though, see that I should document the limitation of not using { } within substitution keys, and add an example of it to the tests.

Would that be an acceptable way to proceed?

@schavis schavis added the needs-eng-review Community PR waiting for ENG review label Sep 12, 2023
@maxb
Copy link
Contributor Author

maxb commented Aug 18, 2024

I'll just close this out ... HashiCorp aren't interested, and I no longer use Vault, and am discouraged from doing so again in the future by the license change.

@maxb maxb closed this Aug 18, 2024
@maxb maxb deleted the followup-to-13414 branch August 18, 2024 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-eng-review Community PR waiting for ENG review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants