Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,44 @@
# App
APP_NAME=SproutPHP
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:9090
APP_TIMEZONE=UTC
APP_LOCALE=en
APP_KEY=your-secret-key-here

# Repo Info For Versioning
SPROUT_REPO=SproutPHP/framework
SPROUT_USER_AGENT=sproutphp-app

# Database
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_NAME=sprout
DB_USER=root
DB_PASS=

# Mail
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@sproutphp.com
MAIL_FROM_NAME=SproutPHP

# Cache
CACHE_DRIVER=file
CACHE_PATH=/storage/cache

# Security
CSRF_ENABLED=true
XSS_PROTECTION=true
CORS_ENABLED=false

# View
VIEW_ENGINE=twig
TWIG_CACHE=false
TWIG_DEBUG=true
155 changes: 155 additions & 0 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# SproutPHP Configuration Guide

This document outlines all available configuration options for SproutPHP framework.

## Environment Variables (.env file)

### Application Configuration
```env
APP_NAME=SproutPHP
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:9090
APP_TIMEZONE=UTC
APP_LOCALE=en
APP_KEY=your-secret-key-here
```

### Database Configuration
```env
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_NAME=sprout
DB_USER=root
DB_PASS=
DB_PREFIX=
```

### Security Configuration
```env
# CSRF Protection
CSRF_ENABLED=true
CSRF_TOKEN_NAME=_token
CSRF_EXPIRE=3600

# XSS Protection
XSS_PROTECTION=true
XSS_MODE=block # 'block', 'sanitize', or '0' to disable

# Content Security Policy
CSP_ENABLED=true
CSP_REPORT_ONLY=false
CSP_REPORT_URI=

# CORS
CORS_ENABLED=false
CORS_ALLOWED_ORIGINS=*
CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE
CORS_ALLOWED_HEADERS=Content-Type,Authorization
```

### Mail Configuration
```env
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@sproutphp.com
MAIL_FROM_NAME=SproutPHP
```

### Cache Configuration
```env
CACHE_DRIVER=file
CACHE_PATH=/storage/cache
```

### Session Configuration
```env
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_PATH=/storage/sessions
```

### Logging Configuration
```env
LOG_DRIVER=file
LOG_LEVEL=debug
LOG_PATH=/storage/logs
```

### View Configuration
```env
VIEW_ENGINE=twig
TWIG_CACHE=false
TWIG_DEBUG=true
TWIG_AUTO_RELOAD=true
TWIG_STRICT_VARIABLES=false
```

### Framework Configuration
```env
SPROUT_REPO=SproutPHP/framework
SPROUT_USER_AGENT=sproutphp-app
```

## Configuration Usage

### In PHP Code
```php
// Get app name
$appName = config('app.name');

// Get database host
$dbHost = config('database.connections.mysql.host');

// Check if XSS protection is enabled
$xssEnabled = config('security.xss.enabled');

// Get CSP mode
$cspMode = config('security.csp.report_only');
```

### In Twig Templates
```twig
{# Get app name #}
<h1>{{ config('app.name') }}</h1>

{# Check environment #}
{% if config('app.env') == 'local' %}
<div class="debug-info">Development Mode</div>
{% endif %}
```

## Security Features

### XSS Protection
The framework automatically adds XSS protection headers based on your configuration:

- **Development**: Relaxed CSP policy allowing inline styles and external images
- **Production**: Strict CSP policy for maximum security

### CSRF Protection
CSRF tokens are automatically generated and validated for state-changing requests (POST, PUT, PATCH, DELETE).

### Content Security Policy
CSP headers are automatically set based on your environment:
- **Local/Debug**: Allows `unsafe-inline` for styles and external images
- **Production**: Strict policy with no unsafe directives

## Environment-Specific Behavior

### Local Environment
- Debug information displayed
- Relaxed security policies
- Detailed error messages
- HTMX debug indicator

### Production Environment
- No debug information
- Strict security policies
- Generic error pages
- Optimized performance settings
3 changes: 2 additions & 1 deletion app/Controllers/HomeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class HomeController
public function index()
{
$release = getLatestRelease();
$appName = config('app.name', 'SproutPHP');
return "<p>A minimilistic php-framework designed for go-to developer without the need for javascript or heavy modules. 🌳</p>
<small>SproutPHP latest release: <strong>$release</strong></small>";
<small>{$appName} latest release: <strong>$release</strong></small>";
}
}
37 changes: 30 additions & 7 deletions app/Middlewares/XssProtection.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,40 @@ public function handle(Request $request, callable $next)
{
$response = $next($request);

header("X-XSS-Protection: 1; mode=block");
// Get security configuration
$xssEnabled = config('security.xss.enabled', true);
$xssMode = config('security.xss.mode', 'block');

if ($xssEnabled) {
header("X-XSS-Protection: 1; mode={$xssMode}");
}

header("X-Content-Type-Options: nosniff");

// Set relaxed ContentSecurityPolicy in development, strict in production
if ((function_exists('env') && env('APP_ENV') === 'local') || (function_exists('env') && env('APP_DEBUG') === 'true')) {
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' https://img.shields.io; object-src 'none';");
} else {
// Production: strict ContentSecurityPolicy
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; object-src 'none';");
// Set Content Security Policy based on environment
$cspEnabled = config('security.csp.enabled', true);
if ($cspEnabled) {
$cspPolicy = $this->getCspPolicy();
header("Content-Security-Policy: {$cspPolicy}");
}

return $response;
}

private function getCspPolicy(): string
{
$env = config('app.env', 'local');
$debug = config('app.debug', false);

// Base CSP policy
$basePolicy = "default-src 'self'; script-src 'self'; object-src 'none';";

if ($env === 'local' || $debug) {
// Development: relaxed policy
return $basePolicy . " style-src 'self' 'unsafe-inline'; img-src 'self' https://img.shields.io;";
} else {
// Production: strict policy
return $basePolicy . " style-src 'self'; img-src 'self';";
}
}
}
4 changes: 2 additions & 2 deletions app/Views/layouts/base.twig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="htmx-powered-by" content="SproutPHP">
<title>
{% block title %}
SproutPHP
{{ config('app.name', 'SproutPHP') }}
{% endblock %}
</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Expand All @@ -18,7 +18,7 @@
{% block content %}{% endblock %}
</main>

{% if env('APP_ENV') == 'local' %}
{% if config('app.env', 'local') == 'local' %}
<div style="z-index:9999;position:fixed;bottom:75px;right:10px;background:#eef;border:1px solid #ccd;padding:0.5rem;font-family:monospace;font-size:0.8rem;border-radius:4px;">
⚡ HTMX Active
<br/>
Expand Down
38 changes: 38 additions & 0 deletions config/app.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

return [
// Application settings
'name' => env('APP_NAME', 'SproutPHP'),
'env' => env('APP_ENV', 'local'),
'debug' => env('APP_DEBUG', true),
'url' => env('APP_URL', 'http://localhost'),
'timezone' => env('APP_TIMEZONE', 'UTC'),
'locale' => env('APP_LOCALE', 'en'),
'key' => env('APP_KEY', 'your-secret-key-here'),

// Framework settings
'version' => '1.0.0',
'framework' => 'SproutPHP',
'repo' => env('SPROUT_REPO', 'SproutPHP/framework'),
'user_agent' => env('SPROUT_USER_AGENT', 'sproutphp-app'),

// Global middleware
'global_middleware' => [
\App\Middlewares\VerifyCsrfToken::class,
\App\Middlewares\XssProtection::class,
],

// Session settings
'session' => [
'driver' => env('SESSION_DRIVER', 'file'),
'lifetime' => env('SESSION_LIFETIME', 120),
'path' => env('SESSION_PATH', '/storage/sessions'),
],

// Logging
'log' => [
'driver' => env('LOG_DRIVER', 'file'),
'level' => env('LOG_LEVEL', 'debug'),
'path' => env('LOG_PATH', '/storage/logs'),
],
];
22 changes: 22 additions & 0 deletions config/cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

return [
'default' => env('CACHE_DRIVER', 'file'),

'stores' => [
'file' => [
'driver' => 'file',
'path' => env('CACHE_PATH', '/storage/cache'),
],

'redis' => [
'driver' => 'redis',
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'password' => env('REDIS_PASSWORD'),
'database' => env('REDIS_DB', 0),
],
],

'prefix' => env('CACHE_PREFIX', 'sprout_'),
];
39 changes: 39 additions & 0 deletions config/database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

return [
'default' => env('DB_CONNECTION', 'mysql'),

'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', 3306),
'database' => env('DB_NAME', 'sprout'),
'username' => env('DB_USER', 'root'),
'password' => env('DB_PASS', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => env('DB_PREFIX', ''),
'strict' => true,
'engine' => null,
],

'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', '/storage/database.sqlite'),
'prefix' => env('DB_PREFIX', ''),
],

'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', 5432),
'database' => env('DB_NAME', 'sprout'),
'username' => env('DB_USER', 'postgres'),
'password' => env('DB_PASS', ''),
'charset' => 'utf8',
'prefix' => env('DB_PREFIX', ''),
'schema' => 'public',
],
]
];
Loading