Fix #834: Do not remove trailing slash from Safe app url (#839)

* Fix #834: Do not remove trailing slash from Safe app url

* Use correct url in id

* prevent adding existing App

* Limiting App name length persisted in origin field

Co-authored-by: Richard Meissner <richard@gnosis.io>
Co-authored-by: nicosampler <nf.dominguez.87@gmail.com>
This commit is contained in:
Richard Meissner 2020-05-07 13:47:51 +02:00 committed by GitHub
parent a98652abe4
commit 051080c239
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 15 deletions

View File

@ -13,6 +13,7 @@ import GnoForm from '~/components/forms/GnoForm'
import { required } from '~/components/forms/validator' import { required } from '~/components/forms/validator'
import Img from '~/components/layout/Img' import Img from '~/components/layout/Img'
import appsIconSvg from '~/routes/safe/components/Transactions/TxsTable/TxType/assets/appsIcon.svg' import appsIconSvg from '~/routes/safe/components/Transactions/TxsTable/TxType/assets/appsIcon.svg'
import { isValid as isURLValid } from '~/utils/url'
const FORM_ID = 'add-apps-form' const FORM_ID = 'add-apps-form'
@ -51,9 +52,7 @@ type Props = {
} }
const urlValidator = (value: string) => { const urlValidator = (value: string) => {
return /(?:^|[ \t])((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/gm.test(value) return isURLValid(value) ? undefined : 'Please, provide a valid url'
? undefined
: 'Please, provide a valid url'
} }
const composeValidatorsApps = (...validators: Function[]): FieldValidator => (value: Field, values, meta) => { const composeValidatorsApps = (...validators: Function[]): FieldValidator => (value: Field, values, meta) => {
@ -92,7 +91,15 @@ const ManageApps = ({ appList, onAppAdded, onAppToggle }: Props) => {
} }
const uniqueAppValidator = (value) => { const uniqueAppValidator = (value) => {
const exists = appList.find((a) => a.url === value.trim()) const exists = appList.find((a) => {
try {
const currentUrl = new URL(a.url)
const newUrl = new URL(value)
return currentUrl.href === newUrl.href
} catch (error) {
return 'There was a problem trying to validate the URL existence.'
}
})
return exists ? 'This app is already registered.' : undefined return exists ? 'This app is already registered.' : undefined
} }

View File

@ -29,40 +29,45 @@ export const getAppInfoFromUrl = async (appUrl: string) => {
return res return res
} }
let cleanedUpAppUrl = appUrl.trim() res.url = appUrl.trim()
if (cleanedUpAppUrl.substr(-1) === '/') { let noTrailingSlashUrl = res.url
cleanedUpAppUrl = cleanedUpAppUrl.substr(0, cleanedUpAppUrl.length - 1) if (noTrailingSlashUrl.substr(-1) === '/') {
res.url = cleanedUpAppUrl noTrailingSlashUrl = noTrailingSlashUrl.substr(0, noTrailingSlashUrl.length - 1)
} }
try { try {
const appInfo = await axios.get(`${cleanedUpAppUrl}/manifest.json`) const appInfo = await axios.get(`${noTrailingSlashUrl}/manifest.json`)
// verify imported app fulfil safe requirements // verify imported app fulfil safe requirements
if (!appInfo || !appInfo.data || !appInfo.data.name || !appInfo.data.description) { if (!appInfo || !appInfo.data || !appInfo.data.name || !appInfo.data.description) {
throw Error('The app does not fulfil the structure required.') throw Error('The app does not fulfil the structure required.')
} }
// the DB origin field has a limit of 100 characters
const originFieldSize = 100
const jsonDataLength = 20
const remainingSpace = originFieldSize - res.url.length - jsonDataLength
res = { res = {
...res, ...res,
...appInfo.data, ...appInfo.data,
id: JSON.stringify({ url: cleanedUpAppUrl, name: appInfo.data.name }), id: JSON.stringify({ url: res.url, name: appInfo.data.name.substring(0, remainingSpace) }),
error: false, error: false,
} }
if (appInfo.data.iconPath) { if (appInfo.data.iconPath) {
try { try {
const iconInfo = await axios.get(`${cleanedUpAppUrl}/${appInfo.data.iconPath}`) const iconInfo = await axios.get(`${noTrailingSlashUrl}/${appInfo.data.iconPath}`, { timeout: 1000 * 10 })
if (/image\/\w/gm.test(iconInfo.headers['content-type'])) { if (/image\/\w/gm.test(iconInfo.headers['content-type'])) {
res.iconUrl = `${cleanedUpAppUrl}/${appInfo.data.iconPath}` res.iconUrl = `${noTrailingSlashUrl}/${appInfo.data.iconPath}`
} }
} catch (error) { } catch (error) {
console.error(`It was not possible to fetch icon from app ${cleanedUpAppUrl}`) console.error(`It was not possible to fetch icon from app ${res.url}`)
} }
} }
return res return res
} catch (error) { } catch (error) {
console.error(`It was not possible to fetch app from ${cleanedUpAppUrl}: ${error.message}`) console.error(`It was not possible to fetch app from ${res.url}: ${error.message}`)
return res return res
} }
} }