|
|
@@ -1,270 +0,0 @@
|
|
|
-import { vi, describe, beforeEach, it } from 'vitest'
|
|
|
-
|
|
|
-import path from 'node:path'
|
|
|
-import sinon from 'sinon'
|
|
|
-const modulePath = path.join(
|
|
|
- import.meta.dirname,
|
|
|
- '../../../app/js/StaticServerForbidSymlinks'
|
|
|
-)
|
|
|
-
|
|
|
-describe('StaticServerForbidSymlinks', function () {
|
|
|
- beforeEach(async function (ctx) {
|
|
|
- ctx.settings = {
|
|
|
- path: {
|
|
|
- compilesDir: '/compiles/here',
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- ctx.fs = {}
|
|
|
-
|
|
|
- vi.doMock('@overleaf/settings', () => ({
|
|
|
- default: ctx.settings,
|
|
|
- }))
|
|
|
-
|
|
|
- vi.doMock('fs', () => ({
|
|
|
- default: ctx.fs,
|
|
|
- }))
|
|
|
-
|
|
|
- ctx.ForbidSymlinks = (await import(modulePath)).default
|
|
|
-
|
|
|
- ctx.dummyStatic = (rootDir, options) => (req, res, next) =>
|
|
|
- // console.log "dummyStatic serving file", rootDir, "called with", req.url
|
|
|
- // serve it
|
|
|
- next()
|
|
|
-
|
|
|
- ctx.StaticServerForbidSymlinks = ctx.ForbidSymlinks(
|
|
|
- ctx.dummyStatic,
|
|
|
- ctx.settings.path.compilesDir
|
|
|
- )
|
|
|
- ctx.req = {
|
|
|
- params: {
|
|
|
- project_id: '12345',
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- ctx.res = {}
|
|
|
- ctx.req.url = '/12345/output.pdf'
|
|
|
- })
|
|
|
-
|
|
|
- describe('sending a normal file through', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.fs.realpath = sinon
|
|
|
- .stub()
|
|
|
- .callsArgWith(
|
|
|
- 1,
|
|
|
- null,
|
|
|
- `${ctx.settings.path.compilesDir}/${ctx.req.params.project_id}/output.pdf`
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
- it('should call next', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(200)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res, err => {
|
|
|
- if (err) reject(err)
|
|
|
- resolve()
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a missing file', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.fs.realpath = sinon
|
|
|
- .stub()
|
|
|
- .callsArgWith(
|
|
|
- 1,
|
|
|
- { code: 'ENOENT' },
|
|
|
- `${ctx.settings.path.compilesDir}/${ctx.req.params.project_id}/unknown.pdf`
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a new line', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = '/12345/output.pdf\nother file'
|
|
|
- ctx.fs.realpath = sinon.stub().yields()
|
|
|
- })
|
|
|
-
|
|
|
- it('should process the correct file', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = () => {
|
|
|
- ctx.fs.realpath.should.have.been.calledWith(
|
|
|
- `${ctx.settings.path.compilesDir}/12345/output.pdf\nother file`
|
|
|
- )
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a symlink file', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.fs.realpath = sinon
|
|
|
- .stub()
|
|
|
- .callsArgWith(1, null, `/etc/${ctx.req.params.project_id}/output.pdf`)
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a relative file', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = '/12345/../67890/output.pdf'
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a unnormalized file containing .', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = '/12345/foo/./output.pdf'
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a file containing an empty path', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = '/12345/foo//output.pdf'
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a non-project file', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = '/.foo/output.pdf'
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a file outside the compiledir', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = '/../bar/output.pdf'
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a file with no leading /', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = './../bar/output.pdf'
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 404', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(404)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with a github style path', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.req.url = '/henryoswald-latex_example/output/output.log'
|
|
|
- ctx.fs.realpath = sinon
|
|
|
- .stub()
|
|
|
- .callsArgWith(
|
|
|
- 1,
|
|
|
- null,
|
|
|
- `${ctx.settings.path.compilesDir}/henryoswald-latex_example/output/output.log`
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
- it('should call next', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(200)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res, err => {
|
|
|
- if (err) reject(err)
|
|
|
- resolve()
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe('with an error from fs.realpath', function () {
|
|
|
- beforeEach(function (ctx) {
|
|
|
- ctx.fs.realpath = sinon.stub().callsArgWith(1, 'error')
|
|
|
- })
|
|
|
-
|
|
|
- it('should send a 500', async function (ctx) {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- ctx.res.sendStatus = function (resCode) {
|
|
|
- resCode.should.equal(500)
|
|
|
- resolve()
|
|
|
- }
|
|
|
- ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-})
|