Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Categories Used:
- Provide Nushell completions (packages still need to install them) [\#827](https://github.com/ouch-org/ouch/pull/827) ([FrancescElies](https://github.com/FrancescElies))
- Support `.lz` decompression [\#838](https://github.com/ouch-org/ouch/pull/838) ([zzzsyyy](https://github.com/zzzsyyy))
- Support `.lzma` decompression (and fix `.lzma` being a wrong alias for `.xz`) [\#838](https://github.com/ouch-org/ouch/pull/838) ([zzzsyyy](https://github.com/zzzsyyy))
- Add landlock support for linux filesystem isolation [\#723](https://github.com/ouch-org/ouch/pull/723) ([valoq](https://github.com/valoq))

### Improvements

Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ gzp = { version = "0.11.3", default-features = false, features = [
"snappy_default",
] }
ignore = "0.4.23"
landlock = "0.4.2"
libc = "0.2.155"
linked-hash-map = "0.5.6"
lz4_flex = "0.11.3"
Expand All @@ -39,6 +40,7 @@ sevenz-rust2 = { version = "0.13.1", features = ["compress", "aes256"] }
snap = "1.1.1"
tar = "0.4.42"
tempfile = "3.10.1"
thiserror = "2.0.12"
time = { version = "0.3.36", default-features = false }
unrar = { version = "0.5.7", optional = true }
liblzma = "0.4"
Expand Down
25 changes: 25 additions & 0 deletions src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub struct CliArgs {
#[arg(short = 'c', long, global = true)]
pub threads: Option<usize>,

/// Disable the sandbox feature
#[arg(long, global = true)]
pub disable_sandbox: bool,

// Ouch and claps subcommands
#[command(subcommand)]
pub cmd: Subcommand,
Expand Down Expand Up @@ -85,6 +89,10 @@ pub enum Subcommand {
/// Archive target files instead of storing symlinks (supported by `tar` and `zip`)
#[arg(long, short = 'S')]
follow_symlinks: bool,

/// Mark sandbox as disabled
#[arg(long, global = true)]
disable_sandbox: bool,
},
/// Decompresses one or more files, optionally into another folder
#[command(visible_alias = "d")]
Expand All @@ -104,6 +112,10 @@ pub enum Subcommand {
/// Disable Smart Unpack
#[arg(long)]
no_smart_unpack: bool,

/// Mark sandbox as disabled
#[arg(long, global = true)]
disable_sandbox: bool,
},
/// List contents of an archive
#[command(visible_aliases = ["l", "ls"])]
Expand All @@ -115,6 +127,10 @@ pub enum Subcommand {
/// Show archive contents as a tree
#[arg(short, long)]
tree: bool,

/// Mark sandbox as disabled
#[arg(long, global = true)]
disable_sandbox: bool,
},
}

Expand Down Expand Up @@ -155,12 +171,14 @@ mod tests {
// This is usually replaced in assertion tests
password: None,
threads: None,
disable_sandbox: false,
cmd: Subcommand::Decompress {
// Put a crazy value here so no test can assert it unintentionally
files: vec!["\x00\x11\x22".into()],
output_dir: None,
remove: false,
no_smart_unpack: false,
disable_sandbox: false,
},
}
}
Expand All @@ -175,6 +193,7 @@ mod tests {
output_dir: None,
remove: false,
no_smart_unpack: false,
disable_sandbox: false,
},
..mock_cli_args()
}
Expand All @@ -187,6 +206,7 @@ mod tests {
output_dir: None,
remove: false,
no_smart_unpack: false,
disable_sandbox: false,
},
..mock_cli_args()
}
Expand All @@ -199,6 +219,7 @@ mod tests {
output_dir: None,
remove: false,
no_smart_unpack: false,
disable_sandbox: false,
},
..mock_cli_args()
}
Expand All @@ -214,6 +235,7 @@ mod tests {
fast: false,
slow: false,
follow_symlinks: false,
disable_sandbox: false,
},
..mock_cli_args()
}
Expand All @@ -228,6 +250,7 @@ mod tests {
fast: false,
slow: false,
follow_symlinks: false,
disable_sandbox: false,
},
..mock_cli_args()
}
Expand All @@ -242,6 +265,7 @@ mod tests {
fast: false,
slow: false,
follow_symlinks: false,
disable_sandbox: false,
},
..mock_cli_args()
}
Expand All @@ -267,6 +291,7 @@ mod tests {
fast: false,
slow: false,
follow_symlinks: false,
disable_sandbox: false,
},
format: Some("tar.gz".into()),
..mock_cli_args()
Expand Down
1 change: 1 addition & 0 deletions src/commands/compress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub fn compress_files(
question_policy: QuestionPolicy,
file_visibility_policy: FileVisibilityPolicy,
level: Option<i16>,
disable_sandbox: bool,
) -> crate::Result<bool> {
// If the input files contain a directory, then the total size will be underestimated
let file_writer = BufWriter::with_capacity(BUFFER_CAPACITY, output_file);
Expand Down
24 changes: 23 additions & 1 deletion src/commands/decompress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
utils::{
self,
io::lock_and_flush_output_stdio,
is_path_stdin,
is_path_stdin, landlock,
logger::{info, info_accessible},
nice_directory_display, user_wants_to_continue,
},
Expand All @@ -39,6 +39,7 @@ pub struct DecompressOptions<'a> {
pub quiet: bool,
pub password: Option<&'a [u8]>,
pub remove: bool,
pub disable_sandbox: bool,
}

/// Decompress a file
Expand Down Expand Up @@ -79,6 +80,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
options.question_policy,
options.is_output_dir_provided,
options.is_smart_unpack,
options.disable_sandbox,
)? {
files
} else {
Expand Down Expand Up @@ -176,6 +178,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
options.question_policy,
options.is_output_dir_provided,
options.is_smart_unpack,
options.disable_sandbox,
)? {
files
} else {
Expand Down Expand Up @@ -211,6 +214,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
options.question_policy,
options.is_output_dir_provided,
options.is_smart_unpack,
options.disable_sandbox,
)? {
files
} else {
Expand Down Expand Up @@ -244,6 +248,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
options.question_policy,
options.is_output_dir_provided,
options.is_smart_unpack,
options.disable_sandbox,
)? {
files
} else {
Expand Down Expand Up @@ -287,6 +292,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
options.question_policy,
options.is_output_dir_provided,
options.is_smart_unpack,
options.disable_sandbox,
)? {
files
} else {
Expand Down Expand Up @@ -323,7 +329,20 @@ fn execute_decompression(
question_policy: QuestionPolicy,
is_output_dir_provided: bool,
is_smart_unpack: bool,
disable_sandbox: bool,
) -> crate::Result<ControlFlow<(), usize>> {
// init landlock sandbox to restrict file system write access to output_dir
// The output directory iseither specified with the -d option or the current working directory is used
// TODO: restrict acess to the current working directory to allow only creating new files
// TODO: move to unpack and smart_unpack to cover the differetn dirctories used for
// decompression
//if !input_is_stdin && options.remove {
//permit write access to input_file_path
//} else {
//}

landlock::init_sandbox(&[output_dir], disable_sandbox);

if is_smart_unpack {
return smart_unpack(unpack_fn, output_dir, output_file_path, question_policy);
}
Expand Down Expand Up @@ -387,6 +406,9 @@ fn smart_unpack(
nice_directory_display(temp_dir_path)
));

//first attempt to restict to the tmp file and allow only to rename it in the parent
//landlock::init_sandbox(Some(temp_dir_path));

let files = unpack_fn(temp_dir_path)?;

let root_contains_only_one_element = fs::read_dir(temp_dir_path)?.take(2).count() == 1;
Expand Down
9 changes: 8 additions & 1 deletion src/commands/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
commands::warn_user_about_loading_zip_in_memory,
extension::CompressionFormat::{self, *},
list::{self, FileInArchive, ListOptions},
utils::{io::lock_and_flush_output_stdio, user_wants_to_continue},
utils::{io::lock_and_flush_output_stdio, landlock, user_wants_to_continue},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
};

Expand All @@ -22,7 +22,14 @@ pub fn list_archive_contents(
list_options: ListOptions,
question_policy: QuestionPolicy,
password: Option<&[u8]>,
disable_sandbox: bool,
) -> crate::Result<()> {
//rar uses a temporary file which needs to be defined early to be permitted in landlock
let mut temp_file = tempfile::NamedTempFile::new()?;

// Initialize landlock sandbox with write access restricted to /tmp/<tmp_file> as required by some formats
landlock::init_sandbox(&[temp_file.path()], disable_sandbox);

let reader = fs::File::open(archive_path)?;

// Zip archives are special, because they require io::Seek, so it requires it's logic separated
Expand Down
9 changes: 7 additions & 2 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub fn run(
fast,
slow,
follow_symlinks,
disable_sandbox,
} => {
// After cleaning, if there are no input files left, exit
if files.is_empty() {
Expand Down Expand Up @@ -116,6 +117,7 @@ pub fn run(
question_policy,
file_visibility_policy,
level,
args.disable_sandbox,
);

if let Ok(true) = compress_result {
Expand Down Expand Up @@ -151,6 +153,7 @@ pub fn run(
output_dir,
remove,
no_smart_unpack,
disable_sandbox,
} => {
let mut output_paths = vec![];
let mut formats = vec![];
Expand Down Expand Up @@ -216,10 +219,12 @@ pub fn run(
<[u8] as ByteSlice>::from_os_str(str).expect("convert password to bytes failed")
}),
remove,
disable_sandbox: args.disable_sandbox,
})
})
}
Subcommand::List { archives: files, tree } => {
// check again if we need to provide disable_sandbox as argument here
Subcommand::List { archives: files, tree, disable_sandbox} => {
let mut formats = vec![];

if let Some(format) = args.format {
Expand Down Expand Up @@ -257,9 +262,9 @@ pub fn run(
args.password
.as_deref()
.map(|str| <[u8] as ByteSlice>::from_os_str(str).expect("convert password to bytes failed")),
args.disable_sandbox,
)?;
}

Ok(())
}
}
Expand Down
Loading
Loading