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

fix(#19314): allow ssh/altssh subdomains in repo URLs to match webhook payload #19315

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

Conversation

mtbennett-godaddy
Copy link

@mtbennett-godaddy mtbennett-godaddy commented Jul 30, 2024

ℹ️ Edit: There was a request to make things reusable between the Application and ApplicationSet webhooks. See #19315 (comment) for notes on those refactors. Original PR description follows.


Fixes #19314.

Checklist:

  • Either (a) I've created an enhancement proposal and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
  • The title of the PR states what changed and the related issues number (used for the release note).
  • The title of the PR conforms to the Toolchain Guide
  • I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
  • I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
  • Does this PR require documentation updates?
  • I've updated documentation as required by this PR.
  • I have signed off all my commits as required by DCO
  • I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
  • My build is green (troubleshooting builds).
  • My new feature complies with the feature status guidelines.
  • I have added a brief description of why this PR is necessary and/or what this PR solves.
  • Optional. My organization is added to USERS.md.
  • Optional. For bug fixes, I've indicated what older releases this fix should be cherry-picked into (this may or may not happen depending on risk/complexity).

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
@mtbennett-godaddy mtbennett-godaddy requested a review from a team as a code owner July 30, 2024 23:16
Copy link

bunnyshell bot commented Jul 30, 2024

✅ Preview Environment deployed on Bunnyshell

Component Endpoints
argocd https://argocd-agrnra.bunnyenv.com/
argocd-ttyd https://argocd-web-cli-agrnra.bunnyenv.com/

See: Environment Details | Pipeline Logs

Available commands (reply to this comment):

  • 🔴 /bns:stop to stop the environment
  • 🚀 /bns:deploy to redeploy the environment
  • /bns:delete to remove the environment

Copy link

bunnyshell bot commented Jul 30, 2024

✅ Preview Environment created on Bunnyshell but will not be auto-deployed

See: Environment Details

Available commands (reply to this comment):

  • 🚀 /bns:deploy to deploy the environment

Copy link

codecov bot commented Jul 31, 2024

Codecov Report

Attention: Patch coverage is 60.39604% with 120 lines in your changes missing coverage. Please review.

Project coverage is 55.72%. Comparing base (b824956) to head (c3396ab).

Files with missing lines Patch % Lines
util/webhook/webhook.go 16.66% 65 Missing ⚠️
server/webhookhandler/webhookhandler.go 72.72% 38 Missing and 7 partials ⚠️
applicationset/webhookhandler/webhookhandler.go 88.23% 4 Missing ⚠️
...t-controller/commands/applicationset_controller.go 63.63% 3 Missing and 1 partial ⚠️
server/server.go 86.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #19315      +/-   ##
==========================================
- Coverage   55.85%   55.72%   -0.13%     
==========================================
  Files         321      322       +1     
  Lines       44490    44474      -16     
==========================================
- Hits        24849    24784      -65     
- Misses      17077    17136      +59     
+ Partials     2564     2554      -10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Comment on lines 30 to 32
// https://www.rfc-editor.org/rfc/rfc3986#section-3.2.1
// https://github.com/shadow-maint/shadow/blob/master/libmisc/chkname.c#L36
const usernameRegex = `[a-zA-Z0-9_\.][a-zA-Z0-9_\.-]{0,30}[a-zA-Z0-9_\.\$-]?`
Copy link
Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Should we instead move this to a common util package?

Copy link
Author

Choose a reason for hiding this comment

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

I had the same thought but wasn’t sure if I should dig in that much. (Wasn’t sure if the 2 webhooks were being allowed to diverge intentionally for some reason.) There’s another new feature in the Application webhook (maxPayloadSizeMB) that isn’t here either that I wanted to pull in.

I’ll see what I can do about reusing as much as possible between the 2 webhooks.

Copy link
Member

Choose a reason for hiding this comment

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

I have a similar PR open where the thing with the util package is already done (#16292). Maybe this can be used as an inspiration to these two PRs can be combined somehow.

Comment on lines 253 to 256
regexEscapedHostname := regexp.QuoteMeta(urlObj.Hostname())
regexEscapedPath := regexp.QuoteMeta(urlObj.EscapedPath()[1:])
regexpStr := fmt.Sprintf(`(?i)^(http://|https://|%s@|ssh://(%s@)?((alt)?ssh\.)?)%s(:[0-9]+|)[:/]%s(\.git)?$`,
usernameRegex, usernameRegex, regexEscapedHostname, regexEscapedPath)
Copy link
Author

Choose a reason for hiding this comment

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

…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Comment on lines 30 to 32
// https://www.rfc-editor.org/rfc/rfc3986#section-3.2.1
// https://github.com/shadow-maint/shadow/blob/master/libmisc/chkname.c#L36
const usernameRegex = `[a-zA-Z0-9_\.][a-zA-Z0-9_\.-]{0,30}[a-zA-Z0-9_\.\$-]?`
Copy link
Member

Choose a reason for hiding this comment

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

Should we instead move this to a common util package?

Comment on lines 253 to 256
regexEscapedHostname := regexp.QuoteMeta(urlObj.Hostname())
regexEscapedPath := regexp.QuoteMeta(urlObj.EscapedPath()[1:])
regexpStr := fmt.Sprintf(`(?i)^(http://|https://|%s@|ssh://(%s@)?((alt)?ssh\.)?)%s(:[0-9]+|)[:/]%s(\.git)?$`,
usernameRegex, usernameRegex, regexEscapedHostname, regexEscapedPath)
Copy link
Member

Choose a reason for hiding this comment

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

Ditto with this, if it's identical to the appset webhook handling logic, can we just move it to a shared location?

…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
…sh-subdomain

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Comment on lines 152 to 153
func GetApiUrlRegex(originalUrl string) (*regexp.Regexp, error) {
return getUrlRegex(originalUrl, `(?i)^https?://%[2]s(:[0-9]+)?/?$`)
Copy link
Author

Choose a reason for hiding this comment

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

The original regex used for this (here) allowed for SSH URLs which, AFAIK, aren’t valid for an API URL. If I’m mistaken on that, I can easily expand this to account for that. (There were no tests for this regex before. I added basic positive and negative cases in new Test_GetApiUrlRegex.)

@mtbennett-godaddy
Copy link
Author

@crenshaw-dev

The common webhook handling code (HTTP- and provider-related bits and the queue logic) has been split from the use case-specific webhook payload handling (to make the former reusable across instances of the latter):

  • Webhook
    • util/webhook was refactored to have all the reusable parts of the webhook handling.
    • has a payloadHandler field of type WebhookPayloadHandler, which is an interface for the different webhook payload handlers to implement.
    • has a HandleRequest function that does all the provider-specific payload parsing and authentication and then adds the payload to a queue.
    • has a worker pool running to pull payloads off the queue and call its payload handler’s HandlePayload function.
  • WebhookPayloadHandler
    • has a HandlePayload function that handles the payload for the handler’s particular use case.
    • server/webhookhandler is new and contains the ApplicationWebhookPayloadHandler, which implements WebhookPayloadHandler for applications.
    • applicationset/webhook was refactored to applicationset/webhookhandler to be the ApplicationSetWebhookPayloadHandler, which implements WebhookPayloadHandler for application sets.
  • server.go creates a Webhook with an ApplicationWebhookPayloadHandler instance and assigns the webhook’s HandleRequest function to the /api/webhook endpoint.
  • applicationset_controller.go creates a Webhook with an ApplicationSetWebhookPayloadHandler instance and assigns the webhook’s HandleRequest function to the /api/webhook endpoint.
  • Tests
    • The URL regex tests stayed in util/webhook since that functionality is still there. (The existing tests only covered web URLs. I added tests for API URLs.)
    • The remaining tests that were in util/webhook moved to server/webhookhandler with minimal refactors. (server/webhookhandler/webhookhandler_test.go shows as a new file in the diff but it is almost entirely just a copy/paste of the original util/webhook/webhook_test.go file.)
    • The applicationset/webhookhandler tests required minimal refactors.

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Comment on lines +148 to +149
func GetWebUrlRegex(originalUrl string) (*regexp.Regexp, error) {
return getUrlRegex(originalUrl, `(?i)^((https?|ssh)://)?(%[1]s@)?((alt)?ssh\.)?%[2]s(:[0-9]+)?[:/]%[3]s$`)
Copy link
Author

Choose a reason for hiding this comment

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

@robinlieb As far as I can tell, this regex covers the changes you introduced in #16292.

The updated regex matches the following sequence of parts:

  1. optional: protocol (http, https, or ssh) followed by ://
  2. optional: username followed by @
    • previously, the regex did not allow for usernames in http, https, or protocol-less URLs.
  3. optional: ssh or altssh subdomain
    • this is the tiny change I originally set out to introduce 😅
  4. required: hostname parsed from original URL
  5. optional: : followed by port number
  6. required: : or /
  7. required: path parsed from original URL followed by optional .git

Feel free to suggest any additional test cases in webhook_test.go and I’ll incorporate them and make sure the regex works against them.

Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
Signed-off-by: Matthew Bennett <mtbennett@godaddy.com>
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.

Webhooks not compatible with alternative SSH addressing schemes
3 participants