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