PHPackages                             hassan-lateef/guardian - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. hassan-lateef/guardian

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

hassan-lateef/guardian
======================

A Laravel package that provides a deep file security validation layer before files are stored — detecting malicious code, double extensions, MIME spoofing, and embedded payloads.

1.12.20(2w ago)03↓100%MITPHPPHP ^8.1

Since Mar 6Pushed 2w agoCompare

[ Source](https://github.com/hassan402-paymentrequired/guardian)[ Packagist](https://packagist.org/packages/hassan-lateef/guardian)[ RSS](/packages/hassan-lateef-guardian/feed)WikiDiscussions main Synced today

READMEChangelog (3)Dependencies (3)Versions (4)Used By (0)

Guardian
========

[](#guardian)

Guardian is a Laravel package for validating uploaded files before you store them.

It is designed to catch common upload abuse cases such as:

- blocked executable extensions like `.php`, `.sh`, `.exe`
- double extensions like `avatar.php.png`
- MIME spoofing where the file contents do not match the extension
- suspicious payloads embedded inside text files, images, PDFs, or SVGs
- malformed or fake image/PDF/ZIP-based documents
- dangerous SVG elements, attributes, and `javascript:` links

What Guardian Currently Does
----------------------------

[](#what-guardian-currently-does)

The active scan pipeline in the package is:

1. `ExtensionScanner`
2. `MimeScanner`
3. `ContentScanner`
4. `SvgScanner`
5. `StructuralScanner`

Guardian fails fast. The first scanner that rejects the file stops the pipeline.

Installation
------------

[](#installation)

```
composer require hassan-lateef/guardian
```

Publish the config:

```
php artisan vendor:publish --tag=guardian-config
```

Usage
-----

[](#usage)

### Middleware

[](#middleware)

Guardian registers the `guardian` middleware alias automatically through the package service provider.

```
use Illuminate\Support\Facades\Route;

Route::post('/upload', UploadController::class)->middleware('guardian');

Route::middleware('guardian')->group(function () {
    Route::post('/avatar', [ProfileController::class, 'updateAvatar']);
    Route::post('/document', [DocumentController::class, 'store']);
});
```

The middleware scans every uploaded file in the request, including nested file arrays.

### Validation Rule

[](#validation-rule)

```
use Hassan\Guardian\Rules\GuardianRule;

$request->validate([
    'avatar' => ['required', 'file', new GuardianRule()],
    'document' => ['required', 'file', new GuardianRule()],
]);
```

You can also use the facade helper:

```
use Guardian;

$request->validate([
    'avatar' => ['required', 'file', Guardian::rule()],
]);
```

### Manual Inspection

[](#manual-inspection)

```
use Guardian;
use Hassan\Guardian\Exceptions\MaliciousFileException;

public function store(Request $request)
{
    // Throws MaliciousFileException if rejected
    Guardian::inspect($request->file('upload'));
}
```

If you want a result object instead of an exception:

```
use Guardian;

public function store(Request $request)
{
    $result = Guardian::check($request->file('upload'));

    if (! $result->passed) {
        return back()->withErrors([
            'upload' => $result->reason,
        ]);
    }
}
```

### Multiple Files

[](#multiple-files)

```
Guardian::inspectMany($request->file('documents'));
```

Scanner Details
---------------

[](#scanner-details)

### 1. Extension Scanner

[](#1-extension-scanner)

Checks the original filename and:

- rejects files with no extension
- rejects any extension in `guardian.blocked_extensions`
- rejects multi-part extensions when `guardian.reject_double_extensions` is `true`

Example rejected names:

- `shell.php`
- `avatar.php.png`
- `backup.tar.php`

### 2. MIME Scanner

[](#2-mime-scanner)

Uses PHP `fileinfo` to detect the file's real MIME type from the file bytes, then verifies:

- the MIME exists in `guardian.allowed_mimes`
- the uploaded extension matches the configured extensions for that MIME

This catches renamed files whose contents do not match their extension.

### 3. Content Scanner

[](#3-content-scanner)

Reads up to the first `1 MB` of the file and scans based on extension group:

- `full`: runs all configured dangerous patterns
- `light`: runs a smaller high-confidence rule set
- `skip`: does not content-scan the file

Current default groups:

- `full`: `svg`, `txt`, `csv`, `rtf`, `html`, `htm`, `xml`
- `light`: `jpg`, `jpeg`, `png`, `gif`, `webp`, `bmp`, `tif`, `tiff`, `ico`, `pdf`
- `skip`: office docs, archives, audio, and video formats listed in config

Examples of patterns checked include:

- PHP opening tags
- `eval()`, `exec()`, `system()`, `shell_exec()`
- common obfuscation patterns
- web shell signatures
- `` tags and `javascript:` payloads
- null byte injection

### 4. SVG Scanner

[](#4-svg-scanner)

Runs only for `.svg` files when `guardian.svg_deep_scan` is enabled.

It parses the SVG as XML and rejects configured dangerous elements and attributes.

Default forbidden elements include:

- `script`
- `foreignObject`
- `feImage`
- `iframe`
- `embed`
- `object`
- `animate`
- `animateMotion`
- `set`
- `link`

Default forbidden attributes include a long list of event handlers such as:

- `onclick`
- `onload`
- `onerror`
- `onbegin`
- `onanimationstart`

It also blocks:

- `javascript:` inside `href` or `xlink:href`
- dangerous CSS inside `style`
- `xml:base`

Important: the current config intentionally allows some SVG features that older docs often block, such as legitimate `` references and harmless visual elements, while still blocking dangerous URI and style payloads.

### 5. Structural Scanner

[](#5-structural-scanner)

Performs format-specific validation when `guardian.structural_validation` is enabled.

Current behavior:

- images: validates parseability with GD, or Imagick if GD is unavailable
- SVG: validates that the file is parseable XML
- PDF: checks for the `%PDF-` header
- ZIP, DOCX, XLSX: checks for the `PK` ZIP header

If `guardian.re_encode_images` is `true`, supported images are re-encoded through GD to strip embedded payloads and metadata from the temporary uploaded file before you store it.

Structural validation currently applies to:

- `jpg`, `jpeg`, `png`, `gif`, `webp`
- `svg`
- `pdf`
- `zip`, `docx`, `xlsx`

Other allowed file types still pass through the earlier scanners, but do not currently receive extra structural validation.

Configuration
-------------

[](#configuration)

After publishing the config, you can tune Guardian through `config/guardian.php`.

### Allowed MIME Map

[](#allowed-mime-map)

Guardian only accepts MIME types defined in `allowed_mimes`.

The shipped config includes support for:

- images
- PDF and common office documents
- text files such as `txt`, `csv`, `rtf`
- archives such as `zip`, `rar`, `7z`, `gz`, `tar`
- common audio and video formats

### Blocked Extensions

[](#blocked-extensions)

The default blocked list includes executable and server-side formats such as:

- `php`, `phtml`, `phar`
- shell script extensions
- Windows executable/script extensions
- Python, Ruby, Perl, CGI
- Java archive/class formats
- ASP, ASPX, JSP
- `.htaccess`, `.htpasswd`

### Useful Config Flags

[](#useful-config-flags)

```
'reject_double_extensions' => true,
'structural_validation' => true,
'scan_content' => true,
'svg_deep_scan' => true,
're_encode_images' => false,
'log_rejections' => true,
'log_channel' => env('GUARDIAN_LOG_CHANNEL', 'stack'),
```

### Content Scan Map

[](#content-scan-map)

```
'content_scan_map' => [
    'full' => ['svg', 'txt', 'csv', 'rtf', 'html', 'htm', 'xml'],
    'light' => ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'tif', 'tiff', 'ico', 'pdf'],
    'skip' => ['docx', 'xlsx', 'pptx', 'doc', 'xls', 'ppt', 'zip', 'rar', '7z', 'gz', 'tar', 'mp3', 'wav', 'ogg', 'weba', 'mp4', 'mpeg', 'mpg', 'webm', 'ogv', 'avi', 'mov'],
],
```

### Optional Size Limit

[](#optional-size-limit)

The core `Guardian` class also checks `guardian.max_file_size` if you define it in your config. The published config does not currently include this key by default, but the runtime supports it.

Example:

```
'max_file_size' => 5 * 1024 * 1024, // 5 MB
```

Rejections and Responses
------------------------

[](#rejections-and-responses)

`Guardian::inspect()` throws `Hassan\Guardian\Exceptions\MaliciousFileException` when a file is rejected.

The exception renders a `422` JSON response automatically:

```
{
  "message": "File rejected by security scanner.",
  "reason": "..."
}
```

You can also catch it manually:

```
use Hassan\Guardian\Exceptions\MaliciousFileException;

try {
    Guardian::inspect($request->file('upload'));
} catch (MaliciousFileException $e) {
    return back()->withErrors([
        'upload' => 'Your file was rejected: '.$e->getReason(),
    ]);
}
```

Logging
-------

[](#logging)

When `log_rejections` is enabled, Guardian logs rejected uploads with:

- original filename
- file size
- client MIME type
- rejection reason
- scanner class
- request IP
- request URL

ClamAV Status
-------------

[](#clamav-status)

The repository contains a `ClamAvScanner` class and related config keys under `guardian.clamav`.

Current package state:

- the class exists
- config exists
- it is not part of the default `Guardian` pipeline

That means enabling `guardian.clamav.enabled` in config alone does not currently add ClamAV scanning to normal `Guardian::inspect()` or `Guardian::check()` calls.

If you want to experiment with a custom pipeline, you can override the scanner list:

```
use Hassan\Guardian\Core\ClamAvScanner;
use Hassan\Guardian\Core\ContentScanner;
use Hassan\Guardian\Core\ExtensionScanner;
use Hassan\Guardian\Core\MimeScanner;
use Hassan\Guardian\Core\StructuralScanner;
use Hassan\Guardian\Core\SvgScanner;
use Hassan\Guardian\Core\Guardian as GuardianEngine;

$guardian = app(GuardianEngine::class)->withScanners([
    ExtensionScanner::class,
    MimeScanner::class,
    ContentScanner::class,
    SvgScanner::class,
    StructuralScanner::class,
    ClamAvScanner::class,
]);

$guardian->inspect($request->file('upload'));
```

Requirements
------------

[](#requirements)

From `composer.json`, the package currently requires:

- PHP `^8.1`
- `ext-gd`
- `ext-libxml`
- `ext-dom`
- `ext-fileinfo`

Notes
-----

[](#notes)

- Guardian validates uploads before storage. It does not replace secure storage, safe file serving, or authorization checks.
- Browser-reported MIME types are not trusted.
- For image re-encoding to happen, GD support for the image format must be available.
- If neither GD nor Imagick is available at runtime, image structural validation is skipped with a warning log.

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance97

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

15d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5ad4e7134d7bf9f5a2773b31f8ff7d07b9a1b172640ab09ad181a939c604c009?d=identicon)[hassan402-paymentrequired](/maintainers/hassan402-paymentrequired)

---

Top Contributors

[![hassan402-paymentrequired](https://avatars.githubusercontent.com/u/175612412?v=4)](https://github.com/hassan402-paymentrequired "hassan402-paymentrequired (9 commits)")

---

Tags

laravelvalidationsecurityfile-uploadsanitizationmalware

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/hassan-lateef-guardian/health.svg)

```
[![Health](https://phpackages.com/badges/hassan-lateef-guardian/health.svg)](https://phpackages.com/packages/hassan-lateef-guardian)
```

###  Alternatives

[siriusphp/validation

Data validation library. Validate arrays, array objects, domain models etc using a simple API. Easily add your own validators on top of the already dozens built-in validation rules

181737.1k12](/packages/siriusphp-validation)[olssonm/l5-zxcvbn

Implementation of the zxcvbn project by @dropbox for Laravel. Uses zxcvbn-php by @bjeavons.

28302.7k1](/packages/olssonm-l5-zxcvbn)[progsmile/request-validator

Simple PHP Request Validator

33113.0k1](/packages/progsmile-request-validator)[rebelinblue/laravel-zxcvbn

Service provider to use the zxcvbn project by @dropbox in Laravel 5.4 and above

1158.4k](/packages/rebelinblue-laravel-zxcvbn)[pacerit/laravel-polish-validation-rules

Simple Polish Validation rules for Laravel and Lumen framework

1448.2k](/packages/pacerit-laravel-polish-validation-rules)[laravel-validation-rules/ip

Validate if an ip address is public or private.

1729.7k](/packages/laravel-validation-rules-ip)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
