diff --git a/app/background-process/networks/dat/web-api.js b/app/background-process/networks/dat/web-api.js index 4e4a7ec1d3..fce7b07dfb 100644 --- a/app/background-process/networks/dat/web-api.js +++ b/app/background-process/networks/dat/web-api.js @@ -9,6 +9,7 @@ import { queryPermission, requestPermission } from '../../ui/permissions' import { DAT_HASH_REGEX, DAT_QUOTA_DEFAULT_BYTES_ALLOWED, + DAT_VALID_PATH_REGEX, UserDeniedError, PermissionsError, @@ -26,7 +27,8 @@ import { ProtectedFileNotWritableError, FileAlreadyExistsError, FolderAlreadyExistsError, - ParentFolderDoesntExistError + ParentFolderDoesntExistError, + InvalidPathError } from '../../../lib/const' const DEFAULT_TIMEOUT = 5e3 @@ -184,6 +186,7 @@ export default { var senderOrigin = archivesDb.extractOrigin(this.sender.getURL()) yield assertWritePermission(archive, this.sender) yield assertQuotaPermission(archive, senderOrigin, Buffer.byteLength(data, opts.encoding)) + yield assertValidFilePath(filepath) return new Promise((resolve, reject) => { // protected files if (isProtectedFilePath(filepath)) { @@ -231,6 +234,7 @@ export default { createDirectory: m(function * (url) { var { archive, filepath } = lookupArchive(url) yield assertWritePermission(archive, this.sender) + yield assertValidPath(filepath) return new Promise((resolve, reject) => { // protected files if (isProtectedFilePath(filepath)) { @@ -316,7 +320,7 @@ function checkProtocolPermission (sender) { // helper to check if filepath refers to a file that userland is not allowed to edit directly function isProtectedFilePath (filepath) { - return filepath === '/' || filepath === '/dat.json' + return filepath === '/dat.json' } function assertWritePermission (archive, sender) { @@ -356,6 +360,19 @@ function assertQuotaPermission (archive, senderOrigin, byteLength) { }) } +function assertValidFilePath (filepath) { + return co(function * () { + if (filepath.slice(-1) === '/') throw new InvalidPathError('Files can not have a trailing slash') + yield assertValidPath (filepath) + }) +} + +function assertValidPath (fileOrFolderPath) { + return co(function * () { + if (!DAT_VALID_PATH_REGEX.test(fileOrFolderPath)) throw new InvalidPathError('Path contains invalid characters') + }) +} + // helper to handle the URL argument that's given to most args // - can get a dat hash, or dat url // - returns { archive, filepath } diff --git a/app/lib/const.js b/app/lib/const.js index 72550cea61..6893711091 100644 --- a/app/lib/const.js +++ b/app/lib/const.js @@ -6,6 +6,9 @@ export const BKR_SERVER_PORT = 17760 // 64 char hex export const DAT_HASH_REGEX = /^[0-9a-f]{64}$/i +// url file paths +export const DAT_VALID_PATH_REGEX = /^[a-z0-9\-._~!$&'()*+,;=:@\/]+$/i + // dat settings export const DAT_MANIFEST_FILENAME = 'dat.json' export const DAT_QUOTA_DEFAULT_BYTES_ALLOWED = (process.env.beaker_dat_quota_default_bytes_allowed || 104857600) // 100mb @@ -35,6 +38,7 @@ export const ProtectedFileNotWritableError = zerr('ProtectedFileNotWritableError export const FileAlreadyExistsError = zerr('FileAlreadyExistsError') export const FolderAlreadyExistsError = zerr('FolderAlreadyExistsError') export const ParentFolderDoesntExistError = zerr('ParentFolderDoesntExistError') +export const InvalidPathError = zerr('InvalidPathError') // dat network config export const DAT_DOMAIN = 'dat.local' diff --git a/app/shell-window/ui/navbar.js b/app/shell-window/ui/navbar.js index cf473f9641..dd05cba090 100644 --- a/app/shell-window/ui/navbar.js +++ b/app/shell-window/ui/navbar.js @@ -4,7 +4,7 @@ import * as pages from '../pages' import * as zoom from '../pages/zoom' import * as yo from 'yo-yo' import emitStream from 'emit-stream' -import * as prettyHash from 'pretty-hash' +import prettyHash from 'pretty-hash' import { UpdatesNavbarBtn } from './navbar/updates' import { DropMenuNavbarBtn } from './navbar/drop-menu' import { SiteInfoNavbarBtn } from './navbar/site-info' @@ -278,18 +278,20 @@ function render (id, page) { } function renderPrettyLocation (value, isHidden) { - var valueRendered + var valueRendered = value if (/^(dat|http|https):/.test(value)) { - var { protocol, host, pathname, search, hash } = new URL(value) - if (protocol === 'dat:' && isDatHashRegex.test(host)) host = prettyHash(host) - valueRendered = [ - yo`${protocol.slice(0, -1)}`, - yo`://`, - yo`${host}`, - yo`${pathname}${search}${hash}`, - ] - } else { - valueRendered = value + try { + var { protocol, host, pathname, search, hash } = new URL(value) + if (protocol === 'dat:' && isDatHashRegex.test(host)) host = prettyHash(host) + valueRendered = [ + yo`${protocol.slice(0, -1)}`, + yo`://`, + yo`${host}`, + yo`${pathname}${search}${hash}`, + ] + } catch (e) { + // invalid URL, just use value + } } return yo`