Skip to content

feat: Import/Export symbol classification pipeline - #174

Merged
mergify[bot] merged 21 commits into
mainfrom
20-implement-importexport-symbol-classification-pipeline
Jun 19, 2026
Merged

mergify[bot] merged 21 commits into
mainfrom
20-implement-importexport-symbol-classification-pipeline

Conversation

@unclesp1d3r

Copy link
Copy Markdown
Member

Summary

Implements the import/export symbol classification pipeline (issue #20). A new ImportClassifier converts ContainerInfo imports, exports, and section names into tagged, scored FoundStrings, wired into the extraction path as the single symbol emission point — so the previously-dormant Tag::Import/Tag::Export ranking boosts finally fire on live output. Adds four semantic tags (Crypto/Network/FileIO/EntryPoint) recognized via static API-name sets and the existing demangler.

Origin plan: docs/plans/2026-06-19-001-feat-symbol-classification-pipeline-plan.md (from docs/brainstorms/2026-06-19-symbol-classification-pipeline-requirements.md).

What changed (by implementation unit)

  • U1 — Four Tag variants (Crypto/Network/FileIO/EntryPoint, serde crypto/network/fileio/entry-point) and a StringSource::SectionName variant, threaded through FromStr, the table + YARA display formatters, both CLI long_help literals, and every variant-enumerating test list.
  • U2 — RankingConfig boosts: Crypto +50, Network +45, FileIO +35, EntryPoint +40.
  • U3 — New src/classification/imports.rs: ImportClassifier + extract_symbol_strings. Imports carry Tag::Import plus a semantic tag from LazyLock<HashSet> API sets, with section from library or a format default (.idata/.dynsym/__LINKEDIT) and RVA when present; exports carry Tag::Export + RVA + Tag::EntryPoint for known entry points; section names emit with StringSource::SectionName.
  • U6 — ASCII extraction now emits Encoding::Utf8 (ASCII is a UTF-8 subset) so a byte-scanned occurrence of a symbol shares the (text, encoding) dedup key and merges. --enc ascii is redefined as a content filter (EncodingFilter::AsciiContent).
  • U4 — Routed extract_symbol_strings into collect_all_strings as the single emission point.
  • U5 — Docs: once_cell → std::sync::LazyLock.

Live verification on the ELF fixture: imports tag as ['Import','fileio'] (e.g. read/write) with library sections, exports _start/main tag as ['Export','entry-point'], section names emit, and there are zero duplicate (text,encoding) keys (AE4 merge confirmed).

Notable decisions

  • --enc ascii is now a content filter (R16, CLI contract change). Since extraction no longer labels rows Encoding::Ascii, --enc ascii matches rows whose text is pure-ASCII and whose encoding is not UTF-16 — preserving the flag's prior "narrow ASCII strings" meaning. JSON output no longer emits "encoding":"Ascii" (now "Utf8"). Documented in the AGENTS.md CLI table and GOTCHAS.md.
  • Section-name rows are scoped to recognized formats. The unknown/raw fallback's synthetic raw-bytes section is not a real section, so Unknown format skips section-name rows (keeps empty-file output empty; unknown-binary rows all attributed to raw-bytes).
  • Demangling stays in the pipeline, not the classifier (deviates from plan KTD3). Code review found that demangling exports during extraction ran outside the pipeline's catch_unwind guard (a cpp_demangle panic on a malicious export name could abort the process) and made raw mode show demangled text. The pipeline's classify_strings already demangles every mangled symbol under catch_unwind (skipped in raw mode), so the classifier emission was redundant. The classifier now only tags; demangling happens downstream, preserving AE2 end-to-end. KTD3 predated the knowledge that the pipeline already demangles.

Test plan

  • cargo nextest run — 632 pass, 23 skipped
  • just lint-rust — clean (clippy -D warnings)
  • New tests/classification_symbols.rs covers R1-R8, AE1-AE3, the Unknown-format skip, and end-to-end fixture parsing (ELF/PE/Mach-O)
  • --enc ascii content-filter semantics asserted end-to-end (encoding_filter_ascii_is_content_based)
  • Regenerated insta snapshots (flow1_top3_json, yara_all_tag_types)
  • CI: clippy, rustfmt, tests, CodeQL, cargo-deny

Note: a pre-existing unnecessary_sort_by clippy break (the floated toolchain's newer clippy flags it on 5 untouched call sites) is fixed in cd74359 so CI is green.

Residual review findings (not blocking; for maintainer judgment)

  • P2 — dedup representative metadata can be nondeterministic. When a symbol name also appears in byte-scanned section content, dedup unions tags (so the row stays tagged and appears once — AE4 holds) but adopts the source/section/rva of a nondeterministic first occurrence. Fixing it properly means preferring the ImportName/ExportName occurrence in dedup's representative selection, which is out of scope per the plan's Scope Boundaries ("Redesigning deduplication is out of scope"). Partially documented in KTD1. Suggested follow-up.
  • P2 — StringSource is not #[non_exhaustive]. Adding SectionName is technically an exhaustive-match break for external crates that match StringSource (the Tag enum is #[non_exhaustive]; StringSource is not). Low real impact at v0.1.0. Consider adding #[non_exhaustive] to StringSource for consistency with Tag — a public-API policy call left to the maintainer.

https://claude.ai/code/session_012sGrToJP47o1ZcGzNKGHt8

…lity

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
…sification

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
…sification pipeline

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
…sification pipeline

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
The floated toolchain's clippy (1.96) flags unnecessary_sort_by on these
pre-existing descending sorts. Convert to sort_by_key(Reverse(..)) -- same
stable comparator, behavior-preserving -- so clippy -D warnings passes.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
…e source

Add four semantic Tag variants (serde renames crypto/network/fileio/entry-point)
and a StringSource::SectionName variant for section-name rows. Thread the tags
through FromStr, the table and YARA display formatters, and both CLI --only-tags
/--no-tags long_help literals. Extend the variant-enumerating test lists and the
JSON source-serialization test; regenerate the YARA all-tags snapshot.

Implements U1 (R9). Foundational unit for the symbol classification pipeline.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Crypto +50, Network +45, FileIO +35, EntryPoint +40 in RankingConfig::default().
Extend test_default_config_values and add a test that a Crypto-tagged string
outranks an otherwise identical untagged peer by at least the boost.

Implements U2 (R10).

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
New ImportClassifier converts ContainerInfo imports, exports, and section names
into tagged FoundStrings: imports carry Tag::Import plus a semantic tag (Crypto/
Network/FileIO) from static LazyLock API sets, with section from library or a
format default (.idata/.dynsym/__LINKEDIT) and RVA when present; exports carry
Tag::Export with RVA, are demangled (Tag::DemangledSymbol), and gain
Tag::EntryPoint for known entry points; section names emit with
StringSource::SectionName. extract_symbol_strings composes all three.

Symbols are emitted as Encoding::Utf8 so byte-scanned occurrences share the
dedup key (KTD7). Provenance tests use hand-built ContainerInfo (KTD1).

Implements U3 (R1-R8, R13; AE1-AE3).

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
… content filter

ASCII extraction now emits Encoding::Utf8 (ASCII is a UTF-8 subset) so a
byte-scanned occurrence of a symbol name shares the (text, encoding) dedup key
with the classifier's occurrence and merges into one row (KTD7, enables AE4).
The Encoding::Ascii variant is retained but no longer produced as output (R15).

Since no row is labeled Encoding::Ascii anymore, --enc ascii is redefined as a
content filter (new EncodingFilter::AsciiContent): it matches rows whose text is
pure-ASCII and whose encoding is not UTF-16, preserving the flag's prior
narrow-strings-only meaning (R16). The enabled_encodings ASCII entry still gates
whether the ASCII byte-scan runs.

Implements U6 (R15, R16). Updates extracted-output encoding assertions and the
flow1 JSON snapshot (Ascii -> Utf8).

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Replace the untagged import/export push in collect_all_strings with a single
extract_symbol_strings call inside the include_symbols gate, so symbols reach
live output already tagged and the Import/Export and semantic ranking boosts
fire. Section-name rows ride the same gate.

Scope section-name emission to recognized formats: the unknown/raw fallback's
synthetic 'raw-bytes' section is not a real section, so Unknown format skips
section-name rows (keeps empty-file output empty and unknown-binary rows all
attributed to 'raw-bytes').

Update symbol-property tests to the classifier contract (RVA/section/tags),
assert content via SectionData source where section names now also appear, and
add an Unknown-format regression test.

Implements U4 (R11, R12; AE4).

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
…:LazyLock

The implementation uses std::sync::LazyLock; update the classification docs
prose and example to match. Implements U5 (R14).

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Evaluate the O(1) UTF-16 encoding guards before the O(n) text.is_ascii()
content scan so UTF-16 rows skip the scan. Same boolean result; tightens the
config.rs comment to scope the no-longer-Ascii claim to the extraction path.

From ce-simplify-code review.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
…symbol rows

Code review found that demangling exports inside process_exports (during
extraction) ran outside the pipeline's catch_unwind guard -- a cpp_demangle
panic on a malicious export name would abort the process -- and also caused raw
mode to show demangled text. Demangling already happens once in the pipeline's
classify_strings (under catch_unwind, skipped in raw mode), so the classifier
emission was redundant. Remove it: ImportClassifier is now stateless and only
tags (Export + EntryPoint); the pipeline demangles downstream, preserving AE2
end-to-end. This also lets a mangled export share a dedup key with any
byte-scanned occurrence of the same raw name. (Deviates from plan KTD3, which
predated the knowledge that the pipeline already demangles.)

Also: skip empty section names (PE all-null name fields) so no zero-length row
reaches output; document the --enc ascii content-filter contract in the AGENTS.md
CLI table and GOTCHAS.md; strengthen the --enc ascii CLI test to assert
content-based filtering; re-point the export test to the new contract.

From ce-code-review (P1 panic-safety, P2 docs, P3 empty-name guard).

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Copilot AI review requested due to automatic review settings June 19, 2026 17:50
@unclesp1d3r unclesp1d3r linked an issue Jun 19, 2026 that may be closed by this pull request
8 tasks
@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Architecture & Semantic Classification

This PR implements a centralized import/export symbol classification pipeline by introducing ImportClassifier as the single emission point for tagged symbol strings. Previously, imports and exports were emitted untagged directly from BasicExtractor; now all symbol emission routes through the classifier, enabling consistent tagging and scoring across the extraction pipeline.

API Changes

New Public Types and Functions:

  • ImportClassifier struct with methods: process_imports(), process_exports(), process_section_names()
  • extract_symbol_strings() function as the unified entry point for symbol extraction
  • Four new Tag variants: Crypto, Network, FileIO, EntryPoint (with serde mappings to "crypto", "network", "fileio", "entry-point")
  • New StringSource::SectionName variant for emitting section names as standalone rows
  • New EncodingFilter::AsciiContent variant for content-based ASCII filtering
  • StringSource and Tag enums marked #[non_exhaustive] to prevent external exhaustive matching (semver safety)

Encoding Reclassification:
ASCII extraction now emits Encoding::Utf8 instead of Encoding::Ascii, treating ASCII as a UTF-8 subset. This enables deduplication with byte-scanned occurrences of the same text. The --enc ascii flag is redefined from an exact encoding match to a content filter: it matches rows where text.is_ascii() and encoding is not UTF-16, preserving the original "narrow ASCII strings" semantics.

Binary Format Handling

The classifier implements format-aware symbol processing:

  • Imports: Assigns section information from the import's library name when available, otherwise uses format-specific defaults (excludes format-specific section names for Mach-O's library-less imports). RVA is populated only when ImportInfo::address is present.
  • Exports: Always includes RVA from ExportInfo::address. Entry-point detection applies a static set of known entry points.
  • Section Names: Emitted as standalone FoundString rows with StringSource::SectionName, skipping empty names. Section-name rows are omitted when container_info.format is BinaryFormat::Unknown, excluding the raw-bytes fallback's synthetic sections.

Semantic Classification

Four static API-name HashSets enable semantic tagging:

  • Crypto (e.g., EVP_EncryptInit, CryptEncrypt, AES_encrypt): +50 ranking boost
  • Network (e.g., WSASocket, connect, send): +45 boost
  • FileIO (e.g., CreateFileA, ReadFile, open): +35 boost
  • EntryPoint (detected export names): +40 boost

Each import receives Tag::Import plus one semantic tag when its name matches an API set. Exports receive Tag::Export and optionally Tag::EntryPoint. Demangling is deliberately deferred to the pipeline's classify_strings function (running under catch_unwind), not performed in the classifier; this preserves end-to-end error isolation and raw-mode consistency.

Test Coverage

The PR includes 632 passing tests (23 skipped) with comprehensive symbol classification validation:

  • New integration test module tests/classification_symbols.rs (302 lines) covers semantic tag augmentation, format-specific section assignment, RVA population, entry-point detection, and section-name emission
  • Fixture tests on ELF/PE/Mach-O verify correct tagging and entry-point recognition
  • Encoding reclassification verified in ASCII extraction tests and integration tests
  • Content-based AsciiContent filtering validated with new integration test encoding_filter_ascii_is_content_based
  • CLI help text updated and verified to include new canonical tags

Walkthrough

Introduces an ImportClassifier as the single emission point for tagged FoundString rows derived from ContainerInfo imports, exports, and section names. Adds four new Tag variants (Crypto, Network, FileIO, EntryPoint) with ranking boosts. ASCII extraction output is relabeled to Encoding::Utf8, and --enc ascii is redefined as a content-based filter via EncodingFilter::AsciiContent.

Changes

Import/Export Symbol Classification Pipeline

Layer / File(s) Summary
New Tag variants and StringSource::SectionName
src/types/mod.rs, src/types/tests.rs
Tag gains Crypto, Network, FileIO, EntryPoint with serde renames and FromStr parsing; StringSource gains SectionName; tests verify all new from_str round-trips.
ASCII encoding normalization and AsciiContent filter
src/extraction/ascii/extraction.rs, src/extraction/ascii/tests.rs, src/pipeline/config.rs, src/pipeline/filter.rs, src/main.rs
ASCII extractor emits Encoding::Utf8 at all construction sites; EncodingFilter::AsciiContent variant added, filtering by text.is_ascii() while excluding UTF-16; --enc ascii maps to AsciiContent at the CLI boundary.
Ranking boosts for new semantic tags
src/classification/ranking.rs
RankingConfig::default() adds boosts for the four new tags; rank_strings switches to sort_by_key(Reverse); tests assert new defaults and that Crypto-tagged strings outscore untagged peers by at least 50.
ImportClassifier and extract_symbol_strings
src/classification/imports.rs, src/classification/mod.rs
New module defines static LazyLock API-name sets for semantic classification; ImportClassifier emits tagged FoundString rows for imports (with optional semantic tag and per-format section default), exports (always with RVA and optional EntryPoint tag), and non-empty section names; extract_symbol_strings aggregates all three, suppressing section-name rows for BinaryFormat::Unknown; module re-exports both public items.
BasicExtractor routing and sort cleanup
src/extraction/basic_extractor.rs, src/extraction/dedup/mod.rs, src/output/mod.rs, examples/*
collect_all_strings replaces manual import/export loops with a single extract_symbol_strings call; deduplicate, OutputMetadata::compute_top_tags, and examples switch to sort_by_key(Reverse) for consistent descending-score ordering.
Output formatter support for new tags
src/output/table/formatting.rs, src/output/yara/mod.rs, src/output/json.rs, src/main.rs
Table, YARA, and JSON formatters gain match arms for Crypto, Network, FileIO, EntryPoint; JSON source-type test covers StringSource::SectionName; CLI --only-tags/--no-tags help lists extended; --enc help text clarified.
Classification symbol tests
tests/classification_symbols.rs
New test file covers import semantic enrichment, section selection, RVA propagation, export raw emission and entry-point detection, section-name skipping for empty names and unknown formats, and ELF/PE/Mach-O fixture end-to-end contracts ensuring imports/exports always carry their respective tags.
Extraction and integration test updates
src/extraction/tests.rs, tests/integration_extraction.rs, tests/integration_cli.rs, tests/integration_cli_coverage.rs, tests/test_ascii_extraction.rs, tests/test_deduplication.rs, tests/output_yara_integration.rs
All encoding assertions updated to Encoding::Utf8; symbol tests assert Tag::Import/Export/EntryPoint, RVA, and section fields; new --enc ascii content-filter integration test verifies only ASCII-text rows are emitted; YARA snapshot extended for new tags; dedup test switched to dynamic baseline.
Docs, plans, and config
docs/brainstorms/, docs/plans/, docs/src/classification.md, GOTCHAS.md, AGENTS.md, CHANGELOG.md, AI_POLICY.md, .mdformat.toml, .claude/settings.json, mise.toml
New brainstorm and plan docs specify pipeline design, acceptance examples, and implementation units; classification.md example switches once_cell to std::sync::LazyLock; GOTCHAS.md and AGENTS.md document redefined --enc ascii semantics; formatting and config housekeeping.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI (main.rs)
  participant BasicExtractor
  participant ImportClassifier
  participant RankingEngine
  participant FilterEngine

  CLI->>BasicExtractor: extract(config, container_info)
  BasicExtractor->>ImportClassifier: extract_symbol_strings(container_info)
  ImportClassifier->>ImportClassifier: process_imports → Tag::Import + optional Crypto/Network/FileIO
  ImportClassifier->>ImportClassifier: process_exports → Tag::Export + optional EntryPoint
  ImportClassifier->>ImportClassifier: process_section_names → StringSource::SectionName [non-Unknown]
  ImportClassifier-->>BasicExtractor: Vec<FoundString> (tagged)
  BasicExtractor->>RankingEngine: rank_strings (boost by Crypto/Network/FileIO/EntryPoint)
  RankingEngine-->>BasicExtractor: sorted by score desc (sort_by_key Reverse)
  BasicExtractor->>FilterEngine: apply(EncodingFilter::AsciiContent or other)
  FilterEngine->>FilterEngine: AsciiContent: keep if !UTF-16 && text.is_ascii()
  FilterEngine-->>CLI: filtered FoundStrings
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • EvilBit-Labs/Stringy#125: Introduces the RankingEngine/RankingConfig that this PR extends with Crypto/Network/FileIO/EntryPoint default tag boosts and the sort_by_key(Reverse) sort pattern.
  • EvilBit-Labs/Stringy#141: The pipeline architecture and --enc CLI plumbing changes in src/main.rs and filtering components directly intersect with this PR's EncodingFilter::AsciiContent addition and CLI mapping.
  • EvilBit-Labs/Stringy#147: Touches the same CliEncoding → EncodingFilter mapping in src/main.rs that this PR changes to route --enc ascii to AsciiContent.

Poem

🔍 The imports marched in, each wearing a tag,
Crypto, Network, FileIO — no symbol left to lag.
ASCII strings shed their old name with a shrug,
Now labeled as UTF-8, snug as a bug.
extract_symbol_strings — one call to rule them all,
The classifier stands guard so no symbol shall fall. 🦀

🚥 Pre-merge checks | ✅ 9 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning PR title omits required scope; must follow Conventional Commits format with type(scope): description. Update title to: feat(classification): import/export symbol classification pipeline
✅ Passed checks (9 passed)
Check name Status Explanation
Description check ✅ Passed Description is comprehensive and directly related to the changeset, providing clear context for the implementation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 70.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No Unsafe Code ✅ Passed Crate maintains #![forbid(unsafe_code)] in both src/lib.rs and src/main.rs; no unsafe blocks, functions, or allow(unsafe_code) pragmas present in PR changes.
Error Handling ✅ Passed The PR's new ImportClassifier and extract_symbol_strings functions are correctly designed as infallible—they don't need StringyError handling. All operations (string cloning, length calculation...
Ascii Only ✅ Passed Comprehensive scan of all 36 changed files in the PR found zero prohibited Unicode punctuation (no em-dashes, smart quotes, or emojis). Only intentional test data (Chinese characters in integration...
File Size Limit ✅ Passed All new files are well under 500 lines: src/classification/imports.rs (261), tests/classification_symbols.rs (302), tests/integration_cli_coverage.rs (272), docs files (98, 232). No blanket #[allow...
Section Weight Consistency ✅ Passed PR does not modify any container parser files (src/container/*.rs); check is not applicable. Existing container parsers (ELF/PE/Mach-O) maintain consistent weight ranges per specifications (strings...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 20-implement-importexport-symbol-classification-pipeline

Comment @coderabbitai help to get the list of available commands and usage tips.

@mergify

mergify Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?:

🟢 Full CI must pass

Wonderful, this rule succeeded.

All CI checks must pass. This protection prevents manual merges that bypass the merge queue.

  • check-success = coverage
  • check-success = msrv (stable minus 1 releases)
  • check-success = msrv (stable minus 2 releases)
  • check-success = msrv (stable minus 3 releases)
  • check-success = msrv (stable minus 4 releases)
  • check-success = msrv (stable)
  • check-success = quality
  • check-success = test
  • check-success = test-cross-platform (macos-latest, macOS)
  • check-success = test-cross-platform (ubuntu-latest, Linux)
  • check-success = test-cross-platform (windows-latest, Windows)

🟢 Do not merge outdated PRs

Wonderful, this rule succeeded.

Make sure PRs are within 10 commits of the base branch before merging

  • #commits-behind <= 10

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 19, 2026
Commit 2d7d25a removed zig from idiomatic_version_file_enable_tools without
adding it to [tools], so mise stopped reading .zig-version (0.15.2) and CI's
gen-fixtures step failed with 'zig couldn't exec process'. Re-add zig to the
idiomatic list so mise auto-installs the pinned version (exec_auto_install is
on); using the existing .zig-version pin keeps fixture layouts and insta
snapshots reproducible. Pre-existing break on main, unrelated to the feature.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements a symbol-classification emission path that turns parsed imports/exports/section names into tagged FoundStrings, wiring them into extraction so existing ranking boosts (Import/Export and new semantic tags) apply to live output. Also normalizes ASCII extraction output to Encoding::Utf8 and redefines --enc ascii as a content-based filter to keep deduplication stable.

Changes:

  • Added ImportClassifier + extract_symbol_strings and routed symbol emission through the extractor (imports/exports tagged; section names emitted as StringSource::SectionName).
  • Introduced four new semantic tags (Crypto/Network/FileIO/EntryPoint) and added ranking boosts + formatter/CLI/test wiring.
  • Normalized “ASCII extraction” to emit Encoding::Utf8 and updated --enc ascii filtering semantics + tests/snapshots/docs accordingly.

Reviewed changes

Copilot reviewed 38 out of 38 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_deduplication.rs Makes dedup assertion resilient to new section-name rows by comparing against pre-dedup count.
tests/test_ascii_extraction.rs Updates expectations for ASCII-extracted strings now labeled Encoding::Utf8.
tests/snapshots/output_yara_integration__yara_all_tag_types.snap Snapshot updates to include new tag types in YARA output.
tests/snapshots/integration_flows_1_5__flow1_top3_json.snap Snapshot updates for encoding relabel from Ascii -> Utf8 in JSON output.
tests/output_yara_integration.rs Extends “all tag types” fixture to cover new semantic tags.
tests/integration_extraction.rs Updates encoding assertions; strengthens symbol-property assertions (tags, section/rva presence).
tests/integration_cli.rs Ensures CLI --help lists new canonical tag names.
tests/integration_cli_coverage.rs Adds end-to-end test asserting --enc ascii is content-based.
tests/classification_symbols.rs New unit/integration coverage for import/export/section-name symbol classification and fixture runs.
src/types/tests.rs Extends tag parsing tests to cover new tag variants.
src/types/mod.rs Adds new Tag variants and StringSource::SectionName.
src/pipeline/filter.rs Implements EncodingFilter::AsciiContent behavior and unit tests.
src/pipeline/config.rs Adds EncodingFilter::AsciiContent with documentation.
src/output/yara/mod.rs Maps new tags to canonical YARA tag names.
src/output/table/formatting.rs Adds display strings and test coverage for new tags.
src/output/mod.rs Refactors sort comparator to sort_by_key(Reverse(...)) for clippy compliance.
src/output/json.rs Extends tag/source serialization tests for new tags and SectionName.
src/main.rs Maps --enc ascii to AsciiContent; updates tag lists in clap long_help.
src/extraction/tests.rs Updates extraction tests for UTF-8 labeling + symbol/section-name behavior.
src/extraction/dedup/mod.rs Refactors score sorting to sort_by_key(Reverse(...)).
src/extraction/basic_extractor.rs Replaces untagged import/export emission with extract_symbol_strings.
src/extraction/ascii/tests.rs Updates ASCII extractor tests to expect Encoding::Utf8.
src/extraction/ascii/extraction.rs Changes ASCII extraction output encoding from Ascii to Utf8.
src/classification/ranking.rs Adds boosts for new tags and a regression test asserting boost effect; refactors sort.
src/classification/mod.rs Exposes imports module and re-exports ImportClassifier / extract_symbol_strings.
src/classification/imports.rs New classifier implementation for imports/exports/section names.
GOTCHAS.md Documents the --enc ascii contract change and encoding relabeling.
examples/output_formats.rs Refactors sorting to sort_by_key(Reverse(...)).
examples/basic_extraction.rs Refactors sorting to sort_by_key(Reverse(...)).
docs/src/classification.md Updates docs from once_cell to std::sync::LazyLock.
docs/plans/2026-06-19-001-feat-symbol-classification-pipeline-plan.md Adds the implementation plan and rationale for the pipeline integration.
docs/brainstorms/2026-06-19-symbol-classification-pipeline-requirements.md Adds requirements doc (needs alignment with final implemented decisions).
CHANGELOG.md Formatting-only adjustments (blank lines/line wrapping).
AI_POLICY.md Formatting-only adjustments (line wrapping).
AGENTS.md Updates CLI flag notes to document --enc ascii content-based filtering.
.mdformat.toml Removes mdformat exclude list from config (keeps core validation settings).
.claude/settings.json Updates Claude plugin settings metadata under .claude/.

Comment thread src/types/mod.rs Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main.rs`:
- Line 50: The help text for the `--enc` CLI option needs to be updated to
reflect that the `ascii` value now uses content-based filtering (AsciiContent)
rather than strict encoding matching. Locate the help text definition for the
`enc` parameter in src/main.rs and update it to clarify that `ascii` means
filtering based on narrow ASCII content semantics, not exact encoding
validation, so users understand the actual behavior when they use `--enc ascii`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4f1d0548-60be-41fd-b3e8-00a845f07a48

📥 Commits

Reviewing files that changed from the base of the PR and between 7dc0404 and 26e4f79.

⛔ Files ignored due to path filters (2)
  • tests/snapshots/integration_flows_1_5__flow1_top3_json.snap is excluded by !**/*.snap, !**/tests/snapshots/**
  • tests/snapshots/output_yara_integration__yara_all_tag_types.snap is excluded by !**/*.snap, !**/tests/snapshots/**
📒 Files selected for processing (35)
  • .claude/settings.json
  • .mdformat.toml
  • AGENTS.md
  • AI_POLICY.md
  • CHANGELOG.md
  • GOTCHAS.md
  • docs/brainstorms/2026-06-19-symbol-classification-pipeline-requirements.md
  • docs/plans/2026-06-19-001-feat-symbol-classification-pipeline-plan.md
  • docs/src/classification.md
  • examples/basic_extraction.rs
  • examples/output_formats.rs
  • src/classification/imports.rs
  • src/classification/mod.rs
  • src/classification/ranking.rs
  • src/extraction/ascii/extraction.rs
  • src/extraction/ascii/tests.rs
  • src/extraction/basic_extractor.rs
  • src/extraction/dedup/mod.rs
  • src/extraction/tests.rs
  • src/main.rs
  • src/output/json.rs
  • src/output/mod.rs
  • src/output/table/formatting.rs
  • src/output/yara/mod.rs
  • src/pipeline/config.rs
  • src/pipeline/filter.rs
  • src/types/mod.rs
  • src/types/tests.rs
  • tests/classification_symbols.rs
  • tests/integration_cli.rs
  • tests/integration_cli_coverage.rs
  • tests/integration_extraction.rs
  • tests/output_yara_integration.rs
  • tests/test_ascii_extraction.rs
  • tests/test_deduplication.rs
💤 Files with no reviewable changes (1)
  • .mdformat.toml

Comment thread src/main.rs Resolved
…rsion file settings

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Copilot AI review requested due to automatic review settings June 19, 2026 18:17
@coderabbitai coderabbitai Bot added the documentation Improvements or additions to documentation label Jun 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 39 changed files in this pull request and generated 2 comments.

Comment thread src/types/mod.rs Resolved
Comment thread src/classification/imports.rs Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mise.toml`:
- Line 47: Add "zig" to the idiomatic_version_file_enable_tools array (which
currently contains "python", "rust", and "bun") so that mise will honor the
.zig-version file. Additionally, replace the "latest" version value for zig with
a specific pinned version number to ensure reproducible builds and prevent
silent breaks from version-dependent binary layout changes.
- Line 42: The zig tool is configured to use "latest" version on line 42, but
zig is missing from the idiomatic_version_file_enable_tools list on line 47,
causing mise to ignore the existing .zig-version file pinned to 0.15.2. Add zig
to the idiomatic_version_file_enable_tools list (alongside other tools like
python, rust, and bun that follow the same pattern) to enable mise to respect
the .zig-version file and ensure deterministic fixture builds and snapshot
stability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c36a8426-3022-41f0-afbc-05e791745e3d

📥 Commits

Reviewing files that changed from the base of the PR and between f59eece and cd614a2.

⛔ Files ignored due to path filters (1)
  • mise.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • mise.toml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mise.toml`:
- Line 47: Add "zig" to the idiomatic_version_file_enable_tools array (which
currently contains "python", "rust", and "bun") so that mise will honor the
.zig-version file. Additionally, replace the "latest" version value for zig with
a specific pinned version number to ensure reproducible builds and prevent
silent breaks from version-dependent binary layout changes.
- Line 42: The zig tool is configured to use "latest" version on line 42, but
zig is missing from the idiomatic_version_file_enable_tools list on line 47,
causing mise to ignore the existing .zig-version file pinned to 0.15.2. Add zig
to the idiomatic_version_file_enable_tools list (alongside other tools like
python, rust, and bun that follow the same pattern) to enable mise to respect
the .zig-version file and ensure deterministic fixture builds and snapshot
stability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c36a8426-3022-41f0-afbc-05e791745e3d

📥 Commits

Reviewing files that changed from the base of the PR and between f59eece and cd614a2.

⛔ Files ignored due to path filters (1)
  • mise.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • mise.toml
🛑 Comments failed to post (2)
mise.toml (2)

42-42: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if mise.toml exists and view the relevant section
if [ -f mise.toml ]; then
  echo "=== mise.toml content ==="
  cat -n mise.toml
else
  echo "mise.toml not found"
fi

Repository: EvilBit-Labs/Stringy

Length of output: 3434


🏁 Script executed:

# Check for any .mise.toml or .tool-versions files that might contain zig version
find . -maxdepth 2 -type f \( -name ".mise.toml" -o -name ".tool-versions" -o -name "mise.lock" \) 2>/dev/null

Repository: EvilBit-Labs/Stringy

Length of output: 76


🏁 Script executed:

# Search for references to zig version in justfile and related docs
rg -i "zig.*version|gen-fixtures" --type-list | head -20
rg -i "zig.*version|gen-fixtures" -l

Repository: EvilBit-Labs/Stringy

Length of output: 705


🏁 Script executed:

# Check GOTCHAS.md for any zig/reproducibility guidance
if [ -f GOTCHAS.md ]; then
  rg -i "zig|reproducib|snapshot|fixture" GOTCHAS.md -A 2
fi

Repository: EvilBit-Labs/Stringy

Length of output: 1592


🏁 Script executed:

# Check for version files (.zig-version, .python-version, .rust-version, etc.)
find . -maxdepth 1 -type f \( -name ".zig-version" -o -name ".python-version" -o -name ".rust-version" -o -name ".bun-version" \)

Repository: EvilBit-Labs/Stringy

Length of output: 97


🏁 Script executed:

# Check the justfile for gen-fixtures to understand zig usage
rg -A 10 "gen-fixtures" justfile

Repository: EvilBit-Labs/Stringy

Length of output: 2539


🏁 Script executed:

# Check mise.lock to see if zig version is captured there
head -50 mise.lock

Repository: EvilBit-Labs/Stringy

Length of output: 2716


🏁 Script executed:

# Check the .zig-version file
cat .zig-version

Repository: EvilBit-Labs/Stringy

Length of output: 71


🏁 Script executed:

# Check if zig is mentioned elsewhere in documentation about version management
rg -i "zig.*version" docs/ -A 2

Repository: EvilBit-Labs/Stringy

Length of output: 46


Enable zig version file support in mise configuration

Line 42 sets zig = "latest" but line 47 does not include zig in idiomatic_version_file_enable_tools. This causes mise to ignore the existing .zig-version file (pinned to 0.15.2), making fixture builds non-deterministic and breaking snapshot stability. The .zig-version file already exists; add zig to the enable list to honor it.

Suggested fix
-idiomatic_version_file_enable_tools = [ "python", "rust", "bun" ]
+idiomatic_version_file_enable_tools = [ "python", "rust", "bun", "zig" ]

The project already maintains .zig-version = 0.15.2 and documentation confirms that zig version changes break insta snapshots. Enabling idiomatic version file support aligns zig with python, rust, and bun (all set to "latest" with version-file backing).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mise.toml` at line 42, The zig tool is configured to use "latest" version on
line 42, but zig is missing from the idiomatic_version_file_enable_tools list on
line 47, causing mise to ignore the existing .zig-version file pinned to 0.15.2.
Add zig to the idiomatic_version_file_enable_tools list (alongside other tools
like python, rust, and bun that follow the same pattern) to enable mise to
respect the .zig-version file and ensure deterministic fixture builds and
snapshot stability.

Sources: Coding guidelines, Learnings


47-47: ⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "Check current mise zig config:"
rg -n '^\s*zig\s*=|idiomatic_version_file_enable_tools' mise.toml

echo
echo "Search for zig version files / declarations in repo:"
fd -HI '(^\.zig-version$|zig-version|\.tool-versions$|mise\.toml$)'
rg -n '(^|\s)zig(\s|=)' .tool-versions mise.toml 2>/dev/null || true

echo
echo "Cross-check docs expecting pinned zig behavior:"
rg -n 'Zig version|pinned in `mise.toml`|breaking insta snapshots|zig cc'

Repository: EvilBit-Labs/Stringy

Length of output: 2799


Add zig to idiomatic_version_file_enable_tools and pin its version

Zig is critical for cross-compiling test fixtures (ELF, PE, Mach-O), yet it's absent from the idiomatic version-file tool list on line 47. A .zig-version file exists but won't be honored by mise without this entry. Additionally, Zig is currently set to "latest" (line 42), which contradicts the documented requirement that "Zig version is pinned in mise.toml" (tests/fixtures/README.md:137). Unpinned Zig versions can alter compiled binary layouts and silently break insta snapshots.

Add "zig" to the idiomatic_version_file_enable_tools array, and update Zig to a pinned version instead of "latest" to ensure reproducible builds across CI and contributor machines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mise.toml` at line 47, Add "zig" to the idiomatic_version_file_enable_tools
array (which currently contains "python", "rust", and "bun") so that mise will
honor the .zig-version file. Additionally, replace the "latest" version value
for zig with a specific pinned version number to ensure reproducible builds and
prevent silent breaks from version-dependent binary layout changes.

Source: Coding guidelines

zig = latest pulled 0.16.0, which generates a materially different ELF fixture
(81 imports vs 30) than the committed insta snapshots, which were calibrated for
the .zig-version pin (0.15.2). Pin zig to 0.15.2 in [tools] so CI-generated
fixtures match the snapshots and the version is reproducible across zig releases.
Resolves the elf_symbol_extraction snapshot failure.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
CI runs 'mise install --locked', which requires every pinned tool version in
mise.lock. The lockfile still held zig 0.16.0 (from the earlier zig=latest),
so pinning mise.toml to 0.15.2 failed to resolve under --locked. Relock zig to
0.15.2 for all 9 target platforms and prune the stale 0.16.0 entry so the pin
installs cleanly on every CI runner.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Copilot AI review requested due to automatic review settings June 19, 2026 18:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 39 changed files in this pull request and generated 2 comments.

Comment thread src/types/mod.rs Resolved
Comment thread src/pipeline/filter.rs Resolved
- Mark StringSource #[non_exhaustive] (matches Tag) so future variants are not
  semver-breaking for external exhaustive matches (Copilot)
- Clarify --enc help text: ascii is narrow-ASCII content filtering (CodeRabbit)
- Soften imports.rs module doc: demangle adds DemangledSymbol on success;
  names that fail to demangle are left unchanged (Copilot)
- Update origin requirements doc R3/R6/R8 to match the implementation: Mach-O
  __LINKEDIT default, demangle-in-pipeline note, StringSource::SectionName (Copilot)

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/brainstorms/2026-06-19-symbol-classification-pipeline-requirements.md`:
- Around line 38-45: The requirements R1, R5, and R8 describe FoundString
emission for imports, exports, and section names but do not explicitly specify
the encoding format as Encoding::Utf8. Add clarification to the requirements
document by either adding a note to each of R1, R5, and R8 stating that symbol
strings are emitted with Encoding::Utf8, or by creating a new dedicated
requirement (e.g., "Encoding") that documents this constraint applies to all
symbol strings to enable deduplication with byte-scanned occurrences. This
ensures the implementation contract for encoding is clearly documented and
prevents confusion during implementation.
- Line 45: Requirement R8 specifies that section names emit as FoundStrings with
StringSource::SectionName but does not document that section-name rows are
filtered out for BinaryFormat::Unknown format. The synthetic 'raw-bytes' section
used as fallback for Unknown format carries no analytic value and would pollute
output, so this filtering behavior needs to be explicitly documented. Add a
clarification sentence to R8 stating that section-name rows are emitted only for
recognized container formats (BinaryFormat::Elf, BinaryFormat::Pe,
BinaryFormat::MachO) and excluded for BinaryFormat::Unknown, with the rationale
that the Unknown format's synthetic section has no semantic value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6d45ba91-7dad-4a3b-9b2f-6e5e2f904197

📥 Commits

Reviewing files that changed from the base of the PR and between a832026 and ed01698.

⛔ Files ignored due to path filters (1)
  • mise.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • docs/brainstorms/2026-06-19-symbol-classification-pipeline-requirements.md
  • src/classification/imports.rs
  • src/main.rs
  • src/types/mod.rs

Comment on lines +38 to +45
- R1. Import names emit as `FoundString`s with `source: StringSource::ImportName`, always carrying `Tag::Import`, at `confidence: 1.0`.
- R2. Imports whose name matches the crypto, network, or file-I/O API sets additionally carry `Tag::Crypto`, `Tag::Network`, or `Tag::FileIO`; imports that match none carry only `Tag::Import`.
- R3. An import's originating library populates the `section` field when present; when absent, a format-appropriate default is used (`.idata` for PE, `.dynsym` for ELF, `__LINKEDIT` for Mach-O -- Mach-O imports never carry a library, so they always use the default).
- R4. An import's RVA populates from `ImportInfo::address` when present (it is optional).
- R5. Export names emit as `FoundString`s with `source: StringSource::ExportName`, always carrying `Tag::Export`, with RVA from `ExportInfo::address` (always available).
- R6. Exports are run through the existing `SymbolDemangler`; successfully demangled exports additionally carry `Tag::DemangledSymbol`. (Implementation note: demangling runs in the pipeline's `classify_strings` -- under `catch_unwind` and skipped in raw mode -- not in the classifier, so a third-party demangler panic never aborts extraction.)
- R7. Exports whose name is a known entry point (`main`, `_start`, `DllMain`, `WinMain`, `wWinMain`) additionally carry `Tag::EntryPoint`.
- R8. Section names emit as standalone `FoundString`s with `source: StringSource::SectionName` (a dedicated variant, distinct from `SectionData`, so section-name rows are distinguishable from byte-scanned section content) at `confidence: 1.0`, ranked alongside other output.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Clarify symbol encoding in requirements.

Requirements R1–R5 describe FoundString emission for imports, exports, and section names but do not specify Encoding::Utf8 as the emission format. The PR summary states "ASCII extraction now emits Encoding::Utf8 (ASCII is a UTF-8 subset), enabling deduplication," and the context shows all classifier symbol strings are constructed with Encoding::Utf8. This constraint should be explicitly stated in the requirements—either in each requirement (R1, R5, R8) or in a single "Encoding" section—to prevent implementation confusion and document the deduplication contract.

Add a note in each of R1, R5, and R8, or add a new requirement line (e.g., "Symbol strings are emitted with Encoding::Utf8 to enable deduplication with byte-scanned occurrences"), documenting this non-obvious constraint.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 38-38: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/brainstorms/2026-06-19-symbol-classification-pipeline-requirements.md`
around lines 38 - 45, The requirements R1, R5, and R8 describe FoundString
emission for imports, exports, and section names but do not explicitly specify
the encoding format as Encoding::Utf8. Add clarification to the requirements
document by either adding a note to each of R1, R5, and R8 stating that symbol
strings are emitted with Encoding::Utf8, or by creating a new dedicated
requirement (e.g., "Encoding") that documents this constraint applies to all
symbol strings to enable deduplication with byte-scanned occurrences. This
ensures the implementation contract for encoding is clearly documented and
prevents confusion during implementation.

- R5. Export names emit as `FoundString`s with `source: StringSource::ExportName`, always carrying `Tag::Export`, with RVA from `ExportInfo::address` (always available).
- R6. Exports are run through the existing `SymbolDemangler`; successfully demangled exports additionally carry `Tag::DemangledSymbol`. (Implementation note: demangling runs in the pipeline's `classify_strings` -- under `catch_unwind` and skipped in raw mode -- not in the classifier, so a third-party demangler panic never aborts extraction.)
- R7. Exports whose name is a known entry point (`main`, `_start`, `DllMain`, `WinMain`, `wWinMain`) additionally carry `Tag::EntryPoint`.
- R8. Section names emit as standalone `FoundString`s with `source: StringSource::SectionName` (a dedicated variant, distinct from `SectionData`, so section-name rows are distinguishable from byte-scanned section content) at `confidence: 1.0`, ranked alongside other output.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Document section-name filtering for the Unknown format.

R8 specifies that section names emit as StringSource::SectionName ranked strings, but does not mention that section-name rows are filtered out for BinaryFormat::Unknown. The implementation context shows: "The unknown/raw fallback uses a synthetic 'raw-bytes' section that carries no analytic value (and would pollute empty-file output), so skip section-name rows for Unknown format." This filtering rule should be documented in R8 or as a separate clarification to prevent test/output mismatches.

Add a clarification line to R8, e.g.: "Section-name rows are emitted only for recognized container formats (BinaryFormat::Elf, BinaryFormat::Pe, BinaryFormat::MachO); BinaryFormat::Unknown is excluded because its synthetic 'raw-bytes' section carries no semantic value."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/brainstorms/2026-06-19-symbol-classification-pipeline-requirements.md`
at line 45, Requirement R8 specifies that section names emit as FoundStrings
with StringSource::SectionName but does not document that section-name rows are
filtered out for BinaryFormat::Unknown format. The synthetic 'raw-bytes' section
used as fallback for Unknown format carries no analytic value and would pollute
output, so this filtering behavior needs to be explicitly documented. Add a
clarification sentence to R8 stating that section-name rows are emitted only for
recognized container formats (BinaryFormat::Elf, BinaryFormat::Pe,
BinaryFormat::MachO) and excluded for BinaryFormat::Unknown, with the rationale
that the Unknown format's synthetic section has no semantic value.

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.53917% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/classification/imports.rs 99.31% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@mergify

mergify Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Queued — the merge queue status continues in this comment ↓.

@unclesp1d3r

Copy link
Copy Markdown
Member Author

@Mergifyio queue

@mergify

mergify Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 12 minutes 9 seconds in the queue, including 11 minutes 47 seconds running CI.

Required conditions to merge
  • check-success = coverage
  • check-success = msrv (stable minus 1 releases)
  • check-success = msrv (stable minus 2 releases)
  • check-success = msrv (stable minus 3 releases)
  • check-success = msrv (stable minus 4 releases)
  • check-success = msrv (stable)
  • check-success = quality
  • check-success = test
  • check-success = test-cross-platform (macos-latest, macOS)
  • check-success = test-cross-platform (ubuntu-latest, Linux)
  • check-success = test-cross-platform (windows-latest, Windows)
  • all of [🛡 Merge Protections rule Do not merge outdated PRs]:
  • all of [🛡 Merge Protections rule Enforce conventional commit]:
  • all of [🛡 Merge Protections rule Full CI must pass]:
    • check-success = coverage
    • check-success = msrv (stable minus 1 releases)
    • check-success = msrv (stable minus 2 releases)
    • check-success = msrv (stable minus 3 releases)
    • check-success = msrv (stable minus 4 releases)
    • check-success = msrv (stable)
    • check-success = quality
    • check-success = test
    • check-success = test-cross-platform (macos-latest, macOS)
    • check-success = test-cross-platform (ubuntu-latest, Linux)
    • check-success = test-cross-platform (windows-latest, Windows)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-neutral = Mergify Merge Protections
    • check-skipped = Mergify Merge Protections
    • check-success = Mergify Merge Protections

@mergify mergify Bot added the queued label Jun 19, 2026
mergify Bot added a commit that referenced this pull request Jun 19, 2026
@mergify mergify Bot mentioned this pull request Jun 19, 2026
50 of 54 tasks
@mergify
mergify Bot merged commit 62d7769 into main Jun 19, 2026
28 checks passed
@mergify
mergify Bot deleted the 20-implement-importexport-symbol-classification-pipeline branch June 19, 2026 22:00
@mergify mergify Bot removed the queued label Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Import/Export Symbol Classification Pipeline

2 participants