## Getting Started
### Minimal Setup for v2.0.0
1. **Update dependencies** in `composer.json`:
```json
{
"require": {
"php": "^8.2",
"symfony/framework-bundle": "^6.4|^7.4|^8.0",
"aubes/csp-bundle": "^2.0"
}
}
Run composer update.
Configure basic CSP in config/packages/aubes_csp.yaml:
groups:
default:
enabled: true
mode: enforce
directives:
default-src: "'self'"
script_src: "'self'"
First use case: Enable the bundle in config/bundles.php and run migrations if using the report logger:
return [
// ...
Aubes\CSPBundle\AubesCSPBundle::class => ['all' => true],
];
Verify installation with the new audit command:
php bin/console csp:check
Leverage built-in presets (strict, permissive, api) as a starting point:
# config/packages/aubes_csp.yaml
groups:
api:
preset: api # Auto-configures safe API defaults
enabled: true
mode: enforce
Use PHP attributes for dynamic CSP groups:
use Aubes\CSPBundle\Attribute\CSPGroup;
#[CSPGroup('admin')]
class AdminController extends AbstractController {
// All methods inherit the 'admin' group
}
#[CSPGroup('public')]
#[Route('/public', name: 'public_')]
class PublicController extends AbstractController {
#[CSPDisabled] // Disable CSP for this method
public function legacyEndpoint() { ... }
}
Automate nonce generation for scripts/styles:
{# config/packages/aubes_csp.yaml #}
groups:
default:
directives:
script_src: "'self' csp_nonce()"
{# templates/base.html.twig #}
{% csp_script %}
const script = document.createElement('script');
script.src = '/dynamic-script.js';
document.body.appendChild(script);
{% end_csp_script %}
Subscribe to CSPViolationEvent for custom logging:
// src/EventListener/CSPViolationListener.php
use Aubes\CSPBundle\Event\CSPViolationEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(event: CSPViolationEvent::class)]
public function onCSPViolation(CSPViolationEvent $event): void {
$this->logger->error('CSP Violation', [
'violation' => $event->getViolation(),
'group' => $event->getGroupName(),
]);
}
Combine enforcing and report-only groups per request:
groups:
enforcing:
mode: enforce
directives:
script_src: "'self'"
reporting:
mode: report-only
directives:
script_src: "'unsafe-inline'" # Track violations only
Apply groups in controllers:
use Aubes\CSPBundle\CSP;
public function dashboard(CSP $csp): Response {
$csp->applyGroups(['enforcing', 'reporting']);
return $this->render('dashboard.html.twig');
}
Symfony Profiler:
Enable the Web Debug Toolbar panel for real-time CSP inspection (requires symfony/web-profiler-bundle).
FrankenPHP/RoadRunner:
Use the ResetInterface implementation to clear CSP state between requests:
$csp = app(CSP::class);
$csp->reset(); // Clears all groups
Hash-Based CSP: Generate hashes for inline scripts/styles:
{% set scriptHash = csp_hash('alert("Hello")', 'sha256') %}
<script nonce="{{ csp_nonce() }}">alert("Hello")</script>
# config/packages/aubes_csp.yaml
groups:
default:
directives:
script_src: "'self' {{ scriptHash }}"
Reporting Endpoints:
Configure modern Reporting-Endpoints alongside legacy Report-To:
reporting_endpoints:
my_endpoint:
url: "https://your-reporting-endpoint.com/report"
group: "default"
backward_compatibility: true
Directive Naming:
YAML config now uses underscores (script_src instead of script-src). Update all config files.
Nonce Encoding: Nonces are now base64 (was hex). If storing/reading nonces directly (e.g., in cookies), decode them:
$hexNonce = base64_decode($base64Nonce); // Reverse if needed
Report Controller:
The ReportController no longer accepts LoggerInterface. Extend it by dispatching CSPViolationEvent:
use Aubes\CSPBundle\Event\CSPViolationEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
public function __construct(
private EventDispatcherInterface $dispatcher
) {}
public function report(CSPReport $report): void {
$event = new CSPViolationEvent($report, 'default');
$this->dispatcher->dispatch($event);
}
Group Conflicts:
CSP::addGroup() throws InvalidArgumentException if the group exists. Use CSP::setGroup() to overwrite:
$csp->setGroup('admin', $newPolicy); // Replaces existing group
Multi-Group Constraints:
A request can have only one enforcing group and one report-only group. Mixing modes throws LogicException:
$csp->applyGroups(['enforce_group', 'report_group']); // Valid
$csp->applyGroups(['enforce_group1', 'enforce_group2']); // Throws LogicException
Debug Mode:
Enable debug: true in config to force all groups into report-only mode (no enforcement):
debug: true
Audit Command:
Run php bin/console csp:check to detect:
base-uri).'unsafe-inline', *).script-src, style-src).Profiler Panel: Use the Symfony profiler to inspect:
Twig Nonce Resolution: Nonces in Twig are automatically added to all active groups. If a script/style fails, verify:
$csp->applyGroups()).csp_nonce() (e.g., `script_src: "'self' csp_nonce()").Custom Presets: Extend presets by creating a compiler pass:
use Aubes\CSPBundle\DependencyInjection\Compiler\PresetPass;
public function process(ContainerBuilder $container): void {
$container->addCompilerPass(new PresetPass([
'custom_preset' => [
'directives' => [
'default-src' => "'self' https://cdn.example.com",
],
],
]));
}
Custom Violation Handlers:
Subscribe to CSPViolationEvent to:
Sentry\captureException($event->getViolation()->getException()).ViolationRepository::save($event->getViolation()).Dynamic Directives: Override directives at runtime:
$csp->getGroup('default')->setDirective('script_src', "'self' csp_nonce()");
Custom Sources:
Extend CSPSource enum for domain-specific sources:
enum CustomSource implements CSPSource {
case API_ENDPOINT;
public function getValue(): string { return "'self' https://api.example.com"; }
}
Forgetting to Apply Groups: CSP policies are not applied automatically. Always call:
$csp->applyGroups(['default']);
in your controller/middleware.
Overusing unsafe-inline:
The audit command flags this as a security risk. Use `csp
How can I help you explore Laravel packages today?