Add server-side validation for snippet names
This commit is contained in:
parent
74a2b54880
commit
2d12a1a8fa
|
@ -13,13 +13,13 @@
|
||||||
import { snippets } from "stores/builder"
|
import { snippets } from "stores/builder"
|
||||||
import { getSequentialName } from "helpers/duplicate"
|
import { getSequentialName } from "helpers/duplicate"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
import { ValidSnippetNameRegex } from "@budibase/shared-core"
|
||||||
|
|
||||||
export let snippet
|
export let snippet
|
||||||
|
|
||||||
export const show = () => drawer.show()
|
export const show = () => drawer.show()
|
||||||
export const hide = () => drawer.hide()
|
export const hide = () => drawer.hide()
|
||||||
|
|
||||||
const roughValidNameRegex = /^[_$A-Z\xA0-\uFFFF][_$A-Z0-9\xA0-\uFFFF]*$/i
|
|
||||||
const firstCharNumberRegex = /^[0-9].*$/
|
const firstCharNumberRegex = /^[0-9].*$/
|
||||||
|
|
||||||
let drawer
|
let drawer
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
drawer.hide()
|
drawer.hide()
|
||||||
notifications.success(`Snippet ${newSnippet.name} saved`)
|
notifications.success(`Snippet ${newSnippet.name} saved`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error saving snippet")
|
notifications.error(error.message || "Error saving snippet")
|
||||||
}
|
}
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
@ -69,21 +69,16 @@
|
||||||
if (!name?.length) {
|
if (!name?.length) {
|
||||||
return "Name is required"
|
return "Name is required"
|
||||||
}
|
}
|
||||||
if (firstCharNumberRegex.test(name)) {
|
|
||||||
return "Can't start with a number"
|
|
||||||
}
|
|
||||||
if (!roughValidNameRegex.test(name)) {
|
|
||||||
return "No special characters or spaces"
|
|
||||||
}
|
|
||||||
if (snippets.some(snippet => snippet.name === name)) {
|
if (snippets.some(snippet => snippet.name === name)) {
|
||||||
return "That name is already in use"
|
return "That name is already in use"
|
||||||
}
|
}
|
||||||
const js = `(function ${name}(){return true})()`
|
if (firstCharNumberRegex.test(name)) {
|
||||||
try {
|
return "Can't start with a number"
|
||||||
return eval(js) === true ? null : "Invalid name"
|
|
||||||
} catch (error) {
|
|
||||||
return "Invalid name"
|
|
||||||
}
|
}
|
||||||
|
if (!ValidSnippetNameRegex.test(name)) {
|
||||||
|
return "No special characters or spaces"
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { auth, permissions } from "@budibase/backend-core"
|
||||||
import { DataSourceOperation } from "../../../constants"
|
import { DataSourceOperation } from "../../../constants"
|
||||||
import { WebhookActionType } from "@budibase/types"
|
import { WebhookActionType } from "@budibase/types"
|
||||||
import Joi from "joi"
|
import Joi from "joi"
|
||||||
|
import { ValidSnippetNameRegex } from "@budibase/shared-core"
|
||||||
|
|
||||||
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
||||||
const OPTIONAL_NUMBER = Joi.number().optional().allow(null)
|
const OPTIONAL_NUMBER = Joi.number().optional().allow(null)
|
||||||
|
@ -226,6 +227,21 @@ export function applicationValidator(opts = { isCreate: true }) {
|
||||||
base.name = appNameValidator.optional()
|
base.name = appNameValidator.optional()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const snippetValidator = Joi.array()
|
||||||
|
.optional()
|
||||||
|
.items(
|
||||||
|
Joi.object({
|
||||||
|
name: Joi.string()
|
||||||
|
.pattern(new RegExp(ValidSnippetNameRegex))
|
||||||
|
.error(
|
||||||
|
new Error(
|
||||||
|
"Snippet name cannot include spaces or special characters, and cannot start with a number"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
code: OPTIONAL_STRING,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
return auth.joiValidator.body(
|
return auth.joiValidator.body(
|
||||||
Joi.object({
|
Joi.object({
|
||||||
_id: OPTIONAL_STRING,
|
_id: OPTIONAL_STRING,
|
||||||
|
@ -235,6 +251,7 @@ export function applicationValidator(opts = { isCreate: true }) {
|
||||||
template: Joi.object({
|
template: Joi.object({
|
||||||
templateString: OPTIONAL_STRING,
|
templateString: OPTIONAL_STRING,
|
||||||
}).unknown(true),
|
}).unknown(true),
|
||||||
|
snippets: snippetValidator,
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ export enum BuilderSocketEvent {
|
||||||
export const SocketSessionTTL = 60
|
export const SocketSessionTTL = 60
|
||||||
export const ValidQueryNameRegex = /^[^()]*$/
|
export const ValidQueryNameRegex = /^[^()]*$/
|
||||||
export const ValidColumnNameRegex = /^[_a-zA-Z0-9\s]*$/g
|
export const ValidColumnNameRegex = /^[_a-zA-Z0-9\s]*$/g
|
||||||
|
export const ValidSnippetNameRegex = /^[a-z-_][a-z0-9-_]*$/i
|
||||||
|
|
||||||
export const REBOOT_CRON = "@reboot"
|
export const REBOOT_CRON = "@reboot"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue