Skip to main content

SecurityConfig

Struct SecurityConfig 

Source
pub struct SecurityConfig<State = Unvalidated> { /* private fields */ }
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);

§Typestate

SecurityConfig carries a phantom State type parameter — Unvalidated (the default) or Validated — that tracks whether validate has been called. Builder methods are only available in the Unvalidated state; security-sensitive APIs (the ArchiveFormat trait and everything downstream of it) require SecurityConfig<Validated>. This makes skipping validation a compile error instead of a runtime gap.

§Sealing

Fields are private and reachable only through Deref<Target = SecurityConfigFields>, so cfg.max_file_size continues to work as plain field access for both states. DerefMut is implemented only for SecurityConfig<Unvalidated>, so a SecurityConfig<Validated>’s fields cannot be reassigned after the fact — the only way to produce one is validate itself, and it stays that way for its entire lifetime.

Implementations§

Source§

impl SecurityConfig<Unvalidated>

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<SecurityConfig<Validated>>

Validates that the configuration values are logically consistent, transitioning to the Validated typestate on success.

Returns an error if any field has a value that would make security enforcement impossible (zero limits or non-positive ratio). Consumes self: the only way to obtain a SecurityConfig<Validated>, which is what every security-sensitive API in this crate requires. Once returned, the Validated config’s fields can no longer be reassigned (see the “Sealing” section on the type-level docs), so this check can never be silently invalidated afterward.

§Errors

Returns ArchiveError::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
  • max_tar_metadata_bytes is zero
  • any entry in allowed_extensions or banned_path_components is empty, contains a null byte, or exceeds crate::MAX_CONFIG_ENTRY_LENGTH bytes
§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 with_max_tar_metadata_bytes(self, size: u64) -> Self

Sets the maximum bytes the TAR reader may consume for headers and metadata records (GNU long-name/long-link, PAX extended headers, GNU sparse extension blocks) in the gap between two consecutive entries.

§Examples
use exarch_core::SecurityConfig;

let config = SecurityConfig::default().with_max_tar_metadata_bytes(64 * 1024);
assert_eq!(config.max_tar_metadata_bytes, 64 * 1024);
Source§

impl<State> SecurityConfig<State>

Read-only queries and crate-internal helpers available regardless of validation state.

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<State: Clone> Clone for SecurityConfig<State>

Source§

fn clone(&self) -> SecurityConfig<State>

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<State: Debug> Debug for SecurityConfig<State>

Source§

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

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

impl Default for SecurityConfig<Unvalidated>

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
  • max_tar_metadata_bytes: 4 MiB
Source§

impl<State> Deref for SecurityConfig<State>

Source§

type Target = SecurityConfigFields

The resulting type after dereferencing.
Source§

fn deref(&self) -> &SecurityConfigFields

Dereferences the value.
Source§

impl DerefMut for SecurityConfig<Unvalidated>

Only Unvalidated configs are mutable — see the “Sealing” section on SecurityConfig’s type-level docs for why this is the crux of the typestate guarantee.

Source§

fn deref_mut(&mut self) -> &mut SecurityConfigFields

Mutably dereferences the value.

Auto Trait Implementations§

§

impl<State> Freeze for SecurityConfig<State>

§

impl<State> RefUnwindSafe for SecurityConfig<State>
where State: RefUnwindSafe,

§

impl<State> Send for SecurityConfig<State>
where State: Send,

§

impl<State> Sync for SecurityConfig<State>
where State: Sync,

§

impl<State> Unpin for SecurityConfig<State>
where State: Unpin,

§

impl<State> UnsafeUnpin for SecurityConfig<State>

§

impl<State> UnwindSafe for SecurityConfig<State>
where State: UnwindSafe,

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<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
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.