mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #219 from overleaf/jpa-read-image-name-for-synctex
[misc] consume and validate a custom imageName for synctex requests
This commit is contained in:
commit
b3ed820444
7 changed files with 290 additions and 98 deletions
|
@ -21,6 +21,12 @@ const ProjectPersistenceManager = require('./ProjectPersistenceManager')
|
||||||
const logger = require('logger-sharelatex')
|
const logger = require('logger-sharelatex')
|
||||||
const Errors = require('./Errors')
|
const Errors = require('./Errors')
|
||||||
|
|
||||||
|
function isImageNameAllowed(imageName) {
|
||||||
|
const ALLOWED_IMAGES =
|
||||||
|
Settings.clsi && Settings.clsi.docker && Settings.clsi.docker.allowedImages
|
||||||
|
return !ALLOWED_IMAGES || ALLOWED_IMAGES.includes(imageName)
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = CompileController = {
|
module.exports = CompileController = {
|
||||||
compile(req, res, next) {
|
compile(req, res, next) {
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
|
@ -165,14 +171,21 @@ module.exports = CompileController = {
|
||||||
const { file } = req.query
|
const { file } = req.query
|
||||||
const line = parseInt(req.query.line, 10)
|
const line = parseInt(req.query.line, 10)
|
||||||
const column = parseInt(req.query.column, 10)
|
const column = parseInt(req.query.column, 10)
|
||||||
|
const { imageName } = req.query
|
||||||
const { project_id } = req.params
|
const { project_id } = req.params
|
||||||
const { user_id } = req.params
|
const { user_id } = req.params
|
||||||
|
|
||||||
|
if (imageName && !isImageNameAllowed(imageName)) {
|
||||||
|
return res.status(400).send('invalid image')
|
||||||
|
}
|
||||||
|
|
||||||
return CompileManager.syncFromCode(
|
return CompileManager.syncFromCode(
|
||||||
project_id,
|
project_id,
|
||||||
user_id,
|
user_id,
|
||||||
file,
|
file,
|
||||||
line,
|
line,
|
||||||
column,
|
column,
|
||||||
|
imageName,
|
||||||
function (error, pdfPositions) {
|
function (error, pdfPositions) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return next(error)
|
return next(error)
|
||||||
|
@ -191,14 +204,20 @@ module.exports = CompileController = {
|
||||||
const page = parseInt(req.query.page, 10)
|
const page = parseInt(req.query.page, 10)
|
||||||
const h = parseFloat(req.query.h)
|
const h = parseFloat(req.query.h)
|
||||||
const v = parseFloat(req.query.v)
|
const v = parseFloat(req.query.v)
|
||||||
|
const { imageName } = req.query
|
||||||
const { project_id } = req.params
|
const { project_id } = req.params
|
||||||
const { user_id } = req.params
|
const { user_id } = req.params
|
||||||
|
|
||||||
|
if (imageName && !isImageNameAllowed(imageName)) {
|
||||||
|
return res.status(400).send('invalid image')
|
||||||
|
}
|
||||||
return CompileManager.syncFromPdf(
|
return CompileManager.syncFromPdf(
|
||||||
project_id,
|
project_id,
|
||||||
user_id,
|
user_id,
|
||||||
page,
|
page,
|
||||||
h,
|
h,
|
||||||
v,
|
v,
|
||||||
|
imageName,
|
||||||
function (error, codePositions) {
|
function (error, codePositions) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return next(error)
|
return next(error)
|
||||||
|
@ -218,13 +237,7 @@ module.exports = CompileController = {
|
||||||
const { project_id } = req.params
|
const { project_id } = req.params
|
||||||
const { user_id } = req.params
|
const { user_id } = req.params
|
||||||
const { image } = req.query
|
const { image } = req.query
|
||||||
if (
|
if (image && !isImageNameAllowed(image)) {
|
||||||
image &&
|
|
||||||
Settings.clsi &&
|
|
||||||
Settings.clsi.docker &&
|
|
||||||
Settings.clsi.docker.allowedImages &&
|
|
||||||
!Settings.clsi.docker.allowedImages.includes(image)
|
|
||||||
) {
|
|
||||||
return res.status(400).send('invalid image')
|
return res.status(400).send('invalid image')
|
||||||
}
|
}
|
||||||
logger.log({ image, file, project_id }, 'word count request')
|
logger.log({ image, file, project_id }, 'word count request')
|
||||||
|
|
|
@ -431,7 +431,15 @@ module.exports = CompileManager = {
|
||||||
})
|
})
|
||||||
}, // directory exists
|
}, // directory exists
|
||||||
|
|
||||||
syncFromCode(project_id, user_id, file_name, line, column, callback) {
|
syncFromCode(
|
||||||
|
project_id,
|
||||||
|
user_id,
|
||||||
|
file_name,
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
imageName,
|
||||||
|
callback
|
||||||
|
) {
|
||||||
// If LaTeX was run in a virtual environment, the file path that synctex expects
|
// If LaTeX was run in a virtual environment, the file path that synctex expects
|
||||||
// might not match the file path on the host. The .synctex.gz file however, will be accessed
|
// might not match the file path on the host. The .synctex.gz file however, will be accessed
|
||||||
// wherever it is on the host.
|
// wherever it is on the host.
|
||||||
|
@ -444,10 +452,12 @@ module.exports = CompileManager = {
|
||||||
const compileDir = getCompileDir(project_id, user_id)
|
const compileDir = getCompileDir(project_id, user_id)
|
||||||
const synctex_path = `${base_dir}/output.pdf`
|
const synctex_path = `${base_dir}/output.pdf`
|
||||||
const command = ['code', synctex_path, file_path, line, column]
|
const command = ['code', synctex_path, file_path, line, column]
|
||||||
CompileManager._runSynctex(project_id, user_id, command, function (
|
CompileManager._runSynctex(
|
||||||
error,
|
project_id,
|
||||||
stdout
|
user_id,
|
||||||
) {
|
command,
|
||||||
|
imageName,
|
||||||
|
function (error, stdout) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
}
|
}
|
||||||
|
@ -455,11 +465,15 @@ module.exports = CompileManager = {
|
||||||
{ project_id, user_id, file_name, line, column, command, stdout },
|
{ project_id, user_id, file_name, line, column, command, stdout },
|
||||||
'synctex code output'
|
'synctex code output'
|
||||||
)
|
)
|
||||||
return callback(null, CompileManager._parseSynctexFromCodeOutput(stdout))
|
return callback(
|
||||||
})
|
null,
|
||||||
|
CompileManager._parseSynctexFromCodeOutput(stdout)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
syncFromPdf(project_id, user_id, page, h, v, callback) {
|
syncFromPdf(project_id, user_id, page, h, v, imageName, callback) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
callback = function (error, filePositions) {}
|
callback = function (error, filePositions) {}
|
||||||
}
|
}
|
||||||
|
@ -468,10 +482,12 @@ module.exports = CompileManager = {
|
||||||
const base_dir = Settings.path.synctexBaseDir(compileName)
|
const base_dir = Settings.path.synctexBaseDir(compileName)
|
||||||
const synctex_path = `${base_dir}/output.pdf`
|
const synctex_path = `${base_dir}/output.pdf`
|
||||||
const command = ['pdf', synctex_path, page, h, v]
|
const command = ['pdf', synctex_path, page, h, v]
|
||||||
CompileManager._runSynctex(project_id, user_id, command, function (
|
CompileManager._runSynctex(
|
||||||
error,
|
project_id,
|
||||||
stdout
|
user_id,
|
||||||
) {
|
command,
|
||||||
|
imageName,
|
||||||
|
function (error, stdout) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
}
|
}
|
||||||
|
@ -483,7 +499,8 @@ module.exports = CompileManager = {
|
||||||
null,
|
null,
|
||||||
CompileManager._parseSynctexFromPdfOutput(stdout, base_dir)
|
CompileManager._parseSynctexFromPdfOutput(stdout, base_dir)
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
_checkFileExists(dir, filename, callback) {
|
_checkFileExists(dir, filename, callback) {
|
||||||
|
@ -513,7 +530,7 @@ module.exports = CompileManager = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
_runSynctex(project_id, user_id, command, callback) {
|
_runSynctex(project_id, user_id, command, imageName, callback) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
callback = function (error, stdout) {}
|
callback = function (error, stdout) {}
|
||||||
}
|
}
|
||||||
|
@ -533,9 +550,10 @@ module.exports = CompileManager = {
|
||||||
compileName,
|
compileName,
|
||||||
command,
|
command,
|
||||||
directory,
|
directory,
|
||||||
Settings.clsi && Settings.clsi.docker
|
imageName ||
|
||||||
|
(Settings.clsi && Settings.clsi.docker
|
||||||
? Settings.clsi.docker.image
|
? Settings.clsi.docker.image
|
||||||
: undefined,
|
: undefined),
|
||||||
timeout,
|
timeout,
|
||||||
{},
|
{},
|
||||||
compileGroup,
|
compileGroup,
|
||||||
|
|
|
@ -71,6 +71,78 @@ Hello world
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('syncToCode', function () {
|
||||||
|
beforeEach(function (done) {
|
||||||
|
Client.compile(this.project_id, this.request, done)
|
||||||
|
})
|
||||||
|
it('should error out with an invalid imageName', function () {
|
||||||
|
Client.syncFromCodeWithImage(
|
||||||
|
this.project_id,
|
||||||
|
'main.tex',
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
'something/evil:1337',
|
||||||
|
(error, body) => {
|
||||||
|
expect(String(error)).to.include('statusCode=400')
|
||||||
|
expect(body).to.equal('invalid image')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should produce a mapping a valid imageName', function () {
|
||||||
|
Client.syncFromCodeWithImage(
|
||||||
|
this.project_id,
|
||||||
|
'main.tex',
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
process.env.TEXLIVE_IMAGE,
|
||||||
|
(error, result) => {
|
||||||
|
expect(error).to.not.exist
|
||||||
|
expect(result).to.deep.equal({
|
||||||
|
pdf: [
|
||||||
|
{ page: 1, h: 133.77, v: 134.76, height: 6.92, width: 343.71 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('syncToPdf', function () {
|
||||||
|
beforeEach(function (done) {
|
||||||
|
Client.compile(this.project_id, this.request, done)
|
||||||
|
})
|
||||||
|
it('should error out with an invalid imageName', function () {
|
||||||
|
Client.syncFromPdfWithImage(
|
||||||
|
this.project_id,
|
||||||
|
'main.tex',
|
||||||
|
100,
|
||||||
|
200,
|
||||||
|
'something/evil:1337',
|
||||||
|
(error, body) => {
|
||||||
|
expect(String(error)).to.include('statusCode=400')
|
||||||
|
expect(body).to.equal('invalid image')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should produce a mapping a valid imageName', function () {
|
||||||
|
Client.syncFromPdfWithImage(
|
||||||
|
this.project_id,
|
||||||
|
1,
|
||||||
|
100,
|
||||||
|
200,
|
||||||
|
process.env.TEXLIVE_IMAGE,
|
||||||
|
(error, result) => {
|
||||||
|
expect(error).to.not.exist
|
||||||
|
expect(result).to.deep.equal({
|
||||||
|
code: [{ file: 'main.tex', line: 3, column: -1 }]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('wordcount', function () {
|
describe('wordcount', function () {
|
||||||
beforeEach(function (done) {
|
beforeEach(function (done) {
|
||||||
Client.compile(this.project_id, this.request, done)
|
Client.compile(this.project_id, this.request, done)
|
||||||
|
@ -80,8 +152,9 @@ Hello world
|
||||||
this.project_id,
|
this.project_id,
|
||||||
'main.tex',
|
'main.tex',
|
||||||
'something/evil:1337',
|
'something/evil:1337',
|
||||||
(error, result) => {
|
(error, body) => {
|
||||||
expect(String(error)).to.include('statusCode=400')
|
expect(String(error)).to.include('statusCode=400')
|
||||||
|
expect(body).to.equal('invalid image')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -100,9 +100,7 @@ Hello world
|
||||||
3,
|
3,
|
||||||
5,
|
5,
|
||||||
(error, body) => {
|
(error, body) => {
|
||||||
if (error != null) {
|
expect(String(error)).to.include('statusCode=404')
|
||||||
throw error
|
|
||||||
}
|
|
||||||
expect(body).to.equal('Not Found')
|
expect(body).to.equal('Not Found')
|
||||||
return done()
|
return done()
|
||||||
}
|
}
|
||||||
|
@ -117,9 +115,7 @@ Hello world
|
||||||
100,
|
100,
|
||||||
200,
|
200,
|
||||||
(error, body) => {
|
(error, body) => {
|
||||||
if (error != null) {
|
expect(String(error)).to.include('statusCode=404')
|
||||||
throw error
|
|
||||||
}
|
|
||||||
expect(body).to.equal('Not Found')
|
expect(body).to.equal('Not Found')
|
||||||
return done()
|
return done()
|
||||||
}
|
}
|
||||||
|
@ -160,9 +156,7 @@ Hello world
|
||||||
3,
|
3,
|
||||||
5,
|
5,
|
||||||
(error, body) => {
|
(error, body) => {
|
||||||
if (error != null) {
|
expect(String(error)).to.include('statusCode=404')
|
||||||
throw error
|
|
||||||
}
|
|
||||||
expect(body).to.equal('Not Found')
|
expect(body).to.equal('Not Found')
|
||||||
return done()
|
return done()
|
||||||
}
|
}
|
||||||
|
@ -177,9 +171,7 @@ Hello world
|
||||||
100,
|
100,
|
||||||
200,
|
200,
|
||||||
(error, body) => {
|
(error, body) => {
|
||||||
if (error != null) {
|
expect(String(error)).to.include('statusCode=404')
|
||||||
throw error
|
|
||||||
}
|
|
||||||
expect(body).to.equal('Not Found')
|
expect(body).to.equal('Not Found')
|
||||||
return done()
|
return done()
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,10 @@ module.exports = Client = {
|
||||||
},
|
},
|
||||||
|
|
||||||
syncFromCode(project_id, file, line, column, callback) {
|
syncFromCode(project_id, file, line, column, callback) {
|
||||||
|
Client.syncFromCodeWithImage(project_id, file, line, column, '', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
syncFromCodeWithImage(project_id, file, line, column, imageName, callback) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
callback = function (error, pdfPositions) {}
|
callback = function (error, pdfPositions) {}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +80,7 @@ module.exports = Client = {
|
||||||
{
|
{
|
||||||
url: `${this.host}/project/${project_id}/sync/code`,
|
url: `${this.host}/project/${project_id}/sync/code`,
|
||||||
qs: {
|
qs: {
|
||||||
|
imageName,
|
||||||
file,
|
file,
|
||||||
line,
|
line,
|
||||||
column
|
column
|
||||||
|
@ -86,12 +91,19 @@ module.exports = Client = {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
}
|
}
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
return callback(new Error(`statusCode=${response.statusCode}`), body)
|
||||||
|
}
|
||||||
return callback(null, body)
|
return callback(null, body)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
syncFromPdf(project_id, page, h, v, callback) {
|
syncFromPdf(project_id, page, h, v, callback) {
|
||||||
|
Client.syncFromPdfWithImage(project_id, page, h, v, '', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
syncFromPdfWithImage(project_id, page, h, v, imageName, callback) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
callback = function (error, pdfPositions) {}
|
callback = function (error, pdfPositions) {}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +111,7 @@ module.exports = Client = {
|
||||||
{
|
{
|
||||||
url: `${this.host}/project/${project_id}/sync/pdf`,
|
url: `${this.host}/project/${project_id}/sync/pdf`,
|
||||||
qs: {
|
qs: {
|
||||||
|
imageName,
|
||||||
page,
|
page,
|
||||||
h,
|
h,
|
||||||
v
|
v
|
||||||
|
@ -109,6 +122,9 @@ module.exports = Client = {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
}
|
}
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
return callback(new Error(`statusCode=${response.statusCode}`), body)
|
||||||
|
}
|
||||||
return callback(null, body)
|
return callback(null, body)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -208,7 +224,7 @@ module.exports = Client = {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
}
|
}
|
||||||
if (response.statusCode !== 200) {
|
if (response.statusCode !== 200) {
|
||||||
return callback(new Error(`statusCode=${response.statusCode}`))
|
return callback(new Error(`statusCode=${response.statusCode}`), body)
|
||||||
}
|
}
|
||||||
return callback(null, JSON.parse(body))
|
return callback(null, JSON.parse(body))
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,48 @@ const modulePath = require('path').join(
|
||||||
)
|
)
|
||||||
const tk = require('timekeeper')
|
const tk = require('timekeeper')
|
||||||
|
|
||||||
|
function tryImageNameValidation(method, imageNameField) {
|
||||||
|
describe('when allowedImages is set', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.Settings.clsi = { docker: {} }
|
||||||
|
this.Settings.clsi.docker.allowedImages = [
|
||||||
|
'repo/image:tag1',
|
||||||
|
'repo/image:tag2'
|
||||||
|
]
|
||||||
|
this.res.send = sinon.stub()
|
||||||
|
this.res.status = sinon.stub().returns({ send: this.res.send })
|
||||||
|
|
||||||
|
this.CompileManager[method].reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with an invalid image', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.req.query[imageNameField] = 'something/evil:1337'
|
||||||
|
this.CompileController[method](this.req, this.res, this.next)
|
||||||
|
})
|
||||||
|
it('should return a 400', function () {
|
||||||
|
expect(this.res.status.calledWith(400)).to.equal(true)
|
||||||
|
})
|
||||||
|
it('should not run the query', function () {
|
||||||
|
expect(this.CompileManager[method].called).to.equal(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with a valid image', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.req.query[imageNameField] = 'repo/image:tag1'
|
||||||
|
this.CompileController[method](this.req, this.res, this.next)
|
||||||
|
})
|
||||||
|
it('should not return a 400', function () {
|
||||||
|
expect(this.res.status.calledWith(400)).to.equal(false)
|
||||||
|
})
|
||||||
|
it('should run the query', function () {
|
||||||
|
expect(this.CompileManager[method].called).to.equal(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
describe('CompileController', function () {
|
describe('CompileController', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.CompileController = SandboxedModule.require(modulePath, {
|
this.CompileController = SandboxedModule.require(modulePath, {
|
||||||
|
@ -248,7 +290,7 @@ describe('CompileController', function () {
|
||||||
|
|
||||||
this.CompileManager.syncFromCode = sinon
|
this.CompileManager.syncFromCode = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(5, null, (this.pdfPositions = ['mock-positions']))
|
.yields(null, (this.pdfPositions = ['mock-positions']))
|
||||||
return this.CompileController.syncFromCode(this.req, this.res, this.next)
|
return this.CompileController.syncFromCode(this.req, this.res, this.next)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -264,13 +306,15 @@ describe('CompileController', function () {
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
return it('should return the positions', function () {
|
it('should return the positions', function () {
|
||||||
return this.res.json
|
return this.res.json
|
||||||
.calledWith({
|
.calledWith({
|
||||||
pdf: this.pdfPositions
|
pdf: this.pdfPositions
|
||||||
})
|
})
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
tryImageNameValidation('syncFromCode', 'imageName')
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('syncFromPdf', function () {
|
describe('syncFromPdf', function () {
|
||||||
|
@ -289,7 +333,7 @@ describe('CompileController', function () {
|
||||||
|
|
||||||
this.CompileManager.syncFromPdf = sinon
|
this.CompileManager.syncFromPdf = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(5, null, (this.codePositions = ['mock-positions']))
|
.yields(null, (this.codePositions = ['mock-positions']))
|
||||||
return this.CompileController.syncFromPdf(this.req, this.res, this.next)
|
return this.CompileController.syncFromPdf(this.req, this.res, this.next)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -299,13 +343,15 @@ describe('CompileController', function () {
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
return it('should return the positions', function () {
|
it('should return the positions', function () {
|
||||||
return this.res.json
|
return this.res.json
|
||||||
.calledWith({
|
.calledWith({
|
||||||
code: this.codePositions
|
code: this.codePositions
|
||||||
})
|
})
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
tryImageNameValidation('syncFromPdf', 'imageName')
|
||||||
})
|
})
|
||||||
|
|
||||||
return describe('wordcount', function () {
|
return describe('wordcount', function () {
|
||||||
|
@ -340,42 +386,6 @@ describe('CompileController', function () {
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when allowedImages is set', function () {
|
tryImageNameValidation('wordcount', 'image')
|
||||||
beforeEach(function () {
|
|
||||||
this.Settings.clsi = { docker: {} }
|
|
||||||
this.Settings.clsi.docker.allowedImages = [
|
|
||||||
'repo/image:tag1',
|
|
||||||
'repo/image:tag2'
|
|
||||||
]
|
|
||||||
this.res.send = sinon.stub()
|
|
||||||
this.res.status = sinon.stub().returns({ send: this.res.send })
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with an invalid image', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
this.req.query.image = 'something/evil:1337'
|
|
||||||
this.CompileController.wordcount(this.req, this.res, this.next)
|
|
||||||
})
|
|
||||||
it('should return a 400', function () {
|
|
||||||
expect(this.res.status.calledWith(400)).to.equal(true)
|
|
||||||
})
|
|
||||||
it('should not run the query', function () {
|
|
||||||
expect(this.CompileManager.wordcount.called).to.equal(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with a valid image', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
this.req.query.image = 'repo/image:tag1'
|
|
||||||
this.CompileController.wordcount(this.req, this.res, this.next)
|
|
||||||
})
|
|
||||||
it('should not return a 400', function () {
|
|
||||||
expect(this.res.status.calledWith(400)).to.equal(false)
|
|
||||||
})
|
|
||||||
it('should run the query', function () {
|
|
||||||
expect(this.CompileManager.wordcount.called).to.equal(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -394,13 +394,14 @@ describe('CompileManager', function () {
|
||||||
this.stdout = `NODE\t${this.page}\t${this.h}\t${this.v}\t${this.width}\t${this.height}\n`
|
this.stdout = `NODE\t${this.page}\t${this.h}\t${this.v}\t${this.width}\t${this.height}\n`
|
||||||
this.CommandRunner.run = sinon
|
this.CommandRunner.run = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(7, null, { stdout: this.stdout })
|
.yields(null, { stdout: this.stdout })
|
||||||
return this.CompileManager.syncFromCode(
|
return this.CompileManager.syncFromCode(
|
||||||
this.project_id,
|
this.project_id,
|
||||||
this.user_id,
|
this.user_id,
|
||||||
this.file_name,
|
this.file_name,
|
||||||
this.line,
|
this.line,
|
||||||
this.column,
|
this.column,
|
||||||
|
'',
|
||||||
this.callback
|
this.callback
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -428,7 +429,7 @@ describe('CompileManager', function () {
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
return it('should call the callback with the parsed output', function () {
|
it('should call the callback with the parsed output', function () {
|
||||||
return this.callback
|
return this.callback
|
||||||
.calledWith(null, [
|
.calledWith(null, [
|
||||||
{
|
{
|
||||||
|
@ -441,6 +442,44 @@ describe('CompileManager', function () {
|
||||||
])
|
])
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('with a custom imageName', function () {
|
||||||
|
const customImageName = 'foo/bar:tag-0'
|
||||||
|
beforeEach(function () {
|
||||||
|
this.CommandRunner.run.reset()
|
||||||
|
this.CompileManager.syncFromCode(
|
||||||
|
this.project_id,
|
||||||
|
this.user_id,
|
||||||
|
this.file_name,
|
||||||
|
this.line,
|
||||||
|
this.column,
|
||||||
|
customImageName,
|
||||||
|
this.callback
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should execute the synctex binary in a custom docker image', function () {
|
||||||
|
const synctex_path = `${this.Settings.path.compilesDir}/${this.project_id}-${this.user_id}/output.pdf`
|
||||||
|
const file_path = `${this.Settings.path.compilesDir}/${this.project_id}-${this.user_id}/${this.file_name}`
|
||||||
|
this.CommandRunner.run
|
||||||
|
.calledWith(
|
||||||
|
`${this.project_id}-${this.user_id}`,
|
||||||
|
[
|
||||||
|
'/opt/synctex',
|
||||||
|
'code',
|
||||||
|
synctex_path,
|
||||||
|
file_path,
|
||||||
|
this.line,
|
||||||
|
this.column
|
||||||
|
],
|
||||||
|
`${this.Settings.path.compilesDir}/${this.project_id}-${this.user_id}`,
|
||||||
|
customImageName,
|
||||||
|
60000,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
.should.equal(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return describe('syncFromPdf', function () {
|
return describe('syncFromPdf', function () {
|
||||||
|
@ -460,6 +499,7 @@ describe('CompileManager', function () {
|
||||||
this.page,
|
this.page,
|
||||||
this.h,
|
this.h,
|
||||||
this.v,
|
this.v,
|
||||||
|
'',
|
||||||
this.callback
|
this.callback
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -479,7 +519,7 @@ describe('CompileManager', function () {
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
return it('should call the callback with the parsed output', function () {
|
it('should call the callback with the parsed output', function () {
|
||||||
return this.callback
|
return this.callback
|
||||||
.calledWith(null, [
|
.calledWith(null, [
|
||||||
{
|
{
|
||||||
|
@ -490,6 +530,36 @@ describe('CompileManager', function () {
|
||||||
])
|
])
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('with a custom imageName', function () {
|
||||||
|
const customImageName = 'foo/bar:tag-1'
|
||||||
|
beforeEach(function () {
|
||||||
|
this.CommandRunner.run.reset()
|
||||||
|
this.CompileManager.syncFromPdf(
|
||||||
|
this.project_id,
|
||||||
|
this.user_id,
|
||||||
|
this.page,
|
||||||
|
this.h,
|
||||||
|
this.v,
|
||||||
|
customImageName,
|
||||||
|
this.callback
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should execute the synctex binary in a custom docker image', function () {
|
||||||
|
const synctex_path = `${this.Settings.path.compilesDir}/${this.project_id}-${this.user_id}/output.pdf`
|
||||||
|
this.CommandRunner.run
|
||||||
|
.calledWith(
|
||||||
|
`${this.project_id}-${this.user_id}`,
|
||||||
|
['/opt/synctex', 'pdf', synctex_path, this.page, this.h, this.v],
|
||||||
|
`${this.Settings.path.compilesDir}/${this.project_id}-${this.user_id}`,
|
||||||
|
customImageName,
|
||||||
|
60000,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
.should.equal(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue