PHPackages                             allyson/laravel-safe-mode - 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. [Security](/categories/security)
4. /
5. allyson/laravel-safe-mode

ActiveLibrary[Security](/categories/security)

allyson/laravel-safe-mode
=========================

Protege ambientes Laravel contra comandos Artisan destrutivos em produção, com auditoria completa.

v1.0.0(2w ago)00MITPHPPHP ^8.5

Since Mar 6Pushed 2w agoCompare

[ Source](https://github.com/allysonpdm/laravel-safe-mode)[ Packagist](https://packagist.org/packages/allyson/laravel-safe-mode)[ RSS](/packages/allyson-laravel-safe-mode/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (8)Versions (2)Used By (0)

allyson/laravel-safe-mode
=========================

[](#allysonlaravel-safe-mode)

[![PHP](https://camo.githubusercontent.com/c0cd2fac930abe3f83e13dff488a34edef374651e38cf83eb06f6b43d1f6693e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e352532422d626c7565)](https://www.php.net)[![Laravel](https://camo.githubusercontent.com/5b99ec644e12357132ddb95970822afd67efedef92fcccb6270926adebd8b101/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31322e782d726564)](https://laravel.com)[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

> Protege ambientes Laravel contra execução acidental de comandos Artisan destrutivos em produção — com auditoria completa, notificações e whitelist de IPs.

---

O problema que essa lib resolve
-------------------------------

[](#o-problema-que-essa-lib-resolve)

```
# Rodado em produção por engano → dados PERDIDOS
php artisan migrate:fresh
php artisan migrate:refresh
php artisan db:wipe
```

Isso já aconteceu em empresas grandes. O SafeMode impede.

---

Como funciona
-------------

[](#como-funciona)

```
User roda: php artisan migrate:fresh
                │
                ▼
      SafeModeService.handle()
                │
                ├── Comando não está na lista? → Passa livre
                │
                ▼
      Verifica APP_ENV + SAFE_MODE
                │
                ▼
      Verifica host do banco default
                │
         ┌──────┴──────────────────┐
         │                         │
     Host LOCAL                Host REMOTO
     (localhost, 127.x, 192.168.x)   │
         │                         ▼
         │               SAFE_MODE=true (padrão)
         │               ┌─────────────────────┐
         └──────────┐    │  Registra tentativa  │
                    │    │  BLOQUEADA no audit  │
                    ▼    │  Lança exceção        │
                 Permite └─────────────────────┘
                 executar
                            SAFE_MODE=false
                         ┌─────────────────────┐
                         │  Registra auditoria  │
                         │  Permite executar    │
                         └─────────────────────┘

```

> Em ambos os casos com host remoto, o banco de auditoria **precisa estar disponível**. Se não estiver, o comando é **bloqueado incondicionalmente**.

---

Instalação
----------

[](#instalação)

```
composer require allyson/laravel-safe-mode
```

O Service Provider é registrado automaticamente via [Laravel Package Auto-Discovery](https://laravel.com/docs/packages#package-discovery).

### Configuração inicial

[](#configuração-inicial)

```
php artisan safe-mode:install
```

O assistente irá:

1. Publicar `config/safe-mode.php`
2. Publicar as migrations
3. Perguntar **qual conexão** usará para salvar as auditorias
4. Atualizar o `.env` automaticamente
5. Executar as migrations (opcional)

---

Variáveis de ambiente
---------------------

[](#variáveis-de-ambiente)

VariávelPadrãoDescrição`SAFE_MODE``true``true` = bloqueia em servidor remoto | `false` = audita sem bloquear`SAFE_MODE_AUDIT_CONNECTION`*(vazio)*Nome da conexão do banco de auditoria`SAFE_MODE_FORCE_PRODUCTION``true`Força safe mode quando `APP_ENV=production``SAFE_MODE_ALLOWED_IPS`*(vazio)*IPs adicionais considerados locais (separados por vírgula)`SAFE_MODE_WEBHOOK_ENABLED``false`Habilita notificações via webhook`SAFE_MODE_WEBHOOK_CHANNEL``generic`Canal: `slack`, `discord` ou `generic``SAFE_MODE_WEBHOOK_URL`*(vazio)*URL do webhookExemplo de `.env`:

```
SAFE_MODE=true
SAFE_MODE_AUDIT_CONNECTION=mysql_audit
SAFE_MODE_FORCE_PRODUCTION=true
SAFE_MODE_ALLOWED_IPS=10.0.0.10,192.168.1.50

# Webhook opcional
SAFE_MODE_WEBHOOK_ENABLED=true
SAFE_MODE_WEBHOOK_CHANNEL=slack
SAFE_MODE_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
```

---

Configuração (`config/safe-mode.php`)
-------------------------------------

[](#configuração-configsafe-modephp)

```
return [
    'enabled'              => env('SAFE_MODE', true),
    'force_on_production'  => env('SAFE_MODE_FORCE_PRODUCTION', true),

    'blocked_commands' => [
        'migrate:fresh',
        'migrate:refresh',
        'migrate:reset',
        'db:wipe',
        'down',
    ],

    'audit_connection' => env('SAFE_MODE_AUDIT_CONNECTION'),

    'allowed_ips' => array_filter(explode(',', env('SAFE_MODE_ALLOWED_IPS', ''))),

    'webhook' => [
        'enabled' => env('SAFE_MODE_WEBHOOK_ENABLED', false),
        'channel' => env('SAFE_MODE_WEBHOOK_CHANNEL', 'generic'),
        'url'     => env('SAFE_MODE_WEBHOOK_URL'),
    ],
];
```

---

Tabela de auditoria (`safe_mode_audits`)
----------------------------------------

[](#tabela-de-auditoria-safe_mode_audits)

CampoTipoDescrição`id`bigintChave primária`command`stringComando executado (ex: `migrate:fresh`)`user`stringUsuário do SO (PHP process)`machine`stringHostname da máquina`ip`stringIP da máquina`database_host`stringHost do banco alvo`connection`stringNome da conexão Laravel`app_env`string`APP_ENV` no momento`output`longtextSaída do comando (quando capturada)`exit_code`integerCódigo de saída`blocked`boolean`true` = bloqueado, `false` = auditado`created_at`timestampData/hora do registro---

Detecção de host local
----------------------

[](#detecção-de-host-local)

O `LocalIpDetector` considera local:

- `127.0.0.1`, `localhost`, `::1`, `0.0.0.0`
- Redes `10.x.x.x` (RFC 1918)
- Redes `192.168.x.x` (RFC 1918)
- Redes `172.16.x.x` a `172.31.x.x` (RFC 1918)
- `169.254.x.x` (link-local)
- Hostnames que **resolvem** para um dos IPs acima
- IPs adicionais configurados em `SAFE_MODE_ALLOWED_IPS`
- Conexões SQLite (sempre locais)

---

Adicionar comandos à lista de proteção
--------------------------------------

[](#adicionar-comandos-à-lista-de-proteção)

```
// config/safe-mode.php
'blocked_commands' => [
    'migrate:fresh',
    'migrate:refresh',
    'migrate:reset',
    'db:wipe',
    'down',
    // adicione seus próprios:
    'meu-comando:perigoso',
],
```

---

Notificações via Webhook
------------------------

[](#notificações-via-webhook)

### Slack

[](#slack)

```
SAFE_MODE_WEBHOOK_CHANNEL=slack
SAFE_MODE_WEBHOOK_URL=https://hooks.slack.com/services/T00/B00/xxx
```

### Discord

[](#discord)

```
SAFE_MODE_WEBHOOK_CHANNEL=discord
SAFE_MODE_WEBHOOK_URL=https://discord.com/api/webhooks/xxx/yyy
```

### HTTP Genérico (JSON)

[](#http-genérico-json)

```
SAFE_MODE_WEBHOOK_CHANNEL=generic
SAFE_MODE_WEBHOOK_URL=https://meu-sistema.com/webhook/safe-mode
```

Payload enviado:

```
{
  "status": "blocked",
  "command": "migrate:fresh",
  "user": "deploy",
  "machine": "prod-web-01",
  "ip": "10.0.0.5",
  "database_host": "rds.amazonaws.com",
  "app_env": "production",
  "created_at": "2026-03-06 14:32:00"
}
```

---

Estrutura do pacote
-------------------

[](#estrutura-do-pacote)

```
laravel-safe-mode/
├── src/
│   ├── SafeModeServiceProvider.php
│   ├── Config/
│   │   └── safe-mode.php
│   ├── Console/
│   │   └── InstallCommand.php
│   ├── Exceptions/
│   │   ├── UnsafeCommandException.php
│   │   └── AuditConnectionException.php
│   ├── Models/
│   │   └── SafeModeAudit.php
│   ├── Services/
│   │   ├── SafeModeService.php
│   │   ├── ConnectionInspector.php
│   │   ├── AuditService.php
│   │   └── WebhookNotifier.php
│   └── Support/
│       └── LocalIpDetector.php
├── database/
│   └── migrations/
│       └── 2025_01_01_000000_create_safe_mode_audits_table.php
├── composer.json
└── README.md

```

---

Requisitos
----------

[](#requisitos)

- PHP **8.5+**
- Laravel **12.x**

---

Licença
-------

[](#licença)

MIT — veja [LICENSE](LICENSE).

Estrutura final

```
laravel-safe-mode/
├── composer.json
├── README.md
├── database/migrations/
│   └── 2025_01_01_000000_create_safe_mode_audits_table.php
└── src/
    ├── SafeModeServiceProvider.php
    ├── Config/safe-mode.php
    ├── Console/InstallCommand.php
    ├── Exceptions/
    │   ├── UnsafeCommandException.php
    │   └── AuditConnectionException.php
    ├── Models/SafeModeAudit.php
    ├── Services/
    │   ├── SafeModeService.php        ← orquestrador principal
    │   ├── ConnectionInspector.php    ← detecta se host é local/remoto
    │   ├── AuditService.php           ← salva registros + dispara webhook
    │   └── WebhookNotifier.php        ← Slack / Discord / HTTP genérico
    └── Support/LocalIpDetector.php    ← RFC 1918 + resolução de hostnames

```

---

Decisões de design implementadas
--------------------------------

[](#decisões-de-design-implementadas)

RequisitoImplementaçãoBloquear migrate:fresh etc. em host remotoSafeModeService via CommandStarting eventSAFE\_MODE=false audita sem bloquearFluxo no SafeModeService.handle()Banco de auditoria obrigatórioConnectionInspector.assertAuditConnectionAvailable() — sem banco, bloqueia sempresafe-mode:install interativoInstallCommand com seleção de conexão, testes, atualização do .envForçar safe mode em APP\_ENV=productionforce\_on\_production na configWhitelist de IPs extrasSAFE\_MODE\_ALLOWED\_IPS + LocalIpDetector.isWhitelisted()Notificações webhookWebhookNotifier para Slack, Discord e HTTP genéricoSQLite sempre localConnectionInspector verifica driver antes do hostblocked vs audited separadosCampo blocked boolean na tabela + método registerBlocked()

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance97

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

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

Unknown

Total

1

Last Release

16d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/27ef618d6449b3209374d96ef45e55f079e3c80453d39620c5ac338d27442bc0?d=identicon)[allysonpdm](/maintainers/allysonpdm)

---

Top Contributors

[![allysonpdm](https://avatars.githubusercontent.com/u/4742011?v=4)](https://github.com/allysonpdm "allysonpdm (2 commits)")

---

Tags

laravelartisanAuditprotectionproductionsafe mode

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/allyson-laravel-safe-mode/health.svg)

```
[![Health](https://phpackages.com/badges/allyson-laravel-safe-mode/health.svg)](https://phpackages.com/packages/allyson-laravel-safe-mode)
```

PHPackages © 2026

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