{"id":13852349,"url":"https://github.com/drupal-composer/drupal-paranoia","last_synced_at":"2026-03-05T11:03:13.297Z","repository":{"id":45234013,"uuid":"129131494","full_name":"drupal-composer/drupal-paranoia","owner":"drupal-composer","description":"Composer Plugin for improving the security of composer-based Drupal projects by moving all PHP files out of docroot","archived":false,"fork":false,"pushed_at":"2021-12-29T13:58:43.000Z","size":46,"stargazers_count":62,"open_issues_count":4,"forks_count":7,"subscribers_count":11,"default_branch":"1.x","last_synced_at":"2025-04-09T20:14:52.108Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drupal-composer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-11T17:39:22.000Z","updated_at":"2024-03-29T10:58:32.000Z","dependencies_parsed_at":"2022-08-04T13:00:13.087Z","dependency_job_id":null,"html_url":"https://github.com/drupal-composer/drupal-paranoia","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupal-composer%2Fdrupal-paranoia","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupal-composer%2Fdrupal-paranoia/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupal-composer%2Fdrupal-paranoia/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drupal-composer%2Fdrupal-paranoia/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drupal-composer","download_url":"https://codeload.github.com/drupal-composer/drupal-paranoia/tar.gz/refs/heads/1.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103872,"owners_count":21048245,"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-08-04T22:01:16.484Z","updated_at":"2025-04-09T20:14:56.727Z","avatar_url":"https://github.com/drupal-composer.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"[![Packagist Downloads](https://img.shields.io/packagist/dt/drupal-composer/drupal-paranoia.svg)](https://packagist.org/packages/drupal-composer/drupal-paranoia)\n[![Testing](https://github.com/drupal-composer/drupal-paranoia/actions/workflows/testing.yml/badge.svg)](https://github.com/drupal-composer/drupal-paranoia/actions/workflows/testing.yml)\n\n# Drupal Paranoia\nComposer plugin for improving the website security for composer-based Drupal websites by moving all __PHP files out of docroot__.\n\n## Why use this Plugin?\nThe critical security issue with [Coder](https://www.drupal.org/project/coder) is a good example to consider moving PHP files outside of docroot:\n- [SA-CONTRIB-2016-039 - Remote Code Execution](https://www.drupal.org/node/2765575)\n- https://twitter.com/drupalsecurity/status/753263548458004480\n\nMore related links:\n- [Moving all PHP files out of the docroot](https://www.drupal.org/node/2767907)\n- [#1672986: Option to have all php files outside of web root](https://www.drupal.org/node/1672986)\n\n## Requirements\nExcept for Windows, this plugin should work on environments that have Composer support. Do you use Windows? [Help us](https://github.com/drupal-composer/drupal-paranoia/issues/5).\n\n## Installation\nMake sure you have a based [drupal-composer/drupal-project](https://github.com/drupal-composer/drupal-project) project created.\n\nRename your current docroot directory to `/app`.\n```\ncd drupal-project-root\nmv web app\n```\n\nUpdate the `composer.json` of your root package with the following values:\n```json\n\"extra\": {\n    \"drupal-paranoia\": {\n        \"app-dir\": \"app\",\n        \"web-dir\": \"web\"\n    },\n    \"installer-paths\": {\n        \"app/core\": [\"type:drupal-core\"],\n        \"app/libraries/{$name}\": [\"type:drupal-library\"],\n        \"app/modules/contrib/{$name}\": [\"type:drupal-module\"],\n        \"app/profiles/contrib/{$name}\": [\"type:drupal-profile\"],\n        \"app/themes/contrib/{$name}\": [\"type:drupal-theme\"],\n        \"drush/contrib/{$name}\": [\"type:drupal-drush\"]\n    }\n}\n```\n\nExplaining:\n- __/app__ folder: Drupal full installation.\n- __/web__ folder: Will contain only symlinks of the assets files and PHP stub files (index.php, install.php, etc) from the `/app` folder.\n\nUse `composer require ...` to install this Plugin on your project.\n```\ncomposer require drupal-composer/drupal-paranoia:~1\n```\n\nDone! The plugin and the new docroot are now installed.\n\n### Asset file types\nThe asset files are symlinked from `/app` to `/web` folder.\n\nDefault asset file types are provided by the plugin:\n```\nrobots.txt\n.htaccess\n*.css\n*.eot\n*.ico\n*.gif\n*.jpeg\n*.jpg\n*.js\n*.map\n*.otf\n*.png\n*.svg\n*.ttf\n*.woff\n*.woff2\n```\n\nTo extend the list of assets file types you can use the `asset-files` config:\n```json\n\"extra\": {\n    \"drupal-paranoia\": {\n        \"app-dir\": \"app\",\n        \"web-dir\": \"web\",\n        \"asset-files\": [\n            \"somefile.txt\",\n            \"*.md\"\n        ]\n    },\n    \"...\"\n}\n```\n\nIf you need to modify the list you can use the `post-drupal-set-asset-file-types` event:\n```json\n\"scripts\": {\n    \"post-drupal-set-asset-file-types\": [\n        \"DrupalProject\\\\composer\\\\ScriptHandler::setAssetFileTypes\"\n    ],\n    \"...\"\n},\n```\n\n```php\n\u003c?php\n\n/**\n * @file\n * Contains \\DrupalProject\\composer\\ScriptHandler.\n */\n\nnamespace DrupalProject\\composer;\n\nuse DrupalComposer\\DrupalParanoia\\AssetFileTypesEvent;\n\nclass ScriptHandler {\n\n  public static function setAssetFileTypes(AssetFileTypesEvent $event) {\n    $asset_file_types = $event-\u003egetAssetFileTypes();\n    // Do what you want with the asset file types.\n    $event-\u003esetAssetFileTypes($asset_file_types);\n  }\n\n}\n```\n\nBy the purpose of this plugin, the following files types are __not allowed__ and if listed they will be ignored:\n```\n*.inc\n*.install\n*.module\n*.phar\n*.php\n*.profile\n*.theme\n```\n\n### Exclude paths\nWith the drupal-paranoia option excludes, you can provide paths that should not be symlinked or stubbed to `/web` folder. The plugin provides no excludes by default.\n\n```json\n\"extra\": {\n    \"drupal-paranoia\": {\n        \"app-dir\": \"app\",\n        \"web-dir\": \"web\",\n        \"excludes\": [\n            \"core/install.php\",\n            \"sites/simpletest\"\n        ]\n    },\n    \"...\"\n}\n```\n\n__NOTE:__ Consider to exclude `/install.php` from your site. There are security concerns when this URL is publicly available, it can be used to create a list of contributed modules existing on the site.\nYou can exclude it via plugin as described above or via `.htaccess` rules. \n- [DO#2840973: Install system should not produce PHP errors](https://www.drupal.org/node/2840973)    \n- https://www.drupalxray.com\n\n### Web server docroot\nChange the document root config of your web server to point to `/web` folder.\n\n## Plugin events\nThis plugin fires the following named event during its execution process:\n\n- __drupal-paranoia-post-command-run__: Occurs after the command `drupal:paranoia` is executed.\n\n### Example of event subscriber\n\n```php\n\u003c?php\n\nnamespace MyVendor;\n\nuse Composer\\Composer;\nuse Composer\\EventDispatcher\\EventSubscriberInterface;\nuse Composer\\IO\\IOInterface;\nuse Composer\\Plugin\\PluginInterface;\nuse DrupalComposer\\DrupalParanoia\\PluginEvents as DrupalParanoiaPluginEvents;\n\nclass MyClass implements PluginInterface, EventSubscriberInterface\n{\n    protected $composer;\n    protected $io;\n\n    public function activate(Composer $composer, IOInterface $io)\n    {\n        $this-\u003ecomposer = $composer;\n        $this-\u003eio = $io;\n    }\n\n    public static function getSubscribedEvents()\n    {\n        return array(\n            DrupalParanoiaPluginEvents::POST_COMMAND_RUN =\u003e 'postDrupalParanoiaCommand',\n        );\n    }\n\n    public function postDrupalParanoiaCommand(CommandEvent $event) {\n        // Add your custom action.\n    }\n}\n```\n\n## Local development\nEvery time you install or update a Drupal package via Composer, the `/web` folder will be recreated.\n\n```\ncomposer require drupal/devel:~1.0\n\u003e drupal-paranoia: docroot folder has been rebuilt.\n```\n\nWhen working with themes, CSS and JS for example, it may be necessary to rebuild the folder manually to symlink the new assets.\n```\ncomposer drupal:paranoia\n```\n\n### Public files\nThis plugin assumes that the public files folder exists at `app/sites/\u003csite\u003e/files` and symlinks `web/sites/\u003csite\u003e/files -\u003e ../../../app/sites/\u003csite\u003e/files`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrupal-composer%2Fdrupal-paranoia","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrupal-composer%2Fdrupal-paranoia","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrupal-composer%2Fdrupal-paranoia/lists"}