vulnforge-mcp / src /capability_revocation /capability_revocation_audit.ts
mason-keller
feat: add kernel capability revocation & ephemeral syscall confinement synthesizer (v1.40.0)
a253c72
Raw History Blame Contribute Delete
29.4 kB
/**
* VulnForge MCP - Autonomous Kernel Capability Revocation & Ephemeral Syscall Confinement Synthesizer
* @module capability_revocation/capability_revocation_audit
*
* Implements static and dynamic architectural security auditing for:
* 1. VF-CAPREV-001 (CRITICAL, CWE-250/CWE-269): Unrestricted Ambient Linux Capabilities & Missing Ephemeral Capability Dropping
* 2. VF-CAPREV-002 (CRITICAL, CWE-284/CWE-693): Inadequate Landlock LSM Filesystem Confinement & Unrestricted Path Traversal
* 3. VF-CAPREV-003 (CRITICAL, CWE-754/CWE-653): Unenforced eBPF LSM Syscall Confinement & Missing LSM Probes
* 4. VF-CAPREV-004 (HIGH, CWE-276/CWE-281): Permissive Inherited File Descriptor & Socket Capability Leakage
* 5. VF-CAPREV-005 (HIGH, CWE-400/CWE-770): Absent Ephemeral Process Lifetime Quotas & Missing cgroups v2 Kernel Resource Confinement
* 6. VF-CAPREV-006 (HIGH, CWE-385/CWE-362): Time-of-Check to Time-of-Use (TOCTOU) Capability Restoration Race
* 7. VF-CAPREV-007 (HIGH, CWE-778/CWE-754): Unaudited Capability Transition Events & Missing Tamper-Evident Confinement Audit Logging
* 8. VF-CAPREV-008 (HIGH, CWE-284/CWE-732): Fail-Open Kernel Capability Fallback on Unsupported LSM Environment
*/
import {
FuzzMutationCase,
CapabilityRevocationArtifactFormat,
CapabilityRevocationAuditInput,
CapabilityRevocationAuditResult,
CapabilityRevocationAuditSummary,
CapabilityRevocationFindingCode,
CapabilityRevocationSecurityPolicy,
PatchRecommendation,
SecurityFinding
} from '../types/index.js';
export const DEFAULT_CAPABILITY_REVOCATION_POLICY: CapabilityRevocationSecurityPolicy = {
policyVersion: '1.40.0',
capabilityDropping: {
enforceCapabilityDrop: true,
clearAmbientCapabilities: true,
dropBoundingSet: true,
allowedCapabilities: [],
disallowRootCapabilities: true
},
landlockLsmConfinement: {
enforceLandlockRuleset: true,
abiVersion: 3,
enforceNoNewPrivs: true,
readOnlyPaths: ['/usr', '/lib', '/etc/ssl'],
readWritePaths: ['/tmp/agent-ephemeral'],
deniedPaths: ['/proc/sys', '/sys', '/root'],
disallowExecutablePaths: true
},
ebpfLsmHooks: {
enforceEbpfLsmHooks: true,
monitoredHooks: ['file_open', 'socket_connect', 'task_prctl', 'bpf'],
blockUnauthorizedSockets: true,
blockRawSockets: true,
blockPrivilegedSyscalls: true
},
fdCloexecSanitization: {
enforceFdCloexec: true,
closeInheritedFds: true,
maxFdToPreserve: 2,
disallowLeakedSockets: true
},
cgroupsResourceConfinement: {
enforceCgroupsLimits: true,
maxPids: 64,
maxMemoryBytes: 2147483648,
cpuQuotaUs: 100000,
executionTimeoutSeconds: 300
},
atomicPrivilegeTransition: {
enforceAtomicTransition: true,
enforceSecurebits: true,
securebitsFlags: ['SECBIT_NOROOT', 'SECBIT_NO_SETUID_FIXUP', 'SECBIT_NOROOT_LOCKED'],
synchronizeThreadPrivileges: true
},
auditTrailVerification: {
cryptographicEventChaining: true,
hashAlgorithm: 'SHA-256',
logCapabilityTransitions: true,
failSecureOnAuditTampering: true
},
failClosedPosture: {
failClosedOnUnsupportedLsm: true,
requireLandlockSupport: true,
requireEbpfLsmSupport: true,
abortOnDegradedEnvironment: true
}
};
/**
* Detects whether content belongs to Kernel Capability Revocation & Ephemeral Syscall Confinement artifacts.
*/
export function isCapabilityRevocationContent(content: string): boolean {
if (!content) return false;
const c = content.toLowerCase();
return (
c.includes('capability_revocation') ||
c.includes('capabilityrevocation') ||
c.includes('landlock') ||
c.includes('landlock_create_ruleset') ||
c.includes('landlock_restrict_self') ||
c.includes('cap_set_proc') ||
c.includes('pr_cap_ambient_clear_all') ||
c.includes('pr_set_no_new_privs') ||
c.includes('ebpf_lsm') ||
c.includes('bpf_lsm') ||
c.includes('fd_cloexec') ||
c.includes('o_cloexec') ||
c.includes('confinement_manifest') ||
c.includes('cgroupsresourceconfinement') ||
c.includes('capabilitydropping') ||
c.includes('landlocklsmconfinement') ||
c.includes('failclosedposture')
);
}
/**
* Detects artifact format of Capability Revocation & Ephemeral Syscall Confinement specs.
*/
export function detectCapabilityRevocationFormat(
content: string,
filePath?: string
): CapabilityRevocationArtifactFormat {
if (filePath) {
const lower = filePath.toLowerCase();
if (lower.endsWith('.landlock') || lower.includes('landlock')) return 'LANDLOCK_RULES';
if (lower.endsWith('.lsm.c') || lower.includes('ebpf_lsm') || lower.includes('bpf_lsm')) return 'EBPF_LSM_POLICY';
if (lower.includes('cap_drop') || lower.includes('capability')) return 'CAPABILITY_DROP_SPEC';
if (lower.includes('confinement') || lower.includes('confinement_manifest')) return 'CONFINEMENT_MANIFEST';
}
const c = content.toLowerCase();
if (c.includes('landlock_create_ruleset') || c.includes('landlock_access_fs') || c.includes('landlock_ruleset_attr')) {
return 'LANDLOCK_RULES';
}
if (c.includes('sec("lsm/') || c.includes('bpf_lsm_') || c.includes('bpf_prog_type_lsm')) {
return 'EBPF_LSM_POLICY';
}
if (c.includes('pr_cap_ambient_clear_all') || c.includes('cap_set_proc') || c.includes('allowedcapabilities')) {
return 'CAPABILITY_DROP_SPEC';
}
if (c.includes('confinement_manifest') || c.includes('confinementmanifest') || c.includes('cgroupsresourceconfinement')) {
return 'CONFINEMENT_MANIFEST';
}
if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
return 'JSON';
}
if (content.includes('---') || (content.includes(':') && !content.includes(';'))) {
return 'YAML';
}
return 'UNKNOWN';
}
/**
* Synthesizes a hardened Capability Revocation & Ephemeral Syscall Confinement policy from partial configuration.
*/
export function synthesizeCapabilityRevocationPolicyFromConfig(
config?: Partial<CapabilityRevocationSecurityPolicy>
): CapabilityRevocationSecurityPolicy {
return {
policyVersion: config?.policyVersion || DEFAULT_CAPABILITY_REVOCATION_POLICY.policyVersion,
capabilityDropping: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.capabilityDropping,
...(config?.capabilityDropping || {})
},
landlockLsmConfinement: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.landlockLsmConfinement,
...(config?.landlockLsmConfinement || {})
},
ebpfLsmHooks: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.ebpfLsmHooks,
...(config?.ebpfLsmHooks || {})
},
fdCloexecSanitization: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.fdCloexecSanitization,
...(config?.fdCloexecSanitization || {})
},
cgroupsResourceConfinement: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.cgroupsResourceConfinement,
...(config?.cgroupsResourceConfinement || {})
},
atomicPrivilegeTransition: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.atomicPrivilegeTransition,
...(config?.atomicPrivilegeTransition || {})
},
auditTrailVerification: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.auditTrailVerification,
...(config?.auditTrailVerification || {})
},
failClosedPosture: {
...DEFAULT_CAPABILITY_REVOCATION_POLICY.failClosedPosture,
...(config?.failClosedPosture || {})
}
};
}
export const synthesizeCapabilityRevocationSecurityPolicy = synthesizeCapabilityRevocationPolicyFromConfig;
/**
* Generates an adversarial mutation fuzz suite for Capability Revocation & Ephemeral Syscall Confinement testing.
*/
export function generateCapabilityRevocationFuzzSuite(
basePolicy?: Partial<CapabilityRevocationSecurityPolicy>
): FuzzMutationCase[] {
const policy = synthesizeCapabilityRevocationPolicyFromConfig(basePolicy);
const fuzzCases: FuzzMutationCase[] = [
{
id: 'FUZZ-CAPREV-001',
field: 'capabilityDropping.enforceCapabilityDrop',
mutationType: 'STATE_INJECTION',
payload: { enforceCapabilityDrop: false, clearAmbientCapabilities: false, allowedCapabilities: ['CAP_SYS_ADMIN'] },
description: 'Retain ambient Linux capabilities and skip prctl ambient clear',
expectedStatus: 400
},
{
id: 'FUZZ-CAPREV-002',
field: 'landlockLsmConfinement.enforceLandlockRuleset',
mutationType: 'STATE_INJECTION',
payload: { enforceLandlockRuleset: false, enforceNoNewPrivs: false, readWritePaths: ['/'] },
description: 'Disable Landlock ruleset enforcement and permit root filesystem write access',
expectedStatus: 400
},
{
id: 'FUZZ-CAPREV-003',
field: 'ebpfLsmHooks.enforceEbpfLsmHooks',
mutationType: 'STATE_INJECTION',
payload: { enforceEbpfLsmHooks: false, blockUnauthorizedSockets: false },
description: 'Disable eBPF LSM syscall hook interception allowing arbitrary sockets',
expectedStatus: 400
},
{
id: 'FUZZ-CAPREV-004',
field: 'fdCloexecSanitization.enforceFdCloexec',
mutationType: 'STATE_INJECTION',
payload: { enforceFdCloexec: false, closeInheritedFds: false, maxFdToPreserve: 1024 },
description: 'Disable FD_CLOEXEC allowing child processes to inherit privileged descriptors',
expectedStatus: 400
},
{
id: 'FUZZ-CAPREV-005',
field: 'cgroupsResourceConfinement.enforceCgroupsLimits',
mutationType: 'STATE_INJECTION',
payload: { enforceCgroupsLimits: false, maxPids: 999999 },
description: 'Remove cgroups v2 process table boundaries risking runaway fork bombs',
expectedStatus: 400
},
{
id: 'FUZZ-CAPREV-006',
field: 'atomicPrivilegeTransition.enforceAtomicTransition',
mutationType: 'STATE_INJECTION',
payload: { enforceAtomicTransition: false, enforceSecurebits: false },
description: 'Omit securebits locks allowing threads to re-acquire root privileges across execve',
expectedStatus: 400
},
{
id: 'FUZZ-CAPREV-007',
field: 'auditTrailVerification.cryptographicEventChaining',
mutationType: 'STATE_INJECTION',
payload: { cryptographicEventChaining: false, logCapabilityTransitions: false },
description: 'Disable cryptographic SHA-256 hash chaining on capability transition audit events',
expectedStatus: 400
},
{
id: 'FUZZ-CAPREV-008',
field: 'failClosedPosture.failClosedOnUnsupportedLsm',
mutationType: 'STATE_INJECTION',
payload: { failClosedOnUnsupportedLsm: false, abortOnDegradedEnvironment: false },
description: 'Permit unconfined fail-open execution on unsupported kernel LSM host environments',
expectedStatus: 400
}
];
return fuzzCases;
}
/**
* Conducts static and dynamic architectural security audits for Kernel Capability Revocation & Ephemeral Syscall Confinement.
*/
export async function auditCapabilityRevocationSecurity(
input: CapabilityRevocationAuditInput
): Promise<CapabilityRevocationAuditResult> {
const content = input.content || '';
const filePath = input.filePath || 'confinement_policy.json';
const format = input.format || detectCapabilityRevocationFormat(content, filePath);
const findings: SecurityFinding[] = [];
const patchRecommendations: PatchRecommendation[] = [];
let parsedConfig: any = null;
try {
parsedConfig = JSON.parse(content);
} catch {
parsedConfig = null;
}
const raw = content.toLowerCase();
// Helper check functions
const has = (key: string): boolean => raw.includes(key.toLowerCase());
const hasInConfig = (path: string[], expectedVal?: any): boolean => {
if (!parsedConfig) return false;
let curr = parsedConfig;
for (const p of path) {
if (curr && typeof curr === 'object' && p in curr) {
curr = curr[p];
} else {
return false;
}
}
if (expectedVal !== undefined) {
return curr === expectedVal;
}
return Boolean(curr);
};
// 1. VF-CAPREV-001 (CRITICAL, CWE-250/CWE-269): Unrestricted Ambient Linux Capabilities & Missing Ephemeral Capability Dropping
const retainsAmbientCaps =
hasInConfig(['capabilityDropping', 'clearAmbientCapabilities'], false) ||
hasInConfig(['capabilityDropping', 'enforceCapabilityDrop'], false) ||
has('allow_ambient_capabilities: true') ||
has('clear_ambient_capabilities: false') ||
has('cap_sys_admin') ||
has('cap_net_admin') ||
(parsedConfig?.capabilityDropping?.allowedCapabilities &&
parsedConfig.capabilityDropping.allowedCapabilities.some((cap: string) =>
['CAP_SYS_ADMIN', 'CAP_NET_ADMIN', 'CAP_SYS_PTRACE', 'CAP_DAC_OVERRIDE', 'CAP_SYS_RAWIO'].includes(
cap.toUpperCase()
)
)) ||
(!has('pr_cap_ambient_clear_all') &&
!has('clearambientcapabilities') &&
!hasInConfig(['capabilityDropping', 'clearAmbientCapabilities'], true));
if (retainsAmbientCaps) {
findings.push({
id: 'VF-CAPREV-001',
findingCode: 'VF-CAPREV-001',
title: 'Unrestricted Ambient Linux Capabilities & Missing Ephemeral Capability Dropping',
severity: 'CRITICAL',
category: 'kernel_capability_revocation',
cwe: 'CWE-250',
target: filePath,
description:
'The process does not deterministically drop ambient or bounding Linux capabilities after initialization. Retaining high-privilege capabilities (e.g., CAP_SYS_ADMIN, CAP_NET_ADMIN) allows compromised AI agent subprocesses to perform privileged operations or escape container boundaries.',
evidence: parsedConfig?.capabilityDropping
? `capabilityDropping: ${JSON.stringify(parsedConfig.capabilityDropping)}`
: 'Missing PR_CAP_AMBIENT_CLEAR_ALL or cap_set_proc capability reduction routine',
impact:
'Compromised child processes inherit dangerous administrative capabilities, leading to complete host/container compromise.',
remediation:
'Invoke prctl(PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0, 0) and cap_set_proc() immediately after process startup, ensuring an empty ambient capability set.',
patchRecommendation: {
patchType: 'capability_dropping_enforce',
targetFile: filePath,
description: 'Drop all ambient and bounding capabilities via PR_CAP_AMBIENT_CLEAR_ALL and cap_set_proc().'
}
});
}
// 2. VF-CAPREV-002 (CRITICAL, CWE-284/CWE-693): Inadequate Landlock LSM Filesystem Confinement & Unrestricted Path Traversal
const lacksLandlockConfinement =
hasInConfig(['landlockLsmConfinement', 'enforceLandlockRuleset'], false) ||
hasInConfig(['landlockLsmConfinement', 'enforceNoNewPrivs'], false) ||
has('enforce_landlock_ruleset: false') ||
has('enforce_no_new_privs: false') ||
(parsedConfig?.landlockLsmConfinement?.readWritePaths &&
parsedConfig.landlockLsmConfinement.readWritePaths.includes('/')) ||
(!has('landlock_create_ruleset') &&
!has('landlocklsmconfinement') &&
!hasInConfig(['landlockLsmConfinement', 'enforceLandlockRuleset'], true));
if (lacksLandlockConfinement) {
findings.push({
id: 'VF-CAPREV-002',
findingCode: 'VF-CAPREV-002',
title: 'Inadequate Landlock LSM Filesystem Confinement & Unrestricted Path Traversal',
severity: 'CRITICAL',
category: 'landlock_filesystem_confinement',
cwe: 'CWE-284',
target: filePath,
description:
'Filesystem confinement via Linux Landlock LSM is missing or misconfigured. Running without PR_SET_NO_NEW_PRIVS or with unconstrained root directory write access exposes the host system to arbitrary file overwriting and path traversal.',
evidence: parsedConfig?.landlockLsmConfinement
? `landlockLsmConfinement: ${JSON.stringify(parsedConfig.landlockLsmConfinement)}`
: 'Missing landlock_create_ruleset or landlock_restrict_self invocation',
impact:
'Autonomous AI agents can traverse host directory hierarchies, overwrite critical binaries, and persist malicious artifacts.',
remediation:
'Define explicit Landlock ABI v3 rulesets restricting read-only hierarchies to dependency directories and isolated ephemeral write paths, preceded by prctl(PR_SET_NO_NEW_PRIVS, 1).',
patchRecommendation: {
patchType: 'landlock_ruleset_enforce',
targetFile: filePath,
description: 'Apply strict Landlock filesystem confinement ruleset with isolated ephemeral read/write boundaries.'
}
});
}
// 3. VF-CAPREV-003 (CRITICAL, CWE-754/CWE-653): Unenforced eBPF LSM Syscall Confinement & Missing LSM Probes
const lacksEbpfLsm =
hasInConfig(['ebpfLsmHooks', 'enforceEbpfLsmHooks'], false) ||
hasInConfig(['ebpfLsmHooks', 'blockUnauthorizedSockets'], false) ||
has('enforce_ebpf_lsm_hooks: false') ||
has('block_unauthorized_sockets: false') ||
(!has('bpf_lsm_') &&
!has('ebpflsmhooks') &&
!hasInConfig(['ebpfLsmHooks', 'enforceEbpfLsmHooks'], true));
if (lacksEbpfLsm) {
findings.push({
id: 'VF-CAPREV-003',
findingCode: 'VF-CAPREV-003',
title: 'Unenforced eBPF LSM Syscall Confinement & Missing LSM Probes',
severity: 'CRITICAL',
category: 'ebpf_lsm_syscall_confinement',
cwe: 'CWE-754',
target: filePath,
description:
'The execution environment does not attach eBPF LSM hooks to sensitive kernel syscall checkpoints (file_open, socket_connect, task_prctl, bpf), permitting arbitrary raw socket creation or unconfined syscall invocation.',
evidence: parsedConfig?.ebpfLsmHooks
? `ebpfLsmHooks: ${JSON.stringify(parsedConfig.ebpfLsmHooks)}`
: 'Missing eBPF LSM attachment hooks on sensitive kernel operations',
impact:
'Agent workloads can establish unauthorized outbound network sockets or load malicious unverified eBPF programs.',
remediation:
'Attach eBPF LSM hooks to intercept security_socket_connect, security_file_open, and security_task_prctl, enforcing strict protocol denylists.',
patchRecommendation: {
patchType: 'ebpf_lsm_hooks_enforce',
targetFile: filePath,
description: 'Attach eBPF LSM hooks to deny unconfined socket connections and privileged syscalls.'
}
});
}
// 4. VF-CAPREV-004 (HIGH, CWE-276/CWE-281): Permissive Inherited File Descriptor & Socket Capability Leakage
const leaksFds =
hasInConfig(['fdCloexecSanitization', 'enforceFdCloexec'], false) ||
hasInConfig(['fdCloexecSanitization', 'closeInheritedFds'], false) ||
has('enforce_fd_cloexec: false') ||
has('close_inherited_fds: false') ||
(parsedConfig?.fdCloexecSanitization?.maxFdToPreserve !== undefined &&
parsedConfig.fdCloexecSanitization.maxFdToPreserve > 2) ||
(!has('fd_cloexec') &&
!has('o_cloexec') &&
!hasInConfig(['fdCloexecSanitization', 'enforceFdCloexec'], true));
if (leaksFds) {
findings.push({
id: 'VF-CAPREV-004',
findingCode: 'VF-CAPREV-004',
title: 'Permissive Inherited File Descriptor & Socket Capability Leakage',
severity: 'HIGH',
category: 'fd_cloexec_leakage',
cwe: 'CWE-276',
target: filePath,
description:
'File descriptors allocated by the parent runtime do not enforce FD_CLOEXEC, allowing child agent processes to inherit active database connections, IPC pipes, or administrative sockets.',
evidence: parsedConfig?.fdCloexecSanitization
? `fdCloexecSanitization: ${JSON.stringify(parsedConfig.fdCloexecSanitization)}`
: 'Missing FD_CLOEXEC flags on allocated file descriptors and inherited sockets',
impact:
'Child processes can read, write, or hijack communication channels established by the parent orchestrator.',
remediation:
'Apply O_CLOEXEC / SOCK_CLOEXEC on all descriptor allocations and close all file descriptors greater than STDERR_FILENO prior to executing subcommands.',
patchRecommendation: {
patchType: 'fd_cloexec_enforce',
targetFile: filePath,
description: 'Set FD_CLOEXEC on all descriptors and close inherited non-standard descriptors before exec.'
}
});
}
// 5. VF-CAPREV-005 (HIGH, CWE-400/CWE-770): Absent Ephemeral Process Lifetime Quotas & Missing cgroups v2 Kernel Resource Confinement
const lacksCgroups =
hasInConfig(['cgroupsResourceConfinement', 'enforceCgroupsLimits'], false) ||
has('enforce_cgroups_limits: false') ||
(parsedConfig?.cgroupsResourceConfinement?.maxPids !== undefined &&
parsedConfig.cgroupsResourceConfinement.maxPids > 256) ||
(!has('cgroups') &&
!has('pids.max') &&
!hasInConfig(['cgroupsResourceConfinement', 'enforceCgroupsLimits'], true));
if (lacksCgroups) {
findings.push({
id: 'VF-CAPREV-005',
findingCode: 'VF-CAPREV-005',
title: 'Absent Ephemeral Process Lifetime Quotas & Missing cgroups v2 Kernel Resource Confinement',
severity: 'HIGH',
category: 'cgroups_resource_confinement',
cwe: 'CWE-400',
target: filePath,
description:
'Autonomous AI agent processes run without cgroups v2 slice boundaries (pids.max, memory.high, cpu.max). Unmetered agent execution can result in fork-bomb exhaustion or runaway memory consumption.',
evidence: parsedConfig?.cgroupsResourceConfinement
? `cgroupsResourceConfinement: ${JSON.stringify(parsedConfig.cgroupsResourceConfinement)}`
: 'Missing cgroups v2 resource controller bindings (pids.max, memory.max)',
impact:
'Runaway or hostile agent child tasks can deplete host process tables and memory, inducing node-wide Denial of Service.',
remediation:
'Enforce cgroups v2 resource confinement setting pids.max <= 64, memory.high <= 2GB, and bind process execution to bounded lifetime watchdogs.',
patchRecommendation: {
patchType: 'cgroups_confinement_enforce',
targetFile: filePath,
description: 'Bind agent execution to cgroups v2 with strict pids.max and memory limits.'
}
});
}
// 6. VF-CAPREV-006 (HIGH, CWE-385/CWE-362): Time-of-Check to Time-of-Use (TOCTOU) Capability Restoration Race
const vulnerableToctou =
hasInConfig(['atomicPrivilegeTransition', 'enforceAtomicTransition'], false) ||
hasInConfig(['atomicPrivilegeTransition', 'enforceSecurebits'], false) ||
has('enforce_atomic_transition: false') ||
has('enforce_securebits: false') ||
(parsedConfig?.atomicPrivilegeTransition?.securebitsFlags &&
parsedConfig.atomicPrivilegeTransition.securebitsFlags.length === 0) ||
(!has('securebits') &&
!has('secbit_noroot') &&
!hasInConfig(['atomicPrivilegeTransition', 'enforceAtomicTransition'], true));
if (vulnerableToctou) {
findings.push({
id: 'VF-CAPREV-006',
findingCode: 'VF-CAPREV-006',
title: 'Time-of-Check to Time-of-Use (TOCTOU) Capability Restoration Race',
severity: 'HIGH',
category: 'toctou_privilege_restoration',
cwe: 'CWE-385',
target: filePath,
description:
'Privilege transitions are not executed atomically across all task threads or omit locked securebits (SECBIT_NOROOT_LOCKED, SECBIT_NO_SETUID_FIXUP_LOCKED), permitting capability re-acquisition across execve or thread cloning.',
evidence: parsedConfig?.atomicPrivilegeTransition
? `atomicPrivilegeTransition: ${JSON.stringify(parsedConfig.atomicPrivilegeTransition)}`
: 'Missing PR_SET_SECUREBITS lock flags and thread synchronization',
impact:
'Threads can exploit transition latency windows to re-elevate capabilities or execute setuid binaries.',
remediation:
'Lock securebits flags (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED | SECBIT_NO_SETUID_FIXUP_LOCKED) and ensure atomic capability transition synchronization across threads.',
patchRecommendation: {
patchType: 'securebits_lock_enforce',
targetFile: filePath,
description: 'Lock securebits flags and enforce atomic process-wide privilege transitions.'
}
});
}
// 7. VF-CAPREV-007 (HIGH, CWE-778/CWE-754): Unaudited Capability Transition Events & Missing Tamper-Evident Confinement Audit Logging
const lacksAuditTrail =
hasInConfig(['auditTrailVerification', 'cryptographicEventChaining'], false) ||
hasInConfig(['auditTrailVerification', 'logCapabilityTransitions'], false) ||
has('cryptographic_event_chaining: false') ||
has('log_capability_transitions: false') ||
(!has('cryptographiceventchaining') &&
!has('logcapabilitytransitions') &&
!hasInConfig(['auditTrailVerification', 'cryptographicEventChaining'], true));
if (lacksAuditTrail) {
findings.push({
id: 'VF-CAPREV-007',
findingCode: 'VF-CAPREV-007',
title: 'Unaudited Capability Transition Events & Missing Tamper-Evident Confinement Audit Logging',
severity: 'HIGH',
category: 'confinement_audit_trail',
cwe: 'CWE-778',
target: filePath,
description:
'Capability drops, Landlock rule applications, and eBPF LSM denials are executed without cryptographically verifiable hash chaining, preventing forensic validation and audit integrity.',
evidence: parsedConfig?.auditTrailVerification
? `auditTrailVerification: ${JSON.stringify(parsedConfig.auditTrailVerification)}`
: 'Missing cryptographic hash-chained audit telemetry for confinement transitions',
impact:
'Security incidents cannot be attributed or audited, and hostile privilege escalation attempts remain unrecorded.',
remediation:
'Implement tamper-evident SHA-256 chained audit event logging for all capability transitions and Landlock LSM violations.',
patchRecommendation: {
patchType: 'audit_trail_chain_enforce',
targetFile: filePath,
description: 'Enable cryptographic SHA-256 hash chaining on capability and LSM transition audit logs.'
}
});
}
// 8. VF-CAPREV-008 (HIGH, CWE-284/CWE-732): Fail-Open Kernel Capability Fallback on Unsupported LSM Environment
const failOpenFallback =
hasInConfig(['failClosedPosture', 'failClosedOnUnsupportedLsm'], false) ||
hasInConfig(['failClosedPosture', 'abortOnDegradedEnvironment'], false) ||
has('fail_closed_on_unsupported_lsm: false') ||
has('abort_on_degraded_environment: false') ||
(!has('failclosedonunsupportedlsm') &&
!has('abortondegradedenvironment') &&
!hasInConfig(['failClosedPosture', 'failClosedOnUnsupportedLsm'], true));
if (failOpenFallback) {
findings.push({
id: 'VF-CAPREV-008',
findingCode: 'VF-CAPREV-008',
title: 'Fail-Open Kernel Capability Fallback on Unsupported LSM Environment',
severity: 'HIGH',
category: 'fail_closed_lsm_posture',
cwe: 'CWE-284',
target: filePath,
description:
'The confinement policy permits fail-open execution if the underlying kernel lacks Landlock or eBPF LSM support, allowing autonomous AI agents to run unconfined in degraded host environments.',
evidence: parsedConfig?.failClosedPosture
? `failClosedPosture: ${JSON.stringify(parsedConfig.failClosedPosture)}`
: 'Missing fail-closed assertion on unsupported kernel LSM capabilities',
impact:
'Agents deployed on older or stripped host kernels execute with unconfined privileges without administrative warning.',
remediation:
'Set failClosedOnUnsupportedLsm: true to immediately abort process initialization if required LSM subsystems are uninitialized or unsupported.',
patchRecommendation: {
patchType: 'fail_closed_lsm_posture_enforce',
targetFile: filePath,
description: 'Enforce strict fail-closed termination when required kernel LSM subsystems are unavailable.'
}
});
}
// Calculate audit score & summary
const totalChecks = 8;
const findingsCount = findings.length;
const passedChecks = totalChecks - findingsCount;
const score = Math.max(0, Math.round((passedChecks / totalChecks) * 100));
const critical = findings.filter((f) => f.severity === 'CRITICAL').length;
const high = findings.filter((f) => f.severity === 'HIGH').length;
const medium = findings.filter((f) => f.severity === 'MEDIUM').length;
const low = findings.filter((f) => f.severity === 'LOW').length;
const passed = findingsCount === 0;
const complianceStatus = critical > 0 || high > 0 ? 'FAIL' : medium > 0 ? 'WARN' : 'PASS';
const attestationStatus = passed ? 'VERIFIED_SECURE' : 'ATTESTATION_FAILED';
const summary: CapabilityRevocationAuditSummary = {
score,
totalChecks,
passedChecks,
findingsCount,
severityCounts: { critical, high, medium, low },
format,
complianceStatus,
passed,
capabilityDropEnforced: !retainsAmbientCaps,
landlockConfinementAttested: !lacksLandlockConfinement,
ebpfLsmHooksVerified: !lacksEbpfLsm,
fdCloexecSanitized: !leaksFds,
cgroupsLimitsBound: !lacksCgroups,
atomicTransitionEnforced: !vulnerableToctou,
auditTrailChained: !lacksAuditTrail,
failClosedPostureGuaranteed: !failOpenFallback
};
const recommendedPolicy = synthesizeCapabilityRevocationPolicyFromConfig(parsedConfig || {});
return {
targetFile: filePath,
findings,
summary,
recommendedPolicy,
patchRecommendations,
passed,
complianceStatus,
attestationStatus,
fuzzCasesGenerated: 8
};
}