CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.
Go to file
Jeremy Anderson 7992a48d10 CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
scripts CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
src CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
tests CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
BLOG.md CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
Cargo.toml CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
FIX-NOTES-glyphfix.md CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
MANIFEST.md CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
QUICKSTART.md CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
README.md CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
TODO.md CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00
corbel-purge-ss.png CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.' 2026-08-12 16:52:57 -04:00

README.md

CorbelPurge

Strict Rust document sanitizer & threat neutralizer for PDF, EPUB, Markdown, and DOCX.

Author: Jeremy Anderson — https://git.dcos.net/dcosnet/corbel License: GPL-3.0-or-later

Corbel-Purge-ss

CorbelPurge parses documents into a unified intermediate representation (Document struct), runs a layered contextual scanner that distinguishes educational security literature from active malicious injections, and produces cleansed derivatives with all executable content stripped. Malicious payloads are carved into quarantine tarballs with full forensic reports.

What it does

Given a file path, Pipeline::run() in src/core/pipeline.rs:

  1. Detects format via DocumentFormat::from_path() (extension-based: .pdf, .epub, .md/.markdown, .docx).
  2. Parses via parsers::Dispatcher into a Document containing TextNode (static text with semantic context) and ExecutableVector (active content like JS streams, embedded files, script tags, VBA macros) items.
  3. Scans with a two-pass engine:
    • heuristics::inspect_vector() classifies every executable vector against file-signature tables, shellcode patterns, phishing heuristics, and URI allowlists.
    • context_filter::evaluate() checks text nodes for suspicious signatures and determines whether the surrounding context is educational (code blocks, CVE writeups, academic language) or weaponized.
    • cve_tags::match_cve() annotates findings with known exploit IDs.
  4. Quarantines (when malicious findings exist) — quarantine::handle() carves payloads into quarantine_<ts>_<sha>.tar.gz with original.<ext>, report.json, report.md, and one .bin per payload.
  5. Cleanses (when recommended) — produces a sanitized derivative:
    • Markdown mode (default): cleanse::sanitizer::sanitize() emits a safe Markdown file. Text nodes at malicious locations are stripped; hyperlinks lose their destinations.
    • PreserveFormat mode (--preserve-format): cleanse::repackage::repackage() rebuilds the original format with malicious entries removed. EPUB entries are stripped from the ZIP, DOCX macros/embeddings/external-links are removed, PDF objects are deleted via lopdf.

Supported formats: PDF, EPUB, Markdown, DOCX.

Build

# Headless CLI (default — no GUI deps)
cargo build --release

# With iced GUI
cargo build --release --features gui --bin corbel-purge-gui

Requires Rust 1.70+ (stable).

Binaries:

  • target/release/corbel-purge — headless CLI
  • target/release/corbel-purge-gui — iced dashboard GUI (gui feature)

CLI usage

The CLI is in src/main.rs and parses its own arguments (no clap dependency).

# Scan a single file
corbel-purge scan path/to/suspicious.pdf

# Scan with format-preserving output (keeps original format)
corbel-purge scan path/to/book.epub --preserve-format

# Scan with a custom workspace
corbel-purge scan path/to/file.pdf --workspace /tmp/corbel

# Scan a directory recursively
corbel-purge scan-dir path/to/inbox --recursive --workspace /tmp/corbel

# CI gate: exit non-zero on threat
corbel-purge scan path/to/file.pdf --abort-on-threat

# Quiet mode: print only the JSON report path
corbel-purge scan path/to/file.pdf --quiet

# Version
corbel-purge --version

Exit codes

Code Meaning
0 No threats found
1 Hard error (parse failure, IO, etc.)
2 One or more malicious findings

Environment variables

Variable Default Purpose
CORBEL_QUARANTINE_DIR ./corbel_quarantine Quarantine tarball output dir
CORBEL_CLEANSE_DIR ./corbel_clean Cleansed document output dir
CORBEL_ABORT_ON_THREAT false Abort on first malicious finding
CORBEL_EMIT_SUSPICIOUS true Include Suspicious findings in report
CORBEL_EMIT_MARKDOWN_REPORT true Generate Markdown report alongside JSON

Library usage

use corbel_purge::{Pipeline, Config, CleanseMode};

// Scan a file on disk
let pipeline = Pipeline::with_config(Config::with_workspace("/tmp/output"));
let result = pipeline.run("suspicious.pdf")?;
println!("malicious: {}", result.scan_report.malicious_count());

// Scan in-memory bytes (e.g. from an email gateway)
let bytes = std::fs::read("upload.docx")?;
let result = pipeline.run_on_bytes(
    bytes,
    corbel_purge::DocumentFormat::Docx,
    Some("upload.docx".into()),
)?;

// PreserveFormat mode
let mut config = Config::default();
config.cleanse_mode = CleanseMode::PreserveFormat;
let pipeline = Pipeline::with_config(config);
let result = pipeline.run("book.epub")?;

Source layout

src/
├── main.rs                  # CLI: scan, scan-dir, --version, arg parsing
├── lib.rs                   # Crate root, CorbelError enum, public re-exports
├── util.rs                  # sha256_hex(), truncate_with_ellipsis(), read_with_cap()
├── bin/
│   └── gui.rs               # iced 0.13 dashboard GUI (gui feature)
├── core/
│   ├── types.rs             # Document, TextNode, ExecutableVector, Finding, ScanReport,
│   │                         # Location, TextContext, VectorType, ThreatClassification,
│   │                         # MaliciousType, Recommendation, DocumentMetadata
│   ├── config.rs            # Config, CleanseMode, env overrides, workspace dirs
│   └── pipeline.rs          # Pipeline, PipelineResult, DocumentFormat::from_path()
├── parsers/
│   ├── mod.rs                # DocumentParser trait, Dispatcher
│   ├── pdf_parser.rs         # lopdf-based PDF structure walker
│   ├── epub_parser.rs        # ZIP-based EPUB container inspector
│   ├── md_parser.rs          # pulldown-cmark text extractor
│   └── docx_parser.rs        # OOXML ZIP inspector (regex XML extraction)
├── scanner/
│   ├── mod.rs                # scan() entrypoint, CVE tag injection
│   ├── heuristics.rs         # classify_vector() for executable vectors
│   ├── context_filter.rs     # evaluate() for text nodes, educational vs weaponized
│   ├── signatures.rs         # PHISHING_TLDS, KNOWN_FILE_SIGNATURES,
│   │                         # SHELLCODE_PATTERNS, COMMON_PHISHING_BRANDS,
│   │                         # SUSPICIOUS_URL_KEYWORDS, URL_SHORTENER_DOMAINS
│   └── cve_tags.rs           # CVE_TABLE (7 entries), match_cve(), cve_tag()
├── quarantine/
│   ├── mod.rs                # handle() — tarball writer, QuarantineOutcome
│   ├── extractor.rs          # extract_payloads(), ExtractedPayload, filename sanitization
│   └── reporter.rs           # build_json_report(), build_markdown_report()
├── cleanse/
│   ├── mod.rs                # cleanse() dispatcher (Markdown vs PreserveFormat)
│   ├── sanitizer.rs          # sanitize() — Markdown re-serializer, build_clean_markdown()
│   └── repackage.rs          # repackage() — format-preserving (EPUB/DOCX/PDF)
└── ui/
    ├── mod.rs                # GUI module gate (behind `gui` feature)
    ├── viewer.rs              # Stub
    └── alert_modal.rs        # Stub

Testing

# Regenerate test fixtures (requires pypdf, reportlab, python-docx)
pip install pypdf reportlab python-docx
python3 scripts/gen_fixtures.py
python3 scripts/gen_md_epub_fixtures.py
python3 scripts/gen_docx_fixtures.py
python3 scripts/gen_zip_bomb_fixtures.py

# Run all tests
cargo test

Adding a new format

  1. Create src/parsers/<format>_parser.rs implementing DocumentParser.
  2. Add a variant to DocumentFormat in src/core/types.rs (and its Display impl).
  3. Add a match arm in Dispatcher::parse() in src/parsers/mod.rs.
  4. Add an extension check in DocumentFormat::from_path() in src/core/pipeline.rs.
  5. Add repackage support in src/cleanse/repackage.rs (optional).

The scanner, quarantine, and cleanse modules consume only the Document UIR and require no changes for basic format support.

Non-goals

  • No document editing or annotation.
  • No execution of embedded scripting (PDF JS, EPUB scripts, VBA macros).
  • No network access during scanning — all analysis is local.
  • No sandbox hardening of the tool itself.

License

GPL-3.0-or-later. Copyright (c) 2026 Jeremy Anderson. https://git.dcos.net/dcosnet/corbel