If you try to pass a file entity as a validated context in a context-aware plugin, it will fail. I've tracked this down to PrimitiveTypeConstraintValidator::validate() under the following conditions:

$typed_data = Drupal\Core\TypedData\Plugin\DataType\Uri::__set_state(array(
   'value' => 'public://field/image/Photo on 4-28-14 at 12.01 PM.jpg',
   'definition' => 
  Drupal\Core\TypedData\DataDefinition::__set_state(array(
     'definition' => 
    array (
      'type' => 'uri',
      'label' => 'URI value',
    ),
  )),
   'name' => 'value',
   ...
))
$value = 'public://field/image/Photo on 4-28-14 at 12.01 PM.jpg';

Where this specifically fails in PrimitiveTypeConstraintValidator::validate():

    if ($typed_data instanceof UriInterface && filter_var($value, FILTER_VALIDATE_URL) === FALSE) {
      $valid = FALSE;
    }

This is due to the fact that the file URI has spaces in it. If this were URL-encoded then it would pass.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Dave Reid’s picture

Issue summary: View changes
Dave Reid’s picture

Issue tags: +Needs tests
Dave Reid’s picture

Issue tags: +Media Initiative
cbr’s picture

Assigned: Unassigned » cbr
Status: Active » Needs work

Will start to work on this.

slashrsm’s picture

This will also fail if uri contains non-ASCII characters. Do we wanto to fix that also?

See: http://php.net/manual/en/filter.filters.validate.php (FILTER_VALIDATE_URL row)

cbr’s picture

Replaced filter validation url with a regular expression match.

Reference to regex: http://www.ietf.org/rfc/rfc3986.txt (Page 50)

cbr’s picture

Berdir’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
    @@ -49,7 +49,7 @@ public function validate($value, Constraint $constraint) {
    -    if ($typed_data instanceof UriInterface && filter_var($value, FILTER_VALIDATE_URL) === FALSE) {
    +    if ($typed_data instanceof UriInterface && preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $value) == FALSE) {
    

    I suggest you put the actual regex string in a constant on this class (const URI_VALIDATION_REGEX = ... and add the reference to the RFC standard to the docblock.

  2. +++ b/core/modules/system/src/Tests/TypedData/TypedDataTest.php
    @@ -236,8 +236,8 @@ public function testGetAndSet() {
         $this->assertNull($typed_data->getValue(), 'URI wrapper is null-able.');
         $this->assertEqual($typed_data->validate()->count(), 0);
    -    $typed_data->setValue('invalid');
    -    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
    +    $typed_data->setValue('public://field/image/Photo on 4-28-14 at 12.01 PM水.jpg');
    +    $this->assertEqual($typed_data->validate()->count(), 0, 'Validation detected invalid value.');
    

    Now you *replaced* the invalid validation, don't do that, add yours instead, we still want to make sure that invalid strings don't validate.

    As we replace the API function with our own, I think we should considerably extend our test coverage here to test a list of valid and invalid strings. You could set up an array of $uri => 1/2 and then loop over that and validate it, making sure the value is as expected.

cbr’s picture

Well I did my best but cannot completely figured out how the validation should be handled.

What I did do is write a test with a list of valid and invalid URI's according to several references.
For the test see my TEST-ONLY patch.

If you'd like the test including some regular expressions I tried (but which failed) then download my full patch file.

What I do notice is that FILTER_VALIDATE_URL comes quite close to a solution, there are only a few problems with FILTER_VALIDATE_INT it does not validate correctly on white spaces in the filename, some special characters in filename and IDN characters in domain names.

I'll follow this issue and will help wherever I can.

For my references check out:
http://www.ietf.org/rfc/rfc3986.txt
http://en.wikipedia.org/wiki/Filename
http://www.domainnameshop.com/faq.cgi?id=8&session=106ee5e67d523298
http://en.wikipedia.org/wiki/Filename

Status: Needs review » Needs work
slashrsm’s picture

Thank you @ChrisjuhB. Having an extensive test suite for this will help us a lot!

I went through http://www.ietf.org/rfc/rfc3986.txt a bit and as far as I understand it it does not allow URIs with spaces at all. It says spaces (and other stuff like unicode, ...) should always be %-encoded. This kind of makes sense. Let's say we allow spaces in URIs. That would also make "http://exam ple.com" valid, which is obviously wrong. Is it possible that we're going a bit against the specs when it comes to handling URIs?

Taking what I wrote above into consideration it seems that the only option we have here is %-encoding URIs before we send then to filter_var.

Any thoughts?

Berdir’s picture

Status: Needs work » Needs review
FileSize
677 bytes

Well, we're calling it uri, but doesn't that simply mean that $file->uri is *not* a valid URI? What if we just don't claim it is? Should possibly have custom validation instead that does things like checking for matching stream wrapper and if possible, check for existence of that file?

Changing the validation would affect validation of URI's that actually *are* valid, we can't just change it for $file->uri.

slashrsm’s picture

Yes, this probably makes sense. I wouldn't check if file exists though as that might bring to much performance overhead.

Dave Reid’s picture

We should have *some* sort of validation for the uri property: is it using a valid stream wrapper, does a file exist at that location, etc?

Status: Needs review » Needs work

The last submitted patch, 16: it-is-no-uri-2278073-16.patch, failed testing.

garphy’s picture

Assigned: Unassigned » garphy
garphy’s picture

Assigned: garphy » Unassigned
Status: Needs work » Needs review
FileSize
685 bytes

Rerolled the patch.

garphy’s picture

Taking another way to fix this : sticking with "uri" datatype and perform URI validation through file_create_url().

Dave Reid’s picture

Garphy and I discussed several different options that we thought about trying:

1. Attempting to decode and then re-encode the URI before validation.
2. Creating a new TypedData type for an 'Drupal URI' that would perform different validation.
3. Using file_create_url()
4. Using Symfony's Url Constraint validation, which uses a regex

I think this solution offers us the best path forward, to make sure file entities and the various stream wrappers provided by Drupal or contrib would be compatible with the validation, which this does, since it is assumed that the file_create_url() for each stream wrapper is responsible for provided a valid fully-qualified URI. We will file a follow-up to replace the filter_var() validation with something better, likely with the Symfony validation which seems to use a much better regex, but we would still need the file_create_url() part, so this is a positive change anyway. It might also make sense for a different typed data type since this file_create_url() call really should happen higher in the typed data value itself. Both of those could be left in our follow-up.

jhedstrom’s picture

Status: Needs review » Needs work
Issue tags: -Needs tests +Needs reroll

Needs a reroll.

Also removing the 'needs tests' tag as the patch above has a test (although it would be nice to upload separately to illustrate the current failing behavior).

cafuego’s picture

Assigned: Unassigned » cafuego
jonathan_hunt’s picture

Assigned: cafuego » jonathan_hunt

DrupalSouth code sprint "reroll" to come

jonathan_hunt’s picture

FileSize
870 bytes

Re-rolled

jonathan_hunt’s picture

Assigned: jonathan_hunt » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll +DrupalSouth
garphy’s picture

It seems that changes introduced in #2412509: Change LinkItem.uri to type 'uri' rather than 'string' and introduce user-path: scheme fixed the original issue as the last patch only adds the related unit test which passes.

I'm not setting this RTBC as I contributed to the issue several months ago.

cafuego’s picture

Status: Needs review » Reviewed & tested by the community

RTBCing, I had alook at this with Jonathan yesterday. Original issue fixed, but no test coverage; this patch adds it.

slashrsm’s picture

Nice. +1 for RTBC

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

This is only adding test code therefore permitted under beta evaluation rules. Committed 3363c81 and pushed to 8.0.x. Thanks!

diff --git a/core/modules/system/src/Tests/TypedData/TypedDataTest.php b/core/modules/system/src/Tests/TypedData/TypedDataTest.php
index 649ff6f..9cfeb8d 100644
--- a/core/modules/system/src/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/src/Tests/TypedData/TypedDataTest.php
@@ -240,7 +240,7 @@ public function testGetAndSet() {
     $typed_data->setValue('invalid');
     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
     $typed_data->setValue('public://field/image/Photo on 4-28-14 at 12.01 PM.jpg');
-    $this->assertEqual($typed_data->validate()->count(), 0, 'Filename with spaces is valid (see https://www.drupal.org/node/2278073).');
+    $this->assertEqual($typed_data->validate()->count(), 0, 'Filename with spaces is valid.');
 
     // Generate some files that will be used to test the binary data type.
     $files = array();

Adding a link to the issue is not necessary. Fixed on commit.

  • alexpott committed 3363c81 on 8.0.x
    Issue #2278073 by cbr, garphy, jonathan_hunt, Berdir: Files with spaces...

Status: Fixed » Closed (fixed)

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