SafeExecTests.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. const chai = require('chai')
  2. const should = chai.should()
  3. const { expect } = chai
  4. const modulePath = '../../../app/js/SafeExec'
  5. const { Errors } = require('@overleaf/object-persistor')
  6. const SandboxedModule = require('sandboxed-module')
  7. describe('SafeExec', function () {
  8. let settings, options, safeExec
  9. beforeEach(function () {
  10. settings = { enableConversions: true }
  11. options = { timeout: 10 * 1000, killSignal: 'SIGTERM' }
  12. const ObjectPersistor = { Errors }
  13. safeExec = SandboxedModule.require(modulePath, {
  14. globals: { process },
  15. requires: {
  16. '@overleaf/settings': settings,
  17. '@overleaf/object-persistor': ObjectPersistor,
  18. },
  19. })
  20. })
  21. describe('safeExec', function () {
  22. it('should execute a valid command', function (done) {
  23. safeExec(['/bin/echo', 'hello'], options, (err, stdout, stderr) => {
  24. stdout.should.equal('hello\n')
  25. stderr.should.equal('')
  26. should.not.exist(err)
  27. done()
  28. })
  29. })
  30. it('should error when conversions are disabled', function (done) {
  31. settings.enableConversions = false
  32. safeExec(['/bin/echo', 'hello'], options, err => {
  33. expect(err).to.exist
  34. done()
  35. })
  36. })
  37. it('should execute a command with non-zero exit status', function (done) {
  38. safeExec(['/usr/bin/env', 'false'], options, err => {
  39. expect(err).to.exist
  40. expect(err.name).to.equal('FailedCommandError')
  41. expect(err.code).to.equal(1)
  42. expect(err.stdout).to.equal('')
  43. expect(err.stderr).to.equal('')
  44. done()
  45. })
  46. })
  47. it('should handle an invalid command', function (done) {
  48. safeExec(['/bin/foobar'], options, err => {
  49. err.code.should.equal('ENOENT')
  50. done()
  51. })
  52. })
  53. it('should handle a command that runs too long', function (done) {
  54. safeExec(
  55. ['/bin/sleep', '10'],
  56. { timeout: 500, killSignal: 'SIGTERM' },
  57. err => {
  58. expect(err).to.exist
  59. expect(err.name).to.equal('FailedCommandError')
  60. expect(err.code).to.equal('SIGTERM')
  61. done()
  62. }
  63. )
  64. })
  65. })
  66. describe('as a promise', function () {
  67. beforeEach(function () {
  68. safeExec = safeExec.promises
  69. })
  70. it('should execute a valid command', async function () {
  71. const { stdout, stderr } = await safeExec(['/bin/echo', 'hello'], options)
  72. stdout.should.equal('hello\n')
  73. stderr.should.equal('')
  74. })
  75. it('should throw a ConversionsDisabledError when appropriate', async function () {
  76. settings.enableConversions = false
  77. try {
  78. await safeExec(['/bin/echo', 'hello'], options)
  79. } catch (err) {
  80. expect(err.name).to.equal('ConversionsDisabledError')
  81. return
  82. }
  83. expect('method did not throw an error').not.to.exist
  84. })
  85. it('should throw a FailedCommandError when appropriate', async function () {
  86. try {
  87. await safeExec(['/usr/bin/env', 'false'], options)
  88. } catch (err) {
  89. expect(err.name).to.equal('FailedCommandError')
  90. expect(err.code).to.equal(1)
  91. return
  92. }
  93. expect('method did not throw an error').not.to.exist
  94. })
  95. })
  96. })