Follow-up to #3308369: Block access to yarn.lock and package.json

Problem/Motivation

#2332029: Add test coverage for .htaccess rules added test coverage to ensure that files with certain extensions cannot be accessed through the browser. However, extending this coverage was missed in #3308369: Block access to yarn.lock and package.json.
It turns out that not only tests for the two new files are missing, but the functionality for yarn.lock seems broken.

Proposed resolution

Fix missing coverage for yarn.lock and package.json.
Fix functionality for yarn.lock.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Eric_A created an issue. See original summary.

Eric_A’s picture

Issue summary: View changes
Eric_A’s picture

Eric_A’s picture

Assigned: Eric_A » Unassigned
Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 3: htaccess-test-yarnlock-packagejson-3327115-3.patch, failed testing. View results

Eric_A’s picture

Status: Needs work » Needs review
FileSize
1.13 KB

Oops...

Status: Needs review » Needs work

The last submitted patch, 6: htaccess-test-yarnlock-packagejson-3327115-6.patch, failed testing. View results

Eric_A’s picture

Title: Add test coverage for .htaccess rules for yarn.lock and package.json » .htaccess rule for yarn.lock is broken
Assigned: Unassigned » Eric_A
Category: Task » Bug report
Priority: Normal » Major

Looks like the functionality for yarn.lock is in fact broken. Would be a bit embarrassing to enhance the .htaccess in 9.5.0 and 10.0.0 and having to then fix it immediately in 9.5.1 and 10.0.1, so escalating priority to major.

Testing Drupal\Tests\system\Functional\System\HtaccessTest
F. 2 / 2 (100%)

Time: 00:14.187, Memory: 4.00 MB

There was 1 failure:

1) Drupal\Tests\system\Functional\System\HtaccessTest::testFileAccess
Response code to core/modules/system/tests/fixtures/HtaccessTest/yarn.lock should be 403
Failed asserting that 200 matches expected 403.

/var/www/html/vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php:96
/var/www/html/core/modules/system/tests/src/Functional/System/HtaccessTest.php:152
/var/www/html/core/modules/system/tests/src/Functional/System/HtaccessTest.php:110
/var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php:728

FAILURES!
Tests: 2, Assertions: 76, Failures: 1.

Eric_A’s picture

Title: .htaccess rule for yarn.lock is broken » .htaccess rules broken since yarn.lock got added
Assigned: Eric_A » Unassigned
Status: Needs work » Needs review
FileSize
1.13 KB
2.06 KB

I'm really not into regex, but the patch attached seems to improve the situation.
It seems the original patch displaced a $.

Status: Needs review » Needs work

The last submitted patch, 9: htaccess-test-yarnlock-packagejson-3327115-9.patch, failed testing. View results

Eric_A’s picture

Right, this is drupal/drupal, not drupal/core.
I had changed the scaffold htaccess, but forgot to change the root .htaccess.

Eric_A’s picture

Issue summary: View changes
longwave’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for spotting this and adding the test. I agree that the regex is malformed and the $ and | have accidentally been added the wrong way round.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

I think we need a few more tweaks to be consistent with the ^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$

In fact I think really we should be putting both of these in that part of the rule for consistency... so it should be:
^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config|yarn\.lock|package\.json)$

There are two more things that would be fixed by this... the dot would be escaped and therefore packageAjson would not be matched. But also the path would have to be exactly yarn.lock or package.json.

alexpott’s picture

Here's the fix as per #14

xjm’s picture

xjm’s picture

Issue summary: View changes
FileSize
63 bytes
63 bytes
231.36 KB
201.05 KB

I thought it might be easier to review the total change to the file from before #3308369: Block access to yarn.lock and package.json, so this is what I did:

  1. Committed the approaches from #11 and #15 to local branches from 10.1.x named "11" and 15".

  2. Compared changes in the relevant files in those branches to 2233484^, which is the last commit before #3308369: Block access to yarn.lock and package.json, i.e.:

    git diff --color-words="."  2233484^ -- core/assets/scaffold/files/htaccess 
    git diff --color-words="."  2233484^ -- .htaccess
    

    .

Here's images of that to compare the two versions in color more easily:

#11

Scaffold file

.htaccess

Only local images are allowed.

#15

Scaffold file

.htaccess

xjm’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
236.77 KB
209.27 KB

Oh and since I can, here's the "colorized interdiff" for #15.

I consider this RTBC. If someone else can review and +1, I am also willing to commit this once the tests finish (despite the code freeze) to fix a bad regression for 10.0.0.

effulgentsia’s picture

#15 looks good to me. RTBC+1.

pandaski’s picture

Did a manual local test using Apache for testing htaccess.

The following are tested:

D10.0.x and D9.5.x

9.5.0-rc2 and 10.0.0-rc3

Before the patch:

both core/package.json and core/yarn.lock files are accessible with 200.

After the patch (via curl https://www.drupal.org/files/issues/2022-12-14/3327115-15.patch | git apply):

both core/package.json and core/yarn.lock files are 403 Forbidden

  • xjm committed efb94e3a on 10.1.x
    Issue #3327115 by Eric_A, alexpott, xjm, longwave, pandaski: .htaccess...

  • xjm committed ec92f543 on 10.0.x
    Issue #3327115 by Eric_A, alexpott, xjm, longwave, pandaski: .htaccess...

  • xjm committed 6942a389 on 9.5.x
    Issue #3327115 by Eric_A, alexpott, xjm, longwave, pandaski: .htaccess...
xjm’s picture

Thanks @pandaski for the manual test!

The test failure in https://www.drupal.org/pift-ci-job/2545386 is a known random that happens about once a week -- in fact it also just today happened in HEAD in https://www.drupal.org/pift-ci-job/2544788. So that is not blocking.

Committed to 10.1.x, and cherry-picked to 10.0.x and 9.5.x. Thanks!

I don't think this hotfix needs to be in the release notes; we can just use the release note @longwave added to the original issue.

xjm’s picture

Status: Reviewed & tested by the community » Fixed
Eric_A’s picture

Thanks!

Realizing just now that there's also web.config. That one looks much less broken. Maybe it needs just the escaping of the dots.
To be honest I didn't really look into that file. Which seems to be a common pattern for many. :-)

xjm’s picture

@Eric_A I may also forget about it 50% of the time. Including when committing this issue. However, looking at its current code, I think it's OK:

          <match url="\.(engine|inc|install|module|profile|po|sh|.*sql|theme|tw\
ig|tpl(\.php)?|xtmpl|yml|svn-base)$|^(code-style\.pl|Entries.*|Repository|Root|\
Tag|Template|all-wcprops|entries|format|composer\.(json|lock)|\.htaccess|yarn.l\
ock|package.json)$" />

If there's anything not-right about it, let's open a followup? Thanks!

effulgentsia’s picture

FWIW, I did check web.config before writing #19, and thought it was okay since it already included yarn.lock and package.json. However, I did miss that the dots weren't escaped there. I don't believe that's a security problem: it just means that web.config is denying access to file names (e.g., yarn-lock) that it doesn't have to deny access to. But a follow-up to make that consistent with what's in .htaccess is worth doing.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

O&#039;Briat’s picture

This should be send to nginx to update their page about drupal:https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/ (which is by the way an outdated doc).

Here's the fix for nginx:

   # Protect files and directories from prying eyes.
    location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|composer\.(json|lock)|web\.config|yarn\.lock|package\.json$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ {
        deny all;
        return 404;
    }

If I understand it correctly apache match only the last part of the url/filepath (most right directory or filename) but nginx match the whole path.

So in order to deny all composer.json, composer.lock, web.config, yarn.lock or package.json, I move this part to specific part of the regex:


-    location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ {
+    location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|composer\.(json|lock)|web\.config|yarn\.lock|package\.json$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ {

Could someone test and validate this modification?

As I don't understand what Entries.*|Repository|Root|Tag|Template is referring too, it would be nice to document this part.