Output looks correct
No, it only "looks correct", because the browser ignores white-space characters.
What happens is that the string "penuts_thumb.png" is surrounded by whitespace. When this whitespace is serialized as part of the src attribute value, it is encoded (normalized) -- this is why you see %0A (code for newline) anf %09 (code for tab).
This transformation helps to see exactly what is generated in each case:
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" indent="yes"/>
 <xsl:variable name="workspace" select="'/workspace'"/>
 <xsl:template match="/">
     <img src="{$workspace}/uploads/{/data/news-articles/entry/image-thumbnail}"/>
     ===========
     <xsl:apply-templates/>
 </xsl:template>
 <xsl:template match="entry">
  "<xsl:value-of select="image-thumbnail"/>"
 </xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<data>
 <news-articles>
  <entry>
    <image-thumbnail>
                     penuts_thumb.png
    </image-thumbnail>
  </entry>
 </news-articles>
</data>
produces this output:
<img src="/workspace/uploads/%0A                     penuts_thumb.png%0A    ">
     ===========
"
penuts_thumb.png
    "
As we can see (thanks to the quotes) in the second case the string "penuts_thumb.png" is also surrounded by a lot of whitespace characters.
Solution:
Use the normalize-space() function in this way:
<img src=
"{$workspace}/uploads/{normalize-space(/data/news-articles/entry/image-thumbnail)}"/>