diff --git a/doc/06-config.md b/doc/06-config.md index 2a86394e8..2b32a6dfa 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -265,4 +265,10 @@ Example: } ``` +## htaccess-protect + +Defaults to `true`. If set to `false`, Composer will not create `.htaccess` files in the composer home, cache, and data directories. + +Previously, Composer unconditionally created these files to mitigate the potential for someone to expose these directories under their Apache document root. The default value of this option preserves the previous behavior. + ← [Repositories](05-repositories.md) | [Community](07-community.md) → diff --git a/res/composer-schema.json b/res/composer-schema.json index b21abf214..49929bdb8 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -274,6 +274,10 @@ "archive-dir": { "type": "string", "description": "The default archive path when not provided on cli, defaults to \".\"." + }, + "htaccess-protect": { + "type": "boolean", + "description": "Defaults to true. If set to false, Composer will not create .htaccess files in the composer home, cache, and data directories." } } }, diff --git a/src/Composer/Config.php b/src/Composer/Config.php index d51fe6905..a7fca2988 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -60,6 +60,7 @@ class Config 'platform' => array(), 'archive-format' => 'tar', 'archive-dir' => '.', + 'htaccess-protect' => true, // valid keys without defaults (auth config stuff): // bitbucket-oauth // github-oauth @@ -215,6 +216,7 @@ class Config case 'cache-vcs-dir': case 'cafile': case 'capath': + case 'htaccess-protect': // convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config $env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_')); diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index a0ca79785..42be783c8 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -164,16 +164,19 @@ class Factory 'data-dir' => self::getDataDir($home), ))); - // Protect directory against web access. Since HOME could be - // the www-data's user home and be web-accessible it is a - // potential security risk - $dirs = array($config->get('home'), $config->get('cache-dir'), $config->get('data-dir')); - foreach ($dirs as $dir) { - if (!file_exists($dir . '/.htaccess')) { - if (!is_dir($dir)) { - Silencer::call('mkdir', $dir, 0777, true); + $htaccessProtect = (bool) $config->get('htaccess-protect'); + if ($htaccessProtect) { + // Protect directory against web access. Since HOME could be + // the www-data's user home and be web-accessible it is a + // potential security risk + $dirs = array($config->get('home'), $config->get('cache-dir'), $config->get('data-dir')); + foreach ($dirs as $dir) { + if (!file_exists($dir . '/.htaccess')) { + if (!is_dir($dir)) { + Silencer::call('mkdir', $dir, 0777, true); + } + Silencer::call('file_put_contents', $dir . '/.htaccess', 'Deny from all'); } - Silencer::call('file_put_contents', $dir . '/.htaccess', 'Deny from all'); } } diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 79c124fc5..890ec19fb 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -310,4 +310,12 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->assertEquals(0, $config->get('process-timeout')); putenv('COMPOSER_PROCESS_TIMEOUT'); } + + public function testHtaccessProtect() + { + putenv('COMPOSER_HTACCESS_PROTECT=0'); + $config = new Config(true); + $this->assertEquals(0, $config->get('htaccess-protect')); + putenv('COMPOSER_HTACCESS_PROTECT'); + } }