Download the PHP package twarimitswe-aaron/gatekeeper-cdr without Composer
On this page you can find all versions of the php package twarimitswe-aaron/gatekeeper-cdr. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download twarimitswe-aaron/gatekeeper-cdr
More information about twarimitswe-aaron/gatekeeper-cdr
Files in twarimitswe-aaron/gatekeeper-cdr
Package gatekeeper-cdr
Short Description A zero-trust Content Disarm and Reconstruction (CDR) engine for multi-format file sanitisation.
License AGPL-3.0-only
Informations about the package gatekeeper-cdr
Table of Contents
- What is Gatekeeper?
- Why CDR?
- Architecture
- Memory Model
- Typestate Pipeline
- Error Model
- Supported Formats
- Project Structure
- Getting Started
- Prerequisites
- Build
- Run Tests
- Run the CLI Example
- Using Gatekeeper as a Library
- As a Rust Dependency
- API Reference
- FFI Bindings (Planned)
- Roadmap
- Contributing
- License
What is Gatekeeper?
Gatekeeper is a static library that accepts multi-format file byte streams, surgically removes all non-pixel content, and reconstructs an immaculate output binary from the raw colour matrix upward. It is designed to be embedded directly into application source repositories via native FFI bindings โ no infrastructure changes required.
It does not scrub files in place. The entire philosophy is:
Decode to naked pixels. Re-encode from zero. Share nothing with the original.
Why CDR?
A file that "looks" clean to a human viewer can carry:
| Threat Vector | Example |
|---|---|
| Steganographic payloads | Data hidden in JPEG DCT coefficient LSBs |
| Exploit shellcode | Embedded in APP0โAPP15 markers |
| Personal data leakage | EXIF GPS coordinates, device serial numbers |
| Tracking fingerprints | ICC profile unique identifiers |
| Polyglot containers | Executable bytes after the EOI/IEND marker |
| C2 callbacks | URLs encoded inside COM/XMP marker blocks |
Classic AV scanning misses all of these. CDR eliminates the attack surface entirely by making it structurally impossible for the output to contain anything other than colour values.
Architecture
Memory Model
Gatekeeper enforces a strict zero-copy architecture at the format-detection layer:
The sniffer compares magic bytes using direct subslice equality (payload[..2] == JPEG_SOI). No intermediate buffers or Vec are constructed during format detection โ the comparison resolves in a single register-level load.
Typestate Pipeline
Every sanitizer enforces its stage transitions at compile time using Rust's typestate pattern with newtype tuple structs. Calling stages out of order is a compile error, not a runtime panic. Passing raw bytes to a save routine is also a compile error โ only SanitizedOutput is accepted.
Inside the crate, inner values are always extracted via the formal pattern:
Error Model
All errors are defined in src/errors.rs as a single CdrError enum backed by thiserror. No String allocations occur at any error variant โ every branch carries fixed-size typed data.
Dual-Output Contract
Every call to disarm() returns a DisarmResult containing two buffers:
| Input | buffer |
png_buffer |
Rationale |
|---|---|---|---|
| JPEG | JPEG (q85, metadata stripped) | Some(PNG) |
Two distinct representations |
| PNG | PNG (lossless) | None |
buffer IS already the lossless PNG |
| GIF | GIF (extensions stripped) | Some(PNG) |
Two distinct representations |
| WebP | PNG (no Rust WebP encoder) | None |
buffer IS already the lossless PNG |
| PDF (actions stripped) | None |
Not an image | |
| Office | Office/ZIP (active content stripped) | None |
Not an image |
Why is the sanitized file a different size?
Gatekeeper never scrubs bytes in place. Every image passes through:
The output shares zero bytes with the input. Size changes are normal and come from three separate causes:
1. Format change (often much larger โ expected)
| What you compare | Why it grows |
|---|---|
JPEG in โ png_buffer out |
Lossy JPEG discards information; lossless PNG stores every decoded pixel exactly. Typically 2โ5ร larger. This is correct โ use buffer (JPEG) when size matters, png_buffer when you need a mathematically exact pixel guarantee. |
GIF in โ png_buffer out |
GIF is palette-indexed and LZW-compressed; the PNG companion is full RGBA lossless. Usually larger. |
WebP in โ buffer out |
No pure-Rust WebP encoder exists yet, so WebP is decoded to pixels and emitted as PNG. |
2. Lossy generation loss (JPEG / GIF native buffer)
| Path | Behaviour |
|---|---|
JPEG โ JPEG (buffer) |
Fully decoded to RGB, then re-quantized at quality 85. This destroys steganographic DCT payloads. Size vs the original depends on the source quality: a q60 upload may grow; a q95 upload may shrink. Gatekeeper does not copy the original quantization tables โ that would preserve hidden data. |
GIF โ GIF (buffer) |
Re-quantized with NeuQuant into a fresh local palette. Extension/comment blocks are dropped (smaller), but LZW efficiency may differ from the hand-tuned original. |
3. Re-compression of lossless formats (PNG native buffer)
PNG sanitization decodes pixels and writes a new PNG containing only IHDR + PLTE/tRNS (if needed) + IDAT + IEND. All metadata chunks (tEXt, iCCP, eXIf, trailing polyglot bytes) are removed โ which reduces size.
The remaining IDAT size depends on deflate level and PNG filter strategy:
| Setting | Effect |
|---|---|
Compression::Best (zlib level 9) |
Strong deflate โ already used. |
| Adaptive Paeth filtering (per scanline) | Critical for size. Without it, IDAT can be 2โ3ร larger than the source even after metadata is stripped. Gatekeeper enables adaptive Paeth on every PNG encode path. |
PNG outputs may still be slightly larger than files passed through slow offline optimizers (optipng -o7, Zopfli, brute-force filter search). Gatekeeper targets real-time sanitization, not maximum offline compression.
Quick reference: which buffer should I use?
| Goal | Use |
|---|---|
| Smallest image output | buffer (native format: JPEG/GIF/PNG) |
| Exact pixel proof / zero-trust archive | png_buffer when present, or buffer for PNG/WebP inputs |
| Compare size fairly | Compare buffer to the same format as the input, not png_buffer to a JPEG |
Size fields on DisarmResult
To log both outputs: result.buffer.len() and result.png_buffer.as_ref().map(|p| p.len()).
Enterprise Readiness Analysis
What Gatekeeper Already Does Right
| Property | Status | Detail |
|---|---|---|
| Zero-copy format detection | โ | Magic bytes compared via direct slice equality, no allocations |
| Decompression-bomb guards | โ | Geometry + pixel-budget checks fire before any allocation |
| Typestate pipeline | โ | Stage transitions are compile errors, not runtime panics |
| Dual-output (native + PNG) | โ | Single call returns both the native format and a lossless PNG |
No String on error paths |
โ | Every CdrError variant carries typed structured data |
| Async streaming | โ | AsyncImageStream + disarm_bytes_async() via Tokio |
| Input size caps | โ | Hard limits enforced before any decoder work begins |
Known Gaps vs. Enterprise CDR
Commercial CDR products (Glasswall, Votiro, OPSWAT MetaDefender) address the following that Gatekeeper does not yet:
| Gap | Impact | Planned Fix |
|---|---|---|
| Double decode for JPEG dual-output | 2ร CPU cost per JPEG; highest-priority fix | sanitize_jpeg_dual() โ decode once, encode to both JPEG and PNG from the same pixel buffer (Phase 12) |
| 32 MiB hard input cap | Blocks large document workflows | CdrPolicy struct passed into disarm() with configurable limits (Phase 13) |
| No deterministic JPEG output | Encoder version changes produce different bytes for the same input | Use a fixed published quantization table instead of quality parameter |
| No audit receipt | Cannot cryptographically prove a file was sanitized | Return a Blake3 hash of the output buffer alongside the bytes |
| No policy engine | One fixed set of limits for all callers | CdrPolicy { max_bytes, jpeg_quality, allowed_formats, โฆ } |
| WebP output is PNG | Format change surprises callers expecting WebP back | Add libwebp bindings via webp crate for true WebPโWebP |
| GIF nearest-colour quantization | Palette-heavy images shift colours visibly | NeuQuant re-encode (done); Wu/median-cut optional |
Is the Current Approach Production-Ready?
For embedded / edge deployments (IoT gateways, upload proxies, CI artifact scanning) โ yes. The architecture is sound: zero-copy parsing, compile-time stage enforcement, bomb guards, and dual-output with no wasted allocations.
For enterprise SaaS at scale (100k+ files/day), the single highest-impact change is eliminating the double decode for JPEG:
This single change halves CPU cost for all JPEG inputs. Everything else in the roadmap is additive.
Supported Formats
| Format | Detection | Sanitize | Native Output | PNG Output | Status |
|---|---|---|---|---|---|
| JPEG | โ Magic + EOI check | โ zune-jpeg decode | โ JPEG (q85, metadata stripped) | โ Lossless PNG | Complete |
| PNG | โ Magic + IHDR check | โ png crate decode | โ PNG (lossless, adaptive Paeth) | โ (buffer IS the PNG) | Complete |
| GIF | โ Magic check | โ gif crate decode | โ GIF (NeuQuant re-indexed) | โ Lossless RGBA PNG | Complete |
| WebP | โ RIFF+WEBP check | โ image-webp decode | โ PNG (no pure-Rust WebP encoder) | โ (buffer IS the PNG) | Complete |
| Office | โ ZIP Magic check | โ ZIP unwrap + active-content strip | โ ZIP re-encode | โ (not an image) | Complete |
โ
%PDF- check |
โ
lopdf recursive strip |
โ PDF re-encode | โ (not an image) | Complete |
Project Structure
Getting Started
Prerequisites
- Rust 1.85+ (Edition 2024 requires Rust โฅ 1.85)
Build
This produces:
target/debug/libgatekeeper.rlibโ Rust linkable librarytarget/debug/libgatekeeper.soโ Native shared library (cdylib)
For a release (optimised) build:
Run Tests
Expected output:
Run the CLI Example
The examples/disarm_image.rs driver lets you test the full pipeline against any real file:
Sample output:
Using Gatekeeper as a Library
As a Rust Dependency
Add to your Cargo.toml:
Or for a local checkout:
API Reference
gatekeeper::disarm(payload: &[u8]) -> Result<SanitizedOutput, CdrError>
The primary entry point. Detects format, runs the full CDR pipeline, and returns a SanitizedOutput token โ a distinct type that can only be produced by a completed pipeline run.
To enforce that a save function only ever accepts sanitised data:
gatekeeper::sniff_format(payload: &[u8]) -> Result<FileFormat, CdrError>
Identify the format of a byte slice without modifying or decoding it. Useful for routing in larger pipelines.
gatekeeper::sanitizers::jpeg::sanitize_jpeg(input: &[u8]) -> Result<SanitizedOutput, CdrError>
Call the JPEG sanitizer directly, bypassing the format sniffer.
FFI Bindings (Planned)
The cdylib target is already compiled and emits a native shared library (.so / .dll / .dylib).
The sections below show the planned import and usage API for each target language.
These bindings do not exist yet โ they are the design target for Phases 7โ11.
| Language | Bridge / tool | Install package | Status |
|---|---|---|---|
| Node.js | napi-rs |
npm install gatekeeper-cdr |
Phase 7 โ complete |
| Python | PyO3 |
pip install gatekeeper-cdr |
Phase 8 โ complete |
| PHP | ext-php-rs |
composer require gatekeeper/cdr |
Phase 9 โ complete |
| C / C++ | Raw extern "C" |
Link libgatekeeper.so |
Phase 9 โ complete |
| Go | CGo + extern "C" |
go get github.com/Twarimitswe-Aaron/gatekeeper-cdr/bindings/go |
Phase 10 โ complete |
| Java | JNI via jni crate |
Maven / Gradle dependency | Phase 11 โ pending Maven Central |
Node.js (via napi-rs)
Python (via PyO3)
PHP (via ext-php-rs)
C / C++ (Raw FFI)
Go (via CGo)
Roadmap
- [x] Phase 1 โ Cargo manifest, error model, format sniffer
- [x] Phase 2 โ JPEG sanitization pipeline (typestate + zune-jpeg + png)
- [x] Phase 3 โ PNG sanitization pipeline
- [x] Phase 4 โ GIF and WebP support
- [x] Phase 5 โ PDF sanitization (remove embedded JavaScript, OLE streams)
- [x] Phase 6 โ Office format sanitization (DOCX / XLSX / PPTX)
- [x] Phase 7 โ
napi-rsNode.js bindings โ publish to npm - [x] Phase 8 โ
PyO3Python bindings โ publish to PyPI - [x] Phase 9 โ
ext-php-rsPHP bindings + C/C++ raw header โ publish to Packagist - [x] Phase 10 โ CGo Go bindings โ publish Go module to pkg.go.dev
- [x] Phase 11 โ JNI Java bindings โ pending Maven Central publish
- [ ] Phase 12 โ Single-pass JPEG dual-output (
sanitize_jpeg_dual) to eliminate double-decode - [ ] Phase 13 โ
CdrPolicystruct: configurable size limits, quality, format allowlist - [x] Phase 14 โ Async pipeline via Tokio for streaming large files
- [ ] Phase 15 โ WASM target for browser-side CDR
Contributing
Gatekeeper is open-source under AGPLv3 and actively welcomes contributions. Please read the full guide before opening a PR:
๐ CONTRIBUTING.md
Quick summary:
- Fork the repository
- Create a branch โ
git checkout -b feat/png-sanitizer - Write tests โ new code must include unit tests
- Check โ
cargo test && cargo clippy && cargo fmt --check - Open a PR against
mainusing the PR template
For larger changes (new format support, architectural changes), please open an issue first to discuss the approach before writing code.
License
Gatekeeper is licensed under the GNU Affero General Public License v3.0 (AGPLv3).
This means:
- โ You may use, modify, and distribute this code freely
- โ You may use it in commercial applications
- โ ๏ธ If you modify it and run it as a network service, you must publish your modifications under the same license
- โ ๏ธ All derivative works must carry the AGPLv3 license
See LICENSE for the full text.
All versions of gatekeeper-cdr with dependencies
ext-ffi Version *