Skip to content

dom-crawler + browser-kit empty file upload error "Path cannot be empty" #49014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rudiedirkx opened this issue Jan 17, 2023 · 17 comments
Closed

Comments

@rudiedirkx
Copy link

rudiedirkx commented Jan 17, 2023

Symfony version(s) affected

6.0.11

Description

Using Symfony's dom-crawler and browser-kit (from Goutte) I load a simple page with a simple form: https://webblocks.nl/_form1.html and submit it without giving a file. I would expect an empty file to be uploaded (like a real browser does), or no file to be uploaded at all, but instead browser-kit crashes because it tries to read the no-file like it's a real file.

I thought it had something to do with the special/nested file input name: files[b] instead of file, but they both break! Special/nested file input name in this form: https://webblocks.nl/_form2.html both it breaks the same exact way.

How to reproduce

composer require fabpot/goutte

<?php
require 'vendor/autoload.php';
$goutte = new \Goutte\Client();
$crawler = $goutte->request('GET', 'https://webblocks.nl/_form1.html'); // OR:
// $crawler = $goutte->request('GET', 'https://webblocks.nl/_form2.html');
$form = $crawler->selectButton('Submit it')->form();
$crawler = $goutte->submit($form, [
	'values' => [
		'text' => 'Oele',
	],
]);

Error:

ValueError: Path cannot be empty

From vendor/symfony/mime/Part/DataPart.php:68:

if (false === $handle = @fopen($path, 'r', false)) {
    throw new InvalidArgumentException(sprintf('Unable to open path "%s".', $path));

It doesn't crash on the InvalidArgumentException, but the ValueError from fopen I assume. But that's not the point.

dom-crawler gives browser-kit an empty file, because it sees a file input, and the file is never filled, because I only submit values[text]=Oele


I'm using the Goutte client, because it's an easy package, but I assume it works with just dom-crawler and browser-kit.

Possible Solution

The best solution would be to do exactly what the browser does: send a non-file. I have no idea how that works. Somewhere along the process browser-kit knows the file is empty:

^ array:1 [▼
  "file" => array:5 [▼
    "name" => ""
    "type" => ""
    "tmp_name" => ""
    "error" => 4
    "size" => 0
  ]
]

but it still tries to read it etc.

The second best solution would be to just completely ignore the file and not send it at all. dom-crawler's Form::getFiles() could filter on emptiness.

Additional Context

The full browser-kit Request just before it all breaks:

^ Symfony\Component\BrowserKit\Request {#686 ▼
  #uri: "https://webblocks.nl/_http_server.php"
  #method: "POST"
  #parameters: array:2 [▼
    "op" => "Submit it"
    "values" => array:1 [▼
      "text" => "Oele"
    ]
  ]
  #files: array:1 [▼
    "file" => array:5 [▼
      "name" => ""
      "type" => ""
      "tmp_name" => ""
      "error" => 4
      "size" => 0
    ]
  ]
  #cookies: []
  #server: array:4 [▼
    "HTTP_USER_AGENT" => "Symfony BrowserKit"
    "HTTP_REFERER" => "https://webblocks.nl/_form1.html"
    "HTTP_HOST" => "webblocks.nl"
    "HTTPS" => true
  ]
  #content: null
}

I feel like I must be doing something wrong, because this is such an obvious bug, but I'm not doing anything weird, just load a form and submit it without a file. Has nobody ever done that? How can this be a bug?

@xabbuh
Copy link
Member

xabbuh commented Jan 18, 2023

Can you create a small example application that allows to reproduce your issue?

@rudiedirkx
Copy link
Author

The How to reproduce is all you need. That's it.

mkdir goutte
cd goutte
composer require fabpot/goutte
nano test.php # put that code in
php test.php

PHP Fatal error: Uncaught ValueError: Path cannot be empty in goutte/vendor/symfony/mime/Part/DataPart.php:68

@ging-dev
Copy link
Contributor

The How to reproduce is all you need. That's it.

mkdir goutte
cd goutte
composer require fabpot/goutte
nano test.php # put that code in
php test.php

PHP Fatal error: Uncaught ValueError: Path cannot be empty in goutte/vendor/symfony/mime/Part/DataPart.php:68

The file field is not filled in but in principle it must be filled in, or removed like this:

<?php
use Goutte\Client;

require __DIR__.'/vendor/autoload.php';

$client = new Client();

$crawler = $client->request('GET', 'https://webblocks.nl/_form1.html');

$form = $crawler->filter('form')->first()->form();

// need to remove file field from form
$form->remove('file');

$crawler = $client->submit($form, [
    'values' => [
        'text' => 'Hi bro'
    ]
]);

echo $crawler->html();

@rudiedirkx
Copy link
Author

Why? A browser doesn't do that. A web crawler should take of that automatically, not the dev manually. I don't always know if the form I'm submitting has file fields. Is it the developer's responsibility to crawl the form for file fields before submitting? That makes dom-crawler + browser-kit a whole lot less useful/easy to use.

IF removing it is the way to go, dom-crawler or browser-kit should do that automatically IMO, BUT I don't even think that it's the way to go. A real browser just sends an empty file. Symfony should too.

@rudiedirkx
Copy link
Author

rudiedirkx commented Jan 25, 2023

Whatever the solution is, this very simple code shouldn't break the way it does:

$form = $crawler->selectButton('Submit it')->form();
$crawler = $goutte->submit($form, [
	'values' => [
		'text' => 'Oele',
	],
]);

That's example code. That can't become an uncaught exception somewhere deep in Mime. I don't know where the bug is, but it has to be a bug.

@carsonbot
Copy link

Hey, thanks for your report!
There has not been a lot of activity here for a while. Is this bug still relevant? Have you managed to find a workaround?

@rudiedirkx
Copy link
Author

It's still a bug. There is a specific workaround (manually remove the file field), but not a general/reusable.

See Possible Solution in the OP. If that sounds okay, I can make a PR.

@xabbuh
Copy link
Member

xabbuh commented Jan 19, 2024

@rudiedirkx Can you please check if #53586 fixes your issue?

@carsonbot
Copy link

Hey, thanks for your report!
There has not been a lot of activity here for a while. Is this bug still relevant? Have you managed to find a workaround?

@rudiedirkx
Copy link
Author

The bug is still a bug, with a specific workaround, but not a general. #53586 fixes the exception, but not correctly IMO.

@carsonbot carsonbot removed the Stalled label Jul 23, 2024
@carsonbot
Copy link

Hey, thanks for your report!
There has not been a lot of activity here for a while. Is this bug still relevant? Have you managed to find a workaround?

@rudiedirkx
Copy link
Author

Still a bug. If this is acceptable, I'll make a PR:

The second best solution would be to just completely ignore the file and not send it at all. dom-crawler's Form::getFiles() could filter on emptiness.

The first best solution is too complicated for me. The second best is acceptable.

@carsonbot carsonbot removed the Stalled label Jan 24, 2025
@xabbuh
Copy link
Member

xabbuh commented Jan 24, 2025

@rudiedirkx please do so :)

@rudiedirkx
Copy link
Author

My fix would break all the files tests. Apparently there is a use to getting empty files? It's tested anyway. Not file fields, but files (the name and tmp_name etc array). I can 'fix' the test so it doesn't fail, but then it just tests for "the list of files is always empty", also not exactly useful.

@rudiedirkx
Copy link
Author

@rudiedirkx Can you please check if #53586 fixes your issue?

If your PR just deleted the file from $uploadedFiles instead of making it text, that would be a fix. Better than my attempt.

@xabbuh
Copy link
Member

xabbuh commented Jan 28, 2025

@rudiedirkx I have pushed an update to my PR. Is that what you had in mind?

@rudiedirkx
Copy link
Author

rudiedirkx commented Jan 28, 2025

Yep, exactly like that. Keep the empty files out of the request at the last moment. Much better than my attempt at early intervention. And all tests (except PHP 8.5 👀) pass! Maybe add a test for submitting a form with an empty file field? I don't know if that would be in dom-crawler or http-browser... Or how to test a real submit...

nicolas-grekas added a commit that referenced this issue Feb 11, 2025
…nicolas-grekas)

This PR was merged into the 6.4 branch.

Discussion
----------

[BrowserKit] Fix submitting forms with empty file fields

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | Fix #49014
| License       | MIT

Replaces #53586 and #59621

Commits
-------

0fbfc3e [BrowserKit] Fix submitting forms with empty file fields
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants