Add host check
This commit is contained in:
parent
dbb440eadf
commit
651073f69f
@ -2,11 +2,13 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||||
"github.com/TicketsBot/GoPanel/utils"
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type activateIntegrationBody struct {
|
type activateIntegrationBody struct {
|
||||||
@ -77,6 +79,7 @@ func ActivateIntegrationHandler(ctx *gin.Context) {
|
|||||||
|
|
||||||
// Since we've checked the length, we can just iterate over the secrets and they're guaranteed to be correct
|
// Since we've checked the length, we can just iterate over the secrets and they're guaranteed to be correct
|
||||||
secretMap := make(map[int]string)
|
secretMap := make(map[int]string)
|
||||||
|
secretValues := make(map[string]string)
|
||||||
for secretName, value := range data.Secrets {
|
for secretName, value := range data.Secrets {
|
||||||
if len(value) == 0 || len(value) > 255 {
|
if len(value) == 0 || len(value) > 255 {
|
||||||
ctx.JSON(400, utils.ErrorStr("Secret values must be between 1 and 255 characters"))
|
ctx.JSON(400, utils.ErrorStr("Secret values must be between 1 and 255 characters"))
|
||||||
@ -90,6 +93,7 @@ func ActivateIntegrationHandler(ctx *gin.Context) {
|
|||||||
if secret.Name == secretName {
|
if secret.Name == secretName {
|
||||||
found = true
|
found = true
|
||||||
secretMap[secret.Id] = value
|
secretMap[secret.Id] = value
|
||||||
|
secretValues[secret.Name] = value
|
||||||
break inner
|
break inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +106,23 @@ func ActivateIntegrationHandler(ctx *gin.Context) {
|
|||||||
|
|
||||||
// Validate secrets
|
// Validate secrets
|
||||||
if integration.Public && integration.Approved && integration.ValidationUrl != nil {
|
if integration.Public && integration.Approved && integration.ValidationUrl != nil {
|
||||||
res, statusCode, err := utils.SecureProxyClient.DoRequest(http.MethodPost, *integration.ValidationUrl, nil, data.Secrets)
|
integrationHeaders, err := dbclient.Client.CustomIntegrationHeaders.GetByIntegration(integrationId)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(500, utils.ErrorJson(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := make(map[string]string)
|
||||||
|
for _, header := range integrationHeaders {
|
||||||
|
value := header.Value
|
||||||
|
for key, secret := range secretValues {
|
||||||
|
value = strings.ReplaceAll(value, fmt.Sprintf("%%%s%%", key), secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
headers[header.Name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
res, statusCode, err := utils.SecureProxyClient.DoRequest(http.MethodPost, *integration.ValidationUrl, headers, secretValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if statusCode == http.StatusRequestTimeout {
|
if statusCode == http.StatusRequestTimeout {
|
||||||
ctx.JSON(400, utils.ErrorStr("Secret validation server did not respond in time (contact the integration author)"))
|
ctx.JSON(400, utils.ErrorStr("Secret validation server did not respond in time (contact the integration author)"))
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||||
"github.com/TicketsBot/GoPanel/utils"
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
"github.com/TicketsBot/database"
|
"github.com/TicketsBot/database"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type integrationCreateBody struct {
|
type integrationCreateBody struct {
|
||||||
@ -68,6 +70,19 @@ func CreateIntegrationHandler(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data.ValidationUrl != nil {
|
||||||
|
sameHost, err := isSameValidationUrlHost(data.WebhookUrl, *data.ValidationUrl)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(500, utils.ErrorJson(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sameHost {
|
||||||
|
ctx.JSON(400, utils.ErrorStr("Validation URL must be on the same host as the webhook URL"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
integration, err := dbclient.Client.CustomIntegrations.Create(userId, data.WebhookUrl, data.ValidationUrl, data.Method, data.Name, data.Description, data.ImageUrl, data.PrivacyPolicyUrl)
|
integration, err := dbclient.Client.CustomIntegrations.Create(userId, data.WebhookUrl, data.ValidationUrl, data.Method, data.Name, data.Description, data.ImageUrl, data.PrivacyPolicyUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(500, utils.ErrorJson(err))
|
ctx.JSON(500, utils.ErrorJson(err))
|
||||||
@ -124,3 +139,14 @@ func CreateIntegrationHandler(ctx *gin.Context) {
|
|||||||
|
|
||||||
ctx.JSON(200, integration)
|
ctx.JSON(200, integration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSameValidationUrlHost(webhookUrl, validationUrl string) (bool, error) {
|
||||||
|
webhookStripped := utils.GetUrlHost(strings.ReplaceAll(webhookUrl, "%", ""))
|
||||||
|
validationStripped := utils.GetUrlHost(strings.ReplaceAll(validationUrl, "%", ""))
|
||||||
|
|
||||||
|
if webhookStripped == "Invalid URL" || validationStripped == "Invalid URL" {
|
||||||
|
return false, errors.New("invalid webhook or validation URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.ToLower(utils.SecondLevelDomain(webhookStripped)) == strings.ToLower(utils.SecondLevelDomain(validationStripped)), nil
|
||||||
|
}
|
@ -88,6 +88,19 @@ func UpdateIntegrationHandler(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data.ValidationUrl != nil {
|
||||||
|
sameHost, err := isSameValidationUrlHost(data.WebhookUrl, *data.ValidationUrl)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(500, utils.ErrorJson(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sameHost {
|
||||||
|
ctx.JSON(400, utils.ErrorStr("Validation URL must be on the same host as the webhook URL"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update integration metadata
|
// Update integration metadata
|
||||||
err = dbclient.Client.CustomIntegrations.Update(database.CustomIntegration{
|
err = dbclient.Client.CustomIntegrations.Update(database.CustomIntegration{
|
||||||
Id: integration.Id,
|
Id: integration.Id,
|
||||||
|
@ -79,7 +79,11 @@
|
|||||||
|
|
||||||
let res = await axios.post(`${API_URL}/api/${guildId}/integrations/${integrationId}`, data);
|
let res = await axios.post(`${API_URL}/api/${guildId}/integrations/${integrationId}`, data);
|
||||||
if (res.status !== 204) {
|
if (res.status !== 204) {
|
||||||
notifyError(res.data.error);
|
if (res.data.client_error) {
|
||||||
|
notifyError(`${res.data.error}: ${res.data.client_error}`);
|
||||||
|
} else {
|
||||||
|
notifyError(res.data.error);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user