-
Notifications
You must be signed in to change notification settings - Fork 56
chore: rolling promotion dev -> main #2874
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,6 +69,48 @@ function currentUid(): bigint { | |
| return BigInt(process.getuid()); | ||
| } | ||
|
|
||
| function currentGid(): bigint { | ||
| if (process.getegid === undefined) | ||
| throw new CanonicalInstallLinkError('canonical link requires a POSIX group identity'); | ||
| return BigInt(process.getegid()); | ||
| } | ||
|
|
||
| /** | ||
| * Pure safety classifier for a parent directory of the canonical install link. | ||
| * Ownership contract: owned by the current uid, at least one hard link, never | ||
| * world-writable, and group-writable ONLY when the directory's group is the | ||
| * process's effective group — the Debian/Ubuntu user-private-group layout | ||
| * (umask 002 → `~/.local/bin` is 0775 <user>:<user>), which is as private as | ||
| * 0755. A group-writable directory owned by any other group stays rejected: | ||
| * unknown group members could swap the `genie` link. | ||
| */ | ||
| export function classifyOwnedDirectorySafety( | ||
| stat: Pick<BigIntStats, 'uid' | 'gid' | 'nlink' | 'mode'>, | ||
| identity: { uid: bigint; gid: bigint }, | ||
| ): { ok: true } | { ok: false; reason: string; remedy: string } { | ||
| const mode = Number(stat.mode & 0o777n); | ||
| const octal = mode.toString(8).padStart(3, '0'); | ||
| if (stat.uid !== identity.uid) { | ||
| return { | ||
| ok: false, | ||
| reason: `is owned by uid ${stat.uid}, not the current user (uid ${identity.uid})`, | ||
| remedy: 'chown it to your user (it may have been created with sudo), then retry', | ||
| }; | ||
| } | ||
| if (stat.nlink < 1n) return { ok: false, reason: 'has no hard links', remedy: 'recreate the directory, then retry' }; | ||
| if ((mode & 0o002) !== 0) { | ||
| return { ok: false, reason: `is world-writable (mode ${octal})`, remedy: 'run: chmod o-w <path>, then retry' }; | ||
| } | ||
| if ((mode & 0o020) !== 0 && stat.gid !== identity.gid) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Do not treat the effective group as a private group. A shared effective group can own a user-owned Reject group-writable directories unless a trusted platform-specific check proves the group is private. Alternatively, require sticky-directory semantics before allowing shared-group writes. Update the 🤖 Prompt for AI Agents |
||
| return { | ||
| ok: false, | ||
| reason: `is writable by group ${stat.gid}, which is not your effective group (gid ${identity.gid}); mode ${octal}`, | ||
| remedy: 'run: chmod g-w <path>, then retry', | ||
| }; | ||
| } | ||
| return { ok: true }; | ||
| } | ||
|
|
||
| function fdReferencePath(fd: number): string { | ||
| if (process.platform === 'linux') return `/proc/self/fd/${fd}`; | ||
| if (process.platform === 'darwin') return `/dev/fd/${fd}`; | ||
|
|
@@ -127,8 +169,9 @@ function assertSafeOwnedDirectoryStat(stat: BigIntStats, label: string): void { | |
| if (!stat.isDirectory() || stat.isSymbolicLink()) { | ||
| throw new CanonicalInstallLinkError(`${label} is not a physical directory`); | ||
| } | ||
| if (stat.uid !== currentUid() || stat.nlink < 1n || Number(stat.mode & 0o022n) !== 0) { | ||
| throw new CanonicalInstallLinkError(`${label} is not current-user-owned with safe permissions`); | ||
| const verdict = classifyOwnedDirectorySafety(stat, { uid: currentUid(), gid: currentGid() }); | ||
| if (!verdict.ok) { | ||
| throw new CanonicalInstallLinkError(`${label} ${verdict.reason} — ${verdict.remedy.replace('<path>', label)}`); | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the user's effective GID is shared (for example, macOS's common
staffgroup or a Unix account whose primary group contains multiple users), this condition accepts a group-writable~/.local/binmerely because its GID equalsgetegid(). That equality proves membership, not exclusivity: any other group member can rename or replace thegenieentry in a non-sticky 0775 directory, causing arbitrary code to run as the victim the next time they invoke it. Keep rejecting group-writable ancestors unless the group is verified to be user-private.Useful? React with 👍 / 👎.