Permission check pipeline, fix engine, and state types for Linux filesystem permissions.
whyno-core is the pure-logic engine behind the whyno permission debugger. It takes a pre-gathered snapshot of filesystem state and deterministically evaluates whether an operation is allowed — with zero I/O at check time. When a check fails, it generates least-privilege fix suggestions ranked by impact.
Overview
whyno-core answers one question: "Why can't this user do this to that file?"
Given a SystemState (subject identity, path walk, mount table, MAC state, and the requested operation), the check pipeline evaluates seven layers of Linux permission enforcement:
- Mount — read-only,
noexec,nosuidfilesystem options - FsFlags — inode flags (
immutable,append-only) - Traversal —
+xon every ancestor directory - DAC — discretionary access control (uid/gid/mode bits + capability overrides)
- ACL — POSIX.1e access control lists with mask evaluation
- SELinux — mandatory access control (feature-gated)
- AppArmor — mandatory access control (feature-gated)
Each layer returns Pass, Fail, or Degraded (state unavailable). The fix engine then generates repair suggestions for every failed layer, simulates their cascading effects, and prunes redundant fixes.
Features
- Pure logic, zero I/O — all filesystem state is gathered externally and passed in via
SystemState - Seven-layer check pipeline — mount options, fs flags, traversal, DAC, ACL, SELinux, AppArmor
- Capability-aware DAC — models
CAP_DAC_OVERRIDE,CAP_DAC_READ_SEARCH,CAP_FOWNER, and more - POSIX.1e ACL evaluation — full mask interaction, named user/group entries, effective permission computation
- Fix engine — generates
chmod,chown,setfacl,mount -o remount, andchattrsuggestions - Impact scoring — fixes ranked 1 (least privilege) to 6 (broadest impact)
- Cascade simulation — applies fixes to cloned state, prunes cross-layer redundancies
- Serializable — all types derive
Serializefor JSON output - JSON Schema — all public types derive
JsonSchemaviaschemars
Installation
[]
= "0.3"
Quick Start
Build a SystemState
The check pipeline operates on a SystemState struct built from gathered OS data. The pipeline performs no I/O.
use ;
use Operation;
let subject = ResolvedSubject ;
let walk = vec!;
let mounts = MountTable;
let state = SystemState ;
Run Checks
use run_checks;
let report = run_checks;
if report.is_allowed else
Generate Fixes
use generate_fixes;
let plan = generate_fixes;
for fix in &plan.fixes
for warning in &plan.warnings
Key Types
State Types
| Type | Module | Purpose |
|---|---|---|
SystemState |
state |
Complete gathered state — subject, path walk, mounts, operation, MAC state |
Probe<T> |
state |
Tri-state: Known(T), Unknown, or Inaccessible — never produces false-green results |
ResolvedSubject |
state::subject |
UID, GID, supplementary groups, and effective capability bitmask |
PathComponent |
state::path |
Single path segment with stat, ACL, fs-flags, and mount reference |
MountTable |
state::mount |
Collection of mount entries with device-based lookup |
PosixAcl |
state::acl |
POSIX.1e ACL with mask application and effective permission helpers |
MacState |
state::mac |
SELinux and AppArmor probe results |
Operation |
operation |
Read, Write, Execute, Delete, Create, or Stat |
Check Types
| Type | Purpose |
|---|---|
CheckReport |
Aggregated results — is_allowed() for quick verdict, failed_layers() for diagnostics |
LayerResult |
Pass (with optional warnings), Fail (with detail), or Degraded (state unavailable) |
CoreLayer |
Mount, FsFlags, Traversal, Dac, Acl |
MacLayer |
SeLinux, AppArmor |
Fix Types
| Type | Purpose |
|---|---|
FixPlan |
Ordered fix list with warnings for high-impact changes |
Fix |
Target layer, structured action, impact score (1–6), and description |
FixAction |
Chmod, Chown, SetAcl, Remount, or Chattr — renderable to shell commands |
Check Pipeline
run_checks() evaluates layers in a fixed order. All layers always run — no short-circuiting.
| Order | Layer | What it checks |
|---|---|---|
| 1 | Mount | ro for writes, noexec for execute, nosuid advisory |
| 2 | FsFlags | immutable blocks all writes, append-only blocks non-append writes |
| 3 | Traversal | +x on every ancestor directory |
| 4 | DAC | uid/gid/mode bits + capability overrides |
| 5 | ACL | POSIX.1e named entries with mask application |
| 6 | SELinux | AVC decision over pre-gathered mac_state (selinux feature) |
| 7 | AppArmor | Profile mode over pre-gathered mac_state (apparmor feature) |
Feature Flags
| Feature | Default | Effect |
|---|---|---|
selinux |
off | Enables SELinux check layer — without it, SELinux results are always Degraded |
apparmor |
off | Enables AppArmor check layer — without it, AppArmor results are always Degraded |
test-helpers |
off | Exposes test_helpers module with fluent builders for constructing SystemState in tests |
Dependencies
serde+serde_json— serialization for all public typesschemars—JsonSchemaderive for schema generationthiserror— structured error typesenum-map— fixed-size enum-keyed maps for layer results
Minimum Supported Rust Version
Rust 1.78. Pure Rust, no platform-specific code — compiles on any target rustc supports.