• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* eslint-disable camelcase */
2var t = require('tap')
3var test = t.test
4var assert = require('assert')
5var requireInject = require('require-inject')
6const common = require('../common-tap.js')
7var cache_dir = common.pkg
8common.skipIfWindows('windows does not use correct-mkdir behavior')
9
10test('correct-mkdir: no race conditions', function (t) {
11  var mock_fs = {}
12  var did_hook = false
13  mock_fs.lstat = function (path, cb) {
14    if (path === cache_dir) {
15      // Return a non-matching owner
16      cb(null, {
17        uid: +process.getuid() + 1,
18        isDirectory: function () {
19          return true
20        }
21      })
22      if (!did_hook) {
23        did_hook = true
24        doHook()
25      }
26    } else {
27      assert.ok(false, 'Unhandled stat path: ' + path)
28    }
29  }
30  var chown_in_progress = 0
31  var mock_chownr = function (path, uid, gid, cb) {
32    ++chown_in_progress
33    process.nextTick(function () {
34      --chown_in_progress
35      cb(null)
36    })
37  }
38  var mocks = {
39    'graceful-fs': mock_fs,
40    'chownr': mock_chownr,
41    'infer-owner': requireInject('infer-owner', { fs: mock_fs })
42  }
43  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)
44
45  var calls_in_progress = 3
46  function handleCallFinish () {
47    t.equal(chown_in_progress, 0, 'should not return while chown still in progress')
48    if (!--calls_in_progress) {
49      t.end()
50    }
51  }
52  function doHook () {
53    // This is fired during the first correctMkdir call, after the stat has finished
54    // but before the chownr has finished
55    // Buggy old code will fail and return a cached value before initial call is done
56    correctMkdir(cache_dir, handleCallFinish)
57  }
58  // Initial call
59  correctMkdir(cache_dir, handleCallFinish)
60  // Immediate call again in case of race condition there
61  correctMkdir(cache_dir, handleCallFinish)
62})
63
64test('correct-mkdir: ignore ENOENTs from chownr', function (t) {
65  var mock_fs = {}
66  mock_fs.lstat = function (path, cb) {
67    if (path === cache_dir) {
68      cb(null, {
69        isDirectory: function () {
70          return true
71        }
72      })
73    } else {
74      assert.ok(false, 'Unhandled stat path: ' + path)
75    }
76  }
77  var mock_chownr = function (path, uid, gid, cb) {
78    cb(Object.assign(new Error(), {code: 'ENOENT'}))
79  }
80  var mocks = {
81    'graceful-fs': mock_fs,
82    'chownr': mock_chownr
83  }
84  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)
85
86  function handleCallFinish (err) {
87    t.ifErr(err, 'chownr\'s ENOENT errors were ignored')
88    t.end()
89  }
90  correctMkdir(cache_dir, handleCallFinish)
91})
92
93// NEED TO RUN LAST
94
95// These test checks that Windows users are protected by crashes related to
96// unexpectedly having a UID/GID other than 0 if a user happens to add these
97// variables to their environment. There are assumptions in correct-mkdir
98// that special-case Windows by checking on UID-related things.
99test('correct-mkdir: SUDO_UID and SUDO_GID non-Windows', function (t) {
100  process.env.SUDO_UID = 999
101  process.env.SUDO_GID = 999
102  process.getuid = function () { return 0 }
103  process.getgid = function () { return 0 }
104  var mock_fs = {}
105  mock_fs.lstat = function (path, cb) {
106    if (path === cache_dir) {
107      cb(null, {
108        uid: 0,
109        isDirectory: function () {
110          return true
111        }
112      })
113    } else {
114      assert.ok(false, 'Unhandled stat path: ' + path)
115    }
116  }
117  var mock_chownr = function (path, uid, gid, cb) {
118    t.is(uid, +process.env.SUDO_UID, 'using the environment\'s UID')
119    t.is(gid, +process.env.SUDO_GID, 'using the environment\'s GID')
120    cb(null, {})
121  }
122  var mocks = {
123    'graceful-fs': mock_fs,
124    'chownr': mock_chownr
125  }
126  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)
127
128  function handleCallFinish () {
129    t.end()
130  }
131  correctMkdir(cache_dir, handleCallFinish)
132})
133
134test('correct-mkdir: SUDO_UID and SUDO_GID Windows', function (t) {
135  process.env.SUDO_UID = 999
136  process.env.SUDO_GID = 999
137  delete process.getuid
138  delete process.getgid
139  var mock_fs = {}
140  mock_fs.lstat = function (path, cb) {
141    if (path === cache_dir) {
142      cb(null, {
143        uid: 0,
144        isDirectory: function () {
145          return true
146        }
147      })
148    } else {
149      assert.ok(false, 'Unhandled stat path: ' + path)
150    }
151  }
152  var mock_chownr = function (path, uid, gid, cb) {
153    t.fail('chownr should not be called at all on Windows')
154    cb(new Error('nope'))
155  }
156  var mocks = {
157    'graceful-fs': mock_fs,
158    'chownr': mock_chownr
159  }
160  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)
161
162  function handleCallFinish (err) {
163    t.ifErr(err, 'chownr was not called because Windows')
164    t.end()
165  }
166  correctMkdir(cache_dir, handleCallFinish)
167})
168