uncrx-rs
Install v0.3.0
Uncrx-rs
A Rust library and command-line tool for converting Chrome CRX extension files to ZIP archives that can be easily extracted.
Description
Uncrx-rs provides both a library and a CLI tool to help you convert CRX (Chrome Extension) files to ZIP format. CRX files are Chrome's packaged extension format, which contains a header with metadata and a ZIP archive. This tool extracts the ZIP portion, making it easy to inspect, modify, or extract Chrome extensions.
Features
- 🚀 Library API - Use
uncrx-rsas a library in your Rust projects - 💻 CLI Tool - Command-line interface for quick conversions
- 🎨 TUI Mode - Interactive terminal user interface for browsing and extracting CRX files
- 📦 Multi-platform - Supports macOS, Linux, and Windows
- ✅ Well-tested - Comprehensive test suite
Table of Contents
Installation
As a Library
Add uncrx-rs to your Cargo.toml:
[dependencies] uncrx-rs = "0.3.0"
Or use cargo add:
cargo add uncrx-rs
As a Binary
Using Cargo
cargo install uncrx-rs
Using Homebrew (macOS)
brew install uncrx-rs
Building from Source
git clone https://github.com/iltumio/uncrx-rs.git cd uncrx-rs cargo build --release
The binary will be available at target/release/uncrx.
Usage
CLI Mode
Extract a CRX file to a directory:
uncrx extension.crx
This will extract the extension to out/extension/ by default.
Specify a custom output directory:
uncrx extension.crx -o my-output-dir # or uncrx extension.crx --output-dir my-output-dir
TUI Mode
Launch the interactive terminal user interface:
uncrx
The TUI allows you to:
- Browse directories and find CRX files
- Navigate with arrow keys or
j/k - Extract CRX files by pressing Enter
- Refresh the file list with
r - Quit with
qorEsc
TUI Controls:
↑/↓orj/k: Navigate files and directoriesEnter: Open directory or extract CRX fileR: Refresh file listQorEsc: Quit
Library Usage
use std::fs; use uncrx_rs::uncrx::helpers::parse_crx; fn main() -> Result<(), Box<dyn std::error::Error>> { // Read the CRX file let data = fs::read("extension.crx")?; // Parse the CRX file let extension = parse_crx(&data)?; // Access the extracted ZIP data let zip_data = &extension.zip; // Access metadata println!("Version: {}", extension.version); println!("Public key length: {} bytes", extension.public_key.len()); // Save the ZIP to a file fs::write("extension.zip", zip_data)?; Ok(()) }
The parse_crx function returns a CrxExtension struct containing:
version: The CRX format versionpublic_key: The extension's public keysignature: The extension signature (if present)zip: The ZIP archive data
Contributing
Contributions are welcome! Feel free to open issues and send pull requests. We'll evaluate them together in the comment section.
License
This project is licensed under the MIT License.
Safe extraction and limits
Existing destinations are refused by default. Use uncrx extension.crx --overwrite
to replace one explicitly. The same option works with the TUI: uncrx --overwrite.
Extraction happens in a private staging directory beside the destination. Invalid
archives, CRC errors, cancellation and limit violations leave the old directory
unchanged. Publishing preserves a backup until the new directory is in place and
attempts rollback if the final rename fails. This is not a crash-durable filesystem
transaction: after a power loss or forced termination during publishing, inspect
.uncrx-backup-* in the output directory to recover the previous contents.
The default limits, shared by CLI and TUI, are:
| Option | Default |
|---|---|
--max-input-bytes |
268435456 (256 MiB) |
--max-entry-bytes |
268435456 (256 MiB) |
--max-total-bytes |
1073741824 (1 GiB) |
--max-entries |
10000 |
--timeout-seconds |
120 |
Limits count actual bytes read/written as well as declared ZIP sizes. Timeout and
TUI cancellation are cooperative between reads; they do not interrupt a blocked
filesystem call. Press q or Esc during processing to cancel and quit. Once
publishing starts it completes or rolls back before exit.
Only stored and Deflate ZIP entries are enabled. Unsafe paths, Windows device
names, symlinks, special files and duplicate/conflicting file paths are rejected.
Files use normal creation permissions; archive executable/setuid bits and timestamps
are not restored. Choose an output parent not writable by untrusted users. A
persistent .uncrx.lock file coordinates concurrent extractions into that parent;
it is not an extracted archive entry and should not be removed while the tool runs.
CRX2 and CRX3 headers are limited to 16 MiB. Unknown versions and malformed headers
return errors. Signatures are parsed as metadata, not cryptographically verified.
Successful parsing/extraction does not authenticate the extension or its publisher.
For CRX3, the legacy public_key and signature fields expose the first RSA proof,
or the first ECDSA proof when there is no RSA proof. See the
Chromium wire format.
Library features
Rust 1.89 or newer is required. CLI and TUI remain enabled by default. For a small parser-only dependency without terminal or ZIP extraction dependencies:
[dependencies] uncrx-rs = { version = "0.3.0", default-features = false }
Enable the extract feature for uncrx_rs::extract::{extract_crx_file, extract_zip_to_directory, ExtractionOptions}. For a CLI without the TUI, build
with --no-default-features --features cli.
parse_crx(&[u8]) retains the owned return type. parse_crx_ref(&[u8]) borrows the
ZIP payload and avoids an archive-sized copy:
use uncrx_rs::uncrx::helpers::parse_crx_ref; let data = std::fs::read("extension.crx")?; let parsed = parse_crx_ref(&data)?; std::fs::write("extension.zip", parsed.zip)?; Ok::<(), anyhow::Error>(())
Development checks
cargo fmt --all --check cargo clippy --locked --all-targets --all-features -- -D warnings cargo test --locked --all-features cargo test --locked --no-default-features cargo test --locked --no-default-features --features cli cargo audit --deny warnings cargo build --locked --all-features python3 scripts/test_tui.py target/debug/uncrx # Unix PTY integration checks
CI runs tests on Linux, macOS and Windows, checks Rust 1.89, and audits dependencies
weekly as well as on changes. Dependabot proposes Cargo and GitHub Actions updates.
Release CI is generated with dist 0.33.0; action pins are configured in
workspace.metadata.dist.github-action-commits in Cargo.toml. Update those pins
and run dist generate together when updating release actions. The website uses
Oranda 0.6.5 with a checked-in archive checksum.