Style attributes dropped with parentheses in image url

Hello all, this is something of an experience report as well as documenting an issue I had in case it is helpful to someone else in the same place. Context:

  • I was using a style attribute on an element to set the background-image.
  • The image in question had ‘parentheses’ in the name, eg. image(1).jpg
  • This caused the entire style attribute to be dropped.

Here is an ellie with the important part of the problem: https://ellie-app.com/9mW3b26n4zPa1

Image divs are drawn to the screen using:

makeImageDiv url =
    let
        cssUrl =
            [ "url("
            , url
            , ")"
            ] |> String.concat
    in
    div
            [ Attributes.style "background-image" cssUrl 
            , Attributes.class "image-div"
            ]
            [] 

so basically the url is wrapped up in url(<url>) and that is given as the background-image in a style attribute. In the ellie, I give it three urls, to create three divs (urls snipped for brevity & clarity):

imageUrls = 
    let 
        badUrl =
            "[snip] Boutique(1).jpg?[snip]"
    in
    [ "[snip]Demo.png?[snip]"
    , badUrl
    , badUrl
        |> String.replace "(" "%28"
        |> String.replace ")" "%29"
    ]

So, the first one with no parentheses in the url displays. The second one, with ( and ) in the url does not display. The third one displays, and this is the same url as the one which does not, except that I use String.replace to percent-encode the ( and ) characters.

When I say the second one doesn’t display, I mean the entire style attribute is dropped, so if you use the inspector on the ellie output you see:

<div class="image-div" style="background-image=url(...)"></div>
<div class="image-div"></div>
<div class="image-div" style="background-image=url(...)"></div>

So as I say, this is mostly just in case someone comes up against this problem as well, but I guess I have a couple of questions:

  1. Is this intended behaviour? I’m sure there is some XSS or other security reason as to why this is, can anyone enlighten me?
  2. I was a bit surprised that Url.percentEncode doesn’t encode ( and ) in the way that I have done with String.replace. Again is this intended behaviour?
> import Url
> Url.percentEncode "()"
"()" : String
>

That is not an Elm issue - your code is just invalid CSS.

You can fix that either by

  • quoting URL: url("image(1).jpg")
  • escaping parentheses with \: url(image\(1\).jpg)
  • or using %-encoding as you said

From CSS2 - 4.3.4 URLs and URIs:

Some characters appearing in an unquoted URI, such as parentheses, white space characters, single quotes (’) and double quotes ("), must be escaped with a backslash so that the resulting URI value is a URI token: ‘(’, ‘)’.

Depending on the type of URI, it might also be possible to write the above characters as URI-escapes (where “(” = %28, “)” = %29, etc.) as described in [RFC3986].

1 Like

Ahhh, that makes sense, thanks.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.