isexe 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/options.ts +7 -0
- package/src/posix.ts +12 -13
- package/test/fixtures/index.ts +4 -1
- package/test/posix.ts +44 -17
package/package.json
CHANGED
package/src/options.ts
CHANGED
|
@@ -20,6 +20,13 @@ export interface IsexeOptions {
|
|
|
20
20
|
*/
|
|
21
21
|
gid?: number
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* effective group ID list to use when checking executable mode flags
|
|
25
|
+
* on posix
|
|
26
|
+
* Defaults to process.getgroups()
|
|
27
|
+
*/
|
|
28
|
+
groups?: number[]
|
|
29
|
+
|
|
23
30
|
/**
|
|
24
31
|
* The ;-delimited path extension list for win32 implementation.
|
|
25
32
|
* Defaults to process.env.PATHEXT
|
package/src/posix.ts
CHANGED
|
@@ -31,7 +31,10 @@ export const isexe = async (
|
|
|
31
31
|
* Synchronously determine whether a path is executable according to
|
|
32
32
|
* the mode and current (or specified) user and group IDs.
|
|
33
33
|
*/
|
|
34
|
-
export const sync = (
|
|
34
|
+
export const sync = (
|
|
35
|
+
path: string,
|
|
36
|
+
options: IsexeOptions = {}
|
|
37
|
+
): boolean => {
|
|
35
38
|
const { ignoreErrors = false } = options
|
|
36
39
|
try {
|
|
37
40
|
return checkStat(statSync(path), options)
|
|
@@ -46,23 +49,19 @@ const checkStat = (stat: Stats, options: IsexeOptions) =>
|
|
|
46
49
|
stat.isFile() && checkMode(stat, options)
|
|
47
50
|
|
|
48
51
|
const checkMode = (stat: Stats, options: IsexeOptions) => {
|
|
49
|
-
|
|
52
|
+
const myUid = options.uid ?? process.getuid?.()
|
|
53
|
+
const myGroups = options.groups ?? process.getgroups?.() ?? []
|
|
54
|
+
const myGid = options.gid ?? process.getgid?.() ?? myGroups[0]
|
|
55
|
+
if (myUid === undefined || myGid === undefined) {
|
|
50
56
|
throw new Error('cannot get uid or gid')
|
|
51
57
|
}
|
|
58
|
+
|
|
59
|
+
const groups = new Set([myGid, ...myGroups])
|
|
60
|
+
|
|
52
61
|
const mod = stat.mode
|
|
53
62
|
const uid = stat.uid
|
|
54
63
|
const gid = stat.gid
|
|
55
64
|
|
|
56
|
-
const myUid =
|
|
57
|
-
options.uid !== undefined
|
|
58
|
-
? options.uid
|
|
59
|
-
: process.getuid && process.getuid()
|
|
60
|
-
|
|
61
|
-
const myGid =
|
|
62
|
-
options.gid !== undefined
|
|
63
|
-
? options.gid
|
|
64
|
-
: process.getgid && process.getgid()
|
|
65
|
-
|
|
66
65
|
const u = parseInt('100', 8)
|
|
67
66
|
const g = parseInt('010', 8)
|
|
68
67
|
const o = parseInt('001', 8)
|
|
@@ -70,7 +69,7 @@ const checkMode = (stat: Stats, options: IsexeOptions) => {
|
|
|
70
69
|
|
|
71
70
|
return !!(
|
|
72
71
|
mod & o ||
|
|
73
|
-
(mod & g && gid
|
|
72
|
+
(mod & g && groups.has(gid)) ||
|
|
74
73
|
(mod & u && uid === myUid) ||
|
|
75
74
|
(mod & ug && myUid === 0)
|
|
76
75
|
)
|
package/test/fixtures/index.ts
CHANGED
|
@@ -6,23 +6,26 @@ export const createFixtures = (t: Tap.Test) => {
|
|
|
6
6
|
'meow.cat': '#!/usr/bin/env cat\nmeow\n',
|
|
7
7
|
'mine.cat':'#!/usr/bin/env cat\nmine\n',
|
|
8
8
|
'ours.cat':'#!/usr/bin/env cat\nours\n',
|
|
9
|
+
'others.cat':'#!/usr/bin/env cat\nothers\n',
|
|
9
10
|
'fail.false':'#!/usr/bin/env false\n',
|
|
10
11
|
})
|
|
11
12
|
|
|
12
13
|
const meow = resolve(dir, 'meow.cat')
|
|
13
14
|
const mine = resolve(dir, 'mine.cat')
|
|
14
15
|
const ours = resolve(dir, 'ours.cat')
|
|
16
|
+
const others = resolve(dir, 'others.cat')
|
|
15
17
|
const fail = resolve(dir, 'fail.false')
|
|
16
18
|
const enoent = resolve(dir, 'enoent.exe')
|
|
17
19
|
const modes = {
|
|
18
20
|
[meow]: 0o755,
|
|
19
21
|
[mine]: 0o744,
|
|
20
22
|
[ours]:0o754,
|
|
23
|
+
[others]:0o754,
|
|
21
24
|
[fail]:0o644,
|
|
22
25
|
}
|
|
23
26
|
for (const [path, mode] of Object.entries(modes)) {
|
|
24
27
|
chmodSync(path, mode)
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
return { meow, mine, ours, fail, enoent, modes }
|
|
30
|
+
return { meow, mine, ours, others, fail, enoent, modes }
|
|
28
31
|
}
|
package/test/posix.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import t from 'tap'
|
|
2
2
|
|
|
3
3
|
import { createFixtures } from './fixtures/index'
|
|
4
|
-
const { meow, fail, mine, ours, enoent, modes } = createFixtures(t)
|
|
4
|
+
const { meow, fail, mine, ours, others, enoent, modes } = createFixtures(t)
|
|
5
5
|
|
|
6
6
|
const isWindows = process.platform === 'win32'
|
|
7
7
|
|
|
@@ -13,20 +13,21 @@ import * as fsPromises from 'fs/promises'
|
|
|
13
13
|
// uid/gid comparisons
|
|
14
14
|
const mockStat = (path: string, st: Stats) =>
|
|
15
15
|
Object.assign(st, {
|
|
16
|
-
uid: 123,
|
|
17
|
-
gid: 321,
|
|
16
|
+
uid: path === others || path === ours ? 987 : 123,
|
|
17
|
+
gid: path === others ? 987 : 321,
|
|
18
18
|
mode: !modes[path] ? st.mode : setMode(st.mode, modes[path]),
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
const setMode = (orig: number, mode: number) => (orig & 0o7777000) | mode
|
|
22
22
|
|
|
23
|
-
const { getuid, getgid } = process
|
|
23
|
+
const { getuid, getgid, getgroups } = process
|
|
24
24
|
t.teardown(() => {
|
|
25
|
-
Object.assign(process, { getuid, getgid })
|
|
25
|
+
Object.assign(process, { getuid, getgid, getgroups })
|
|
26
26
|
})
|
|
27
27
|
Object.assign(process, {
|
|
28
28
|
getuid: () => 123,
|
|
29
29
|
getgid: () => 321,
|
|
30
|
+
getgroups: () => [321, 987],
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
const { isexe, sync } = t.mock('../dist/cjs/posix.js', {
|
|
@@ -44,11 +45,13 @@ const { isexe, sync } = t.mock('../dist/cjs/posix.js', {
|
|
|
44
45
|
t.test('basic tests', async t => {
|
|
45
46
|
t.equal(await isexe(meow), true)
|
|
46
47
|
t.equal(await isexe(ours), true)
|
|
48
|
+
t.equal(await isexe(others), true)
|
|
47
49
|
t.equal(await isexe(mine), true)
|
|
48
50
|
t.equal(await isexe(fail), false)
|
|
49
51
|
|
|
50
52
|
t.equal(sync(meow), true)
|
|
51
53
|
t.equal(sync(ours), true)
|
|
54
|
+
t.equal(sync(others), true)
|
|
52
55
|
t.equal(sync(mine), true)
|
|
53
56
|
t.equal(sync(fail), false)
|
|
54
57
|
|
|
@@ -60,51 +63,75 @@ t.test('basic tests', async t => {
|
|
|
60
63
|
|
|
61
64
|
t.test('override uid/gid', async t => {
|
|
62
65
|
t.test('same uid, different gid', async t => {
|
|
63
|
-
const o = { gid: 654 }
|
|
64
|
-
t.equal(await isexe(meow), true)
|
|
65
|
-
t.equal(await isexe(ours),
|
|
66
|
-
t.equal(await isexe(
|
|
67
|
-
t.equal(await isexe(
|
|
68
|
-
t.equal(
|
|
69
|
-
t.equal(sync(
|
|
70
|
-
t.equal(sync(
|
|
71
|
-
t.equal(sync(
|
|
66
|
+
const o = { gid: 654, groups: [] }
|
|
67
|
+
t.equal(await isexe(meow, o), true)
|
|
68
|
+
t.equal(await isexe(ours, o), false)
|
|
69
|
+
t.equal(await isexe(others, o), false)
|
|
70
|
+
t.equal(await isexe(mine, o), true)
|
|
71
|
+
t.equal(await isexe(fail, o), false)
|
|
72
|
+
t.equal(sync(meow, o), true)
|
|
73
|
+
t.equal(sync(ours, o), false)
|
|
74
|
+
t.equal(sync(others, o), false)
|
|
75
|
+
t.equal(sync(mine, o), true)
|
|
76
|
+
t.equal(sync(fail, o), false)
|
|
72
77
|
})
|
|
73
78
|
|
|
74
79
|
t.test('different uid, same gid', async t => {
|
|
75
80
|
const o = { uid: 456 }
|
|
76
81
|
t.equal(await isexe(meow, o), true)
|
|
77
82
|
t.equal(await isexe(ours, o), true)
|
|
83
|
+
t.equal(await isexe(others, o), true)
|
|
78
84
|
t.equal(await isexe(mine, o), false)
|
|
79
85
|
t.equal(await isexe(fail, o), false)
|
|
80
86
|
t.equal(sync(meow, o), true)
|
|
81
87
|
t.equal(sync(ours, o), true)
|
|
88
|
+
t.equal(sync(others, o), true)
|
|
82
89
|
t.equal(sync(mine, o), false)
|
|
83
90
|
t.equal(sync(fail, o), false)
|
|
84
91
|
})
|
|
85
92
|
|
|
86
93
|
t.test('different uid, different gid', async t => {
|
|
87
|
-
const o = { uid: 456, gid: 654 }
|
|
94
|
+
const o = { uid: 456, gid: 654, groups: [] }
|
|
88
95
|
t.equal(await isexe(meow, o), true)
|
|
89
96
|
t.equal(await isexe(ours, o), false)
|
|
97
|
+
t.equal(await isexe(others, o), false)
|
|
90
98
|
t.equal(await isexe(mine, o), false)
|
|
91
99
|
t.equal(await isexe(fail, o), false)
|
|
92
100
|
t.equal(sync(meow, o), true)
|
|
93
101
|
t.equal(sync(ours, o), false)
|
|
102
|
+
t.equal(sync(others, o), false)
|
|
94
103
|
t.equal(sync(mine, o), false)
|
|
95
104
|
t.equal(sync(fail, o), false)
|
|
96
105
|
})
|
|
106
|
+
|
|
107
|
+
t.test('root can run anything runnable', async t => {
|
|
108
|
+
const o = { uid: 0, gid: 999, groups: [] }
|
|
109
|
+
t.equal(await isexe(meow, o), true)
|
|
110
|
+
t.equal(await isexe(ours, o), true)
|
|
111
|
+
t.equal(await isexe(others, o), true)
|
|
112
|
+
t.equal(await isexe(mine, o), true)
|
|
113
|
+
t.equal(await isexe(fail, o), false)
|
|
114
|
+
t.equal(sync(meow, o), true)
|
|
115
|
+
t.equal(sync(ours, o), true)
|
|
116
|
+
t.equal(sync(others, o), true)
|
|
117
|
+
t.equal(sync(mine, o), true)
|
|
118
|
+
t.equal(sync(fail, o), false)
|
|
119
|
+
})
|
|
97
120
|
})
|
|
98
121
|
|
|
99
122
|
t.test('getuid/getgid required', async t => {
|
|
100
|
-
const { getuid, getgid } = process
|
|
101
|
-
t.teardown(() => { Object.assign(process, { getuid, getgid })})
|
|
123
|
+
const { getuid, getgid, getgroups } = process
|
|
124
|
+
t.teardown(() => { Object.assign(process, { getuid, getgid, getgroups })})
|
|
102
125
|
process.getuid = undefined
|
|
103
126
|
process.getgid = undefined
|
|
127
|
+
process.getgroups = undefined
|
|
104
128
|
t.throws(() => sync(meow), {
|
|
105
129
|
message: 'cannot get uid or gid'
|
|
106
130
|
})
|
|
107
131
|
t.rejects(isexe(meow), {
|
|
108
132
|
message: 'cannot get uid or gid'
|
|
109
133
|
})
|
|
134
|
+
// fine as long as a group/user is specified though
|
|
135
|
+
t.equal(sync(meow, { uid: 999, groups: [321] }), true)
|
|
136
|
+
t.equal(await isexe(meow, { uid: 999, groups: [321] }), true)
|
|
110
137
|
})
|