Skip to content

Commit

Permalink
fix: add url(protocol) validation to add callback mutation (#3513)
Browse files Browse the repository at this point in the history
  • Loading branch information
dolcalmi authored Nov 8, 2023
1 parent fd0d82b commit ecd1f46
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 14 deletions.
2 changes: 1 addition & 1 deletion core/api/.env
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export KRATOS_PG_HOST="localhost"
export KRATOS_PG_PORT="5433"

export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
# TODO: rename to OTEL_SERVICE_NAME
# TODO: rename to OTEL_SERVICE_NAME
# https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_service_name
export TRACING_SERVICE_NAME="galoy-dev"

Expand Down
2 changes: 1 addition & 1 deletion core/api/dev/svix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,4 @@ curl --silent -X 'GET' \
-H "Authorization: Bearer $SVIX_SECRET" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' | jq
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const CallbackEndpointAddInput = GT.Input({
const CallbackEndpointAdd = GT.Field<
null,
GraphQLPublicContextAuth,
{ input: { url: string } }
{ input: { url: string | Error } }
>({
extensions: {
complexity: 120,
Expand All @@ -26,6 +26,9 @@ const CallbackEndpointAdd = GT.Field<
},
resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => {
const { url } = args.input
if (url instanceof Error) {
return { errors: [{ message: url.message }] }
}

const result = await Callback.addEndpoint({
accountId: domainAccount.id,
Expand Down
26 changes: 22 additions & 4 deletions core/api/src/graphql/public/types/scalar/endpoint-url.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import { URL } from "url"

import { GT } from "@/graphql/index"
import { InputValidationError } from "@/graphql/error"

const EndpointUrl = GT.Scalar({
name: "EndpointUrl",
description: "Url that will be fetched on events for the account",
serialize(value) {
if (typeof value !== "string") {
return "Invalid value for EndpointUrl"
parseValue(value) {
if (typeof value === "string") {
return validUrlValue(value)
}
return value
return new InputValidationError({ message: "Invalid type for EndpointUrl" })
},
parseLiteral(ast) {
if (ast.kind === GT.Kind.STRING) {
return validUrlValue(ast.value)
}
return new InputValidationError({ message: "Invalid type for EndpointUrl" })
},
})

function validUrlValue(value: string) {
try {
new URL(value)
return value
} catch (error) {
return new InputValidationError({ message: "Invalid value for EndpointUrl" })
}
}

export default EndpointUrl
33 changes: 26 additions & 7 deletions core/api/src/services/svix/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { ApplicationIn, Svix } from "svix"

import { SvixError, UnknownSvixError } from "./errors"

import { baseLogger } from "@/services/logger"
import { InvalidUrlError } from "@/domain/callback/errors"
import { parseErrorMessageFromUnknown } from "@/domain/shared"

import {
addAttributesToCurrentSpan,
wrapAsyncFunctionsToRunInSpan,
} from "@/services/tracing"
import { baseLogger } from "@/services/logger"

function prefixObjectKeys(
obj: Record<string, string>,
Expand Down Expand Up @@ -56,7 +58,7 @@ export const CallbackService = (config: SvixConfig) => {
if ((err as SvixError).code === 409) {
// we create app on the fly, so we are expecting this error and can ignore it
} else {
return new UnknownSvixError(err)
return handleCommonErrors(err)
}
}
}
Expand Down Expand Up @@ -91,7 +93,7 @@ export const CallbackService = (config: SvixConfig) => {
baseLogger.info({ res }, `message sent successfully to ${accountCallbackId}`)
return res
} catch (err) {
return new UnknownSvixError(err)
return handleCommonErrors(err)
}
}

Expand All @@ -106,7 +108,7 @@ export const CallbackService = (config: SvixConfig) => {
const res = await svix.authentication.appPortalAccess(accountCallbackId, {})
return res
} catch (err) {
return new UnknownSvixError(err)
return handleCommonErrors(err)
}
}

Expand All @@ -128,7 +130,7 @@ export const CallbackService = (config: SvixConfig) => {
})
return res
} catch (err) {
return new UnknownSvixError(err)
return handleCommonErrors(err)
}
}

Expand All @@ -142,7 +144,7 @@ export const CallbackService = (config: SvixConfig) => {
const res = await svix.endpoint.list(accountCallbackId)
return res.data.map((endpoint) => ({ id: endpoint.id, url: endpoint.url }))
} catch (err) {
return new UnknownSvixError(err)
return handleCommonErrors(err)
}
}

Expand All @@ -159,7 +161,7 @@ export const CallbackService = (config: SvixConfig) => {
await svix.endpoint.delete(accountCallbackId, endpointId)
return true
} catch (err) {
return new UnknownSvixError(err)
return handleCommonErrors(err)
}
}

Expand All @@ -168,3 +170,20 @@ export const CallbackService = (config: SvixConfig) => {
fns: { sendMessage, getWebsocketPortal, addEndpoint, listEndpoints, deleteEndpoint },
})
}

const handleCommonErrors = (err: Error | string | unknown) => {
const errMsg = parseErrorMessageFromUnknown(err)

const match = (knownErrDetail: RegExp): boolean => knownErrDetail.test(errMsg)

switch (true) {
case match(KnownSvixErrorMessages.InvalidHttpsUrl):
return new InvalidUrlError("URL must be https")

default:
return new UnknownSvixError(errMsg)
}
}
export const KnownSvixErrorMessages = {
InvalidHttpsUrl: /endpoint_https_only/,
} as const

0 comments on commit ecd1f46

Please sign in to comment.