{"id":18482842,"url":"https://github.com/spaze/csp-config","last_synced_at":"2025-04-08T18:31:59.097Z","repository":{"id":7905167,"uuid":"56821091","full_name":"spaze/csp-config","owner":"spaze","description":"Build Content Security Policy from a config file","archived":false,"fork":false,"pushed_at":"2024-10-26T00:19:48.000Z","size":91,"stargazers_count":14,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-23T16:51:22.574Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spaze.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-04-22T02:42:55.000Z","updated_at":"2024-08-20T06:30:10.000Z","dependencies_parsed_at":"2023-01-13T14:33:21.137Z","dependency_job_id":"b133e305-fd28-4c80-bf80-d6ab6a543cac","html_url":"https://github.com/spaze/csp-config","commit_stats":{"total_commits":85,"total_committers":2,"mean_commits":42.5,"dds":0.0117647058823529,"last_synced_commit":"dc96424826ee35628da7dd016f00a5cae4493f0a"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spaze%2Fcsp-config","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spaze%2Fcsp-config/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spaze%2Fcsp-config/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spaze%2Fcsp-config/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spaze","download_url":"https://codeload.github.com/spaze/csp-config/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247902301,"owners_count":21015426,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T12:31:11.940Z","updated_at":"2025-04-08T18:31:54.086Z","avatar_url":"https://github.com/spaze.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# csp-config\nBuild Content Security Policy from a config file. Supports different policy per page or module, and snippets you can add dynamically, if needed.\n\n[![PHP Tests](https://github.com/spaze/csp-config/workflows/PHP%20Tests/badge.svg)](https://github.com/spaze/csp-config/actions?query=workflow%3A%22PHP+Tests%22)\n\nThe library is designed to be usable with any framework (or without one) but comes with a bridge for [Nette Framework](https://nette.org/).\n\n\u003e Please note that this library will only build the header value and you still need to send the header yourself!\n\n## Installation\n\nThe best way to install the library is using [Composer](https://getcomposer.org/):\n\n```sh\ncomposer require spaze/csp-config\n```\n\n## Nette Framework configuration\nIf you're using Nette Framework you can add the extension to your config file:\n\n```neon\nextensions:\n    contentSecurityPolicy: Spaze\\ContentSecurityPolicy\\Bridges\\Nette\\CspConfigExtension\n```\n\n### Example configuration\n\nThis is an example configuration, it's here to explain things and it's intentionally incomplete. You can also check [the configuration used for my site](https://github.com/spaze/michalspacek.cz/blob/master/site/app/config/contentsecuritypolicy.neon).\n\n```neon\ncontentSecurityPolicy:\n    snippets:\n        slideshare:\n            child-src:\n                - https://www.slideshare.net\n    policies:\n        *.*:\n            default-src: \"'none'\"\n            form-action: \"'none'\"\n            report-uri: https://report-uri.com.example.net\n            report-to: default\n        www.*.*:\n            default-src: \"'none'\"\n            script-src:\n                - \"'strict-dynamic'\"\n                - \"'nonce'\"\n                - \"'self'\"\n                - \"'report-sample'\"\n            upgrade-insecure-requests:\n        www.trainings.training:\n            @extends: www.*.*\n            connect-src: https://api.example.com\n        admin.*.*:\n            @extends: www.*.*\n        admin.blog.add:\n            @extends: admin.*.*\n            connect-src: \"'self'\"\n        admin.blog.edit:\n            @extends: admin.blog.add\n    policiesReportOnly:\n      *.*:\n        default-src: \"'self'\"\n```\n\nLet's explain:\n- `snippets`\nThis is where you define your snippets. A snippet consists of one or more Content Security Policy directives that can be added to the current Content Security Policy header with the `addSnippet(string $snippetName)` method like this: `$this-\u003econtentSecurityPolicy-\u003eaddSnippet($type);` You can use it to add use it to extend your policy when there's a video on the page for example. There are sample snippets in [snippets.neon](https://github.com/spaze/csp-config/blob/master/snippets.neon) which you can directly include in your configuration if you want.\n\n- `policies`\nYour CSP policies go here. The keys below mean `[module.]presenter.action`, wildcards are supported.\n  - `*.*` means *use these for all presenters and actions*. As you can see in the example above, I've used quite restrictive policy and will allow more later on.\n  - `www.*.*` applies to all presenters and actions in the \"www\" module.\n  - `@extends: www.*.*` this configuration extends the `www.*.*` configuration, any values specified will be added, or merged. Use it to extend the default policy for some pages or actions. You can disable merging by prefixing the directive name with `!`, effectively overwriting the extended values, [see below](#overriding-values). \n\n- `policiesReportOnly`\nLike `policies` but intended to be used with `Content-Security-Policy-Report-Only` header, see below.\n\nPolicies can contain a few special keys and values:\n- keys with no values, like `upgrade-insecure-requests:` in the example above, will make the policy header contain just the key name and no values\n- `'nonce'` will add a CSP nonce (`'nonce-somethingrandomandunique`') to the header. Nonces were defined in CSP2 and are used in a recommended policy using [CSP3 `'strict-dynamic'`](https://exploited.cz/xss/csp/strict.php). For this to work [spaze/nonce-generator](https://github.com/spaze/nonce-generator) is needed. It will also return the immutable nonce so you can add it to your `\u003cscript\u003e` tags. This can be nicely automated with [spaze/sri-macros](https://github.com/spaze/sri-macros).\n\n#### Overriding values\nIf you don't want the extended values to be merged with the original values, prefix the directive name in the configuration with an exclamation mark (`!`).\nConsider the following simple example configuration:\n\n```neon\ncontentSecurityPolicy:\n    policies:\n        *.*:\n            default-src: \"'none'\"\n        www.*:\n            @extends: *.*\n            default-src: \"'self'\"\n```\n\nCalling `getHeader('www:...')` would then return `default-src 'none' 'self'` which makes no sense and `'none'` would even be ignored.\n\nChange the configuration to this (note the `!` prefix in `default-src`):\n\n```neon\ncontentSecurityPolicy:\n    policies:\n        *.*:\n            default-src: \"'none'\"\n        www.*:\n            @extends: *.*\n            !default-src: \"'self'\"\n```\n\nThen calling `getHeader('www:...')` would return `default-src 'self'` which is probably what you'd want in this case.\n\n### How to send the generated header in Nette Framework\n```php\n$header = $this-\u003econtentSecurityPolicy-\u003egetHeader($presenter-\u003egetAction(true));\nif ($header) {\n    $this-\u003ehttpResponse-\u003esetHeader('Content-Security-Policy', $header);\n}\n```\n\nYou can get `$presenter` from `\\Nette\\Application\\Application` like this for example:\n\n```php\n/** @var \\Nette\\Application\\UI\\Presenter $presenter */\n$presenter = $this-\u003eapplication-\u003egetPresenter();\n$actionName = $presenter-\u003egetAction(true);\n```\nAnd get `$this-\u003eapplication` from dependency injection container:\n\n```php\npublic function __construct(private \\Nette\\Application\\Application $application)\n{\n}\n```\n\nIf you're in a presenter then you can use `$this-\u003egetAction(true)` instead.\n\n### Report-only policy\nUse `policiesReportOnly` configuration key to define policies to use with `Content-Security-Policy-Report-Only` header:\n\n```neon\ncontentSecurityPolicy:\n    policies:\n        *.*:\n            default-src: \"'none'\"\n    policiesReportOnly:\n        *.*:\n            default-src: \"'self'\"\n```\n\nGet the policy by calling `getHeaderReportOnly()` method:\n\n```php\n$header = $this-\u003econtentSecurityPolicy-\u003egetHeaderReportOnly($presenter-\u003egetAction(true));\nif ($header) {\n    $this-\u003ehttpResponse-\u003esetHeader('Content-Security-Policy-Report-Only', $header);\n}\n```\n\nYou can send both *enforce* and *report-only* policies which is useful for policy upgrades for example:\n\n```php\n$header = $this-\u003econtentSecurityPolicy-\u003egetHeader($presenter-\u003egetAction(true));\nif ($header) {\n    $this-\u003ehttpResponse-\u003esetHeader('Content-Security-Policy', $header);\n}\n$header = $this-\u003econtentSecurityPolicy-\u003egetHeaderReportOnly($presenter-\u003egetAction(true));\nif ($header) {\n    $this-\u003ehttpResponse-\u003esetHeader('Content-Security-Policy-Report-Only', $header);\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspaze%2Fcsp-config","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspaze%2Fcsp-config","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspaze%2Fcsp-config/lists"}