From 5212a64f945b82b03e095a28fffa162a7ded176d Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Tue, 24 Mar 2026 20:48:40 -0400 Subject: [PATCH 01/11] feat(evaluator): add configuration files for mcpServers and dependencies Signed-off-by: UncleSp1d3r --- .codex/config.toml | 4 ++++ .gemini/settings.json | 12 ++++++++++++ .mcp.json | 12 ++++++++++++ tessl.json | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 .codex/config.toml create mode 100644 .gemini/settings.json create mode 100644 .mcp.json create mode 100644 tessl.json diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 00000000..43343249 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,4 @@ +[mcp_servers.tessl] +type = "stdio" +command = "tessl" +args = [ "mcp", "start" ] diff --git a/.gemini/settings.json b/.gemini/settings.json new file mode 100644 index 00000000..ebfccaac --- /dev/null +++ b/.gemini/settings.json @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "tessl": { + "type": "stdio", + "command": "tessl", + "args": [ + "mcp", + "start" + ] + } + } +} diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 00000000..ebfccaac --- /dev/null +++ b/.mcp.json @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "tessl": { + "type": "stdio", + "command": "tessl", + "args": [ + "mcp", + "start" + ] + } + } +} diff --git a/tessl.json b/tessl.json new file mode 100644 index 00000000..63c7a654 --- /dev/null +++ b/tessl.json @@ -0,0 +1,40 @@ +{ + "name": "stringy", + "mode": "vendored", + "dependencies": { + "actionbook/rust-skills": { + "version": "3ea748280d2fa5680675fe4abe1a5e764f7c021e", + "source": "https://github.com/actionbook/rust-skills", + "include": { + "skills": [ + "coding-guidelines", + "domain-cli", + "m01-ownership", + "m02-resource", + "m03-mutability", + "m04-zero-cost", + "m05-type-driven", + "m06-error-handling", + "m07-concurrency", + "m09-domain", + "m10-performance", + "m11-ecosystem", + "m12-lifecycle", + "m13-domain-error", + "m14-mental-model", + "m15-anti-pattern", + "meta-cognition-parallel", + "rust-call-graph", + "rust-code-navigator", + "rust-deps-visualizer", + "rust-learner", + "rust-refactor-helper", + "rust-skill-creator", + "rust-symbol-analyzer", + "rust-trait-explorer", + "unsafe-checker" + ] + } + } + } +} From 7cbd9fd1463ff0bc53fa66db244b55b9a832282f Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Tue, 24 Mar 2026 23:48:18 -0400 Subject: [PATCH 02/11] feat(evaluator): add debug-level tracing for skipped rules (#172) Add the `log` crate as the project's logging facade and emit `log::debug!` when evaluate_rules skips a rule due to expected evaluation errors (BufferOverrun, InvalidOffset, TypeReadError, IoError). This lets users distinguish "rule didn't match" from "rule failed internally" by enabling RUST_LOG=debug. Also converts library-side eprintln! calls to log macros: - parser/loader.rs: parse failure warnings -> log::warn! - output/mod.rs: confidence validation -> log::warn! (removed #[cfg(debug_assertions)] gate -- zero-cost with no backend) Binary-side eprintln! in main.rs stays as-is (user-facing CLI output). Closes #172 Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: UncleSp1d3r --- Cargo.lock | 1 + Cargo.toml | 2 ++ src/evaluator/engine/mod.rs | 22 ++++++++++++++-------- src/output/mod.rs | 8 ++++---- src/parser/loader.rs | 7 +++---- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2234ebf..4eb8feb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -638,6 +638,7 @@ dependencies = [ "criterion", "ctrlc", "insta", + "log", "memchr", "memmap2", "nix", diff --git a/Cargo.toml b/Cargo.toml index fe5d40cb..0f3c7a04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -150,6 +150,7 @@ clap = { version = "4.6.0", features = ["derive"] } clap-stdin = "0.8.1" clap_complete = "4.5.66" ctrlc = { version = "3.5.2", features = ["termination"] } +log = "0.4" memchr = "2.8.0" memmap2 = "0.9.10" nom = "8.0.0" @@ -158,6 +159,7 @@ serde_json = "1.0.149" thiserror = "2.0.18" [build-dependencies] +log = "0.4" nom = "8.0.0" serde = { version = "1.0.228", features = ["derive"] } thiserror = "2.0.18" diff --git a/src/evaluator/engine/mod.rs b/src/evaluator/engine/mod.rs index ed68ef32..19c76509 100644 --- a/src/evaluator/engine/mod.rs +++ b/src/evaluator/engine/mod.rs @@ -14,6 +14,7 @@ use crate::parser::ast::MagicRule; use crate::{EvaluationConfig, LibmagicError}; use super::{EvaluationContext, RuleMatch, offset, operators, types}; +use log::debug; /// Evaluate a single magic rule against a file buffer /// @@ -190,16 +191,15 @@ pub fn evaluate_rules( let match_data = match evaluate_single_rule(rule, buffer) { Ok(data) => data, Err( - LibmagicError::EvaluationError( + e @ (LibmagicError::EvaluationError( crate::error::EvaluationError::BufferOverrun { .. } | crate::error::EvaluationError::InvalidOffset { .. } | crate::error::EvaluationError::TypeReadError(_), ) - | LibmagicError::IoError(_), + | LibmagicError::IoError(_)), ) => { - // Expected evaluation errors for individual rules -- skip gracefully. - // TODO: emit debug-level trace when a rule is skipped due to error, - // so users can distinguish "rule didn't match" from "rule failed internally". + // Expected evaluation errors for individual rules -- skip gracefully + debug!("Skipping rule '{}': {}", rule.message, e); continue; } Err(e) => { @@ -244,14 +244,20 @@ pub fn evaluate_rules( return Err(e); } Err( - LibmagicError::EvaluationError( + e @ (LibmagicError::EvaluationError( crate::error::EvaluationError::BufferOverrun { .. } | crate::error::EvaluationError::InvalidOffset { .. } | crate::error::EvaluationError::TypeReadError(_), ) - | LibmagicError::IoError(_), + | LibmagicError::IoError(_)), ) => { - // Expected child evaluation errors -- skip gracefully + // Expected child evaluation errors -- skip gracefully. + // Individual child failures are logged by the recursive evaluate_rules call; + // this arm is defensive for errors that propagate from the batch. + debug!( + "Skipping child evaluation under rule '{}': {}", + rule.message, e + ); } Err(e) => { // Unexpected errors in children should propagate diff --git a/src/output/mod.rs b/src/output/mod.rs index c0889982..d4e99de2 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -17,6 +17,8 @@ use std::path::PathBuf; use std::sync::LazyLock; +use log::warn; + use crate::parser::ast::Value; /// Shared `TagExtractor` instance, initialized once on first use. @@ -523,19 +525,17 @@ impl EvaluationResult { /// assert_eq!(result.matches.len(), 1); /// ``` pub fn add_match(&mut self, match_result: MatchResult) { - #[cfg(debug_assertions)] Self::validate_match_result(&match_result); self.matches.push(match_result); } /// Validate a match result before adding it - #[cfg(debug_assertions)] fn validate_match_result(match_result: &MatchResult) { // Validate confidence score range if match_result.confidence > 100 { - eprintln!( - "Warning: Match result has confidence score > 100: {}", + warn!( + "Match result has confidence score > 100: {}", match_result.confidence ); } diff --git a/src/parser/loader.rs b/src/parser/loader.rs index c62f575a..2be1a64d 100644 --- a/src/parser/loader.rs +++ b/src/parser/loader.rs @@ -6,6 +6,8 @@ //! Provides functions for loading magic rules from individual files and //! directories, with automatic format detection and error handling. +use log::warn; + use crate::error::ParseError; use crate::parser::ast::MagicRule; use std::path::{Path, PathBuf}; @@ -79,7 +81,6 @@ use super::format::{MagicFileFormat, detect_format}; /// # Panics /// /// This function does not panic under normal operation. -#[allow(clippy::print_stderr)] pub fn load_magic_directory(dir_path: &Path) -> Result, ParseError> { use std::fs; @@ -176,10 +177,8 @@ pub fn load_magic_directory(dir_path: &Path) -> Result, ParseErro } // Log warnings for partial failures (some files parsed, some failed) - // Note: Using eprintln for now; consider a logging framework in the future - #[allow(clippy::print_stderr)] for (path, e) in &parse_failures { - eprintln!("Warning: Failed to parse '{}': {}", path.display(), e); + warn!("Failed to parse '{}': {}", path.display(), e); } Ok(all_rules) From 7ec4646d6139d93c23eaf52ff6aaf9759e183f97 Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Tue, 24 Mar 2026 23:48:29 -0400 Subject: [PATCH 03/11] ci(mergify): switch to merge queue with @mergifyio queue for human PRs Adopt the queue-based Mergify style from stringy/opnDossier: - dosubot: autoqueue, quality check only - dependabot: autoqueue, full CI required - release-plz: autoqueue, DCO only (exempt from full CI) - Human PRs: manually enqueued via @mergifyio queue command - Add conventional commit enforcement (skip for bots) - Raise outdated PR limit from 3 to 10 commits behind Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: UncleSp1d3r --- .mergify.yml | 104 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 34 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index 68d0da21..fefe51af 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -1,12 +1,24 @@ -pull_request_rules: - - name: Auto-approve and merge dependabot PRs - conditions: +queue_rules: + # Dosubot: only needs quality (fmt + clippy) to pass + - name: dosubot + merge_method: squash + autoqueue: true + queue_conditions: - base = main - - author = dependabot[bot] - - -draft - label != do-not-merge - - -files~=\.github/workflows/release\.yml - - check-success = DCO + - author = dosubot[bot] + merge_conditions: + - check-success = quality + + # Dependabot: full CI required + - name: dependabot + merge_method: squash + autoqueue: true + queue_conditions: + - base = main + - label != do-not-merge + - author = dependabot[bot] + merge_conditions: - check-success = quality - check-success = test - check-success = test-cross-platform (ubuntu-latest, Linux) @@ -14,19 +26,27 @@ pull_request_rules: - check-success = test-cross-platform (macos-latest, macOS) - check-success = test-cross-platform (windows-latest, Windows) - check-success = coverage - actions: - review: - type: APPROVE - message: Automatically approved by Mergify - merge: - method: squash - - name: Auto-approve and merge dosubot PRs - conditions: + + # Release-plz: DCO only (exempt from full CI -- code already tested on main) + - name: release-plz + merge_method: squash + autoqueue: true + queue_conditions: - base = main - - author = dosubot[bot] - - -draft - label != do-not-merge + - head ~= ^release-plz- + merge_conditions: - check-success = DCO + + # Human PRs: manually enqueued via @mergifyio queue command + - name: default + merge_method: squash + queue_conditions: + - base = main + - author != dependabot[bot] + - author != dosubot[bot] + - -head ~= ^release-plz- + merge_conditions: - check-success = quality - check-success = test - check-success = test-cross-platform (ubuntu-latest, Linux) @@ -34,36 +54,51 @@ pull_request_rules: - check-success = test-cross-platform (macos-latest, macOS) - check-success = test-cross-platform (windows-latest, Windows) - check-success = coverage + +pull_request_rules: + - name: Auto-approve dosubot PRs + conditions: + - base = main + - author = dosubot[bot] actions: review: type: APPROVE message: Automatically approved by Mergify - merge: - method: squash - - name: Auto-merge release-plz PRs + + - name: Auto-approve dependabot PRs conditions: - base = main - - head ~= ^release-plz- - - -draft - - label != do-not-merge - - check-success = DCO + - author = dependabot[bot] actions: - merge: - method: squash - - name: Keep bot PRs up to date with main + review: + type: APPROVE + message: Automatically approved by Mergify + + - name: Keep PRs up to date with main conditions: - base = main - -conflict - -draft - - or: - - author = dosubot[bot] - - head ~= ^release-plz- actions: update: {} + merge_protections: - - name: CI must pass - description: All CI checks must pass. Release-plz PRs are exempt because they - only bump versions and changelogs (code was already tested on main), and + - name: Enforce conventional commit + description: >- + Require conventional commit format per https://www.conventionalcommits.org/en/v1.0.0/. + Skipped for bots. + if: + - base = main + - author != dependabot[bot] + - author != dosubot[bot] + - -head ~= ^release-plz- + success_conditions: + - "title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\\(.+\\))?!?:" + + - name: Full CI must pass + description: >- + All CI checks must pass. Release-plz PRs are exempt because they only + bump versions and changelogs (code was already tested on main), and GITHUB_TOKEN-triggered force-pushes suppress CI. if: - base = main @@ -76,9 +111,10 @@ merge_protections: - check-success = test-cross-platform (macos-latest, macOS) - check-success = test-cross-platform (windows-latest, Windows) - check-success = coverage + - name: Do not merge outdated PRs description: Make sure PRs are within 10 commits of the base branch before merging if: - base = main success_conditions: - - "#commits-behind <= 3" + - "#commits-behind <= 10" From cc787ddccf2146fd41ccb602288786dbc3e86265 Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Tue, 24 Mar 2026 23:57:03 -0400 Subject: [PATCH 04/11] chore(ci): update mise-action and download-artifact versions in workflows Signed-off-by: UncleSp1d3r --- .github/workflows/benchmarks.yml | 4 +- .github/workflows/ci.yml | 10 +- .github/workflows/compatibility.yml | 2 +- .github/workflows/copilot-setup-steps.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/release-plz.yml | 96 ++++++++--------- .github/workflows/release.yml | 16 +-- .github/workflows/scorecard.yml | 122 +++++++++++----------- dist-workspace.toml | 4 +- 9 files changed, 129 insertions(+), 129 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index dc7c1996..78b91969 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -27,14 +27,14 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true github_token: ${{ secrets.GITHUB_TOKEN }} - name: Cache cargo registry - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: | ~/.cargo/registry diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e302ce7c..60198a78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true @@ -70,7 +70,7 @@ jobs: needs: test steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true @@ -84,7 +84,7 @@ jobs: needs: test steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true @@ -99,7 +99,7 @@ jobs: run: cargo llvm-cov report --lcov --output-path lcov.info - name: Upload to Codecov - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + uses: codecov/codecov-action@4481f553995cc5011b158ce191746ac1a1d0f815 # v5.5.3 with: files: lcov.info fail_ci_if_error: false diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index 967ba071..fcff20af 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 418cc856..12a4e5ea 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c127d7a4..bef27839 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 with: install: true cache: true diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index cc2376ff..96639100 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -1,54 +1,54 @@ name: release-plz on: - push: - branches: [main] + push: + branches: [main] jobs: - release-plz-release: - name: Release-plz release - runs-on: ubuntu-latest - permissions: - contents: write - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - # persist-credentials required for pushing signed tags via git CLI - persist-credentials: true - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 - with: - install: true - - name: Run release-plz - uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5 - with: - command: release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + release-plz-release: + name: Release-plz release + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + # persist-credentials required for pushing signed tags via git CLI + persist-credentials: true + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 + with: + install: true + - name: Run release-plz + uses: release-plz/action@49c995b2442b0f60dd4aab07a164cc00643471fc # v0.5.128 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - release-plz-pr: - name: Release-plz PR - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - concurrency: - group: release-plz-${{ github.ref }} - cancel-in-progress: false - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - persist-credentials: false - - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 - with: - install: true - - name: Run release-plz - uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5 - with: - command: release-pr - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + release-plz-pr: + name: Release-plz PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + concurrency: + group: release-plz-${{ github.ref }} + cancel-in-progress: false + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 + with: + install: true + - name: Run release-plz + uses: release-plz/action@49c995b2442b0f60dd4aab07a164cc00643471fc # v0.5.128 + with: + command: release-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index be250762..cd56279d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -135,7 +135,7 @@ jobs: run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: artifacts-* path: target/distrib/ @@ -151,7 +151,7 @@ jobs: dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - name: Attest - uses: actions/attest-build-provenance@v4 + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 with: subject-path: "target/distrib/*${{ join(matrix.targets, ', ') }}*" - id: cargo-dist @@ -190,7 +190,7 @@ jobs: persist-credentials: false submodules: recursive - name: Install cached dist - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: cargo-dist-cache path: ~/.cargo/bin/ @@ -202,7 +202,7 @@ jobs: shell: bash # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: artifacts-* path: target/distrib/ @@ -259,14 +259,14 @@ jobs: persist-credentials: false submodules: recursive - name: Install cached dist - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: artifacts-* path: target/distrib/ @@ -286,7 +286,7 @@ jobs: path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: artifacts-* path: artifacts @@ -326,7 +326,7 @@ jobs: token: ${{ secrets.HOMEBREW_TAP_TOKEN }} # So we have access to the formula - name: Fetch homebrew formulae - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: artifacts-* path: Formula/ diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index d1f3469b..80b1ce31 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -4,75 +4,75 @@ name: Scorecard supply-chain security on: - # For Branch-Protection check. Only the default branch is supported. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection - branch_protection_rule: - # To guarantee Maintained check is occasionally updated. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained - schedule: - - cron: '36 23 * * 3' - push: - branches: [ "main" ] + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: "36 23 * * 3" + push: + branches: ["main"] # Declare default permissions as read-only. permissions: read-all jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-latest - # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. - if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' - permissions: - # Needed to upload the results to the code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - # Uncomment the permissions below if installing in a private repository. - # contents: read - # actions: read + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. + if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' + permissions: + # Needed to upload the results to the code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read - steps: - - name: "Checkout code" - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false + steps: + - name: "Checkout code" + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - - name: "Run analysis" - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 - with: - results_file: results.sarif - results_format: sarif - # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: - # - you want to enable the Branch-Protection check on a *public* repository, or - # - you are installing Scorecard on a *private* repository - # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. - # repo_token: ${{ secrets.SCORECARD_TOKEN }} + - name: "Run analysis" + uses: ossf/scorecard-action@99c09fe975337306107572b4fdf4db224cf8e2f2 # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} - # Public repositories: - # - Publish results to OpenSSF REST API for easy access by consumers - # - Allows the repository to include the Scorecard badge. - # - See https://github.com/ossf/scorecard-action#publishing-results. - # For private repositories: - # - `publish_results` will always be set to `false`, regardless - # of the value entered here. - publish_results: true + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true - # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore - # file_mode: git + # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore + # file_mode: git - # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF - # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: SARIF file - path: results.sarif - retention-days: 5 + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 - # Upload the results to GitHub's code scanning dashboard (optional). - # Commenting out will disable upload of results to your repo's Code Scanning dashboard - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6.4.32.62.4.32.6 - with: - sarif_file: results.sarif + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6.4.32.62.4.32.6 + with: + sarif_file: results.sarif diff --git a/dist-workspace.toml b/dist-workspace.toml index c778bce3..7f91241e 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -51,6 +51,6 @@ publish-jobs = [ "homebrew" ] # GitHub release configuration [dist.github-action-commits] "actions/checkout" = "v6.0.2" -"actions/download-artifact" = "v8" -"actions/attest-build-provenance" = "v4" +"actions/download-artifact" = "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" +"actions/attest-build-provenance" = "a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32" "actions/upload-artifact" = "v7.0.0" From a626b47b91bdf3a1abf6ca482a13fd10e33de94d Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Wed, 25 Mar 2026 00:00:55 -0400 Subject: [PATCH 05/11] fix(evaluator): improve comment accuracy for child skip arm and loader docs Address code review findings: - Reword child rule skip comment to accurately reflect that the arm is defensive/unreachable under current implementation - Update loader.rs rustdoc from "logged to stderr" to "logged at warn level" to match the eprintln! -> log::warn! conversion Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: UncleSp1d3r --- src/evaluator/engine/mod.rs | 7 ++++--- src/parser/loader.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/evaluator/engine/mod.rs b/src/evaluator/engine/mod.rs index 19c76509..959fec54 100644 --- a/src/evaluator/engine/mod.rs +++ b/src/evaluator/engine/mod.rs @@ -251,9 +251,10 @@ pub fn evaluate_rules( ) | LibmagicError::IoError(_)), ) => { - // Expected child evaluation errors -- skip gracefully. - // Individual child failures are logged by the recursive evaluate_rules call; - // this arm is defensive for errors that propagate from the batch. + // Defensive: under the current implementation, individual child + // failures are caught and logged inside the recursive evaluate_rules + // call (they never propagate here). This arm guards against future + // changes that might alter that error-handling strategy. debug!( "Skipping child evaluation under rule '{}': {}", rule.message, e diff --git a/src/parser/loader.rs b/src/parser/loader.rs index 2be1a64d..573dcde7 100644 --- a/src/parser/loader.rs +++ b/src/parser/loader.rs @@ -29,7 +29,7 @@ use super::format::{MagicFileFormat, detect_format}; /// and propagates the error to the caller. /// /// - **Non-critical errors** (individual file parse failures): -/// These are logged to stderr with a warning message and the file is skipped. Processing +/// These are logged at warn level and the file is skipped. Processing /// continues with remaining files. /// /// # Behavior From c83a578647f5cc86066f8f0da3ff6324a94e7c04 Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Wed, 25 Mar 2026 00:07:42 -0400 Subject: [PATCH 06/11] fix(mergify): add author guard for release-plz and do-not-merge for default queue Address CodeRabbit review findings: - Add `author = release-plz[bot]` to release-plz queue to prevent fork-based branch name spoofing - Add `label != do-not-merge` to default queue for consistency with all other queue rules Signed-off-by: UncleSp1d3r --- .mergify.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mergify.yml b/.mergify.yml index fefe51af..d90f6013 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -34,6 +34,7 @@ queue_rules: queue_conditions: - base = main - label != do-not-merge + - author = release-plz[bot] - head ~= ^release-plz- merge_conditions: - check-success = DCO @@ -43,6 +44,7 @@ queue_rules: merge_method: squash queue_conditions: - base = main + - label != do-not-merge - author != dependabot[bot] - author != dosubot[bot] - -head ~= ^release-plz- From a6a6b7614465356e996c91361f92a8b4ee23aa4e Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Wed, 25 Mar 2026 00:12:11 -0400 Subject: [PATCH 07/11] fix(evaluator): narrow TypeReadError skip to exclude UnsupportedType Replace the TypeReadError(_) wildcard in both rule-skip match arms with explicit data-dependent variants (BufferOverrun, InvalidPStringLength). This ensures TypeReadError::UnsupportedType propagates as an error instead of being silently skipped, surfacing evaluator capability gaps. Signed-off-by: UncleSp1d3r --- src/evaluator/engine/mod.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/evaluator/engine/mod.rs b/src/evaluator/engine/mod.rs index 959fec54..ac08c98c 100644 --- a/src/evaluator/engine/mod.rs +++ b/src/evaluator/engine/mod.rs @@ -194,11 +194,16 @@ pub fn evaluate_rules( e @ (LibmagicError::EvaluationError( crate::error::EvaluationError::BufferOverrun { .. } | crate::error::EvaluationError::InvalidOffset { .. } - | crate::error::EvaluationError::TypeReadError(_), + | crate::error::EvaluationError::TypeReadError( + crate::evaluator::types::TypeReadError::BufferOverrun { .. } + | crate::evaluator::types::TypeReadError::InvalidPStringLength { .. }, + ), ) | LibmagicError::IoError(_)), ) => { - // Expected evaluation errors for individual rules -- skip gracefully + // Expected data-dependent evaluation errors -- skip gracefully. + // TypeReadError::UnsupportedType is intentionally NOT caught here + // so that evaluator capability gaps propagate as errors. debug!("Skipping rule '{}': {}", rule.message, e); continue; } @@ -247,7 +252,12 @@ pub fn evaluate_rules( e @ (LibmagicError::EvaluationError( crate::error::EvaluationError::BufferOverrun { .. } | crate::error::EvaluationError::InvalidOffset { .. } - | crate::error::EvaluationError::TypeReadError(_), + | crate::error::EvaluationError::TypeReadError( + crate::evaluator::types::TypeReadError::BufferOverrun { .. } + | crate::evaluator::types::TypeReadError::InvalidPStringLength { + .. + }, + ), ) | LibmagicError::IoError(_)), ) => { From 73b3019a6dc02369729d71381878a768eb80011f Mon Sep 17 00:00:00 2001 From: "dosubot[bot]" <131922026+dosubot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 04:13:06 +0000 Subject: [PATCH 08/11] docs: Dosu updates for PR #184 --- docs/src/development.md | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/docs/src/development.md b/docs/src/development.md index 0dc84fcb..f65e0fa9 100644 --- a/docs/src/development.md +++ b/docs/src/development.md @@ -411,28 +411,48 @@ For full details on code review criteria, DCO requirements, and project governan ### Logging -Use the `log` crate for debugging: +The project uses `log = "0.4"` as its logging facade. Logging is used throughout the codebase to provide diagnostic information during development and troubleshooting. -```rust -use log::{debug, error, info, warn}; +**Logging Conventions:** -pub fn parse_rule(input: &str) -> Result { - debug!("Parsing rule: {}", input); +- `log::debug!()` for diagnostic information (such as skipped rule evaluations in `src/evaluator/engine/mod.rs`) +- `log::warn!()` for warnings (parse failures in `src/parser/loader.rs`, confidence validation issues in `src/output/mod.rs`) - let result = do_parsing(input)?; +Example usage: - info!("Successfully parsed rule: {}", result.message); - Ok(result) +```rust +use log::{debug, warn}; + +pub fn evaluate_rules(rules: &[MagicRule], buffer: &[u8]) -> Result> { + for rule in rules { + match evaluate_single_rule(rule, buffer) { + Ok(data) => { /* ... */ } + Err(e) => { + // Expected evaluation errors -- skip gracefully + debug!("Skipping rule '{}': {}", rule.message, e); + continue; + } + } + } + // ... } ``` -Run with logging: +Enable logging during development: ```bash +# See debug output during testing RUST_LOG=debug cargo test -RUST_LOG=libmagic_rs=trace cargo run + +# See debug output when running the CLI +RUST_LOG=debug cargo run -- --use-builtin file.bin + +# Filter to specific module +RUST_LOG=libmagic_rs::evaluator=debug cargo run ``` +Debug-level logging is particularly useful for understanding rule evaluation behavior in `src/evaluator/engine/mod.rs`, where it shows which rules are skipped due to buffer overruns, invalid offsets, or type read errors. + ### Debugging Tests ```bash From eeeae3ad36167994038f3caf402e9afd82570f3b Mon Sep 17 00:00:00 2001 From: "dosubot[bot]" <131922026+dosubot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 04:13:58 +0000 Subject: [PATCH 09/11] docs: Dosu updates for PR #184 --- docs/src/getting-started.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/src/getting-started.md b/docs/src/getting-started.md index f8fa4474..edd50360 100644 --- a/docs/src/getting-started.md +++ b/docs/src/getting-started.md @@ -6,7 +6,7 @@ This guide will help you get up and running with libmagic-rs, whether you want t ### Prerequisites -- **Rust 1.89+** (2024 edition) +- **Rust 1.91+** (2024 edition) - **Git** for cloning the repository - **Cargo** (comes with Rust) @@ -65,6 +65,20 @@ echo -ne '\x7fELF' | ./target/release/rmagic --use-builtin - ./target/release/rmagic --help ``` +### Troubleshooting Detection Issues + +Enable debug logging to see when rules are skipped due to evaluation errors (buffer overruns, invalid offsets, type read failures) versus simply not matching: + +```bash +# CLI: Enable debug logs for a single invocation +RUST_LOG=debug cargo run -- --use-builtin example.bin + +# Or with the compiled binary +RUST_LOG=debug ./target/release/rmagic --use-builtin example.bin +``` + +Debug output shows which rules the evaluator skips and why, helping diagnose unexpected file detection behavior. + ### Library Usage Add libmagic-rs to your `Cargo.toml`: From d35b4811b1bc0a2723f59128abff676bc85a7557 Mon Sep 17 00:00:00 2001 From: "dosubot[bot]" <131922026+dosubot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 04:15:25 +0000 Subject: [PATCH 10/11] docs: Dosu updates for PR #184 --- docs/src/troubleshooting.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/src/troubleshooting.md b/docs/src/troubleshooting.md index 1b843d8c..eb8afc4a 100644 --- a/docs/src/troubleshooting.md +++ b/docs/src/troubleshooting.md @@ -238,6 +238,27 @@ RUST_LOG=debug cargo run -- example.bin RUST_LOG=libmagic_rs=trace cargo test ``` +### Diagnosing Rule Evaluation Issues + +**Problem**: File types are not detected correctly, or you want to understand why certain rules match or fail + +**Solution**: Enable debug logging to see detailed information about rule evaluation + +```bash +# CLI tool with builtin rules +RUST_LOG=debug rmagic --use-builtin example.bin + +# CLI tool with custom magic file +RUST_LOG=debug rmagic --magic custom.magic example.bin +``` + +Debug logging shows which rules are skipped during evaluation and why. This helps distinguish between: + +- **Rule didn't match**: Normal behavior where the rule's test condition evaluated to false +- **Rule failed internally**: Evaluation errors like `BufferOverrun`, `InvalidOffset`, `TypeReadError`, or `IoError` that cause the rule to be skipped + +For library usage, initialize a logger that respects the `RUST_LOG` environment variable (such as `env_logger`), then run your program with `RUST_LOG=debug` to see the same diagnostic information. + ### Use Debug Output ```rust From 345f42d53d62461a34ec5757cfeeb81f4c214a27 Mon Sep 17 00:00:00 2001 From: UncleSp1d3r Date: Wed, 25 Mar 2026 00:23:39 -0400 Subject: [PATCH 11/11] fix(ci): correct malformed version comment in scorecard workflow The codeql-action version comment was corrupted with concatenated version strings (v4.32.6.4.32.62.4.32.6). The SHA is correct for v4.32.6 -- only the comment needed fixing. Signed-off-by: UncleSp1d3r --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 80b1ce31..3fc661b2 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -73,6 +73,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6.4.32.62.4.32.6 + uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: sarif_file: results.sarif