diff --git a/base/git/git.go b/base/git/git.go index fe860752..46b535c6 100644 --- a/base/git/git.go +++ b/base/git/git.go @@ -20,9 +20,13 @@ const ( func RefToName(ref string) (branch string) { // refs are always given in the form "refs/heads/{branch name}" or "refs/tags/{tag name}" - branch = strings.Split(ref, "refs/")[1] + parts := strings.SplitN(ref, "refs/", 2) + if len(parts) < 2 { + return ref + } + branch = parts[1] if strings.HasPrefix(branch, "heads/") { - branch = strings.Split(branch, "heads/")[1] + branch = strings.SplitN(branch, "heads/", 2)[1] } // if we got a tag ref, just leave it as "tags/{tag name}" return branch diff --git a/gcalbot/gcalbot/handler.go b/gcalbot/gcalbot/handler.go index 8f7c1ed5..ab448bb9 100644 --- a/gcalbot/gcalbot/handler.go +++ b/gcalbot/gcalbot/handler.go @@ -160,5 +160,7 @@ func (h *Handler) handleConfigure(msg chat1.MsgSummary) error { } func (h *Handler) LoginToken(username string) string { - return hex.EncodeToString(hmac.New(sha256.New, []byte(h.tokenSecret)).Sum([]byte(username))) + mac := hmac.New(sha256.New, []byte(h.tokenSecret)) + mac.Write([]byte(username)) + return hex.EncodeToString(mac.Sum(nil)) } diff --git a/gitlabbot/gitlabbot/handler.go b/gitlabbot/gitlabbot/handler.go index 033afd0e..431a53a1 100644 --- a/gitlabbot/gitlabbot/handler.go +++ b/gitlabbot/gitlabbot/handler.go @@ -88,6 +88,15 @@ func (h *Handler) handleSubscribe(ctx context.Context, cmd string, msg chat1.Msg return nil } + isAllowed, err := base.IsAtLeastWriter(h.kbc, msg.Sender.Username, msg.Channel) + if err != nil { + return fmt.Errorf("error getting role status: %s", err) + } + if !isAllowed { + h.ChatEcho(msg.ConvID, "You must be at least a writer to configure me!") + return nil + } + hostedURL, repo, err := parseRepoInput(args[0]) if err != nil { h.ChatEcho(msg.ConvID, "Invalid repo: %q, expected `` or `https://domain.com/owner/repo`", repo) diff --git a/jirabot/src/cmd-config.ts b/jirabot/src/cmd-config.ts index 09d03a42..fa0113d8 100644 --- a/jirabot/src/cmd-config.ts +++ b/jirabot/src/cmd-config.ts @@ -6,6 +6,22 @@ import * as JiraOauth from './jira-oauth' import * as Utils from './utils' import * as Jira from './jira' +const isPrivateHost = (hostname: string): boolean => { + if (hostname === 'localhost' || hostname === '::1') return true + const ipv4 = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) + if (ipv4) { + const [a, b] = [Number(ipv4[1]), Number(ipv4[2])] + if (a === 10) return true + if (a === 172 && b >= 16 && b <= 31) return true + if (a === 192 && b === 168) return true + if (a === 127) return true + if (a === 169 && b === 254) return true + if (a === 0) return true + } + if (/\.(local|internal|corp|lan|intranet)$/.test(hostname)) return true + return false +} + const makeNewTeamChannelConfig = async ( context: Context, messageContext: Message.MessageContext, @@ -244,8 +260,43 @@ const handleTeamConfig = async ( return Errors.makeResult(undefined) } switch (parsedMessage.toSet.name) { - case 'jiraHost': - // TODO check admin + case 'jiraHost': { + const teamDetails = await context.bot.team.listTeamMemberships({ + team: parsedMessage.context.teamName, + }) + const isAdmin = [ + ...(teamDetails.members.owners || []), + ...(teamDetails.members.admins || []), + ].some(m => m.username === parsedMessage.context.senderUsername) + if (!isAdmin) { + replyChat( + context, + parsedMessage, + 'You must be a team admin to configure the Jira server.' + ) + return Errors.makeError(undefined) + } + + const rawHost = parsedMessage.toSet.value.replace(/\/+$/, '') + let parsedURL: URL + try { + parsedURL = new URL( + rawHost.startsWith('http') ? rawHost : `https://${rawHost}` + ) + } catch { + replyChat(context, parsedMessage, `Invalid jiraHost: ${rawHost}`) + return Errors.makeError(undefined) + } + const hostname = parsedURL.hostname + if (isPrivateHost(hostname)) { + replyChat( + context, + parsedMessage, + `jiraHost must be a public hostname, not an internal address.` + ) + return Errors.makeError(undefined) + } + const detailsRet = await JiraOauth.generateNewJiraLinkDetails() if (detailsRet.type === Errors.ReturnType.Error) { Errors.reportErrorAndReplyChat( @@ -258,7 +309,7 @@ const handleTeamConfig = async ( const details = detailsRet.result const newConfig = { - jiraHost: parsedMessage.toSet.value.replace(/\/+$/, ''), + jiraHost: rawHost, jiraAuth: { consumerKey: details.consumerKey, publicKey: details.publicKey, @@ -287,6 +338,7 @@ const handleTeamConfig = async ( jiraConfigToMessageBody(context, newConfig) ) return Errors.makeResult(undefined) + } default: Errors.reportErrorAndReplyChat(context, parsedMessage.context, { type: Errors.ErrorType.UnknownParam, diff --git a/pollbot/pollbot/http.go b/pollbot/pollbot/http.go index da9776d7..305a38e9 100644 --- a/pollbot/pollbot/http.go +++ b/pollbot/pollbot/http.go @@ -152,5 +152,7 @@ func (h *HTTPSrv) handleImage(w http.ResponseWriter, r *http.Request) { func (h *HTTPSrv) handleHealthCheck(_ http.ResponseWriter, _ *http.Request) {} func (h *HTTPSrv) LoginToken(username string) string { - return hex.EncodeToString(hmac.New(sha256.New, []byte(h.tokenSecret)).Sum([]byte(username))) + mac := hmac.New(sha256.New, []byte(h.tokenSecret)) + mac.Write([]byte(username)) + return hex.EncodeToString(mac.Sum(nil)) } diff --git a/triviabot/triviabot/handler.go b/triviabot/triviabot/handler.go index 79a3e16a..7534b0d7 100644 --- a/triviabot/triviabot/handler.go +++ b/triviabot/triviabot/handler.go @@ -123,18 +123,19 @@ func (h *Handler) HandleCommand(ctx context.Context, msg chat1.MsgSummary) error } cmd := strings.TrimSpace(msg.Content.Text.Body) switch { + // Trivia commands are open to all channel members, including readers. case strings.HasPrefix(cmd, "!trivia begin"): h.stats.Count("start") h.handleStart(msg) case strings.HasPrefix(cmd, "!trivia end"): h.stats.Count("stop") h.handleStop(msg) - case strings.HasPrefix(cmd, "!trivia top"): - h.stats.Count("top") - return h.handleTop(ctx, msg.ConvID) case strings.HasPrefix(cmd, "!trivia reset"): h.stats.Count("reset") return h.handleReset(ctx, msg) + case strings.HasPrefix(cmd, "!trivia top"): + h.stats.Count("top") + return h.handleTop(ctx, msg.ConvID) } return nil }