-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdirectory.ts
More file actions
70 lines (66 loc) · 2.89 KB
/
directory.ts
File metadata and controls
70 lines (66 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* @file Stub-directory creation + permission audit. The IPC directory is
* created with 0o700 so other users can't read or plant files; on POSIX we
* also verify ownership and permission bits before any write, since a
* pre-existing directory could belong to a different user or have permissive
* modes inherited from umask.
*/
import process from 'node:process'
import { ErrorCtor } from '../primordials/error'
import { getFs, getPath } from './_internal'
/**
* Ensure IPC directory exists for stub file creation. Uses restrictive (0o700)
* permissions so other users cannot read or write stub files. On POSIX, after
* `mkdir` we verify the directory is owned by the current user and not
* world/group-writable — protects against a prior local attacker pre-creating
* `.socket-ipc/<app>/` with permissive modes and planting symlinks for stub
* filenames. Throws if the directory fails the check.
*
* @internal
*/
export async function ensureIpcDirectory(filePath: string): Promise<void> {
const fs = getFs()
const path = getPath()
const dir = path.dirname(filePath)
await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 })
// Windows skip-path; tested on Windows runners.
/* c8 ignore start */
if (process.platform === 'win32') {
return
}
/* c8 ignore stop */
// oxlint-disable-next-line socket/prefer-exists-sync -- need lstat to discriminate symlink/dir via isDirectory().
const stats = await fs.promises.lstat(dir)
// Defensive: mkdir just succeeded so dir is a directory.
/* c8 ignore start */
if (!stats.isDirectory()) {
throw new ErrorCtor(`IPC path is not a directory: ${dir}`)
}
/* c8 ignore stop */
const getuid = process.getuid
/* c8 ignore next - process.getuid is always present on POSIX. */
const ownUid = typeof getuid === 'function' ? getuid.call(process) : -1
/* c8 ignore next 5 - Cross-user ownership guard fires only if the
IPC directory was created by a different uid; can't be triggered
in-test without spawning a separate user. */
if (ownUid !== -1 && stats.uid !== ownUid) {
throw new ErrorCtor(
`IPC directory ${dir} is owned by another user (uid ${stats.uid}); refusing to use it.`,
)
}
// Permission bits only (mask out file-type bits). Reject any group or
// other access — only owner bits may be set.
// eslint-disable-next-line no-bitwise
const mode = stats.mode & 0o777
/* c8 ignore next 7 - chmod-tightening fires only if umask leaves
group/other bits set; default Node umask 0o022 strips group-write
but keeps group/other read+execute, so the value depends on the
CI runner's umask. */
// eslint-disable-next-line no-bitwise
if ((mode & 0o077) !== 0) {
// Tighten an over-permissive directory we just inherited. Use chmod
// rather than fail outright so a first-run that inherits e.g. 0o755
// from umask still succeeds.
await fs.promises.chmod(dir, 0o700)
}
}