diff --git a/lib/ldp.js b/lib/ldp.js index 448e3ac52..e6ed2de7c 100644 --- a/lib/ldp.js +++ b/lib/ldp.js @@ -243,7 +243,14 @@ class LDP { 'PUT request requires a content-type via the Content-Type header') } + // reject resource with percent-encoded $ extension + const dollarExtensionRegex = /%(?:24)\.[^%(?:24)]*$/ // /\$\.[^$]*$/ + if ((url.url || url).match(dollarExtensionRegex)) { + throw error(400, 'Resource with a $.ext is not allowed by the server') + } + // check if a folder or file with same name exists + // const urlItem = url.url || url await this.checkItemName(url) // First check if we are above quota @@ -350,17 +357,27 @@ class LDP { async checkItemName (url) { let testName - const itemUrl = (url.url || url) + const { hostname, pathname } = this.resourceMapper._parseUrl(url) // (url.url || url) + let itemUrl = this.resourceMapper.resolveUrl(hostname, pathname) const container = itemUrl.endsWith('/') try { const testUrl = container ? itemUrl.slice(0, -1) : itemUrl + '/' const { path: testPath } = await this.resourceMapper.mapUrlToFile({ url: testUrl }) - // testName = fs.lstatSync(testPath).isDirectory() testName = container ? fs.lstatSync(testPath).isFile() : fs.lstatSync(testPath).isDirectory() - } catch (err) { testName = false } - + } catch (err) { + testName = false + + // item does not exist, check one level up the tree + if (itemUrl.endsWith('/')) itemUrl = itemUrl.substring(0, itemUrl.length - 1) + itemUrl = itemUrl.substring(0, itemUrl.lastIndexOf('/') + 1) + const { pathname } = this.resourceMapper._parseUrl(itemUrl) // (url.url || url) + // check not at root + if (pathname !== '/') { + await this.checkItemName(itemUrl) + } + } if (testName) { - throw error(200, 'Container and resource cannot have the same name in URI') + throw error(404, 'Container and resource cannot have the same name in URI') } } diff --git a/test/integration/http-test.js b/test/integration/http-test.js index a4d913af9..cbe7d5bc9 100644 --- a/test/integration/http-test.js +++ b/test/integration/http-test.js @@ -535,13 +535,29 @@ describe('HTTP APIs', function () { .expect(hasHeader('acl', 'baz.ttl' + suffixAcl)) .expect(201, done) }) - it('should not create new resource if folder with same name exists', function (done) { - server.put('/foo/bar') + it('should not create a resource with percent-encoded $.ext', function (done) { + server.put('/foo/bar/baz%24.ttl') .send(putRequestBody) .set('content-type', 'text/turtle') - .expect(hasHeader('describedBy', 'bar' + suffixMeta)) - .expect(hasHeader('acl', 'bar' + suffixAcl)) - .expect(200, done) + // .expect(hasHeader('describedBy', 'baz.ttl' + suffixMeta)) + // .expect(hasHeader('acl', 'baz.ttl' + suffixAcl)) + .expect(400, done) // 404 + }) + it('should create a resource without extension', function (done) { + server.put('/foo/bar/baz') + .send(putRequestBody) + .set('content-type', 'text/turtle') + .expect(hasHeader('describedBy', 'baz' + suffixMeta)) + .expect(hasHeader('acl', 'baz' + suffixAcl)) + .expect(201, done) + }) + it('should not create new resource if a folder/resource with same name will exist in tree', function (done) { + server.put('/foo/bar/baz/test.ttl') + .send(putRequestBody) + .set('content-type', 'text/turtle') + .expect(hasHeader('describedBy', 'test.ttl' + suffixMeta)) + .expect(hasHeader('acl', 'test.ttl' + suffixAcl)) + .expect(404, done) }) it('should return 201 when trying to put to a container without content-type', function (done) { @@ -587,7 +603,7 @@ describe('HTTP APIs', function () { return Promise.all([ rm('/false-file-48484848'), createTestResource('/.acl'), - createTestResource('/profile/card$.ttl'), + createTestResource('/profile/card'), createTestResource('/delete-test-empty-container/.meta.acl'), createTestResource('/put-resource-1.ttl'), createTestResource('/put-resource-with-acl.ttl'), diff --git a/test/integration/ldp-test.js b/test/integration/ldp-test.js index 0027cfcbc..cca0cc2fc 100644 --- a/test/integration/ldp-test.js +++ b/test/integration/ldp-test.js @@ -162,13 +162,13 @@ describe('LDP', function () { it.skip('with a larger file to exceed allowed quota', function () { const randstream = stringToStream(randomBytes(2100)) - return ldp.put('localhost', '/resources/testQuota.txt', randstream).catch((err) => { + return ldp.put('/localhost', '/resources/testQuota.txt', randstream).catch((err) => { assert.notOk(err) }) }) it('should fail if a over quota', function () { const hellostream = stringToStream('hello world') - return ldp.put('localhost', '/resources/testOverQuota.txt', hellostream).catch((err) => { + return ldp.put('/localhost', '/resources/testOverQuota.txt', hellostream).catch((err) => { assert.equal(err.status, 413) }) })