Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
http://seclists.org/fulldisclosure/2015/Oct/42
While we block vendor with .htaccess, that's not foolproof
Proposed resolution
Delete vendor test code during composer install and update by implementing additional composer script hooks.
Code to be deleted is based on a list maintained in \Drupal\Core\Composer\Composer
Remaining tasks
final review & commit
re-install all dependencies with composer and commit with test code removed
User interface changes
n/a
API changes
n/a
Comment | File | Size | Author |
---|---|---|---|
#76 | 2585165.74.patch | 6.28 MB | alexpott |
#73 | 2585165-3-72.patch | 6.27 MB | alexpott |
| |||
#61 | increment-important-bits.txt | 580 bytes | pwolanin |
#61 | 2585165-important-bits-61.patch | 5.96 KB | pwolanin |
#56 | increment-important-bits.txt | 4 KB | pwolanin |
Comments
Comment #2
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commentedHere's a start on a new composer script to make sure certain dirs are absent.
I think this is useful regardless of anything else as a backstop since users of Drupal may install the dev dependencies on a live site without realizing that's potentially a security problem.
Comment #3
dawehnerMy personal recommendation would be:
* Drop /vendor entirely
* Add a hook_requirement when dev dependencies are installed which gives a warning
* Patch composer to be able to configure the default behaviour for `composer install` to be the --no-dev variant
Comment #4
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commentedHere's a patch that happens when I run composer install and locally commit the changes with patch #2 applied.
Comment #5
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commented@dawhener I think you over-estimate the competence of most people to use composer at all - an easy solution here is to just put the no-dev version into git as alexpott suggested, but I think we should also rm known problem test dirs as per patch #2 so people using composer themselves get extra protection, including because they will need to run it for module installations
Comment #6
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commentedwhitespace fix in core/lib/Drupal/Core/Composer/Composer.php
Comment #7
alexpottWhen we original .htaccess fix we accepted that it was not perfect. For a start we should also generate a web.config file too for anyone using IIS. And we should have a note for users of other webservers and we should have note of how to move vendor out of webroot.
But given that these limitations were discussed when we did the original fix - is this really critical?
I think we should remove the require-dev dependencies from Composer - this represents about 5Mb of code uncompressed that just does not need to be there for 99.9% of sites. Yes it will mean that developers will have to get in the habit of installing composer and doing composer install --dev if they want to develop on Drupal but this is how other projects work. But even this is not really enough. Until vendor is outside of webroot we are putting our security in our dependencies hands.
Comment #8
gregglesI think this is admirable and ideal, but not a good thing to intermix in this issue.
The web.config files that we ship get very little attention (e.g. #1543392: Add a web.config to the several directories similar to the .htaccess file is years old, currently 7 months stale and it is for defense-in-depth for another security issue).
The typical Nginx configuration is not vulnerable to this issue since executable files are usually listed explicitly and it's unlikely someone would explicitly list this file.
Comment #9
larowlanMy opinion, restructure so we have a web folder and keep vendor outside it. That's how every other composer based project gets around this. Fairly big at this stage, but everything else is brittle
Comment #10
alexpottJoomla doesn't https://github.com/joomla/joomla-cms/tree/staging/libraries/vendor ... just saying.
Comment #11
chx CreditAttribution: chx commentedWhat about scanning for test and tests case insensitive in vendor/ and nuke it?
Comment #12
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commented@larowlan - we should make sure that's possible, but it would be very disruptive (and non-experts would not understand) if Drupal shipped with a docroot + vendor directory. It's too late for 8.x for that change, we debated it a few months ago when this first came up.
So, the patch here is an easy 1st pass fix. We can add more or follow up with chx's suggesiton.
Comment #13
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commented@alexpott - Drupal has a *known* XSS vector in the vendor test code. I don't see the same in joomla
Comment #14
alexpottSo let's go for the general approach but I think we need to fix the implementation so that it fails if it does not remove the directories. This is protect us from thinking we're secure if mink moves its tests.
We should use the
postPackageInstall
andpostPackageUpdate
events and if the package matches remove the specific folders and if we can't remove the folder it should throw and exception or something and fail hard.Comment #15
alexpottAlso can we get a followup which is also an RC target to remove all tests using this script from the dependencies since this will greatly reduce the tarball size and the amount of space a D8 instance takes.
Comment #16
alexpottSomething like this.
Comment #17
alexpottRelevant discussion on composer https://github.com/composer/composer/issues/4438
Comment #18
alexpottHere's everything that should be removed... a 6mb patch! This is a significant saving for anyone still using ftp or the likes. It should also make the D8 package significantly smaller too.
Comment #20
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commented#16 looks reasonable. Let's get some version of the that basic cleanup in asap.
Comment #21
MixologicThis is blocked by #2573687: Setup GitHub OAuth for Composer on Testbots - the testbots will potentially run into githubs API limits, so that needs to be solved before we can ask the testbots to hit github.
Comment #22
alexpottThe diff needs to be made with
--binary
.@Mixologic thanks for this information. The current solution this issue is looking at is removing all the unnecessary test code from dependencies - that does not required drupalci to do anything funky with composer / github.
Comment #23
chx CreditAttribution: chx commentedMy heart sings at this patch. So. much. removal!
Comment #25
tarekdj CreditAttribution: tarekdj as a volunteer commentedError running composer update:
Fatal error: Call to undefined method Composer\DependencyResolver\Operation\UpdateOperation::getPackage()
Comment #27
tarekdj CreditAttribution: tarekdj as a volunteer commentedOops! sent the wrong patch! The right one one the way
Comment #28
tarekdj CreditAttribution: tarekdj as a volunteer commentedInterdiff-22-25:
Comment #29
tarekdj CreditAttribution: tarekdj as a volunteer commentedComment #31
dawehnerIt is a bit sad that
/vendor/behat/mink/driver-testsuite
is removed as it has quite some good examples, but for sure this is not at all a good reason to keep it.Comment #33
geertvd CreditAttribution: geertvd at XIO commentedNow this is failing on
composer install
.So apparently we need to use
$package = $event->getOperation()->getPackage()
for install and$package = $event->getOperation()->getTargetPackage()
for updateComment #35
geertvd CreditAttribution: geertvd at XIO commentedright,
--binary
Comment #36
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commentedActually - reconsidering one detail here.
We do NOT want to throw an exception since that means anyone tying to rebuild with --no-dev (including ourselves) will have that fail until additional core patches are applied.
Comment #37
alexpott@pwolanin did you actually test that? I think not. The advantage of the change I made in #16 is that this fires after each package is installed or updated. Not after everything. I've tested this and it works as expected.
Comment #41
tarekdj CreditAttribution: tarekdj as a volunteer commented@alexpott after running a composer update the following files are updated:
Should we consider those changes in the patch ?
Comment #42
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commented@tarkdj - I think you should be running composer install, not composer update?
@alexpott oh I see :
static::$packageToCleanup[$package_name]
Clever, yes, that should be fine.
Comment #43
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commentedFor patches here is would be helpful to just see the "important bits" as a patch. The committers can run composer install themselves and commit the test files removal without having a patch for it.
Also - I'm not sure about the logic of assuming something is a directory if it's not a file - we could have symlinks?
Comment #44
pwolanin CreditAttribution: pwolanin as a volunteer commentedHere's a patch for the important bits. It handles symlinks and tracks success of each operation and throws an exception on failure.
To see it work locally I did:
This works except we seem to have something writing out a new .gitignore and failing to fully remove vendor/mikey179/vfsStream/src/test
Comment #45
pwolanin CreditAttribution: pwolanin as a volunteer commentedThe package name and the target dir may not be equal.
Fixing.
Comment #46
pwolanin CreditAttribution: pwolanin as a volunteer commentedWeird - seems the package name is lower case, but the directory name is mixed case, and
$package->getTargetDir()
is empty. I'm confused about how this is working.Amusingly, since OSX HFS is case-insensitive, the change to the Composer.php file patch works as desired on OSX HFS, but likely not on GNU/Linux ext3, xfs, etc.
Fixing also the package name to lower-case in our composer file might be the most consistent fix. Needs some testing on different machines, and this may end up appearing to be a move of files from mikey179/vfsStream to mikey179/vfsstream
Comment #47
dawehnerThe package is uppercase, see https://packagist.org/packages/mikey179/vfsStream and https://github.com/mikey179/vfsStream/blob/master/composer.json
Comment #48
pwolanin CreditAttribution: pwolanin as a volunteer commented@dawehner - composer seems to disagree based on the output of
$package_name = $package->getName();
Comment #49
pwolanin CreditAttribution: pwolanin as a volunteer commentedIt's looks like 1) case doesn't matter, but 2) the package name should be lower case 3) internally to composer it's converted to lower case.
re: #3 see: https://github.com/composer/composer/blob/master/src/Composer/Package/Ba...
other info:
https://github.com/composer/packagist/issues/186
"Package names are now required to be lowercase."
And: https://getcomposer.org/doc/02-libraries.md
"While package names are case insensitive, the convention is all lowercase and dashes for word separation."
Note also that github seems to be case insensitive: https://github.com/mikey179/vfsstream/blob/master/composer.json works and git clone https://github.com/mikey179/vfsstream.git works.
Comment #50
dawehnerWell, beside that, isn't it still totally unimportant for this issue? Given that composer uses lowercase internally, everything should be alright. vfsstream has a gazillion of installations, I doubt anyone is not using some case sensitive filesystem. I'm sure that alexpott uses a proper filesystem for example.
Comment #51
alexpottYep I use a case sensitive file system.
Comment #52
dawehnerSo let's please revert all those out of scope changes, please.
Comment #53
gregglesI would say that case insensitive file systems are pretty common. It's the default type for all Mac which is a common developer platform and I *believe* many (most?) Windows machines are also case insensitive.
I don't know if that's worth including code related to case sensitivity or not, just noting the discrepancy with the statements in #50.
Comment #54
pwolanin CreditAttribution: pwolanin as a volunteer commented@dawhenr - I don't understand your comment. We need to list the package name and maybe more important the git URL as lower case in our composer.json in order to install the package in the (lower case) location that the composer Package class says it will be.
Comment #55
pwolanin CreditAttribution: pwolanin as a volunteer commentedDiscussed with daweher in IRC - he didn't realize that in the current situation we are not finding the array element at all because the Package class lowercases the name, so I think he's ok with the composer.json change as an acceptable solution.
Comment #56
pwolanin CreditAttribution: pwolanin as a volunteer commentedHere's an alternative to search over the array keys. Seems to work for me and hopefully dawehner will like it better.
Comment #57
dawehnerThank you @pwolanin.
nitpick, could be typehinted with string
Comment #61
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commentedsure, easy cleanup
Comment #62
catchWhen we do #1475510: Remove external dependencies from the core repo and let Composer manage the dependencies instead these would no longer get packaged at all (assuming we keep require-dev out of tarballs which I think we should).
While this looks OK for a quick fix, what happens with these scripts if the library doesn't get installed at all? Also this looks like we'd need to manually maintain the list of libraries, for example if a library gets split into extra dependencies upstream (which happened recently with Zend Feed). Could some of the listing be parsed from composer itself?
Comment #63
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commented@catch. The biggest problem would be solved if we skip the dev dependencies in git and the tarball.
See #37 we only check items that are installed or updated, so there is no problem if we don't have one of the packages in the list.
This patch is not a work of art but it would be good enough to get us to release if we do nothing else then commit this and then re-install the deps and commit them with tests removed. Yes, the list needs to be maintained, but it also gives us control to not remove some tests if e.g. we need to leverage them for core tests.
Comment #64
dawehnerJust skipping the dev dependencies in git is not a solution, people who want to run tests otherwise would need to composer install and then ignore some of the changes in
/vendor
Comment #65
pwolanin CreditAttribution: pwolanin as a volunteer and at Acquia commented@dawehner - yes, you are right. It would still be likely that sites would pull in the dev dependencies and then deploy with the vulnerable code, which is why I like this patch as a safeguard.
Comment #66
Mile23If you are managing the dependencies with composer, then you can say
composer install --no-dev
in your deployment process. Then you don't have mink at all.If you're expecting to run the tests packaged with e.g. mink, then with the current patch you need to say
composer install --prefer-source --no-scripts
, which would leave the tests, but also fail to add all our htaccess and special-case autoload dumping. So then you need to docomposer run-script pre-autoload-dump
andcomposer run-script post-autoload-dump
.I vote for:
composer install --no-dev
on the packaging script for the tarball (with an unfortunate side effect of being unable to run PHPUnit).vendor/
directory from the repo. #1475510: Remove external dependencies from the core repo and let Composer manage the dependencies insteadI just did a
composer install --no-dev
and tried to run the phpunit_example tests in the simpletest UI. Result: WSOD. So some work is needed there before packaging the tarball without dev.Comment #67
MixologicI think you mean build with --dev
Comment #68
Mile23Nope. :-) See point 2. We'd need coverage for
--no-dev
.Comment #69
Mixologicso adding a testbot step = run all the tests with both --dev and --no-dev? The testbots do not use packaged tarballs.
Comment #70
alexpott@Mile23
Clone mink - don't clone Drupal and run any version of Drupal install.
Comment #71
alexpott@Mixologic for obvious reasons testbot is only going to work with
--dev
Comment #72
Mile23Agreed.
Well, no. :-) We still have SimpleTest and it still works part-way for a user driving the UI. It will also work with run-tests.sh. Try it:
Simpletest should discover all the tests it can run without phpunit installed, which it currently doesn't do (it assumes phpunit is around).
Then the tarball can ship with --no-dev, so at least the security concern is gone there.
If someone wants to deploy with the repo, the onus is on them to do --no-dev for production, which they're used to doing anyway if they know composer at all.
But: This means a new behavior for the tarball which needs to be tested.
The only way to test that is with another testbot step that builds with --no-dev.
Comment #73
alexpott@Mile23 the fact that Simpletest UI does not check whether of not PHPUnit exists is not relevant for this issue since this issue does not break it doing
--no-dev
breaks it.Here's the full patch to commit.
Comment #74
Mile236 MB patch?
Right. It's relevant to my solution which is to build with --no-dev which exists to solve the problem of security holes in your test frameworks.
Comment #76
alexpott@Mile23 building with
--no-dev
does not exist to fix security flaws in anything. Storing vendor outside of webroot should be recommended secure Drupal practice because even this fix, which deletes more test code than a--no-dev
build, does nothing if there is security issue with direct access to a non testfile and the .htaccess file in vendor is not working.The *real* issue is that composer should distinguish between distribution and source and distribution should only contain what is actually necessary for runtime and nothing else.
And yes this deletes 6mb of unnecessary test code. I just forgot the --binary switch again.
Comment #77
catchCommitted/pushed to 8.0.x, thanks!
Comment #80
barryvdh CreditAttribution: barryvdh commentedIt is possible to distinguish source and distributed packages. This can be done with .gitattributes using the export-ignore feature. https://www.reddit.com/r/PHP/comments/2jzp6k/i_dont_need_your_tests_in_m...
This can be fixed upstream in Mink, for example see https://github.com/minkphp/Mink/pull/691
This is also what happened in one of the other packages; https://github.com/paragonie/random_compat/commit/e1ddb31788f16f4527074f...
That is a good thing, but broke your Composer plugin. It should not break when a folder is moved/renamed or exluded from dists. For example, when installing from source (--prefer-source) the tests folder will exist, but when using --prefer-dist, it's gone.
The packaged distribution should be dist, but when people are updating composer on their own (and hit API limits or something), you should take both cases into account.
Comment #81
bojanz CreditAttribution: bojanz at Centarro commentedImportant followup: #2664274: Combination of --prefer-dist and .gitattributes confuses our vendor test cleanup (regarding #80).