63 lines
2.2 KiB
JavaScript
63 lines
2.2 KiB
JavaScript
import { readFile } from 'node:fs/promises'
|
|
|
|
function groupId(groupFile, name) {
|
|
for (const line of groupFile.split('\n')) {
|
|
const fields = line.split(':')
|
|
if (fields[0] === name && /^\d+$/.test(fields[2] ?? '')) return Number(fields[2])
|
|
}
|
|
return null
|
|
}
|
|
|
|
function supplementaryGroups(status) {
|
|
const line = status.split('\n').find(value => value.startsWith('Groups:'))
|
|
if (line === undefined) return []
|
|
return line.slice('Groups:'.length).trim().split(/\s+/).filter(Boolean).map(Number).filter(Number.isInteger)
|
|
}
|
|
|
|
function mountOption(mounts, mountPoint, prefix) {
|
|
for (const line of mounts.split('\n')) {
|
|
const fields = line.split(' ')
|
|
if (fields[1] !== mountPoint) continue
|
|
return fields[3]?.split(',').find(value => value.startsWith(prefix)) ?? null
|
|
}
|
|
return null
|
|
}
|
|
|
|
async function optionalRead(path) {
|
|
try { return await readFile(path, 'utf8') } catch { return '' }
|
|
}
|
|
|
|
export async function detectRuntimeCapabilities({
|
|
groupPath = '/etc/group',
|
|
initStatusPath = '/proc/1/status',
|
|
mountsPath = '/proc/mounts',
|
|
ptraceScopePath = '/proc/sys/kernel/yama/ptrace_scope',
|
|
} = {}) {
|
|
const [groupsFile, initStatus, mounts, ptraceScopeValue] = await Promise.all([
|
|
optionalRead(groupPath),
|
|
optionalRead(initStatusPath),
|
|
optionalRead(mountsPath),
|
|
optionalRead(ptraceScopePath),
|
|
])
|
|
const enabledGroup = groupId(groupsFile, 'dsh-sudo-true')
|
|
const disabledGroup = groupId(groupsFile, 'dsh-sudo-false')
|
|
const initGroups = supplementaryGroups(initStatus)
|
|
const dshRootCapabilityEffective = enabledGroup !== null && initGroups.includes(enabledGroup)
|
|
const sudoSelection = dshRootCapabilityEffective
|
|
? 'enabled'
|
|
: (disabledGroup !== null && initGroups.includes(disabledGroup) ? 'disabled' : 'unknown')
|
|
const ptraceScope = /^\d+$/.test(ptraceScopeValue.trim()) ? Number(ptraceScopeValue.trim()) : null
|
|
const hidepid = mountOption(mounts, '/proc', 'hidepid=')
|
|
|
|
return {
|
|
dshRootCapabilityEffective,
|
|
agentIsolationEffective: false,
|
|
details: {
|
|
sudoSelection,
|
|
ptraceScope,
|
|
procHidepid: hidepid === null ? null : Number(hidepid.slice('hidepid='.length)),
|
|
agentIsolationReason: 'shared-node-process-identity',
|
|
},
|
|
}
|
|
}
|