Giter Club home page Giter Club logo

drupal-paranoia's Introduction

Packagist Downloads Testing

Drupal Paranoia

Composer plugin for improving the website security for composer-based Drupal websites by moving all PHP files out of docroot.

Why use this Plugin?

The critical security issue with Coder is a good example to consider moving PHP files outside of docroot:

More related links:

Requirements

Except for Windows, this plugin should work on environments that have Composer support. Do you use Windows? Help us.

Installation

Make sure you have a based drupal-composer/drupal-project project created.

Rename your current docroot directory to /app.

cd drupal-project-root
mv web app

Update the composer.json of your root package with the following values:

"extra": {
    "drupal-paranoia": {
        "app-dir": "app",
        "web-dir": "web"
    },
    "installer-paths": {
        "app/core": ["type:drupal-core"],
        "app/libraries/{$name}": ["type:drupal-library"],
        "app/modules/contrib/{$name}": ["type:drupal-module"],
        "app/profiles/contrib/{$name}": ["type:drupal-profile"],
        "app/themes/contrib/{$name}": ["type:drupal-theme"],
        "drush/contrib/{$name}": ["type:drupal-drush"]
    }
}

Explaining:

  • /app folder: Drupal full installation.
  • /web folder: Will contain only symlinks of the assets files and PHP stub files (index.php, install.php, etc) from the /app folder.

Use composer require ... to install this Plugin on your project.

composer require drupal-composer/drupal-paranoia:~1

Done! The plugin and the new docroot are now installed.

Asset file types

The asset files are symlinked from /app to /web folder.

Default asset file types are provided by the plugin:

robots.txt
.htaccess
*.css
*.eot
*.ico
*.gif
*.jpeg
*.jpg
*.js
*.map
*.otf
*.png
*.svg
*.ttf
*.woff
*.woff2

To extend the list of assets file types you can use the asset-files config:

"extra": {
    "drupal-paranoia": {
        "app-dir": "app",
        "web-dir": "web",
        "asset-files": [
            "somefile.txt",
            "*.md"
        ]
    },
    "..."
}

If you need to modify the list you can use the post-drupal-set-asset-file-types event:

"scripts": {
    "post-drupal-set-asset-file-types": [
        "DrupalProject\\composer\\ScriptHandler::setAssetFileTypes"
    ],
    "..."
},
<?php

/**
 * @file
 * Contains \DrupalProject\composer\ScriptHandler.
 */

namespace DrupalProject\composer;

use DrupalComposer\DrupalParanoia\AssetFileTypesEvent;

class ScriptHandler {

  public static function setAssetFileTypes(AssetFileTypesEvent $event) {
    $asset_file_types = $event->getAssetFileTypes();
    // Do what you want with the asset file types.
    $event->setAssetFileTypes($asset_file_types);
  }

}

By the purpose of this plugin, the following files types are not allowed and if listed they will be ignored:

*.inc
*.install
*.module
*.phar
*.php
*.profile
*.theme

Exclude paths

With 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.

"extra": {
    "drupal-paranoia": {
        "app-dir": "app",
        "web-dir": "web",
        "excludes": [
            "core/install.php",
            "sites/simpletest"
        ]
    },
    "..."
}

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. You can exclude it via plugin as described above or via .htaccess rules.

Web server docroot

Change the document root config of your web server to point to /web folder.

Plugin events

This plugin fires the following named event during its execution process:

  • drupal-paranoia-post-command-run: Occurs after the command drupal:paranoia is executed.

Example of event subscriber

<?php

namespace MyVendor;

use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use DrupalComposer\DrupalParanoia\PluginEvents as DrupalParanoiaPluginEvents;

class MyClass implements PluginInterface, EventSubscriberInterface
{
    protected $composer;
    protected $io;

    public function activate(Composer $composer, IOInterface $io)
    {
        $this->composer = $composer;
        $this->io = $io;
    }

    public static function getSubscribedEvents()
    {
        return array(
            DrupalParanoiaPluginEvents::POST_COMMAND_RUN => 'postDrupalParanoiaCommand',
        );
    }

    public function postDrupalParanoiaCommand(CommandEvent $event) {
        // Add your custom action.
    }
}

Local development

Every time you install or update a Drupal package via Composer, the /web folder will be recreated.

composer require drupal/devel:~1.0
> drupal-paranoia: docroot folder has been rebuilt.

When working with themes, CSS and JS for example, it may be necessary to rebuild the folder manually to symlink the new assets.

composer drupal:paranoia

Public files

This plugin assumes that the public files folder exists at app/sites/<site>/files and symlinks web/sites/<site>/files -> ../../../app/sites/<site>/files.

drupal-paranoia's People

Contributors

brambaud avatar curryed avatar jkribeiro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

drupal-paranoia's Issues

Fail gracefully when $this->appDir./sites not found

During installation of this package, during early site builds where the 'sites' directory does not exist under $this->appDir/sites an InvalidArgumentException or a DirectoryNotFoundException will be thrown by the Symfony Finder package.

This can cause failed installations and also causes the drupal:paranoia command to fail.

These exceptions should be caught by the Installer and handled gracefully.

It is safe to assumes public files directories do not exist under $this->appDir if the sites directory is not found.

Expected behavior

Failing gracefully without attempting to link the non existent directories.

Actual behavior

Uncaught exception breaking package installation, plugin events and the provided commands.

Steps to reproduce

During install

  1. Create a project without a 'sites' directory inside the configured app-dir
  2. Require drupal-composer/drupal-paranoia
  3. See the uncaught exception in console output

When running as a command

  1. Create a project without a 'sites' directory inside the configured app-dir
  2. Require drupal-composer/drupal-paranoia
  3. Run composer drupal:paranoia
  4. See the uncaught exception in console output

Possible solution

#20

InvalidArgumentException

I have tested this on linux (Debian 9 - via a vagrant box) with composer 1.6.5 and a simple composer.json file based very hard on drupal-composer/drupal-project with the modifications as described in the configuration section of drupal-paranoia.

When running composer install I get this error:

- Installing drupal/core (8.5.4):
                                                                                                  
  [RuntimeException]                                                                              
  Could not delete app/core/.git/objects/pack/pack-8445df06ad688ca62d2204f2d5de6550340fb381.idx:  

And the installer has stopped after that. I dont know if it completed everything, but I do have a generated app and htdocs folder. The app folder contains the 'core' folder but no drupal scaffold files.
The htdocs folder looks like this:

htdocs
  core
    modules
      statistics
        statistics.php
      install.php
      rebuild.php
  sites
    default
  index.php

If I manually run

composer drupal:paranoia

I get the error:

[InvalidArgumentException]                                                      
$from (/vagrant/htdocs/sites/default/files) and $to () must be absolute paths.

As a test, I try to manually force the generation of the drupal scaffold files:

composer drupal-scaffold

but then I get:

[Symfony\Component\Console\Exception\CommandNotFoundException]  
  Command "drupal-scaffold" is not defined.                       
                                                                  
  Did you mean this?                                              
      drupal:scaffold

So instead I try:

composer drupal:scaffold

but then I get:

$ composer drupal:scaffold
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Composer\Installer\InstallationManager::getInstallPath() must implement interface Composer\Package\PackageInterface, null given, called in /v
agrant/vendor/drupal-composer/drupal-scaffold/src/Handler.php on line 270 and defined in phar:///usr/bin/composer.phar/src/Composer/Installer/InstallationManager.php:248
Stack trace:
#0 /vagrant/vendor/drupal-composer/drupal-scaffold/src/Handler.php(270): Composer\Installer\InstallationManager->getInstallPath(NULL)
#1 /vagrant/vendor/drupal-composer/drupal-scaffold/src/Handler.php(147): DrupalComposer\DrupalScaffold\Handler->getWebRoot()
#2 /vagrant/vendor/drupal-composer/drupal-scaffold/src/DrupalScaffoldCommand.php(31): DrupalComposer\DrupalScaffold\Handler->downloadScaffold()
#3 phar:///usr/bin/composer.phar/vendor/symfony/console/Command/Command.php(242): DrupalComposer\DrupalScaffold\DrupalScaffoldCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object
(Symfony\Component\Console\Output\ConsoleOutput))
#4 pha in phar:///usr/bin/composer.phar/src/Composer/Installer/InstallationManager.php on line 248

Fatal error: Uncaught TypeError: Argument 1 passed to Composer\Installer\InstallationManager::getInstallPath() must implement interface Composer\Package\PackageInterface, null given, called in /vagran
t/vendor/drupal-composer/drupal-scaffold/src/Handler.php on line 270 and defined in phar:///usr/bin/composer.phar/src/Composer/Installer/InstallationManager.php:248
Stack trace:
#0 /vagrant/vendor/drupal-composer/drupal-scaffold/src/Handler.php(270): Composer\Installer\InstallationManager->getInstallPath(NULL)
#1 /vagrant/vendor/drupal-composer/drupal-scaffold/src/Handler.php(147): DrupalComposer\DrupalScaffold\Handler->getWebRoot()
#2 /vagrant/vendor/drupal-composer/drupal-scaffold/src/DrupalScaffoldCommand.php(31): DrupalComposer\DrupalScaffold\Handler->downloadScaffold()
#3 phar:///usr/bin/composer.phar/vendor/symfony/console/Command/Command.php(242): DrupalComposer\DrupalScaffold\DrupalScaffoldCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object
(Symfony\Component\Console\Output\ConsoleOutput))
#4 pha in phar:///usr/bin/composer.phar/src/Composer/Installer/InstallationManager.php on line 248

This is my complete composer.json file:

{
    "name": "intracto/paranoiatest",
    "description": "Add a new blanco site",
    "type": "project",
    "authors": [
        {
            "name": "Joery Lemmens"
        }
    ],
    "require": {
        "composer/installers": "^1.0.21",
        "cweagans/composer-patches": "^1.6",
        "drupal-composer/drupal-paranoia": "~1",
        "drupal-composer/drupal-scaffold": "^2.5",
        "drupal/core": "^8.5",
        "drush/drush": "^9.2",
        "oomphinc/composer-installers-extender": "^1.1",
        "vlucas/phpdotenv": "^2.4",
        "webflo/drupal-finder": "^1.1",
        "webmozart/path-util": "^2.3"
    },
    "require-dev": {
        "webflo/drupal-core-require-dev": "~8.5.3"
    },
    "conflict": {
        "drupal/drupal": "*"
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "config": {
        "sort-packages": true,
        "discard-changes": true
    },
    "autoload": {
        "classmap": [
            "scripts/composer/ScriptHandler.php"
        ],
        "files": ["load.environment.php"]
    },
    "scripts": {
        "pre-install-cmd": [
            "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
        ],
        "pre-update-cmd": [
            "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
        ],
        "post-install-cmd": [
            "DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
        ],
        "post-update-cmd": [
            "DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
        ]
    },
    "extra": {
        "installer-paths": {
            "app/core": ["type:drupal-core"],
            "app/profiles/{$name}": ["type:drupal-profile"],
            "app/modules/contrib/{$name}": ["type:drupal-module"],
            "app/modules/custom/{$name}": ["type:drupal-custom-module"],
            "app/themes/contrib/{$name}": ["type:drupal-theme"],
            "app/themes/custom/{$name}": ["type:drupal-custom-theme"],
            "app/libraries/{$name}": ["type:drupal-library"],
            "drush/contrib/{$name}": ["type:drupal-drush"]
        },
        "drupal-scaffold": {
            "initial": {
                ".editorconfig": "../.editorconfig",
                ".gitattributes": "../.gitattributes"
            }
        },
        "patches-file": "composer.patches.json",
        "drupal-app-dir": "app",
        "drupal-web-dir": "htdocs"
    },

    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    ]
}

Windows support

Hi, I tested this out, but in the end the composer install ended with this error:

...
web.config (https://cgit.drupalcode.org/drupal/plain/web.config?h=8.5.4): Downloading (100%)
In Filesystem.php line 342:
$from (C:\websites\test\htdocs/sites/default/files) and $to () must be absolute paths.

The site seems to be partially working: after the composer install (after the message above) I pointed a new entry (site.test) in my host file to the drupal-web-dir (I used 'htdocs' instead of 'web') so I pointed site.test to C:\websites\test\htdocs. When I surf to site.test I do see the drupal install page, but there is no theming at all.

The resulted folder structure ofter the composer install looks like this:

testsite
  app
    core
    modules
    profiles
    sites
    themes
    (all the drupal scaffold files)
  htdocs
    core
      modules
        statistics
          statistics.php
      install.php
      rebuild.php
    sites
      default (this is completely empty)
  vendor
    (all vendor folders)
  composer.json
  composer.lock

Support for composer 2

Installing plugin with Composer 2 install failed.

➜  www.demo-site.com composer require drupal-composer/drupal-paranoia:~1

  [InvalidArgumentException]
  Package drupal-composer/drupal-paranoia at version ~1 has a PHP requirement incompatible with your PHP version, PHP extensions and Composer version

➜  www.demo-site.com php -v
PHP 7.3.9 (cli) (built: Feb 17 2020 12:46:14) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.9, Copyright (c) 1998-2018 Zend Technologies

➜  www.demo-site.com composer -V
Composer version 2.0.2 2020-10-25 23:03:59

Issue links

Expected behavior

Instal

Actual behavior

Error out

Steps to reproduce

  1. Install composer 2
  2. Install Drupal 8 or 9 using drupal-composer
  3. Apply the fixes to the compose.json file
  4. Install drupal paranoia using the composer command

Context

Possible solution

Adding support to composer 2 in the composer.json or creating a branch for composer 2

Missing robots.txt in the web directory

There is no symlink to the robots.txt in the web directory.

Is this the wanted behavior? If yes, why?

Expected behavior

A symlink to the robots.txt should exist in the web directory.

Actual behavior

There is no symlink to the robots.txt in the web directory.

Steps to reproduce

  1. Install and configure drupal-paranoia as explained in the documentation with a based drupal-composer/drupal-project project.
  2. Check the web directory: there is no symlink to the robots.txt

Possible solutions

  1. Add the robots.txt to the $assetFileTypes property of the \DrupalComposer\DrupalParanoia\Installer class.
  2. Add a configuration option like drupal-web-dir-extra-asset-files-types that will be merged with the $assetFileTypes property.
  3. Dispatch an event at the end of the \DrupalComposer\DrupalParanoia\Installer::install() method to let people extend as needed.

IMHO the event dispatcher is the best solution as it will be possible to extend the plugin.

I'll provide a PR when we'll agreed on what to do :)

Xdebug Support with Paranoia activated

Issue links

https://stackoverflow.com/questions/14629975/xdebug-in-netbeans-not-stopping-on-breakpoint-inside-file-in-web-roots-paren/16092457

Expected behavior

When setting a breakpoint with a given IDE (PHPStorm in this case) one is able to debug.

Actual behavior

The breakpoint is not detected, and complains about path mappings.

Steps to reproduce

  1. Run project with an xdebug enabled version of PHP (7.2 in this case)
  2. Require paranoia & configure
  3. Set a breakpoint in IDE
  4. Run code, no breakpoint detected.

I spent a couple hours trying every possible combination of path mappings with no success. Removed paranoia and everything immediately worked. I thought about having the ability to "disable" paranoia for development purposes, but I'm not sure how one would do this aside from removing it from the project which is undesirable.

Make files dir path configurable

In case of files dir have non-standart path, it should be possible to provide it for drupal-paranoia too.

And it can be located outside of drupal installation.

Support multisite.

Drupal Paranoia does not support multisite.

Expected behavior

The folder files should be symlinked.

web/sites
├── site1
│   └── files -> ../../../src/sites/site1/files
├── site2
│   └── files -> ../../../src/sites/site2/files
└── default
    └── files -> ../../../src/sites/default/files

Actual behavior

The folder files is not symlinked. But the content is.
So each time you run composer drupal:paranoia you lost all your files.

web/sites
├── site1
│   └── files
├── site2
│   └── files
└── default
    └── files -> ../../../src/sites/default/files

Steps to reproduce

  1. Configure Drupal has a multisite: you can follow https://www.drupal.org/docs/8/multisite
  2. Run composer drupal:paranoia

Possible solution

I didn't check for a solution right now but I'll open a PR shortly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.