Skip to content

Commit db64ec6

Browse files
committed
feat: client-side input validation for platform urls
1 parent 72103ed commit db64ec6

File tree

3 files changed

+68
-9
lines changed

3 files changed

+68
-9
lines changed

apps/frontend/src/components/ui/app/extensions/Platformsmodal.vue

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
label="Product URL"
3030
name="builtbybit_url"
3131
type="url"
32-
:rules="[rules.required(), rules.url()]"
32+
:rules="[
33+
validationRules.required(),
34+
validationRules.url(),
35+
validationRules.platformUrl('BUILTBYBIT'),
36+
]"
3337
:required="localEnabled.BUILTBYBIT"
3438
placeholder="https://builtbybit.com/resources/..."
3539
class="mt-3"
@@ -62,7 +66,11 @@
6266
label="Product URL"
6367
name="sourcexchange_url"
6468
type="url"
65-
:rules="[rules.required(), rules.url()]"
69+
:rules="[
70+
validationRules.required(),
71+
validationRules.url(),
72+
validationRules.platformUrl('SOURCEXCHANGE'),
73+
]"
6674
:required="localEnabled.SOURCEXCHANGE"
6775
placeholder="https://sourcexchange.net/products/..."
6876
class="mt-3"
@@ -95,7 +103,11 @@
95103
label="Repository URL"
96104
name="github_url"
97105
type="url"
98-
:rules="[rules.required(), rules.url()]"
106+
:rules="[
107+
validationRules.required(),
108+
validationRules.url(),
109+
validationRules.platformUrl('GITHUB'),
110+
]"
99111
:required="localEnabled.GITHUB"
100112
placeholder="https://github.com/user/repo"
101113
class="mt-3"
@@ -118,7 +130,7 @@
118130
</template>
119131

120132
<script setup lang="ts">
121-
const { rules } = useFormValidation()
133+
const { rules: validationRules } = useFormValidation()
122134
123135
interface Props {
124136
isOpen: boolean
@@ -177,23 +189,29 @@ const handleClose = () => {
177189
localUrls.value.BUILTBYBIT &&
178190
fieldValidation.value.builtbybit_url !== false
179191
) {
180-
platforms.BUILTBYBIT = localUrls.value.BUILTBYBIT
192+
platforms.BUILTBYBIT = localUrls.value.BUILTBYBIT.endsWith('/')
193+
? localUrls.value.BUILTBYBIT.slice(0, -1)
194+
: localUrls.value.BUILTBYBIT
181195
}
182196
183197
if (
184198
localEnabled.value.SOURCEXCHANGE &&
185199
localUrls.value.SOURCEXCHANGE &&
186200
fieldValidation.value.sourcexchange_url !== false
187201
) {
188-
platforms.SOURCEXCHANGE = localUrls.value.SOURCEXCHANGE
202+
platforms.SOURCEXCHANGE = localUrls.value.SOURCEXCHANGE.endsWith('/')
203+
? localUrls.value.SOURCEXCHANGE.slice(0, -1)
204+
: localUrls.value.SOURCEXCHANGE
189205
}
190206
191207
if (
192208
localEnabled.value.GITHUB &&
193209
localUrls.value.GITHUB &&
194210
fieldValidation.value.github_url !== false
195211
) {
196-
platforms.GITHUB = localUrls.value.GITHUB
212+
platforms.GITHUB = localUrls.value.GITHUB.endsWith('/')
213+
? localUrls.value.GITHUB.slice(0, -1)
214+
: localUrls.value.GITHUB
197215
}
198216
199217
emit('save', platforms)

apps/frontend/src/composables/useFormValidation.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,46 @@ export const useFormValidation = () => {
144144
message || 'Identifier is already taken by a published extension',
145145
trigger: 'blur',
146146
}),
147+
148+
platformUrl: (
149+
platform: 'GITHUB' | 'BUILTBYBIT' | 'SOURCEXCHANGE',
150+
message?: string
151+
): ValidationRule => ({
152+
validator: (value: string) => {
153+
if (!value) {
154+
return false
155+
}
156+
157+
try {
158+
const url = new URL(value)
159+
const hostname = url.hostname
160+
161+
switch (platform) {
162+
case 'GITHUB':
163+
return (
164+
hostname === 'github.com' &&
165+
url.pathname.split('/').filter(Boolean).length >= 2
166+
)
167+
case 'BUILTBYBIT':
168+
return (
169+
hostname === 'builtbybit.com' &&
170+
/^\/resources\/[\w.-]+(\.\d+)?\/?$/.test(url.pathname)
171+
)
172+
case 'SOURCEXCHANGE':
173+
return (
174+
hostname === 'www.sourcexchange.net' &&
175+
/^\/products\/[\w.-]+\/?$/.test(url.pathname)
176+
)
177+
default:
178+
return false
179+
}
180+
} catch {
181+
return false
182+
}
183+
},
184+
message: message || 'Please enter a valid platform URL',
185+
trigger: 'blur',
186+
}),
147187
}
148188

149189
const debounce = (func: Function, delay: number) => {

apps/frontend/src/pages/app/extensions/[id].vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@
310310
class="font-mono"
311311
:supports-images="true"
312312
:rows="10"
313-
:placeholder="`(ノ*・_・)ノ \\\n**markdown** is supported`"
313+
:placeholder="`\`[ > <]\` \\\n**markdown** is supported`"
314314
:richtext="true"
315315
/>
316316
</div>
@@ -320,7 +320,8 @@
320320
class="border-neutral-700 bg-neutral-950 p-4 xl:-mb-[2px] xl:border-b"
321321
>
322322
<template v-if="!form.description || form.description == ''">
323-
<p>(ノ*・_・)ノ</p>
323+
<!-- prettier-ignore -->
324+
<pre><ProseCode>[ > <]</ProseCode></pre>
324325
<p><b>markdown</b> is supported</p>
325326
</template>
326327
<client-only v-else>

0 commit comments

Comments
 (0)