rastray 0.1.1

Blazing-fast static analysis CLI for security, dependency, and performance audits.
rastray-0.1.1 is not a library.

rastray

CI Security audit OpenSSF Scorecard License: MIT OR Apache-2.0 MSRV

Blazing-fast static analysis CLI for security, dependency, and performance audits.

rastray is a single-binary, Rust-native command-line scanner that walks a project tree in parallel and runs a registry of pluggable analyzers against it — looking for hard-coded secrets, vulnerable or out-of-date dependencies, and hot-path performance smells. It is designed to be fast enough to run in pre-commit hooks and strict enough to gate CI pipelines.

It is not another lint wrapper. rastray carries its own crawler, its own diagnostic renderer (powered by miette), and emits both human-friendly terminal output and machine-readable JSON from the same engine.


Why rastray?

Most security/dep/perf tools in the polyglot world fall into one of three buckets:

  1. Language-locked (bandit for Python, npm audit for Node, cargo audit for Rust). You end up running four of them in CI.
  2. Heavy SaaS (Snyk, SonarQube). Paid, network-dependent, slow.
  3. Generic linters with plugins. Good signal, but configuration sprawl.

rastray aims to be the fourth option: one offline binary, one config-free invocation, polyglot from day one, and aggressively fast because it is built on ignore::WalkBuilder (the engine that powers ripgrep) plus a tokio runtime for network-bound advisory lookups.


Installation

Prebuilt binaries (recommended)

Each release attaches statically-linked binaries for the common platforms. The shell installer downloads, checksum-verifies, and extracts the right archive for your OS / arch:

Linux / macOS

curl -fsSL https://github.com/balangyaoejuspher/rastray/releases/latest/download/install.sh | sh

Windows (PowerShell)

irm https://github.com/balangyaoejuspher/rastray/releases/latest/download/install.ps1 | iex

Both installers honor RASTRAY_VERSION (e.g. 0.1.0) and RASTRAY_INSTALL_DIR. See install/README.md for details.

From crates.io

cargo install rastray --locked

Prerequisites (for source builds)

  • Rust 1.86.0 or newer (rustup default stable)
  • A working C/C++ toolchain for linking:
    • Windows → Visual Studio Build Tools with the Desktop development with C++ workload (provides link.exe)
    • macOS → Xcode Command Line Tools (xcode-select --install)
    • Linux → build-essential / gcc + pkg-config

From source

git clone https://github.com/balangyaoejuspher/rastray.git
cd rastray
cargo build --release
# Binary lands at ./target/release/rastray

Usage

rastray [OPTIONS] [PATH]

PATH defaults to the current directory.

Common invocations

# Scan the current project, human-friendly output
rastray

# Scan a specific directory, only show medium+ findings
rastray ./services/api --min-severity medium

# Emit JSON for CI ingestion
rastray --json > rastray-report.json

# Force inclusion of hidden files and ignored paths
rastray --hidden --no-ignore

# Limit parallelism (default is num_cpus)
rastray -j 4

# Crank verbosity for debugging the crawler
rastray -vv

Flags

Flag Default Description
PATH . Directory or file to scan.
--min-severity <LEVEL> low Suppress findings below this severity. One of: info, low, medium, high, critical.
--json off Shortcut for --format json.
--format <FMT> inferred human, json, gh-actions, or sarif. Overrides --json when both are set.
-o, --output <FILE> stdout Write json / sarif output to a file instead of stdout. No effect for human/gh-actions.
--no-ignore off Ignore .gitignore, .ignore, and global ignore files.
--hidden off Descend into hidden files and directories.
--follow-links off Follow symlinks during the walk.
-j, --threads <N> auto Worker thread count for the parallel crawler.
--max-depth <N> unlimited Cap directory recursion depth.
--config <FILE> auto Path to a .rastray.toml config file. By default, rastray walks up from the scan path looking for one.
--no-config off Skip config-file discovery and loading.
--fail-on <LEVEL> inherited Exit code 1 if any finding is at or above this severity. One of: info, low, medium, high, critical, never. Defaults to --min-severity. Overrides [scan].fail_on in config.
-v, --verbose off Repeat for more detail (-v, -vv, -vvv).
-q, --quiet off Suppress non-finding output. Mutually exclusive with --verbose.

Configuration file

If a .rastray.toml file exists in the scan directory (or any ancestor), rastray loads it automatically. Use --config to point at a specific file or --no-config to skip loading entirely.

[scan]
fail_on = "high"            # exit non-zero only on findings >= high (default: any)

[scan.ignore]
paths = ["target/**", "dist/**", "vendor/**"]

[rules]
"RSTR-SEC-005" = false                          # disable a rule entirely
"RSTR-PERF-001" = { severity = "low" }          # downgrade a rule's severity
"RSTR-PERF-002" = { enabled = false }           # explicit form

Exit codes

rastray follows the standard CI-friendly convention:

Code Meaning
0 Scan completed; no findings at or above the fail-on threshold.
1 Scan completed; at least one finding at or above the fail-on threshold.
2 Runtime error (I/O failure, malformed input, configuration error).

The fail-on threshold defaults to --min-severity and can be overridden via --fail-on <LEVEL> or [scan].fail_on in .rastray.toml. Use --fail-on never (or fail_on = "never") to always exit 0 regardless of findings — useful for advisory CI runs.

Wire it into CI as:

rastray --min-severity high || exit $?

Architecture

                  ┌────────────┐
                  │   cli.rs   │   clap-derive parser
                  └─────┬──────┘
                        │ Cli
                  ┌─────▼──────┐
                  │ crawler.rs │   ignore::WalkBuilder + mpsc aggregator
                  └─────┬──────┘
                        │ CrawlSummary
                  ┌─────▼──────┐
                  │  modules/  │   Analyzer trait registry
                  │  secrets   │
                  │  deps      │
                  │  perf      │
                  └─────┬──────┘
                        │ Vec<Finding>
                  ┌─────▼──────┐
                  │ reporter.rs│   Human (miette) | JSON (serde)
                  └────────────┘
  • main.rs — orchestrator. Installs the miette hook, parses CLI, runs the crawler, dispatches analyzers, applies severity filtering, renders, returns ExitCode.
  • cli.rsclap derive structs (Cli, Severity, OutputFormat). Handles --json / --format reconciliation.
  • crawler.rs — parallel filesystem walk. Hard-blocks noise dirs (.git, node_modules, target, dist, build, .venv, venv, __pycache__). Classifies each entry as Manifest | Source | Config | Other.
  • reporter.rsFinding, Location, Report. Dual renderer: miette::Diagnostic for humans, serde_json::to_string_pretty for machines. Source spans are read lazily and degrade gracefully on I/O errors.
  • modules/ — analyzer trait + registry. New analyzers implement Analyzer and are appended to default_registry().

Adding a new analyzer

  1. Create src/modules/<name>.rs.
  2. Define a unit struct and implement Analyzer:
    pub struct MyAnalyzer;
    impl MyAnalyzer { pub fn new() -> Self { Self } }
    impl Analyzer for MyAnalyzer {
        fn name(&self) -> &'static str { "my-analyzer" }
        fn analyze(&self, crawl: &CrawlSummary) -> Result<Vec<Finding>, AnalyzerError> {
            Ok(Vec::new())
        }
    }
    
  3. Register it in default_registry() in src/modules/mod.rs.

JSON output schema

{
  "stats": {
    "files_scanned": 0,
    "manifests": 0,
    "source_files": 0,
    "config_files": 0,
    "other_files": 0,
    "crawl_errors": 0,
    "skipped": 0,
  },
  "perf": {
    "walk_ms": 0,
    "analyze_ms": 0,
    "total_ms": 0,
    "bytes_scanned": 0,
  },
  "findings": [
    {
      "code": "RSTR-XXX-000",
      "message": "...",
      "severity": "low|medium|high|critical|info",
      "category": "secret|dependency|performance|crawler|internal",
      "help": "remediation hint or null",
      "location": {
        "file": "relative/path/to/file",
        "line": 0,
        "column": 0,
        "byte_offset": 0,
        "byte_length": 0,
      },
    },
  ],
}

The JSON output is considered stable within a minor version and follows semantic versioning. See CHANGELOG.md for any schema additions.


Continuous integration

A ready-to-copy GitHub Actions workflow is available under examples/github-actions/. It runs rastray on every push and pull request, posts findings as inline annotations (--format gh-actions), and uploads a SARIF report to GitHub Code Scanning (--format sarif --output rastray.sarif).

See examples/github-actions/README.md for setup instructions.

Drop-in .rastray.toml snippets for common adoption patterns (advisory, strict, monorepo) are in examples/config/.


Security

rastray is itself a security-focused tool, so it holds itself to its own standards:

  • No unsafe Rust anywhere in the codebase.
  • No unwrap / expect / panic! in user-facing code paths.
  • TLS via rustls only — no OpenSSL surface area.
  • Minimal default feature flags on tokio and reqwest to keep the dependency graph small.
  • Pinned MSRV (1.86.0).

To report a vulnerability, please do not open a public issue. See SECURITY.md for the disclosure process.


Contributing

rastray is currently source-available but closed to external code contributions while the architecture stabilises. Bug reports, security reports, feature requests, and forks are welcome. See CONTRIBUTING.md for the full policy and the rules that apply to pre-approved pull requests.


License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.