@ebony-vinyl/dsh-our-free-model 2.8.0 → 2.8.3

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.
@@ -38,14 +38,52 @@ export async function openExternal(value, {
38
38
  } catch { return false }
39
39
  }
40
40
 
41
+ /**
42
+ * The account this process runs as, spelled so `icacls` cannot misresolve it.
43
+ *
44
+ * A bare account name is ambiguous: `icacls <file> /grant:r <USERNAME>:F` is
45
+ * resolved against both the account database and the computer account, and when
46
+ * the two differ only in case the computer account wins. The ACE then names
47
+ * `S-1-5-21-<machine>` — a SID no user token carries — while `inheritance:r` in
48
+ * the same call has already removed the inherited ACEs. The file is left with a
49
+ * DACL nothing can satisfy, so the writer's own `renameSync` fails EPERM and the
50
+ * file becomes unreadable, unwritable and undeletable (issue #738).
51
+ *
52
+ * `whoami /user` reports the SID of the current token and a SID has nothing to
53
+ * resolve. Its output is ASCII, so the OEM code page problem that kept the
54
+ * account name out of `whoami` in the first place (issue #456, PR #637) cannot
55
+ * reach it: a SID carries no localizable text. The qualified name stays as the
56
+ * fallback for a missing or failing `whoami`, and is still strictly safer than
57
+ * the bare name because it names the account domain instead of letting icacls
58
+ * choose between two candidates.
59
+ *
60
+ * Memoized: a token's SID cannot change while the process runs, and the stores
61
+ * this serves flush on a timer, so one `whoami` per process is enough.
62
+ */
63
+ let granteeCache
64
+ function currentUserGrantee(system32) {
65
+ if (granteeCache !== undefined) return granteeCache
66
+ try {
67
+ const out = execFileSync(path.join(system32, 'whoami.exe'), ['/user', '/fo', 'csv', '/nh'],
68
+ { encoding: 'latin1', windowsHide: true, shell: false })
69
+ const sid = /"(S-1-[0-9-]+)"/.exec(out)?.[1]
70
+ if (sid !== undefined) {
71
+ granteeCache = `*${sid}`
72
+ return granteeCache
73
+ }
74
+ } catch { /* fall through to the qualified name */ }
75
+ const user = process.env.USERNAME ?? ''
76
+ const domain = process.env.USERDOMAIN ?? ''
77
+ granteeCache = domain === '' ? user : `${domain}\\${user}`
78
+ return granteeCache
79
+ }
80
+
41
81
  export function restrictPermissions(file, directory = false) {
42
82
  if (process.platform !== 'win32') return fs.chmodSync(file, directory ? 0o700 : 0o600)
43
83
  const system32 = path.join(process.env.SystemRoot ?? 'C:\\Windows', 'System32')
44
- // whoami prints the account in the OEM code page, which corrupts non-ASCII
45
- // machine names once decoded as UTF-8; Node reads env vars through the
46
- // UTF-16 APIs, so USERNAME is always a correct Unicode string.
47
- const user = process.env.USERNAME ?? ''
84
+ // The `*` prefix marks the grantee as a SID, not a name icacls should look up.
85
+ const grantee = currentUserGrantee(system32)
48
86
  execFileSync(path.join(system32, 'icacls.exe'),
49
- [file, '/inheritance:r', '/grant:r', `${user}:${directory ? '(OI)(CI)F' : 'F'}`],
87
+ [file, '/inheritance:r', '/grant:r', `${grantee}:${directory ? '(OI)(CI)F' : 'F'}`],
50
88
  { stdio: 'ignore', windowsHide: true, shell: false })
51
89
  }