Re: form-date with newline in filename

[email protected] (Bill Moseley) Wed, 5 Jul 2017 09:20:50 -0700
Newsgroups perl.libwww
Message-ID <CAKhN_m6-P3f6_kBXJPmys0mtJiiuODmSf3vnEwdrcpNkjk40wg@mail.gmail.com>
--f403045c0cb4f1654a0553946358
Content-Type: text/plain; charset="UTF-8"

On Tue, Jul 4, 2017 at 8:36 PM, Olaf Alders <[email protected]>
wrote:

>
> > On Jul 4, 2017, at 4:41 PM, Bill Moseley <[email protected]> wrote:
> >
> > I'm trying to understand if the Perl code is doing the right thing by
> placing a newline directly in the double-quoted filename in the
> Content-Disposition header.
> >
> > Even though it's probably a bad thing to do, POSIX allows newlines in
> filenames. Receiving systems, of course, must be careful how that filename
> is used -- but in this case it's never actually used in a filesystem (i.e.
> it's just considered metadata).
> >
> > The code below does a full round-trip successfully (meaning the newline
> in the filename is preserved), but that's all within Perl.
> >
> > I'm POSTing to a service written in Golang and that library is
> complaining about malformed headers.
> >
> > My question: Is HTTP::Request not escaping correctly or is Golang
> library not parsing correctly?
> >
> > use strict;
> > use warnings;
> > use HTTP::Request::Common;
> > use HTTP::Response;
> > use HTTP::Body;
> > use Data::Dumper;
> >
> > my $filename = "name with\na newline";
> >
> > my $req = POST(
> >     'http://example.com/post',
> >     content_type => 'form-data',
> >     Content => [
> >         file => [
> >             $0,
> >             $filename,
> >         ],
> >         one => 1,
> >         two => 2,
> >     ],
> > );
> >
> > my $res = HTTP::Response->parse( $req->as_string );
> > my $body = HTTP::Body->new( join( ' ' ,$res->content_type),
> $res->content_length );
> > $body->add( $res->decoded_content );
> > print Dumper $body->upload;
> >
> > Above returns:
> >
> > $VAR1 = {
> >           'file' => {
> >                       'filename' => 'name with
> > a newline',
> >                       'tempname' => '/var/folders/sz/
> w4rntlpx76vcy5xrp441m0qw0000gn/T/6QNv98gd5B',
> >                       'size' => 561,
> >                       'headers' => {
> >                                      'Content-Disposition' =>
> 'form-data; name="file"; filename="name with
> > a newline"',
> >                                      'Content-Type' => 'text/plain'
> >                                    },
> >                       'name' => 'file'
> >                     }
> >         };
> >
> > It seems like header folding is no longer allowed, but I'm not clear
> that this is a case of header-folding:
> >
> > https://stackoverflow.com/questions/521275/how-to-
> escape-a-line-break-literal-in-the-http-header
> >
> > Or asked another way, is Perl or Golang breaking Postel's law?
>
> Hi Bill,
>
> Is it possible for you to print the actual outgoing headers?  With the
> newlines being involved, the order of the headers could make a difference
> here.
>
> Best,
>
> Olaf
>
>
Sure, but I think this is more of a question about how the header is
created. I thought maybe an issue with header-folding, but maybe not.

https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html say a header value
("field-content") can include a "quoted-string", which is what
HTTP::Request (or HTTP::Headers) is doing.

But https://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html says a
quoted-string is:

A string of text is parsed as a single word if it is quoted using
double-quote marks.

       quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )

       qdtext         = <any TEXT except <">>


And "TEXT" is:

       TEXT           = <any OCTET except CTLs,
                        but including LWS>

And "CTL" is:

       CTL            = <any US-ASCII control character
                        (octets 0 - 31) and DEL (127)>

So, that looks like newlines cannot be used.

Is that the correct reading?

I'm also not sure there is a standard way to escape the newlines -- i.e.
might have to be an agreement between the sender and receiver.

If the sender permits newlines in filenames it should be able to represent
those in the header in a standard way -- even if not a good idea to use
newlines in filenames.

What is expected with HTTP::Request::Common?  Should the HTTP::* methods
handle escaping or it expected that all escaping must be done be the caller?






-- 
Bill Moseley
[email protected]

--f403045c0cb4f1654a0553946358
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><div class=3D"gmail_extra"><br><div class=3D"gmail_quo=
te">On Tue, Jul 4, 2017 at 8:36 PM, Olaf Alders <span dir=3D"ltr">&lt;<a hr=
ef=3D"mailto:[email protected]" target=3D"_blank">olaf@wundersolutio=
ns.com</a>&gt;</span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"=
margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-lef=
t:1ex"><div class=3D"gmail-HOEnZb"><div class=3D"gmail-h5"><br>
&gt; On Jul 4, 2017, at 4:41 PM, Bill Moseley &lt;<a href=3D"mailto:moseley=
@hank.org">[email protected]</a>&gt; wrote:<br>
&gt;<br>
&gt; I&#39;m trying to understand if the Perl code is doing the right thing=
 by placing a newline directly in the double-quoted filename in the Content=
-Disposition header.<br>
&gt;<br>
&gt; Even though it&#39;s probably a bad thing to do, POSIX allows newlines=
 in filenames. Receiving systems, of course, must be careful how that filen=
ame is used -- but in this case it&#39;s never actually used in a filesyste=
m (i.e. it&#39;s just considered metadata).<br>
&gt;<br>
&gt; The code below does a full round-trip successfully (meaning the newlin=
e in the filename is preserved), but that&#39;s all within Perl.<br>
&gt;<br>
&gt; I&#39;m POSTing to a service written in Golang and that library is com=
plaining about malformed headers.<br>
&gt;<br>
&gt; My question: Is HTTP::Request not escaping correctly or is Golang libr=
ary not parsing correctly?<br>
&gt;<br>
&gt; use strict;<br>
&gt; use warnings;<br>
&gt; use HTTP::Request::Common;<br>
&gt; use HTTP::Response;<br>
&gt; use HTTP::Body;<br>
&gt; use Data::Dumper;<br>
&gt;<br>
&gt; my $filename =3D &quot;name with\na newline&quot;;<br>
&gt;<br>
&gt; my $req =3D POST(<br>
&gt;=C2=A0 =C2=A0 =C2=A0&#39;<a href=3D"http://example.com/post" rel=3D"nor=
eferrer" target=3D"_blank">http://example.com/post</a>&#39;,<br>
&gt;=C2=A0 =C2=A0 =C2=A0content_type =3D&gt; &#39;form-data&#39;,<br>
&gt;=C2=A0 =C2=A0 =C2=A0Content =3D&gt; [<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0file =3D&gt; [<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0$0,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0$filename,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0],<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0one =3D&gt; 1,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0two =3D&gt; 2,<br>
&gt;=C2=A0 =C2=A0 =C2=A0],<br>
&gt; );<br>
&gt;<br>
&gt; my $res =3D HTTP::Response-&gt;parse( $req-&gt;as_string );<br>
&gt; my $body =3D HTTP::Body-&gt;new( join( &#39; &#39; ,$res-&gt;content_t=
ype), $res-&gt;content_length );<br>
&gt; $body-&gt;add( $res-&gt;decoded_content );<br>
&gt; print Dumper $body-&gt;upload;<br>
&gt;<br>
&gt; Above returns:<br>
&gt;<br>
&gt; $VAR1 =3D {<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&#39;file&#39; =3D&gt; {<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0&#39;filename&#39; =3D&gt; &#39;name with<br>
&gt; a newline&#39;,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0&#39;tempname&#39; =3D&gt; &#39;/var/folders/sz/<wbr>w4rntlpx7=
6vcy5xrp441m0qw0000gn<wbr>/T/6QNv98gd5B&#39;,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0&#39;size&#39; =3D&gt; 561,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0&#39;headers&#39; =3D&gt; {<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &#39;Content=
-Disposition&#39; =3D&gt; &#39;form-data; name=3D&quot;file&quot;; filename=
=3D&quot;name with<br>
&gt; a newline&quot;&#39;,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &#39;Content=
-Type&#39; =3D&gt; &#39;text/plain&#39;<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 },<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0&#39;name&#39; =3D&gt; &#39;file&#39;<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0}<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0};<br>
&gt;<br>
&gt; It seems like header folding is no longer allowed, but I&#39;m not cle=
ar that this is a case of header-folding:<br>
&gt;<br>
&gt; <a href=3D"https://stackoverflow.com/questions/521275/how-to-escape-a-=
line-break-literal-in-the-http-header" rel=3D"noreferrer" target=3D"_blank"=
>https://stackoverflow.com/<wbr>questions/521275/how-to-<wbr>escape-a-line-=
break-literal-<wbr>in-the-http-header</a><br>
&gt;<br>
&gt; Or asked another way, is Perl or Golang breaking Postel&#39;s law?<br>
<br>
</div></div>Hi Bill,<br>
<br>
Is it possible for you to print the actual outgoing headers?=C2=A0 With the=
 newlines being involved, the order of the headers could make a difference =
here.<br>
<br>
Best,<br>
<br>
Olaf<br>
<br></blockquote><div><br></div><div>Sure, but I think this is more of a qu=
estion about how the header is created. I thought maybe an issue with heade=
r-folding, but maybe not.</div><div><br></div><div><a href=3D"https://www.w=
3.org/Protocols/rfc2616/rfc2616-sec4.html">https://www.w3.org/Protocols/rfc=
2616/rfc2616-sec4.html</a> say a header value (&quot;field-content&quot;) c=
an include a &quot;<span style=3D"color:rgb(0,0,0)">quoted-string&quot;, wh=
ich is what HTTP::Request (or HTTP::Headers) is doing.</span><br></div><div=
><span style=3D"color:rgb(0,0,0)"><br></span></div><div><span style=3D"colo=
r:rgb(0,0,0)">But=C2=A0</span><font color=3D"#000000"><a href=3D"https://ww=
w.w3.org/Protocols/rfc2616/rfc2616-sec2.html">https://www.w3.org/Protocols/=
rfc2616/rfc2616-sec2.html</a> says a quoted-string is:</font></div><div><fo=
nt color=3D"#000000"><br></font></div></div></div><blockquote style=3D"marg=
in:0px 0px 0px 40px;border:none;padding:0px"><div class=3D"gmail_extra"><di=
v class=3D"gmail_quote"><div><p style=3D"color:rgb(0,0,0);font-family:Times=
;font-size:medium">A string of text is parsed as a single word if it is quo=
ted using double-quote marks.</p></div></div></div><div class=3D"gmail_extr=
a"><div class=3D"gmail_quote"><div><pre style=3D"color:rgb(0,0,0)">       q=
uoted-string  =3D ( &lt;&quot;&gt; *(qdtext | quoted-pair ) &lt;&quot;&gt; =
)</pre></div></div></div><div class=3D"gmail_extra"><div class=3D"gmail_quo=
te"><div><pre style=3D"color:rgb(0,0,0)">       qdtext         =3D &lt;any =
TEXT except &lt;&quot;&gt;&gt;</pre></div></div></div></blockquote><div cla=
ss=3D"gmail_extra"><div class=3D"gmail_quote"><div><pre style=3D"color:rgb(=
0,0,0)"><br></pre><pre style=3D"color:rgb(0,0,0)"><font face=3D"arial, helv=
etica, sans-serif">And &quot;TEXT&quot; is:</font></pre></div></div></div><=
blockquote style=3D"margin:0px 0px 0px 40px;border:none;padding:0px"><div c=
lass=3D"gmail_extra"><div class=3D"gmail_quote"><div><pre style=3D"color:rg=
b(0,0,0)"><pre>       TEXT           =3D &lt;any OCTET except CTLs,
                        but including LWS&gt;</pre></pre></div></div></div>=
</blockquote><font color=3D"#000000"><span style=3D"white-space:pre"><font =
face=3D"arial, helvetica, sans-serif">And &quot;CTL&quot; is:</font></span>=
</font><div><font color=3D"#000000"><span style=3D"white-space:pre"><font f=
ace=3D"arial, helvetica, sans-serif"><br></font></span></font></div><blockq=
uote style=3D"margin:0px 0px 0px 40px;border:none;padding:0px"><div><pre st=
yle=3D"color:rgb(0,0,0)">       CTL            =3D &lt;any US-ASCII control=
 character
                        (octets 0 - 31) and DEL (127)&gt;</pre></div></bloc=
kquote><div><div class=3D"gmail_extra"><div class=3D"gmail_quote"><div>So, =
that looks like newlines cannot be used.</div><div><br></div><div>Is that t=
he correct reading?</div><div><br></div><div>I&#39;m also not sure there is=
 a standard way to escape the newlines -- i.e. might have to be an agreemen=
t between the sender and receiver.</div><div><br></div><div>If the sender p=
ermits newlines in filenames it should be able to represent those in the he=
ader in a standard way -- even if not a good idea to use newlines in filena=
mes.</div><div><br></div><div>What is expected with HTTP::Request::Common?=
=C2=A0 Should the HTTP::* methods handle escaping or it expected that all e=
scaping must be done be the caller?</div><div><br></div><div><br></div><div=
>=C2=A0</div></div><br><br clear=3D"all"><div><br></div>-- <br><div class=
=3D"gmail_signature">Bill Moseley<br><a href=3D"mailto:[email protected]" ta=
rget=3D"_blank">[email protected]</a></div>
</div></div></div>

--f403045c0cb4f1654a0553946358--