gadgets.util.escapeString() should make arbitrary text safe to include
as content in a normal HTML element, e.g. <b>, <div>.
gadgets.util.escapeString() should make arbitrary text safe in
javascript strings. (We may be here, not sure.)
gadgets.util.escapeString() should make arbitrary text safe for some
HTML attributes. (This is definitely ill-specified at the moment.)
iGoogle defined their equivalent of escapeString to escape <>"', I
believe. We could make the opensocial function equivalent, but that
would restrict it's usage in javascript strings and HTML attributes.
Cheers,
Brian
If escapeString("<") and escapeString("<") both return "<", what
are the expected results of gadgets.util.unescapeString("<") and
gadgets.util.unescapeString("&lt;")?
I wonder why idempotency is a desired feature of gadgets.util.escapeString().
// Maps code points to the value to replace them with.
// If the value is "false", the character is removed entirely, otherwise
// it will be replaced with an html entity.
var escapeCodePoints = {
// nul; most browsers truncate because they use c strings under the covers.
0 : false,
// new line
10 : true,
// carriage return
13 : true,
// double quote
34 : true,
// single quote
39 : true,
// less than
60 : true,
// greater than
62 : true,
// Backslash
92 : true,
// line separator
8232 : true,
// paragraph separator
8233 : true
};
Hmm... I don't think that's ideal because unescape() would then have
to be an identity function to satisfy both conditions.
Given the two premises:
unescape(escape(S)) === S (1)
escape(escape(S)) === escape(S) (2)
It can be deduced from (1) that:
unescape(unescape(escape(S))) === unescape(S) (3)
It can be obtained from (2) that:
unescape(unescape(escape(S))) === unescape(unescape(escape(escape(S)))) (4)
Substitute "escape(S)" into S in (3), and we obtain from (4) that:
unescape(unescape(escape(escape(S)))) === unescape(escape(S)) (5)
From (3), (4), (5), and (1) we then have:
unescape(S) === S
Let's call S a "terminal" string if escape(S) === S; otherwise,
"non-terminal". An idempotent escape() maps non-terminal strings to
terminal strings and terminal strings to themselves. If escape() is
not a trivial identity function -- in other words, if the set of
non-terminal strings is not empty -- at least one non-terminal string
and one terminal string map to the same terminal string which can't be
correctly decoded by unescape() unless both unescape() and escape()
are identity functions.
Please go say "+1" to the spec proposal to add a sanitization function. =)
<static> String unescapeString(str)
Zhen is right that the only function that is idempotent and has a reverse (as in f'(f(x)) == x ) is the identity function. If there is anyone in doubt, we should clear that out of the way first.
Not doing autoescaping leads immediately to XSS everywhere. If
removing autoescaping is a goal of this proposal, -1.
(Tempted to go register throw-away e-mail accounts to vote -1 more
times on any such proposal.)
-1 from me too.
When we discussed this and the change was made, I did bring up the
point of escaping & to avoid these issues, but it was decided to stick
to not escaping & due to double escaping, etc. Still, this had no
security implications so we agreed on it - but it does bring up some
issues brought up above that I do agree with. Nevertheless, there is a
reason we settled on this solution as a compromise, and not escaping
stuff at all is currently not an option, until we can override
innerHTML with a html-sanitized version (caja). The risk is too big,
and this was already decided/discussed at meetings months ago.
The idea about providing (non-default!) ways to stop escaping is OK.
Since caja has a client-side JS innerHTML sanitizer it should be easy
to copy this and tell people to use it. The problem is, we can't
guarantee (or force) that people use it without something like Caja.
Since we don't control what gadgets people write, arguably that won't
solve anything. But it would still be nice to have...
Not possible, unless you want to replace the default string escaping
with default HTML sanitizing instead. That seems contrary to the
general theme of the comments on this thread, however.
Hrm. I'm confused. Can you explain this a different way, perhaps
with some examples?
> I'd like to hear from Kevin Brown, Brian and Peter whether this is something
> they can live with.
I'm not sure giving up idempotence is a good idea. I like the idea of
being able to tell gadget developers "gadgets.util.escapeString is
magic security dust, sprinkle it liberally around anything you are
going to assign to innerHTML". That's not a good idea unless
escapeString is idempotent, because you end up with double-escaping
issues.
Compare that with the explanation you need to give to a gadget
developer about how to avoid XSS if escapeString isn't idempotent.
They need to rewrite their code to make sure that escaping only
happens in a single place, and adding a new escape or unescape may
introduce either functionality or security bugs. A non-idempotent
escapeString will make people work harder to be secure.
All that said, having escapeString() and unescapeString() not be
functional inverses is pretty weird. The weirdness might be worth it,
since it makes escapeString easier to use. Assuming we have a solid
definition of what escapeString and unescapeString do and how they
should be used, can we live with the weirdness?
> I wouldn't mind diving deeper into the whole escaping controversy for the 0.9 release
Agreed.
Cheers,
Brian
I'm not aware of any other unescaping API that doesn't always return
the same string you pass to its escaping counterpart. I honestly doubt
(un)escapeString will be easier to use if developers have to learn the
quirky escaping/unescaping rules.
Seriously, can we not treat gadget developers like they are
incompetent? (Hey, we have made you a knife. We don't trust you not to
cut your fingers off, so the blade is plastic and blunt. We know it
looks funny and doesn't match the fork. Just deal with it.)
Hrm. I'm confused. Can you explain this a different way, perhaps
On Sun, Apr 27, 2008 at 1:30 AM, Reinoud Elhorst <rei...@hyves.nl> wrote:
> My proposal is:
> Let browser(x) be the function that the browser uses to translate from
> HTML-->text. In that case, (un)escape should be defined as:
>
> unescape(escape(S)) == S
> and
> browser(escape(S)) == S
> (note that unescape and browser do not have to be the same function, they
> are only guaranteed to output the same on the result from the escape
> function)
with some examples?
I'm not sure giving up idempotence is a good idea. I like the idea of
> I'd like to hear from Kevin Brown, Brian and Peter whether this is something
> they can live with.
being able to tell gadget developers "gadgets.util.escapeString is
magic security dust, sprinkle it liberally around anything you are
going to assign to innerHTML". That's not a good idea unless
escapeString is idempotent, because you end up with double-escaping
issues.
Compare that with the explanation you need to give to a gadget
developer about how to avoid XSS if escapeString isn't idempotent.
They need to rewrite their code to make sure that escaping only
happens in a single place, and adding a new escape or unescape may
introduce either functionality or security bugs. A non-idempotent
escapeString will make people work harder to be secure.
All that said, having escapeString() and unescapeString() not be
functional inverses is pretty weird. The weirdness might be worth it,
since it makes escapeString easier to use. Assuming we have a solid
definition of what escapeString and unescapeString do and how they
should be used, can we live with the weirdness?