2

Suppose I have some string and I want to use function "replace" (ignore console.log and white chars in your head)

console.log( 
    "truefalse"["replace"]("true") 
)

And I want to reduce used characters of this code using jsfuck (more info here) which I do by hand as follows (ignore log, comments, white chars, new lines )

console.log(
    // "truefalse"[
    ([]+!![]+![])[

    // long 'replace' word:
    []+(!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]

    // ]("true") 
    ]([]+!![])
);

As you can see I convert first code, to code which use only 6 characters: []{}!+ (this is idea of jsfuck)

Question: It is possible to call function replace with 2 arguments - Like that: "truefalse"["replace"]("true","1") and write it in similar way in JSF (without any kind of 'eval' which interpret string as code)? I have/see problem with comma which separate arguments... :(

PS: I give link to my fork of jsfuck (which is up to date - and produce much smaller output than jsfuck.com old site)

j08691
  • 204,283
  • 31
  • 260
  • 272
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
  • 1
    You can create a regex without necessarily needing to type `/`: `new RegExp('e', 'g')`. You can also get any character with `String.fromCharCode` (e.g. `String.fromCharCode(47) === '/'`) – Gershom Maes Aug 26 '20 at 16:00
  • @Gershom Actually by `new Regexp` you reduce Question2 to Question1 (or at least we are close to solution if exists) - write down you comment as answer I give +1. The second part with fromCharCode is problematic because it produce string and to "run" this you sould use `eval` or something – Kamil Kiełczewski Aug 26 '20 at 16:06

2 Answers2

3

You can pass 2 arguments to a function by playing with Array.prototype.reduce.

The idea is to call reduce on an array with 2 values, and pass only one argument (callback) to it (i.e. no argument for the reducing starting value). That way the first and second value of that array will serve as arguments to the callback, which will only be called once.

In your example, the array with 2 values would have to be:

["true", "1"]

which can be formed using concat:

["true"]["concat"]("1")

Then call reduce on that, and pass replace as the callback function:

["true"]["concat"]("1")["reduce"]("truefalse"["replace"])

The only issue to resolve now is to ensure that the callback is called with the right this-binding, because "truefalse" does not play a role in the actual invocation of the callback call now. We can solve this with a call of .bind:

So the expression to evaluate is this one

console.log(
    ["true"]["concat"]("1")["reduce"](""["replace"]["bind"]("truefalse"))
);
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
trincot
  • 317,000
  • 35
  • 244
  • 286
  • Great answer :) ! I wonder - It is possible to do In "flow" way which allow to call another replace in right side of result without nesting it - smething like that: `"truefalse"["replace"]("true","1")["replace"]("false","0")....` (I can create another separate question for this if you think it will be good) – Kamil Kiełczewski Aug 26 '20 at 18:27
  • Really clever! Looks like `reduce` is the key to currying without commas. And this approach naturally extends to any number of args. In case this is generalized to accept `n` arguments, need to make sure that `reduce` is skipped altogether in the case of `n = 1` (since `[ 1 ].reduce(fn)` never calls `fn` at all) – Gershom Maes Aug 26 '20 at 18:28
  • @Kamil, I don't immediately see how to avoid nesting... not sure if it is possible. You can try another question. It would certainly keep me busy. – trincot Aug 26 '20 at 18:34
  • question is [here](https://stackoverflow.com/q/63604058/860099) – Kamil Kiełczewski Aug 26 '20 at 18:58
1

Question 2 has a simple solution: prefer the new RegExp constructor; e.g. /e/g becomes new RegExp('e', 'g');

The following would resolve question 1 if it didn't rely on the ... operator:

We can replace:

console.log('a', 'b', 'c');

With:

console.log(...[ 'a' ].concat([ 'b' ]).concat([ 'c' ]));

Using .concat allows us to group values together without using commas.

I am close to saying that question 1 cannot be properly solved without interpreting a string as js code (edit: refuted by trincot!):

  • Arguments will need to be held in an array (direct arguments cannot be used since they always require commas; Array is the only datatype that can be converted into arguments)
  • You will need to use Function.prototype.apply in order to consume Array arguments
  • Function.prototype.apply will always require the use of a comma, to separate the this value from the array of arguments
  • The only way to avoid multiple arguments to Function.prototype.apply is to apply some form of currying
  • All currying will be facilitated by Function.prototype.(apply|bind|call)
  • All three of these currying methods require multiple arguments, and therefore necessitate commas!
Gershom Maes
  • 7,358
  • 2
  • 35
  • 55
  • You'll want to use `apply`, not `call`, and `null` is not (always) the value needed for `this`, but this can be solved for any example. – trincot Aug 26 '20 at 16:20
  • Ah you're right, and you raise an additional issue: `.apply` will need two parameters (`null` and the array itself), and therefore a comma... I need to think about that. – Gershom Maes Aug 26 '20 at 16:23
  • Indeed, the comma brings us back to square 1 ;-) – trincot Aug 26 '20 at 16:25
  • (3) `dots` are forbidden :P you can use `[]()!+` double quotes `"` and letters. For instance - I cannot use `"truefalse"["replace"].call` but I can `"truefalse"["replace"]["call"]` (but `...` have different meaning so If you want to use it, you need to show how to replace them to above restrictions – Kamil Kiełczewski Aug 26 '20 at 16:31
  • Yeah I think this is fully impossible. `new Function(/* code */)` is the master key of jsfuck, without it you are really quite limited in what you can do. – Klaycon Aug 26 '20 at 17:29