Skip to content

Commit

Permalink
fix: add additional url validation
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranjamie committed Nov 5, 2020
1 parent 5c1be36 commit 1b67fbd
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 5 deletions.
2 changes: 2 additions & 0 deletions packages/app/src/common/track.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ScreenPaths } from '@store/onboarding/types';
import { DecodedAuthRequest } from './dev/types';
import { event, page, setConfig, Providers } from '@blockstack/stats';
import { validUrl } from './validate-url';

export const SECRET_KEY_FAQ_WHERE = 'View Secret Key FAQ (Where)';
export const SECRET_KEY_FAQ_LOSE = 'View Secret Key FAQ (Lose)';
Expand Down Expand Up @@ -62,6 +63,7 @@ export const doTrackScreenChange = (
if (titleNameMap[screen]) {
document.title = titleNameMap[screen];
}
if (decodedAuthRequest && !validUrl(decodedAuthRequest.redirect_uri)) return;
const appURL = decodedAuthRequest ? new URL(decodedAuthRequest?.redirect_uri) : null;
// eslint-disable-next-line @typescript-eslint/no-misused-promises
setTimeout(async () => {
Expand Down
9 changes: 6 additions & 3 deletions packages/app/src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DecodedAuthRequest } from './dev/types';
import { wordlists } from 'bip39';
import { FinishedTxData, shouldUsePopup } from '@stacks/connect';
import DOMPurify from 'dompurify';
import { validUrl } from './validate-url';

export const getAuthRequestParam = () => {
const { hash } = document.location;
Expand Down Expand Up @@ -57,8 +57,11 @@ export const finalizeAuthResponse = ({
authRequest,
authResponse,
}: FinalizeAuthParams) => {
const sanitizedUri = DOMPurify.sanitize(decodedAuthRequest.redirect_uri);
const redirect = `${sanitizedUri}?authResponse=${authResponse}`;
const dangerousUri = decodedAuthRequest.redirect_uri;
if (!validUrl(dangerousUri) || dangerousUri.includes('javascript')) {
throw new Error('Cannot proceed auth with malformed url');
}
const redirect = `${dangerousUri}?authResponse=${authResponse}`;
if (!shouldUsePopup()) {
document.location.href = redirect;
return;
Expand Down
13 changes: 13 additions & 0 deletions packages/app/src/common/validate-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// https://stackoverflow.com/a/5717133/1141891
export function validUrl(str: string) {
const pattern = new RegExp(
'^(https?:\\/\\/)?' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
'(\\#[-a-z\\d_]*)?$',
'i'
); // fragment locator
return !!pattern.test(str);
}
4 changes: 4 additions & 0 deletions packages/app/src/pages/connect/choose-account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useAnalytics } from '@common/hooks/use-analytics';
import { ScreenPaths } from '@store/onboarding/types';
import { useWallet } from '@common/hooks/use-wallet';
import { Navigate } from '@components/navigate';
import { validUrl } from '../../common/validate-url';

interface ChooseAccountProps {
next: (identityIndex: number) => void;
Expand Down Expand Up @@ -66,6 +67,9 @@ export const ChooseAccount: React.FC<ChooseAccountProps> = ({ next }) => {
!wallet.walletConfig.hideWarningForReusingIdentity &&
authRequest.scopes.includes('publish_data')
) {
if (validUrl(authRequest && authRequest.redirect_uri)) {
throw new Error('Cannot proceed with malformed url');
}
const url = new URL(authRequest?.redirect_uri);
const apps = wallet.walletConfig.identities[identityIndex]?.apps;
if (apps) {
Expand Down
8 changes: 7 additions & 1 deletion packages/app/src/store/onboarding/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AppState } from '..';
import { validUrl } from '../../common/validate-url';

export const selectCurrentScreen = (state: AppState) => state.onboarding.screen;

Expand Down Expand Up @@ -30,7 +31,12 @@ export const selectFullAppIcon = (state: AppState) => {
let icon = selectAppIcon(state);
const authRequest = selectDecodedAuthRequest(state);
const absoluteURLPattern = /^https?:\/\//i;
if (authRequest?.redirect_uri && icon && !absoluteURLPattern.test(icon)) {
if (
authRequest?.redirect_uri &&
icon &&
!absoluteURLPattern.test(icon) &&
validUrl(authRequest.redirect_uri)
) {
const url = new URL(authRequest.redirect_uri);
url.pathname = icon;
icon = url.toString();
Expand Down
1 change: 0 additions & 1 deletion packages/test-app/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export const App: React.FC = () => {
userSession,
finished: ({ userSession, authResponse }) => {
const userData = userSession.loadUserData();
// setState(() => userData);
setAppPrivateKey(userSession.loadUserData().appPrivateKey);
setAuthResponse(authResponse);
setState({ userData });
Expand Down

0 comments on commit 1b67fbd

Please sign in to comment.