Repository navigation
Labels
area:analyzerBinary analyzer functionalityBinary analyzer functionalitylang:rustRust implementationRust implementationneeds:testsNeeds test coverageNeeds test coveragestatus:backlogTask in backlogTask in backlogstory-points: 55 story points5 story pointstype:enhancementNew feature or requestNew feature or request
Milestone
Description
Problem Statement
Currently, the string extraction pipeline generates multiple FoundString entries for identical string text that appears in different locations within a binary. This creates significant noise in the output and makes analysis difficult.
Example scenario:
- The string "https://example.com" appears in:
.rodatasection at offset 0x1000.datasection at offset 0x3500- PE resource section at offset 0x8000
Without deduplication, three separate FoundString entries are emitted, even though they represent the same semantic content.
Goals
Implement intelligent string deduplication that:
- Reduces noise by consolidating duplicate string text into canonical entries
- Preserves metadata from all occurrences (offsets, sections, sources)
- Merges tags intelligently to combine semantic classifications
- Calculates combined relevance scores based on occurrence patterns
- Maintains traceability so analysts can see all locations where a string appears
Proposed Solution
Implementation Plan
Create src/extraction/dedup.rs with the following components:
1. Canonical String Structure
pub struct CanonicalString {
pub text: String,
pub encoding: Encoding,
pub occurrences: Vec<StringOccurrence>,
pub merged_tags: Vec<Tag>,
pub combined_score: i32,
}
pub struct StringOccurrence {
pub offset: u64,
pub rva: Option<u64>,
pub section: Option<String>,
pub source: StringSource,
pub original_tags: Vec<Tag>,
pub original_score: i32,
}2. Deduplication Algorithm
pub fn deduplicate(strings: Vec<FoundString>) -> Vec<CanonicalString> {
// 1. Group strings by (text, encoding) key
// 2. For each group, create CanonicalString with:
// - All occurrences preserved
// - Union of all tags across occurrences
// - Combined score = max(scores) + occurrence_bonus
// 3. Sort by combined_score descending
}3. Scoring Strategy
- Base score: Take the maximum score from all occurrences
- Occurrence bonus: +5 points for each additional occurrence (indicates importance)
- Cross-section bonus: +10 if string appears in multiple section types
- Multi-source bonus: +15 if string appears from different sources (e.g., both section data and imports)
4. Integration Points
Update the extraction pipeline in the architecture:
String Extraction → **Deduplication** → Classification → Ranking → Output
Output Format Considerations
The deduplicated output should show:
- Primary metadata from the "best" occurrence (highest original score)
- Count of total occurrences
- Optional verbose mode to list all locations
JSONL format:
{
"text": "https://example.com",
"encoding": "Utf8",
"occurrences": 3,
"locations": [
{"offset": 4096, "section": ".rodata", "source": "SectionData"},
{"offset": 13568, "section": ".data", "source": "SectionData"},
{"offset": 32768, "section": ".rsrc", "source": "ResourceString"}
],
"tags": ["Url"],
"score": 95
}Acceptance Criteria
-
src/extraction/dedup.rsmodule created with deduplication logic -
deduplicate()function handles identical text with different encodings separately - All occurrence metadata is preserved in the canonical structure
- Tags are merged intelligently (union, with duplicates removed)
- Combined scoring algorithm implemented with occurrence bonuses
- Unit tests covering:
- Basic deduplication of identical strings
- Metadata preservation from multiple occurrences
- Tag merging logic
- Score calculation with bonuses
- Edge cases (empty strings, single occurrence, etc.)
- Integration test with real binary containing duplicate strings
- Documentation updated in
docs/src/architecture.md
Technical Considerations
Edge Cases
- Empty strings: Should they be deduplicated or filtered?
- Whitespace variations: Are " test" and "test " the same?
- Encoding normalization: How to handle UTF-8 vs UTF-16 of same text?
Performance
- Use
HashMap<(String, Encoding), Vec<FoundString>>for efficient grouping - Consider memory implications for binaries with thousands of duplicate strings
- Benchmark against large binaries (>100MB) to ensure acceptable performance
Configuration
Consider adding configuration options:
--no-dedup: Skip deduplication for raw output--dedup-threshold: Only deduplicate strings appearing N+ times--dedup-mode: [strict|normalized] for handling whitespace
Related Files
src/types.rs: ContainsFoundStringstructure definitionsrc/extraction/mod.rs: Main extraction module (currently placeholder)docs/src/architecture.md: Architecture documentation to update
Task Context
Requirements: 2.5
Task-ID: stringy-analyzer/string-deduplication
Metadata
Metadata
Assignees
Labels
area:analyzerBinary analyzer functionalityBinary analyzer functionalitylang:rustRust implementationRust implementationneeds:testsNeeds test coverageNeeds test coveragestatus:backlogTask in backlogTask in backlogstory-points: 55 story points5 story pointstype:enhancementNew feature or requestNew feature or request
Activity
unclesp1d3r commentedon Sep 30, 2025
@coderabbitai Enhance this issue body with additional context and a proposed solution. Improve the issue title and assign the proper labels and other metadata.
[-]String Deduplication[/-][+]Implement String Deduplication with Metadata Preservation[/+]coderabbitai commentedon Sep 30, 2025
Tip
For best results, initiate chat on the files or code changes.
🧩 Analysis chain
🏁 Script executed:
Length of output: 1270
🏁 Script executed:
Length of output: 846
🏁 Script executed:
Length of output: 10488
🏁 Script executed:
Length of output: 103
@unclesp1d3r I've successfully enhanced issue #13! Here's what I updated:
Changes Made
📝 Improved Title
📄 Enhanced Issue Body
The issue now includes:
src/extraction/dedup.rsCanonicalStringandStringOccurrence🏷️ Labels & Metadata
The existing labels are already appropriate and well-assigned:
area:analyzer- Correct functional areatype:enhancement- Appropriate for new featurestatus:backlog- Current statuslang:rust- Implementation languageneeds:tests- Aligns with acceptance criteriaThe issue is now ready for implementation with clear technical guidance, acceptance criteria, and considerations for edge cases and performance! 🚀
unclesp1d3r commentedon Nov 11, 2025
@coderabbitai Should this be broken up into any sub-issues or is this a small enough unit of work?
coderabbitai commentedon Nov 11, 2025
Tip
For best results, initiate chat on the files or code changes.
🧩 Analysis chain
🏁 Script executed:
Length of output: 3523
@unclesp1d3r Based on the project's typical issue granularity, this should remain a single issue. Here's my analysis:
✅ Keep as Single Issue
After reviewing similar issues in your project, #13 fits perfectly within your typical scope:
Similar-sized issues:
Why keep it unified:
CanonicalString,StringOccurrence, scoring algorithm, and tests are interdependentsrc/extraction/dedup.rs) with well-defined inputs/outputs🔧 Optional Split (if needed later)
If you encounter blockers or want smaller increments, you could split into:
However, I'd recommend implementing this as-is. The acceptance criteria provides good checkpoints for tracking progress without creating separate issues.
unclesp1d3r commentedon Nov 13, 2025
@coderabbitai Enhance this issue body with additional context and a proposed solution. Improve the issue title and assign the proper labels and other metadata.
coderabbitai commentedon Nov 13, 2025
Tip
For best results, initiate chat on the files or code changes.
🧩 Analysis chain
🏁 Script executed:
Length of output: 6080
@unclesp1d3r Good news! Issue #13 has already been fully enhanced with all the requested improvements. ✅
Current State Summary
✅ Title Enhanced
"Implement String Deduplication with Metadata Preservation"
✅ Comprehensive Body Content
The issue includes:
CanonicalString,StringOccurrence)✅ Labels & Metadata Properly Assigned
area:analyzer- Correct functional areatype:enhancement- New featurestatus:backlog- Current statelang:rust- Implementation languageneeds:tests- Testing requirementstory-points: 5- Estimation labelThe issue is ready for implementation with comprehensive technical guidance! Is there anything specific you'd like me to adjust or add? 🚀
unclesp1d3r commentedon Jan 4, 2026
@traycerai generate
traycerai commentedon Jan 4, 2026
Hi @unclesp1d3r, I have updated the Implementation Plan to reflect the recent changes in the ticket.
traycerai commentedon Jan 4, 2026
Plan
Observations
The Stringy codebase has a well-structured extraction pipeline with
BasicExtractorreturningVec<FoundString>. TheFoundStringstructure contains all necessary metadata (text, encoding, offset, rva, section, tags, score, source, confidence). The architecture document already plans forfile:src/extraction/dedup.rsas part of the extraction module. Standard Rust collections (HashMap,HashSet) are already used throughout the codebase. The output module is currently a placeholder, providing flexibility for deduplicated output formatting.Approach
Implement string deduplication as a post-extraction processing step that groups
FoundStringentries by(text, encoding)keys, preserving all occurrence metadata in a newCanonicalStringstructure. The deduplication module will merge tags using set union semantics, calculate combined scores with occurrence-based bonuses, and maintain complete traceability. This fits naturally between the extraction and classification phases in the existing pipeline:String Extraction → Deduplication → Classification → Ranking → Output.Implementation Steps
1. Create Deduplication Module Structure
File:
file:src/extraction/dedup.rsDefine the core data structures for canonical strings and occurrences:
Create
CanonicalStringstruct with fields:text: String- the deduplicated string contentencoding: Encoding- encoding type fromfile:src/types.rsoccurrences: Vec<StringOccurrence>- all locations where string appearsmerged_tags: Vec<Tag>- union of tags from all occurrencescombined_score: i32- calculated score with bonusesCreate
StringOccurrencestruct with fields:offset: u64- file offsetrva: Option<u64>- relative virtual addresssection: Option<String>- section namesource: StringSource- extraction source typeoriginal_tags: Vec<Tag>- tags from this specific occurrenceoriginal_score: i32- score from this specific occurrenceconfidence: f32- confidence score from noise filteringImplement
SerializeandDeserializetraits for both structs usingserde(already a dependency infile:Cargo.toml)2. Implement Core Deduplication Algorithm
Function:
pub fn deduplicate(strings: Vec<FoundString>) -> Vec<CanonicalString>Algorithm steps:
HashMap<(String, Encoding), Vec<FoundString>>to group strings by keyVec<FoundString>and populate the HashMapStringOccurrenceentries from eachFoundStringin the groupHashSetto ensure uniqueness, then convert toVec<Tag>CanonicalStringwith all metadataVec<CanonicalString>bycombined_scorein descending order3. Implement Scoring Strategy
Function:
fn calculate_combined_score(occurrences: &[StringOccurrence]) -> i32Scoring components:
original_scoreacross all occurrences5 * (occurrences.len() - 1)points for multiple occurrences10points if string appears in sections with different names (useHashSet<Option<String>>to count unique sections)15points if string appears from differentStringSourcevariants (useHashSet<StringSource>to count unique sources)(max_confidence * 10.0) as i32wheremax_confidenceis the highest confidence value across occurrencesReturn the sum of all components.
4. Implement Tag Merging Logic
Function:
fn merge_tags(occurrences: &[StringOccurrence]) -> Vec<Tag>Implementation:
HashSet<Tag>to collect unique tagsoriginal_tagsVec<Tag>and sort for consistent outputNote: The
Tagenum infile:src/types.rsalready implementsPartialEqandEq, making it suitable for HashSet usage.5. Add Conversion Utilities
Function:
fn found_string_to_occurrence(fs: FoundString) -> StringOccurrenceConvert
FoundStringtoStringOccurrenceby mapping fields:original_tagsoriginal_scoreFunction:
impl CanonicalString { pub fn to_found_string(&self) -> FoundString }Create a representative
FoundStringfromCanonicalStringfor backward compatibility:6. Integrate with Extraction Module
File:
file:src/extraction/mod.rspub mod dedup;declaration in the module list (around line 109-130)pub use dedup::{CanonicalString, StringOccurrence, deduplicate};File:
file:src/lib.rsCanonicalStringandStringOccurrenceto the re-exports (around line 83-85)7. Implement Comprehensive Unit Tests
File:
file:src/extraction/dedup.rs(test module)Test cases to implement:
CanonicalStringwith 3 occurrences8. Create Integration Tests
File:
file:tests/test_deduplication.rs(new file)Integration test scenarios:
BasicExtractorto extract stringsdeduplicate()functionfile:tests/integration_extraction.rspatterns9. Update Architecture Documentation
File:
file:docs/src/architecture.mdUpdates needed:
In the "High-Level Architecture" section (line 8), update the pipeline to show deduplication explicitly:
In the "Extraction Module" section (line 43-51), update status from "Framework Ready" to include deduplication implementation details
In the "Data Flow" section (line 128-130), expand the deduplication pseudocode with actual implementation details
Add a new subsection "Deduplication System" explaining:
10. Configuration Options (Optional Enhancement)
File:
file:src/extraction/config.rsAdd optional deduplication configuration to
ExtractionConfig:enable_deduplication: bool- toggle deduplication on/off (default: true)dedup_threshold: Option<usize>- only deduplicate strings appearing N+ timespreserve_all_occurrences: bool- whether to keep full occurrence list or just countUpdate the
Defaultimplementation andvalidate()method accordingly.Data Flow Diagram
sequenceDiagram participant Binary as Binary File participant Parser as Container Parser participant Extractor as String Extractor participant Dedup as Deduplicator participant Output as Output Formatter Binary->>Parser: Read binary data Parser->>Parser: Detect format (ELF/PE/Mach-O) Parser->>Parser: Extract sections, imports, exports Parser->>Extractor: ContainerInfo with metadata Extractor->>Extractor: Extract from .rodata/.rdata Extractor->>Extractor: Extract from resources Extractor->>Extractor: Extract from imports/exports Extractor->>Dedup: Vec<FoundString> (with duplicates) Dedup->>Dedup: Group by (text, encoding) Dedup->>Dedup: Create StringOccurrence for each Dedup->>Dedup: Merge tags (union) Dedup->>Dedup: Calculate combined scores Dedup->>Output: Vec<CanonicalString> (deduplicated) Output->>Output: Format as JSON/Human/YARA Output->>Binary: Display resultsKey Implementation Details
(String, Encoding)tuple - ensures UTF-8 and UTF-16 versions are separateHashMap<(String, Encoding), Vec<FoundString>>for O(1) groupingHashSet<Tag>for deduplication, then convert to sortedVec<Tag>Vec<CanonicalString>sorted bycombined_scoredescendingto_found_string()method for existing pipeline integrationTesting Strategy
Import In IDE
🤖 Prompt for AI Agents
Execution Information
Branch: main
Commit: 1c2a33b
💡 Tips
Supported Commands (Inside Comments)
@traycerai generateto iterate on the previous version of the implementation plan.Supported Commands (Inside Description)
@traycerai ignoreanywhere in the ticket description to prevent this ticket from being processed.@traycerai branch:<branch-name>anywhere in the ticket description to specify the target branch for the implementation plan.Community