php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #74472 readdir strips leading and trailing quotes in a filename
Submitted: 2017-04-19 04:36 UTC Modified: 2017-04-19 14:29 UTC
From: james at workinout dot com Assigned:
Status: Not a bug Package: Filesystem function related
PHP Version: 7.1.4 OS: Fedora
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: james at workinout dot com
New email:
PHP Version: OS:

 

 [2017-04-19 04:36 UTC] james at workinout dot com
Description:
------------
in drupal, some files named with leading and trailing quotes

filename === '{{ THEME SANITIZED }}.behaviors.js'

$entry = readdir() --- > $entry === -> {{ THEME SANITIZED }}.behaviors.js  (NO QUOTES)

stat will not work because the stripped quotes are needed: stat
complains like below:

stat --format=%G (command used..)

stat: cannot stat '{{': No such file or directory
stat: cannot stat 'THEME': No such file or directory
stat: cannot stat 'SANITIZED': No such file or directory
stat: cannot stat '}}.behaviors.js': No such file or directory
stat: cannot stat '{{': No such file or directory
stat: cannot stat 'THEME': No such file or directory
stat: cannot stat 'SANITIZED': No such file or directory
stat: cannot stat '}}.behaviors.js': No such file or directory



Test script:
---------------
Solution is to put the quotes back in filename.

$entry = "'" . $entry . "'".

stat works ok then...

if ($handle = opendir($name)) {

      while (false !== ($entry = readdir($handle))) {

         if (preg_match('/\.$|\..$/', $entry)) {
                  continue;
         }

     //special case for filenames with single quotes (they are stripped by readdir() )
     // used in omega themes with {{ in the filename..

                       if(preg_match('/\{/', $entry)) {

                           $entry = "'" . $entry . "'";

                       }

<after this, stat will work....

Expected result:
----------------
providing an optional argument in readdir() to not strip leading,trailing quotes would be useful


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-04-19 05:13 UTC] requinix@php.net
-Status: Open +Status: Not a bug -Type: Feature/Change Request +Type: Bug
 [2017-04-19 05:13 UTC] requinix@php.net
The files don't have quotes.
http://cgit.drupalcode.org/omega/tree/omega/starterkits/default/js?h=7.x-4.x

The filename must be escaped in the shell command, like with escapeshellarg. Not doing so will cause problems for files that contain spaces or other potentially unsafe characters (such as '{'). For example,
  exec("stat --format=%G " . escapeshellarg($entry));
The fact that the escaped version has quotes is simply because that's the easiest way to make a string safe.

The quotes you're seeing mentioned somewhere are probably indicative of either PHP code (quotes form a string) or an actual shell command (author escaped the filename).
 [2017-04-19 05:28 UTC] james at workinout dot com
the quotes I AM seeing are when I do ls -al in a bash shell. see below

the filename ON the filesystem has leading and trailing quotes, as I said.

i tried escapeshellarg() but it simply puts in backslashes in front
of the quotes, which of course, creates a filename that does not exist.

drwxr-xr-x  2 james apache 4096 Apr 10 19:21 .
drwxr-xr-x 10 james apache 4096 Apr 10 19:21 ..
-rw-r--r--  1 james apache 2900 Apr 10 19:21 '{{ THEME SANITIZED }}.behaviors.js'

obviously, the quotes are there...
 [2017-04-19 05:37 UTC] requinix@php.net
-Status: Not a bug +Status: Feedback
 [2017-04-19 05:37 UTC] requinix@php.net
Well that's not how the files are bundled. Where did you download it from?

As for the backslashes, those are supposed to be there. That's how escaping works.
https://www.shellscript.sh/escape.html

Besides, I couldn't reproduce what you're describing anyways. And though this is Linux, I can't imagine it being any different on OSX.

# ls -la
total 4
drwxrwxrwx 2 root root  0 Apr 18 22:33 .
drwxrwxrwx 2 root root  0 Apr 18 22:32 ..
-rw-rw-rw- 1 root root  0 Apr 18 22:32 'foo'
-rw-rw-rw- 1 root root 85 Apr 18 22:33 test.php
# cat test.php
<?php

$h = opendir(".");
while ($f = readdir($h)) {
        var_dump($f);
}
closedir($h);

# ~/php/PHP-7.1.4/bin/php test.php
string(1) "."
string(2) ".."
string(5) "'foo'"
string(8) "test.php"
 [2017-04-19 06:18 UTC] james at workinout dot com
-Status: Feedback +Status: Open
 [2017-04-19 06:18 UTC] james at workinout dot com
actually, running it all on fedora 25, sorry about that typo

strange. downloaded from drupal.org as far as i remember.
its weird, I know. cant ever recall seeing a filename with 
quotes in it. i know about backslashes. used those with
fortran in 1975. 

Entry before: {{ THEME SANITIZED }}.behaviors.js     (quotes trimmed by readdir() )
Entry after: \'{{ THEME SANITIZED }}.behaviors.js\'  (after using addslashes() )
perhaps echo() and var_dump() use different methods. var_dump using getc/putc ? echo not ?
 [2017-04-19 06:27 UTC] requinix@php.net
-Status: Open +Status: Not a bug
 [2017-04-19 06:27 UTC] requinix@php.net
echo will output a thing as it is with no modifications, besides converting it to a string if it isn't one already. var_dump, being a debugging tool, will output using a representation that makes it easier to know what the thing's exact type and value is; strings will have quotes around them to signify that they are strings, for instance.

As you can see, readdir is not stripping quotes because there are no quotes to begin with. You have code that is adding the quotes somewhere, then on top of it apparently using addslashes which will escape the quotes with backslashes. I don't know why the code is doing any of that but I'm confident it should not be.

Like I said, you need to be using escapeshellarg to put strings into shell commands. It does all the work for you - don't put quotes around the string yourself, don't addslashes yourself.
 [2017-04-19 06:47 UTC] spam2 at rhsoft dot net
Nonsense - the quotes you see in ls - la are added by ls itself when the file name contains spaces and are a quoting so that noobs can copy and paste 

Just type "ls - la | cat" and you see what a non-interactive shell sees

RTFM escapeshellargs
 [2017-04-19 06:54 UTC] requinix@php.net
> Nonsense - the quotes you see in ls - la are added by ls itself when the file
> name contains spaces and are a quoting so that noobs can copy and paste
[citation needed]

# touch "'foo'"
# touch '"bar"'
# touch 'with spaces'
# ls -la
total 4
drwxrwxrwx 2 root root 0 Apr 18 23:53 .
drwx------ 2 root root 0 Apr 18 23:49 ..
-rw-rw-rw- 1 root root 0 Apr 18 23:53 "bar"
-rw-rw-rw- 1 root root 0 Apr 18 23:53 'foo'
-rw-rw-rw- 1 root root 0 Apr 18 23:53 with spaces
# ls -la | cat
total 4
drwxrwxrwx 2 root root 0 Apr 18 23:53 .
drwx------ 2 root root 0 Apr 18 23:49 ..
-rw-rw-rw- 1 root root 0 Apr 18 23:53 "bar"
-rw-rw-rw- 1 root root 0 Apr 18 23:53 'foo'
-rw-rw-rw- 1 root root 0 Apr 18 23:53 with spaces
# ls -laQ
total 4
drwxrwxrwx 2 root root 0 Apr 18 23:53 "."
drwx------ 2 root root 0 Apr 18 23:49 ".."
-rw-rw-rw- 1 root root 0 Apr 18 23:53 "\"bar\""
-rw-rw-rw- 1 root root 0 Apr 18 23:53 "'foo'"
-rw-rw-rw- 1 root root 0 Apr 18 23:53 "with spaces"
 [2017-04-19 08:03 UTC] spam2 at rhsoft dot net
requinix@php.net: you missed the "actually, running it all on fedora 25" and most likely you use some outdated stuff like Debian

here you go:
https://bugzilla.redhat.com/show_bug.cgi?id=1361694
https://unix.stackexchange.com/questions/258679/why-is-ls-suddenly-wrapping-items-with-spaces-in-single-quotes

[harry@rh:/downloads]$ /bin/ls -la
insgesamt 132
drwxrwxrwt   2 root  root       122880 19. Apr 10:01 .
drwxr-xr-x+ 36 harry root         4096 16. Mär 15:44 ..
-rw-r-----   1 harry verwaltung      0 19. Apr 10:01 'with spaces'

[harry@rh:/downloads]$ /bin/ls -la | cat
insgesamt 132
drwxrwxrwt   2 root  root       122880 19. Apr 10:01 .
drwxr-xr-x+ 36 harry root         4096 16. Mär 15:44 ..
-rw-r-----   1 harry verwaltung      0 19. Apr 10:01 with spaces
 [2017-04-19 08:11 UTC] requinix@php.net
-Operating System: osx 10.12 +Operating System: Fedora
 [2017-04-19 08:11 UTC] requinix@php.net
Then that would explain why OP thought the filename had quotes.

...wow, that's a stupid decision.
 [2017-04-19 08:27 UTC] spam2 at rhsoft dot net
well, whatever theme he is using - i would stop to use anything from that developer which did not get the basics that a filename on webservers only should have [a-z][0-9]-_ meaning no special chars, no spaces, no uppercase letters

anyways the "ls" out put is to help noobs copy&paste

stat with spaces
versus
stat 'with spaces'

however, it's not uncommon that outputs of interactive shells are different than what scripts really see (colors, automatic paging and so on) - hence pipe it through "cat" leads to get the non-interactive version because of the pipe
 [2017-04-19 14:25 UTC] james at workinout dot com
escapeshellarg() works. after it adds back single quotes around the file then stat works. using echo shows the single quotes are added back. but as i said below, readdir() is stripping the quotes, otherwise, I would not need to use escapeshellarg.
as someone said below, its better not to use quotes in filenames ..
 [2017-04-19 14:29 UTC] requinix@php.net
James. There are no quotes in the filename. Not at all. ls is lying to you.
 [2017-04-19 14:36 UTC] spam2 at rhsoft dot net
> escapeshellarg() works. after it adds back single quotes around the file

there is nothing to add BACK because there was nothing

> using echo shows the single quotes are added back

no

> but as i said below, readdir() is stripping the quotes, 

DAMNED there are no quotes in the filename, read the responses you got here and try to understand at least the basics 

> otherwise, I would not need to use escapeshellarg

DAMNED you ALWAYS have to use escapeshellarg() when you supply soemting as shell param BECAUSE it filename could contain a space or special chars which are DANGEROUS and could pe interpreted by the caller in unpredicatable ways

SIMPLE EXAMPLE:
file is called '-rf *' and yes YOU CAN create such file by "touch \*" - guess what happens with passthru("rm /folder/$file");
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Fri Dec 04 02:01:23 2020 UTC