# ============================================================
# Sudimedia Pilot - Apache configuration (root)
# ============================================================
#
# This file controls access rules, security headers, and URL
# behavior for the application root.
#
# Compatible with Apache 2.4+ and most shared hosts (OVH,
# O2Switch, Hostinger, Infomaniak, LWS...).
#
# If directives fail on your host, comment them with # rather
# than deleting them: the commented form makes troubleshooting
# easier.
# ============================================================

# ------------------------------------------------------------
# General hardening
# ------------------------------------------------------------

# Disable directory listing (prevents /assets/, /pages/ etc.
# from exposing their file list if no index exists)
Options -Indexes

# Do not follow symlinks owned by other users
Options -FollowSymLinks +SymLinksIfOwnerMatch

# Disable content negotiation. Without this, a request for an extensionless
# URL may serve a neighbouring file that happens to share the same name.
Options -MultiViews

# Do not allow .htaccess override from subdirectories beyond
# what is explicitly permitted. Commented out: some shared hosts
# reject AllowOverride directives inside .htaccess.
# AllowOverride AuthConfig FileInfo Indexes Limit Options=FollowSymLinks

# ------------------------------------------------------------
# Deny access to sensitive files and directories
# ------------------------------------------------------------

# Hide dotfiles (.git, .env, .htaccess itself, etc.)
<FilesMatch "^\.">
    Require all denied
</FilesMatch>

# Block direct access to include files (must be loaded via PHP)
<FilesMatch "\.(inc\.php|sql|sqlite|sqlite-journal|sqlite-wal|sqlite-shm|md|log|bak|old|backup|swp|dist|sh)$">
    Require all denied
</FilesMatch>

# Block access to common dev/config file patterns
<FilesMatch "^(composer\.(json|lock)|package(-lock)?\.json|yarn\.lock|phpunit\.xml|README)(\..+)?$">
    Require all denied
</FilesMatch>

# Specific block on install.php.done (renamed install marker)
<Files "install.php.done">
    Require all denied
</Files>

# ------------------------------------------------------------
# Protect the data/ directory (redundant with data/.htaccess,
# but belt-and-braces)
# ------------------------------------------------------------

# The optional group covers both layouts: Pilot at the domain root
# (/data/...) and Pilot in a subdirectory (/pilot/data/...).
RedirectMatch 403 ^/(.*/)?data(/.*)?$

# ------------------------------------------------------------
# Security headers (backup in case PHP headers fail)
# ------------------------------------------------------------
#
# PHP config.php already sets these headers on every request.
# These Apache-level headers serve as a fallback for static
# files (CSS, JS, fonts) that bypass PHP.

<IfModule mod_headers.c>
    # Prevent content-type sniffing
    Header always set X-Content-Type-Options "nosniff"

    # Disallow embedding in frames (clickjacking protection)
    Header always set X-Frame-Options "DENY"

    # Limit referrer information on outbound links
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # Disable dangerous browser features for all resources
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"

    # Cross-origin isolation
    Header always set Cross-Origin-Opener-Policy "same-origin"
    Header always set Cross-Origin-Resource-Policy "same-origin"

    # Remove potentially leaky identifying headers.
    # Both forms are required: the always form applies to error responses,
    # the plain form to normal responses.
    Header unset X-Powered-By
    Header always unset X-Powered-By
    Header unset Server
    Header always unset Server
</IfModule>

# ------------------------------------------------------------
# Static assets: cache and MIME
# ------------------------------------------------------------

<IfModule mod_mime.c>
    AddType text/css                      .css
    AddType application/javascript        .js
    AddType image/svg+xml                 .svg
    AddType font/woff2                    .woff2
    AddType font/woff                     .woff
</IfModule>

<IfModule mod_expires.c>
    ExpiresActive On

    # Immutable assets (fingerprint-based cache busting is not used
    # in V1; keep moderate TTL to allow quick updates)
    ExpiresByType text/css                "access plus 7 days"
    ExpiresByType application/javascript  "access plus 7 days"
    ExpiresByType image/svg+xml           "access plus 30 days"
    ExpiresByType image/png               "access plus 30 days"
    ExpiresByType image/jpeg              "access plus 30 days"
    ExpiresByType font/woff2              "access plus 1 year"
    ExpiresByType font/woff               "access plus 1 year"

    # No directive for text/html: PHP already sends Cache-Control
    # no-store, no-cache, must-revalidate and Pragma on every page that
    # starts a session, through session.cache_limiter. Adding a value here
    # would create a second source of truth for the same policy.
</IfModule>

# Compression (most hosts support this)
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml
    AddOutputFilterByType DEFLATE application/javascript application/json
    AddOutputFilterByType DEFLATE application/xml image/svg+xml
    AddOutputFilterByType DEFLATE font/ttf font/otf
</IfModule>

# ------------------------------------------------------------
# Default document
# ------------------------------------------------------------

DirectoryIndex index.php

# ------------------------------------------------------------
# Error pages
# ------------------------------------------------------------

ErrorDocument 403 "Accès refusé."
ErrorDocument 404 "Page introuvable."
# ErrorDocument expects a path from the domain root, not from Pilot's own
# directory. The line below therefore only works if Pilot is installed at
# the root. For a subdirectory install, uncomment it and prefix the install
# path, for example /pilot/pages/error-500.php
# ErrorDocument 500 /pages/error-500.php

# ------------------------------------------------------------
# URL rewriting: minimal (no pretty URLs in V1)
# ------------------------------------------------------------

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Ensure HTTPS redirection if you want to enforce it.
    # Commented out: some hosts require specific handling,
    # enable manually after verifying HTTPS works.
    # RewriteCond %{HTTPS} !=on
    # RewriteCond %{HTTP:X-Forwarded-Proto} !=https
    # RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

    # Block common attack patterns in query strings
    RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E)   [NC,OR]
    RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2})    [OR]
    RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})   [OR]
    RewriteCond %{QUERY_STRING} (\.\./|\.\.\\)                   [OR]
    RewriteCond %{QUERY_STRING} (union.*select.*\(|concat.*\()   [NC]
    RewriteRule ^ - [F]

    # Block suspicious user-agents (common scanners).
    # The empty user-agent condition was removed: some legitimate clients
    # send none, and blocking them produced refusals that are hard to
    # diagnose for no real gain.
    RewriteCond %{HTTP_USER_AGENT} (nikto|sqlmap|nmap|masscan)   [NC]
    RewriteRule ^ - [F]
</IfModule>

# ------------------------------------------------------------
# HTTP method restriction
# ------------------------------------------------------------

# Only GET, POST, HEAD are needed for Sudimedia Pilot.
# Block obscure methods that could be abused.
<IfModule mod_rewrite.c>
    RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$
    RewriteRule ^ - [F]
</IfModule>
