Merge pull request #46689 from nextcloud/fix/46688/validate-new-file-name

fix(files): validate input when creating file/directory
pull/44788/head
Ferdinand Thiessen 4 months ago committed by GitHub
commit b83948392f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      apps/files/src/components/FileEntry/FileEntryName.vue
  2. 62
      apps/files/src/components/NewNodeDialog.vue
  3. 4
      dist/files-init.js
  4. 2
      dist/files-init.js.map
  5. 4
      dist/files-main.js
  6. 2
      dist/files-main.js.map

@ -211,23 +211,22 @@ export default defineComponent({
isFileNameValid(name: string) {
const trimmedName = name.trim()
const char = trimmedName.indexOf('/') !== -1
? '/'
: forbiddenCharacters.find((char) => trimmedName.includes(char))
if (trimmedName === '.' || trimmedName === '..') {
throw new Error(t('files', '"{name}" is an invalid file name.', { name }))
} else if (trimmedName.length === 0) {
throw new Error(t('files', 'File name cannot be empty.'))
} else if (trimmedName.indexOf('/') !== -1) {
throw new Error(t('files', '"/" is not allowed inside a file name.'))
} else if (char) {
throw new Error(t('files', '"{char}" is not allowed inside a file name.', { char }))
} else if (trimmedName.match(window.OC.config.blacklist_files_regex)) {
throw new Error(t('files', '"{name}" is not an allowed filetype.', { name }))
} else if (this.checkIfNodeExists(name)) {
throw new Error(t('files', '{newName} already exists.', { newName: name }))
}
const char = forbiddenCharacters.find((char) => trimmedName.includes(char))
if (char) {
throw new Error(t('files', '"{char}" is not allowed inside a file name.', { char }))
}
return true
},

@ -20,10 +20,12 @@
<form @submit.prevent="onCreate">
<NcTextField ref="input"
data-cy-files-new-node-dialog-input
class="dialog__input"
:error="!isUniqueName"
:helper-text="errorMessage"
:label="label"
:value.sync="localDefaultName" />
:value.sync="localDefaultName"
@keyup="checkInputValidity" />
</form>
</NcDialog>
</template>
@ -34,15 +36,19 @@ import type { PropType } from 'vue'
import { defineComponent } from 'vue'
import { translate as t } from '@nextcloud/l10n'
import { getUniqueName } from '@nextcloud/files'
import { loadState } from '@nextcloud/initial-state'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
import logger from '../logger.js'
interface ICanFocus {
focus: () => void
}
const forbiddenCharacters = loadState<string[]>('files', 'forbiddenCharacters', [])
export default defineComponent({
name: 'NewNodeDialog',
components: {
@ -147,6 +153,60 @@ export default defineComponent({
this.$emit('close', null)
}
},
/**
* Check if the file name is valid and update the
* input validity using browser's native validation.
* @param event the keyup event
*/
checkInputValidity(event: KeyboardEvent) {
const input = event.target as HTMLInputElement
const newName = this.localDefaultName.trim?.() || ''
logger.debug('Checking input validity', { newName })
try {
this.isFileNameValid(newName)
input.setCustomValidity('')
input.title = ''
} catch (e) {
if (e instanceof Error) {
input.setCustomValidity(e.message)
input.title = e.message
} else {
input.setCustomValidity(t('files', 'Invalid file name'))
}
} finally {
input.reportValidity()
}
},
isFileNameValid(name: string) {
const trimmedName = name.trim()
const char = trimmedName.indexOf('/') !== -1
? '/'
: forbiddenCharacters.find((char) => trimmedName.includes(char))
if (trimmedName === '.' || trimmedName === '..') {
throw new Error(t('files', '"{name}" is an invalid file name.', { name }))
} else if (trimmedName.length === 0) {
throw new Error(t('files', 'File name cannot be empty.'))
} else if (char) {
throw new Error(t('files', '"{char}" is not allowed inside a file name.', { char }))
} else if (trimmedName.match(window.OC.config.blacklist_files_regex)) {
throw new Error(t('files', '"{name}" is not an allowed filetype.', { name }))
}
return true
},
},
})
</script>
<style lang="scss" scoped>
.dialog__input {
:deep(input:invalid) {
// Show red border on invalid input
border-color: var(--color-error);
color: red;
}
}
</style>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save