Skip to content

Commit

Permalink
fix: prevent invalid roots to be defined
Browse files Browse the repository at this point in the history
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
  • Loading branch information
skjnldsv committed Apr 7, 2023
1 parent da0db10 commit 922dcf2
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 17 deletions.
2 changes: 1 addition & 1 deletion __tests__/files/folder.spec.ts
Expand Up @@ -2,7 +2,7 @@ import { Folder } from '../../lib/files/folder'
import { FileType } from '../../lib/files/fileType'
import { Permission } from '../../lib/permissions'

describe('File creation', () => {
describe('Folder creation', () => {
test('Valid dav folder', () => {
const folder = new Folder({
source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/',
Expand Down
59 changes: 49 additions & 10 deletions __tests__/files/node.spec.ts
Expand Up @@ -153,12 +153,27 @@ describe('Sanity checks', () => {
owner: 'emma',
root: true as unknown as string,
})).toThrowError('Invalid root format')

expect(() => new File({
source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg',
mime: 'image/jpeg',
owner: 'emma',
root: 'https://cloud.domain.com/remote.php/dav/',
})).toThrowError('Root must start with a leading slash')

expect(() => new File({
source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg',
mime: 'image/jpeg',
owner: 'emma',
root: '/files/john',
})).toThrowError('Root must be part of the source')

expect(() => new File({
source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg',
mime: 'image/jpeg',
owner: 'emma',
root: '/remote.php/dav/files/emma',
})).toThrowError('The root must be relative to the service. e.g /files/emma')
})
})

Expand Down Expand Up @@ -213,43 +228,67 @@ describe('Dav service detection', () => {

describe('Root and paths detection', () => {
test('Unknown root', () => {
const file1 = new File({
const file = new File({
source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg',
mime: 'image/jpeg',
owner: 'emma',
})
expect(file1.root).toBe('/files/emma/Photos')
expect(file1.dirname).toBe('/')
expect(file.root).toBe('/files/emma/Photos')
expect(file.dirname).toBe('/')
})

test('Provided root dav service', () => {
const file1 = new File({
const file = new File({
source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg',
mime: 'image/jpeg',
owner: 'emma',
root: '/files/emma',
})
expect(file1.root).toBe('/files/emma')
expect(file1.dirname).toBe('/Photos')
expect(file.root).toBe('/files/emma')
expect(file.dirname).toBe('/Photos')
})

test('Root with ending slash is removed', () => {
const file1 = new File({
const file = new File({
source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg',
mime: 'image/jpeg',
owner: 'emma',
root: '/files/emma/',
})
expect(file1.root).toBe('/files/emma')
expect(file.root).toBe('/files/emma')
expect(file.dirname).toBe('/Photos')
expect(file.path).toBe('/Photos/picture.jpg')
})

test('Root and source are the same', () => {
const file1 = new File({
const folder = new Folder({
source: 'https://cloud.domain.com/remote.php/dav/files/emma',
owner: 'emma',
root: '/files/emma',
})
expect(folder.root).toBe('/files/emma')
expect(folder.dirname).toBe('/')
expect(folder.path).toBe('/')
})

test('Source contains a similar root path', () => {
const folder = new Folder({
source: 'https://domain.com/remote.php/dav/files/emma/files/emma',
owner: 'emma',
root: '/files/emma',
})
expect(folder.root).toBe('/files/emma')
expect(folder.dirname).toBe('/files')
expect(folder.path).toBe('/files/emma')

const file = new File({
source: 'https://domain.com/remote.php/dav/files/emma/files/emma.jpeg',
mime: 'image/jpeg',
owner: 'emma',
root: '/files/emma',
})
expect(file1.dirname).toBe('/')
expect(file.root).toBe('/files/emma')
expect(file.dirname).toBe('/files')
expect(file.path).toBe('/files/emma.jpeg')
})
})
17 changes: 12 additions & 5 deletions lib/files/node.ts
Expand Up @@ -22,7 +22,7 @@
import { basename, extname, dirname } from 'path'
import { Permission } from '../permissions'
import { FileType } from './fileType'
import NodeData, { Attribute, validateData } from './nodeData'
import NodeData, { Attribute, isDavRessource, validateData } from './nodeData'

export abstract class Node {
private _data: NodeData
Expand All @@ -31,7 +31,7 @@ export abstract class Node {

constructor(data: NodeData, davService?: RegExp) {
// Validate data
validateData(data)
validateData(data, davService || this._knownDavService)

this._data = data
this._attributes = data.attributes || {} as any
Expand Down Expand Up @@ -70,7 +70,9 @@ export abstract class Node {
*/
get dirname(): string {
if (this.root) {
return dirname(this.source.split(this.root).pop() || '/')
// Using replace would remove all part matching root
const firstMatch = this.source.indexOf(this.root)
return dirname(this.source.slice(firstMatch + this.root.length) || '/')
}
return dirname(this.source)
}
Expand Down Expand Up @@ -128,7 +130,7 @@ export abstract class Node {
* Is this a dav-related ressource ?
*/
get isDavRessource(): boolean {
return this.source.match(this._knownDavService) !== null
return isDavRessource(this.source, this._knownDavService)
}

/**
Expand All @@ -152,7 +154,12 @@ export abstract class Node {
/**
* Get the absolute path of this object relative to the root
*/
get path(): string|null {
get path(): string {
if (this.root) {
// Using replace would remove all part matching root
const firstMatch = this.source.indexOf(this.root)
return this.source.slice(firstMatch + this.root.length) || '/'
}
return (this.dirname + '/' + this.basename).replace(/\/\//g, '/')
}

Expand Down
18 changes: 17 additions & 1 deletion lib/files/nodeData.ts
Expand Up @@ -20,6 +20,7 @@
*
*/

import { join } from "path"
import { Permission } from "../permissions"

export interface Attribute { [key: string]: any }
Expand Down Expand Up @@ -62,11 +63,15 @@ export default interface NodeData {
*/
root?: string
}

export const isDavRessource = function(source: string, davService: RegExp): boolean {
return source.match(davService) !== null
}

/**
* Validate Node construct data
*/
export const validateData = (data: NodeData) => {
export const validateData = (data: NodeData, davService: RegExp) => {
if ('id' in data && (typeof data.id !== 'number' || data.id < 0)) {
throw new Error('Invalid id type of value')
}
Expand Down Expand Up @@ -121,4 +126,15 @@ export const validateData = (data: NodeData) => {
if (data.root && !data.root.startsWith('/')) {
throw new Error('Root must start with a leading slash')
}

if (data.root && !data.source.includes(data.root)) {
throw new Error('Root must be part of the source')
}

if (data.root && isDavRessource(data.source, davService)) {
const service = data.source.match(davService)![0]
if (!data.source.includes(join(service, data.root))) {
throw new Error('The root must be relative to the service. e.g /files/emma')
}
}
}

0 comments on commit 922dcf2

Please sign in to comment.