2

Related to Properly simulate "pattern" attribute with javascript: How do I set an input pattern using JavaScript?

when I try element.pattern = 'a\S' (as a trivial example), then the pattern ends up in aS (with the backslash being removed). The same happens with

element.pattern = `a\S`

Of course I could double the backslash, but I'd like to create the javascript using a string constant that is used in CGI's textfield({-pattern => PATTERN}).

A bit like this:

#...perl part to create initial HTML element...
textfield(-pattern => PERL_PATTERN, ...);

#...creating JavaScript to add or remove pattern dynamically
element = document.getElementById(...);
if (...)
    element.pattern = `${\PERL_PATTERN}`;
else
    element.removeAttribute('pattern');

I'd like to avoid something ugly as

use constant PERL_PATTERN => 'a\S';
use constant JAVASCRIPT_PATTERN => PERL_PATTERN =~ s/\\/\\\\/gr;

to duplicate the pattern, but with backslashes doubled. Is there a way to keep the backslash in JavaScript?

4
  • 2
    There was an older question for this. However, I did not feel comfortable closing this question against the older one, since the older one has been spreading disinformation for literally a decade. Therefore I did the opposite and closed the older question against this one. Commented Sep 18 at 13:43
  • Have you tried something like this? perl -e 'use constant PERL_PATTERN => q"a\S"; use constant JAVASCRIPT_PATTERN => quotemeta(PERL_PATTERN); print "${\JAVASCRIPT_PATTERN}\n";' They actually both work for me, but I suspect javascript may need the backslash to be escaped, and quotemeta should work. Commented Sep 21 at 0:44
  • @user3408541 This might work, but it's much like the use constant JAVASCRIPT_PATTERN => PERL_PATTERN =~ s/\\/\\\\/gr I do not like. Commented Sep 22 at 8:41
  • @U. Windl I am not sure there is a way around it. It looks like you have to escape the \S somehow. Maybe use constant PERL_PATTERN => "a\\S"; which is just another way to escape it manually. All that will work. There are many ways to escape it manually, but if you want something different or unusual I dont know there is anything. Commented Sep 22 at 9:03

3 Answers 3

3

You can use String.raw to accomplish what you're trying to do.

element.pattern = String.raw`a\S`
Sign up to request clarification or add additional context in comments.

4 Comments

So simple once you know how to do it ;-)
So dangerous if you do not know how to sanitise it...
Any input that is supplied by one user and used by another should be sanitized for wherever it's used, raw string or not
The pattern is from a constant (I thought it's obvious from PERL_PATTERN), and JavaScript just adds or removes it, depending on another user input field. In general I agree, however.
1

To properly generate a JS string literal from Perl, you can use the following:

sub string_to_js_lit { "`" . quotemeta( $_[0] ) . "`" }

my $js = "
   ...
   element.pattern = ${\string_to_js_lit( PERL_PATTERN )};
   ...
";

Details follow.


Since ${\SUB} is a trick to call a sub or constant from within a Perl string literal, it sounds like you are asking how to do the following correctly in Perl:

my $js = "
   ...
   element.pattern = `${\PERL_PATTERN}`;  // XXX
   ...
";

You are attempting to generate JavaScript code. So you need something like this:

sub string_to_js_lit { ... }

my $js = "
   ...
   element.pattern = ${\string_to_js_lit( PERL_PATTERN )};
   ...
";

I believe the following is a simple but sufficient implementation of string_to_js_lit:

sub string_to_js_lit { "`" . quotemeta( $_[0] ) . "`" }

Note that the following is wrong (code injection bug):

my $js = "
   ...
   element.pattern = String.raw`${\PERL_PATTERN}`;  // XXX
   ...
";

3 Comments

Can you give an example for code injection? The code will be injected to Perl, not JavaScript, right?
A code injection bug occurs when two different things are combined. In this case, the Perl code is combining a regex pattern with JS code. To do it properly, the regex pattern must first be converted to JS code so that JS code is combined with JS code. Imagine if the value of PERL_PATTERN contains a backtick.
Fixed string_to_js_lit. It now handles CR and LF.
0

Using String.raw introduces a vulnerability you do not have if you JSON.encode the pattern

const r1 = String.raw`a\S`;         // 'a\\S' as characters a and \S
const user = '</script><img src=x onerror=alert(1)>';
const r2 = String.raw`hello ${user}`; // inserts user as-is, no escaping

If you can use it safely, then good for you, otherwise you need to escape.

Here is a test of simple escaping

const el = document.createElement('input');
el.pattern = 'a\\S';
console.log(el.pattern);           // "a\S"
console.log(el.getAttribute('pattern')); // "a\S"
new RegExp(el.pattern).test('aX'); // true

You can code it on the server like this which is XSS safe

use JSON::PP 'encode_json';
my $js_pattern = encode_json($perl_pattern);  # will escape backslashes
print "element.pattern = $js_pattern;";

Alternatively set the string you want as a pattern in a data attribute on the server

const element = document.getElementById('foo');
element.pattern = element.dataset.pattern;
console.log(element.pattern);
<input id="foo" data-pattern="a\S">

3 Comments

This answer simply doubles the backslash, something that I did not want to do.
Which you have to if you do not want to use String.raw, which is vulnerable to script injections
You cannot know, but the pattern for input isn't user-supplied. I just wanted to follow the "one source principle" (i.e. not duplicate literal data (and variants of it) if possible).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.