Skip to main content

SecurityConfig

Struct SecurityConfig 

Source
#[non_exhaustive]
pub struct SecurityConfig { pub max_file_size: u64, pub max_total_size: u64, pub max_compression_ratio: f64, pub max_file_count: usize, pub max_path_depth: usize, pub allowed: AllowedFeatures, pub preserve_permissions: bool, pub allowed_extensions: Vec<String>, pub banned_path_components: Vec<String>, pub allow_solid_archives: bool, pub max_solid_block_memory: u64, }
Expand description

Security configuration with default-deny settings.

This configuration controls various security checks performed during archive extraction to prevent common vulnerabilities.

§Performance Note

This struct contains heap-allocated collections (Vec<String>). For performance, pass by reference (&SecurityConfig) rather than cloning. If shared ownership is needed across threads, consider wrapping in Arc<SecurityConfig>.

§Examples

use exarch_core::SecurityConfig;

// Use secure defaults
let config = SecurityConfig::default();

// Customize via fluent builder
let custom = SecurityConfig::default()
    .with_max_file_size(100 * 1024 * 1024)
    .with_max_total_size(1024 * 1024 * 1024)
    .with_allow_symlinks(true);

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§max_file_size: u64

Maximum size for a single file in bytes.

§max_total_size: u64

Maximum total size for all extracted files in bytes.

§max_compression_ratio: f64

Maximum compression ratio allowed (uncompressed / compressed).

§max_file_count: usize

Maximum number of files that can be extracted.

§max_path_depth: usize

Maximum path depth allowed.

§allowed: AllowedFeatures

Feature flags controlling what archive features are allowed.

Use this to enable symlinks, hardlinks, absolute paths, etc.

§preserve_permissions: bool

Preserve file permissions from archive.

§allowed_extensions: Vec<String>

List of allowed file extensions (empty = allow all).

Extensions are matched case-insensitively (e.g., "txt" matches both file.txt and file.TXT). The leading dot must be omitted.

When this list is non-empty, files without a file extension are treated as not allowed and will be skipped during extraction.

§banned_path_components: Vec<String>

List of banned path components (e.g., “.git”, “.ssh”).

§allow_solid_archives: bool

Allow extraction from solid 7z archives.

Solid archives compress multiple files together as a single block. While this provides better compression ratios, it has security implications:

  • Memory exhaustion: Extracting a single file requires decompressing the entire solid block into memory
  • Denial of service: Malicious archives can create large solid blocks that exhaust available memory

Security Recommendation: Only enable for trusted archives.

Default: false (solid archives rejected)

§max_solid_block_memory: u64

Maximum memory for solid archive extraction (bytes).

7z Solid Archive Memory Model:

Solid compression in 7z stores multiple files in a single compressed block. Extracting ANY file requires decompressing the ENTIRE solid block into memory, which can cause memory exhaustion attacks.

Validation Strategy:

  • Pre-validates total uncompressed size of all files in archive
  • This is a conservative heuristic (assumes single solid block)
  • Reason: sevenz-rust2 v0.20 doesn’t expose solid block boundaries

Security Guarantee:

  • Total uncompressed data cannot exceed this limit
  • Combined with max_file_size, prevents unbounded memory growth
  • Enforced ONLY when allow_solid_archives is true

Note: Only applies when allow_solid_archives is true.

Default: 512 MB (536,870,912 bytes)

Recommendation: Set to 1-2x available RAM for trusted archives only.

Implementations§

Source§

impl SecurityConfig

Source

pub fn permissive() -> Self

Creates a permissive configuration for trusted archives.

This configuration allows symlinks, hardlinks, absolute paths, and solid archives. Use only when extracting archives from trusted sources.

Source

pub fn validate(&self) -> Result<()>

Validates that the configuration values are logically consistent.

Returns an error if any field has a value that would make security enforcement impossible (zero limits or non-positive ratio).

§Errors

Returns ExtractionError::InvalidConfiguration if:

  • max_compression_ratio is not positive
  • max_file_size is zero
  • max_total_size is zero
  • max_path_depth is zero
  • max_file_count is zero
  • max_solid_block_memory is zero
§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default();
assert!(config.validate().is_ok());

let bad = SecurityConfig::default().with_max_file_size(0);
assert!(bad.validate().is_err());
Source

pub fn with_max_file_size(self, size: u64) -> Self

Sets the maximum size for a single extracted file in bytes.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_max_file_size(100 * 1024 * 1024);
assert_eq!(config.max_file_size, 100 * 1024 * 1024);
Source

pub fn with_max_total_size(self, size: u64) -> Self

Sets the maximum total size for all extracted files in bytes.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_max_total_size(1024 * 1024 * 1024);
assert_eq!(config.max_total_size, 1024 * 1024 * 1024);
Source

pub fn with_max_compression_ratio(self, ratio: f64) -> Self

Sets the maximum allowed compression ratio (uncompressed / compressed).

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_max_compression_ratio(50.0);
assert_eq!(config.max_compression_ratio, 50.0);
Source

pub fn with_max_file_count(self, count: usize) -> Self

Sets the maximum number of files that can be extracted.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_max_file_count(500);
assert_eq!(config.max_file_count, 500);
Source

pub fn with_max_path_depth(self, depth: usize) -> Self

Sets the maximum path depth allowed.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_max_path_depth(16);
assert_eq!(config.max_path_depth, 16);
Source

pub fn with_allowed(self, allowed: AllowedFeatures) -> Self

Sets the feature flags controlling allowed archive features.

§Examples
use exarch_core::SecurityConfig;
use exarch_core::config::AllowedFeatures;

let features = AllowedFeatures::default();
let config = SecurityConfig::default().with_allowed(features);
assert!(!config.allowed.symlinks);

Enables or disables symlinks in extracted archives.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_allow_symlinks(true);
assert!(config.allowed.symlinks);

Enables or disables hardlinks in extracted archives.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_allow_hardlinks(true);
assert!(config.allowed.hardlinks);
Source

pub fn with_allow_absolute_paths(self, allow: bool) -> Self

Enables or disables absolute paths in archive entries.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_allow_absolute_paths(true);
assert!(config.allowed.absolute_paths);
Source

pub fn with_allow_world_writable(self, allow: bool) -> Self

Enables or disables world-writable files.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_allow_world_writable(true);
assert!(config.allowed.world_writable);
Source

pub fn with_preserve_permissions(self, preserve: bool) -> Self

Enables or disables preserving file permissions from the archive.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_preserve_permissions(true);
assert!(config.preserve_permissions);
Source

pub fn with_allowed_extensions(self, extensions: Vec<String>) -> Self

Sets the list of allowed file extensions.

An empty list allows all extensions.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default()
    .with_allowed_extensions(vec!["txt".to_string(), "pdf".to_string()]);
assert!(config.is_extension_allowed("txt"));
assert!(!config.is_extension_allowed("exe"));
Source

pub fn with_banned_path_components(self, components: Vec<String>) -> Self

Sets the list of banned path components.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_banned_path_components(vec![".git".to_string()]);
assert!(!config.is_path_component_allowed(".git"));
assert!(config.is_path_component_allowed(".ssh"));
Source

pub fn with_allow_solid_archives(self, allow: bool) -> Self

Enables or disables extraction from solid 7z archives.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_allow_solid_archives(true);
assert!(config.allow_solid_archives);
Source

pub fn with_max_solid_block_memory(self, size: u64) -> Self

Sets the maximum memory for solid archive extraction in bytes.

Only applies when allow_solid_archives is true.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default()
    .with_allow_solid_archives(true)
    .with_max_solid_block_memory(1024 * 1024 * 1024);
assert_eq!(config.max_solid_block_memory, 1024 * 1024 * 1024);
Source

pub fn is_path_component_allowed(&self, component: &str) -> bool

Validates whether a path component is allowed.

Comparison is case-insensitive to prevent bypass on case-insensitive filesystems (Windows, macOS default).

Source

pub fn is_extension_allowed(&self, extension: &str) -> bool

Validates whether a file extension is allowed.

When allowed_extensions is empty, all extensions are permitted. When it is non-empty, only listed extensions are permitted.

Source

pub fn is_path_extension_allowed(&self, extension: Option<&str>) -> bool

Returns true if a file with the given optional extension may be extracted.

When allowed_extensions is non-empty and extension is None (the file has no extension), the file is treated as not allowed.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_allowed_extensions(vec!["txt".to_string()]);

assert!(config.is_path_extension_allowed(Some("txt")));
assert!(!config.is_path_extension_allowed(Some("exe")));
// Files without an extension are blocked when the allowlist is non-empty.
assert!(!config.is_path_extension_allowed(None));

// Empty allowlist permits everything, including extension-less files.
let permissive = SecurityConfig::default();
assert!(permissive.is_path_extension_allowed(None));

Trait Implementations§

Source§

impl Clone for SecurityConfig

Source§

fn clone(&self) -> SecurityConfig

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SecurityConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for SecurityConfig

Source§

fn default() -> Self

Creates a SecurityConfig with secure default settings.

Default values:

  • max_file_size: 50 MB
  • max_total_size: 500 MB
  • max_compression_ratio: 100.0
  • max_file_count: 10,000
  • max_path_depth: 32
  • allowed: All features disabled (deny-by-default)
  • preserve_permissions: false
  • allowed_extensions: empty (allow all)
  • banned_path_components: [".git", ".ssh", ".gnupg", ".aws", ".kube", ".docker", ".env"]
  • allow_solid_archives: false (solid archives rejected)
  • max_solid_block_memory: 512 MB

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.