///|
fn positive_limit(
diagnostics : Array[Diagnostic],
code : String,
label : String,
value : Int?,
) -> Unit {
match value {
Some(n) if n <= 0 =>
diagnostics.push({ code, message: "\{label} must be greater than zero", })
_ => ()
}
}
///|
/// Validate policy-level invariants before architecture-specific compilation.
pub fn validate_policy(policy : Policy) -> Array[Diagnostic] {
let diagnostics : Array[Diagnostic] = []
let names : Map[String, Bool] = Map([])
for rule in policy.syscall_rules {
if rule.name.trim().is_empty() {
diagnostics.push({
code: "MJ001",
message: "syscall name must not be empty",
})
} else if names.contains(rule.name) {
diagnostics.push({
code: "MJ002",
message: "duplicate syscall rule: \{rule.name}",
})
} else {
names[rule.name] = true
}
match rule.action {
Errno(value) if value <= 0 || value > 4095 =>
diagnostics.push({
code: "MJ003",
message: "errno for \{rule.name} must be in 1..4095",
})
_ => ()
}
}
let paths : Map[String, Bool] = Map([])
for rule in policy.path_rules {
if rule.path.trim().is_empty() {
diagnostics.push({ code: "MJ004", message: "path must not be empty", })
} else if paths.contains(rule.path) {
diagnostics.push({
code: "MJ012",
message: "duplicate path rule: \{rule.path}",
})
} else {
paths[rule.path] = true
}
if rule.path.contains("\u{0}") ||
rule.path.contains("\n") ||
rule.path.contains("\t") {
diagnostics.push({
code: "MJ011",
message: "path must not contain NUL, newline, or tab",
})
}
if rule.rights.is_empty() {
diagnostics.push({
code: "MJ005",
message: "path rule for \{rule.path} grants no rights",
})
}
}
positive_limit(diagnostics, "MJ006", "cpu_seconds", policy.limits.cpu_seconds)
positive_limit(diagnostics, "MJ007", "open_files", policy.limits.open_files)
positive_limit(diagnostics, "MJ008", "processes", policy.limits.processes)
match policy.limits.address_space_bytes {
Some(n) if n <= 0L =>
diagnostics.push({
code: "MJ009",
message: "address_space_bytes must be greater than zero",
})
_ => ()
}
match policy.limits.file_size_bytes {
Some(n) if n <= 0L =>
diagnostics.push({
code: "MJ010",
message: "file_size_bytes must be greater than zero",
})
_ => ()
}
diagnostics
}