Skip to content

Core String Extraction Framework: StringExtractor Trait and Configuration #8

Closed
2 of 2 issues completed
Closed

Description

@unclesp1d3r

Background

StringyMcStringFace aims to be a smarter alternative to the strings command by leveraging format-specific knowledge to extract and prioritize meaningful strings from binaries. The container parsing layer (ELF, PE, Mach-O) is already implemented and provides structured metadata via ContainerInfo. Now we need the core extraction framework that will scan binary data and produce FoundString instances.

Current State

  • ✅ Container parsers implemented (src/container/)
  • ✅ Type definitions complete (FoundString, Encoding, SectionType, etc.)
  • ❌ String extraction logic missing (src/extraction/mod.rs is empty)
  • ❌ No extraction configuration framework
  • ❌ No trait abstraction for different extraction strategies

Objectives

Create a flexible, trait-based extraction framework in src/extraction/mod.rs that:

  1. Defines the StringExtractor trait - An interface for different extraction strategies (basic ASCII, Unicode, context-aware, etc.)
  2. Implements ExtractionConfig - Configurable parameters for extraction behavior
  3. Provides a default extractor - A basic implementation to get started

Proposed Solution

1. StringExtractor Trait

/// Trait for implementing string extraction strategies
pub trait StringExtractor {
    /// Extract strings from binary data with metadata context
    fn extract(
        &self,
        data: &[u8],
        container_info: &ContainerInfo,
        config: &ExtractionConfig,
    ) -> Result<Vec<FoundString>>;
    
    /// Extract strings from a specific section
    fn extract_from_section(
        &self,
        data: &[u8],
        section: &SectionInfo,
        config: &ExtractionConfig,
    ) -> Result<Vec<FoundString>>;
}

2. ExtractionConfig Structure

/// Configuration parameters for string extraction
#[derive(Debug, Clone)]
pub struct ExtractionConfig {
    /// Minimum string length to consider
    pub min_length: usize,
    
    /// Maximum string length to extract
    pub max_length: usize,
    
    /// Encodings to search for
    pub encodings: Vec<Encoding>,
    
    /// Whether to extract from executable sections
    pub scan_code_sections: bool,
    
    /// Whether to include strings from debug sections
    pub include_debug: bool,
    
    /// Section types to prioritize
    pub section_priority: Vec<SectionType>,
    
    /// Whether to include import/export names
    pub include_symbols: bool,
}

impl Default for ExtractionConfig {
    fn default() -> Self {
        Self {
            min_length: 4,
            max_length: 4096,
            encodings: vec\![Encoding::Ascii, Encoding::Utf8],
            scan_code_sections: true,
            include_debug: false,
            section_priority: vec\![
                SectionType::StringData,
                SectionType::ReadOnlyData,
                SectionType::Resources,
            ],
            include_symbols: true,
        }
    }
}

3. Basic Implementation

Create a BasicExtractor that implements StringExtractor with straightforward sequential scanning for ASCII and UTF-8 strings.

Architecture Integration

The extraction framework fits into the pipeline as follows:

Binary Data → Container Parser → ContainerInfo
                                        ↓
                                  StringExtractor + ExtractionConfig
                                        ↓
                                  Vec<FoundString>
                                        ↓
                                  Classification (future)
                                        ↓
                                  Output Formatting

Implementation Details

File Structure

  • Main trait and config: src/extraction/mod.rs
  • Basic extractor: src/extraction/basic.rs (or inline in mod.rs initially)
  • Tests: Unit tests in each module, integration tests in tests/

Key Considerations

  • Use existing FoundString type (already defined in src/types.rs)
  • Leverage ContainerInfo to make format-aware decisions
  • Ensure zero-copy where possible (use slices and indices)
  • Handle encoding detection robustly (invalid sequences should not panic)
  • Set appropriate StringSource values based on where strings are found

Acceptance Criteria

  • StringExtractor trait defined with documented methods
  • ExtractionConfig struct with sensible defaults
  • At least one working implementation (BasicExtractor)
  • Unit tests covering:
    • ASCII string extraction
    • UTF-8 string extraction
    • Configuration validation
    • Edge cases (empty data, invalid encodings, boundary conditions)
  • Documentation with usage examples
  • Integration with existing ContainerInfo architecture

Related

Notes

  • The issue originally mentioned RawString struct, but FoundString already exists in src/types.rs and should be used instead
  • Future extractors can implement format-specific logic (e.g., PE resource strings, Mach-O LC_ID_DYLIB)
  • This is the foundation for the entire extraction pipeline - needs to be solid and extensible

Activity

  1. added this to the v0.1 milestone on Sep 30, 2025
  2. self-assigned this
    on Sep 30, 2025
  3. unclesp1d3r commented on Sep 30, 2025

    @unclesp1d3r
    MemberAuthor

    @coderabbitai Enhance this issue body with additional context and a proposed solution. Improve the issue title and assign the proper labels and other metadata.

  4. changed the title [-]String Extraction Framework[/-] [+]Core String Extraction Framework: StringExtractor Trait and Configuration[/+] on Sep 30, 2025
  5. coderabbitai commented on Sep 30, 2025

    @coderabbitai
    Contributor

    Tip

    For best results, initiate chat on the files or code changes.

    🧩 Analysis chain

    🏁 Script executed:

    #!/bin/bash
    # Fetch issue #8 details
    gh issue view 8 --json title,body,labels,assignees,state,url
    

    Length of output: 1167


    🏁 Script executed:

    #!/bin/bash
    # Explore the project structure to understand context
    echo "=== Project Structure ==="
    fd -t d -d 2
    
    echo -e "\n=== Check if extraction module exists ==="
    fd -e rs extraction
    
    echo -e "\n=== Look for existing string extraction code ==="
    rg -n "string.*extract" -i --type rust -C 2
    
    echo -e "\n=== Check main crate structure ==="
    cat Cargo.toml 2>/dev/null || echo "Cargo.toml not found"
    
    echo -e "\n=== Check src directory structure ==="
    fd -e rs . src 2>/dev/null | head -20
    

    Length of output: 3214


    🏁 Script executed:

    #!/bin/bash
    # Read key files to understand the architecture
    echo "=== src/extraction/mod.rs ==="
    cat src/extraction/mod.rs
    
    echo -e "\n=== src/types.rs ==="
    cat src/types.rs
    
    echo -e "\n=== src/container/mod.rs ==="
    cat src/container/mod.rs
    
    echo -e "\n=== src/lib.rs (to understand module structure) ==="
    cat src/lib.rs
    

    Length of output: 7810


    🏁 Script executed:

    #!/bin/bash
    # Update issue #8 with enhanced content
    gh issue edit 8 \
      --title "Core String Extraction Framework: StringExtractor Trait and Configuration" \
      --body "## Background
    
    StringyMcStringFace aims to be a smarter alternative to the \`strings\` command by leveraging format-specific knowledge to extract and prioritize meaningful strings from binaries. The container parsing layer (ELF, PE, Mach-O) is already implemented and provides structured metadata via \`ContainerInfo\`. Now we need the core extraction framework that will scan binary data and produce \`FoundString\` instances.
    
    ## Current State
    
    - ✅ Container parsers implemented (\`src/container/\`)
    - ✅ Type definitions complete (\`FoundString\`, \`Encoding\`, \`SectionType\`, etc.)
    - ❌ String extraction logic missing (\`src/extraction/mod.rs\` is empty)
    - ❌ No extraction configuration framework
    - ❌ No trait abstraction for different extraction strategies
    
    ## Objectives
    
    Create a flexible, trait-based extraction framework in \`src/extraction/mod.rs\` that:
    
    1. **Defines the \`StringExtractor\` trait** - An interface for different extraction strategies (basic ASCII, Unicode, context-aware, etc.)
    2. **Implements \`ExtractionConfig\`** - Configurable parameters for extraction behavior
    3. **Provides a default extractor** - A basic implementation to get started
    
    ## Proposed Solution
    
    ### 1. StringExtractor Trait
    
    \`\`\`rust
    /// Trait for implementing string extraction strategies
    pub trait StringExtractor {
        /// Extract strings from binary data with metadata context
        fn extract(
            &self,
            data: &[u8],
            container_info: &ContainerInfo,
            config: &ExtractionConfig,
        ) -> Result<Vec<FoundString>>;
        
        /// Extract strings from a specific section
        fn extract_from_section(
            &self,
            data: &[u8],
            section: &SectionInfo,
            config: &ExtractionConfig,
        ) -> Result<Vec<FoundString>>;
    }
    \`\`\`
    
    ### 2. ExtractionConfig Structure
    
    \`\`\`rust
    /// Configuration parameters for string extraction
    #[derive(Debug, Clone)]
    pub struct ExtractionConfig {
        /// Minimum string length to consider
        pub min_length: usize,
        
        /// Maximum string length to extract
        pub max_length: usize,
        
        /// Encodings to search for
        pub encodings: Vec<Encoding>,
        
        /// Whether to extract from executable sections
        pub scan_code_sections: bool,
        
        /// Whether to include strings from debug sections
        pub include_debug: bool,
        
        /// Section types to prioritize
        pub section_priority: Vec<SectionType>,
        
        /// Whether to include import/export names
        pub include_symbols: bool,
    }
    
    impl Default for ExtractionConfig {
        fn default() -> Self {
            Self {
                min_length: 4,
                max_length: 4096,
                encodings: vec![Encoding::Ascii, Encoding::Utf8],
                scan_code_sections: true,
                include_debug: false,
                section_priority: vec![
                    SectionType::StringData,
                    SectionType::ReadOnlyData,
                    SectionType::Resources,
                ],
                include_symbols: true,
            }
        }
    }
    \`\`\`
    
    ### 3. Basic Implementation
    
    Create a \`BasicExtractor\` that implements \`StringExtractor\` with straightforward sequential scanning for ASCII and UTF-8 strings.
    
    ## Architecture Integration
    
    The extraction framework fits into the pipeline as follows:
    
    \`\`\`
    Binary Data → Container Parser → ContainerInfo
                                            ↓
                                      StringExtractor + ExtractionConfig
                                            ↓
                                      Vec<FoundString>
                                            ↓
                                      Classification (future)
                                            ↓
                                      Output Formatting
    \`\`\`
    
    ## Implementation Details
    
    ### File Structure
    - Main trait and config: \`src/extraction/mod.rs\`
    - Basic extractor: \`src/extraction/basic.rs\` (or inline in mod.rs initially)
    - Tests: Unit tests in each module, integration tests in \`tests/\`
    
    ### Key Considerations
    - Use existing \`FoundString\` type (already defined in \`src/types.rs\`)
    - Leverage \`ContainerInfo\` to make format-aware decisions
    - Ensure zero-copy where possible (use slices and indices)
    - Handle encoding detection robustly (invalid sequences should not panic)
    - Set appropriate \`StringSource\` values based on where strings are found
    
    ## Acceptance Criteria
    
    - [ ] \`StringExtractor\` trait defined with documented methods
    - [ ] \`ExtractionConfig\` struct with sensible defaults
    - [ ] At least one working implementation (\`BasicExtractor\`)
    - [ ] Unit tests covering:
      - ASCII string extraction
      - UTF-8 string extraction  
      - Configuration validation
      - Edge cases (empty data, invalid encodings, boundary conditions)
    - [ ] Documentation with usage examples
    - [ ] Integration with existing \`ContainerInfo\` architecture
    
    ## Related
    
    - **Requirement**: 2.1 (String Extraction Framework)
    - **Task ID**: stringy-analyzer/string-extraction-framework
    - **Blocks**: Classification and scoring functionality (future issues)
    - **Repository**: https://github.com/EvilBit-Labs/StringyMcStringFace
    
    ## Notes
    
    - The issue originally mentioned \`RawString\` struct, but \`FoundString\` already exists in \`src/types.rs\` and should be used instead
    - Future extractors can implement format-specific logic (e.g., PE resource strings, Mach-O LC_ID_DYLIB)
    - This is the foundation for the entire extraction pipeline - needs to be solid and extensible"
    
    echo "Issue updated successfully"
    

    Length of output: 87


    🏁 Script executed:

    #!/bin/bash
    # Check available labels in the repository
    gh label list --limit 100
    
    echo -e "\n=== Current issue labels ==="
    gh issue view 8 --json labels --jq '.labels[].name'
    

    Length of output: 741


    @unclesp1d3r I've successfully enhanced issue #8 with comprehensive details:

    Changes Made

    ✅ Title Updated

    Changed from "String Extraction Framework" to "Core String Extraction Framework: StringExtractor Trait and Configuration" for better clarity and specificity.

    ✅ Body Enhanced With

    1. Background & Context - Explains how this fits into StringyMcStringFace's architecture and relates to existing container parsers
    2. Current State - Clear checklist showing what's done (parsers, types) and what's missing (extraction logic)
    3. Detailed Objectives - Three clear goals for the trait, config, and default implementation
    4. Proposed Solution - Complete API designs with Rust code examples for:
      • StringExtractor trait with two methods
      • ExtractionConfig struct with sensible defaults (min_length: 4, encodings, section priorities, etc.)
      • Suggestion for a BasicExtractor implementation
    5. Architecture Diagram - Visual pipeline showing data flow
    6. Implementation Details - File structure, key considerations (zero-copy, encoding handling, etc.)
    7. Acceptance Criteria - Testable checklist including unit tests for ASCII, UTF-8, edge cases
    8. Notes - Clarified that FoundString (not RawString) should be used, and mentioned extensibility for future format-specific extractors

    ✅ Labels & Metadata

    Existing labels are perfect and remain unchanged:

    • area:analyzer - Correct functional area
    • type:enhancement - Appropriate type
    • status:backlog - Current status
    • lang:rust - Implementation language
    • needs:tests - Correctly reflects acceptance criteria

    The issue is now ready for implementation with clear technical specifications and acceptance criteria!

  6. unclesp1d3r commented on Nov 12, 2025

    @unclesp1d3r
    MemberAuthor

    @traycerai generate

  7. traycerai commented on Nov 12, 2025

    @traycerai

    Hi @unclesp1d3r, I have updated the Implementation Plan to reflect the recent changes in the ticket.

    New Plan

    Observations

    The codebase has a well-structured foundation with complete container parsers (ELF, PE, Mach-O) and format-specific extractors (PE resources, Mach-O load commands). The missing piece is the core string extraction framework that will scan binary data for ASCII/UTF-8 strings. The existing FoundString type and ContainerInfo structure provide all necessary metadata. The task requires implementing a trait-based architecture with configuration support and a basic extractor implementation.

    Approach

    Implement the StringExtractor trait, ExtractionConfig struct, and BasicExtractor in src/extraction/mod.rs. The trait will define methods for extracting strings from full binary data and individual sections. The config will provide sensible defaults for minimum/maximum lengths, encoding selection, and section filtering. The BasicExtractor will implement sequential scanning for ASCII/UTF-8 strings using the algorithm pattern from the documentation. All components will integrate seamlessly with existing types and parsers.

    Reasoning

    I explored the repository structure, examined existing type definitions in src/types.rs, reviewed container parsers to understand section metadata handling, analyzed format-specific extractors (pe_resources.rs, macho_load_commands.rs) to understand the FoundString creation pattern, checked the library and CLI entry points, and reviewed documentation for extraction algorithms and architecture requirements.

    Proposed File Changes

    📄 src/extraction/mod.rs (MODIFY) 🔗

    References

    Add the core string extraction framework to this module:

    1. Import necessary types: Import Result, FoundString, ContainerInfo, SectionInfo, ExtractionConfig (to be defined), Encoding, StringSource, and SectionType from crate::types.

    2. Define ExtractionConfig struct: Create a configuration struct with the following fields:

      • min_length: usize - minimum string length (default: 4)
      • max_length: usize - maximum string length (default: 4096)
      • encodings: Vec<Encoding> - encodings to search for (default: ASCII, UTF-8)
      • scan_code_sections: bool - whether to scan executable sections (default: true)
      • include_debug: bool - whether to include debug sections (default: false)
      • section_priority: Vec<SectionType> - section types to prioritize (default: StringData, ReadOnlyData, Resources)
      • include_symbols: bool - whether to include import/export names (default: true)
    3. Implement Default for ExtractionConfig: Provide sensible defaults matching the specification in the GitHub issue.

    4. Define StringExtractor trait: Create a trait with two methods:

      • extract(&self, data: &[u8], container_info: &ContainerInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>> - extract strings from entire binary using container metadata
      • extract_from_section(&self, data: &[u8], section: &SectionInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>> - extract strings from a specific section
    5. Create BasicExtractor struct: Define a simple struct that will implement the StringExtractor trait.

    6. Implement BasicExtractor::new(): Add a constructor method.

    7. Implement helper function is_printable_ascii: Create a function that checks if a byte is printable ASCII (0x20-0x7E) or common whitespace (tab, newline, carriage return).

    8. Implement helper function extract_ascii_utf8_strings: Create a private function that scans byte data for ASCII/UTF-8 strings:

      • Iterate through bytes tracking current string and start offset
      • Accumulate printable characters
      • When non-printable byte encountered, check if accumulated string meets minimum length
      • Convert valid byte sequences to String, handling UTF-8 validation
      • Return vector of tuples containing (text, offset, length)
    9. Implement StringExtractor for BasicExtractor:

      • extract method: Iterate through sections in container_info, filter based on config (skip debug if not included, respect scan_code_sections), call extract_from_section for each, aggregate results
      • extract_from_section method:
        • Validate section bounds against data length
        • Extract section data slice using section.offset and section.size
        • Call extract_ascii_utf8_strings helper with section data and config min_length
        • For each found string, create FoundString with:
          • text: extracted string
          • encoding: Encoding::Ascii or Encoding::Utf8 (use UTF-8 if valid UTF-8, else ASCII)
          • offset: section.offset + relative offset from helper
          • rva: calculate from section.rva if available (section.rva + relative offset)
          • section: Some(section.name.clone())
          • length: string byte length as u32
          • tags: empty vector (classification happens later)
          • score: 0 (scoring happens later)
          • source: StringSource::SectionData
        • Filter strings by max_length from config
        • Return Result with vector of FoundString
    10. Add comprehensive documentation: Include module-level docs explaining the trait-based architecture, usage examples showing how to create a BasicExtractor and use it with ContainerInfo, and document all public types and methods with examples.

    11. Update module exports: Keep existing exports (extract_resources, extract_resource_strings, extract_load_command_strings) and add new exports for StringExtractor, ExtractionConfig, and BasicExtractor.

    12. Add unit tests: Create a #[cfg(test)] module with tests for:

    • is_printable_ascii with various byte values
    • extract_ascii_utf8_strings with test data containing ASCII strings, UTF-8 strings, null terminators, and binary data
    • ExtractionConfig::default() to verify default values
    • BasicExtractor::extract_from_section with mock section data
    • Edge cases: empty data, strings at boundaries, strings exceeding max_length, invalid UTF-8 sequences
    • Verify offset and RVA calculations are correct

    The implementation should follow the pattern established by existing extractors in pe_resources.rs and macho_load_commands.rs, ensuring consistency in how FoundString instances are created and populated.

    📄 src/lib.rs (MODIFY) 🔗

    References

    Update the library exports to include the new extraction framework types:

    1. Add to re-exports section: In the pub use types:: block (around line 55), add ExtractionConfig to the list of re-exported types from the types module (note: this will be added to types.rs, but if keeping it in extraction module, export from there instead).

    2. Update module documentation: In the module-level documentation (around line 39), update the extraction module status from "✅ PE resources complete" to "✅ Core framework and PE resources complete" to reflect the new functionality.

    3. Add usage example: In the "Basic Usage" section (around line 16), add an example showing how to use the new extraction framework:

      use stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor};
      
      let extractor = BasicExtractor::new();
      let config = ExtractionConfig::default();
      let strings = extractor.extract(&data, &container_info, &config)?;

    These changes ensure the new extraction framework is properly exposed in the public API and documented for users.

    📄 tests/integration_extraction.rs (NEW) 🔗

    References

    Create a new integration test file for the string extraction framework:

    1. Import necessary modules: Import stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor}, stringy::container::{detect_format, create_parser, ContainerParser}, stringy::types::{Encoding, StringSource}, and standard library modules for file I/O.

    2. Add fixture helper function: Create get_fixture_path(name: &str) function similar to existing integration tests that constructs path to test fixtures.

    3. Test: test_basic_extractor_ascii_strings:

      • Create test data with embedded ASCII strings ("Hello", "World", "Test123")
      • Create a mock SectionInfo with appropriate offset and size
      • Use BasicExtractor to extract strings from the section
      • Verify correct number of strings found
      • Verify string text, encoding (ASCII), offsets, and source are correct
    4. Test: test_basic_extractor_utf8_strings:

      • Create test data with UTF-8 strings including multi-byte characters
      • Extract strings and verify UTF-8 encoding is detected
      • Verify text is correctly decoded
    5. Test: test_basic_extractor_min_length_filtering:

      • Create data with strings of various lengths (2, 3, 4, 5, 10 characters)
      • Use config with min_length=4
      • Verify only strings >= 4 characters are extracted
    6. Test: test_basic_extractor_max_length_filtering:

      • Create data with very long string (>4096 characters)
      • Use default config with max_length=4096
      • Verify string is truncated or excluded based on implementation
    7. Test: test_basic_extractor_with_elf_fixture:

      • Load the ELF test fixture (test_binary_elf)
      • Parse with ElfParser to get ContainerInfo
      • Use BasicExtractor with default config to extract strings
      • Verify strings are found in appropriate sections (.rodata, etc.)
      • Verify offsets and RVAs are correctly calculated
      • Check that section names are populated
    8. Test: test_basic_extractor_with_pe_fixture:

      • Load the PE test fixture (test_binary_pe.exe)
      • Parse with PeParser to get ContainerInfo
      • Extract strings using BasicExtractor
      • Verify strings are found in .rdata or .data sections
      • Verify all FoundString fields are properly populated
    9. Test: test_basic_extractor_section_filtering:

      • Create config with scan_code_sections=false and include_debug=false
      • Parse a binary and extract strings
      • Verify no strings from code or debug sections are returned
    10. Test: test_basic_extractor_empty_data:

      • Test with empty byte slice
      • Verify no panic and empty result vector
    11. Test: test_basic_extractor_boundary_conditions:

      • Test strings at start of section
      • Test strings at end of section
      • Test string spanning entire section
      • Verify offset calculations are correct in all cases
    12. Test: test_extraction_config_defaults:

      • Create ExtractionConfig with default()
      • Verify all default values match specification (min_length=4, max_length=4096, etc.)

    These integration tests ensure the extraction framework works correctly with real binary data and integrates properly with the container parsing layer.

    Diagram

    sequenceDiagram
        participant User
        participant BasicExtractor
        participant ContainerInfo
        participant SectionInfo
        participant FoundString
    
        User->>BasicExtractor: extract(data, container_info, config)
        BasicExtractor->>ContainerInfo: Get sections list
        ContainerInfo-->>BasicExtractor: Vec<SectionInfo>
        
        loop For each section
            BasicExtractor->>BasicExtractor: Filter by config (code/debug)
            alt Section passes filter
                BasicExtractor->>BasicExtractor: extract_from_section(data, section, config)
                BasicExtractor->>BasicExtractor: Get section data slice
                BasicExtractor->>BasicExtractor: extract_ascii_utf8_strings(slice, min_len)
                
                loop For each found string
                    BasicExtractor->>BasicExtractor: Validate UTF-8
                    BasicExtractor->>BasicExtractor: Calculate offset = section.offset + relative
                    BasicExtractor->>BasicExtractor: Calculate RVA if available
                    BasicExtractor->>FoundString: Create with metadata
                    FoundString-->>BasicExtractor: FoundString instance
                end
            end
        end
        
        BasicExtractor-->>User: Vec<FoundString>
    
    Loading

    Import In IDE

    VSCode Icon Cursor Icon Windsurf Icon

    🤖 Prompt for AI Agents
    I have created the following plan after thorough exploration and analysis of the codebase. Follow the below plan verbatim. Trust the files and references. Do not re-verify what's written in the plan. Explore only when absolutely necessary. First implement all the proposed file changes and then I'll review all the changes together at the end.
    
    ### Observations
    
    The codebase has a well-structured foundation with complete container parsers (ELF, PE, Mach-O) and format-specific extractors (PE resources, Mach-O load commands). The missing piece is the core string extraction framework that will scan binary data for ASCII/UTF-8 strings. The existing `FoundString` type and `ContainerInfo` structure provide all necessary metadata. The task requires implementing a trait-based architecture with configuration support and a basic extractor implementation.
    
    ### Approach
    
    Implement the `StringExtractor` trait, `ExtractionConfig` struct, and `BasicExtractor` in `src/extraction/mod.rs`. The trait will define methods for extracting strings from full binary data and individual sections. The config will provide sensible defaults for minimum/maximum lengths, encoding selection, and section filtering. The `BasicExtractor` will implement sequential scanning for ASCII/UTF-8 strings using the algorithm pattern from the documentation. All components will integrate seamlessly with existing types and parsers.
    
    ### Reasoning
    
    I explored the repository structure, examined existing type definitions in `src/types.rs`, reviewed container parsers to understand section metadata handling, analyzed format-specific extractors (`pe_resources.rs`, `macho_load_commands.rs`) to understand the `FoundString` creation pattern, checked the library and CLI entry points, and reviewed documentation for extraction algorithms and architecture requirements.
    
    ## Mermaid Diagram
    
    sequenceDiagram
        participant User
        participant BasicExtractor
        participant ContainerInfo
        participant SectionInfo
        participant FoundString
    
        User->>BasicExtractor: extract(data, container_info, config)
        BasicExtractor->>ContainerInfo: Get sections list
        ContainerInfo-->>BasicExtractor: Vec<SectionInfo>
        
        loop For each section
            BasicExtractor->>BasicExtractor: Filter by config (code/debug)
            alt Section passes filter
                BasicExtractor->>BasicExtractor: extract_from_section(data, section, config)
                BasicExtractor->>BasicExtractor: Get section data slice
                BasicExtractor->>BasicExtractor: extract_ascii_utf8_strings(slice, min_len)
                
                loop For each found string
                    BasicExtractor->>BasicExtractor: Validate UTF-8
                    BasicExtractor->>BasicExtractor: Calculate offset = section.offset + relative
                    BasicExtractor->>BasicExtractor: Calculate RVA if available
                    BasicExtractor->>FoundString: Create with metadata
                    FoundString-->>BasicExtractor: FoundString instance
                end
            end
        end
        
        BasicExtractor-->>User: Vec<FoundString>
    
    ## Proposed File Changes
    
    ### src/extraction/mod.rs(MODIFY)
    
    References: 
    
    - src/types.rs
    - src/extraction/pe_resources.rs
    - src/extraction/macho_load_commands.rs
    
    Add the core string extraction framework to this module:
    
    1. **Import necessary types**: Import `Result`, `FoundString`, `ContainerInfo`, `SectionInfo`, `ExtractionConfig` (to be defined), `Encoding`, `StringSource`, and `SectionType` from `crate::types`.
    
    2. **Define `ExtractionConfig` struct**: Create a configuration struct with the following fields:
       - `min_length: usize` - minimum string length (default: 4)
       - `max_length: usize` - maximum string length (default: 4096)
       - `encodings: Vec<Encoding>` - encodings to search for (default: ASCII, UTF-8)
       - `scan_code_sections: bool` - whether to scan executable sections (default: true)
       - `include_debug: bool` - whether to include debug sections (default: false)
       - `section_priority: Vec<SectionType>` - section types to prioritize (default: StringData, ReadOnlyData, Resources)
       - `include_symbols: bool` - whether to include import/export names (default: true)
    
    3. **Implement `Default` for `ExtractionConfig`**: Provide sensible defaults matching the specification in the GitHub issue.
    
    4. **Define `StringExtractor` trait**: Create a trait with two methods:
       - `extract(&self, data: &[u8], container_info: &ContainerInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>>` - extract strings from entire binary using container metadata
       - `extract_from_section(&self, data: &[u8], section: &SectionInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>>` - extract strings from a specific section
    
    5. **Create `BasicExtractor` struct**: Define a simple struct that will implement the `StringExtractor` trait.
    
    6. **Implement `BasicExtractor::new()`**: Add a constructor method.
    
    7. **Implement helper function `is_printable_ascii`**: Create a function that checks if a byte is printable ASCII (0x20-0x7E) or common whitespace (tab, newline, carriage return).
    
    8. **Implement helper function `extract_ascii_utf8_strings`**: Create a private function that scans byte data for ASCII/UTF-8 strings:
       - Iterate through bytes tracking current string and start offset
       - Accumulate printable characters
       - When non-printable byte encountered, check if accumulated string meets minimum length
       - Convert valid byte sequences to String, handling UTF-8 validation
       - Return vector of tuples containing (text, offset, length)
    
    9. **Implement `StringExtractor` for `BasicExtractor`**:
       - **`extract` method**: Iterate through sections in `container_info`, filter based on config (skip debug if not included, respect `scan_code_sections`), call `extract_from_section` for each, aggregate results
       - **`extract_from_section` method**: 
         - Validate section bounds against data length
         - Extract section data slice using `section.offset` and `section.size`
         - Call `extract_ascii_utf8_strings` helper with section data and config min_length
         - For each found string, create `FoundString` with:
           - `text`: extracted string
           - `encoding`: Encoding::Ascii or Encoding::Utf8 (use UTF-8 if valid UTF-8, else ASCII)
           - `offset`: section.offset + relative offset from helper
           - `rva`: calculate from section.rva if available (section.rva + relative offset)
           - `section`: Some(section.name.clone())
           - `length`: string byte length as u32
           - `tags`: empty vector (classification happens later)
           - `score`: 0 (scoring happens later)
           - `source`: StringSource::SectionData
         - Filter strings by max_length from config
         - Return Result with vector of FoundString
    
    10. **Add comprehensive documentation**: Include module-level docs explaining the trait-based architecture, usage examples showing how to create a BasicExtractor and use it with ContainerInfo, and document all public types and methods with examples.
    
    11. **Update module exports**: Keep existing exports (`extract_resources`, `extract_resource_strings`, `extract_load_command_strings`) and add new exports for `StringExtractor`, `ExtractionConfig`, and `BasicExtractor`.
    
    12. **Add unit tests**: Create a `#[cfg(test)]` module with tests for:
       - `is_printable_ascii` with various byte values
       - `extract_ascii_utf8_strings` with test data containing ASCII strings, UTF-8 strings, null terminators, and binary data
       - `ExtractionConfig::default()` to verify default values
       - `BasicExtractor::extract_from_section` with mock section data
       - Edge cases: empty data, strings at boundaries, strings exceeding max_length, invalid UTF-8 sequences
       - Verify offset and RVA calculations are correct
    
    The implementation should follow the pattern established by existing extractors in `pe_resources.rs` and `macho_load_commands.rs`, ensuring consistency in how `FoundString` instances are created and populated.
    
    ### src/lib.rs(MODIFY)
    
    References: 
    
    - src/extraction/mod.rs(MODIFY)
    
    Update the library exports to include the new extraction framework types:
    
    1. **Add to re-exports section**: In the `pub use types::` block (around line 55), add `ExtractionConfig` to the list of re-exported types from the types module (note: this will be added to types.rs, but if keeping it in extraction module, export from there instead).
    
    2. **Update module documentation**: In the module-level documentation (around line 39), update the `extraction` module status from "✅ PE resources complete" to "✅ Core framework and PE resources complete" to reflect the new functionality.
    
    3. **Add usage example**: In the "Basic Usage" section (around line 16), add an example showing how to use the new extraction framework:
       ```rust
       use stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor};
       
       let extractor = BasicExtractor::new();
       let config = ExtractionConfig::default();
       let strings = extractor.extract(&data, &container_info, &config)?;
       ```
    
    These changes ensure the new extraction framework is properly exposed in the public API and documented for users.
    
    ### tests/integration_extraction.rs(NEW)
    
    References: 
    
    - tests/integration_elf.rs
    - tests/integration_pe.rs
    - src/extraction/mod.rs(MODIFY)
    
    Create a new integration test file for the string extraction framework:
    
    1. **Import necessary modules**: Import `stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor}`, `stringy::container::{detect_format, create_parser, ContainerParser}`, `stringy::types::{Encoding, StringSource}`, and standard library modules for file I/O.
    
    2. **Add fixture helper function**: Create `get_fixture_path(name: &str)` function similar to existing integration tests that constructs path to test fixtures.
    
    3. **Test: `test_basic_extractor_ascii_strings`**: 
       - Create test data with embedded ASCII strings ("Hello", "World", "Test123")
       - Create a mock SectionInfo with appropriate offset and size
       - Use BasicExtractor to extract strings from the section
       - Verify correct number of strings found
       - Verify string text, encoding (ASCII), offsets, and source are correct
    
    4. **Test: `test_basic_extractor_utf8_strings`**:
       - Create test data with UTF-8 strings including multi-byte characters
       - Extract strings and verify UTF-8 encoding is detected
       - Verify text is correctly decoded
    
    5. **Test: `test_basic_extractor_min_length_filtering`**:
       - Create data with strings of various lengths (2, 3, 4, 5, 10 characters)
       - Use config with min_length=4
       - Verify only strings >= 4 characters are extracted
    
    6. **Test: `test_basic_extractor_max_length_filtering`**:
       - Create data with very long string (>4096 characters)
       - Use default config with max_length=4096
       - Verify string is truncated or excluded based on implementation
    
    7. **Test: `test_basic_extractor_with_elf_fixture`**:
       - Load the ELF test fixture (`test_binary_elf`)
       - Parse with ElfParser to get ContainerInfo
       - Use BasicExtractor with default config to extract strings
       - Verify strings are found in appropriate sections (.rodata, etc.)
       - Verify offsets and RVAs are correctly calculated
       - Check that section names are populated
    
    8. **Test: `test_basic_extractor_with_pe_fixture`**:
       - Load the PE test fixture (`test_binary_pe.exe`)
       - Parse with PeParser to get ContainerInfo
       - Extract strings using BasicExtractor
       - Verify strings are found in .rdata or .data sections
       - Verify all FoundString fields are properly populated
    
    9. **Test: `test_basic_extractor_section_filtering`**:
       - Create config with `scan_code_sections=false` and `include_debug=false`
       - Parse a binary and extract strings
       - Verify no strings from code or debug sections are returned
    
    10. **Test: `test_basic_extractor_empty_data`**:
        - Test with empty byte slice
        - Verify no panic and empty result vector
    
    11. **Test: `test_basic_extractor_boundary_conditions`**:
        - Test strings at start of section
        - Test strings at end of section
        - Test string spanning entire section
        - Verify offset calculations are correct in all cases
    
    12. **Test: `test_extraction_config_defaults`**:
        - Create ExtractionConfig with default()
        - Verify all default values match specification (min_length=4, max_length=4096, etc.)
    
    These integration tests ensure the extraction framework works correctly with real binary data and integrates properly with the container parsing layer.

    Developer Humor

    🎯 String Extraction Framework 🎯\n>\n> A trait walked into a binary,\n> *Said \I'll find your strings, no worry!*\n> With ASCII and UTF-8,\n> And config that's first-rate,\n> Now extracting is no longer blurry!\n>\n> 📜 From chaos to clarity, one byte at a time! 🔍


    Execution Information

    Branch: main
    Commit: baf4526

  8. traycerai commented on Nov 12, 2025

    @traycerai

    Plan

    Observations

    The codebase has a well-structured foundation with complete container parsers (ELF, PE, Mach-O) and format-specific extractors (PE resources, Mach-O load commands). The missing piece is the core string extraction framework that will scan binary data for ASCII/UTF-8 strings. The existing FoundString type and ContainerInfo structure provide all necessary metadata. The task requires implementing a trait-based architecture with configuration support and a basic extractor implementation.

    Approach

    Implement the StringExtractor trait, ExtractionConfig struct, and BasicExtractor in src/extraction/mod.rs. The trait will define methods for extracting strings from full binary data and individual sections. The config will provide sensible defaults for minimum/maximum lengths, encoding selection, and section filtering. The BasicExtractor will implement sequential scanning for ASCII/UTF-8 strings using the algorithm pattern from the documentation. All components will integrate seamlessly with existing types and parsers.

    Reasoning

    I explored the repository structure, examined existing type definitions in src/types.rs, reviewed container parsers to understand section metadata handling, analyzed format-specific extractors (pe_resources.rs, macho_load_commands.rs) to understand the FoundString creation pattern, checked the library and CLI entry points, and reviewed documentation for extraction algorithms and architecture requirements.

    Proposed File Changes

    📄 src/extraction/mod.rs (MODIFY) 🔗

    References

    Add the core string extraction framework to this module:

    1. Import necessary types: Import Result, FoundString, ContainerInfo, SectionInfo, ExtractionConfig (to be defined), Encoding, StringSource, and SectionType from crate::types.

    2. Define ExtractionConfig struct: Create a configuration struct with the following fields:

      • min_length: usize - minimum string length (default: 4)
      • max_length: usize - maximum string length (default: 4096)
      • encodings: Vec<Encoding> - encodings to search for (default: ASCII, UTF-8)
      • scan_code_sections: bool - whether to scan executable sections (default: true)
      • include_debug: bool - whether to include debug sections (default: false)
      • section_priority: Vec<SectionType> - section types to prioritize (default: StringData, ReadOnlyData, Resources)
      • include_symbols: bool - whether to include import/export names (default: true)
    3. Implement Default for ExtractionConfig: Provide sensible defaults matching the specification in the GitHub issue.

    4. Define StringExtractor trait: Create a trait with two methods:

      • extract(&self, data: &[u8], container_info: &ContainerInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>> - extract strings from entire binary using container metadata
      • extract_from_section(&self, data: &[u8], section: &SectionInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>> - extract strings from a specific section
    5. Create BasicExtractor struct: Define a simple struct that will implement the StringExtractor trait.

    6. Implement BasicExtractor::new(): Add a constructor method.

    7. Implement helper function is_printable_ascii: Create a function that checks if a byte is printable ASCII (0x20-0x7E) or common whitespace (tab, newline, carriage return).

    8. Implement helper function extract_ascii_utf8_strings: Create a private function that scans byte data for ASCII/UTF-8 strings:

      • Iterate through bytes tracking current string and start offset
      • Accumulate printable characters
      • When non-printable byte encountered, check if accumulated string meets minimum length
      • Convert valid byte sequences to String, handling UTF-8 validation
      • Return vector of tuples containing (text, offset, length)
    9. Implement StringExtractor for BasicExtractor:

      • extract method: Iterate through sections in container_info, filter based on config (skip debug if not included, respect scan_code_sections), call extract_from_section for each, aggregate results
      • extract_from_section method:
        • Validate section bounds against data length
        • Extract section data slice using section.offset and section.size
        • Call extract_ascii_utf8_strings helper with section data and config min_length
        • For each found string, create FoundString with:
          • text: extracted string
          • encoding: Encoding::Ascii or Encoding::Utf8 (use UTF-8 if valid UTF-8, else ASCII)
          • offset: section.offset + relative offset from helper
          • rva: calculate from section.rva if available (section.rva + relative offset)
          • section: Some(section.name.clone())
          • length: string byte length as u32
          • tags: empty vector (classification happens later)
          • score: 0 (scoring happens later)
          • source: StringSource::SectionData
        • Filter strings by max_length from config
        • Return Result with vector of FoundString
    10. Add comprehensive documentation: Include module-level docs explaining the trait-based architecture, usage examples showing how to create a BasicExtractor and use it with ContainerInfo, and document all public types and methods with examples.

    11. Update module exports: Keep existing exports (extract_resources, extract_resource_strings, extract_load_command_strings) and add new exports for StringExtractor, ExtractionConfig, and BasicExtractor.

    12. Add unit tests: Create a #[cfg(test)] module with tests for:

    • is_printable_ascii with various byte values
    • extract_ascii_utf8_strings with test data containing ASCII strings, UTF-8 strings, null terminators, and binary data
    • ExtractionConfig::default() to verify default values
    • BasicExtractor::extract_from_section with mock section data
    • Edge cases: empty data, strings at boundaries, strings exceeding max_length, invalid UTF-8 sequences
    • Verify offset and RVA calculations are correct

    The implementation should follow the pattern established by existing extractors in pe_resources.rs and macho_load_commands.rs, ensuring consistency in how FoundString instances are created and populated.

    📄 src/lib.rs (MODIFY) 🔗

    References

    Update the library exports to include the new extraction framework types:

    1. Add to re-exports section: In the pub use types:: block (around line 55), add ExtractionConfig to the list of re-exported types from the types module (note: this will be added to types.rs, but if keeping it in extraction module, export from there instead).

    2. Update module documentation: In the module-level documentation (around line 39), update the extraction module status from "✅ PE resources complete" to "✅ Core framework and PE resources complete" to reflect the new functionality.

    3. Add usage example: In the "Basic Usage" section (around line 16), add an example showing how to use the new extraction framework:

      use stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor};
      
      let extractor = BasicExtractor::new();
      let config = ExtractionConfig::default();
      let strings = extractor.extract(&data, &container_info, &config)?;

    These changes ensure the new extraction framework is properly exposed in the public API and documented for users.

    📄 tests/integration_extraction.rs (NEW) 🔗

    References

    Create a new integration test file for the string extraction framework:

    1. Import necessary modules: Import stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor}, stringy::container::{detect_format, create_parser, ContainerParser}, stringy::types::{Encoding, StringSource}, and standard library modules for file I/O.

    2. Add fixture helper function: Create get_fixture_path(name: &str) function similar to existing integration tests that constructs path to test fixtures.

    3. Test: test_basic_extractor_ascii_strings:

      • Create test data with embedded ASCII strings ("Hello", "World", "Test123")
      • Create a mock SectionInfo with appropriate offset and size
      • Use BasicExtractor to extract strings from the section
      • Verify correct number of strings found
      • Verify string text, encoding (ASCII), offsets, and source are correct
    4. Test: test_basic_extractor_utf8_strings:

      • Create test data with UTF-8 strings including multi-byte characters
      • Extract strings and verify UTF-8 encoding is detected
      • Verify text is correctly decoded
    5. Test: test_basic_extractor_min_length_filtering:

      • Create data with strings of various lengths (2, 3, 4, 5, 10 characters)
      • Use config with min_length=4
      • Verify only strings >= 4 characters are extracted
    6. Test: test_basic_extractor_max_length_filtering:

      • Create data with very long string (>4096 characters)
      • Use default config with max_length=4096
      • Verify string is truncated or excluded based on implementation
    7. Test: test_basic_extractor_with_elf_fixture:

      • Load the ELF test fixture (test_binary_elf)
      • Parse with ElfParser to get ContainerInfo
      • Use BasicExtractor with default config to extract strings
      • Verify strings are found in appropriate sections (.rodata, etc.)
      • Verify offsets and RVAs are correctly calculated
      • Check that section names are populated
    8. Test: test_basic_extractor_with_pe_fixture:

      • Load the PE test fixture (test_binary_pe.exe)
      • Parse with PeParser to get ContainerInfo
      • Extract strings using BasicExtractor
      • Verify strings are found in .rdata or .data sections
      • Verify all FoundString fields are properly populated
    9. Test: test_basic_extractor_section_filtering:

      • Create config with scan_code_sections=false and include_debug=false
      • Parse a binary and extract strings
      • Verify no strings from code or debug sections are returned
    10. Test: test_basic_extractor_empty_data:

      • Test with empty byte slice
      • Verify no panic and empty result vector
    11. Test: test_basic_extractor_boundary_conditions:

      • Test strings at start of section
      • Test strings at end of section
      • Test string spanning entire section
      • Verify offset calculations are correct in all cases
    12. Test: test_extraction_config_defaults:

      • Create ExtractionConfig with default()
      • Verify all default values match specification (min_length=4, max_length=4096, etc.)

    These integration tests ensure the extraction framework works correctly with real binary data and integrates properly with the container parsing layer.

    Diagram

    sequenceDiagram
        participant User
        participant BasicExtractor
        participant ContainerInfo
        participant SectionInfo
        participant FoundString
    
        User->>BasicExtractor: extract(data, container_info, config)
        BasicExtractor->>ContainerInfo: Get sections list
        ContainerInfo-->>BasicExtractor: Vec<SectionInfo>
        
        loop For each section
            BasicExtractor->>BasicExtractor: Filter by config (code/debug)
            alt Section passes filter
                BasicExtractor->>BasicExtractor: extract_from_section(data, section, config)
                BasicExtractor->>BasicExtractor: Get section data slice
                BasicExtractor->>BasicExtractor: extract_ascii_utf8_strings(slice, min_len)
                
                loop For each found string
                    BasicExtractor->>BasicExtractor: Validate UTF-8
                    BasicExtractor->>BasicExtractor: Calculate offset = section.offset + relative
                    BasicExtractor->>BasicExtractor: Calculate RVA if available
                    BasicExtractor->>FoundString: Create with metadata
                    FoundString-->>BasicExtractor: FoundString instance
                end
            end
        end
        
        BasicExtractor-->>User: Vec<FoundString>
    
    Loading

    Import In IDE

    VSCode Icon Cursor Icon Windsurf Icon

    🤖 Prompt for AI Agents
    I have created the following plan after thorough exploration and analysis of the codebase. Follow the below plan verbatim. Trust the files and references. Do not re-verify what's written in the plan. Explore only when absolutely necessary. First implement all the proposed file changes and then I'll review all the changes together at the end.
    
    ### Observations
    
    The codebase has a well-structured foundation with complete container parsers (ELF, PE, Mach-O) and format-specific extractors (PE resources, Mach-O load commands). The missing piece is the core string extraction framework that will scan binary data for ASCII/UTF-8 strings. The existing `FoundString` type and `ContainerInfo` structure provide all necessary metadata. The task requires implementing a trait-based architecture with configuration support and a basic extractor implementation.
    
    ### Approach
    
    Implement the `StringExtractor` trait, `ExtractionConfig` struct, and `BasicExtractor` in `src/extraction/mod.rs`. The trait will define methods for extracting strings from full binary data and individual sections. The config will provide sensible defaults for minimum/maximum lengths, encoding selection, and section filtering. The `BasicExtractor` will implement sequential scanning for ASCII/UTF-8 strings using the algorithm pattern from the documentation. All components will integrate seamlessly with existing types and parsers.
    
    ### Reasoning
    
    I explored the repository structure, examined existing type definitions in `src/types.rs`, reviewed container parsers to understand section metadata handling, analyzed format-specific extractors (`pe_resources.rs`, `macho_load_commands.rs`) to understand the `FoundString` creation pattern, checked the library and CLI entry points, and reviewed documentation for extraction algorithms and architecture requirements.
    
    ## Mermaid Diagram
    
    sequenceDiagram
        participant User
        participant BasicExtractor
        participant ContainerInfo
        participant SectionInfo
        participant FoundString
    
        User->>BasicExtractor: extract(data, container_info, config)
        BasicExtractor->>ContainerInfo: Get sections list
        ContainerInfo-->>BasicExtractor: Vec<SectionInfo>
        
        loop For each section
            BasicExtractor->>BasicExtractor: Filter by config (code/debug)
            alt Section passes filter
                BasicExtractor->>BasicExtractor: extract_from_section(data, section, config)
                BasicExtractor->>BasicExtractor: Get section data slice
                BasicExtractor->>BasicExtractor: extract_ascii_utf8_strings(slice, min_len)
                
                loop For each found string
                    BasicExtractor->>BasicExtractor: Validate UTF-8
                    BasicExtractor->>BasicExtractor: Calculate offset = section.offset + relative
                    BasicExtractor->>BasicExtractor: Calculate RVA if available
                    BasicExtractor->>FoundString: Create with metadata
                    FoundString-->>BasicExtractor: FoundString instance
                end
            end
        end
        
        BasicExtractor-->>User: Vec<FoundString>
    
    ## Proposed File Changes
    
    ### src/extraction/mod.rs(MODIFY)
    
    References: 
    
    - src/types.rs
    - src/extraction/pe_resources.rs
    - src/extraction/macho_load_commands.rs
    
    Add the core string extraction framework to this module:
    
    1. **Import necessary types**: Import `Result`, `FoundString`, `ContainerInfo`, `SectionInfo`, `ExtractionConfig` (to be defined), `Encoding`, `StringSource`, and `SectionType` from `crate::types`.
    
    2. **Define `ExtractionConfig` struct**: Create a configuration struct with the following fields:
       - `min_length: usize` - minimum string length (default: 4)
       - `max_length: usize` - maximum string length (default: 4096)
       - `encodings: Vec<Encoding>` - encodings to search for (default: ASCII, UTF-8)
       - `scan_code_sections: bool` - whether to scan executable sections (default: true)
       - `include_debug: bool` - whether to include debug sections (default: false)
       - `section_priority: Vec<SectionType>` - section types to prioritize (default: StringData, ReadOnlyData, Resources)
       - `include_symbols: bool` - whether to include import/export names (default: true)
    
    3. **Implement `Default` for `ExtractionConfig`**: Provide sensible defaults matching the specification in the GitHub issue.
    
    4. **Define `StringExtractor` trait**: Create a trait with two methods:
       - `extract(&self, data: &[u8], container_info: &ContainerInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>>` - extract strings from entire binary using container metadata
       - `extract_from_section(&self, data: &[u8], section: &SectionInfo, config: &ExtractionConfig) -> Result<Vec<FoundString>>` - extract strings from a specific section
    
    5. **Create `BasicExtractor` struct**: Define a simple struct that will implement the `StringExtractor` trait.
    
    6. **Implement `BasicExtractor::new()`**: Add a constructor method.
    
    7. **Implement helper function `is_printable_ascii`**: Create a function that checks if a byte is printable ASCII (0x20-0x7E) or common whitespace (tab, newline, carriage return).
    
    8. **Implement helper function `extract_ascii_utf8_strings`**: Create a private function that scans byte data for ASCII/UTF-8 strings:
       - Iterate through bytes tracking current string and start offset
       - Accumulate printable characters
       - When non-printable byte encountered, check if accumulated string meets minimum length
       - Convert valid byte sequences to String, handling UTF-8 validation
       - Return vector of tuples containing (text, offset, length)
    
    9. **Implement `StringExtractor` for `BasicExtractor`**:
       - **`extract` method**: Iterate through sections in `container_info`, filter based on config (skip debug if not included, respect `scan_code_sections`), call `extract_from_section` for each, aggregate results
       - **`extract_from_section` method**: 
         - Validate section bounds against data length
         - Extract section data slice using `section.offset` and `section.size`
         - Call `extract_ascii_utf8_strings` helper with section data and config min_length
         - For each found string, create `FoundString` with:
           - `text`: extracted string
           - `encoding`: Encoding::Ascii or Encoding::Utf8 (use UTF-8 if valid UTF-8, else ASCII)
           - `offset`: section.offset + relative offset from helper
           - `rva`: calculate from section.rva if available (section.rva + relative offset)
           - `section`: Some(section.name.clone())
           - `length`: string byte length as u32
           - `tags`: empty vector (classification happens later)
           - `score`: 0 (scoring happens later)
           - `source`: StringSource::SectionData
         - Filter strings by max_length from config
         - Return Result with vector of FoundString
    
    10. **Add comprehensive documentation**: Include module-level docs explaining the trait-based architecture, usage examples showing how to create a BasicExtractor and use it with ContainerInfo, and document all public types and methods with examples.
    
    11. **Update module exports**: Keep existing exports (`extract_resources`, `extract_resource_strings`, `extract_load_command_strings`) and add new exports for `StringExtractor`, `ExtractionConfig`, and `BasicExtractor`.
    
    12. **Add unit tests**: Create a `#[cfg(test)]` module with tests for:
       - `is_printable_ascii` with various byte values
       - `extract_ascii_utf8_strings` with test data containing ASCII strings, UTF-8 strings, null terminators, and binary data
       - `ExtractionConfig::default()` to verify default values
       - `BasicExtractor::extract_from_section` with mock section data
       - Edge cases: empty data, strings at boundaries, strings exceeding max_length, invalid UTF-8 sequences
       - Verify offset and RVA calculations are correct
    
    The implementation should follow the pattern established by existing extractors in `pe_resources.rs` and `macho_load_commands.rs`, ensuring consistency in how `FoundString` instances are created and populated.
    
    ### src/lib.rs(MODIFY)
    
    References: 
    
    - src/extraction/mod.rs(MODIFY)
    
    Update the library exports to include the new extraction framework types:
    
    1. **Add to re-exports section**: In the `pub use types::` block (around line 55), add `ExtractionConfig` to the list of re-exported types from the types module (note: this will be added to types.rs, but if keeping it in extraction module, export from there instead).
    
    2. **Update module documentation**: In the module-level documentation (around line 39), update the `extraction` module status from "✅ PE resources complete" to "✅ Core framework and PE resources complete" to reflect the new functionality.
    
    3. **Add usage example**: In the "Basic Usage" section (around line 16), add an example showing how to use the new extraction framework:
       ```rust
       use stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor};
       
       let extractor = BasicExtractor::new();
       let config = ExtractionConfig::default();
       let strings = extractor.extract(&data, &container_info, &config)?;
       ```
    
    These changes ensure the new extraction framework is properly exposed in the public API and documented for users.
    
    ### tests/integration_extraction.rs(NEW)
    
    References: 
    
    - tests/integration_elf.rs
    - tests/integration_pe.rs
    - src/extraction/mod.rs(MODIFY)
    
    Create a new integration test file for the string extraction framework:
    
    1. **Import necessary modules**: Import `stringy::extraction::{BasicExtractor, ExtractionConfig, StringExtractor}`, `stringy::container::{detect_format, create_parser, ContainerParser}`, `stringy::types::{Encoding, StringSource}`, and standard library modules for file I/O.
    
    2. **Add fixture helper function**: Create `get_fixture_path(name: &str)` function similar to existing integration tests that constructs path to test fixtures.
    
    3. **Test: `test_basic_extractor_ascii_strings`**: 
       - Create test data with embedded ASCII strings ("Hello", "World", "Test123")
       - Create a mock SectionInfo with appropriate offset and size
       - Use BasicExtractor to extract strings from the section
       - Verify correct number of strings found
       - Verify string text, encoding (ASCII), offsets, and source are correct
    
    4. **Test: `test_basic_extractor_utf8_strings`**:
       - Create test data with UTF-8 strings including multi-byte characters
       - Extract strings and verify UTF-8 encoding is detected
       - Verify text is correctly decoded
    
    5. **Test: `test_basic_extractor_min_length_filtering`**:
       - Create data with strings of various lengths (2, 3, 4, 5, 10 characters)
       - Use config with min_length=4
       - Verify only strings >= 4 characters are extracted
    
    6. **Test: `test_basic_extractor_max_length_filtering`**:
       - Create data with very long string (>4096 characters)
       - Use default config with max_length=4096
       - Verify string is truncated or excluded based on implementation
    
    7. **Test: `test_basic_extractor_with_elf_fixture`**:
       - Load the ELF test fixture (`test_binary_elf`)
       - Parse with ElfParser to get ContainerInfo
       - Use BasicExtractor with default config to extract strings
       - Verify strings are found in appropriate sections (.rodata, etc.)
       - Verify offsets and RVAs are correctly calculated
       - Check that section names are populated
    
    8. **Test: `test_basic_extractor_with_pe_fixture`**:
       - Load the PE test fixture (`test_binary_pe.exe`)
       - Parse with PeParser to get ContainerInfo
       - Extract strings using BasicExtractor
       - Verify strings are found in .rdata or .data sections
       - Verify all FoundString fields are properly populated
    
    9. **Test: `test_basic_extractor_section_filtering`**:
       - Create config with `scan_code_sections=false` and `include_debug=false`
       - Parse a binary and extract strings
       - Verify no strings from code or debug sections are returned
    
    10. **Test: `test_basic_extractor_empty_data`**:
        - Test with empty byte slice
        - Verify no panic and empty result vector
    
    11. **Test: `test_basic_extractor_boundary_conditions`**:
        - Test strings at start of section
        - Test strings at end of section
        - Test string spanning entire section
        - Verify offset calculations are correct in all cases
    
    12. **Test: `test_extraction_config_defaults`**:
        - Create ExtractionConfig with default()
        - Verify all default values match specification (min_length=4, max_length=4096, etc.)
    
    These integration tests ensure the extraction framework works correctly with real binary data and integrates properly with the container parsing layer.

    Developer Humor

    🎯 String Extraction Framework 🎯\n>\n> A trait walked into a binary,\n> *Said \I'll find your strings, no worry!*\n> With ASCII and UTF-8,\n> And config that's first-rate,\n> Now extracting is no longer blurry!\n>\n> 📜 From chaos to clarity, one byte at a time! 🔍


    Execution Information

    Branch: main
    Commit: baf4526


    💡 Tips

    Supported Commands (Inside Comments)

    • Use @traycerai generate to iterate on the previous version of the implementation plan.

    Supported Commands (Inside Description)

    • Add @traycerai ignore anywhere in the ticket description to prevent this ticket from being processed.
    • Add @traycerai branch:<branch-name> anywhere in the ticket description to specify the target branch for the implementation plan.

    Community

    • Join our Discord Community to get help, request features, and share feedback.
    • Follow us on X/Twitter for updates and announcements.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions