Skip to content

Implement path-based and pattern-based filter rules #139

@inureyes

Description

@inureyes

Summary

Implement comprehensive path-based and pattern-based filter rules for file transfer filtering. This issue completes the filter implementation by adding advanced matching capabilities.

Parent Epic

Implementation Details

1. Extension Matcher

// src/server/filter/pattern.rs

/// Match by file extension
#[derive(Debug, Clone)]
pub struct ExtensionMatcher {
    extensions: Vec<String>,
    case_sensitive: bool,
}

impl ExtensionMatcher {
    pub fn new(extensions: Vec<String>, case_sensitive: bool) -> Self {
        let extensions = if case_sensitive {
            extensions
        } else {
            extensions.into_iter().map(|e| e.to_lowercase()).collect()
        };
        Self { extensions, case_sensitive }
    }
}

impl Matcher for ExtensionMatcher {
    fn matches(&self, path: &Path) -> bool {
        if let Some(ext) = path.extension() {
            let ext_str = ext.to_string_lossy();
            let ext_cmp = if self.case_sensitive {
                ext_str.to_string()
            } else {
                ext_str.to_lowercase()
            };
            self.extensions.contains(&ext_cmp)
        } else {
            false
        }
    }
    
    fn clone_box(&self) -> Box<dyn Matcher> {
        Box::new(self.clone())
    }
}

2. Size-based Filter

/// Match by file size (requires stat)
#[derive(Debug, Clone)]
pub struct SizeMatcher {
    min_size: Option<u64>,
    max_size: Option<u64>,
}

impl SizeMatcher {
    pub fn new(min: Option<u64>, max: Option<u64>) -> Self {
        Self { min_size: min, max_size: max }
    }
}

// Note: Size matching requires file metadata, handled differently
pub trait SizeAwareFilter: TransferFilter {
    fn check_with_size(
        &self,
        path: &Path,
        size: u64,
        operation: Operation,
        user: &str,
    ) -> FilterResult;
}

3. Composite Matcher

/// Combine multiple matchers with AND/OR logic
#[derive(Debug, Clone)]
pub enum CompositeMatcher {
    And(Vec<Box<dyn Matcher>>),
    Or(Vec<Box<dyn Matcher>>),
    Not(Box<dyn Matcher>),
}

impl Matcher for CompositeMatcher {
    fn matches(&self, path: &Path) -> bool {
        match self {
            CompositeMatcher::And(matchers) => matchers.iter().all(|m| m.matches(path)),
            CompositeMatcher::Or(matchers) => matchers.iter().any(|m| m.matches(path)),
            CompositeMatcher::Not(matcher) => !matcher.matches(path),
        }
    }
    
    fn clone_box(&self) -> Box<dyn Matcher> {
        Box::new(self.clone())
    }
}

4. Directory Matcher

/// Match specific directories or patterns in directory path
#[derive(Debug, Clone)]
pub struct DirectoryMatcher {
    /// Match if any component of the path contains this directory
    directory_name: String,
}

impl DirectoryMatcher {
    pub fn new(name: &str) -> Self {
        Self { directory_name: name.to_string() }
    }
}

impl Matcher for DirectoryMatcher {
    fn matches(&self, path: &Path) -> bool {
        path.components().any(|c| {
            c.as_os_str().to_string_lossy() == self.directory_name
        })
    }
    
    fn clone_box(&self) -> Box<dyn Matcher> {
        Box::new(self.clone())
    }
}

5. Extended Configuration

filter:
  enabled: true
  rules:
    # Match by extension
    - name: "block-executables"
      extensions: [exe, sh, bat, ps1]
      action: deny
      
    # Match by size (max 100MB for uploads)
    - name: "limit-upload-size"
      max_size: 104857600
      action: deny
      operations: [upload]
      
    # Composite rule: deny .env files except in /home
    - name: "protect-env-files"
      composite:
        type: and
        matchers:
          - pattern: "*.env"
          - not:
              path_prefix: /home/
      action: deny
      
    # Block hidden directories
    - name: "block-hidden"
      directory: ".git"
      action: deny
      
    # Allow specific paths only (whitelist mode)
    - name: "whitelist-data"
      path_prefix: /data/
      action: allow

Files to Create/Modify

File Action
src/server/filter/pattern.rs Modify - Add extension, composite matchers
src/server/filter/path.rs Modify - Add directory matcher
src/server/filter/policy.rs Modify - Support composite rules
src/server/config/types.rs Modify - Extended filter config

Testing Requirements

  1. Unit test: Extension matching (case sensitivity)
  2. Unit test: Composite AND/OR/NOT
  3. Unit test: Directory component matching
  4. Unit test: Complex rule combinations

Acceptance Criteria

  • Extension matcher implementation
  • Composite matcher (AND/OR/NOT)
  • Directory component matcher
  • Size-aware filtering interface
  • Extended configuration support
  • Tests passing

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions