#![allow(non_snake_case)]
use std::{io::BufReader, process::ExitCode};
use nix::errno::Errno;
use syd::{
config::API_MAJOR_VERSION,
err::SydResult,
path::{XPath, XPathBuf},
sandbox::Sandbox,
};
#[cfg(all(
not(coverage),
not(feature = "prof"),
not(target_os = "android"),
not(target_arch = "riscv64"),
target_page_size_4k,
target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;
syd::main! {
use lexopt::prelude::*;
syd::set_sigpipe_dfl()?;
let mut name = None;
let mut optj = false;
let mut optJ = false;
let mut optM = Vec::new();
let mut syd = Sandbox::new();
let mut paths = Vec::new();
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
match arg {
Short('h') => {
help();
return Ok(ExitCode::SUCCESS);
}
Short('j') => optj = true,
Short('J') => optJ = true,
Short('m') => {
let cmd = parser.value().map(XPathBuf::from)?;
if syd.is_locked() {
eprintln!("syd-cat: Failed to execute magic command `{cmd}': sandbox locked!");
return Err(Errno::EBUSY.into());
} else {
syd.config(&cmd.to_string())?;
}
}
Short('M') => optM.push(parser.value()?.parse::<String>()?),
Short('p') => name = Some(parser.value()?.parse::<String>()?),
Value(path) => paths.push(XPathBuf::from(path)),
_ => return Err(arg.unexpected().into()),
}
}
if optj && optJ {
eprintln!("syd-cat: -j and -J are mutually exclusive!");
return Err(Errno::EINVAL.into());
}
if let Some(name) = name {
if optj || optJ {
eprintln!("syd-cat: -p cannot be used with JSON output!");
return Err(Errno::EINVAL.into());
}
if name == "list" {
list();
} else {
dump(&name)?;
}
return Ok(ExitCode::SUCCESS);
}
for path in paths {
let fext = if let Some(fext) = path.extension() {
fext
} else {
return Err(Errno::EOPNOTSUPP.into());
};
let syd_ext = XPathBuf::from(format!("syd-{API_MAJOR_VERSION}"));
let ips_ext = XPath::from_bytes(b"ipset");
let net_ext = XPath::from_bytes(b"netset");
#[expect(clippy::disallowed_methods)]
#[expect(clippy::disallowed_types)]
if *fext == *syd_ext {
syd.parse_config_file(&path)?;
} else if *fext == *ips_ext || *fext == *net_ext {
let file = std::fs::File::open(path.as_path())?;
syd.parse_netset(BufReader::new(file))?;
} else {
return Err(Errno::EOPNOTSUPP.into());
}
}
for cmd in optM {
if syd.is_locked() {
eprintln!("syd-cat: Failed to execute magic command `{cmd}': sandbox locked!");
return Err(Errno::EPERM.into());
} else {
syd.config(&cmd)?;
}
}
if optj {
println!(
"{}",
serde_json::to_string_pretty(&syd).or(Err(Errno::EINVAL))?
);
} else if optJ {
print!("{}", serde_json::to_string(&syd).or(Err(Errno::EINVAL))?);
} else {
print!("{syd}");
}
Ok(ExitCode::SUCCESS)
}
fn help() {
println!("Usage: syd-cat [-hjJmM] [-p name] <path>...");
println!("Tool to parse, validate and display Syd configuration.");
println!("Given a list of paths, parses and validates configuration.");
println!("Prints configuration to standard output on success.");
println!("Use -j to display as JSON and -J for compact JSON output.");
println!("Use -p <name> to display rules of the profile with the given name.");
println!("Use -p list to get a list of profiles.");
println!("Use -m <magic> to run a magic command at init, may be repeated.");
println!("Use -M <magic> to run a magic command at exit, may be repeated.");
println!("Supported configuration file extensions:");
println!(" - ipset");
println!(" - netset");
println!(" - syd-{API_MAJOR_VERSION}");
}
fn list() {
println!("chrome");
println!("container");
println!("core");
println!("cwd");
println!("debug");
println!("enforce");
println!("firefox");
println!("fs");
println!("gui");
println!("hide");
println!("immutable");
println!("landlock");
println!("lang");
println!("ldd");
println!("lib");
println!("linux");
println!("ltp");
println!("nix");
println!("nixstore");
println!("noipv4");
println!("noipv6");
println!("nomagic");
println!("nomem");
println!("nopie");
println!("noxdev");
println!("oci");
println!("paludis");
println!("quiet");
println!("rand");
println!("readonly");
println!("tty");
println!("user");
println!("xdg");
}
fn dump(name: &str) -> SydResult<()> {
match name {
"container" => {
println!("# Syd profile: Container");
println!(
"# Number of rules: {}",
syd::config::PROFILE_CONTAINER.len()
);
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_CONTAINER {
println!("{command}");
}
}
"immutable" => {
println!("# Syd profile: Immutable Container");
println!(
"# Number of rules: {}",
syd::config::PROFILE_IMMUTABLE.len()
);
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_IMMUTABLE {
println!("{command}");
}
}
"landlock" => {
println!("# Syd profile: LandLock");
println!("# Number of rules: {}", syd::config::PROFILE_LANDLOCK.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_LANDLOCK {
println!("{command}");
}
}
"linux" => {
println!("# Syd profile: Linux");
println!("# Number of rules: {}", syd::config::PROFILE_LINUX.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_LINUX {
println!("{command}");
}
}
"ltp" => {
println!("# Syd profile: LTP");
println!("# Number of rules: {}", syd::config::PROFILE_LTP.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_LTP {
println!("{command}");
}
}
"kcov" => {
println!("# Syd profile: KCOV");
println!("# Number of rules: {}", syd::config::PROFILE_KCOV.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_KCOV {
println!("{command}");
}
}
"kvm" => {
println!("# Syd profile: KVM");
println!("# Number of rules: {}", syd::config::PROFILE_KVM.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_KVM {
println!("{command}");
}
}
"kvm_native" => {
println!("# Syd profile: KVM-Native");
println!(
"# Number of rules: {}",
syd::config::PROFILE_KVM_NATIVE.len()
);
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_KVM_NATIVE {
println!("{command}");
}
}
"nix" => {
println!("# Syd profile: NIX");
println!("# Number of rules: {}", syd::config::PROFILE_NIX.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NIX {
println!("{command}");
}
}
"tty" => {
println!("# Syd profile: TTY");
println!("# Number of rules: {}", syd::config::PROFILE_TTY.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_TTY {
println!("{command}");
}
}
"tty_native" => {
println!("# Syd profile: TTY-Native");
println!(
"# Number of rules: {}",
syd::config::PROFILE_TTY_NATIVE.len()
);
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_TTY_NATIVE {
println!("{command}");
}
}
"paludis" => {
println!("# Syd profile: Paludis");
println!("# Number of rules: {}", syd::config::PROFILE_PALUDIS.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_PALUDIS {
println!("{command}");
}
}
"cwd" | "pwd" => {
println!("# Syd profile: CWD");
println!("# Number of rules: {}", syd::config::PROFILE_CWD.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_CWD {
println!("{command}");
}
}
"hide" => {
println!("# Syd profile: Hide");
println!("# Number of rules: {}", syd::config::PROFILE_HIDE.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_HIDE {
println!("{command}");
}
}
"noipv4" => {
println!("# Syd profile: NoIpv4");
println!("# Number of rules: {}", syd::config::PROFILE_NOIPV4.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NOIPV4 {
println!("{command}");
}
}
"noipv6" => {
println!("# Syd profile: NoIpv6");
println!("# Number of rules: {}", syd::config::PROFILE_NOIPV6.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NOIPV6 {
println!("{command}");
}
}
"privileged" => {
println!("# Syd profile: Privileged");
println!(
"# Number of rules: {}",
syd::config::PROFILE_PRIVILEGED.len()
);
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_PRIVILEGED {
println!("{command}");
}
}
"core" => {
println!("# Syd profile: Allow Coredump");
println!("# Number of rules: {}", syd::config::PROFILE_CORE.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_CORE {
println!("{command}");
}
}
"debug" => {
println!("# Syd profile: Allow Debuggers");
println!("# Number of rules: {}", syd::config::PROFILE_DEBUG.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_DEBUG {
println!("{command}");
}
}
"enforce" => {
println!("# Syd profile: Enforce Sandboxing: set default action to Deny");
println!("# Number of rules: {}", syd::config::PROFILE_ENFORCE.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_ENFORCE {
println!("{command}");
}
}
"nomem" => {
println!("# Syd profile: Unsafe Memory (no W^X)");
println!("# Number of rules: {}", syd::config::PROFILE_NOMEM.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NOMEM {
println!("{command}");
}
}
"nopie" => {
println!("# Syd profile: No PIE (Position Independent Executable)");
println!("# Number of rules: {}", syd::config::PROFILE_NOPIE.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NOPIE {
println!("{command}");
}
}
"nomagic" => {
println!("# Syd profile: Enforce No Magic Links");
println!("# Number of rules: {}", syd::config::PROFILE_NO_MAGIC.len());
println!("# Copyright (c) 2026 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NO_MAGIC {
println!("{command}");
}
}
"noxdev" => {
println!("# Syd profile: Enforce No Cross Mounts");
println!("# Number of rules: {}", syd::config::PROFILE_NO_XDEV.len());
println!("# Copyright (c) 2026 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NO_XDEV {
println!("{command}");
}
}
"quiet" | "silent" => {
println!("# Syd profile: Quiet");
println!("# Number of rules: {}", syd::config::PROFILE_QUIET.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_QUIET {
println!("{command}");
}
}
"rand" => {
println!("# Syd profile: Rand");
println!("# Number of rules: {}", syd::config::PROFILE_RAND.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_RAND {
println!("{command}");
}
}
"ro" | "readonly" => {
println!("# Syd profile: Read Only");
println!("# Number of rules: {}", syd::config::PROFILE_READONLY.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_READONLY {
println!("{command}");
}
}
"chrome" => {
println!("# Syd profile: Chrome-family browsers");
println!("# Number of rules: {}", syd::config::PROFILE_CHROME.len());
println!("# Copyright (c) 2026 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_CHROME {
println!("{command}");
}
}
"ff" | "firefox" => {
println!("# Syd profile: Firefox");
println!("# Number of rules: {}", syd::config::PROFILE_FIREFOX.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_FIREFOX {
println!("{command}");
}
}
"fs" => {
println!("# Syd profile: Filesystem");
println!("# Number of rules: {}", syd::config::PROFILE_FS.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_FS {
println!("{command}");
}
}
"lang" => {
println!("# Syd profile: Allow language and timezone environment variables");
println!("# Number of rules: {}", syd::config::PROFILE_LANG.len());
println!("# Copyright (c) 2026 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_LANG {
println!("{command}");
}
}
"gui" => {
println!("# Syd profile: Graphical User Interface");
println!("# Number of rules: {}", syd::config::PROFILE_GUI.len());
println!("# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_GUI {
println!("{command}");
}
}
"xdg" => {
println!("# Syd profile: Allow XDG environment variables");
println!("# Number of rules: {}", syd::config::PROFILE_XDG.len());
println!("# Copyright (c) 2026 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_XDG {
println!("{command}");
}
}
"ldd" => {
println!("# Syd profile: Allow ldd(1)");
println!("# Number of rules: {}", syd::config::PROFILE_LDD.len());
println!("# Copyright (c) 2026 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_LDD {
println!("{command}");
}
}
"off" => {
println!("# Syd profile: Off");
println!("# Number of rules: {}", syd::config::PROFILE_OFF.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_OFF {
println!("{command}");
}
}
"lib" => {
println!("# Syd profile: LibSyd");
println!("# Number of rules: {}", syd::config::PROFILE_LIB.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_LIB {
println!("{command}");
}
}
"oci" => {
println!("# Syd profile: OCI");
println!("# Number of rules: {}", syd::config::PROFILE_OCI.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_OCI {
println!("{command}");
}
}
"trace" => {
println!("# Syd profile: Trace");
println!("# Number of rules: {}", syd::config::PROFILE_TRACE.len());
println!("# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_TRACE {
println!("{command}");
}
}
"user" => {
println!("# Syd profile: User \"{name}\"");
println!("# Number of rules: {}", syd::config::PROFILE_USER.len());
println!("# Copyright (c) 2023, 2024 Ali Polatel <alip@chesswob.org>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_USER {
println!("{command}");
}
}
"nixstore" => {
println!("# Syd profile: Nix store");
println!(
"# Number of rules: {}",
syd::config::PROFILE_NIX_STORE.len()
);
println!("# Copyright (c) 2025 Emery Hemingway <emery+syd@slow.janky.email>");
println!("# SPDX-License-Identifier: GPL-3.0");
for command in syd::config::PROFILE_NIX_STORE {
println!("{command}");
}
}
_ => return Err(Errno::EINVAL.into()),
}
Ok(())
}