Skip to content

Commit

Permalink
Merge pull request #654 from pokt-foundation/app-secret-key-security-bug
Browse files Browse the repository at this point in the history
App secret key security bug
  • Loading branch information
RabeeAbuBaker authored Jun 28, 2024
2 parents 8dc9a69 + 3786ac2 commit f8db47c
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 87 deletions.
2 changes: 1 addition & 1 deletion app/components/ChainSandbox/ChainSandbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const ChainSandbox = ({ apps, chains }: ChainSandboxProps) => {
} = state

const appId = selectedApp?.id
const secretKey = selectedApp?.settings.secretKey as string
const secretKey = selectedApp?.settings?.secretKey as string

const chainUrl = useMemo(
() => `${getAppEndpointUrl(selectedChain, appId)}${chainRestPath || ""}`.trim(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useChainSandboxContext from "~/components/ChainSandbox/state"
const ChainSandboxHeaders = () => {
const { state, dispatch } = useChainSandboxContext()
const { includeSecretKey, requestHeaders, selectedApp } = state
const secretKey = selectedApp?.settings.secretKey as string
const secretKey = selectedApp?.settings?.secretKey as string

useEffect(() => {
dispatch({
Expand All @@ -21,26 +21,28 @@ const ChainSandboxHeaders = () => {
return (
<Stack gap={12}>
<Title order={6}>Header</Title>
<Group>
<Checkbox
checked={includeSecretKey}
label="Include secret key"
onChange={(event) =>
dispatch({
type: "SET_INCLUDE_SECRET_KEY",
payload: event.currentTarget.checked,
})
}
/>
<PasswordInput
readOnly
aria-label="Secret Key"
style={{
flex: 1,
}}
value={secretKey}
/>
</Group>
{secretKey ? (
<Group>
<Checkbox
checked={includeSecretKey}
label="Include secret key"
onChange={(event) =>
dispatch({
type: "SET_INCLUDE_SECRET_KEY",
payload: event.currentTarget.checked,
})
}
/>
<PasswordInput
readOnly
aria-label="Secret Key"
style={{
flex: 1,
}}
value={secretKey}
/>
</Group>
) : null}

<JsonEditor readOnly value={JSON.stringify(requestHeaders, null, " ")} />
</Stack>
Expand Down
148 changes: 92 additions & 56 deletions app/models/portal/queries.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ query getUserAccount(
notifications {
notificationType
notificationSettings {
accountID
type
active
destination
trigger
events {
full
half
Expand All @@ -72,71 +67,33 @@ query getUserAccount(
roleName
accepted
}
partnerChainIDs
partnerThroughputLimit
partnerAppLimit
integrations {
stripeSubscriptionID
covalentAPIKeyFree
covalentAPIKeyPaid
}
createdAt
updatedAt
deleted
portalApps {
id
name
accountID
appEmoji
description
settings {
appID
environment
secretKey
secretKeyRequired
favoritedChainIDs
}
whitelists {
origins
userAgents
blockchains
contracts {
blockchainID
contracts
}
methods {
blockchainID
methods
}
}
aats {
protocolAppID
aat {
id
appID
publicKey
address
clientPublicKey
signature
version
}
}
createdAt
updatedAt
deleted
}
plan {
id
type
chainIDs
monthlyRelayLimit
throughputLimit
appLimit
dailyLimit
}
}
}

query getUserAccountUsers($accountID: ID!, $accepted: Boolean!) {
getUserAccount(accountID: $accountID, accepted: $accepted) {
users {
id
email
roleName
accepted
}
}
}

query getUserAccountStripeId($accountID: ID!) {
getUserAccount(accountID: $accountID, accepted: true) {
id
Expand Down Expand Up @@ -233,13 +190,92 @@ query getUserPortalApp($portalAppID: ID!, $accountID: ID!) {
}
}

query getUserPortalApps($sortOrder: SortOrder) {
getUserPortalApps(sortOrder: $sortOrder) {
query getMemberUserPortalApp($portalAppID: ID!, $accountID: ID!) {
getUserPortalApp(portalAppID: $portalAppID, accountID: $accountID) {
id
name
accountID
appEmoji
description
settings {
appID
environment
favoritedChainIDs
}
whitelists {
origins
userAgents
blockchains
contracts {
blockchainID
contracts
}
methods {
blockchainID
methods
}
}
aats {
protocolAppID
aat {
id
appID
publicKey
address
clientPublicKey
signature
version
}
}
createdAt
updatedAt
deleted
}
}

query getUserAccountPortalApps(
$accountID: ID!
$accepted: Boolean!
$sortOrder: SortOrder
$roleNameFilters: [RoleName!]
) {
getUserAccount(
accountID: $accountID
accepted: $accepted
sortOrder: $sortOrder
roleNameFilters: $roleNameFilters
) {
id
portalApps {
id
name
appEmoji
settings {
secretKey
secretKeyRequired
}
}
}
}

query getMemberUserAccountPortalApps(
$accountID: ID!
$accepted: Boolean!
$sortOrder: SortOrder
$roleNameFilters: [RoleName!]
) {
getUserAccount(
accountID: $accountID
accepted: $accepted
sortOrder: $sortOrder
roleNameFilters: $roleNameFilters
) {
id
portalApps {
id
name
appEmoji
}
}
}

Expand Down
21 changes: 19 additions & 2 deletions app/routes/account.$accountId.$appId/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import useActionNotification from "~/hooks/useActionNotification"
import { initPortalClient } from "~/models/portal/portal.server"
import { Blockchain, PortalApp, RoleName } from "~/models/portal/sdk"
import { ActionDataStruct } from "~/types/global"
import { getUserAccountRole } from "~/utils/accountUtils"
import { getErrorMessage } from "~/utils/catchError"
import { triggerAppActionNotification } from "~/utils/notifications.server"
import { seo_title_append } from "~/utils/seo"
Expand All @@ -41,11 +42,27 @@ export const loader: LoaderFunction = async ({ request, params }) => {
invariant(appId, "app id not found")

try {
const getUserPortalAppResponse = await portal.getUserPortalApp({
portalAppID: appId,
const getUserAccountUsersResponse = await portal.getUserAccountUsers({
accountID: accountId,
accepted: true,
})

const userRole = getUserAccountRole(
getUserAccountUsersResponse.getUserAccount.users,
user.user.portalUserID,
)

const getUserPortalAppResponse =
userRole === RoleName.Member
? await portal.getMemberUserPortalApp({
portalAppID: appId,
accountID: accountId,
})
: await portal.getUserPortalApp({
portalAppID: appId,
accountID: accountId,
})

return json<AppIdLoaderData>({
app: getUserPortalAppResponse.getUserPortalApp as PortalApp,
})
Expand Down
57 changes: 52 additions & 5 deletions app/routes/account.$accountId.sandbox/route.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { MetaFunction } from "@remix-run/node"
import { useOutletContext } from "@remix-run/react"
import { json, LoaderFunction, MetaFunction } from "@remix-run/node"
import { useLoaderData, useOutletContext } from "@remix-run/react"
import invariant from "tiny-invariant"
import { ErrorBoundaryView } from "~/components/ErrorBoundaryView"
import { PortalApp } from "~/models/portal/sdk"
import { initPortalClient } from "~/models/portal/portal.server"
import { PortalApp, RoleName } from "~/models/portal/sdk"
import { AccountIdLoaderData } from "~/routes/account.$accountId/route"
import SandboxView from "~/routes/account.$accountId.sandbox/view"
import { getUserAccountRole } from "~/utils/accountUtils"
import { getErrorMessage } from "~/utils/catchError"
import { seo_title_append } from "~/utils/seo"
import { requireUser } from "~/utils/user.server"

export const meta: MetaFunction = () => {
return [
Expand All @@ -14,13 +19,55 @@ export const meta: MetaFunction = () => {
]
}

type SandboxLoaderData = {
accountApps: PortalApp[]
}

export const loader: LoaderFunction = async ({ request, params }) => {
const user = await requireUser(request)
const portal = initPortalClient({ token: user.accessToken })

const { accountId } = params
invariant(accountId, "account id not found")

try {
const getUserAccountUsersResponse = await portal.getUserAccountUsers({
accountID: accountId,
accepted: true,
})

const userRole = getUserAccountRole(
getUserAccountUsersResponse.getUserAccount.users,
user.user.portalUserID,
)

const getUserAccountPortalAppsResponse =
userRole === RoleName.Member
? await portal.getMemberUserAccountPortalApps({
accountID: accountId,
accepted: true,
})
: await portal.getUserAccountPortalApps({ accountID: accountId, accepted: true })

return json<SandboxLoaderData>({
accountApps: getUserAccountPortalAppsResponse.getUserAccount
.portalApps as PortalApp[],
})
} catch (error) {
throw new Response(getErrorMessage(error), {
status: 500,
})
}
}

export default function Sandbox() {
const { account, blockchains, userRole } = useOutletContext<AccountIdLoaderData>()
const { blockchains, userRole } = useOutletContext<AccountIdLoaderData>()
const { accountApps } = useLoaderData<SandboxLoaderData>()

return (
<SandboxView
blockchains={blockchains}
portalApps={account.portalApps as PortalApp[]}
portalApps={accountApps as PortalApp[]}
userRole={userRole}
/>
)
Expand Down
3 changes: 2 additions & 1 deletion app/routes/account_.$accountId.app-limit-exceeded/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import invariant from "tiny-invariant"
import { EmptyState } from "~/components/EmptyState"
import { ErrorBoundaryView } from "~/components/ErrorBoundaryView"
import { initPortalClient } from "~/models/portal/portal.server"
import { Account } from "~/models/portal/sdk"
import { isAccountWithinAppLimit } from "~/utils/accountUtils"
import { getErrorMessage } from "~/utils/catchError"
import { seo_title_append } from "~/utils/seo"
Expand Down Expand Up @@ -34,7 +35,7 @@ export const loader: LoaderFunction = async ({ request, params }) => {
if (!getUserAccountResponse) {
return redirect(`/account/${params.accountId}`)
}
const userAccount = getUserAccountResponse.getUserAccount
const userAccount = getUserAccountResponse.getUserAccount as Account
const canCreateApp = isAccountWithinAppLimit(userAccount)
if (canCreateApp) {
return redirect(`/account/${params.accountId}`)
Expand Down
2 changes: 1 addition & 1 deletion app/routes/account_.$accountId.create/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const loader: LoaderFunction = async ({ request, params }) => {
return redirect(`/account/${params.accountId}`)
}

const userAccount = getUserAccountResponse.getUserAccount
const userAccount = getUserAccountResponse.getUserAccount as Account
const userRole = getUserAccountRole(userAccount.users, user.user.portalUserID)

if (!userRole || userRole === RoleName.Member) {
Expand Down

0 comments on commit f8db47c

Please sign in to comment.